以不可检测的方式检查 WebSocket 帧
Inspecting WebSocket frames in an undetectable way
如何在 Chrome 扩展程序或 Firefox 附加组件中读取网页的 WebSocket 帧,并且页面无法检测到?
Inspect WebSockets frames from a Chrome Dev Tools extension提出了类似的问题,但是开发NPAPI插件不再有意义,因为它很快就会被删除。
拦截 WebSocket 数据很容易。只需在页面构造 WebSocket 之前执行以下脚本即可。此代码段对 WebSocket
构造函数进行猴子修补:创建新的 WebSocket 构造函数时,该代码段会订阅 message
事件,您可以从中对数据执行任何操作。
此代码段被设计为与本机代码无法区分,因此页面无法轻易检测到修改(但是,请参阅此 post 末尾的备注)。
(function() {
var OrigWebSocket = window.WebSocket;
var callWebSocket = OrigWebSocket.apply.bind(OrigWebSocket);
var wsAddListener = OrigWebSocket.prototype.addEventListener;
wsAddListener = wsAddListener.call.bind(wsAddListener);
window.WebSocket = function WebSocket(url, protocols) {
var ws;
if (!(this instanceof WebSocket)) {
// Called without 'new' (browsers will throw an error).
ws = callWebSocket(this, arguments);
} else if (arguments.length === 1) {
ws = new OrigWebSocket(url);
} else if (arguments.length >= 2) {
ws = new OrigWebSocket(url, protocols);
} else { // No arguments (browsers will throw an error)
ws = new OrigWebSocket();
}
wsAddListener(ws, 'message', function(event) {
// TODO: Do something with event.data (received data) if you wish.
});
return ws;
}.bind();
window.WebSocket.prototype = OrigWebSocket.prototype;
window.WebSocket.prototype.constructor = window.WebSocket;
var wsSend = OrigWebSocket.prototype.send;
wsSend = wsSend.apply.bind(wsSend);
OrigWebSocket.prototype.send = function(data) {
// TODO: Do something with the sent data if you wish.
return wsSend(this, arguments);
};
})();
在 Chrome 扩展中,代码段可以 运行 通过 content script with run_at:'document_start'
, see Insert code into the page context using a content script。
Firefox 也支持 content scripts,同样的逻辑适用(contentScriptWhen:'start'
)。
注意:前面的代码片段被设计为在页面其余部分之前执行时与本机代码没有区别。检测这些修改的唯一(不寻常且脆弱的)方法是:
将无效参数传递给 WebSocket 构造函数,捕获错误并检查依赖于实现(特定于浏览器)的堆栈跟踪。如果栈帧比平常多一个,那么构造函数可能被篡改了(从页面的角度来看)。
序列化构造函数。未修改的构造函数变为 function WebSocket() { [native code] }
,而修补后的构造函数看起来像 function () { [native code] }
(此问题仅存在于 Chrome 中;在 Firefox 中,序列化是相同的)。
序列化WebSocket.prototype.send
方法。由于该函数未绑定,对其进行序列化 (WebSocket.prototype.send.toString()
) 会揭示非本机实现。这可以通过覆盖 .send
的 .toString
方法来缓解,而页面又可以通过与 Function.prototype.toString
的严格比较检测到该方法。如果您不需要发送的数据,请不要覆盖 OrigWebSocket.prototype.send
.
Rob W 的方法有一个替代方法,可以完全屏蔽与页面的任何交互(for Chrome)
也就是说,你可以拿出一些重炮,然后使用chrome.debugger
。
请注意,使用它会阻止您为有问题的页面打开开发工具(或者,更准确地说,打开开发工具会使它停止工作,因为只有一个调试器客户端可以连接) . 这已得到改进,因为:可以附加多个调试器。
这是一个相当低级的API;您需要使用 debugger protocol yourself. Also, the corresponding events are not in the 1.1 documentation, you'll need to look at the development version.
构建查询
您应该能够接收类似的 WebSocket 事件并检查它们 payloadData
:
{"method":"Network.webSocketFrameSent","params":{"requestId":"3080.31","timestamp":18090.353684,"response":{"opcode":1,"mask":true,"payloadData":"Rock it with HTML5 WebSocket"}}}
{"method":"Network.webSocketFrameReceived","params":{"requestId":"3080.31","timestamp":18090.454617,"response":{"opcode":1,"mask":false,"payloadData":"Rock it with HTML5 WebSocket"}}}
This extension sample 应该提供一个起点。
事实上,这里是一个起点,假设 tabId
是您感兴趣的选项卡:
chrome.debugger.attach({tabId:tab.id}, "1.1", function() {
chrome.debugger.sendCommand({tabId:tabId}, "Network.enable");
chrome.debugger.onEvent.addListener(onEvent);
});
function onEvent(debuggeeId, message, params) {
if (tabId != debuggeeId.tabId)
return;
if (message == "Network.webSocketFrameSent") {
// do something with params.response.payloadData,
// it contains the data SENT
} else if (message == "Network.webSocketFrameReceived") {
// do something with params.response.payloadData,
// it contains the data RECEIVED
}
}
我已经测试了这种方法(链接示例修改如上)并且有效。
只是为了给@Xan 的回答添加一个例外(我没有足够的代表 post 对他的回答发表评论所以我在这里添加它因为我相信它可以为其他人节省一些时间) .
如果 WebSocket 连接是在通过 about:
、data:
和 blob:
方案加载的上下文中建立的,则该示例将不起作用。
相关错误见此处:Attach debugger to worker from chrome devtools extension
如何在 Chrome 扩展程序或 Firefox 附加组件中读取网页的 WebSocket 帧,并且页面无法检测到?
Inspect WebSockets frames from a Chrome Dev Tools extension提出了类似的问题,但是开发NPAPI插件不再有意义,因为它很快就会被删除。
拦截 WebSocket 数据很容易。只需在页面构造 WebSocket 之前执行以下脚本即可。此代码段对 WebSocket
构造函数进行猴子修补:创建新的 WebSocket 构造函数时,该代码段会订阅 message
事件,您可以从中对数据执行任何操作。
此代码段被设计为与本机代码无法区分,因此页面无法轻易检测到修改(但是,请参阅此 post 末尾的备注)。
(function() {
var OrigWebSocket = window.WebSocket;
var callWebSocket = OrigWebSocket.apply.bind(OrigWebSocket);
var wsAddListener = OrigWebSocket.prototype.addEventListener;
wsAddListener = wsAddListener.call.bind(wsAddListener);
window.WebSocket = function WebSocket(url, protocols) {
var ws;
if (!(this instanceof WebSocket)) {
// Called without 'new' (browsers will throw an error).
ws = callWebSocket(this, arguments);
} else if (arguments.length === 1) {
ws = new OrigWebSocket(url);
} else if (arguments.length >= 2) {
ws = new OrigWebSocket(url, protocols);
} else { // No arguments (browsers will throw an error)
ws = new OrigWebSocket();
}
wsAddListener(ws, 'message', function(event) {
// TODO: Do something with event.data (received data) if you wish.
});
return ws;
}.bind();
window.WebSocket.prototype = OrigWebSocket.prototype;
window.WebSocket.prototype.constructor = window.WebSocket;
var wsSend = OrigWebSocket.prototype.send;
wsSend = wsSend.apply.bind(wsSend);
OrigWebSocket.prototype.send = function(data) {
// TODO: Do something with the sent data if you wish.
return wsSend(this, arguments);
};
})();
在 Chrome 扩展中,代码段可以 运行 通过 content script with run_at:'document_start'
, see Insert code into the page context using a content script。
Firefox 也支持 content scripts,同样的逻辑适用(contentScriptWhen:'start'
)。
注意:前面的代码片段被设计为在页面其余部分之前执行时与本机代码没有区别。检测这些修改的唯一(不寻常且脆弱的)方法是:
将无效参数传递给 WebSocket 构造函数,捕获错误并检查依赖于实现(特定于浏览器)的堆栈跟踪。如果栈帧比平常多一个,那么构造函数可能被篡改了(从页面的角度来看)。
序列化构造函数。未修改的构造函数变为
function WebSocket() { [native code] }
,而修补后的构造函数看起来像function () { [native code] }
(此问题仅存在于 Chrome 中;在 Firefox 中,序列化是相同的)。序列化
WebSocket.prototype.send
方法。由于该函数未绑定,对其进行序列化 (WebSocket.prototype.send.toString()
) 会揭示非本机实现。这可以通过覆盖.send
的.toString
方法来缓解,而页面又可以通过与Function.prototype.toString
的严格比较检测到该方法。如果您不需要发送的数据,请不要覆盖OrigWebSocket.prototype.send
.
Rob W 的方法有一个替代方法,可以完全屏蔽与页面的任何交互(for Chrome)
也就是说,你可以拿出一些重炮,然后使用chrome.debugger
。
请注意,使用它会阻止您为有问题的页面打开开发工具(或者,更准确地说,打开开发工具会使它停止工作,因为只有一个调试器客户端可以连接) . 这已得到改进,因为:可以附加多个调试器。
这是一个相当低级的API;您需要使用 debugger protocol yourself. Also, the corresponding events are not in the 1.1 documentation, you'll need to look at the development version.
构建查询您应该能够接收类似的 WebSocket 事件并检查它们 payloadData
:
{"method":"Network.webSocketFrameSent","params":{"requestId":"3080.31","timestamp":18090.353684,"response":{"opcode":1,"mask":true,"payloadData":"Rock it with HTML5 WebSocket"}}}
{"method":"Network.webSocketFrameReceived","params":{"requestId":"3080.31","timestamp":18090.454617,"response":{"opcode":1,"mask":false,"payloadData":"Rock it with HTML5 WebSocket"}}}
This extension sample 应该提供一个起点。
事实上,这里是一个起点,假设 tabId
是您感兴趣的选项卡:
chrome.debugger.attach({tabId:tab.id}, "1.1", function() {
chrome.debugger.sendCommand({tabId:tabId}, "Network.enable");
chrome.debugger.onEvent.addListener(onEvent);
});
function onEvent(debuggeeId, message, params) {
if (tabId != debuggeeId.tabId)
return;
if (message == "Network.webSocketFrameSent") {
// do something with params.response.payloadData,
// it contains the data SENT
} else if (message == "Network.webSocketFrameReceived") {
// do something with params.response.payloadData,
// it contains the data RECEIVED
}
}
我已经测试了这种方法(链接示例修改如上)并且有效。
只是为了给@Xan 的回答添加一个例外(我没有足够的代表 post 对他的回答发表评论所以我在这里添加它因为我相信它可以为其他人节省一些时间) .
如果 WebSocket 连接是在通过 about:
、data:
和 blob:
方案加载的上下文中建立的,则该示例将不起作用。
相关错误见此处:Attach debugger to worker from chrome devtools extension