js逆向--技巧

检测调试

  • 键盘监听 F12
  • 检测浏览器内外的高度差值
  • 检测开发者人员工具变量是否为true
  • 利用console 禁止使用console输出
  • 利用代码运行时间差
  • 利用toString 比如判断是否是"function Function() { [native code] }"
  • 检测栈的层次caller

反调试

  • debugger
    • 非虚拟机:
      • 在debugger那一行下断点,右键断点,edit breakpoint, 填写false; 在循环中无效
      • 直接替换代码:
        • 谷歌内核浏览器默认提供
        • 浏览器插件 油猴
        • 代理替换(中间人):fiddler、charles 可以通过正则匹配文件名
    • 虚拟机:文件显示的是VMxxx
      • eval
      • Function
        • Function.prototype.constructor = function(x) {}
          Function.prototype.constructor_ = Function.prototype.constructor;
          Function.prototype.constructor = function(x) {
              if (x != "debugger") {
                  return Function.prototype.constructor_(x);
              }
              return function() {};
          }
          
  • 死循环:循环语句,无限递归,setInterval()不断调用、两个方法互调(断链)
  • 引向错误的逻辑
    • 通过hook,输出正确调用堆栈

常见的混淆

  • eval
    • 代码以字符串的形式传给eval进行执行
    • 在虚拟机上执行
  • AA混淆
    • 最后一行代码有用,直接最后一行下断点,单步调试,进入虚拟机可看到源码
          ゚ω゚ノ = /`m´)ノ ~┻━┻   //*´∇`sojson.com*/
          ['_'];
          o = (゚ー゚) = _ = 3;
          c = (゚Θ゚) = (゚ー゚) - (゚ー゚);
          (゚Д゚) = (゚Θ゚) = (o ^ _ ^ o) / (o ^ _ ^ o);
          (゚Д゚) = {
              ゚Θ゚: '_',
              ゚ω゚ノ: ((゚ω゚ノ == 3) + '_')[゚Θ゚],
              ゚ー゚ノ: (゚ω゚ノ + '_')[o ^ _ ^ o - (゚Θ゚)],
              ゚Д゚ノ: ((゚ー゚ == 3) + '_')[゚ー゚]
          };
          (゚Д゚)[゚Θ゚] = ((゚ω゚ノ == 3) + '_')[c ^ _ ^ o];
          (゚Д゚)['c'] = ((゚Д゚) + '_')[(゚ー゚) + (゚ー゚) - (゚Θ゚)];
          (゚Д゚)['o'] = ((゚Д゚) + '_')[゚Θ゚];
          (゚o゚) = (゚Д゚)['c'] + (゚Д゚)['o'] + (゚ω゚ノ + '_')[゚Θ゚] + ((゚ω゚ノ == 3) + '_')[゚ー゚] + ((゚Д゚) + '_')[(゚ー゚) + (゚ー゚)] + ((゚ー゚ == 3) + '_')[゚Θ゚] + ((゚ー゚ == 3) + '_')[(゚ー゚) - (゚Θ゚)] + (゚Д゚)['c'] + ((゚Д゚) + '_')[(゚ー゚) + (゚ー゚)] + (゚Д゚)['o'] + ((゚ー゚ == 3) + '_')[゚Θ゚];
          (゚Д゚)['_'] = (o ^ _ ^ o)[゚o゚][゚o゚];
          (゚ε゚) = ((゚ー゚ == 3) + '_')[゚Θ゚] + (゚Д゚).゚Д゚ノ + ((゚Д゚) + '_')[(゚ー゚) + (゚ー゚)] + ((゚ー゚ == 3) + '_')[o ^ _ ^ o - ゚Θ゚] + ((゚ー゚ == 3) + '_')[゚Θ゚] + (゚ω゚ノ + '_')[゚Θ゚];
          (゚ー゚) += (゚Θ゚);
          (゚Д゚)[゚ε゚] = '\\';
          (゚Д゚).゚Θ゚ノ = (゚Д゚ + ゚ー゚)[o ^ _ ^ o - (゚Θ゚)];
          (o゚ー゚o) = (゚ω゚ノ + '_')[c ^ _ ^ o];
          (゚Д゚)[゚o゚] = '\"';
          (゚Д゚)['_']((゚Д゚)['_'](゚ε゚ + (゚Д゚)[゚o゚] + (゚Д゚)[゚ε゚] + (゚Θ゚) + ((o ^ _ ^ o) + (o ^ _ ^ o)) + ((o ^ _ ^ o) + (o ^ _ ^ o)) + (゚Д゚)[゚ε゚] + (゚Θ゚) + (゚ー゚) + (゚Θ゚) + (゚Д゚)[゚ε゚] + (゚Θ゚) + ((o ^ _ ^ o) + (o ^ _ ^ o)) + ((o ^ _ ^ o) - (゚Θ゚)) + (゚Д゚)[゚ε゚] + (゚ー゚) + (c ^ _ ^ o) + (゚Д゚)[゚ε゚] + (゚Θ゚) + ((゚ー゚) + (゚Θ゚)) + (c ^ _ ^ o) + (゚Д゚)[゚ε゚] + (゚Θ゚) + (゚ー゚) + ((゚ー゚) + (゚Θ゚)) + (゚Д゚)[゚ε゚] + (゚Θ゚) + ((゚ー゚) + (゚Θ゚)) + (゚ー゚) + (゚Д゚)[゚ε゚] + (゚Θ゚) + ((゚ー゚) + (゚Θ゚)) + (゚ー゚) + (゚Д゚)[゚ε゚] + (゚Θ゚) + ((゚ー゚) + (゚Θ゚)) + ((゚ー゚) + (o ^ _ ^ o)) + (゚Д゚)[゚ε゚] + (゚ー゚) + (c ^ _ ^ o) + (゚Д゚)[゚ε゚] + ((゚ー゚) + (o ^ _ ^ o)) + ((゚ー゚) + (゚Θ゚)) + (゚Д゚)[゚ε゚] + (゚ー゚) + (c ^ _ ^ o) + (゚Д゚)[゚ε゚] + (゚ー゚) + ((゚ー゚) + (o ^ _ ^ o)) + (゚Д゚)[゚ε゚] + (゚Θ゚) + ((o ^ _ ^ o) + (o ^ _ ^ o)) + ((゚ー゚) + (o ^ _ ^ o)) + (゚Д゚)[゚ε゚] + (゚Θ゚) + ((゚ー゚) + (゚Θ゚)) + ((゚ー゚) + (o ^ _ ^ o)) + (゚Д゚)[゚ε゚] + (゚Θ゚) + ((o ^ _ ^ o) + (o ^ _ ^ o)) + ((o ^ _ ^ o) - (゚Θ゚)) + (゚Д゚)[゚ε゚] + (゚Θ゚) + ((゚ー゚) + (゚Θ゚)) + (゚ー゚) + (゚Д゚)[゚ε゚] + (゚Θ゚) + (゚ー゚) + (゚ー゚) + (゚Д゚)[゚ε゚] + (゚ー゚) + ((゚ー゚) + (o ^ _ ^ o)) + (゚Д゚)[゚ε゚] + ((゚ー゚) + (o ^ _ ^ o)) + (o ^ _ ^ o) + (゚Д゚)[゚o゚])(゚Θ゚))((゚Θ゚) + (゚Д゚)[゚ε゚] + ((゚ー゚) + (゚Θ゚)) + (゚Θ゚) + (゚Д゚)[゚o゚]);
      
        ```
      
  • jj混淆
    • 最后一行断点
    • 对sojson.$_进行hook,然后在控制台通过arguments查看参数;
    • 或者直接单步调试
    • (sojson._)[sojson.$][sojson.$] 通过 ['constructor']['constructor']获取到Function,因此直接hook Function不起效果
        sojson.$ = function(){debugger;}
    
    
        sojson = ~[];
        /*sojson.com*/
        sojson = {
            ___: ++sojson,
            /*sojson.com*/
            $$$$: (![] + "")[sojson],
            __$: ++sojson,
            $_$_: (![] + "")[sojson],
            _$_: ++sojson,
            $_$$: ({} + "")[sojson],
            $$_$: (sojson[sojson] + "")[sojson],
            _$$: ++sojson,
            $$$_: (!"" + "")[sojson],
            $__: ++sojson,
            $_$: ++sojson,
            $$__: ({} + "")[sojson],
            $$_: ++sojson,
            $$$: ++sojson,
            $___: ++sojson,
            $__$: ++sojson
        };
        sojson.$_ = (sojson.$_ = sojson + "")[sojson.$_$] + (sojson._$ = sojson.$_[sojson.__$]) + (sojson.$$/*sojson.com*/
        = (sojson.$ + "")[sojson.__$]) + ((!sojson) + "")[sojson._$$] + (sojson.__ = sojson.$_[sojson.$$_]) + (sojson.$ = (!"" + "")[sojson.__$]) + (sojson._ = (!"" + "")[sojson._$_]) + sojson.$_[sojson.$_$] + sojson.__ + sojson._$ + sojson.$;
        /*sojson.com*/
        sojson.$$ = sojson.$ + (!"" + "")[sojson._$$] + sojson.__ + sojson._ + sojson.$ + sojson.$$/*sojson.com*/
        ;
        sojson.$ = (sojson.___)[sojson.$_][sojson.$_];
        sojson.$(sojson.$(sojson.$$ + "\"" + "\\" + sojson.__$ + sojson.$$_ + sojson.$$_ + sojson.$_$_ + "\\" + sojson.__$ + sojson.$$_ + sojson._$_ + " \\" + sojson.__$ + sojson.$_$ + sojson.___ + sojson.$$$_ + (![] + "")[sojson._$_] + (![] + "")[sojson._$_] + sojson._$ + " \\" + sojson.$$$ + sojson.$_$ + " '\\" + sojson.__$ + sojson.$$_ + sojson.$$$ + sojson._$ + "\\" + sojson.__$ + sojson.$$_ + sojson._$_ + (![] + "")[sojson._$_] + sojson.$$_$ + "'\\" + sojson.$$$ + sojson._$$ + "\"")())(sojson = {
            ___: ++sojson,
            $$$$: (![] + "")[sojson]
        });
    
    
  • FuckJs
    • 好像有解密工具,哪位大佬赏一个呗 😃
    • 代码貌似不可格式化,形状如下:
          [][(![] + [])[!+[] + !![] + !![]] + ([] + {})[+!![]] + (!![] + [])[+!![]] + (!![] + [])[+[]]][([] 
          + {})[!+[] + !![] + !![] + !![] + !![]] + ([] + {})[+!![]] + ([][[]] + [])[+!![]] + (![] + [])[!+[] 
          + !![] + !![]] + (!![] + [])[+[]] + (!![] + [])[+!![]] + ([][[]] + [])[+[]] + ([] + {})[!+[] + !![] 
          + !![] + !![] + !![]] + (!![] + [])[+[]] + ([] + {})[+!![]] + (!![] + [])[+!![]]]([][(![] + [])[!+[] 
          + !![] + !![]] + ([] + {})[+!![]] + (!![] + [])[+!![]] + (!![] + [])[+[]]][([] + {})[!+[] + !![] + !![] 
          + !![] + !![]] + ([] + {})[+!![]] + ([][[]] + [])[+!![]] + (![] + [])[!+[] + !![] + !![]] + (!![] + [])[+[]]
      

js逆向的操作流程

  • 抓包
  • 调试
    • 搜关键词:
      • var xxx
      • .xxx
      • xxx:
      • ['xxx']
    • xhr断点
    • 其他断点
  • 抠取js
    • 抠全了吗
    • this是谁
    • 尽量不修改源js
    • 通过定义全局变量导出目标(函数,对象等)
  • 改写
  • 本地运行出值
  • 提交服务器验证

常见加密类型

  • 取盐校验,不可逆:md5 md4 md2 带密码的md5 sha1 sha256 sha512
    • md5 16位 32位 40位 0123456789ABCDEF
    • sha1 10位 sha256 64位 sha512 128位
  • 对称加密: AES DES 3DES
    • AES
        key = CryptoJS.enc.Utf8.parse('十六位十六进制字符');
        source_str = CryptoJS.enc.Utf8.parse('被加密的字符串');
        CryptoJS.AES.encrypt(source_str, key, {各种配置参数信息});
        其中各种配置参数可能会出现的关键词:
            CryptoJS.mode.ECB  CryptoJS.mode.CBC
            CryptoJS.pad.Pkcs7  iv
    
  • 非对称加密(私钥、公钥):RSA
    • RSA
        setPublicKey(publicKey);
        encrypt(原文);
    
  • Base64 很有可能以=号结束
    MTIzNDU25L2g5aW95ZWm5ZWm5ZWm44CC44CCYWJjZGZkbA==
    window.btoa 对字符串进行 base64编码(注意不能编码中文);
    winodw.atob 对 base64字符串 进行解码(对于包含中文的 base64编码,不能正确解码);
    解决中文编码的办法:
        // 编码
        function utf8_to_b64(str) {
            return window.btoa(unescape(encodeURIComponent(str)));
        }

        // 解码
        function b64_to_utf8(str) {
            return decodeURIComponent(escape(window.atob(str)));
        }

hook

  • 覆盖原函数
function func() {
    console.log("func");
}
// hook
func = function() {
    console.log("hook func");
}

// hook 并执行原函数
var func_ = func;
func = function() {
    console.log("hook func");
    debugger();
    func_();
}

  • 通过Object.defineProperty替换一个对象属性(getter、setter)
(function(){
    var value = "";
    Object.defineProperty(document, 'cookie', {
        set: function(val) {
            console.log(val);
            value = val;
            debugger;
            return val;
        },
        get: function() {
            return value;
        }
    })
})();
  • new Proxy

hook的时机

  • 最好在网页加载的第一个js文件的第一行下端点,然后在控制台手动注入hook,这种方式注入hook的时机还是可能会晚了。
  • 利用浏览器中开发工具Sources下面的Overrides功能,对于js文件名变化的不好处理。
  • 利用FD等中间人软件的替换响应功能注入hook,可以通过正则匹配js文件名
  • 油猴,不推荐

抠代码

  • webpack
  1. 找到加载器,即加载模块的方法
  2. 找到需要调用的模块
  3. 构造一个自执行函数
  4. 定义一个全局变量,导出加密方法
  5. 编写自定义函数,按照网站实际加密流程进行加密
// 加载器
function(e) {
    var t = {};
    function n(r) {
        if (t[r])
            return t[r].exports;
        var o = t[r] = {
            exports: {}
        };
        return e[r].call(o.exports, o, o.exports, n),
        o.exports
    }
    return 和可能是一个逗号表达式
}([
    function(x1, x3, x3) {},
    function(x1, x3, x3) {},
    function(x1, x3, x3) {},
    function(x1, x3, x3) {},
    function(x1, x3, x3) {},
    function(x1, x3, x3) {},
]) 传入数组 或者 对象 }({
    a: function(x1, x3, x3) {},
    b: function(x1, x3, x3) {},
    ...
})
    

RPC

  • 使用websocket
  • 使用javascript编写webscoket代码
  • 将代码注入在目标js中合适的位置
    • 可以使用chrome浏览器自带的override功能,将目标js保存在本地
    • 然后把webscoket代码复制到合适位置
    !(function(){
        windows.my_var = 目标对象或者方法;
        var ws = new WebSocket("ws或者wss://host:post");
        ws.onopen = function(event) {
            xxx
        }
        ws.onmessage = function(event) {
            // 假设要调用目标网站的加密函数
            result = window.my_var.encrypt(event.data);
            ws.send(result); // 发回服务器
        }
    })();
    
  • 手动运行目标网站,触发注入的webscoket代码执行,完成于服务器的连接
  • 这样服务器与注入代码就可以进行通信了

资源

  • JavaScript反调试技巧
    • www.freebuf.com/articles/system/163579.html
posted @ 2022-03-25 13:53  liDB  阅读(829)  评论(0编辑  收藏  举报