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

  很久没写技术型博文了,最近一直在研究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文档记录的一些日志。

客户端收到从主机发来的包:  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的学习参考。

猜你喜欢

转载自liyahui2011.iteye.com/blog/1632971