上周跟大多数游戏玩家一样,我购买了期待已久的辐射4。与其早前版本一样,辐射4也配备有一个pip boy设备,这设备十分炫酷,今天我们就来好好玩玩吧!
这次的开发者Bethesda同时还给我们带来了一款兄弟App用来远程访问咱们的Pip Boy设备。你可以浏览地图,查看库存,修改武器以及快速旅行!
在发现我正在运行的PS4之后,我脑子立即想到:
“嘿,它是如何发现我网络中的游戏的呢?”
首先通过nmap扫描网络找到我的PS4以及正在监听的服务:
$ nmap 192.168.1.* Nmap scan report for unknown0CFE45340EE1 (192.168.1.71) Starting Nmap 6.47 ( http://nmap.org ) at 2015-11-19 22:20 CST ... Host is up (0.00056s latency). Not shown: 999 closed ports PORT STATE SERVICE 27000/tcp open flexlm0 ...
有了这些,我可以辨别在端口27000运行有一个TCP服务:
$ curl 192.168.1.71:27000 #{"lang":"en","version":"1.1.21.0"}
哇!HTTP正在工作,可悲的是我们不知道REST端点。
然而pip boy应用不可能知道PS4的IP地址,所以它肯定开启了一个服务允许其他设备发现它。兴奋于预期的脱离App获取数据,从我的同事 Jon Frederic 捕获到的一些数据包中看来这个发现协议和TCPdump很相像。
在移动App上运行扫描控制台,就这会足以看到一个UDP payload从移动手机发送到广播地址(255.255.255.255),端口28000:
{ "cmd" : "autodiscover" }
服务器响应:
{ "IsBusy" : false, "MachineType" : "PS4" }
如果你有安装socat,你可以直接这么搞:
echo '{"cmd":"autodiscover"}' | / socat - UDP-DATAGRAM:255.255.255.255:28000,broadcast
至今我们得知辐射4服务至少有2个监听服务:28000端口上的UDP,27000端口上的TCP。由于对这个协议不太了解,所以我们还需要更多的研究研究从pip boy到控制台的正常通信。我们可以继续嗅探通信,通过ARP投毒进行中间人攻击,或者我们可以写一个Relay
relay功能建立另一个辐射4移动应用服务进行连接,实际上数据包是发送到合法的那一个。
你可以使用类似socat的工具来完成这一步,但是我选择使用Node.js来完成这个工作。为什么这么选择在之后你应该会了解的。
对于relay我们还得在真是客户端(pip boy app)和辐射4服务器之间写一个函数作为媒介。每次客户端连接到relay,我们都会创建一个假的客户端来连接到真实的辐射4服务器。客户端与服务端通信,在它们传递消息的同时复制这些消息。
首先我们得打开服务端socket:
var upstreamInfo = { address: YOUR_CONSOLE_OR_PC_IP port: 28000 // The Fallout 4 UDP Port } var server = dgram.createSocket('udp4') server.bind(upstreamInfo.port, '0.0.0.0')
对于每条接收到消息,我们都要设置另外一个socket作为假客户端。接着在真实服务端和pip boy客户端直接转发消息。
server.on('message', function (message, clientInfo) { var fakeClient = dgram.createSocket('udp4') // fakeClient.on('message', ...) // Defined below // fakeClient.bind(...) })
上面注释的两部分,下面写的更详细。我们每次获得消息都会复制,注意遥测数据,将消息发送给真实客户端并提供复制缓冲和遥测的callback
fakeClient.on('message', function (message, serverInfo) { var copiedBuffer = new Buffer(message.length) message.copy(copiedBuffer) // Now emulate our server server.send(message, 0, message.length, clientInfo.port, clientInfo.address) var telemetry = { 'src': serverInfo, 'dst': clientInfo } cb(copiedBuffer, telemetry) })
绑定客户端,同样复制消息,将消息发送给服务器并将数据传给callback
// As soon as our client is ready, go ahead and send their message onward fakeClient.bind(undefined, undefined, function () { var copiedBuffer = new Buffer(message.length) message.copy(copiedBuffer) fakeClient.send(message, 0, message.length, upstreamInfo.port, upstreamInfo.address) var telemetry = { 'src': clientInfo, 'dst': upstreamInfo } cb(copiedBuffer, telemetry) })
TCP relay的设置类似,我们将创建一个TCPRelay,其下还有一个net.Server。最大的不同是我们得维持所有的正在使用的假客户端。
/** * Create a TCP relay for an upstream server * @constructor */ /** * Listen for traffic to relay to/from an upstream server * @param {Object} upstreamInfo * @param {string} upstreamInfo.address * @param {number} upstreamInfo.port * @param {relayCallback} - callback that handles new data */ var TCPRelay = function TCPRelay () { this.server = net.createServer({'allowHalfOpen': true}) }
提供TCPRelay.listen来模拟net.Server.listen
TCPRelay.prototype.listen = function listen (upstreamInfo, cb) { this.server.on('connection', function (client) { // Now we create our fake client var fakeClient = new net.Socket() fakeClient.connect(upstreamInfo.port, upstreamInfo.address) // Get the actual client info for logging where the traffic really came from var actualClientInfo = {} actualClientInfo.address = client.remoteAddress actualClientInfo.port = client.remotePort actualClientInfo.family = client.remoteFamily // Register handlers for the fakeClient // These each get explained below but should be inserted here // fakeClient.on('connect', function() { ... }) // fakeClient.on('data', function() { ... }) // fakeClient.on('close', function() { ... }) // fakeClient.on('end', function() { ... }) }) this.server.listen({'port': upstreamInfo.port}) }
当连接到假客户端,我们准备记录来自客户端的数据。从客户端向假客户端发送消息,这些数据也会发送给辐射4服务器
fakeClient.on('connect', function () { // Once we're connected, we can get each message from the client client.on('data', function (message) { var copiedBuffer = new Buffer(message.length) message.copy(copiedBuffer) // To the server fakeClient.write(message) var serverInfo = {} serverInfo.address = fakeClient.remoteAddress serverInfo.port = fakeClient.remotePort serverInfo.family = fakeClient.remoteFamily var telemetry = { 'src': actualClientInfo, 'dst': serverInfo } cb(copiedBuffer, telemetry) }) })
每次假客户端接收到的数据,都来自辐射4服务器并且会将其转发给真实客户端
fakeClient.on('data', function (message) { var copiedBuffer = new Buffer(message.length) message.copy(copiedBuffer) var serverInfo = {} serverInfo.address = fakeClient.remoteAddress serverInfo.port = fakeClient.remotePort serverInfo.family = fakeClient.remoteFamily var telemetry = { 'src': serverInfo, 'dst': actualClientInfo } client.write(message) cb(copiedBuffer, telemetry) })
另一面我们也需要处理关闭假客户端和真实客户端。记住我们需要直接进行镜像操作,同样的我们也需要关闭客户端。
fakeClient.on('close', function (hadError) { if (hadError) { console.log('closure error') } client.close() }) fakeClient.on('end', function () { client.end() })
现在我们有一个TCPRelay和一个UDPRelay,可以自动发现一个运行的服务,当然也可以做一个小工具来帮助完成任务
我已经将这两个relays打包
pipboylib
pipboyrelay
第二个pipboyrelay是一个CLI工具,其依赖于pipboylib你可以使用这个工具转储数据。
如果你满足以下条件你可以直接运行relay
1.PC或者PS4版本的辐射4(Xbox版本还未确认) 2.Android或者iOS版本的pip boy app 3.在辐射4中启用了in-game选项 4.Node.js及npm
Node.js和npm下,安装pipboyrelay就像安装CLI工具:
npm install -g pipboyrelay
使用以下命令行运行pipboyrelay:
$ pipboyrelay Discovered: { IsBusy: false, MachineType: 'PS4', info: { address: '192.168.1.71', family: 'IPv4', port: 28000, size: 50 } }
它会自动发现在本地网络中活动的游戏服务,并创建Android或者iOS app可识别的端点。打开移动App并导航到连接设置
连接到地址,你应该能够看到这些数据飘过:
UDP and TCP Relay created for: { address: '192.168.1.71', family: 'IPv4', port: 28000, size: 50 } listening [TCP Relay] 192.168.1.71:27000 -> ::ffff:192.168.1.67:55232 00000000: 2300 0000 017b 226c 616e 6722 3a22 656e #....{"lang":"en 00000010: 222c 2276 6572 7369 6f6e 223a 2231 2e31 ","version":"1.1 00000020: 2e32 312e 3022 7d0a .21.0"}. [TCP Relay] 192.168.1.71:27000 -> ::ffff:192.168.1.67:55232 00000000: 754e 0600 0306 5290 0f00 2447 656e 6572 uN....R...$Gener 00000010: 616c 0003 6290 0f00 1e00 0000 0661 900f al..b........a.. 00000020: 004c 6f63 6174 696f 6e73 2044 6973 636f .Locations.Disco ... [TCP Relay] 192.168.1.71:27000 -> ::ffff:192.168.1.67:55232 00000000: 7565 002d 910f 0074 6578 7400 2f91 0f00 ue.-...text./... 00000010: 7368 6f77 4966 5a65 726f 0000 0003 3291 showIfZero....2. 00000020: 0f00 0000 0000 0631 910f 0046 6974 7320 .......1...Fits. ...
注意观察0×18 (0x1e),"Locations Discovered"发生之前:
[TCP Relay] 192.168.1.71:27000 -> ::ffff:192.168.1.67:55232 00000000: 754e 0600 0306 5290 0f00 2447 656e 6572 uN....R...$Gener 00000010: 616c 0003 6290 0f00 1e00 0000 0661 900f al..b........a.. ------------------------------^^ 00000020: 004c 6f63 6174 696f 6e73 2044 6973 636f .Locations.Disco
0x1e是我使用的locations discovered当前数量,30
看起来辐射4格式是一个二进制格式并且是一个纯文本,这是一个好消息,这样我们就更容易找到格式并创造新东西。
当你探索荒地时运行这个relay,你可以在relay中看到坐标滚动。
在OS X和Linux你可以使用nc(netcat)直接从PC或者控制台获取所有STAT
$ nc 192.168.1.71 27000 | xxd | head -n 16 00000000: 2300 0000 017b 226c 616e 6722 3a22 656e #....{"lang":"en 00000010: 222c 2276 6572 7369 6f6e 223a 2231 2e31 ","version":"1.1 00000020: 2e32 312e 3022 7d0a 59f0 0600 0306 81cc .21.0"}.Y....... 00000030: 1e00 2447 656e 6572 616c 0003 91cc 1e00 ..$General...... 00000040: 2200 0000 0690 cc1e 004c 6f63 6174 696f "........Locatio 00000050: 6e73 2044 6973 636f 7665 7265 6400 0092 ns Discovered... 00000060: cc1e 0001 088f cc1e 0003 0091 cc1e 0076 ...............v 00000070: 616c 7565 0090 cc1e 0074 6578 7400 92cc alue.....text... 00000080: 1e00 7368 6f77 4966 5a65 726f 0000 0003 ..showIfZero.... 00000090: 95cc 1e00 0400 0000 0694 cc1e 004c 6f63 .............Loc 000000a0: 6174 696f 6e73 2043 6c65 6172 6564 0000 ations Cleared.. 000000b0: 96cc 1e00 0108 93cc 1e00 0300 95cc 1e00 ................ 000000c0: 7661 6c75 6500 94cc 1e00 7465 7874 0096 value.....text.. 000000d0: cc1e 0073 686f 7749 665a 6572 6f00 0000 ...showIfZero... 000000e0: 0399 cc1e 000a 0000 0006 98cc 1e00 4461 ..............Da 000000f0: 7973 2050 6173 7365 6400 009a cc1e 0001 ys Passed....... ... more data ...
*参考来源: Getcarina ,编译/FB小编鸢尾,转载请注明来自FreeBuf黑客与极客(FreeBuf.COM)