JS逆向的主要思路一般有这几种

1,利用AST反混淆,因为用的就是AST混淆的,所以理论上应该都能用AST再返回去。但是实际操作好像不容易。

2,跟值,一步一步找到加密方法和密钥。现在很多混淆方法,把一句代码变成了1万句(这个没夸张,我遇到好几个,虽然不知道原来的代码量是多少,反正实际运行了几十万行,浏览器竟然也很流畅),再利用一些平坦流的写法,让你无法跟值。

3,完全就不用去逆向,直接搞个浏览器(为了提高效率,可以用无头浏览器),真实的去运行就行了,最后用一些手段得到加密的结果。

为了防御方案3,又有2种方法:1)浏览器指纹;2)判断环境是什么

获取到方案3的浏览器环境指纹,如果这个指纹提交了过多的数据,那么这些数据就都是异常的。

知道方案3用的是什么环境,比如python的selenium,只要是这个,那就都判定为异常。

针对方法1,可以用指纹浏览器,每次都是不同的指纹。

针对方法2,只要知道对方是根据什么判断出是selenium,相应的属性干掉或者补充就行了。

但问题是,指纹的获取和selenium的判断,你怎么知道是什么。

指纹的获取和selenium的判断,本质就是一组js代码,但是这组js代码是什么,是没法确定的,通常只能是百度搜一下,把搜到的都做相应的处理,但是别人可能还根据什么做了判断,就无法得知了。

比如PC端的B站注册,用了fingerprint获取指纹,不管你通过什么方式,实现了fingerprint获取到的指纹每次都不同,但是你会发现,注册的B站账号,还是异常,说明B站检测到了你这个注册途径不正常(每次注册IP肯定会换的,已经排除掉了这个检测的可能)。

这时候,就需要补环境框架了。

js代码简单的说是3部分组成,V8(js最核心的代码,可以简单的理解为js官方发布的),BOM(浏览器实现的js代码,比如alert,比如xmlHttpRequest),DOM(页面节点,节点对应的js方法,也是浏览器自己实现的)。不管是指纹或者判断selenium的代码,基本都在BOM和DOM中。

很多语言都有运行js代码的功能,本质就是调用V8。所以就产生了一种思路,如果我把混淆的js代码放在V8里跑,因为没有BOM和DOM的方法,如果有用到了相关函数,那就会报错,一旦报错了,不就知道这个js代码判断了什么东西了。

以判断selenium的方法window.navigator.webdriver为例,说下js补环境框架下如何知道对方是怎么检测的。

首先,V8环境是没有navigator的,所以需要我们先自己定义,然后挂上代理,此时运行“window.navigator.webdriver”,就能监听到代码有在获取navigator的webdriver属性。

//*******************这部分是框架代码***************************
var navigator={};

window.navigator=new Proxy(window.navigator, {
    set(target, property, value) {
        console.log("proxy set", target, property, value);
        return Reflect.set(...arguments);
    },
    get(target, property, receiver) {
        console.log('proxy get:',target,property);
        return target[property];
    }
});
//*******************这部分是框架代码***************************


//***这是混淆的js代码(当然为了说明问题自然就没混淆)**********
console.log(window.navigator.webdriver);
//***这是混淆的js代码(当然为了说明问题自然就没混淆)**********

补环境的基本思路就是这样,看着很简单,但问题是,你既然能想到这么做,人家也能想到,那么人家可以用一些方法,去检测你是不是补环境框架。

比如,我们刚才定义的navigator,就是个普通的对象,但是真实的navigator,是有原型为Navigator的对象。你在控制台里测试这句代码,会得到这么个结果,这个在我们自己的代码里可没有。

console.log(navigator.__proto__[Symbol.toStringTag])
"Navigator"

所以相对完善一点的定义navigator代码,应该是这样。

var Navigator = function Navigator() {
    throw new TypeError("Illegal constructor");
};

Object.defineProperties(Navigator.prototype, {
    [Symbol.toStringTag]: {
        value: "Navigator",
        configurable: true
    }
});

Navigator.prototype.userAgent="Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36";

navigator = {};
navigator.__proto__ = Navigator.prototype;

for (var property_ in Navigator.prototype) {
    navigator[property_] = Navigator.prototype[property_];
    Navigator.prototype.__defineGetter__(property_, function () {
        throw new TypeError("Illegal constructor");
    });
}

首先,先定义一个Navigator,这个是不能被new的。

然后,定义Navigator原型上的Symbol.toStringTag

然后,把Navigator的属性否赋值给navigator,此时还有个点需要注意,不能直接从Navigator上获取属性。

再比如navigator.plugins,这是指纹用到的检测浏览器插件,这个东西你在控制台里看的话,在内部无限循环。如果你模拟出来的不是这样的,就很容易被检测出来。

最近跑一个混淆的js代码,第一次开始研究这个东西,自认为有几个点比较难(大佬应该觉得是浮云),在此记录一下

1,事件的监听与触发,也可以说是Event的重写

2,webrtc的重写,要自己实现这个,还得解决promise

3,XMLHttpRequest的重写

4,plugins里无限嵌套

5,toString的检测,在控制台里对原生函数执行toString,得到是这么个结果“function alert() { [native code] }”,如果alert是我们自己定义的,就不是这个结构了。有一次偶然,对toString进行监听,发现有个混淆的js代码,输出了这样的字符串"function get cancelBubble() { [native code] }",直接蒙了,完全不知道怎么能得到这样的结果。研究了4,5天吧,每天下班回家搞2小时,最后终于发现了端倪。

最后再说一下,有了补环境框架,基本上也就实现了指纹重写,因为都知道了检测的是什么了,只需要把对应的js代码拿出来,在selenium跑一下就行了。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐