JavaScript逆向时如何解决格式化反调试

在调试的时候,我们一般都会格式化代码再调试,这时会遇到无限debugger, 或者代码跑着一直不出结果,死循环了。此时一般是用正则去匹配,于是我们可以使用如下hook

1
2
3
4
5
6
7
8
9
10
11
12
13
14
RegExp.prototype.my_test = RegExp.prototype.test

let my_regex = function(arguments) {
debugger ;
console.log(this, arguments)
if (this.source == `test`) {
return true
}
return this.my_test(arguments)
}

Object.defineProperty(RegExp.prototype, 'test', {
value: my_regex
})

后来在处理ob(全称JavaScript Obfuscator)混淆时,发现会使用String里的search函数去匹配,所以我们可以使用如下hook

1
2
3
4
5
6
7
8
9
10
11
12
let my_search = function(arguments) {
debugger ;
console.log(this, arguments)
if (arguments == '(((.+)+)+)+$') {
return false;
}
return this.my_search(arguments)
};

Object.defineProperty(String.prototype, 'search', {
value: my_search
});

测试代码例子

1
(function(_0x2af2c1,_0x5729ae){var _0x3bb80f=_0x2dcf,_0x136599=_0x2af2c1();while(!![]){try{var _0x32921f=parseInt(_0x3bb80f(0xbc))/(-0x2f*-0xb7+-0xc50+-0x38c*0x6)+-parseInt(_0x3bb80f(0xc1))/(-0xe3e+0x1484+-0x644)+parseInt(_0x3bb80f(0xbf))/(0x2bf+0x1*-0x1499+-0x11dd*-0x1)+-parseInt(_0x3bb80f(0x8d))/(0x1ff7+0x19e7+-0x39da)+-parseInt(_0x3bb80f(0xa5))/(0x16e3+0x1451+-0x2b2f)*(parseInt(_0x3bb80f(0xcd))/(-0x394*-0x3+0x908+0x85*-0x26))+-parseInt(_0x3bb80f(0x81))/(0x79*0x1+-0x569*0x1+0x4f7)*(-parseInt(_0x3bb80f(0x96))/(0x20e4+-0x161+-0x1f7b))+parseInt(_0x3bb80f(0xa3))/(-0x1*0x12e1+-0xeda+0x21c4);if(_0x32921f===_0x5729ae)break;else _0x136599['push'](_0x136599['shift']());}catch(_0xf76fcd){_0x136599['push'](_0x136599['shift']());}}}(_0x3e14,0xc9*0x12f8+0x1*-0xeb4d1+-0x952*-0x115));function hi(){var _0xf73b53=_0x2dcf,_0x21b2f2={'UFCQd':_0xf73b53(0x7e),'MyoAf':function(_0x101d17,_0x5db4cf){return _0x101d17(_0x5db4cf);},'WRAzg':function(_0x2fbc41,_0x5c91ad){return _0x2fbc41===_0x5c91ad;},'ROAZT':_0xf73b53(0xc9),'AFrmY':function(_0x3264b7,_0x3c5af7){return _0x3264b7===_0x3c5af7;},'lfcce':_0xf73b53(0x7f),'mzhRj':'kMBpD','AZbGw':function(_0x3bf8f2,_0x26a03f){return _0x3bf8f2!==_0x26a03f;},'oSFRf':_0xf73b53(0xaa),'xmjLA':_0xf73b53(0xcf),'voUVp':_0xf73b53(0x98),'TjDqc':function(_0x324314,_0x498551){return _0x324314+_0x498551;},'MjLdR':'chain','TbfQJ':_0xf73b53(0x92),'LVUpa':_0xf73b53(0xc3),'kLQio':_0xf73b53(0xb0),'ZEGgJ':function(_0x7f45b4){return _0x7f45b4();},'BCKVL':function(_0x2b4677,_0x2db01b){return _0x2b4677!==_0x2db01b;},'yBLFu':_0xf73b53(0x84),'FVWnM':function(_0x5dda0e,_0x3b7a03,_0x51f16c){return _0x5dda0e(_0x3b7a03,_0x51f16c);},'dGGJz':_0xf73b53(0xc2)},_0x1e42b5=(function(){var _0x76ca35=_0xf73b53,_0x2389f8={};_0x2389f8['CKmHh']=_0x21b2f2[_0x76ca35(0xc8)];var _0x187799=_0x2389f8,_0x287f4a=!![];return function(_0x410a03,_0x5485b2){var _0x71193a=_0x76ca35;if(_0x187799['CKmHh']===_0x187799[_0x71193a(0x76)]){var _0x1faa8c=_0x287f4a?function(){var _0x2c5b4d=_0x71193a;if(_0x5485b2){var _0x22c5a5=_0x5485b2[_0x2c5b4d(0xae)](_0x410a03,arguments);return _0x5485b2=null,_0x22c5a5;}}:function(){};return _0x287f4a=![],_0x1faa8c;}else{if(_0x498367){var _0x35b1f7=_0x548caa['apply'](_0x385275,arguments);return _0x28ff30=null,_0x35b1f7;}}};}()),_0xed3405=_0x1e42b5(this,function(){var _0x5de0ef=_0xf73b53,_0x43fad1={'udtfN':function(_0x31b48a,_0x5dd2e9){var _0x58b7c4=_0x2dcf;return _0x21b2f2[_0x58b7c4(0xa0)](_0x31b48a,_0x5dd2e9);}};if(_0x21b2f2[_0x5de0ef(0x83)](_0x5de0ef(0x9d),_0x5de0ef(0x9d)))return _0xed3405[_0x5de0ef(0x91)]()['search']('(((.+)+)+)+$')[_0x5de0ef(0x91)]()[_0x5de0ef(0x79)](_0xed3405)[_0x5de0ef(0x8b)]('(((.+)+)+)+$');else{if(_0x48b81e)return _0x144005;else UZhylY['udtfN'](_0x4ad49c,-0x1*0x1091+0xb8*0x2a+-0xd9f);}});_0x21b2f2[_0xf73b53(0x7d)](_0xed3405);var _0x56c8cd=(function(){var _0x131b47=_0xf73b53,_0x3139cc={'BmvIp':function(_0x133012,_0x3ba194){return _0x133012(_0x3ba194);}};if(_0x21b2f2[_0x131b47(0xa2)](_0x21b2f2[_0x131b47(0x8c)],_0x21b2f2[_0x131b47(0x8c)]))return _0x5082a5;else{var _0x509b4c=!![];return function(_0x1e2cf4,_0x5de51f){var _0x303c02=_0x131b47,_0x339326={};_0x339326['ynxTT']=_0x21b2f2[_0x303c02(0xbe)];var _0x1c595d=_0x339326;if(_0x21b2f2['AFrmY'](_0x21b2f2[_0x303c02(0xb6)],_0x21b2f2[_0x303c02(0x87)]))zKrqIw['BmvIp'](_0x1ed5b8,'0');else{var _0x46aa46=_0x509b4c?function(){var _0x128723=_0x303c02;if(_0x5de51f){if(_0x1c595d[_0x128723(0x7b)]===_0x128723(0xc9)){var _0x10e124=_0x5de51f[_0x128723(0xae)](_0x1e2cf4,arguments);return _0x5de51f=null,_0x10e124;}else{var _0x55a8b6=_0x29c4c5[_0x128723(0xae)](_0x59aea0,arguments);return _0x57be67=null,_0x55a8b6;}}}:function(){};return _0x509b4c=![],_0x46aa46;}};}}());(function(){var _0x312c6e=_0xf73b53,_0x45911c={'TnjGM':_0x21b2f2[_0x312c6e(0xa1)],'XKRUY':_0x21b2f2[_0x312c6e(0x8f)],'nblnZ':function(_0x5ea134,_0x22ac32){var _0x4dd84a=_0x312c6e;return _0x21b2f2[_0x4dd84a(0xa7)](_0x5ea134,_0x22ac32);},'ixPsX':_0x21b2f2[_0x312c6e(0x89)],'sFaqQ':function(_0x1d84b0,_0x175a50){var _0x53008b=_0x312c6e;return _0x21b2f2[_0x53008b(0xa7)](_0x1d84b0,_0x175a50);},'GGhtA':_0x21b2f2[_0x312c6e(0xb1)],'MCDgD':function(_0x3798ef,_0x4f4d6d){var _0x52436e=_0x312c6e;return _0x21b2f2[_0x52436e(0xa0)](_0x3798ef,_0x4f4d6d);},'MFqRh':function(_0x34d08b,_0x16a70d){var _0xa5bb1a=_0x312c6e;return _0x21b2f2[_0xa5bb1a(0xbd)](_0x34d08b,_0x16a70d);},'bmGCL':_0x21b2f2[_0x312c6e(0xca)],'eRpRS':_0x21b2f2['kLQio'],'aavYG':function(_0x46b596,_0x3f8783){var _0x18449a=_0x312c6e;return _0x21b2f2[_0x18449a(0xa0)](_0x46b596,_0x3f8783);},'RqLAW':function(_0x425f24,_0x48d93b){var _0x17c616=_0x312c6e;return _0x21b2f2[_0x17c616(0xa7)](_0x425f24,_0x48d93b);},'zEcNC':function(_0x153ba3,_0x369022){var _0x21d816=_0x312c6e;return _0x21b2f2[_0x21d816(0xa0)](_0x153ba3,_0x369022);},'gQVRD':function(_0x30d32b){var _0x5336d2=_0x312c6e;return _0x21b2f2[_0x5336d2(0x7d)](_0x30d32b);}};if(_0x21b2f2[_0x312c6e(0xcb)]('WXJYq',_0x21b2f2[_0x312c6e(0xaf)]))_0x21b2f2['FVWnM'](_0x56c8cd,this,function(){var _0x24f277=_0x312c6e,_0x361240=new RegExp(_0x24f277(0xb0)),_0x5a44c3=new RegExp(_0x45911c[_0x24f277(0xc4)],'i'),_0x57fe67=_0x269895(_0x45911c['XKRUY']);if(!_0x361240[_0x24f277(0x77)](_0x45911c[_0x24f277(0x97)](_0x57fe67,_0x45911c[_0x24f277(0xd0)]))||!_0x5a44c3[_0x24f277(0x77)](_0x45911c['sFaqQ'](_0x57fe67,_0x45911c[_0x24f277(0x9b)])))_0x45911c[_0x24f277(0x90)](_0x57fe67,'0');else{if(_0x45911c[_0x24f277(0x82)](_0x45911c[_0x24f277(0xd2)],_0x45911c[_0x24f277(0xd2)]))_0x269895();else return!![];}})();else{var _0x51bda3=new _0xc2f8e6(cFtQEd[_0x312c6e(0x9f)]),_0x2ebe74=new _0x3fd708(cFtQEd[_0x312c6e(0xc4)],'i'),_0x15e45a=cFtQEd[_0x312c6e(0xa9)](_0x1f753d,cFtQEd[_0x312c6e(0xce)]);!_0x51bda3[_0x312c6e(0x77)](cFtQEd[_0x312c6e(0x8e)](_0x15e45a,_0x312c6e(0x94)))||!_0x2ebe74[_0x312c6e(0x77)](cFtQEd[_0x312c6e(0xc6)](_0x15e45a,_0x312c6e(0x92)))?cFtQEd[_0x312c6e(0x85)](_0x15e45a,'0'):cFtQEd[_0x312c6e(0xad)](_0x2d7a45);}}()),console[_0xf73b53(0x8a)](_0x21b2f2[_0xf73b53(0xc0)]);}hi();function _0x2dcf(_0x2d85e5,_0x3e142d){var _0x2dcfdd=_0x3e14();return _0x2dcf=function(_0x4031e1,_0x1c832f){_0x4031e1=_0x4031e1-(-0x1*0x1811+-0x5e0+-0xb5*-0x2b);var _0x437a54=_0x2dcfdd[_0x4031e1];return _0x437a54;},_0x2dcf(_0x2d85e5,_0x3e142d);}function _0x269895(_0x17c37a){var _0x383aa8=_0x2dcf,_0x513047={'UunGd':'while\x20(true)\x20{}','oTKoG':function(_0x46b359){return _0x46b359();},'kujec':function(_0x4c8d0a,_0x7c2720){return _0x4c8d0a===_0x7c2720;},'xTWRH':'string','MpkIy':_0x383aa8(0x80),'ztVyI':_0x383aa8(0xbb),'JjyaY':function(_0xe7f4ec,_0x411f53){return _0xe7f4ec!==_0x411f53;},'ISYBp':function(_0x2f93af,_0x4f469d){return _0x2f93af+_0x4f469d;},'AoJnv':function(_0x9c3eb1,_0x3ab274){return _0x9c3eb1/_0x3ab274;},'dzBMz':_0x383aa8(0x95),'ZeMPL':function(_0x85fc17,_0x116a9d){return _0x85fc17===_0x116a9d;},'vLeSp':function(_0x2e0238,_0x30f91b){return _0x2e0238%_0x30f91b;},'UsdIb':_0x383aa8(0xab),'bdAFw':_0x383aa8(0x7a),'zqbtZ':'PSIMG','ESQZC':_0x383aa8(0xa6),'BEImO':function(_0x27e063,_0x48d43d){return _0x27e063(_0x48d43d);},'Ygggu':function(_0x16eaaa,_0x45973b){return _0x16eaaa+_0x45973b;},'SbTwP':function(_0xfb207,_0x1fbfd5){return _0xfb207!==_0x1fbfd5;},'iwHWR':_0x383aa8(0xcc),'pDruh':_0x383aa8(0xb7),'riBfp':_0x383aa8(0x78)};function _0x360295(_0x401127){var _0x1918fd=_0x383aa8,_0x15a3a7={'ksnwb':function(_0x2796bd){var _0x22b7dc=_0x2dcf;return _0x513047[_0x22b7dc(0xa4)](_0x2796bd);}};if(_0x513047['kujec'](typeof _0x401127,_0x513047[_0x1918fd(0xb9)])){if(_0x513047[_0x1918fd(0xb8)]==='TCevo')return function(_0x110f7c){}['constructor'](_0x513047[_0x1918fd(0xba)])[_0x1918fd(0xae)](_0x513047['ztVyI']);else _0x15a3a7[_0x1918fd(0x7c)](_0x42d3db);}else{if(_0x513047[_0x1918fd(0x9c)](_0x513047['ISYBp']('',_0x513047['AoJnv'](_0x401127,_0x401127))[_0x513047[_0x1918fd(0xa8)]],-0x1ddf+0x1*0x1737+0x6a9)||_0x513047[_0x1918fd(0xac)](_0x513047[_0x1918fd(0xc5)](_0x401127,-0xd*-0x2f9+0x1edc+-0x456d),-0x194b+-0x9e*0x1a+0x2957))(function(){return!![];}[_0x1918fd(0x79)](_0x513047[_0x1918fd(0x9e)](_0x513047['UsdIb'],_0x513047['bdAFw']))['call'](_0x1918fd(0xb5)));else{if(_0x1918fd(0x9a)!==_0x513047['zqbtZ'])return function(_0x8874f6){}[_0x1918fd(0x79)](_0x513047['UunGd'])[_0x1918fd(0xae)](_0x1918fd(0xbb));else(function(){return![];}[_0x1918fd(0x79)](_0x513047[_0x1918fd(0x93)]+_0x513047['bdAFw'])['apply'](_0x513047['ESQZC']));}}_0x513047[_0x1918fd(0xb4)](_0x360295,++_0x401127);}try{if(_0x513047[_0x383aa8(0x99)](_0x513047[_0x383aa8(0xc7)],_0x513047[_0x383aa8(0xc7)])){if(_0xcb4e6f){var _0x1371b4=_0x4f20a1['apply'](_0x169a52,arguments);return _0x239d19=null,_0x1371b4;}}else{if(_0x17c37a){if(_0x513047[_0x383aa8(0x88)](_0x513047[_0x383aa8(0xd1)],_0x513047[_0x383aa8(0xb3)]))(function(){return![];}[_0x383aa8(0x79)](_0x513047[_0x383aa8(0x86)](_0x383aa8(0xab),_0x513047[_0x383aa8(0xb2)]))[_0x383aa8(0xae)](_0x513047['ESQZC']));else return _0x360295;}else _0x360295(0x85*0x29+0x1ec4+-0x3411);}}catch(_0x40afaa){}}function _0x3e14(){var _0x27d5c8=['gQVRD','apply','yBLFu','function\x20*\x5c(\x20*\x5c)','TbfQJ','bdAFw','riBfp','BEImO','action','lfcce','ovfQC','MpkIy','xTWRH','UunGd','counter','102391pVtUEo','AFrmY','ROAZT','1708764PpQjfF','dGGJz','1447240llIKmg','Hello\x20World!','sqPIM','TnjGM','vLeSp','RqLAW','iwHWR','UFCQd','rVXrP','LVUpa','BCKVL','biVJU','156wSHMlL','XKRUY','\x5c+\x5c+\x20*(?:[a-zA-Z_$][0-9a-zA-Z_$]*)','ixPsX','pDruh','bmGCL','CKmHh','test','RiXmF','constructor','gger','ynxTT','ksnwb','ZEGgJ','rGDQd','pWsSI','TCevo','5492963MuBiwe','MFqRh','WRAzg','lgxpj','zEcNC','Ygggu','mzhRj','kujec','MjLdR','log','search','oSFRf','1142044wixnje','sFaqQ','voUVp','MCDgD','toString','input','UsdIb','chain','length','8KWnAqN','nblnZ','init','SbTwP','PSIMG','GGhtA','JjyaY','Rqovk','ISYBp','eRpRS','MyoAf','xmjLA','AZbGw','13580892MZUafl','oTKoG','246800hjZxkz','stateObject','TjDqc','dzBMz','aavYG','qlTLb','debu','ZeMPL'];_0x3e14=function(){return _0x27d5c8;};return _0x3e14();}

这段代码如果格式化之后,是不会有输出结果,一直卡着不动,加上hook代码后看到 arguments 的值是(((.+)+)+)+$,这正则就是典型的正则攻击。所以我们可以修改hook函数,当arguments 为(((.+)+)+)+$时,直接返回false, 我们就能看到输出结果了。

还有一些格式化检测手段会根据toString后的结果来进行解密,一旦格式化,解密函数就会失效,代码就执行不了,这个情况就得具体问题具体分析了。

联系作者

24年下半年算是比较密集搞APP的一段时间,不得不说,Android逆向要学的东西比Web真是多多了。Web拿着浏览器就开始调试了,Android得学习一堆工具。Android逆向要解决的问题也比Web多多了,连抓包都是个问题。每次都是直接上强度,20年刚来做爬虫时直接就开搞rs, 24年Android逆向时直接就是企业级, 像libsgmain.so, libmpaas_crypto.so这种。好的一点是遇到的难点都是加密,没有风控,要不然就更头疼了。

这里列举下Andorid逆向遇到的问题,root检测,Frida反调试(因为我主要是用Frida分析,用Xposed的话也会遇到Xposed反调试), SSL pinning机制,加固壳, so加密。真是一步一个坑,举步维艰。还好有好多开源的工具, 有大佬们的星球可以学习,要不然真的累麻了。下面记录下每一步都是如何解决的,以供参考。

root检测

最开始是用Magisk里的Shamiko模块,但发现这个过不去一些检测,后面了解到还有kernelSU和APatch,因为手上都是一些便宜手机,带不动kernelSU,就用APatch, 能解决问题。

Frida反调试

大都是通过hook pthread_create解决,搞不定就请大佬出山。这里重点推荐霜哥的星球。

SSL pinning机制

之前APP抓包进阶里写过,用DroidSSLUnpinning完事。

加固壳

开源的Fart,Fartext,MikRom试试,搞不定的话只好请小黄鱼大神解决了

so加密

用Unidbg一把梭,Unidbg搞不定的话,只能慢慢分析so了,我也不会了。这里推荐白龙的星球,写的真的细。

根据https://www.52pojie.cn/thread-1244902-1-1.html 这篇文章写的,现在停留在基础技能的阶段,后续有时间再学习了。遇到问题多去Github, 看雪,吾爱破解找找。

参考资料

联系作者

2025年了,卷逆向越来越没意思了,但终归要吃饭,得有备用方案在手上才不慌。万一某数今年更新了,上一些类似window[“Object”]“getOwnPropertyNames”等奇奇怪怪的检测,一时半会搞不定咋办。要知道document.all刚出来的时候,卡了好久, 要不是大佬出手,不知道还得卡多少天。于是测试了下别人家的方案,多备几套方案在手上。

sdenv

项目地址https://github.com/pysunday/sdenv,根据example/use-local/index.js示例,给它做成服务,跑的挺稳,速度也挺好,在m1上50毫秒,普通服务器上估计300毫秒,用在生产环境中需解决内存泄漏问题。

qxvm

项目地址https://github.com/ylw00/qxVm,根据z_working/rs4Vm.js示例,给它做成服务, 能跑起来。但换成vmp版本的话,跑不起来,因为dom操作不全, 没法用。

node-sandbox

项目地址https://github.com/bnmgh1/node-sandbox,根据demo例子main.js, 给它做成接口服务能跑起来,打印的调用记录也很详细。但换了个网站就卡住了,不知道错在哪里,也不知道怎么排查,因为是魔改的node, 不知道怎么使用node-inspect功能。

cynode

卷卷大陆知识星球里的一个补环境框架,按照demo例子修改后能跑起来,打印的调用记录也很详细,但生成速度很慢,生成一个cookies要3秒,不知道问题出在哪里,得慢慢排查。

浏览器渲染方案

用playwright搞了个浏览器渲染服务,生成cookies速度2.5秒,将就能用

结论

目前来看,sdenv最舒服,又快又稳。浏览器渲染方案将就能用,能再优化下速度就更好了。cynode将就能用,解决了速度慢问题的话那就更舒服了。 node-sandbox如果能知道怎么调试,找到问题,应该能用。qxvm是没法用了,dom树操作不全。

联系作者

这几天一直在处理APP,遇到了libsgmain.so, 问了大佬和找资料后,基于Unidbg的demo跑通了,于是做个简单记录。

  1. 代码里找不到核心加密接口和类,Frida里也hook不到(如ISecureSignatureComponent接口的实现类,JNICLibrary类),是因为这些代码是动态注册的。如何解决看参考资料1
  2. libsgmain.so是假so, 本质上是jar, 在jadx里打开就能看到核心加密类,真正的so文件也在这假so里,形如libsgmainso-*.so
  3. doCommandNative是真正加密的方法,这个方法也用于初始化,至于初始化多少次,看so文件难度了, 我要处理的so相对简单,只有一次初始化,缺啥补啥完事。可以看参考资料2。
  4. Unidbg真是大杀器,so加密小白福音。

参考资料

  1. 奋飞佬的某A系电商App x-sign签名分析
  2. 意识存在感的阿里系某电影票务APP加密参数还原-Unidbg篇

联系作者

remove debugger

有些时候想移除debugger

1
2
3
4
5
6
7
8
_Function = Function;
Function.prototype.constructor = function(){
if (arguments[0].indexOf('debugger') != -1){
//debugger;
return _Function('');
}
return _Function(arguments[0]);
};

hook cookie

我们需要快速定位cookie在哪里生成的,需要hook cookie

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
var cookie_cache = document.cookie;
Object.defineProperty(document, 'cookie', {
get: function() {
debugger;
console.log('Getting cookie');
return cookie_cache;
},
set: function(val) {
debugger;
console.log('Setting cookie', val);
var cookie = val.split(";")[0];
var ncookie = cookie.split("=");
var flag = false;
var cache = cookie_cache.split("; ");
cache = cache.map(function(a){
if (a.split("=")[0] === ncookie[0]){
flag = true;
return cookie;
}
return a;
})
cookie_cache = cache.join("; ");
if (!flag){
cookie_cache += cookie + "; ";
}
this._value = val;
return cookie_cache;
},
});

hook eval

有时候我们需要快速定位vm.js在哪里生成的,需要hook eval

1
2
3
4
5
6
7
8
9
window.__eval = window.eval
var myeval = function(src) {
debugger ;
console.log('=======eval====')
return window.__eval(src)
}
Object.defineProperty(window.prototype, 'eval', {
value: myeval
})

hook random

有时候我们需要固定随机参数,需要hook random

1
2
3
4
old_random = Math.random
window.Math.random = Math.random = function () {
return 0.3
};

hook regex

有时候我们需要知道代码如何进行格式化检测,需要hook 正则

1
2
3
4
5
6
7
8
9
10
11
 RegExp.prototype.my_test = RegExp.prototype.test

let my_regex = function(arguments) {
debugger ;
console.log(this, arguments)
return this.my_test(arguments)
}

Object.defineProperty(RegExp.prototype, 'test', {
value: my_regex
})

联系作者

Node服务设置–max-old-space-size参数

可以先问问GPT这个参数是做什么的

–max-old-space-size 是 Node.js 运行时的一个参数,它用来设置 V8 引擎的老生代内存限制。
V8 的内存空间主要分为新生代空间(Young Generation)和老生代空间(Old Generation),这两种空间的大小决定了应用程序可以使用的最大内存。新生代空间通常较小,并且垃圾回收频率较高;而老生代空间较大,并且垃圾回收频率较低。
当你设置了 –max-old-space-size 的值,你就在 V8 引擎运行时告诉它,它在老生代空间可以使用的最大内存量。如果你的应用程序尝试超过这个限制,V8 引擎会抛出一个 FATAL ERROR: Reached heap limit Allocation failed 的错误,表示内存已用尽,无法分配新的内存。
请注意,–max-old-space-size 只是 V8 引擎可以使用的总内存的一个部分,还有其他部分是由 V8 内部使用的,例如它的执行代码和堆栈空间等。所以,如果你的系统内存较少,你可能会需要减小 –max-old-space-size 的值,以确保其他重要的系统进程也能正常运行。

在PM2中我们就可以如此启动服务 pm2 start server.js -i 2 –node-args=”–max-old-space-size=800” –no-daemon

–max-old-space-size 这个参数比 –max-memory-restart 好用多了,它会尝试进行内存回收,能回收了,就不重启进程,不能回收就才会重启进程,这样可以减少 502 服务器错误。

有了这个参数,也不需要再使用强制内存回收插件了,因为本身自带内存回收。唯一的问题是,PM2守护的进程内存都是从0开始,然后进程一起到达设置的老生代内存大小,此时CPU占比会很高,可能会影响服务执行。

联系作者

未雨绸缪,虽然有了补环境通用方案,但为了应对可能更高的QPS需求,一直在想办法储备更快的cookies生成方案。简单计算下,假设需要400 QPS,而一般的补环境耗时250ms, 那么一个CPU一秒可以处理4个请求,400 QPS就需要100核CPU, 这对资源的消耗就很可观了。如果有个方案能50ms生成一个cookies, 那么就只需要20核CPU就能满足需求。

寻寻觅觅,在如画佬的星球里找到一个补环境方案,环境代码极少。示例代码是在Python中使用execjs来执行生成cookies, 改成Node服务后执行速度非常理想,能做到80ms生成一次。

美中不足的是需要在Node中使用eval执行js代码,会导致内存泄漏,这种内存泄漏使用强制内存回收也回收不了,最后程序会奔溃,用PM2限制内存使用和自动重启可以解决。但不可避免会出现502服务器错误,这在我们公司的生成环境中就不合适,因为运维一旦发现502超过一定数量就得让解决,得寻求其它方案。

要想又快又稳,想来只有算法方案了。

联系作者

之前在群里就看到有道友在讨论这个问题,但因为没有遇到过这个问题,就没细看,这次自己也遇到了,就认真看了下。

在寻找高效的某数 cookies 生成方案时,看到如画佬星球的补环境方案后,深有感触。最近两年,虽然把补环境框架搞的越来越完善,但同时执行速度也越来越慢,想想似乎有点背离补环境的初衷。上一次yidun的加密生成方案尤其明显,补环境方案把整个CPU都打满了,后面搞了个补环境和扣代码结合的方案后跑的就稳稳的,两者之间性能整整差了10倍。

于是就尝试这种最开始学习的补环境方案,缺啥补啥,争取补的环境每一行代码都是有用的,然后就遇到这个 ActiveXObject 问题了。在 proxy 输出日志中,看到有 ActiveXObject 调用日志,于是在浏览器里看,window.ActiveXObject 就是 undefined, 在Node里,window=global 后,window.ActiveXObject 也是 undefined,于是就没管 ActiveXObject 。但生成的 cookies 就是过不去,只好和如画佬的代码做对比,发现就是因为他的代码有 window.ActiveXObject=undefined,所以能过。

于是去了解 ActiveXObject,发现它是IE才有的对象,在IE中,’ActiveXObject’ in window 是true, 而在 Chrome 中 ‘ActiveXObject’ in window 是 false。如果在检测代码中有 ‘ActiveXObject’ in window 这种检测,那么补环境代码中有没有 window.ActiveXObject=undefined 就会影响检测代码执行路径了。在Node中加上 window.ActiveXObject=undefined 后,’ActiveXObject’ in window 也就成了 true 了,检测代码就会认为是 IE浏览器,就会按照IE浏览器来检测。手头上方便调试的只有Chome浏览器,就再继续探究后面如何检测的。神奇的是,我是在Mac上用Chrome调试的代码,最后还得伪装成IE才能通过检测,就离谱。

回过头来看代理日志,有针对in操作符的代理has=> [window] 有无属性名 => [ActiveXObject], 结果 => [false] 这行,就是 代理has输出日志,说明检测代码里有 ‘ActiveXObject’ in window 的检测,只是没注意到。

联系作者

本来是想问ChatGPT在Node.js里如何读取Redis的,问着问着,就拿如何解决document.all问题去问ChatGPT, 顺便测试下其它几个厂商给出的回答,其它几个都是挺常规的回答,只有Copilot给出的回答出乎意料,因为它的回答是基于我的当补环境遇到document.all文章给的。

Copilot 给的回答是

在解决 jsdom 被针对的逆向过程中,遇到了 document.all 的问题。尽管 document.all 的类型是 undefined,但却能够获取值,这让人感到非常困惑。不过,我们有一些解决办法:

  1. 不补环境:有时候网站的校验可能不严格,直接绕过补环境步骤也许就能成功。
  2. 扣算法:这是一种绕过各种检测的方法,直接将值写死。不过,这需要一定的技巧。
  3. 使用 V8 中修改后的不可检测对象:这是风佬提出的方法,可以完美解决问题。1
  4. 请大佬们出山,共同探讨解决方案。
    总之,解决这个问题的途径有很多,我们可以根据具体情况选择合适的方法。

我的文章里说到:

目前看来,解决这个问题的办法有如下几种

  1. 直接不补了,有可能网站校验不严格,直接可以过
  2. 扣算法,这样绕过各种检测,直接把值写死,就是扣算法难搞。
  3. 用风佬修改后的Node
  4. V8里的不可检测对象
  5. 请大佬出山帮你解决

惊不惊喜,意不意外。阿龙的学习笔记作为个人学习笔记从13年就开始写,有一年还因为域名续费失败的原因,导致域名被抢注而换到现在的域名,但还是坚持独立博客写下来,不管有没有人看。最近一份工作的主要工作内容是Web逆向,写了很多补环境相关的技术文章后收到一些道友的关注,并加联系方式讨论相关问题,还收到一位不知名字道友的20块钱巨赏, 甚是鼓舞。今天看到自己的文章被AI引用,甚是开心。

还是那句话,兼济天下则达,独善其身则穷。

联系作者

很多道友使用jsdom来补环境,但没有设置好userAgent(以下简称ua), 如果生成的token中有使用到ua, 反爬人员就可以通过分析token发现是由jsdom生成的,于是针对jsdom的特征做检测。所以使用jsdom时,设置ua很重要。

下面我们来看看怎么设置ua, 网上常见的设置ua代码如下

1
2
3
4
5
6
7
8
9
10
const jsdom = require("jsdom");
const { JSDOM } = jsdom;
const userAgent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
const dom = new JSDOM(``, {
url: "https://example.org/",
referrer: "https://example.com/",
userAgent: userAgent,
});
window = dom.window
console.log(window.navigator.userAgent)

输出结果为 Mozilla/5.0 (darwin) AppleWebKit/537.36 (KHTML, like Gecko) jsdom/24.0.0, 有jsdom特征,与设置的ua不一致。

尝试添加如下代码

1
2
window.navigator.userAgent = userAgent
console.log(window.navigator.userAgent)

输出结果依然为 Mozilla/5.0 (darwin) AppleWebKit/537.36 (KHTML, like Gecko) jsdom/24.0.0

继续尝试增加如下代码

1
2
3
4
navigator = {
userAgent: userAgent
}
console.log(window.navigator.userAgent)

输出结果依然为 Mozilla/5.0 (darwin) AppleWebKit/537.36 (KHTML, like Gecko) jsdom/24.0.0

查看官方文档,使用ResourceLoader可以修改ua, 代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
const jsdom = require("jsdom");  // 引入 jsdom
const { JSDOM } = jsdom; // 引出 JSDOM 类, 等同于 JSDOM = jsdom.JSDOM
const userAgent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
const resourceLoader = new jsdom.ResourceLoader({
userAgent: userAgent
});
const dom = new JSDOM(``, {
url: "https://example.org/",
referrer: "https://example.com/",
resources: resourceLoader,
});
window = dom.window
console.log(window.navigator.userAgent)

输出结果为 Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36, 已经没有jsdom的特征,目标达成。

联系作者