什么是jsvmp
目前,主流的JavaScript代码保护措施主要包括精简、加密和混淆。这些方法的核心思想主要借鉴了传统的软件代码保护技术。然而,由于JavaScript是一种脚本语言,其在传输过程中以带有语法属性的文本源码形式存在,逆向分析比传统的编译后的二进制应用程序更加容易。再加上浏览器性能的提升和调试器功能的日益完善,这些保护方法难以提供有效的保护。
jsvmp(JavaScript Virtual Machine Protection)是一种用于保护JavaScript代码的技术,类似于我们平常看到的代码混淆。但不同的是,它通过将JavaScript代码转换为一种虚拟机指令集来实现代码混淆和保护,具体的执行逻辑由vmp虚拟机来执行,这样可以防止代码被轻易地反编译和理解,从而提高代码的安全性。
对于普通的javascript代码来说,执行逻辑是这样的:
JavaScript代码 -> 词法分析/语法分析 -> 生成AST语法树 -> 生成js指令-> js引擎执行代码
而对于经过vmp混淆后的代码,执行逻辑是这样的,中间多了这么一段:
…… -> 生成AST语法树 -> 将AST转换为vmp指令集 -> 加载进vmp虚拟机 -> js引擎执行代码
在vmp混淆的代码流程中,防护者实现了一个虚拟机来解释和执行这些指令集,并对对虚拟机和指令集进行混淆,这种工作类似于将高级语言编译为汇编的过程,因此,对混淆代直接人肉分析几乎是不可能的
(图片来自梓潼blog)
为什么不用wasm
有些人可能会问,为什么wasm呢?wasm不是能直接编译成字节码,并高效运行吗?
wasm有一个致命的弱点,即wasm和js运行时的环境是隔离的,本身无法直接访问js对象的属性,只能以模块的方式导入导出,这个就决定了它在收集环境参数上受限很大。2020年就有使用wasm的网站,几年过去后,也并未出现大的发展。其次,wasm在执行时必须借助js实现对属性的操作部分,这样频繁的在wasm和js之间交互肯定会带来性能的影响,降低执行效率。同时还需要附带大量代码负责维护两个模块的通信,这势必会带来新的空间开销。
此外,目前可以发现,国内各大厂商的vmp实现几乎天差地别,难以出现通用的反混淆方案,jsvmp的虚拟机设计可以灵活地适应不同的保护需求,相比ast混淆的方式,能够提供更高的安全性,现在对于js混淆来说,已经有了成熟的解混淆方案。尽管在性能上可能会有一定的开销,但在保护代码安全性方面,jsvmp无疑是一个强有力的工具。
jsvmp示例
你能看出这段代码在干什么吗?
function _0x4dff2d(_0x1935a8) {
return ('undefined' == typeof window ? global : window)['_$webrt_1668687510']('484e4f4a403f5243002c210c79d545fd138caff600000000000007561b000b180201fe19203e17000e1b000b180201ff0202000d1b00131e00061a002248001d00a922201d0201220a00001d0202220a00001d020322121d01e222121d01fe220200001d01ff22121d020422131e00061a00224805483c2a1d02052248021d011f224805483c2a1d011d1d011c220200001d01f322201d02062248031d02071d00911b000b02221e0126241b000b191b000b180a0002101c1b000b191e00a948003e22011700201c1b000b03221e0108241b000b191e00a90a0001101b000b191e00a94017000d1b000b040202081a01471b000b191e02092217000c1c1b000b191e020a0117000d1b000b0402020b1a01471b000b051e0123221e00ce241b000b191e00a90a0001101c1b000b061e00a948003e1700111b000b061b000b191e00a91d00a91b000b191e0201011700c11b000b191e01ff0200003e17000d1b000b0402020c1a01471b000b191e01ff0202003f17000d1b000b0402020d1a01471b000b061b000b191e01ff1d01ff1b000b061b000b071b000b19041d01291b000b08261b000b091b000b191e02074903e82a0a0002101c1b000b191e01301b000b0a402217000a1c1b000b1926401700401b000b061e020e221e00ce241b000b191e01300a0001101c1b000b061b000b061e020e221e020f24020000250009180018012f000a00011001011d01301b000b191e011c1700d61b000b06201d02101b000b0b17007f1b000b191e011c1e0205221700191c1b000b191e011c1e02051b000b061e011c1e0205391700551b000b0c1b000b0b041c1b000b061b000b02221e012624131e00061a001b000b061e011c1b000b191e011c0a0003101d011c1b001b000b0d261b000b0e1b000b061e011c1e02054903e82a0a0002101d002716004b1b000b061b000b02221e012624131e00061a001b000b061e011c1b000b191e011c0a0003101d011c1b001b000b0d261b000b0e1b000b061e011c1e02054903e82a0a0002101d00271b000b191e02111700251b000b061b000b191e02111d02111b000b08261b000b0f48054903e82a0a0002101c111b000b191d02121b000b10260a0000101c1b000b111b000b191e0202041c1b000b121b000b191e0203041c1b000b13260a0000101c1b000b14012217000b1c1b000b191e01e217002b1b00201d00841b000b061b000b191e01e21d01e21b000b08261b000b1548054903e82a0a0002101c1b000b191e020617002e1b000b061e0213011700231b000b06201d02131b000b08261b000b16480a4903e82a1b000b190a0003101c1b000b06201d0214000215000160203333333333333333333333333333333333333333333333333333333333333333016d0e3130333c3b3005273a253027212c023c31061a373f3036210332302108313037203232302707303b23363a313007363a3b263a393007333c27303720320a3a20213027023c31213d0a3c3b3b3027023c31213d0b3a202130271d303c323d210b3c3b3b30271d303c323d2109202630271432303b210b213a193a223027163426300163073c3b31302d1a33083039303621273a3b09203b3130333c3b30310925273a213a212c253008213a0621273c3b3204363439390725273a36302626100e3a373f3036217525273a3630262608063a373f30362105213c213930043b3a31300168016202266541141716111013121d1c1f1e19181b1a05040706010003020d0c0f343736313033323d3c3f3e39383b3a25242726212023222d2c2f65646766616063626d6c7e7a6802266441113e3125323d610f1e2604176d657a1833232266630d1c640767607e02001439103c621b19373a240c011a05202f38133f1f3b272c2d6c1d03123634062116306802266741113e3125323d610f1e2604176d657a1833232266630d1c640767607802001439103c621b19373a240c011a05202f38133f1f3b272c2d6c1d031236340621163068016c0264640639303b32213d0a363d3427163a3130142102646506363d342714210f0e3a373f30362175023c3b313a220808113a362038303b21120e3a373f303621751b34233c3234213a2708053f26313a38100e3a373f303621751d3c26213a272c0807253920323c3b26080a253d343b213a380b36343939053d343b213a380b0a0a3b3c323d2138342730051420313c3a1816343b23342607303b3130273c3b32163a3b21302d2167110922303731273c233027133230211a223b05273a253027212c1b343830260939343b32203432302606363d273a38300727203b213c383007363a3b3b303621140a0a22303731273c2330270a3023343920342130130a0a263039303b3c20380a30233439203421301b0a0a22303731273c2330270a2636273c25210a33203b36213c3a3b170a0a22303731273c2330270a2636273c25210a33203b36150a0a22303731273c2330270a2636273c25210a333b130a0a332d31273c2330270a3023343920342130120a0a31273c2330270a203b22273425253031150a0a22303731273c2330270a203b22273425253031110a0a31273c2330270a3023343920342130140a0a263039303b3c20380a203b22273425253031140a0a332d31273c2330270a203b22273425253031090a263039303b3c20380c36343939063039303b3c2038160a063039303b3c20380a1c11100a0730363a2731302702646708313a362038303b21043e302c2602646602646102646002646305383421363d06073032102d250a09710e34782f0831360a063634363d300a04263a383008363033063d34272508163033063d34272505303a34253c16303a02303717273a22263027113c26253421363d30270f373c3b311a373f30362114262c3b360e3c26101a02303717273a222630270166043a25303b0421302621093c3b363a323b3c213a073a3b3027273a2704363a31301204001a01140a100d1610101110110a1007070e263026263c3a3b06213a27343230072630211c21303810263a38301e302c1d302730172c2130310a2730383a23301c213038093c3b31302d303111170c053a3c3b2130271023303b210e1806053a3c3b2130271023303b210d36273034213010393038303b210636343b23342609213a1134213400071907273025393436300309267f01320a3b34213c2330363a3130140e3a373f30362175053920323c3b142727342c084a0b3d212125266a6f097a097a7d0e65786c082e647966287d097b0e65786c082e647966287c2e6628290e34783365786c082e647961287d6f0e34783365786c082e647961287c2e62287c016108393a3634213c3a3b043d27303304333c3930103d2121256f7a7a393a3634393d3a26210825393421333a273807223c3b313a222603223c3b07343b31273a3c3105393c3b202d026462063c253d3a3b3002646d043c25343102646c043c253a3102676503383436026764093834363c3b213a263d0c3834360a253a22302725367c0436273a26032d64640536273c3a2605332d3c3a2604253c3e3002676702676602676102676002676302676202676d02676c08333c2730333a2d7a063a253027347a05753a25277a05753a25217a07363d273a38307a0821273c31303b217a0438263c300266650266640623303b313a2706123a3a3239300e0a253427343806223c21363d1a3b0a313c27303621063c323b0a363a3b263c2621303b210626223c21363d03313a3807253d343b213a38043d3a3a3e40141716111013121d1c1f1e19181b1a05040706010003020d0c0f343736313033323d3c3f3e39383b3a25242726212023222d2c2f65646766616063626d6c787b03343c31013b01330127092621342721013c383001210934373c393c213c30260a213c3830262134382564133d34273122342730163a3b36202727303b362c0c3130233c36301830383a272c0839343b32203432300a2730263a3920213c3a3b0f3423343c390730263a3920213c3a3b0926362730303b013a250a26362730303b19303321103130233c3630053c2d30390734213c3a0a25273a31203621062037073734212130272c012509213a20363d1c3b333a08213c38302f3a3b300a213c3830262134382567073225201c3b333a0b3f26133a3b2126193c26210b253920323c3b26193c26210a213c38302621343825660a30233027163a3a3e3c300721210a26363c3101380b262c3b21342d1027273a270c3b34213c233019303b32213d052721361c05093325033027263c3a3b0b0a0a233027263c3a3b0a0a0836393c303b211c310a213c38302621343825610b302d21303b31133c303931042520263d0334393904213d303b093734263063610a363d0c33273a38163d3427163a3130063763610a6665083734263063610a65026667083734263063610a64026666083734263063610a6702666102666002666307323021013c38300266620b313a381b3a210334393c31092620372621273c3b320825273a213a363a3902666d02666c02616502616401650e646565656565656564646565656502616702616607333a272730343902616104373a312c092621273c3b323c332c022e280261600b373a312c033439672621270a373a312c0a3d34263d6801730320273902616305242030272c0a34263a39310a263c323b092534213d3b343830680921210a2230373c3168067320203c316802616202616d0e0a372c2130310a2630360a313c3102616c0a61676c616c6362676c63093330033027263c3a3b0260650e0a656717610f63223a65656565640260640260670526393c3630026066070610161c1b131a033b3a2209213c383026213438250533393a3a270627343b313a380f3230210101023037163a3a3e3c3026052121223c310821210a2230373c310721210230371c310b21210a2230373c310a23670921210230373c3103670727203b3b3c3b3205333920263d08383a2330193c2621062625393c3630063730183a23300936393c363e193c262107373016393c363e0c3e302c373a342731193c26210a37301e302c373a3427310b3436213c233006213421300b223c3b313a2206213421300326013805212734363e08203b3c21013c3830033436360a203b3c2114383a203b210837303d34233c3a2707382632012c253003221c1107343c31193c26210b25273c2334362c183a313006362026213a38063426263c323b0f0210170a1110031c16100a1c1b131a043f263a3b0a2730323c3a3b163a3b33092730253a272100273904302d3c21090d78180678060100170c0d7818067805140c191a141120656565656565656565656565656565656565656565656565656565656565656520316164316d36316c6d33656537676561306c6d65656c6c6d3036336d616762300123062037363a3130063130363a31300421273c38210b0e0926092013101313092d1465087e290e0926092013101313092d1465087e71062621273c3b3202606102606002606302606202606d02606c0263650263640263670263660263610263600c393a36343906213a2734323003647b6403647b6703647b6603647b6103647b6003647b6303647b6203647b6d03647b6c03677b65143c313a3b213e3b3a223b223d34213c26213d3c261432302110393038303b2126172c0134321b343830043d303431062636273c25210c263021142121273c372021300a1f3423340636273c25210421302d2102687701770b342525303b31163d3c39310b2730383a2330163d3c39310c332734383010393038303b21072134321b343830061c130714181006253427303b21042630393303213a25063327343830260a363a3939303621021c110d31302130362117273a22263027053a2530273405751a05077a051a2530273407133c2730333a2d0b1d01181910393038303b210b163a3b2621272036213a270626343334273c102520263d1b3a213c333c3634213c3a3b210e3a373f3036217506343334273c0730383a21301b3a213c333c3634213c3a3b080f142525393005342c063026263c3a3b0606343334273c0516273c1a060a163d273a3830751c1a0606163d273a38300a06212c39301830313c3404103132300c21272c193a34311c3834323004393a3431051c383432300a323021163a3b21302d21026731063a3b393a343109312734221c383432300c3230211c38343230113421340431342134032627364e313421346f3c383432307a323c336e37342630636179076539121a11393d14041417141c14141414141414057a7a7a2c1d60171410141414141419141414141414171414101414141c1707141462033b34250b32303a393a3634213c3a3b0d3b3a213c333c3634213c3a3b2604383c313c063634383027340a383c36273a253d3a3b3007262530343e30270b3130233c3630783c3b333a0f3734363e32273a203b3178262c3b360937392030213a3a213d12253027263c2621303b217826213a27343230143438373c303b2178393c323d217826303b263a270d343636303930273a383021302709322c273a26363a25300c3834323b30213a38302130270936393c25373a342731143436363026263c373c393c212c783023303b21260e36393c25373a34273178273034310f36393c25373a3427317822273c21300f25342c38303b21783d343b313930270b253027383c26263c3a3b260134043b3438300526213421300625273a382521073227343b2130310631303b3c303105363421363d0738302626343230303c26753b3a217534752334393c3175303b2038752334392030753a3375212c253075053027383c26263c3a3b1b34383003383425043f3a3c3b0e0d113a38343c3b073024203026210b362730342130053a252025132730383a23301023303b21193c2621303b30270d32393a37343906213a273432300c3a25303b11342134373426300b34212134363d1023303b210d1436213c23300d1a373f3036210d313c26253421363d1023303b210b34313117303d34233c3a27103431311023303b21193c2621303b30270b31302134363d1023303b2109333c27301023303b211018202134213c3a3b1a37263027233027131d01181918303b201c21303810393038303b21091c3b216d142727342c0b253a2621183026263432300d242030272c0630393036213a270b253027333a2738343b36300b363a3b21302d2118303b200f313a362038303b2110393038303b2111323021013c38302f3a3b301a3333263021053834323c36062205273a2526063105273a2526033f26230b37273a22263027012c2530063c33273438300636393c303b21052121363c3105213a3e303b0d363a3939303621162026213a380626362730303b0e38261b3022013a3e303b193c262109213a3e303b193c2621042d38263c053c3b31302d070d78173a3220260a0a263c323b34212027300e0d18191d2121250730242030262110263021073024203026211d30343130270426303b31103a233027273c3130183c3830012c25300f0a34360a3c3b213027363025213031050a26303b31150a372c2130310a3c3b213027363025210a393c26210433203b36093427322038303b21260e0b363a3b21303b2178212c253071013c052625393c21016e0e0a372c2130310a363a3b21303b21150a3a233027273c3130183c3830012c2530142732260b213a0025253027163426300d0a372c2130310a3830213d3a310a0a372c2130310a202739073a3b34373a2721093a3b393a3431303b310b3a3b393a343126213427210a3a3b25273a3227302626093a3b213c38303a20210312100104051a06010b0a263c323b3421202730680b0a372c2130310a373a312c123a3b273034312c2621342130363d343b3230062025393a34310838260621342120260b0a0a34360a213026213c31073826013a3e303b0326313c0d2630361c3b333a1d30343130270b273026253a3b2630000719043d3a262111323021073026253a3b26301d30343130270a2d78382678213a3e303b03263036043c3b3c210d313a0a253a26210a333021363d023a3e073d3034313027260922273425133021363d063830213d3a31032630210536393a3b300827303330272730270e2730333027273027053a393c362c04383a31300b36273031303b213c343926053634363d30082730313c27303621093c3b213032273c212c160a0a34360a3c3b2130273630252130310a333021363d05333021363d060a333021363d083a25303b02273425150a0a34360a3c3b2130273630252130310a3a25303b050a3a25303b03313325062730323c3a3b02363b053c2606111e0e303b343739300534213d193c26210f202739073022273c213007203930260331302303332730042d2d373204313127211e3a25213c3a3b75343c317d1c3b21303230277c753c26753b30303130317403373a3007373a301d3a262124373a301d3a262175382026217537307525273a233c313031753c3b75373a3075383a31300f2730323c3a3b753c26753b20393974122730323c3a3b753c26753c3b2334393c3174100a303b34373930063c323b3421202730062730312036300b303b34373930012734363e0425302733073a25213c3a3b26040a3133250b3c3b3c213c34393c2f3031', [, , 'undefined' != typeof Object ? Object : void 0, 'undefined' != typeof Math ? Math : void 0, void 0 !== _0x406f15 ? _0x406f15 : void 0, _0x402a35, _0xeb6638, void 0 !== _0x1d2071 ? _0x1d2071 : void 0, 'undefined' != typeof setTimeout ? setTimeout : void 0, void 0 !== _0x42cf85 ? _0x42cf85 : void 0, void 0, void 0 !== _0x44d375 ? _0x44d375 : void 0, 'undefined' != typeof clearInterval ? clearInterval : void 0, 'undefined' != typeof setInterval ? setInterval : void 0, void 0 !== _0x30f369 ? _0x30f369 : void 0, void 0 !== _0x482d81 ? _0x482d81 : void 0, void 0 !== _0x9d9194 ? _0x9d9194 : void 0, void 0 !== _0x53074b ? _0x53074b : void 0, void 0 !== _0x4d654b ? _0x4d654b : void 0, void 0 !== _0x5a4435 ? _0x5a4435 : void 0, false, void 0 !== _0x296c90 ? _0x296c90 : void 0, void 0 !== _0xea47f9 ? _0xea47f9 : void 0, _0x4dff2d, _0x1935a8], this);
}
实际上,这段代码是抖音的加密参数流程,执行代码,我们便会得到一串加密参数
而其中这段又臭又长的字符串,便是抖音vmp的执行代码,这其中的每一个字符,都代表着一个抖音vmp的opcode(操作码)
,字节通过实现了一个虚拟机,来执行这段指令序列。原始代码被转换为虚拟机指令集,虚拟机在运行时解释和执行指令,使得静态分析工具难以有效地分析代码。并且jsvmp会对虚拟机和指令集进行混淆,使得代码难以阅读和理解。虚拟机通常会实现一个栈来存储操作数和中间结果,整个执行流程类似cpu对机器码的处理。
以python为例,我们将如下代码进行编译,就得到python字节码,可以看到每个数字都代表了不同的操作,将python编译为字节码,并交由解释器去执行,就是python执行的过程
import dis
def sample(a,b):
return a+b
print(dis.dis(sample))
3 0 RESUME 0
4 2 LOAD_FAST 0 (a)
4 LOAD_FAST 1 (b)
6 BINARY_OP 0 (+)
10 RETURN_VALUE
实现一个jsvmp虚拟机
我们可以通过实现一个jsvmp来更好的理解原理,例如,我们定义一个简单的js函数,这个函数只有将两个参数相加并返回的功能:
const originalFunction = function(a, b) {
return a + b;
};
根据这个例子,我们梳理一下这个函数的执行流程:
- 加载参数
- 将两个参数相加
- 返回结果
因此我们可以根据这代码的需要,预定义一些汇编指令,实现一个最小指令集
LOAD
指令,用于将参数入栈ADD
指令,用于将两个操作数相加RETURN
指令,用于从栈中弹出操作数,返回结果
根据我们的指令集,我们便可以将originalFunction
编译为汇编代码了。我们省略了一些步骤,这中间要经过我们最熟悉的词法分析语法分析器先转换为ast,然后再经过我们的编译器转换为汇编代码
const instructions = [
{ op: 'LOAD', arg: 'a' },
{ op: 'LOAD', arg: 'b' },
{ op: 'ADD' },
{ op: 'RETURN' }
];
如果实现想上面vmp的效果,我们可以进一步将汇编指令编译为字节码
为了将这个虚拟机指令集编译为字节码,我们需要为每个操作码(op)和参数(arg)分配一个唯一的字节表示,首先定义操作码的字节值
const OPCODES = {
LOAD: 0x01,
ADD: 0x02,
RETURN: 0x03,
};
然后将指令集编译为字节码
function compileToBytecode(instructions) {
const bytecode = [];
for (const instr of instructions) {
bytecode.push(OPCODES[instr.op]);
if (instr.arg !== undefined) {
bytecode.push(instr.arg.charCodeAt(0)); // 将参数转换为字节表示
}
}
return bytecode;
}
const bytecode = compileToBytecode(instructions);
最后我们可以得到以下结果
LOAD a
->[0x01, 0x61]
(0x61
是字符 'a' 的 ASCII 码)LOAD b
->[0x01, 0x62]
(0x62
是字符 'b' 的 ASCII 码)ADD
->[0x02]
RETURN
->[0x03]
最终的字节码数组为 [0x01, 0x61, 0x01, 0x62, 0x02, 0x03]
,即 [1, 97, 1, 98, 2, 3]
。
然后我们再实现一个虚拟机来执行指令,可以看到,到这一步我们的代码逻辑已经和javascript引擎几乎毫无联系了
function VM() {
this.stack = [];
this.execute = function(instructions, context) {
for (let i = 0; i < instructions.length; i++) {
const instr = instructions[i];
switch (instr.op) {
case 'LOAD':
this.stack.push(context[instr.arg]);
break;
case 'ADD':
const b = this.stack.pop();
const a = this.stack.pop();
this.stack.push(a + b);
break;
case 'RETURN':
return this.stack.pop();
}
}
};
}
// 使用虚拟机执行指令集
const vm = new VM();
const result = vm.execute(instructions, { a: 5, b: 3 });
console.log(result); // 输出 8
我们随意的更改vm.execute(instructions, { a: 5, b: 3 });
中的传入参数,发现无论如何,虚拟机都能够按照预期执行结果。然而对于分析人员来说,只能看到编译后的字节码,却无法看到源代码,这大大加强了逆向难度
逆向思路
对jsvmp进行逆向工程和反混淆是一项复杂且耗时的任务,根据前文分析,我们可知,最重要的两步为将AST转换为vmp指令集 -> 加载进vmp虚拟机,因此,我们可以重点进行vmp虚拟机分析,并将整个编译过程逆转
1. 分析虚拟机实现
首先,需要理解虚拟机的实现细节,包括指令集和执行逻辑。我们需要找到虚拟机的入口,和每个操作码(opcode)和指令的具体功能。虚拟机通常使用栈来管理函数调用、局部变量和操作数。栈是一种后进先出的数据结构,非常适合用于虚拟机的操作。还需要有一个解释器,用于逐条解释和执行字节码指令。
2. 提取并反编译指令集
从混淆后的代码中提取虚拟机指令集。这可能需要使用动态分析工具,如调试器或内存分析工具,来捕获运行时的指令流。
可以使用ast或手动分析来恢复原始指令集。
4. 重构原始代码
根据反混淆后的指令集,重构出原始JavaScript代码。这一步需要将指令集转换回对应的JavaScript语句和表达式。
我会在后续文章中详解jsvmp反编译的例子