为什么补环境:因为网站会根据浏览器与node的不同设置环境监测,一旦代码中含有环境检测的代码,就会触发报错或者蜜罐返回不出或者错误信息,这时候就要学会补环境才能解决此类问题
常见环境检测代码
检测环境存在
这里是JS环境检测代码:
python
function pn() {
if(navigator.appName) {
return hello;
}else{
return ell;
}
}
console.log(pn())
直接运行结果:

这里直接返回错误数据(其实有些node版本会直接报错,因为node中根本不存在navigator这个属性),如果补上环境:

就出来了,这里怎么来的数据呢,直接在逆向的网页控制台中搜,然后补上:

或者直接乱写里面的数据就行,因为这里只是检测是否有此属性而已(不过建议还是正常补吧)
检测属性值长度
根据属性长度检测环境:
python
function ps() {
if (location['href'].length > 10) {
return 'hello'
}else{
return 'wrong data'
}
}
console.log(ps())
直接运行结果:

根据报错补上location:

这时候依旧报错,像这种检查长度的还需要补上length前面的属性值,依旧去浏览器中拿:


异常捕获
异常捕获是补环境中最恶心的,因为它根本不报错,有时还返回错误信息引你到蜜罐,下面是代码展示:
python
function pn() {
try {
console.log(verify_local());
if (document.title) {
return 'hello world'
}
}catch(e) {
// console.log(e)
return 'wrong data'
}
}
function verify_local() {
if (location.host.length > 2) {
return 'xxx'
}
}
console.log(pn())
直接执行结果:

直接就走catch了,这里的verify_local函数和pn函数中都含有没有的环境(分别是location和document)本来会报错,但直接被捕获了,我们拿到错误数据时看不出来(真正做逆向的时候),那遇到这种情况怎么办呢,直接在代码中搜索try,然后将异常捕获全部注释掉,然后运行,让错误都暴露出来:

先补location以及属性:

然后运行:

再补document:


这样就拿到正确数据了,但如果try catch太多的话就很难受了,我们可以这样替换(Ctrl + R):

打印出报错即可

接下来一步一步补到不报错即可
混淆属性名
有一种补完第一层不报错而是抛出undefined,代码如下:
python
var _0x42246 = 'href' // 模拟混淆
// 混淆
function fn() {
if (location[_0x42246]) {
return 'hello world'
}
}
console.log(fn())
运行:

根据报错补上location:

这里出来了undefined而不是数据hello world,其实我们少补了里面的属性,也就是混淆的那个,很容易因为报错而忽略,所以要看仔细:

所以对于这种报错一定要在浏览器代码中打上断点调试一下看看中括号中是什么:

然后浏览器中拿一下补过来:


这样就行了
检测exports
在node中有一个单词叫exports,用来导出函数的模块,而浏览器中没有:


这里就可以用来检测:
python
sss = 'undefined' != typeof exports ? exports : void 0;
console.log(sss) // 只是说有这么个检测方法,但代码不一定这么写
在node中是:

在浏览器中是:

这种想要跟浏览器保持一致就将 != 改为 == 就OK了
检测global
node全局叫做global,浏览器全局叫做window,这时候也可以检测,代码如下:
python
glb = 'undefined' == typeof window ? global : window;
console.log(glb); // 只是说有这么个检测方法,但代码不一定这么写
运行结果

这时候我们也可以通过改符号来过检测
吐环境
检测了解完之后你会发现当代码混淆后,或者检测太多,try catch不报错等等都会导致我们补不全或者说补着很麻烦,有没有一种可能能让所需要的环境都吐出来呢,这时候我们可以用与hook功能类似的方法来做到------Proxy代理方法,它能够做到当代码做某些操作时可以被其拦截到,这层拦截可以进行对外部访问进行过滤和改写,这里我们直接进行代码书写,想自己更加详细的了解可以去此网站:JS逆向方法搜索网站
初始代码
这里只用到get(读取)和set(设置):
python
// 先设置一个对象
target = {
name: '嫂子的姐夫',
age: 20
}
// Proxy(原对象, 对象(键值对))
var p = new Proxy(target, {
get: function(target, propertyKey, receiver) { // 读取数据
// target: 代理的对象(原对象)
// propertyKey: 访问器属性
// receiver: 代理器处理对象
console.log(target, propertyKey, receiver);
},
set: function(target, propertyKey, value, receiver) {
// target: 原对象
// propertyKey: 访问器属性
// value: 所访问属性设置的值
// receiver: 代理器处理对象
console.log(target, propertyKey, value, receiver);
}
})
// 调用get
p.name
// 调用set
p.name = '123'
代码运行结果:

这里为什么调用是p.xxx,因为Proxy将原来的target改名(代理为)p,所以调用就写成p,但这样写会很麻烦,因为当(浏览器中扣下来的)js代码中有环境检测比如location这个对象,我们这样写岂不是将location改为了p,这怎么能行,所以此代码要继续改进,做到加进原js代码中不改变原代码逻辑,类似于py中的装饰器效果,功能是打印出所需要的环境
最终代码
这时候我们需要封装一下:
python
function get_enviroment(proxy_array) {
for (var i=0; i<proxy_array.length; i++) {
handler = '{\n' +
' get: function(target, property, receiver) {\n' +
' console.log("方法:", "get ", "对象:", ' +
'"' + proxy_array[i] + '", ' +
'" 属性:", property, ' +
'" 属性类型:", ' + 'typeof property, ' +
'" 属性值类型:", typeof target[property]);\n' +
' return target[property];\n' +
' },\n' +
'set: function(target, property, value, receiver) {\n' +
' console.log("方法:", "set ", "对象:", ' +
'"' + proxy_array[i] + '", ' +
'" 属性:", property, ' +
'" 属性类型:", ' + 'typeof property, ' +
'" 属性值类型:", typeof target[property]);\n' +
' return Reflect.set(...arguments);\n' +
' }\n' +
'}'
// 还原字符串
eval('try {\n' + proxy_array[i] + ';\n'
+ proxy_array[i] + '=new Proxy(' + proxy_array[i] + ', ' + handler + ')}catch (e){\n' + proxy_array[i] + '={};\n'
+ proxy_array[i] + '= new Proxy(' + proxy_array[i] + ', ' + handler + ')}')
}
}
proxy_array = ['window','document','location','navigator','history','screen']
get_enviroment(proxy_array)
// test
document.title
最主要的逻辑就是这一点,只不过在上面变成了字符串模式:

只要理解这部分即可,用的时候直接拿来用就行,学得多了可以自己再加代码来扩充功能,下面我们来实际操作一下吐环境代码
实操吐环境
练习网站:同花顺cookie加密,相关博客:hook-cookie
我们先用hook找一下cookie加密生成的位置:


这里已经有了,直接往前一个栈看:

还是外部传入,继续往前看:

找到生成位置,直接将这个文件复制到本地:

这里要导出生成cookie的方法...吗?其他加密可能需要,但是cookie直接写:document.cookie就能导出:

然后运行看看:

一般代码全扣都有环境检测,所以掏出吐环境代码,一定要放在代码最上面,运行:

这里没有的环境就吐出来了,但是这里还有个问题,我们看见报错就直接开补么,记得之前说过有蜜罐这种东西吗,它有没有一种可能是将前一个环境不存在作为判断条件然后执行之后的代码:

这时候就要回浏览器中看看了,在报错位置打上断点(尽量打子断点)然后看看过不过子断点,翻页触发接口发现直接越过到了这里:

说明这个环境不用补,而是之前那个环境可能要补,那我们直接去网站这样搜:

搜出来是标签,标签都是对象,像这种缺对象的(没有点大家奥)直接补空对象即可,补完后运行:

然后你会发现window中的document属性的属性值为undefined,这是因为我们没有赋值全局变量,document加入不到window中,我们在补环境最开头写上window = global即可:

下面开始看看那两个undefined怎么补,去浏览器看看:

是个方法,打开上面我给的网站JS逆向方法搜索网站搜索一下:

是个方法我们可以在本地这样补:
- 首先打印看看方法的入参是什么:

- 运行发现啥也没有:

那就接着下一个:

其实浏览器就看出来还是个方法,但还可以去那个网站看看这个方法是干啥的:

然后依旧这样补,然后运行:

像这样,运行出来有的就补上,因为返回的是div标签(从上面网站文档看出来的)所以这样写:

然后继续补document.addEventListener:

运行后,我勒个,出来一堆:

然后这种怎么办,先根据报错行号看看怎么个事儿(Crtl+G搜索行号):


然后打断点跟浏览器的对比:


我们的是undefined但是浏览器却有东西,看看p哪儿来的:

我们缺的就是这个,拆开看看怎么来的:

ok那就补上:

这个值等于什么呢:

ok补上:

这里为什么不写成window.location.hostname = 'xxx'呢,因为location不存在根本存不进去(如下图),所以写成对象形式:

window可以不写,因为window = global会自动存,然后继续看报错:

上面先不管,因为好像出值了,但是不对哈哈哈,那怎么办,这里可能是document里的cookie不对,咱还是导出原来的加密函数到全局比较靠谱:




运行看看:

疯狂报错...哈哈,这样补一下:

这里就出值了,但是应该是假值,到时候py验证一下就知道了,这里就不继续往下写了,因为这样补很麻烦,之后会有更简单的方法,这里就是告诉大家有些东西应该怎么补,比如方法就打印入参,有了就补没了补别的,然后其他对象直接去浏览器搜即可,还有一点就是环境补多了也会报错,也不会出值,还有一但有解决不了的就去问浏览器,就像那个补p的时候,有时候会需要我们去跟栈来找浏览器与本地代码不同的原因
小结
本文到此就结束了,文章旨在介绍补环境,让大家了解一下,下一文将另外两种非纯手动补环境的方法(selenium和jsdom),敬请期待,本文如有什么问题请及时提出,加油加油