Billy Belceb 病毒編寫教程for Win32 ----簡單介紹

看雪資料發表於2015-11-15

【簡要介紹】
~~~~~~~~~~~
    好了,開始把你的大腦中的16位MS-DOS編碼概念,迷人的16位偏移地址,中斷,駐留記憶體的方法...都清除掉。所有這些我們已經使用了很多年的東西,現在已經再也不用了。是的,它們確實現在用不到了。在這篇教程裡面,當我說Win32,我的意思是Windows 95(normal,OSR1,OSR2),Windows 98,Windows NT或Windows 3.x+Win32s。最最明顯的變化,至少在我看來是由中斷變成了API,在這之前是由16位暫存器和偏移地址變到了32位的。Windows給我們開了使用其它語言代替ASM(和C)的方便之門,但是我仍然對ASM情有獨鍾:利用它能更好的理解一些東西和更容易的最佳化(hi Super!)。正如我在上面所說的,你必須使用一種新東西叫做API。你必須知道這些引數必須在堆疊中,而且呼叫這些API是使用的CALL。
    注:在上面我把上面所有提到的平臺叫做Win32,我把Win95(它的所有版本)和Win98叫做Win9x,把Windows 2000叫做Win2k。請注意這一點。

%由16位到32位程式設計的改變%
~~~~~~~~~~~~~~~~~~~~~~~~
     我們現在將會使用雙字(DWORD)而不是單字(WORD)了,而這個改變將會給我們一個全新的世界。在已知的CS,DS,ES和SS:FS,GS之外,我們又多了兩個段。而且我們有32位暫存器如EAX,EBX,ECX,EDX,ESI,EDI,EBP和ESP。讓我們來看看對這些暫存器怎麼操作:假如我們要使用EAX的less significant word(簡稱LSW),我們該怎麼做呢?這個部分可以使用AX來訪問,即處理LSW。假如EAX=0000000,我們想要在它的LSW放置1234h。我們必須簡單地使用一個"mov ax,1234h"就可以了。但是如果我們想要訪問EAX的MSW(Most Significant Word),該怎麼做呢?為了達到這個目的我們不能使用一個暫存器了:我們必須使用ROL。問題並不是在這裡,它是把MSW值移到了LSW。
    當我們得到一個新語言的時候,我們總是要試的一個經典的例子:"Hello world!"  :)

%Win32中的Hello World%
~~~~~~~~~~~~~~~~~~~~~~
    它很簡單,我們必須使用"MessageBoxA"這個API,所以我們用大家已經知道的"extrn"命令來定義它,把引數壓棧然後呼叫這個API。注意這個字串必須為ASCIIZ(ASCII,0),記住引數必須以相反的順序壓棧。

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

                .386                            ; Processor (386+)
                .model flat                     ; Uses 32 bit registers

 extrn          ExitProcess:proc                ; The API it uses
 extrn          MessageBoxA:proc

;-
;利用"extrn"我們把在程式中要用到的所有API列出來。ExitProcess是我們用來把
;控制權交給作業系統的API,而MessageBoxA用來顯示一個經典的Windows訊息框。
;-

 .data
 szMessage       db      "Hello World!",0       ; Message for MsgBox
 szTitle         db      "Win32 rocks!",0       ; Title of that MsgBox

;------------------------------------------------------------------------------------------------------------------------------
;這裡我們不能把真正病毒的資料放這裡了,因為這是一個例子,我們不能
;使用它,而且又因為如果我們不在這裡放置一些資料,TASM將會拒絕彙編。
;無論如何...在第一次產生你的病毒主體的時候用它放置資料。
;-

                .code                           ; Here we go!

 HelloWorld:
                push    00000000h               ; Sytle of MessageBox
                push    offset szTitle          ; Title of MessageBox
                push    offset szMessage        ; The message itself
                push    00000000h               ; Handle of owner

                call    MessageBoxA             ; The API call itself

;------------------------------------------- ; int MessageBox(                                                        
;   HWND hWnd,          // handle of owner window                       
;   LPCTSTR lpText,     // address of text in message box               
;   LPCTSTR lpCaption,  // address of title of message box            
;   UINT uType          // style of message box                       
;  );                                                                 
;                                                                     
;在呼叫這個API之前,我們把引數壓棧,如果你還記得,堆疊使用那個迷人的
;東西叫做LIFO(後進先出Last In First Out),所以我們要按相反的順序來
;壓引數。讓我們看看這個函式的每個引數的簡要描述:
;
; hWnd:標誌將要被建立的訊息框的宿主視窗(owner window)。
;      如果這個引數是NULL,這個訊息框沒有宿主視窗。
; lpText:指向以空字元結尾的包含將要顯示訊息的字串的指標。
; lpCaption:指向一個以空字元結尾的字串的指標,這個字串是這個
;        對話方塊的標題。如果這個引數是一個NULL,預設的標題Error被使用。
; uType:以一些位標誌來確定對話方塊的樣式和行為。這個引數可為一些標誌的組合。
;-

                push    00000000h
                call    ExitProcess

;--------------------------------------------------------------------------------------------------------------------
; VOID ExitProcess(
;   UINT uExitCode      // exit code for all threads
; );
; 這個函式在Win32環境下相當於著名的Int 20h,和Int 21h的00,4C功能等等。
; 它是關閉當前程式的簡單方式,即結束程式執行。下面給出唯一的一個引數:
;
; uExitCode:標誌程式退出的程式碼,並作為所有執行緒終止時的程式碼。使用
; GetExitCodeProcess函式來重新整理這個程式的退出值。使用GetExitCodeThread
; 函式來重新整理一個執行緒的退出值。
;-

 end HelloWorld

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

    正如你看到的,編寫程式碼很簡單。可能沒有16位環境下那麼簡單,但是如果你考慮到32位所帶給我們的優點確實很簡單了。現在,既然你已經知道怎麼來編寫"Hello World",你就有能力來感染整個世界了;)

%Rings%
~~~~~~~
    我知道你對下面的東西很害怕,但是,正如我所演示的,它看起來沒有那麼難。讓我們記住你必須清楚的東西:處理器有4個特權級別:Ring-0,Ring-1,Ring-2和Ring-3,越往後就有越多的限制,而病毒要是用第一個特權級別,幾乎編碼時沒有任何限制。只要記住在迷人的DOS下面,我們總是處於Ring-0...現在想到在Win32平臺下你還可以做相同的事情...好了,停止幻想了,讓我們開始工作。
    Ring-3還表示"使用者"級,在這個級別下,我們有很多的限制,那確實不能我們的需要。Microsoft程式設計師在他們發行Win95的時候犯了個錯誤,聲稱它是"無法感染"的,正如在這個作業系統賣出去之前所表明的,利用可怕的Bizatch(後來改名為Boza,但那是另外一段歷史了)。他們認為這些API不能被一個病毒訪問和使用,但是他們沒有想到病毒編寫者們的超級智慧,所以...我們可以在使用者級下編寫病毒,毫無疑問,你只要看看大量近期釋出的新的Win32執行期病毒,它們都是Ring-3級下的...它們不差,不要誤解了我,Ring-3病毒是現在有可能感染所有Win32環境下檔案的病毒。它們是未來...主要是因為即將釋出的Windows NT 5.0(或者Windows 2000)。我們不得不尋找能使我們的病毒(由Bizatch生成的病毒傳播很差,因為它對API地址"harcoded",並且它們可能會因Windows版本的改變而改變)存活的API,而且我們可以用其它不同的方法來實現,正如我後面解釋到的。
    Ring-0是另外一段歷史了,和Ring-3有著很大的區別。在這個級別下我們擁有核心編碼的級別,"核心(kernel)級別"。是不是很迷人啊?我們可以訪問埠,放置我們還沒有夢想過的程式碼...和原先的彙編最接近的東西。我們不使用一些已知的花招是不能直接訪問一些東西的,如IDT修改,SoPinKy/29A在29A#3裡發表的"Call Gate"技術,或者VMM插入,在Padania或者Fuck Harry病毒裡見到的技術。當我們直接利用VxD的服務時,我們不需要API,而且它們的地址在所有Win9x系統中是被假設為相同的,所以我們"hardcode"它們。我將在fully dedicated to Ring-0這一章裡面深入討論。

%重要的東西%
~~~~~~~~~~~~
    我想無論如何我應該在這篇教程的開頭放上這些,然而我知道知道總比不知道好啊:)好了,讓我們來討論Win32作業系統內部的東西。
    首先,你必須清楚一些概念。讓我們從selector開始。什麼是一個selector呢?相當簡單,它是一個非常大的段,而且它組成了Win32的記憶體,也叫做平坦記憶體。我們可以用4G記憶體(4,294,967,295位元組),僅僅透過使用32位地址。那所有這些記憶體是怎麼組織的呢?看看下面的示意圖:
 __________________________ 
|                          |<-------OFFSET=00000000h <-> 3FFFFFFFh
|    應用程式程式碼和資料    |
|__________________________| 
|                          |<-------OFFSET=40000000h <-> 7FFFFFFFh
|         共享記憶體         |
|__________________________|
|                          |<-------OFFSET=80000000h <-> BFFFFFFFh
|           核心           |
|__________________________|
|                          |<-------OFFSET=C0000000h <-> FFFFFFFFh
|         裝置驅動         |
|__________________________|
                            結果:我們擁有4G可用記憶體。是不是很迷人啊?

    注意一件事情:WinNT的後兩段是分開的。現在我將給出你必須知道的一些定義,其它的一些本文之外的一些概念,我假設你已經知道了。

    VA:

    VA表示Virtual Address,即某些程式的地址,但是在記憶體中(記住在Windows中在記憶體中和在磁碟上是不一樣的)。

    RVA:

    RVA表示Relative Virtual Address。清楚這個概念很重要,RVA是檔案在記憶體對映(由你或由系統)時的偏移地址。

    RAW Data:

    RAW Data是我們用來表示資料物理的儲存的,也就是說,在磁碟(磁碟上的資料!=記憶體中的資料)上的儲存。

    Virtual Data:

    Virtual Data是指那些已經被系統載入記憶體的資料。

    File Mapping:

    一種技術,在所有的Win32環境下都有,由一種快速的(並使用更少記憶體)檔案操作方法和比DOS更容易理解的方法組成。所有我們在記憶體中改變的東西,在磁碟上也會改變。檔案對映還是所有Win32環境(甚至NT)下記憶體之間交換資訊的唯一方法。

%怎麼來編譯東西%
~~~~~~~~~~~~~~~~
    該死,我幾乎忘記了這個:)編譯一個Win32 ASM程式的通常引數是,至少在這篇教程的所有例子中,按如下(當ASM檔案的名字為'program',但是沒有任何副檔名):

        tasm32 /m3 /ml program,,;
        tlink32 /Tpe /aa program,program,,import32.lib
        pewrsec program.exe

    我希望足夠清晰了。你還可以使用makefiles,或者建立一個bat檔案來使它自動完成(就象我做的!)。

相關文章