天天看點

sojson本地反調試原了解析

打開sojson的網址:​​https://www.sojson.com/jsobfuscator.html​​

sojson的js加密位于:

sojson本地反調試原了解析

下面我們拿預設代碼進行測試:

(function(w,) { 
 w.info = "這是一個一系列js操作。"; 
 d.warning = "如果您的JS裡嵌套了PHP,JSP标簽,等等其他非JavaScript的代碼,請提取出來再加密。這個工具不能加密php、jsp等模版内容"; 
 d.intro = "本工具由 www.jsjiami.com 提供接口。"; 
})(window, document);      

下面我們将加密系數全部設定為牛X模式,并啟動絕對不可逆配置後,執行JS混淆加密:

sojson本地反調試原了解析

點選複制結果,複制混淆後的JS代碼進行調試:

sojson本地反調試原了解析

動态調試重置方法過無限debug

下面我們嘗試調試上述代碼,在遊覽器控制台粘貼并運作:

sojson本地反調試原了解析

代碼添加的屬性可以順利通路,但是代碼本身已經進入了無限debug狀态,格式化代碼後如上。

點選兩次跳出目前函數後,發現已經出現遞歸的無限調用,不斷調用這兩個方法:

sojson本地反調試原了解析

如果我們設定條件斷點flase或永不在此處暫停,可以跳過這些斷點,但是函數卻仍然處于無效遞歸調用中,最終導緻控制台崩潰。

在還未設定斷點時右鍵:
sojson本地反調試原了解析
已在此處設定斷點時右鍵,隻能編輯斷點設定false:
sojson本地反調試原了解析

為了跳出這個無限的遞歸,我們可以動态修改某個方法,下面我們先定位到這個無限遞歸調用的起始發起位置:

sojson本地反調試原了解析

可以看到,本質就是_0x4c01b6函數不斷的自己調用自己實作了無限遞歸:

sojson本地反調試原了解析

下面我們執行如下代碼:

_0x4c01b6=function(){}      

再次點選恢複腳本執行後,已成功跳出這次的遞歸調用。

但是過了幾秒之後,又再次進入了無限debug狀态:

sojson本地反調試原了解析

不過從堆棧資訊我們可以看到這次調用是定時器發起的。

這個定時器依然使用​

​_0x4c01b6函數​

​​實作無限遞歸,我們再次将​

​_0x4c01b6函數​

​置空。

sojson本地反調試原了解析

然後給setInterval定時器函數執行的位置打上斷點:

sojson本地反調試原了解析

此時由于定時器已經開始執行,是以執行過程已經固定而且無法取消,我們隻能将執行過程所執行的方法進行修改會置空,進而實作檢測的跳過。

觀察上述代碼可以直達,定時器本質上就是執行了​

​_0x5e82cc​

​​方法,下面我們将​

​_0x5e82cc​

​方法置空:

_0x5e82cc=function(){}      

然後就可以取消全部斷點并恢複腳本執行。

此時已經通過全部檢測,沒有再進入debug狀态。

sojson防代碼格式化原理分析

首先複制遊覽器格式化之後的代碼到本地文本編輯器,根據前面的分析,我們可以删除setInterval定時器,并将_0x4c01b6函數的定義置空跳過無限debug:

sojson本地反調試原了解析

下面我們将修改完的代碼複制粘貼到遊覽器控制台執行,幾秒鐘過後代碼已經崩潰:

sojson本地反調試原了解析

但是如果我們将代碼壓縮到一行複制粘貼到控制台卻可以順利執行,sojson到底是如何實作不讓格式化之後的代碼執行的呢?

我們可以再代碼頭部加上debugger,再重新整理頁面并執行代碼,代碼就會立馬被中斷,我們可以不斷按下F11研究一下執行過程。

前面崩潰時,我們還能看到崩潰時堆棧的位置,我們先讓代碼運作到對應的位置:

sojson本地反調試原了解析

可以看到崩潰前是因為​

​_0x2c2f51​

​這個帶有正則校驗的方法傳回了false。

這個正則校驗了一個函數的文本内容:

sojson本地反調試原了解析

原來如此,當代碼沒有被格式化時,這個函數的文本就不會存在縮進和空格,而格式化之後就多出了很多縮進和空格,于是正則校驗不通過傳回了false,然後就會執行setCookie方法:

sojson本地反調試原了解析

這個方法在執行過程中_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函數,導緻檢測直接通過,無需繼續分析:

sojson本地反調試原了解析

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;
        }
    });
})();      

繼續閱讀