下面我将列出这些天攻克的技术难点。
第一。怎么抓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文档记录的一些日志。
客户端收到从主机发来的包: 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
不过后来我才发现,有现成的东西去截取网络数据包,并能很好的分析它。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的学习参考。