【问题标题】:Node.js - Decoded WinPcap websocket frame is cut offNode.js - 解码的 WinPcap websocket 框架被切断
【发布时间】:2016-08-28 19:35:22
【问题描述】:

我正在尝试在 Windows 7 上使用带有 Node.js (4.5.0) 的 cap 模块来监听 websocket 流量。我能够解析/解码正在捕获的有效负载,但其中一部分已被剪切当有效载荷超过一定长度时关闭。

我正在使用this example 解码 websocket 帧(wsDecoded 函数),我认为问题是我从 cap 模块获得的响应是​​在单个缓冲区中返回整个响应,但是这个示例(以及我看过的其他一些 websocket 库)似乎希望在有效负载超过一定大小时处理多个帧。

我尝试从缓冲区中取出缓冲区并将其分成更小的部分,但是当我这样做时,有效负载看起来就像随机垃圾。

var Cap = require('cap').Cap;
var decoders = require('cap').decoders;
var PROTOCOL = decoders.PROTOCOL;

var c = new Cap();
var device = Cap.findDevice(LOCAL_IP);
var filter = 'port 8088';
var bufSize = 10 * 1024 * 1024;
var buffer = new Buffer(65535);

var linkType = c.open(device, filter, bufSize, buffer);

c.setMinBytes && c.setMinBytes(0);

c.on('packet', function(nbytes, trunc) {
  if (linkType === 'ETHERNET') {
    var ret = decoders.Ethernet(buffer);

    if (ret.info.type === PROTOCOL.ETHERNET.IPV4) {
      ret = decoders.IPV4(buffer, ret.offset);

      if (ret.info.protocol === PROTOCOL.IP.TCP) {
        var datalen = ret.info.totallen - ret.hdrlen;
        ret = decoders.TCP(buffer, ret.offset);
        datalen -= ret.hdrlen;

        var payload = wsDecoded(buffer, ret.offset, ret.offset + datalen);
        console.log(payload.toString());
      }
    }
  }
});

function wsDecoded (data, start, end) {
  var message = data.slice(start, end);
  var FIN = (message[0] & 0x80);
  var RSV1 = (message[0] & 0x40);
  var RSV2 = (message[0] & 0x20);
  var RSV3 = (message[0] & 0x10);
  var Opcode = message[0] & 0x0F;
  var mask = (message[1] & 0x80);
  var length = (message[1] & 0x7F);

  var nextByte = 2;
  if (length === 126) {
    // length = next 2 bytes
    nextByte += 2;
  } else if (length === 127){
    // length = next 8 bytes
    nextByte += 8;
  }

  var maskingKey = null;
  if (mask){
    maskingKey = message.slice(nextByte, nextByte + 4);
    nextByte += 4;
  }

  var payload = message.slice(nextByte, nextByte + length);

  if (maskingKey){
    for (var i = 0; i < payload.length; i++){
     payload[i] = payload[i] ^ maskingKey[i % 4];
    }
  }

  return payload;
}

未被截断的示例结果:

{"Command":"FoldCards","Table":"Ring Game #02","Type":"R","Ghost":"No"}

被截断的示例结果:

{"Command":"Buttons","Table":"Ring Game   #02","Type":"R","Button1":"","Button2":"Ready","Button3":"","Preflop":"No","Call":0,"M

使用 buffer.toString('binary', ret.offset, datalen) 时来自 on('packet') 回调的有效负载。这与我需要的非常接近,但它不会取消对有效负载的屏蔽并留下一些随机字符(我认为这是帧的开始和停止,但不是 100% 确定)

?'{"Command":"TablesSitting","Tables":[]}?'{"Command":"TablesWaiting","Tables":[]}?~☺({"Command":"RingGameLobby","Clear":"Yes","Count":2,"ID":["Ring Game #01","Ring Game #02"],"Game":["NL Hold'em","NL Hold'em"],"GameIndex":[2,2],"Seats":[10,10],"StakesLo":[10,10],"StakesHi":[20,20],"BuyinMin":[400,400],"BuyinMax":[2000,2000],"Players":[0,0],"Waiting":[0,0],"Password":["No","No"]}?~☺{"Command":"TournamentLobby","Clear":"Yes","Count":0,"ID":[],"SnG":[],"Shootout":[],"Game":[],"GameIndex":[],"Buyin":[],"EntryFee":[],"Rebuy":[],"TS":[],"PreReg":[],"Reg":[],"Max":[],"Starts":[],"StartMin":[],"StartTime":[],"Running":[],"Tables":[],"Password":[]}?~ ?{"Command":"Logins","Clear":"Yes","Total":1,"Player":["harrythree"],"Name":[""],"Location":["Greenville"],"Login":["2016-08-27 13:26:45"]}

【问题讨论】:

  • 能否提供“一定尺寸”的值?也许使用了碎片:tools.ietf.org/html/rfc6455#page-33
  • 另外,您能否将有效负载数据的长度与整个数据缓冲区的大小进行比较?也许 websocket 帧不是作为单个 tcp 数据包发送的,而是多个。如果这是真的,你必须等待另一个 tcp 数据包,加入缓冲区,然后读取 websocket 帧
  • 在上面进行编辑以显示完整的响应负载
  • 此外,通过 wsDecoded 函数传递的任何内容都会返回最多 128 个字符

标签: node.js websocket pcap winpcap


【解决方案1】:

您收到的消息被截断,因为您没有正确读取有效载荷长度。 看看:

var length = (message[1] & 0x7F);

var nextByte = 2;
if (length === 126) {
    // in this case next 2 bytes store lengths. you have to read ones from the buffer
    nextByte += 2;
} else if (length === 127){
    // in this case next 8 bytes store lengths. you have to read ones from the buffer
    nextByte += 8;
}

当消息大小超过 125 字节时,您必须像 RFC 所说的那样使用接下来的 2 或 8 个字节来读取正确的有效载荷长度。在您的情况下,您只需跳过这些字节,以便您的“长度”始终为

var payload = message.slice(nextByte, nextByte + length);
                                                   ^
                                              always <= 127

我稍微修改了您的代码以调试问题。这是我正在谈论的实现示例:

var Cap = require('cap').Cap;
var decoders = require('cap').decoders;
var PROTOCOL = decoders.PROTOCOL;

var c = new Cap();
var device = Cap.findDevice('127.0.0.1');
var filter = 'port 3001';
var bufSize = 10 * 1024 * 1024;
var buffer = new Buffer(bufSize);

var linkType = c.open(device, filter, bufSize, buffer);

c.setMinBytes && c.setMinBytes(0);

c.on('packet', function(nbytes, trunc) {

    console.log('************ packet: length ' + nbytes + ' bytes, truncated? '
        + (trunc ? 'yes' : 'no'));

    if (linkType === 'ETHERNET') {
        var ret = decoders.Ethernet(buffer);

        if (ret.info.type === PROTOCOL.ETHERNET.IPV4) {
            ret = decoders.IPV4(buffer, ret.offset);

            if (ret.info.protocol === PROTOCOL.IP.TCP) {
                var datalen = ret.info.totallen - ret.hdrlen;
                ret = decoders.TCP(buffer, ret.offset);
                datalen -= ret.hdrlen;

                console.log('from: ' + ret.info.srcport + ' to ' + ret.info.dstport);

                console.log("Data len: " + datalen);

                var payload = wsDecoded(buffer, ret.offset, ret.offset + datalen);
                if (payload) {
                    console.log("MSG:" + payload.toString());
                } else {
                    console.log("Invalid message");
                }

            }
        }
    }
});

function wsDecoded (data, start, end) {
    var message = data.slice(start, end);
    var index = 0;
    var FIN = (message[index] & 0x80);
    var RSV1 = (message[index] & 0x40);
    var RSV2 = (message[index] & 0x20);
    var RSV3 = (message[index] & 0x10);
    var Opcode = message[index] & 0x0F;

    index++;

    var masked = (message[index] & 0x80);
    var payloadLength = message[index] & (~0x80);
    index++;


    if (payloadLength === 126){
        // length = next 2 bytes
        payloadLength = message.readUInt16BE(index);
        index+=2;
    } else if (payloadLength === 127) {
        // read uint64
        throw new Error("Implement me");
    }

    console.log("Opcode: " + Opcode + ", fin: " + FIN + ", masked: " + masked);

    var maskingKey = null;
    if (masked){
        maskingKey = message.slice(index, index + 4);
        index += 4;
    }

    console.log("Payload length: " + payloadLength);
    var payload = message.slice(index, index + payloadLength);

    if (payload.length != payloadLength) {
        console.warn("Length mismatch");
        return false;
    }

    if (maskingKey){
        for (var i = 0; i < payload.length; i++){
            payload[i] = payload[i] ^ maskingKey[i % 4];
        }
    }

    return payload;
}

【讨论】:

  • 这很有效,而且解释很到位!感谢您的回答!
猜你喜欢
  • 2012-02-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-19
  • 2019-03-07
相关资源
最近更新 更多