軟體保護機制小窺

來源:互聯網
上載者:User
保護機制小窺

【聲明】
我寫文章以交流為主,希望大家在轉載時能保持文章的完整性。

【前言】
這次以介紹保護機製為主,就不寫什麼脫殼方法了,其實以前我也誤導了不少善良的觀眾。瞭解一個加殼軟體最主要的應該是瞭解其保護機制,這也是我後來才領悟的一點。以下介紹的一點保護機制的知識。這裡只為了讓大家和我一起來共同學習和瞭解保護機制的知識。

水貨作者:        ljttt
出廠日期:        2001-01-01    (水貨的東東一般都這樣寫出廠日期的)

①、首先,我們來看個比較簡單的自身保護方法。這段代碼如下:

 0040CC54: 03F3                      add esi,ebx                        <--esi指向某段代碼,ecx為代碼的長度(DWORD)
 0040CC56: 8B06                      mov eax,dword ptr [esi]            <--取出代碼中的 4 個位元組
 0040CC58: 33D0                      xor edx,eax                        <--XOR異或
 0040CC5A: F7D2                      not edx                            <--取反
 0040CC5C: 33D1                      xor edx,ecx                        <--與ecx(代碼長度)異或
 0040CC5E: 03D0                      add edx,eax                        <--與代碼中的 4 個位元組相加
 0040CC60: 83C604                    add esi,00000004                    <--定位到代碼中的下 4 個位元組
 0040CC63: 49                        dec ecx                            <--代碼長度減一
 0040CC64: 75F0                      jnz 0040CC56                        <--迴圈

先來描述一下這段代碼的功能:可以看到這段代碼用 ESI 作為指標,用 ECX 儲存長度。把 ESI 指向的資料進行異或取反,最後在 EDX 中得到一個“值”。

那麼它和自身保護有什麼聯絡呢?我來簡單說一下,如果 ESI = 0040CC54, ECX = 0040CC64 - 0040CC54。那麼這段代碼是不是就是把自身的代碼進行運算(異或取反)呢? OK !

那麼如果你這段代碼中間設下一個斷點,會有什麼變化呢?運算得到的“值”會和原來未設斷時的相同嗎?

我們以SoftICE來簡單介紹一下吧。當你在SoftICE的調試環境下,設下一個斷點時,那麼這個斷點所在的代碼的第一個位元組就會變成 0xCC。
這樣由於設下斷點,代碼就被改變了,那麼運算的“值”就和未設斷時不一樣了。你可能想到有了這個值,就可以用比較的方法來判斷是否自身是否被設下斷點了。不錯,這是一種方法,但是這很容易被......在加殼軟體中,一般會把這個“值”作為還原密匙,來還原下一段代碼。
也就是所謂的 SMC 技巧來還原代碼。

(但是如果你在SoftICE中用 D 指令來顯示這行代碼,卻沒有發現變化。呵呵,但是,如果你再啟動另一個調試器TRW2000,來顯示這行代碼,再看看?呵呵,明白了嗎?)

②、SMC技巧來還原代碼。
在加殼軟體中,分段還原代碼是一種很普遍的方法了。也許你有過這樣的經曆。(怎麼,弄得象散文了?!)你跟蹤過某個軟體,發現了某段代碼,比如 CPUID (相應的十六進位代碼為 0x0F 0xA2)。卻發現當你用十六進位編輯軟體來尋找 0x0F 0xA2 時,卻怎麼也找不到,Why?

其實道理很簡單,就是在程式的可執行檔中儲存的是加密了的資料,只有程式在運行時才會由程式在某處由一段還原代碼來解密這段加密資料,(還原後的資料就是你在調試器中可以“看”到的真實的代碼)。然後,程式才執行這段還原後的代碼。現在你清楚了吧!比如象 ① 中 介紹的那段代碼是用來形成還原密匙的,只有密匙正確時,也就是說它先四下“打量”一下,當“發現”自身沒有被修改(或者被設下斷點)時,才“悄悄地”還原出另一段被加密了的代碼。然後繼續!
有點意思吧!在加殼軟體中,如果它“想”保護某段代碼,就會加密它,然後在程式運行時用另一段代碼形成密匙來還原它!這樣就防止你靜態分析它,當然為了防止你用動態跟蹤的方法,所以它還結合了 ① 中介紹的自身保護方法。這樣,如果你誤入陷阱(在其自身保護的某處設下了一個斷點),那麼還原出的代碼就是一堆垃圾代碼。如果這樣,你可不要寫信給作者,說他程式編得有問題哦?!

(當然 SMC 技巧可以被用作多種用途。不要大家有先入為主的概念,SMC的技巧既可以被加殼軟體用作自身保護的一種方法,也可以被Cracker作為一種破解的方法,所以......怎麼用,還在於你!......你想用來編“病毒”.....老天,為什麼天才總有點反叛因子!)

前面介紹了簡單的自身保護技巧,也許你早就想好了對付它的方法,比如:
不設任何斷點,用單步跟蹤的方法
或者先跟蹤一次記下正確的密匙,下次跟蹤時任意設斷,只在它取密匙時“給”個正確的給它。
或者用 BPM 斷點只跟蹤資料、不跟蹤代碼的方法進行動態跟蹤。
你還可以想得更多。。。。

當然,為了更好地保護自身,所以在加殼軟體中也不會只是這麼簡單地保護自己,你猜加殼軟體中又會用到什麼保護方法可以防止以上方法呢?

③、一種反跟蹤方法:API調用的變形。
你也許喜歡首先在某個關鍵的API函數處設斷,然後才進入程式碼中進行跟蹤。但是在加殼軟體中這種方法可就要小心了。
我們來看看這段代碼:

015F:00411B6A  33C0                XOR       EAX,EAX                    <--ESI指向API函數地址的入口,如CreateFileA( )
015F:00411B6C  AC                  LODSB                                <--擷取一個位元組
015F:00411B6D  3C50                CMP       AL,50                        <--判斷是否為 50
015F:00411B6F  720F                JB        00411B80                    <--小於 50 ,則跳轉到 00411B80
015F:00411B71  3C57                CMP       AL,57                        <--判斷是否為 57
015F:00411B73  770B                JA        00411B80                    <--大於 57 ,則跳轉到 00411B80
......
015F:00411BD3  897A01              MOV       [EDX+01],EDI
015F:00411BD6  8B831F7B0000        MOV       EAX,[EBX+00007B1F]
015F:00411BDC  8B8B237B0000        MOV       ECX,[EBX+00007B23]
015F:00411BE2  8B93277B0000        MOV       EDX,[EBX+00007B27]
015F:00411BE8  8BBB3B7B0000        MOV       EDI,[EBX+00007B3B]
015F:00411BEE  8BB3377B0000        MOV       ESI,[EBX+00007B37]
015F:00411BF4  8BAB337B0000        MOV       EBP,[EBX+00007B33]
015F:00411BFA  8B9B2B7B0000        MOV       EBX,[EBX+00007B2B]
015F:00411C00  E900000000          JMP       00411C05                    <--此處的跳轉將被修改為API函數地址內的一條指令處

(說明:進入這段代碼時,ESI指向了某個 API 函數的入口地址,ESP指向的堆棧中壓入了該 API 函數需要的各個參數。由於代碼較長,沒有完整列出)

以上這段代碼的作用,就是通過分析(或者說反組譯碼) API 函數開始的部分代碼,然後把這部分代碼“複製”到自己的進程空間中執行後,再進入該 API 函數的內部某處代碼繼續執行 API 函數。

這樣,當你在此 API 函數入口處(比如: bpx CreateFileA)設斷時,就會“攔”不到它,為什嗎?因為,它不從 API 函數入口處執行。而是繞了個彎從“側門”進入的。瞧,加殼軟體多有意思!也許,這時你會想,那麼就在 API 函數的內部某處代碼設斷不就行了嗎?你要小心,加殼軟體有善良的,也有喜歡“惡作劇”的,它通過分析 API 函數開始部分的代碼,一方面進行“反組譯碼”,另一方面,如果你在它分析的代碼中設下了斷點時,也可能他會痛下殺手,因為斷點的代碼為 0xCC,它可不喜歡在 API 函數中出現這種指令。如果你由此當機了..........

④、反動態跟蹤的方法。
當然,加殼軟體也可能不只是把眼光放在防範你設斷點,也可能直接就把“眼光”放在防範調試狀態上了。比如最直接的就是檢測你的當前環境中是否載入了調試器或者是某些工具軟體。比如:

This method is most known as 'MeltICE' because it has been freely distributed
via www.winfiles.com. However it was first used by NuMega people to allow Symbol
Loader to check if SoftICE was active or not (the code is located inside nmtrans.dll).

The way it works is very simple:
It tries to open SoftICE drivers handles (SICE, SIWVID for Win9x, NTICE for WinNT)
with the CreateFileA API.

Here is a sample (checking for 'SICE'):

BOOL IsSoftIce95Loaded()
{
   HANDLE hFile;  
   hFile = CreateFile( "////.//SICE", GENERIC_READ | GENERIC_WRITE,
                      FILE_SHARE_READ | FILE_SHARE_WRITE,
                      NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
   if( hFile != INVALID_HANDLE_VALUE )
   {
      CloseHandle(hFile);
      return TRUE;
   }
   return FALSE;
}

以上是摘自FrogsICE的文檔。利用 CreateFile( ) 開啟一些特殊的“檔案”,如果傳回值不是 -1,它就可以“發現”載入了SoftICE。當然這種方法由於可以在WIN98/NT下通用,所以很常見。
類似的,如果你把檢測的字串 //./SICE 改成
//./NTICE                檢測NT下的SoftICE
//./FILEMON                檢測FileMon
//./REGMON                檢測RegMon
//./TRW                    檢測Trw
//./TRWDEBUG            檢測Trw
//./ICEDUMP                檢測IceDump
就可以“發現”其他的跟蹤了。

另一種常見的檢測SoftICE的方法如下,同樣摘自FrogsICE的文檔。

* * SOFTICE SHOULD NOT BE LOADED SO THAT FROGSICE CAN DETECT THIS METHOD * *

This method of detection of SoftICE (as well as the following one) is
used by the majority of packers/encryptors found on Internet.
It seeks the signature of BoundsChecker in SoftICE

    mov     ebp, 04243484Bh        ; 'BCHK'
    mov     ax, 04h
    int     3       
    cmp     al,4
    jnz     SoftICE_Detected

其實檢測SoftICE方法有很多,在 FrogsICE 的文檔中介紹了一些,這裡的介紹只是窺其一斑而已。如果你有最“新”的反跟蹤方法,可一定要通知我哦。^_^

⑤、自身保護和反動態跟蹤相結合:
最後我們來看看這段代碼:

015F:0040DDD8  01FF                ADD       EDI,EDI
015F:0040DDDA  C783CB18000090010000MOV       DWORD PTR [EBX+000018CB],00000190 
015F:0040DDE4  8BEB                MOV       EBP,EBX
015F:0040DDE6  BA561E0000          MOV       EDX,00001E56
015F:0040DDEB  03D3                ADD       EDX,EBX
015F:0040DDED  52                  PUSH      EDX
015F:0040DDEE  6467FF360000        PUSH      DWORD PTR FS:[0000]        <--SEH
015F:0040DDF4  646789260000        MOV       FS:[0000],ESP                <--SEH
015F:0040DDFA  89A3A3760000        MOV       [EBX+000076A3],ESP
015F:0040DE00  BECD1E0000          MOV       ESI,00001ECD
015F:0040DE05  03F3                ADD       ESI,EBX
015F:0040DE07  8BFE                MOV       EDI,ESI                    <--EDI=40DECD
015F:0040DE09  B90F0A0000          MOV       ECX,00000A0F                <--ECX=0xA0F,ECX儲存的是迴圈次數
015F:0040DE0E  8B93FC760000        MOV       EDX,[EBX+000076FC]

;--------------------------------------------------------------------------------------------------
;以下這一段代碼同 1 中介紹的代碼類似。
;也就是把程式碼片段 ( 015F:40D8E6 - 015F: 40DECA ) 之間的代碼和 EDX 的初始值進行運算來形成一個“密匙”
;結果仍然儲存在 EDX 中。
;--------------------------------------------------------------------------------------------------
015F:0040DE14  56                  PUSH      ESI                        <--迴圈開始處
015F:0040DE15  51                  PUSH      ECX                        <--入棧儲存,ECX儲存的是迴圈的次數=A0F。
015F:0040DE16  B979010000          MOV       ECX,00000179
015F:0040DE1B  BEE6180000          MOV       ESI,000018E6
015F:0040DE20  03F3                ADD       ESI,EBX                    <--ESI=40C000+18E6=40D8E6。(40C000是程式入口)
015F:0040DE22  8B06                MOV       EAX,[ESI]                    <--取代碼中的 4 個位元組
015F:0040DE24  33D0                XOR       EDX,EAX
015F:0040DE26  33D1                XOR       EDX,ECX
015F:0040DE28  83C604              ADD       ESI,04
015F:0040DE2B  49                  DEC       ECX                        <--迴圈次數減一,ECX初始值為179
015F:0040DE2C  75F4                JNZ       0040DE22                    <--這一段是和前面介紹的相同的方法進行自身保護
015F:0040DE2E  59                  POP       ECX                        <--出棧
015F:0040DE2F  5E                  POP       ESI                        <--出棧
;--------------------------------------------------------------------------------------------------
;這段代碼用 EDX 中的“密匙”來還原 EDI 指向中的被加密了的代碼,EDI初始值為 40DECD
;--------------------------------------------------------------------------------------------------
015F:0040DE30  AD                  LODSD
015F:0040DE31  33C2                XOR       EAX,EDX
015F:0040DE33  AB                  STOSD
;--------------------------------------------------------------------------------------------------
;這段代碼用來進行反跟蹤
;--------------------------------------------------------------------------------------------------
015F:0040DE34  0F018BA57A0000      SIDT      FWORD PTR [EBX+00007AA5]    <--取IDTR的內容
015F:0040DE3B  8BB3A77A0000        MOV       ESI,[EBX+00007AA7]            <--取IDT表基地址
015F:0040DE41  894E08              MOV       [ESI+08],ECX                <--修改 int 1 的處理常式地址為 ECX,讓你死翹翹。
;--------------------------------------------------------------------------------------------------
;把“密匙”進行變換。
;--------------------------------------------------------------------------------------------------
015F:0040DE44  3393521E0000        XOR       EDX,[EBX+00001E52]
015F:0040DE4A  8BF7                MOV       ESI,EDI
015F:0040DE4C  EB70                JMP       0040DEBE
......(省略)
015F:0040DEBE  FF834E1E0000        INC       DWORD PTR [EBX+00001E4E]
015F:0040DEC4  33D1                XOR       EDX,ECX
;--------------------------------------------------------------------------------------------------
; 判斷迴圈是否結束,即此時後面的所有被加密的代碼都已經被還原
;--------------------------------------------------------------------------------------------------
015F:0040DEC6  49                  DEC       ECX                        <--迴圈次數減一,ECX初始值為A0F
015F:0040DEC7  0F8547FFFFFF        JNZ       0040DE14                    <--迴圈結束處
015F:0040DECD  5F                  POP       EDI                        <--被加密了的代碼
015F:0040DECE  44                  INC       ESP                        <--未還原的代碼

這段代碼比較長,所以要看懂它得花點時間。這是一種把“反動態跟蹤”和“自身保護”結合的一種方法。
可以看到 015F:0040DECD 之後的代碼已經被加密了,這段代碼就是用來還原被加密了的代碼的。當這段代碼迴圈結束後,後面的被加密了的代碼就已經還原出來了,這就是 SMC 技巧的應用。在這段代碼中“密匙”是由前面所有的代碼來運算得到的。並且每迴圈一次就變換一次。這就是 1 中介紹的自身保護。防止你修改它的代碼或者設下斷點跟蹤。
另外,程式中還加入了一種“反動態跟蹤”的方法,就是修改了 單步中斷 的中斷處理常式的地址。這樣,你的跟蹤環境就被破壞了。

015F:0040DE34  0F018BA57A0000      SIDT      FWORD PTR [EBX+00007AA5]    <--取IDTR的內容
015F:0040DE3B  8BB3A77A0000        MOV       ESI,[EBX+00007AA7]            <--取IDT表基地址
015F:0040DE41  894E08              MOV       [ESI+08],ECX                <--修改 int 1 的處理常式地址為 ECX,讓你死翹翹。

這就是加殼軟體的特點,攻防結合。所以一不小心,你就可能落入了它設下的陷阱,見到了“如來佛”。所以為了防止被它送到西天。你就得下點功夫,瞭解自身保護、反跟蹤的特點和技巧。加殼軟體一般就是這樣一個陷阱重重的地方。當然加殼軟體的保護機制也有它的弱點,只要你的程式指令機器“讀”得懂,那麼動態跟蹤不行,就有靜態分析,或者動靜結合。所以才會各種相應的脫殼機的出現。當然這是一個“矛”和“盾”的關係,孰強孰弱,我想關鍵在於運用得當和推陳出新。比如,我常會結合MD5/RSA/BLOWFISH等密碼編譯演算法進行註冊碼計算,怎麼樣?!哈哈,然後用IF指令進行註冊判斷!?...................

【後記】
由於這些保護機制的方法出現的頻率比較多,所以介紹一下也無妨。其實,這些東西很早就有了。但是對於生產水貨的我,常常喜歡來點新瓶裝舊酒。另外,我對ANTI-DEBUG瞭解得還不全面,也希望能夠拋磚引玉,能引出更多的高手,讓大家來進行打假。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.