摘要:但事實(shí)上,即使不了解密碼學(xué),也應(yīng)知道是有意義的,因?yàn)榧用芮昂徒饷芎蟮沫h(huán)節(jié),是不受保護(hù)的。廢代碼注入插入一些永遠(yuǎn)不會發(fā)生的代碼,讓攻擊者在分析代碼的時候被這些無用的廢代碼混淆視聽,增加閱讀難度。
前言
在安全攻防戰(zhàn)場中,前端代碼都是公開的,那么對前端進(jìn)行加密有意義嗎?可能大部分人的回答是,毫無意義,不要自創(chuàng)加密算法,直接用HTTPS吧。但事實(shí)上,即使不了解密碼學(xué),也應(yīng)知道是有意義的,因?yàn)?b>加密前和解密后的環(huán)節(jié),是不受保護(hù)的。HTTPS只能保護(hù)傳輸層,此外別無用處。
而加密環(huán)節(jié)又分:
傳輸加密(對抗鏈路破解)
數(shù)據(jù)加密(對抗協(xié)議破解)
代碼加密(隱藏算法、反調(diào)試...)
本文主要列舉一些我見到的,我想到的一些加密方式,其實(shí)確切的說,應(yīng)該叫混淆,不應(yīng)該叫加密。
那么,代碼混淆的具體原理是什么?其實(shí)很簡單,就是去除代碼中盡可能多的有意義的信息,比如注釋、換行、空格、代碼負(fù)號、變量重命名、屬性重命名(允許的情況下)、無用代碼的移除等等。因?yàn)榇a是公開的,我們必須承認(rèn)沒有任何一種算法可以完全不被破解,所以,我們只能盡可能增加攻擊者閱讀代碼的成本。
原文地址
語法樹AST混淆在保證代碼原本的功能性的情況下,我們可以對代碼的AST按需進(jìn)行變更,然后將變更后的AST在生成一份代碼進(jìn)行輸出,達(dá)到混淆的目的,我們最常用的uglify-js就是這樣對代碼進(jìn)行混淆的,當(dāng)然uglify-js的混淆只是主要進(jìn)行代碼壓縮,即我們下面講到的變量名混淆。
變量名混淆將變量名混淆成閱讀比較難閱讀的字符,增加代碼閱讀難度,上面說的uglify-js進(jìn)行的混淆,就是把變量混淆成了短名(主要是為了進(jìn)行代碼壓縮),而現(xiàn)在大部分安全方向的混淆,都會將其混淆成類16進(jìn)制變量名,效果如下:
var test = "hello";
混淆后:
var _0x7deb = "hello";
注意事項(xiàng):
eval語法,eval函數(shù)中可能使用了原來的變量名,如果不對其進(jìn)行處理,可能會運(yùn)行報(bào)錯,如下:
var test = "hello"; eval("console.log(test)");
如果不對eval中的console.log(test)進(jìn)行關(guān)聯(lián)的混淆,則會報(bào)錯。不過,如果eval語法超出了靜態(tài)分析的范疇,比如:
var test = "hello"; var variableName = "test"; eval("console.log(" + variableName + ")");
這種咋辦呢,可能要進(jìn)行遍歷AST找到其運(yùn)行結(jié)果,然后在進(jìn)行混淆,不過貌似成本比較高。
全局變量的編碼,如果代碼是作為SDK進(jìn)行輸出的,我們需要保存全局變量名的不變,比如:
$變量是放在全局下的,混淆過后如下:
那么如果依賴這一段代碼的模塊,使用$("id")調(diào)用自然會報(bào)錯,因?yàn)檫@個全局變量已經(jīng)被混淆了。
常量提取將JS中的常量提取到數(shù)組中,調(diào)用的時候用數(shù)組下標(biāo)的方式調(diào)用,這樣的話直接讀懂基本不可能了,要么反AST處理下,要么一步一步調(diào)試,工作量大增。
以上面的代碼為例:
var test = "hello";
混淆過后:
var _0x9d2b = ["hello"]; var _0xb7de = function (_0x4c7513) { var _0x96ade5 = _0x9d2b[_0x4c7513]; return _0x96ade5; }; var test = _0xb7de(0);
當(dāng)然,我們可以根據(jù)需求,將數(shù)組轉(zhuǎn)化為二位數(shù)組、三維數(shù)組等,只需要在需要用到的地方獲取就可以。
常量混淆將常量進(jìn)行加密處理,上面的代碼中,雖然已經(jīng)是混淆過后的代碼了,但是hello字符串還是以明文的形式出現(xiàn)在代碼中,可以利用JS中16進(jìn)制編碼會直接解碼的特性將關(guān)鍵字的Unicode進(jìn)行了16進(jìn)制編碼。如下:
var test = "hello";
結(jié)合常量提取得到混淆結(jié)果:
var _0x9d2b = ["x68x65x6cx6cx6f"]; var _0xb7de = function (_0x4c7513) { _0x4c7513 = _0x4c7513 - 0x0; var _0x96ade5 = _0x9d2b[_0x4c7513]; return _0x96ade5; }; var test = _0xb7de("0x0");
當(dāng)然,除了JS特性自帶的Unicode自動解析以外,也可以自定義一些加解密算法,比如對常量進(jìn)行base64編碼,或者其他的什么rc4等等,只需要使用的時候解密就OK,比如上面的代碼用base64編碼后:
var _0x9d2b = ["aGVsbG8="]; // base64編碼后的字符串 var _0xaf421 = function (_0xab132) { // base64解碼函數(shù) var _0x75aed = function(_0x2cf82) { // TODO: 解碼 }; return _0x75aed(_0xab132); } var _0xb7de = function (_0x4c7513) { _0x4c7513 = _0x4c7513 - 0x0; var _0x96ade5 = _0xaf421(_0x9d2b[_0x4c7513]); return _0x96ade5; }; var test = _0xb7de("0x0");運(yùn)算混淆
將所有的邏輯運(yùn)算符、二元運(yùn)算符都變成函數(shù),目的也是增加代碼閱讀難度,讓其無法直接通過靜態(tài)分析得到結(jié)果。如下:
var i = 1 + 2; var j = i * 2; var k = j || i;
混淆后:
var _0x62fae = { _0xeca4f: function(_0x3c412, _0xae362) { return _0x3c412 + _0xae362; }, _0xe82ae: function(_0x63aec, _0x678ec) { return _0x63aec * _0x678ec; }, _0x2374a: function(_0x32487, _0x3a461) { return _0x32487 || _0x3a461; } }; var i = _0x62fae._0e8ca4f(1, 2); var j = _0x62fae._0xe82ae(i, 2); var k = _0x62fae._0x2374a(i, j);
當(dāng)然除了邏輯運(yùn)算符和二元運(yùn)算符以外,還可以將函數(shù)調(diào)用、靜態(tài)字符串進(jìn)行類似的混淆,如下:
var fun1 = function(name) { console.log("hello, " + name); }; var fun2 = function(name, age) { console.log(name + " is " + age + " years old"); } var name = "xiao.ming"; fun1(name); fun2(name, 8);
var _0x62fae = { _0xe82ae: function(_0x63aec, _0x678ec) { return _0x63aec(_0x678ec); }, _0xeca4f: function(_0x92352, _0x3c412, _0xae362) { return _0x92352(_0x3c412, _0xae362) }, _0x2374a: "xiao.ming", _0x5482a: "hello, ", _0x837ce: " is ", _0x3226e: " years old" }; var fun1 = function(name) { console.log(_0x62fae._0x5482a + name); }; var fun2 = function(name, age) { console.log(name + _0x62fae._0x837ce + age + _0x62fae._0x3226e); } var name = _0x62fae._0x2374a; _0x62fae._0xe82ae(fun1, name); _0x62fae._0xeca4f(fun2, name, 0x8);
上面的例子中,fun1和fun2內(nèi)的字符串相加也會被混淆走,靜態(tài)字符串也會被前面提到的字符串提取抽取到數(shù)組中(我就是懶,這部分代碼就不寫了)。
需要注意的是,我們每次遇到相同的運(yùn)算符,需不需要重新生成函數(shù)進(jìn)行替換,這就按個人需求了。
語法丑化將我們常用的語法混淆成我們不常用的語法,前提是不改變代碼的功能。例如for換成do/while,如下:
for (i = 0; i < n; i++) { // TODO: do something } var i = 0; do { if (i >= n) break; // TODO: do something i++; } while (true)動態(tài)執(zhí)行
將靜態(tài)執(zhí)行代碼添加動態(tài)判斷,運(yùn)行時動態(tài)決定運(yùn)算符,干擾靜態(tài)分析。
如下:
var c = 1 + 2;
混淆過后:
function _0x513fa(_0x534f6, _0x85766) { return _0x534f6 + _0x85766; } function _0x3f632(_0x534f6, _0x534f6) { return _0x534f6 - _0x534f6; } // 動態(tài)判定函數(shù) function _0x3fa24() { return true; } var c = _0x3fa24() ? _0x513fa(1, 2) : _0x3f632(1, 2);流程混淆
對執(zhí)行流程進(jìn)行混淆,又稱控制流扁平化,為什么要做混淆執(zhí)行流程呢?因?yàn)樵诖a開發(fā)的過程中,為了使代碼邏輯清晰,便于維護(hù)和擴(kuò)展,會把代碼編寫的邏輯非常清晰。一段代碼從輸入,經(jīng)過各種if/else分支,順序執(zhí)行之后得到不同的結(jié)果,而我們需要將這些執(zhí)行流程和判定流程進(jìn)行混淆,讓攻擊者沒那么容易摸清楚我們的執(zhí)行邏輯。
控制流扁平化又分順序扁平化、條件扁平化,
順序扁平化顧名思義,將按順序、自上而下執(zhí)行的代碼,分解成數(shù)個分支進(jìn)行執(zhí)行,如下代碼:
(function () { console.log(1); console.log(2); console.log(3); console.log(4); console.log(5); })();
流程圖如下:
混淆過后代碼如下:
(function () { var flow = "3|4|0|1|2".split("|"), index = 0; while (!![]) { switch (flow[index++]) { case "0": console.log(3); continue; case "1": console.log(4); continue; case "2": console.log(5); continue; case "3": console.log(1); continue; case "4": console.log(2); continue; } break; } }());
混淆過后的流程圖如下:
流程看起來扁了。
條件扁平化條件扁平化的作用是把所有if/else分支的流程,全部扁平到一個流程中,在流程圖中擁有相同的入口和出口。
如下面的代碼:
function modexp(y, x, w, n) { var R, L; var k = 0; var s = 1; while(k < w) { if (x[k] == 1) { R = (s * y) % n; } else { R = s; } s = R * R % n; L = R; k++; } return L; }
如上代碼,流程圖是這樣的
控制流扁平化后代碼如下:
function modexp(y, x, w, n) { var R, L, s, k; var next = 0; for(;;) { switch(next) { case 0: k = 0; s = 1; next = 1; break; case 1: if (k < w) next = 2; else next = 6; break; case 2: if (x[k] == 1) next = 3; else next = 4; break; case 3: R = (s * y) % n; next = 5; break; case 4: R = s; next = 5; break; case 5: s = R * R % n; L = R; k++; next = 1; break; case 6: return L; } } }
混淆后的流程圖如下:
直觀的感覺就是代碼變扁了,所有的代碼都擠到了一層當(dāng)中,這樣做的好處在于在讓攻擊者無法直觀,或通過靜態(tài)分析的方法判斷哪些代碼先執(zhí)行哪些后執(zhí)行,必須要通過動態(tài)運(yùn)行才能記錄執(zhí)行順序,從而加重了分析的負(fù)擔(dān)。
需要注意的是,在我們的流程中,無論是順序流程還是條件流程,如果出現(xiàn)了塊作用域的變量聲明(const/let),那么上面的流程扁平化將會出現(xiàn)錯誤,因?yàn)閟witch/case內(nèi)部為塊作用域,表達(dá)式被分到case內(nèi)部之后,其他case無法取到const/let的變量聲明,自然會報(bào)錯。
不透明謂詞上面的switch/case的判斷是通過數(shù)字(也就是謂詞)的形式判斷的,而且是透明的,可以看到的,為了更加的混淆視聽,可以將case判斷設(shè)定為表達(dá)式,讓其無法直接判斷,比如利用上面代碼,改為不透明謂詞:
function modexp(y, x, w, n) { var a = 0, b = 1, c = 2 * b + a; var R, L, s, k; var next = 0; for(;;) { switch(next) { case (a * b): k = 0; s = 1; next = 1; break; case (2 * a + b): if (k < w) next = 2; else next = 6; break; case (2 * b - a): if (x[k] == 1) next = 3; else next = 4; break; case (3 * a + b + c): R = (s * y) % n; next = 5; break; case (2 * b + c): R = s; next = 5; break; case (2 * c + b): s = R * R % n; L = R; k++; next = 1; break; case (4 * c - 2 * b): return L; } } }
謂詞用a、b、c三個變量組成,甚至可以把這三個變量隱藏到全局中定義,或者隱藏在某個數(shù)組中,讓攻擊者不能那么輕易找到。
腳本加殼將腳本進(jìn)行編碼,運(yùn)行時 解碼 再 eval 執(zhí)行如:
eval (…………………………..……………. ……………. !@#$%^&* ……………. .…………………………..……………. )
但是實(shí)際上這樣意義并不大,因?yàn)楣粽咧恍枰補(bǔ)lert或者console.log就原形畢露了
改進(jìn)方案:利用Function / (function(){}).constructor將代碼當(dāng)做字符串傳入,然后執(zhí)行,如下:
var code = "console.log("hellow")"; (new Function(code))();
如上代碼,可以對code進(jìn)行加密混淆,例如aaencode,原理也是如此,我們舉個例子
alert("Hello, JavaScript");
利用aaencode混淆過后,代碼如下:
?ω??= /`m′)? ~┻━┻ //*′?`*/ ["_"]; 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))+ (???)+ (?Д?)[?ε?]+((???) + (?Θ?))+ (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) +(o^_^o))+ (c^_^o)+ (?Д?)[?ε?]+(?Θ?)+ ((o^_^o) +(o^_^o))+ (???)+ (?Д?)[?ε?]+(???)+ ((o^_^o) - (?Θ?))+ (?Д?)[?ε?]+((???) + (?Θ?))+ (?Θ?)+ (?Д?)[?o?]) (?Θ?)) ("_");
這段代碼看起來很奇怪,不像是JavaScript代碼,但是實(shí)際上這段代碼是用一些看似表情的符號,聲明了一個16位的數(shù)組(用來表示16進(jìn)制位置),然后將code當(dāng)做字符串遍歷,把每個代碼符號通過string.charCodeAt取這個16位的數(shù)組下標(biāo),拼接成代碼。大概的意思就是把代碼當(dāng)做字符串,然后使用這些符號的拼接代替這一段代碼(可以看到代碼里有很多加號),最后,通過(new Function(code))("_")執(zhí)行。
仔細(xì)觀察上面這一段代碼,把代碼最后的("_")去掉,在運(yùn)行,你會直接看到源代碼,然后Function.constructor存在(?Д?)變量中,感興趣的同學(xué)可以自行查看。
除了aaencode,jjencode原理也是差不多,就不做解釋了,其他更霸氣的jsfuck,這些都是對代碼進(jìn)行加密的,這里就不詳細(xì)介紹了。
反調(diào)試由于JavaScript自帶debugger語法,我們可以利用死循環(huán)性的debugger,當(dāng)頁面打開調(diào)試面板的時候,無限進(jìn)入調(diào)試狀態(tài)。
定時執(zhí)行在代碼開始執(zhí)行的時候,使用setInterval定時觸發(fā)我們的反調(diào)試函數(shù)。
隨機(jī)執(zhí)行在代碼生成階段,隨機(jī)在部分函數(shù)體中注入我們的反調(diào)試函數(shù),當(dāng)代碼執(zhí)行到特定邏輯的時候,如果調(diào)試面板在打開狀態(tài),則無限進(jìn)入調(diào)試狀態(tài)。
內(nèi)容監(jiān)測由于我們的代碼可能已經(jīng)反調(diào)試了,攻擊者可以會將代碼拷貝到自己本地,然后修改,調(diào)試,執(zhí)行,這個時候就需要添加一些檢測進(jìn)行判定,如果不是正常的環(huán)境執(zhí)行,那讓代碼自行失敗。
代碼自檢在代碼生成的時候,為函數(shù)生成一份Hash,在代碼執(zhí)行之前,通過函數(shù) toString 方法,檢測代碼是否被篡改
function module() { // 篡改校驗(yàn) if (Hash(module.toString()) != "JkYxnHlxHbqKowiuy") { // 代碼被篡改! } }環(huán)境自檢
檢查當(dāng)前腳本的執(zhí)行環(huán)境,例如當(dāng)前的URL是否在允許的白名單內(nèi)、當(dāng)前環(huán)境是否正常的瀏覽器。
如果為Nodejs環(huán)境,如果出現(xiàn)異常環(huán)境,甚至我們可以啟動木馬,長期跟蹤。
廢代碼注入插入一些永遠(yuǎn)不會發(fā)生的代碼,讓攻擊者在分析代碼的時候被這些無用的廢代碼混淆視聽,增加閱讀難度。
廢邏輯注入與廢代碼相對立的就是有用的代碼,這些有用的代碼代表著被執(zhí)行代碼的邏輯,這個時候我們可以收集這些邏輯,增加一段判定來決定執(zhí)行真邏輯還是假邏輯,如下:
(function(){ if (true) { var foo = function () { console.log("abc"); }; var bar = function () { console.log("def"); }; var baz = function () { console.log("ghi"); }; var bark = function () { console.log("jkl"); }; var hawk = function () { console.log("mno"); }; foo(); bar(); baz(); bark(); hawk(); } })();
可以看到,所有的console.log都是我們的執(zhí)行邏輯,這個時候可以收集所有的console.log,然后制造假判定來執(zhí)行真邏輯代碼,收集邏輯注入后如下:
(function(){ if (true) { var foo = function () { if ("aDas" === "aDas") { console.log("abc"); } else { console.log("def"); } }; var bar = function () { if ("Mfoi" !== "daGs") { console.log("ghi"); } else { console.log("def"); } }; var baz = function () { if ("yuHo" === "yuHo") { console.log("ghi"); } else { console.log("abc"); } }; var bark = function () { if ("qu2o" === "qu2o") { console.log("jkl"); } else { console.log("mno"); } }; var hawk = function () { if ("qCuo" !== "qcuo") { console.log("jkl"); } else { console.log("mno"); } }; foo(); bar(); baz(); bark(); hawk(); } })();
判定邏輯中生成了一些字符串,在沒有使用字符串提取的情況下,這是可以通過代碼靜態(tài)分析來得到真實(shí)的執(zhí)行邏輯的,或者我們可以使用上文講到的動態(tài)執(zhí)行來決定執(zhí)行真邏輯,可以看一下使用字符串提取和變量名編碼后的效果,如下:
var _0x6f5a = [ "abc", "def", "caela", "hmexe", "ghi", "aaeem", "maxex", "mno", "jkl", "ladel", "xchem", "axdci", "acaeh", "log" ]; (function (_0x22c909, _0x4b3429) { var _0x1d4bab = function (_0x2e4228) { while (--_0x2e4228) { _0x22c909["push"](_0x22c909["shift"]()); } }; _0x1d4bab(++_0x4b3429); }(_0x6f5a, 0x13f)); var _0x2386 = function (_0x5db522, _0x143eaa) { _0x5db522 = _0x5db522 - 0x0; var _0x50b579 = _0x6f5a[_0x5db522]; return _0x50b579; }; (function () { if (!![]) { var _0x38d12d = function () { if (_0x2386("0x0") !== _0x2386("0x1")) { console[_0x2386("0x2")](_0x2386("0x3")); } else { console[_0x2386("0x2")](_0x2386("0x4")); } }; var _0x128337 = function () { if (_0x2386("0x5") !== _0x2386("0x6")) { console[_0x2386("0x2")](_0x2386("0x4")); } else { console[_0x2386("0x2")](_0x2386("0x7")); } }; var _0x55d92e = function () { if (_0x2386("0x8") !== _0x2386("0x8")) { console[_0x2386("0x2")](_0x2386("0x3")); } else { console[_0x2386("0x2")](_0x2386("0x7")); } }; var _0x3402dc = function () { if (_0x2386("0x9") !== _0x2386("0x9")) { console[_0x2386("0x2")](_0x2386("0xa")); } else { console[_0x2386("0x2")](_0x2386("0xb")); } }; var _0x28cfaa = function () { if (_0x2386("0xc") === _0x2386("0xd")) { console[_0x2386("0x2")](_0x2386("0xb")); } else { console[_0x2386("0x2")](_0x2386("0xa")); } }; _0x38d12d(); _0x128337(); _0x55d92e(); _0x3402dc(); _0x28cfaa(); } }());求值陷阱
除了注入執(zhí)行邏輯以外,還可以埋入一個隱蔽的陷阱,在一個永不到達(dá)且無法靜態(tài)分析的分支里,引用該函數(shù),正常用戶不會執(zhí)行,而 AST 遍歷求值時,則會觸發(fā)陷阱!陷阱能干啥呢?
日志上報(bào),及時了解情況
在本地存儲隱寫特征,長期跟蹤
釋放CSRF漏洞,獲得破解者的詳細(xì)信息
開啟自殺程序(頁面崩潰、死循環(huán)、耗盡內(nèi)存等)
加殼干擾在代碼用eval包裹,然后對eval參數(shù)進(jìn)行加密,并埋下陷阱,在解碼時插入無用代碼,干擾顯示,大量換行、注釋、字符串等大量特殊字符,導(dǎo)致顯示卡頓。
結(jié)束大概我想到的混淆就包括這些,單個特性使用的話,混淆效果一般,各個特性組合起來用的話,最終效果很明顯,當(dāng)然這個看個人需求,畢竟混淆是個雙刃劍,在增加了閱讀難度的同時,也增大了腳本的體積,降低了代碼的運(yùn)行效率。
參考文獻(xiàn)代碼混淆之道——控制流扁平與不透明謂詞理論篇
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/104545.html
摘要:接下來,我就來詳解一下如何防止被二次打包。開發(fā)階段移動應(yīng)用開發(fā)時接入安全組件,保護(hù)數(shù)據(jù)安全。 前言 Android APP二次打包則是盜版正規(guī)Android APP,破解后植入惡意代碼重新打包。不管從性能、用戶體驗(yàn)、外觀它都跟正規(guī)APP一模一樣但是背后它確悄悄運(yùn)行著可怕的程序,它會在不知不覺中浪費(fèi)手機(jī)電量、流量,惡意扣費(fèi)、偷窺隱私等等行為。 二次打包問題只是Android應(yīng)用安全風(fēng)險(xiǎn)中...
摘要:網(wǎng)易資深安全工程師鐘亞平在今年的安卓巴士全球開發(fā)者論壇上做了安卓逆向與保護(hù)的演講完整演講內(nèi)容請見這里一文了解安卓逆向分析與保護(hù)機(jī)制,其中就談到了關(guān)于代碼混淆的問題。就是一個混淆代碼的開源項(xiàng)目,能夠?qū)ψ止?jié)碼進(jìn)行混淆縮減體積優(yōu)化等處理。 歡迎訪問網(wǎng)易云社區(qū),了解更多網(wǎng)易技術(shù)產(chǎn)品運(yùn)營經(jīng)驗(yàn)。 在大公司怎么做android代碼混淆的?發(fā)現(xiàn)他們的軟件用apktool反編譯居然沒看到classes....
摘要:為了防止這種現(xiàn)象,我們可以對字節(jié)碼進(jìn)行混淆。動態(tài)鏈接庫是目標(biāo)文件的集合,目標(biāo)文件在動態(tài)鏈接庫中的組織方式是按照特殊方式形成的。 一、已知防護(hù)策略 1.不可或缺的混淆 Java 是一種跨平臺、解釋型語言,Java 源代碼編譯成的class文件中有大量包含語義的變量名、方法名的信息,很容易被反編譯為Java 源代碼。為了防止這種現(xiàn)象,我們可以對Java字節(jié)碼進(jìn)行混淆。混淆不僅能將代碼中的類...
閱讀 2976·2023-04-26 02:25
閱讀 2249·2023-04-25 18:05
閱讀 647·2021-09-30 09:57
閱讀 2943·2021-09-27 14:10
閱讀 1652·2019-08-30 15:44
閱讀 1003·2019-08-29 15:28
閱讀 2524·2019-08-29 14:10
閱讀 2263·2019-08-29 13:30