JS逆向之Hook基础

在咱所有操作里都是直接XHR下断或者搜索下断,但是那些存在Cookie中的值的时候,我们怎么去下断呢?有没什么快速的方法,让我们迅速找到一些,不那么方便寻找的参数设置位置,让我们快速下断呢?其实是有的,那就是Hook。

01

理解Hook

看到这篇文章前,你一定听说了很多次Hook这个词了,我们通过一段代码来了解一下吧。

咱这可以看到有一段小小的代码,有一个window对象和一个fun()方法。

我们在执行fun()方法的时候,会步入到function fun()的函数执行,我们这时候就会思考:

  1. 调用函数的时候,我们怎么获取它传入的参数。
  2. window.a的属性设值我们怎么去获取。

明确一下Hook的目的:

就是代替一步步调试获取过程、结果或者对其进行修改。

02

函数Hook

在Hook前,我们肯定是需要在执行我们需要对其操作代码位置之前下断点的,这个很好理解:如果我们代码已经执行完了,已经不会步过它了,那肯定是不会hook住的。

为了效果直观,只保留函数代码,如下。

01

打印传入参数

是不是非常简单的一个函数,就用于打印123,但是传了一个无用参数456,那我们要获取传入的参数并将其打印如何实现呢?

  1. 先将断点打在执行函数前。
  2. 点击执行。
  3. 断住后,在控制台输入代码(开始对Hook理解)。

我们可以看到只输入fun的时候可以直接看到fun的代码,fun()就是对代码的执行。

那我们只需要将fun方法重写并且不修改原方法,不就可以实现在原代码前、后加入我们自定义的代码了?

  1. 保存原方法代码(第一步)。

我们可以看到:自定义了一个fun1变量去存fun方法代码了,打印出来也是fun的代码,我们是调用fun(‘456’),就不能不保存原来的方法,不然替换了就会影响程序运行。

  1. 重写fun方法完成打印参数的操作(第二步)

用return保存下来的fun原代码执行,所以这个456输出结果肯定是在123前。

  1. 输出结果查看,这就是Hook在执行前加了段代码。

fun1 = fun; // 保存原函数
// 对函数方法重写
fun = function(val){
console.log(val); // 获取到传入的参数,并打印
return fun1(); // 执行原函数的方法
}

总结:对要执行的方法重写,加入我们需要加的方法,用return不影响原代码的执行逻辑。

02

修改函数代码

这个在反调中经常可以用到,简单举例一个吧。

不加断点执行的时候就会被断住,如果放一个循环那不是很难受嘛。

所以我们把它hook掉。

下断点,在执行前给他断住。

  1. 去控制台写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

(0)
联系我们
联系我们
分享本页
返回顶部