吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 10628|回复: 48
收起左侧

[Web逆向] 0基础入门通杀型 js hook

  [复制链接]
roysue 发表于 2021-9-26 10:44

<!-- @import "[TOC]" {cmd="toc" depthFrom=1 depthTo=6 orderedList=false} -->

<!-- code_chunk_output -->

<!-- /code_chunk_output -->

js hook 入门

js hook 基础

js hook非常的简单,具体步骤就是记录之前的函数,然后再重写当前函数即可原理如下图,而因为客户端拥有js的最高解释权所以任何代码都无法阻止hook,只能通过混淆来影响逆向人员分析
7.png
测试demo如下

var _eval=eval
eval=function(arg){
console.log(arg)
    return _eval(arg)
}

结果如下图
1.png
hook 参数稍微不一样,需要用到defineProperty方法,例如下面这样的测试demo

var cookie = document.cookie;
document = Object.defineProperty(document, 'cookie', {
  get: function () {
    console.log('getter: ' + cookie);
    return cookie;
  },
  set: function (value) {
    console.log('setter: ' + value);
    cookie = value
  }
});

打开浏览器随便找一个网站测试一下
6.png

js hook方法检测与过检测

这里检测就要用到hook的特性,或者说hook使用者根本注意不到的一些点(当然如果hooker使用代{过}{滤}理器就不好使了),第一个点就是hook之后toString会更改,如下图

console.log(eval+"");
var _eval=eval
eval=function(arg){
console.log(arg)
    return _eval(arg)
}
console.log(eval+"");
eval.toString()

2.png
可以看到从原来的native code变成了我们自己写的代码,这样防守方就能轻而易举的检测到我们使用了hook这种技巧。但是这种绕过也是很简单的,就是重写我们hook函数的toString方法就好了

var a=eval+""
var _eval=eval
eval=function(arg){
console.log(arg)
    return _eval(arg)
}
eval.toString=function(){return "function eval() { [native code] }"}
console.log(eval+"");
console.log(a===(eval+""))

3.png
这种方式过于简单,而且能被代{过}{滤}理器检测到,所以还可以检测原型链上的toString方法

var a=eval+""
var _eval=eval
eval=function(arg){
console.log(arg)
    return _eval(arg)
}
eval.toString=function(){return "function eval() { [native code] }"}
console.log(eval+"");
console.log(a===(eval+""))
console.log(Function.prototype.toString.call(eval))

4.png
当然如果我们能够注意到这一点,那么这种检测方法也是很好绕过的,就是hook原型链上的方法

var a=eval+""
var _eval=eval
eval=function(arg){
console.log(arg)
    return _eval(arg)
}
eval.toString=function(){return "function eval() { [native code] }"}
var _old=Function.prototype.toString.call
Function.prototype.toString.call=function(arg){
    if(arg==eval)
    return "function eval() { [native code] }"
    return _old(arg);

}
console.log(Function.prototype.toString.call(eval))

5.png

proxy与浏览器

proxy也可以用在浏览器中,测试demo如下

const handler = {
    get: function(obj, prop) {
        console.log(obj,prop)
        return prop in obj ? obj[prop] : 37;
    }
};

const p = new Proxy({}, handler);

8.png
但是我们为什么不在浏览器里面写Proxy而是只在nodejs里面写呢,因为在浏览器中window对象是不能重写的,自然就不能重写window的代{过}{滤}理器,所以我们只能在nodejs中使用Proxy,因为不能重写window就索然无味,但是我们可以监听新赋值的变量,例如有一些变量我们不知道他是什么时候生成的在哪里生成的我们就可以使用下面的这种技巧。

Object.defineProperty(window,"dta",
    {
        get:function (){
            return "hook dta"
        },
        set:function (){
            debugger;
        }
    }
    )

对window.dta进行赋值就会触发set方法,就会在debugger上停下来
9.png

js一键快速hook框架

首先举一个例子,我比较懒想要快速hook一个base64编码函数,但是又不想写冗长的代码怎么办呢,这里主要参考了珍惜大佬的js课程并进行了一定程度的修改,那么就需要写一个hook函数来帮助我们一劳永逸。首先要解决第一个问题就是如何让所有的对象都能访问到我们的hook方法呢,这里我们可以将hook方法定义在原型链上

Function.prototype.hook=function(){}

接下来时第二个问题,如何保存原方法,可以用下面这种形式

var _this=this ;
var Functionname=_this.name;
if(!Functionname)
{
    console.log("hook erro")
    return false;

}
window.dta[Functionname]=this;

最后直接重写方法即可

Function.prototype.hook=function (onEnter,onLeave,context) {
    var _this = this;
    window.dta={};
    var _context = context || window;
    var Functionname = _this.name;
    if (!Functionname) {
        console.log("hook erro")
        return false;

    }

    window.dta[Functionname] = this;
    _context[Functionname] = function () {
console.log(arguments)
        var args = Array.prototype.slice.call(arguments, 0);
        var _this = this;
        var warpper = {args}
        onEnter.call(_this, warpper);
        var result = window.dta[Functionname].apply(this, warpper.args);
       console.log(result)
        var retuenval = onLeave.call(_this, result)
        if (!retuenval) {
            return retuenval
        }
        return result;
    }
}
btoa.hook(function (warpper){
    var args=warpper.args;
    console.log(args)

    },function (retval){
    console.log(retval)
    return true
    }

)

10.png
当然这只是最基础的hook方式我们还要过一些简单的检测,比如重写他的toString方法去掉他的一些修改特征,这种古老又有效的方法,最终成品如下

(() => {
    const $toString = Function.toString
    const myFunction_toString_symbol = Symbol('('.concat('', ')_', (Math.random()) + '').toString(36))
    const myToString = function (){
        return typeof this === 'function' && this[myFunction_toString_symbol] || $toString.call(this)
    }
    function set_native(func, key, value){
        Object.defineProperty(func, key, {
            enumerable: false,
            configurable: true,
            writable: true,
            value: value
        })
    }
    delete Function.prototype.toString
    set_native(Function.prototype, "toString", myToString)
    set_native(Function.prototype.toString, myFunction_toString_symbol, "function toString() { [native code] }")
    globalThis.func_set_native = (func) => {
        set_native(func, myFunction_toString_symbol, `function ${func.name || ''}() { [native code] }`)
    }
}).call(this)

window.dta = {}
Function.prototype.hook = function(onEnter, onLeave, context, Funcname){
    if (!onEnter){
        onEnter = function (warpper){
            var args = warpper.args;
            console.log(args)
        }
    }
    if (!onLeave){
        onLeave = function (retval){
            console.log(retval)
        }
    }

    // btoa.hook()
    var _context = context || window;
    var FuncName = this.name || Funcname;
    if (!FuncName){
        console.error("hook function name is empty!")
        return false
    }
    window.dta[FuncName] = this;

    _context[FuncName] = function (){
        var args = Array.prototype.slice.call(arguments,0)
        var _this = this
        var warpper = {
            args
        }

        onEnter.call(_this, warpper)
        // this -> window
        var retval = window.dta[FuncName].apply(this, warpper.args)

        var hook_retval = onLeave.call(_this, retval)
        if (hook_retval){
            return hook_retval
        }
        return retval
    }
    Object.defineProperty(_context[FuncName], "name", {
        get: function (){
            return FuncName
        }
    })
    func_set_native(_context[FuncName])
}

console.log("quick hook start")

免费评分

参与人数 15吾爱币 +16 热心值 +11 收起 理由
chengdragon + 1 用心讨论,共获提升!
我是不会改名的 + 2 + 1 用心讨论,共获提升!
冰岛的雨季 + 1 + 1 谢谢@Thanks!
frz4013 + 1 我很赞同!
qwe134133987 + 1 + 1 热心回复!
jsang + 1 + 1 谢谢@Thanks!
ofo + 1 在某宝买了楼主的一本书:安卓Frida逆向与抓包实战
xhever + 1 + 1 用心讨论,共获提升!
逍遥一仙 + 3 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
唐小样儿 + 1 + 1 我很赞同!
Stubborn6 + 1 用心讨论,共获提升!
空若野 + 1 + 1 好东西,不明觉厉
52Appfan + 1 + 1 我很赞同!
zpy2 + 1 我很赞同!
gy55you + 1 用心讨论,共获提升!

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

兜兜风f 发表于 2021-9-26 15:20
学习学习啦~~~
 楼主| roysue 发表于 2021-9-26 11:26
Piz.liu 发表于 2021-9-26 11:05
我的水平应该是-99,能不能再详细点,照顾一下啥都不懂的小白 怎么用

可以联系肉丝老师加群   我们的宗旨就是专治不明白
Piz.liu 发表于 2021-9-26 11:05
本帖最后由 Piz.liu 于 2021-9-26 11:15 编辑

我的水平应该是-99,能不能再详细点,照顾一下啥都不懂的小白 怎么用
liangqz 发表于 2021-9-26 11:58
感谢分享
iyeoman 发表于 2021-9-26 12:51
js一键快速hook框架
batcd 发表于 2021-9-26 14:38
我觉得此篇是有学习价值的,我要多看即便吸收不了
995 发表于 2021-9-26 16:06
这哪里是0基础啊,看不懂
lcwxxf 发表于 2021-9-26 19:40
看得有些晕糊糊,功力还不够哇
q6378561 发表于 2021-9-26 19:56
支持一下
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-12-23 15:58

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表