js hook 改进

js批量hook

参考hook

我们可以更改上次的脚本,用来遍历所有的函数来找出其中是否有以加密命名的函数,来尝试找到js中的加密函数,参考自http://bbs.nightteam.cn/thread-210.htm
我们用它的代码测试一下,我们自己的网站

(function() {
    'use strict';
    !function () {
        'use strict';
        var source = ['alert','decodeData','String.fromCharCode',
            'fromCharCode','base64decode','md5','decode','btoa','JSON.stringify',
            'MD5','RSA','AES','CryptoJS','encrypt',
            'strdecode',"encode",'decodeURIComponent','_t'];
        console.log("开始测试是否有解密函数");
        let realCtx, realName;
        function getRealCtx(ctx, funcName) {
            let parts = funcName.split(".");
            let realCtx = ctx;
            for(let i = 0; i < parts.length - 1; i++) {
                realCtx = realCtx[parts[i]];
            }
            return realCtx;
        }
        function getRealName(funcName) {
            let parts = funcName.split(".");
            return parts[parts.length - 1];
        }
        function test(ctx) {
            for(let i = 0; i < source.length; i++) {
                let f = source[i];
                let realCtx = getRealCtx(ctx, f);
                let realName = getRealName(f);
                let chars = realCtx[realName];
                if (chars != undefined){
                    console.log("发现可疑函数:", f);
                    console.log(chars);
                    console.log("---------------------");
                }else{
                    console.log("未发现:", f);
                }
            }
        }
        test(window);
    }();
})();

改进

但是这种方式无法找到局部变量或混淆之后的方法有一定的局限性,我们壳以根据这种思路进行一下小的改进,将系统底层的对象的所有函数都hook上,那么当任何调用的时候都会经过我们重写的函数,以String为例。首先我们遇到了第一个问题,就是一些有一些属性不可被遍历如下图,所以不能用循环的方式遍历,有一些属性不能被重写,哦我们同样也是不能hook它的。

for (const stringHookArrayKey in String_hook_Array) {
    let value = String_hook_Array[stringHookArrayKey]
    if(value["writable"]
        && typeof value.value === "function"){
        console.log(stringHookArrayKey)
        value.value.hook(String)
    }
}

接着我们还要hook原形链中的方法,为了防止套娃调用爆栈,所以我们不处理toString方法,另外发现String中的concat方法一直在搞我们,所以把他也过滤掉。

for (const stringPrototypeHookArrayKey in String_prototype_hook_Array) {
    let value = String_prototype_hook_Array[stringPrototypeHookArrayKey]
    if(value["writable"]
        && typeof value.value === "function"
        && stringPrototypeHookArrayKey !== "toString"
        && stringPrototypeHookArrayKey !== "concat"
    ){
        console.log(stringPrototypeHookArrayKey)
        value.value.hook(String.prototype)
    }
}

最终成品如下,这样有一个小问题,就是虽然我们能把所有东西都hook到,但是会非常的卡顿,因为我们console.log了太多的内容

let String_hook_Array = Object.getOwnPropertyDescriptors(String)
let String_prototype_hook_Array = Object.getOwnPropertyDescriptors(String.prototype)

for (const stringHookArrayKey in String_hook_Array) {
    let value = String_hook_Array[stringHookArrayKey]
    if(value["writable"]
        && typeof value.value === "function"){
        console.log(stringHookArrayKey)
        value.value.hook(String)
    }
}

for (const stringPrototypeHookArrayKey in String_prototype_hook_Array) {
    let value = String_prototype_hook_Array[stringPrototypeHookArrayKey]
    if(value["writable"]
        && typeof value.value === "function"
        && stringPrototypeHookArrayKey !== "toString"
        && stringPrototypeHookArrayKey !== "concat"
    ){
        console.log(stringPrototypeHookArrayKey)
        value.value.hook(String.prototype)
    }
}
console.log("enum hook start")

js版r0trace

接下来终于进入了我们今天的正题,就是如何实现一个像r0trace一样的项目,能够将浏览器的所有操作的都hook上,这样能够更方便的使用hook功能,如果我们不知道具体参数在哪,可以把整个浏览器所有的对象都hook上定位目标参数。首先第一步就是模仿r0trace定义一个黑白名单

    if(!BlackList){
        BlackList = []
    }
    if(!WhiteList){
        WhiteList = []
    }

然后和上面一样,做2步就是将原型链和本身所有的属性都传入startHook函数

    let HookObjDescriptors = Object.getOwnPropertyDescriptors(HookObj)
    if(HookObj.prototype){
        let HookObjPrototypeDescriptors = Object.getOwnPropertyDescriptors(HookObj.prototype)
        startHook(HookObjPrototypeDescriptors, HookObj.prototype)
    }
    startHook(HookObjDescriptors, HookObj)

最后实现startHook函数,只要做一些小的过滤处理即可,最后成品如下,白名单为空就是hook这个对象的所有函数,黑名单中把爆栈的函数过滤掉即可

function batchHook(HookObj, BlackList, WhiteList){
    if(!BlackList){
        BlackList = []
    }
    if(!WhiteList){
        WhiteList = []
    }
    function startHook(Descriptors, HookObj){
        for (const descriptorsKey in Descriptors) {
            let value = Descriptors[descriptorsKey]
            let rawFunc = value["value"]
            if(typeof rawFunc === "function"
                && value["writable"]
                && !BlackList.includes(descriptorsKey)
                && (WhiteList.length ? WhiteList.includes(descriptorsKey) : true))
            {
                console.log(descriptorsKey)
                rawFunc.hook(HookObj)
            }
        }
    }


    let HookObjDescriptors = Object.getOwnPropertyDescriptors(HookObj)
    if(HookObj.prototype){
        let HookObjPrototypeDescriptors = Object.getOwnPropertyDescriptors(HookObj.prototype)
        startHook(HookObjPrototypeDescriptors, HookObj.prototype)
    }
    startHook(HookObjDescriptors, HookObj)

}

batchHook(String, ["toString", "concat"], [])


直接就把我们之前验证码的密钥输出出来了,十分的好用,当然由于它输出的数据太多我们在浏览器里面看着不是很舒服,可以把它输出到一个log文件中,右键直接点击save as即可,如下图成功定位到了我们密钥的位置,当让如果不怕卡死的话还可以将console.log改成console.trace,能够更轻松的通过调用栈。找到它的函数的位置

js版objection 内存漫游

最后介绍一下https://github.com/CC11001100/ast-hook-for-js-RE这个项目,借助这个工具我们可以检索浏览器中的任意数据。首先安装它,非常的简单,用git拉

git clone https://github.com/CC11001100/ast-hook-for-js-RE.git

然后用pycharm打开它,保证自己的nodejs版本大于14.0.0,然后在pycharm的命令行安装

npm install

然后用anyproxy ca命令去启动一个server,我们访问127.0.0.1:8002/去安装一个证书




然后启动proxy-serer,给浏览器配置一个代理或者直接启动的时候就加代理google-chrome --proxy-server="127.0.0.1:10086" --no-sandbox

pycharm中出现内容就说明代理配置成功了

然后打开我们的试题网站做一个测试,直接用hook.search传入我们的参数就好了,非常的牛逼所有出现这个参数的地方都打印出来了

posted on 2021-09-26 11:46  r0ysue  阅读(303)  评论(0编辑  收藏  举报