上周跟大多数游戏玩家一样,我购买了期待已久的辐射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抓取数据

relay功能建立另一个辐射4移动应用服务进行连接,实际上数据包是发送到合法的那一个。

你可以使用类似socat的工具来完成这一步,但是我选择使用Node.js来完成这个工作。为什么这么选择在之后你应该会了解的。

UDP relay

对于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

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你可以使用这个工具转储数据。

pip boy设备relay

如果你满足以下条件你可以直接运行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中看到坐标滚动。

STAT转储

在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,编译/ 鸢尾,转载请注明来自FreeBuf黑客与极客(FreeBuf.COM)

源链接

Hacking more

...