在咱所有操作里都是直接XHR下断或者搜索下断,但是那些存在Cookie中的值的时候,我们怎么去下断呢?有没什么快速的方法,让我们迅速找到一些,不那么方便寻找的参数设置位置,让我们快速下断呢?其实是有的,那就是Hook。
01
理解Hook
看到这篇文章前,你一定听说了很多次Hook这个词了,我们通过一段代码来了解一下吧。
咱这可以看到有一段小小的代码,有一个window对象和一个fun()方法。
我们在执行fun()方法的时候,会步入到function fun()的函数执行,我们这时候就会思考:
- 调用函数的时候,我们怎么获取它传入的参数。
- window.a的属性设值我们怎么去获取。
明确一下Hook的目的:
就是代替一步步调试获取过程、结果或者对其进行修改。
02
函数Hook
在Hook前,我们肯定是需要在执行我们需要对其操作代码位置之前下断点的,这个很好理解:如果我们代码已经执行完了,已经不会步过它了,那肯定是不会hook住的。
为了效果直观,只保留函数代码,如下。
01
打印传入参数
是不是非常简单的一个函数,就用于打印123,但是传了一个无用参数456,那我们要获取传入的参数并将其打印如何实现呢?
- 先将断点打在执行函数前。
- 点击执行。
- 断住后,在控制台输入代码(开始对Hook理解)。
我们可以看到只输入fun的时候可以直接看到fun的代码,fun()就是对代码的执行。
那我们只需要将fun方法重写并且不修改原方法,不就可以实现在原代码前、后加入我们自定义的代码了?
- 保存原方法代码(第一步)。
我们可以看到:自定义了一个fun1变量去存fun方法代码了,打印出来也是fun的代码,我们是调用fun(‘456’),就不能不保存原来的方法,不然替换了就会影响程序运行。
- 重写fun方法完成打印参数的操作(第二步)
用return保存下来的fun原代码执行,所以这个456输出结果肯定是在123前。
- 输出结果查看,这就是Hook在执行前加了段代码。
fun1 = fun; // 保存原函数
// 对函数方法重写
fun = function(val){
console.log(val); // 获取到传入的参数,并打印
return fun1(); // 执行原函数的方法
}
总结:对要执行的方法重写,加入我们需要加的方法,用return不影响原代码的执行逻辑。
02
修改函数代码
这个在反调中经常可以用到,简单举例一个吧。
不加断点执行的时候就会被断住,如果放一个循环那不是很难受嘛。
所以我们把它hook掉。
下断点,在执行前给他断住。
- 去控制台写hook代码,将代码转字符串类型再用eval自执行函数即可运行我们要的结果。
就不会被断住了,或者改成一个打印更直观一点。
fun1 = fun; // 获取原函数代码
// 重写函数方法
fun = function(val){
var func = ‘(‘ + fun1.toString().replace(‘debugger’, ‘console.log(\’修改了debugger\’)’) + ‘)()’; // eval执行需要是实名函数或者自执行,这里用自执行
return eval(func); // 用eval执行字符串函数
}
总结:善用eval()方法执行替换后的String类型的方法。
03
Eval Hook
eval()方法一般用于执行js字符串,用VM虚拟机来运行我们的代码,里面常用参数就是字符串类型的代码。
就像如此,一共有两个eval方法一个就是单纯用于debugger的,一个是用于打印是否运行的,我们执行一下会发现。
发现断住放行后打印了debugger。
考虑一下我们需求,为了过eval的一些特殊设置的代码,我们可以用几个小技巧去过。
先拿到eval的原方法,如果里面有debugger这样自定义的关键词key那就会被换成空函数,什么都不执行。
我们可以看到打印传进来的参数,就是我们写的需要执行的代码,是字符串类型的,所以我们无论是用search()搜索、indexOf()索引啥的只要能找到debugger就可以作为一个判断条件去过他。
eval_ = eval; // 保存原来的eval方法
eval = function(val){ // 重写eval函数
console.log(val, ‘传进来的参数’) // 获取传入的参数
if(val == ‘debugger’){ // 如果有debugger匹配上
return function(){}; //换成空函数
}
return eval_; // 没有debugger就正常执行
}
又或者用replace直接替换,这样还有一个console.log()打印效果,对代码的影响就比较小了。
eval_ = eval; // 保存原来eval方法
eval = function(val){ // 重写eval方法
val = val.replace(‘debugger’, ”); // 将参数替换掉debugger
return eval_(val); // 将替换完的参数运行
}
04
setInterval Hook
通过定时器循环的结果,如果反调debugger如何去过,如果结合eval又如何过?
比如一直往你浏览器缓存写入给你卡死,或者打开新标签页,或者就像这样一直打印。
就会严重影响扣代码或者调试。
那我们也就直接把定时器方法重写就好了。
断点打在执行前。
我们可以知道定时器有两个参数,所以设置两个形参,或者用arguments[0]|arguments[1]去接收也是一样,我们打印看一下结果。
没看到被引号引起来,我们可以直接转成string类型,然后索引或者replace()
这是单setInterval的函数重写,如果带上eval的话,那就先重写一下eval。
这样就把eval和setInterval都hook掉了,在使用时需要灵活应用,试用toString的原因是这些函数其实都是对象就是Object,Object有个toString方法自带的,原型上就是有的,就能用。
这个对象就是Object,JS万物皆对象嘛。
05
Cookie Hook
这个不一定非要是Cookie,但是在应用上cookie最为合适,这段是某奇艺登录的cookie一个key的Hook。
(function(){
‘use strict’;
var data = ”
Object.defineProperty(document, ‘cookie’, {
set: function(val){
if(val.search(‘__dfp’) != -1){
debugger;
console.log(‘__dfp’);
}
data = val;
return val;
},
get: function(){
return data;
}
});
})();
上面这个严格模式自执行,就是为了防止写入失败,我们直观地写一个对象属性来试一下。
你看结果都能打印出来。
我们再加一个要求:如果设置值为123的时候我们给他断住,就方便我们调试了。
这样就被我们断住了。
(function(){
‘use strict’;
var data = ”
Object.defineProperty(obj, ‘val’, {
set: function(val){
if(val == ‘123’){
debugger;
}
data = val;
console.log(‘hook设置值’, val)
return val;
},
get: function(){
console.log(‘hook获取值’, data)
return data;
}
});
})();
06
过Hook检测
为了防止被Hook,有些网站会有一个Hook检测 / 原型链检测,比如函数被改了,我们看一下一个方法的toString()。
01
函数检测
我们可以看到自定义函数的toString()是获取代码字符串,如果我们hook了再打印看看。
就发现a函数方法已经变了,那我们想要网站发现不了怎么办,比如这样。
我们开始Hook一下。
那怎么让他跑起来呢?
这样hook,就可以将原来的toString()方法结果伪造出来了,就过了hook检测。
01
原型链检测
比如这样一句简单的代码,输出结果是切割后的字符串。
如果我们加一个环境的检测呢?
我们可以看到a是一个String的实例对象,那我们直接去看看a.split是不是圆型对象的split,因为我们在a里面没有定义过。
结果是一致的,所以我们检测String.prototype.split就可以检测了。
为什么要Hook原型对象的方法呢?
因为a实例方法hook了没用。
那过检测也很方便,先存一份,再补上去就好了,也可以手动复制字符串。
这样就不会被检测到了,ahhh。
07
结束语
看了文后喜欢就点个关注塞,公众号里有交流群信息,大家一起学习进步~关于Hook如果后续有新增内容,会新增一些实例和知识点,今天的分享就到这里了哈。
声明:文中观点不代表本站立场。本文传送门:https://eyangzhen.com/218413.html