保護機制小窺 ---- 十三少不妨來看看

看雪資料發表於2000-12-03

保護機制小窺

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

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

水貨作者:        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瞭解得還不全面,也希望能夠拋磚引玉,能引出更多的高手,讓大家來進行打假。


【有個問題】
最近上網,總是拔不上來。
後來我開啟了"拔號號出現終端視窗"的功能,發現拔號後會出現兩種提示。比如:
Welcome to xxx xxx !
Welcome to yyy yyy !
如果出現第一種,那麼我總是拔不上來,即使正常的密碼也上不來,會出現而如果出現第二種提示,那麼我總能拔上來。真奇怪!這會是什麼原因?最近,上網實在是件痛苦的事件,總是很難拔上來,一般只有到0:00點以後才會正常。

相關文章