彙編api函數地址的獲取

api函數地址的獲取

                                by Hume/冷雨飄心

這是一個老題目了,如果我們不用任何引入庫,能否在程序中調用api函數?當然可以!方法有很多,你可能早就知道了,如果你已經瞭解了,就此打住,這是爲還不瞭解這一技術而寫.另外這也是病毒必用的技巧之一,如果你對病毒技術感興趣,接着看下去.

這裏假設你瞭解PE的基本結構,如果還不懂,找點資料來看看,到處都是呦.

在幾乎每個病毒的開頭都用下面的語句:
        call    delta
delta: 
        pop    ebp                           
        sub    ebp,offset delta               
        mov    dword ptr [ebp+offset appBase],ebp
       
讓我們考慮一下程序的執行情況,如果下面的代碼由編譯器自動編譯連接,那麼程序執行的基址一般是400000h,如果是在Nt下執行,那麼基址可能不同,比如從100000h開始,不用擔心,操作系統的Loader會自動爲你重定位.但是這裏停下來讓我們看一下,如果你想要把這段代碼附加到其他程序的後面並想讓其正確執行的話,就不是那麼簡單了,因爲你的代碼可能要從555588h處開始執行,而在沒有得到宿主程序許可的情況下期望操作系統自動爲你修正偏移錯誤是不可能的,既然有非常的目的,就得費點力氣,自己搞定重定位.而上面的代碼就是首先得到eip指針,也是delta在程序執行時的實際偏移,然後減掉代碼頭到delta的偏移從而得到你的代碼的真正基址,後面對於偏移的操作都應以這個真正的偏移爲準.這就是你上面看到的.如果不明白,就仔細想一下,nothing difficult!下面的例子演示了這一點,並沒有全部重定位,因爲這只是技術演示.

現在回到本文的正式內容,要想獲得api的地址,得首先獲得諸如kernel32.dll,user32.dll的基址,然後再找到真正的函數地址.如何獲得基址和函數地址呢呢?有幾種方法
        1)搜尋宿主的引入表獲得GetModuleHandleA函數和GetProcAddress的地址,然後通過他返回系統dll的基址.因爲很多程序都要使用這兩個函數,因此在某些情況下是可行的,如果宿主沒有使用GetProcAddress,那你就不得不搜尋Export表了.
        2)直接獲得kernel32.dll的基址,然後再搜尋Export表獲得GetProcAddress和LoadLibraryA的地址,然後我們就能得到任何想調用的函數地址.

        3)硬編碼調用函數,比如在9X下GetModuleHandleA的地址一般是BFF7****.

第一種和第三種方法存在兼容性的問題,假如宿主沒有調用GetModuleHandleA,那麼你就不能獲得基址,別的就更別想了...硬編碼問題更大,操作系統不同則不能運行了,比如9X下可能在有些計算機上正常,但肯定不能在Nt/2K下運行...

第二種方法兼容性比較好,因此作以介紹.

        一點背景:在PE Loader裝入我們的程序啓動後堆棧頂的地址是是程序的返回地址,肯定在Kernel中!

        因此我們可以得到這個地址,然後向低地址縮減驗證一直到找到模塊的起始地址,驗證條件爲PE頭不能大於4096bytes,PE header的ImageBase值應該和當前指針相等,嘿嘿,簡單吧,而且兼容性還不錯.

        要獲得Api的地址首先要獲得GetModuleHandle,LoadLibraryA,GetProcAddress的地址,這是通過搜索Export表來實現的,具體原理就是PE Export表的結構,如果瞭解了PE結構就很簡單了.下面我加了點註釋,沒有優化代碼,是爲了便於理解.

        好,這一部分結束了!

這是一個例子,沒有用任何預引入函數,加了一條invoke InitCommonControls是爲了在2K下也能正常運行,否則不能在2K下不加載!
程序得到MessageBoxA的地址然後顯示一個消息框,目的在於演示,重要部分加了註釋,很好明白.

.586
.model flat, stdcall
option casemap :none  ; case sensitive
include c:\hd\hd.h
include c:\hd\mac.h

;;--------------

GetApiA        proto    :DWORD,:DWORD

;;--------------
    .CODE
appBase        dd ?
k32Base        dd ?

lpApiAddrs      label  near
                dd      offset sGetModuleHandle
                dd      offset sGetProcAddress
                dd      offset sExitProcess
                dd      offset sLoadLibrary
                dd      0

sGetModuleHandle      db "GetModuleHandleA",0
sGetProcAddress        db "GetProcAddress",0
sExitProcess          db "ExitProcess",0
sLoadLibrary          db "LoadLibraryA",0

sMessageBoxA          db "MessageBoxA",0

 
aGetModuleHandle                dd 0
aGetProcAddress                dd 0
aExitProcess                    dd 0
aLoadLibrary                    dd 0
aMessageBoxA                    dd 0

u32                    db "User32.dll",0
k32                    db "Kernel32.dll",0

sztit                  db "By Hume,2002",0
szMsg0                  db "Hey,Hope U enjoy it!",0
;;-----------------------------------------

__Start:
        invoke    InitCommonControls
       
        call    delta
delta: 
        pop    ebp                            ;得到delta地址
        sub    ebp,offset delta                ;因爲在其他程序中基址可能不是默認的所以需要重定位
        mov    dword ptr [ebp+offset appBase],ebp    ;呵呵仔細想想
       
    mov    ecx,[esp]                      ;返回地址
        xor    edx,edx
getK32Base:
        dec    ecx                            ;逐字節比較驗證
        mov    dx,word  ptr [ecx+IMAGE_DOS_HEADER.e_lfanew]  ;就是ecx+3ch
        test    dx,0f000h                      ;Dos Header+stub不可能太大,超過4096byte
        jnz    getK32Base                      ;加速檢驗
        cmp    ecx,dword ptr [ecx+edx+IMAGE_NT_HEADERS.OptionalHeader.ImageBase]
        jnz    getK32Base                      ;看Image_Base值是否等於ecx即模塊起始值,
        mov    [ebp+offset k32Base],ecx        ;如果是,就認爲找到kernel32的Base值
       
        lea    edi,[ebp+offset aGetModuleHandle]
        lea    esi,[ebp+offset lpApiAddrs]
lop_get:
        lodsd
        cmp    eax,0
        jz      End_Get
        push    eax
        push    dword ptr [ebp+offset k32Base]
        call    GetApiA                        ;獲取API地址       
        stosd
        jmp    lop_get
End_Get:
        push    offset u32
        call    dword ptr [ebp+offset aLoadLibrary]    ;在程序空間加載User32.dll
       
        lea    EDX,[EBP+OFFSET sMessageBoxA]
        push    edx
        push    eax
        mov    eax,dword ptr [ebp+aGetProcAddress]    ;用GetProcAddress獲得MessageBoxA的地址
        call    eax                                    ;調用GetProcAddress

        push    40h+1000h                              ;style
        push    offset sztit                            ;title
        push    offset  szMsg0                          ;消息內容
        push    0
        call    eax                                    ;一個消息框產生了...嘿嘿
                                                        ;有理由爲此高興吧,因爲我們沒有預先引入
@@:                                                    ;這些函數
        push    0
        call    [ebp+aExitProcess]
       
;-----------------------------------------
K32_api_retrieve        proc    Base:DWORD ,sApi:DWORD

        push    edx                    ;保存edx   
        xor    eax,eax                ;此時esi=sApi
Next_Api:                              ;edi=AddressOfNames
        mov    esi,sApi
        xor    edx,edx
        dec    edx
Match_Api_name:
        mov    bl,byte  ptr [esi]
        inc    esi
        cmp    bl,0
        jz      foundit

        inc    edx

        push    eax
        mov    eax,[edi+eax*4]        ;AddressOfNames的指針,遞增
        add    eax,Base                ;注意是RVA,一定要加Base值
        cmp    bl,byte  ptr [eax+edx]  ;逐字符比較 
        pop    eax
        jz      Match_Api_name          ;繼續搜尋
        inc    eax                    ;不匹配,下一個api
        loop    Next_Api
        jmp    no_exist                ;若全部搜完,即未存在
foundit:
        pop    edx                    ;edx=AddressOfNameOrdinals
        shl    eax,1                  ;*2得到AddressOfNameOrdinals的指針
        movzx  eax,word  ptr [edx+eax] ;eax返回指向AddressOfFunctions的指針
        ret
no_exist:
        pop    edx
        xor    eax,eax
        ret
K32_api_retrieve        endp
;-----------------------------------------

GetApiA        proc    Base:DWORD,sApi:DWORD
        local    ADDRofFun:DWORD
        pushad
        mov    edi,Base
        add    edi,IMAGE_DOS_HEADER.e_lfanew
        mov    edi,[edi]                      ;現在edi=off PE_HEADER
        add    edi,Base                        ;得到IMAGE_NT_HEADERS的偏移                       

        mov    ebx,edi
        mov    edi,[edi+IMAGE_NT_HEADERS.OptionalHeader.DataDirectory.VirtualAddress]
        add    edi,Base                        ;得到edi=IMAGE_EXPORT_DIRECTORY入口
       
        mov    eax,[edi+1ch]                  ;AddressOfFunctions的地址
        add    eax,Base
        mov    ADDRofFun,eax
                                                ;ecx=NumberOfNames
        mov    ecx,[edi+18h]                 
        mov    edx,[edi+24h]                 
        add    edx,Base                        ;edx=AddressOfNameOrdinals

        mov    edi,[edi+20h]
        add    edi,Base                        ;edi=AddressOfNames
        invoke    K32_api_retrieve,Base,sApi
        mov    ebx,ADDRofFun
        shl    eax,2                          ;要*4纔得到偏移
        add    eax,ebx
        mov    eax,[eax]
        add    eax,Base                        ;加上Base!
        mov    [esp+7*4],eax                  ;eax返回api地址
        popad
        ret
GetApiA        endp

;-----------------------------------------

END    __Start
;------------------------------------------End all
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章