打開sojson的網址:https://www.sojson.com/jsobfuscator.html
sojson的js加密位于:

下面我們拿預設代碼進行測試:
(function(w,) {
w.info = "這是一個一系列js操作。";
d.warning = "如果您的JS裡嵌套了PHP,JSP标簽,等等其他非JavaScript的代碼,請提取出來再加密。這個工具不能加密php、jsp等模版内容";
d.intro = "本工具由 www.jsjiami.com 提供接口。";
})(window, document);
下面我們将加密系數全部設定為牛X模式,并啟動絕對不可逆配置後,執行JS混淆加密:
點選複制結果,複制混淆後的JS代碼進行調試:
動态調試重置方法過無限debug
下面我們嘗試調試上述代碼,在遊覽器控制台粘貼并運作:
代碼添加的屬性可以順利通路,但是代碼本身已經進入了無限debug狀态,格式化代碼後如上。
點選兩次跳出目前函數後,發現已經出現遞歸的無限調用,不斷調用這兩個方法:
如果我們設定條件斷點flase或永不在此處暫停,可以跳過這些斷點,但是函數卻仍然處于無效遞歸調用中,最終導緻控制台崩潰。
在還未設定斷點時右鍵:已在此處設定斷點時右鍵,隻能編輯斷點設定false:![]()
sojson本地反調試原了解析 ![]()
sojson本地反調試原了解析
為了跳出這個無限的遞歸,我們可以動态修改某個方法,下面我們先定位到這個無限遞歸調用的起始發起位置:
可以看到,本質就是_0x4c01b6函數不斷的自己調用自己實作了無限遞歸:
下面我們執行如下代碼:
_0x4c01b6=function(){}
再次點選恢複腳本執行後,已成功跳出這次的遞歸調用。
但是過了幾秒之後,又再次進入了無限debug狀态:
不過從堆棧資訊我們可以看到這次調用是定時器發起的。
這個定時器依然使用
_0x4c01b6函數
實作無限遞歸,我們再次将
_0x4c01b6函數
置空。
然後給setInterval定時器函數執行的位置打上斷點:
此時由于定時器已經開始執行,是以執行過程已經固定而且無法取消,我們隻能将執行過程所執行的方法進行修改會置空,進而實作檢測的跳過。
觀察上述代碼可以直達,定時器本質上就是執行了
_0x5e82cc
方法,下面我們将
_0x5e82cc
方法置空:
_0x5e82cc=function(){}
然後就可以取消全部斷點并恢複腳本執行。
此時已經通過全部檢測,沒有再進入debug狀态。
sojson防代碼格式化原理分析
首先複制遊覽器格式化之後的代碼到本地文本編輯器,根據前面的分析,我們可以删除setInterval定時器,并将_0x4c01b6函數的定義置空跳過無限debug:
下面我們将修改完的代碼複制粘貼到遊覽器控制台執行,幾秒鐘過後代碼已經崩潰:
但是如果我們将代碼壓縮到一行複制粘貼到控制台卻可以順利執行,sojson到底是如何實作不讓格式化之後的代碼執行的呢?
我們可以再代碼頭部加上debugger,再重新整理頁面并執行代碼,代碼就會立馬被中斷,我們可以不斷按下F11研究一下執行過程。
前面崩潰時,我們還能看到崩潰時堆棧的位置,我們先讓代碼運作到對應的位置:
可以看到崩潰前是因為
_0x2c2f51
這個帶有正則校驗的方法傳回了false。
這個正則校驗了一個函數的文本内容:
原來如此,當代碼沒有被格式化時,這個函數的文本就不會存在縮進和空格,而格式化之後就多出了很多縮進和空格,于是正則校驗不通過傳回了false,然後就會執行setCookie方法:
這個方法在執行過程中_0x5f5bb8變量會不斷遞增,這個for循環永遠無法滿足結束條件,進而形成死循環導緻卡死。
那麼對于上述檢測我們可以将那個正則檢測代碼相關的代碼修改為直接傳回true,我根據代碼邏輯将:
var _0x2c2f51 = function() {
var _0x14443b = new RegExp('\x5cw+\x20*\x5c(\x5c)\x20*{\x5cw+\x20*[\x27|\x22].+[\x27|\x22];?\x20*}');
return _0x14443b['test'](_0x38d6b4['removeCookie']['toString']());
};
_0x38d6b4['updateCookie'] = _0x2c2f51;
var _0x5d1737 = '';
var _0x43d0b9 = _0x38d6b4['updateCookie']();
if (!_0x43d0b9) {
_0x38d6b4['setCookie'](['*'], 'counter', 0x1);
} else if (_0x43d0b9) {
_0x5d1737 = _0x38d6b4['getCookie'](null, 'counter');
} else {
_0x38d6b4['removeCookie']();
}
直接修改為:
_0x5d1737 = _0x38d6b4['getCookie'](null, 'counter');
因為通過這一層檢驗後必将隻執行這一行代碼。
當然sojson并不隻有一處對代碼格式化的檢測,我們可以搜尋
new RegExp
關鍵字将所有可能存在格式化檢測的代碼都找出來。
注意:可以直接在原始代碼對應位置加入debugger,不需要一定要在執行時再加斷點。
重新開機遊覽器後,将上述修改後的代碼粘貼到控制台執行,然後搜尋
new RegExp
,将每個涉及正則并且後續使用了test的代碼都打上斷點,點選繼續執行,定位到斷點處:
var _0x55f2e1 = function(_0x54d96d) {
this['rc4Bytes'] = _0x54d96d;
this['states'] = [0x1, 0x0, 0x0];
this['newState'] = function() {
return 'newState';
}
;
this['firstState'] = '\x5cw+\x20*\x5c(\x5c)\x20*{\x5cw+\x20*';
this['secondState'] = '[\x27|\x22].+[\x27|\x22];?\x20*}';
};
_0x55f2e1['prototype']['checkState'] = function() {
var _0x978b9f = new RegExp(this['firstState'] + this['secondState']);
return this['runState'](_0x978b9f['test'](this['newState']['toString']()) ? --this['states'][0x1] : --this['states'][0x0]);
};
_0x55f2e1['prototype']['runState'] = function(_0x5ec77f) {
if (!Boolean(~_0x5ec77f)) {
return _0x5ec77f;
}
return this['getState'](this['rc4Bytes']);
};
_0x55f2e1['prototype']['getState'] = function(_0x5c8d2b) {
for (var _0x1277d0 = 0x0, _0x125c5f = this['states']['length']; _0x1277d0 < _0x125c5f; _0x1277d0++) {
this['states']['push'](Math['round'](Math['random']()));
_0x125c5f = this['states']['length'];
}
return _0x5c8d2b(this['states'][0x0]);
}
正則的實際内容為:
'\x5cw+\x20*\x5c(\x5c)\x20*{\x5cw+\x20*'+'[\x27|\x22].+[\x27|\x22];?\x20*}'
\w+ *\(\) *{\w+ *['|"].+['|"];? *}
上面一大堆代碼的核心在于,runState必須傳入-1才能判斷通過,否則就會執行getState方法進入無限循環。
上述代碼可以直接修改為:
var _0x55f2e1 = function(_0x54d96d) {};
_0x55f2e1['prototype']['checkState'] = function() {
return -1;
};
修改完畢後,重新整理頁面重新運作代碼,順利執行通過全部代碼格式化檢測。
其實還有一套最複雜的正則檢測,但由于我一開始就清空了_0x4c01b6函數,導緻檢測直接通過,無需繼續分析:
hook擷取真實代碼的位置
我們在代碼前加上如下代碼:
(function() {
var temp = "";
Object.defineProperty(window, 'info', {
//hook set方法也就是指派的方法
set: function(val) {
console.log('Hook捕獲到info設定->', val);
debugger;
temp = val;
return val;
},
//hook get方法也就是取值的方法
get: function()
{
return temp;
}
});
})();