js逆向hook
打开浏览器控制台,输入一段加密参数,你看到的是一串十六进制乱码。想要破解它,最直接的办法不是去读那几千行混淆代码,而是找到那个加密函数,在它执行前或者执行后,把参数拦下来。这就是hook——不是去解开锁芯,而是直接撬开门缝看里面怎么转的。
我做过一个案例。某个网站登录时,密码字段被前端用AES加密,密钥藏在某个自执行函数里。常规思路是断点调试,一步步跟栈。但那代码用了ob混淆,变量名全是_0x3f2a这种,跟了半小时,头都大了。后来换了思路,直接在window对象上挂了一个代理,监听所有函数的调用。结果不到五分钟,就看到了那个加密函数的入参——明文密码和密钥就在里面。你根本不需要理解整个加密流程,只需要知道它调用了哪个函数,参数是什么。
hook的本质是替换。把原来的函数存下来,然后用一个新的函数去覆盖它。新函数里,你可以打印参数、修改返回值、甚至直接跳过执行。比如那个JSON.stringify,很多网站用它来序列化数据,你把它hook了,就能看到所有经过它的对象。再比如document.cookie的setter,你hook了它,就能捕获cookie的写入时机和值。
但hook不是万能的。有些函数被Object.defineProperty定义成了不可配置,你直接赋值覆盖会报错。这时候得用Object.getOwnPropertyDescriptor先拿到原属性描述符,然后通过Object.defineProperty重新定义。还有更狠的,代码里检查了函数的toString输出,如果你覆盖的函数长得不像原函数,它就直接崩溃。对付这种,你得把原函数的toString也一起hook了,让它返回原来的字符串。
实际逆向中,我更喜欢用Chrome DevTools的Overrides功能,配合本地替换js文件。这样你可以直接在源码里加console.log,不用在控制台写一堆hook代码。但这种方式有个缺点——如果网站用了Service Worker缓存,或者js文件有完整性校验(比如SRI),本地替换就失效了。这时候还是得回到纯hook方案,在页面加载早期注入脚本。
说个细节。hook的时候,this指向很重要。有些函数内部用了this.xxx,如果你直接调用hook函数,this会指向window,导致报错。所以hook函数里必须用original.apply(this, arguments)来保持上下文。我见过不少新手在这里翻车,以为hook没生效,其实是this丢了。
还有一种情况,函数是闭包内部的,外面根本访问不到。比如某个加密函数定义在一个IIFE里,没有暴露到window上。这时候你得靠事件触发点来找它。比如点击登录按钮,会触发某个事件监听器,你可以在监听器里下断点,然后向上找调用栈。找到那个函数后,再用Function.prototype.toString拿到源码,分析它内部调用了哪些其他函数,然后逐个hook那些内部函数。
hook这东西,说白了就是“截胡”。你不需要成为密码学专家,也不需要完全读懂混淆代码。你只需要找到那个关键的函数,在它执行前或者执行后,插入你的逻辑。就像快递员送包裹,你不用拆开包裹看里面是什么,你只需要在包裹经过你手里的时候,偷偷拍个X光片。
最后提醒一句,hook代码写多了,页面性能会下降。每次函数调用都要经过你的代理层,如果那个函数被高频调用(比如requestAnimationFrame里的循环),页面会明显卡顿。所以hook完记得及时解除,别让它在生产环境里一直挂着。














