JS逆向快速定位关键点之通用hook脚本
大部分网站都会对关键参数进行加密,JS 逆向时,我们首要任务是定位参数具体的加密逻辑。
常见方式包含:关键字搜索、堆栈调试、XHR 及事件监听、AST 内存漫游、JS Hook 注入等
本篇文章以 JS Hook 注入
为切入点,在做JS逆向往往需要定位到一些关键参数位置去分析,比如Cookie、Sign、Token、s等关键参数,这时候就需要借助到JS Hook快速定位。
1. cookie 通用hook
Cookie Hook 用于定位 Cookie 中关键参数生成位置,以下代码演示了当 Cookie 中匹配到了 v 关键字, 则插入断点
(function () {
var cookieTemp = '';
Object.defineProperty(document, 'cookie', {
set: function (val) {
if (val.indexOf('v') != -1) {
debugger
}
console.log('Hook捕获到cookie设置->', val);
cookieTemp = val;
return val;
},
get: function () {
return cookieTemp;
},
});
})();
2. header 参数通用hook
(function () {
// 头部参数 请求对象当中的 设为请求头部参数
var org = window.XMLHttpRequest.prototype.setRequestHeader;
window.XMLHttpRequest.prototype.setRequestHeader = function (key, value) {
// 关键字 在请求当中发现有键是Authorization 断点
if (key == 'Authorization') {
debugger;
}
return org.apply(this, arguments);
}
})();
3. hook过debugger
(()=>{
Function.prototype.__constructor = Function;
Function=function(){
if ( arguments && typeof arguments[0] === 'string' ){
if ("debugger" === arguments[0]){
return
}
}
return Function.apply(this.arguments[0]);
}
})()
// 1. 记录一下以前的Function.prototype.constructor
var xxxx = Function.prototype.constructor;
// 2. 给Function.prototype.constructor设置一个新的功能
Function.prototype.constructor = function(){
// 3. 判断参数是否是debugger
if (arguments[0] === 'debugger'){
return; // 4 如果是debugger. 就不构建函数
} else {
// 5.如果不是debugger. 需要放行.继续执行原来本应该的操作
return xxxx.apply(this, arguments); // 这里不能用call
}
}
// 先保留原 constructor
// 1. 比如判断是否有该debugger词语,替换为同等长度的空格(避免判断长度)
Function.prototype.constructor_ = Function.prototype.constructor;
Function.prototype.constructor = function (a) {
// 如果参数为 debugger,就返回空方法
if(a == "debugger") {
return function (){};
}
// 如果参数不为 debugger,还是返回原方法
return Function.prototype.constructor_(a);
};
2. 如果是定时器的debugger采用以下语句
// 先保留原定时器
var setInterval_ = setInterval
setInterval = function (func, time){
// 如果时间参数为 0x7d0,就返回空方法
// 当然也可以不判断,直接返回空,有很多种写法
if(time == 0x7d0)
{
return function () {};
}
// 如果时间参数不为 0x7d0,还是返回原方法
return setInterval_(func, time)
}
// eval("debugger;");
4. hook URL
URL Hook 用于定位请求 URL 中关键参数生成位置,以下代码演示了当请求的 URL 里包含 login 关键字时,则插入断点:
(function () {
var open = window.XMLHttpRequest.prototype.open;
window.XMLHttpRequest.prototype.open = function (method, url, async) {
if (url.indexOf("login") != -1) {
debugger;
}
return open.apply(this, arguments);
};
})();
5. hook JSON.stringify
JSON.stringify() 方法用于将 JavaScript 值转换为 JSON 字符串,在某些站点的加密过程中可能会遇到,以下代码演示了遇到 JSON.stringify() 时,则插入断点:
(function() {
var stringify = JSON.stringify;
JSON.stringify = function(params) {
console.log("Hook JSON.stringify ——> ", params);
debugger;
return stringify(params);
}
})();
6. hook JSON.parse
JSON.parse() 方法用于将一个 JSON 字符串转换为对象,在某些站点的加密过程中可能会遇到,以下代码演示了遇到 JSON.parse() 时,则插入断点:
(function() {
var parse = JSON.parse;
JSON.parse = function(params) {
console.log("Hook JSON.parse ——> ", params);
debugger;
return parse(params);
}
})();
7. hook eval
JavaScript eval() 函数的作用是计算 JavaScript 字符串,并把它作为脚本代码来执行。如果参数是一个表达式,eval() 函数将执行表达式。如果参数是 Javascript 语句,eval() 将执行 Javascript 语句,经常被用来动态执行 JS。以下代码执行后,之后所有的 eval() 操作都会在控制台打印输出将要执行的 JS 源码:
(function() {
// 保存原始方法
window.__cr_eval = window.eval;
// 重写 eval
var myeval = function(src) {
console.log(src);
console.log("=============== eval end ===============");
debugger;
return window.__cr_eval(src);
}
// 屏蔽 JS 中对原生函数 native 属性的检测
var _myeval = myeval.bind(null);
_myeval.toString = window.__cr_eval.toString;
Object.defineProperty(window, 'eval', {
value: _myeval
});
})();
8. hook Function
以下代码执行后,所有的函数操作都会在控制台打印输出将要执行的 JS 源码:
(function() {
// 保存原始方法
window.__cr_fun = window.Function;
// 重写 function
var myfun = function() {
var args = Array.prototype.slice.call(arguments, 0, -1).join(","),
src = arguments[arguments.length - 1];
console.log(src);
console.log("=============== Function end ===============");
debugger;
return window.__cr_fun.apply(this, arguments);
}
// 屏蔽js中对原生函数native属性的检测
myfun.toString = function() {
return window.__cr_fun + ""
}
Object.defineProperty(window, 'Function', {
value: myfun
});
})();
9. 通用反调试
// ==UserScript==
// @name debugger
// @namespace http://tampermonkey.net/
// @version 1
// @description Stops most anti debugging implementations by JavaScript obfuscaters
// @author ss
// @match *
// @grant unsafeWindow
// @run-at document-start
// ==/UserScript==
(function() {
var _constructor = unsafeWindow.Function.prototype.constructor;
// Hook Function.prototype.constructor
unsafeWindow.Function.prototype.constructor = function() {
var fnContent = arguments[0];
if (fnContent) {
if (fnContent.includes('debugger')) { // An anti-debugger is attempting to stop debugging
var caller = Function.prototype.constructor.caller; // Non-standard hack to get the function caller
var callerContent = caller.toString();
if (callerContent.includes(/\bdebugger\b/gi)) { // Eliminate all debugger statements from the caller, if any
callerContent = callerContent.replace(/\bdebugger\b/gi, ''); // Remove all debugger expressions
eval('caller = ' + callerContent); // Replace the function
}
return (function () {});
}
}
// Execute the normal function constructor if nothing unusual is going on
return _constructor.apply(this, arguments);
};
})();