欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

jpacp插件实现魔兽局域网连接,难点解决

程序员文章站 2024-03-17 19:37:58
...
很久没写技术型博文了,最近一直在研究java的第三方jar包jpacp。想研究研究这玩意是否能实现局域网搜索。经过20多天的努力,将几个技术难点通通解决,最终得出结论,完全可以用它来实现局域网互连,只是如果做出来后,没有著名的WarSeacher那么简洁,精巧,方便。因为它比较依赖于安装环境,1,必须装wincacp 2、必须装jdk。3.界面的美观与操作性也是个问题。

下面我将列出这些天攻克的技术难点。
第一。怎么抓war发送的搜索包,地图包,与建主包。是需要用java写,还是利用开源的项目。
第二。抓到包以后需不需要自己解析,然后发给war,告诉它这是我解析后的包。
第三。抓到以后在局域网里能看到,但你点击它就是无法加入游戏。

很幸运的,我在网上找到了java语言的第三方jar包,后来经过仔细研究源代码,发现可以干很多底层的事情,比如,发送带ip报头的数据包,修改,ip报头的ip地址。


先说解决的难点。利用jpacp抓网络上的包。先声明下,以下的所有环节,都是建立在先装好wincacp,jdk,jpacp的环境上的。(有些图片不能即时插入到对应的文字下面,就只好都放到附件里面了,大家对应着看吧)

利用jpacp的抓包程序

package com.lyh.test;

import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;

import com.lyh.common.Util;

import jpcap.*;
import jpcap.packet.IPPacket;
import jpcap.packet.Packet;
import jpcap.packet.UDPPacket;

class Tcpdump implements PacketReceiver {

public static Packet packet ;

public static NetworkInterface[] devices = JpcapCaptor.getDeviceList();

public static JpcapSender jp;

public void receivePacket(Packet packet) {
this.packet = packet;

System.out.println("======================================");
System.out.println(packet);
System.out.println("sec --"+packet.sec);
System.out.println("uec --"+packet.usec);
System.out.println("datalink dst_mac[]--"+packet.datalink);
System.out.println("datalink src_mac[]--"+packet.datalink);
System.out.println("header --" +" length--"+ packet.header.length + "--" + Arrays.toString(packet.header));
System.out.println("data --"+" length--" +packet.data.length + " --"+ Arrays.toString(packet.data));
System.out.println("len --" + packet.len);
System.out.println("caplen -- " + packet.caplen);
System.out.println("header[] --"+Util.bytesToHexString(packet.header));
System.out.println("data[] --"+Util.bytesToHexString(packet.data));
System.out.println("======================================");
System.out.println();

try {
Thread.sleep(1);
IPPacket ip = (IPPacket)packet ;
try {
ip.setIPv4Parameter(ip.priority, ip.d_flag, ip.t_flag, ip.r_flag, ip.rsv_tos, ip.rsv_frag, ip.dont_frag, ip.more_frag, ip.offset, ip.ident, ip.hop_limit, ip.protocol, InetAddress.getByName("172.29.31.202"), ip.dst_ip);
} catch (UnknownHostException e) {
e.printStackTrace();
}

jp.sendPacket(ip);
} catch (InterruptedException e) {
e.printStackTrace();
}
}

public static void main(String[] args) throws Exception {
if(args.length>1){

for (int i = 0; i < devices.length; i++) {
System.out.println("the index '"+i+"' :"+devices[i].name + "(" + devices[i].description+")");
System.out.println(" data link:"+devices[i].datalink_name + "("
+ devices[i].datalink_description+")");
System.out.print(" MAC address:");
for (byte b : devices[i].mac_address)
System.out.print(Integer.toHexString(b&0xff) + ":");
System.out.println();
for (NetworkInterfaceAddress a : devices[i].addresses)
System.out.println(" address:"+a.address + " " + a.subnet + " "
+ a.broadcast);
}
}else{
JpcapCaptor jpcap = JpcapCaptor.openDevice(devices[0], 1024 * 10 , false, 20);
jpcap.setFilter("host 172.29.31.211", true);
jp = JpcapSender.openDevice(devices[0]);

jpcap.loopPacket(-1, new Tcpdump());

}
}
}

这段代码我整了好久的,主要是分析抓到的包里面的data是什么东西,因为都是16进制的,所以,在网上查了好多资料,才弄明白对应的都是什么意思。这是我用txt文档记录的一些日志。

[i]客户端收到从主机发来的包: f7 32 10 00 03 00 00 00 01 00 00 00 0a 00 00 00 (我建主了)


客户端向主机发送的搜索包: F7 2F 10 00 50 58 33 57 15 00 00 00 00 00 00 00 (我看看你建的什么主)


Dota /6.74c 版本的地图包:

f7 30 9e 00 50 58 33 57 0-7
18 00 00 00 02 00 00 00 8-15
e2 1c fd 0d 74 65 73 74 16-23
20 70 6c 61 79 20 64 6f 24-31
74 61 20 62 79 20 6c 69 32-39
79 61 68 75 69 2e 8f 20 40-47
28 77 77 00 00 01 03 49 48-55
07 01 01 77 01 91 79 01 56-63
e3 f1 8f 87 4d cb 61 71 64-71
73 5d 45 6f 77 19 6f 6d 72-79
6f 61 65 5d 45 0b 6f 75 80-87
41 21 77 37 2f 6b 37 35 88-95
63 2f 77 33 79 9d 01 77 96-103
77 77 63 63 73 f1 01 01 104-111
ff 19 e7 f1 71 73 d1 e5 112-119
07 8b 97 e1 27 07 b3 c7 120-127
1f 1f 89 9d a5 01 f1 00 128-135
0a 00 00 00 01 00 00 00 136-143
01 00 00 00 0a 00 00 00 144-151
63 00 00 00 e0 17 152-157


地图包的byte类型 :
-9, 48, -98, 0, 80, 88, 51, 87, 24, 0, 0-9
0, 0, 1, 0, 0, 0, 114, -27, 101, 11, 10-19
116, 101, 115, 116, 32, 112, 108, 97, 121, 32, 20-29 // 从第二十个开始,32个 游戏信息,大部分是:“当地局域网内游戏(X……”
100, 111, 116, 97, 32, 98, 121, 32, 108, 105, 30-39
121, 97, 104, 117, 105, 46, -113, 32, 40, 119, 40-49
119, 0, 0, 1, 3, 73, 7, 1, 1, 119, 50-59
1, -111, 121, 1, -29, -15, -113, -121, 77, -53, 60-69 // 从69个开始,地图路径,大概32个
97, 113, 115, 93, 69, 111, 119, 25, 111, 109, 70-79
111, 97, 101, 93, 69, 11, 111, 117, 65, 33, 80-89
119, 55, 47, 107, 55, 53, 99, 47, 119, 51, 90-99
121, -99, 1, 119, 119, 119, 99, 99, 115, -15, 100-109
1, 1, -1, 25, -25, -15, 113, 115, -47, -27, 110-119
7, -117, -105, -31, 39, 7, -77, -57, 31, 31, 120-129
-119, -99, -91, 1, -15, 0, 10, 0, 0, 0, 130-139
1, 0, 0, 0, 1, 0, 0, 0, 10, 0, 140-149
0, 0, -71, 0, 0, 0, -32, 23 150-157[/i]


不过后来我才发现,有现成的东西去截取网络数据包,并能很好的分析它。CommView就是一款很好的IP包捕获,分析软件。我后来就都是用它来监测的,非常好用,非常方便。附件里有CommView的截图

报文截取到了,当时那个兴奋啊。。好了,有数据了我就天天分析那些16进制代码,发现有地图包,但是打开war.exe后,在局域网里怎么也看不到我用javaw.exe程序发送的地图包,经过网上的资料搜索后,发现原来是地图版本的问题,在报文头里面有一个字节是代表war版本的,如果对不上,当然就看不到,所以我就改,改成了正确的版本。


public static byte[] warSearch = { (byte) 0xf7, 0x2f, 0x10, 0x00, 0x50,
0x58, 0x33, 0x57, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };

这个0x18是1.24的版本,原来是0x14(是比较早的1.20的版本),改过来后,在魔兽局域网里代开一看,我了个擦,看到了发java建的主,也就是过javaw.exe发过来的数据包,那个兴奋劲哦。真过瘾。

好了,到了这一步,就碰到了最难,解决时间最长的问题。在局域网里点击那个java建的主之后,显示“无法加入游戏”。
我到网上查了资料,说是因为那个IP包里的IP是本机的IP,war收到后,相当于用本机的ip连本机的ip,所以当然不行。解决办法是,必须把那个src_ip也就是来源ip改掉,改成真正建主的ip。

经过一番努力,发现jpacp可以改,并且改成功了。
至于这个过程是比较辛苦,就不谈了,因为毕竟是一个人在干,什么都不明白,只能摸着石头过河,靠网上找资料,自己分析,实验,来检验网上的程序正确性。

我就直接贴代码吧。这是利用jpacp改IP包头的程序。经过测试,利用两台机器,一台机器建主,一台机器连主机,主机跑这个java程序,实验结果是可以连上。连上之后,我在公司控制主自己的兴奋,写了这篇文章。


package com.lyh.test;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Arrays;

import jpcap.JpcapCaptor;
import jpcap.JpcapSender;
import jpcap.NetworkInterface;
import jpcap.PacketReceiver;
import jpcap.packet.EthernetPacket;
import jpcap.packet.Packet;
import jpcap.packet.UDPPacket;

class TestWar4 implements PacketReceiver {

public static Packet packet ;

public static NetworkInterface[] devices = JpcapCaptor.getDeviceList();

public static JpcapSender jp;

static byte[] data;

static boolean t_flag = false;

public static byte[] warSearch = { (byte) 0xf7, 0x2f, 0x10, 0x00, 0x50,
0x58, 0x33, 0x57, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };

public static byte[] replaceDataHead = { 0x00, 0x0B, 0x2F, 0x76, (byte) 0x8D,
0x21, 0x00, 0x23, (byte) 0xAE, (byte) 0xB4, (byte) 0xE0,
(byte) 0xD3, 0x08, 0x00, 0x45, 0x00, 0x00, (byte) 0xBA, 0x5B, 0x57,
0x00, 0x00, 0x40, 0x11, (byte) 0x86, (byte) 0xF0, (byte) 0xAC,
0x1D, 0x1F, (byte) 0xE7, (byte) 0xAC, 0x1D, 0x1F, (byte) 0xCA,
0x17, (byte) 0xE1, 0x17, (byte) 0xE0, 0x00, (byte) 0xA6, 0x0C, 0x6F };

public static byte[] src_mac = { 0x00,0x23, (byte)0xAE, (byte)0xB4,(byte)0xE0, (byte)0xD3};

public static byte[] dst_mac = { 0x00, 0x0B, 0x2F, 0x76, (byte)0x8D, 0x21};

public static byte[] src_port = {0x17,(byte) 0xE1};

public static byte[] dst_port = {0x17,(byte) 0xE0};

public static int len = 42;


DatagramSocket server;

DatagramPacket p;

void init() throws Exception{
server = new DatagramSocket(6113);
p = new DatagramPacket(warSearch, 16, InetAddress.getByName("172.29.31.202"), 6112);
server.send(p);
data = new byte[1024];
p = new DatagramPacket(data, data.length);
server.receive(p);
System.out.println("data[]:" + Arrays.toString(data));
t_flag = true;
}

public void receivePacket(Packet packet) {

try {
if (packet.toString().endsWith("6112") && t_flag) {

this.packet = packet;
Thread.sleep(1000);
UDPPacket udp = (UDPPacket) packet;

//1.改以太网头
EthernetPacket ep = (EthernetPacket)udp.datalink;
ep.src_mac = dst_mac;
ep.dst_mac = src_mac;
//2.修改IP头,将发包地址变为建主的IP。

udp.length = 187;
udp.setIPv4Parameter(udp.priority, udp.d_flag, udp.t_flag,
udp.r_flag, udp.rsv_tos, udp.rsv_frag, udp.dont_frag,
udp.more_frag, 0, 23383, udp.hop_limit,
udp.protocol, InetAddress.getByName("172.29.31.202"),
InetAddress.getByName("172.29.31.211"));

byte[] t_data = new byte[168];
for (int i = 0; i < t_data.length; i++) {
t_data[i] = data[i];
}

//3.改udp头
udp.dst_port = 6112;
udp.src_port = 6113;

udp.data = t_data;
udp.datalink = ep;

jp.sendPacket(udp);
System.out.println("发送完毕!");
}
} catch (Exception e) {
e.printStackTrace();
}
}

public static void main(String[] args) throws Exception {
JpcapCaptor jpcap = JpcapCaptor.openDevice(devices[0], 1024 * 10 , false, 20);
jpcap.setFilter("host 172.29.31.211", true);
jp = JpcapSender.openDevice(devices[0]);
TestWar4 t = new TestWar4();
t.init();
jpcap.loopPacket(-1, t);

}
}




以上几个关键点都解决了,相信开发出一个java写的魔兽局域网搜索器不是问题。
在这里先做个总结吧,以后有时间了,再开发出一套完整的客户端来。

还有,以上内容都是在工作时间写,由于近来公司刚结束完一个项目,所以一直比较闲,也没学公司的资料,就一直在偷偷的搞这个。写的比较粗糙,以后慢慢再改,仅供对jpacp,udp的学习参考。