病毒編程技術-4

來源:互聯網
上載者:User
解析PE檔案的匯出函數表

  PE檔案的函數匯出機制是進行模組間動態調用的重要機制,對於正常的程式,相關操作是由系統載入器在程式載入前自動完成的,對使用者程式是透明的。但要想在病毒代碼中實現函數地址的動態解析以取代載入器,那就有必要瞭解函數匯出表的結構了。在圖1中可以看到在PE頭結構IMAGE_OPTIONAL_HEADER32結構中包含一個DataDirectory數組結構,該結構包含16個成員,每個成員都是一個IMAGE_DATA_DIRECTORY結構:
  typedef struct _IMAGE_DATA_DIRECTORY {
      DWORD   VirtualAddress;
      DWORD   Size;
  } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
  DataDirectory數組的每個結構都指向一個重要的資料結構,第一個成員指嚮導出函數表(索引0),第2個成員指向PE檔案的引入函數表(索引1)。DataDirectory中的第一個成員指嚮導出函數表的IMAGE_EXPORT_DIRECTORY結構:
typedef struct _IMAGE_EXPORT_DIRECTORY {
    DWORD   Characteristics;
    DWORD   TimeDateStamp;
    WORD    MajorVersion;
    WORD    MinorVersion;
    DWORD   Name;
    DWORD   Base;
    DWORD   NumberOfFunctions;
    DWORD   NumberOfNames;
    DWORD   AddressOfFunctions;     // RVA from base of image
    DWORD   AddressOfNames;         // RVA from base of image
    DWORD   AddressOfNameOrdinals;  // RVA from base of image
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;
 AddressOfFunctions是一個雙字數組,包含了所有匯出函數的RVA,另外兩個成員AddressOfNames也是一個雙字數組,包含了指嚮導出函數名字的字串的RVA,AddressOfNameOrdinals是一個字數組(16bit),和AddressOfNames數組是並行的,和AddressOfNames數組一起確定了相應引出函數的序號,該序號可直接用於索引AddressOfFunctions數組擷取匯出函數的地址。因此病毒搜尋指定的API就包含了如下步驟:

a)擷取NumberOfNames的值以及AddressOfNames、AddressOfNameOrdinals和AddressOfFunctions的數組的地址。
b)搜尋AddressOfNames數組,按字串對比,若找到相應的API,轉d
c)若NumberOfNames名字尚未全部搜尋完畢,轉b繼續搜尋,若搜尋完畢,則表明未找到進行錯誤處理,這一步通常可以省略,因為我們已經知道相應的DLL中肯定匯出了相應的函數。
d)擷取當前函數名字指標在AddressOfNames數組中的索引,在AddressOfNameOrdinals數組中取出以該值索引的函數序號,以該序號值作為AddressOfFunctions數組的索引,在AddressOfFunctions數組中取出匯出函數的RVA值,加上基址就得到了運行時匯出函數的地址。

        看起來似乎比較羅嗦,實際上這是PE設計時為考慮靈活性而做出的犧牲。不過實現起來還是比較簡單的,通常彙編代碼編譯後不到100位元組。以下是在Kernel32搜尋GetProcAddress的完整代碼:

       push     esi
                                ;esi=VA Kernel32.BASE
                                ;edi=RVA K32.pehdr
        mov     ebp,esi
        mov     edi,[ebp+edi+peh.DataDirectory]

        push    edi esi

        mov     eax,[ebp+edi+peexc.AddressOfNames]
        mov     edx,[ebp+edi+peexc.AddressOfNameOrdinals]
        call    @F
        db     "GetProcAddress",0
      @@:
        pop     edi
        mov     ecx,15
        sub     eax,4
     next_:
        add     eax,4
        add     edi,ecx
        sub     edi,15
        mov     esi,[ebp+eax]
        add     esi,ebp
        mov     ecx,15
        repz    cmpsb    ;進行字串比較,判斷是否為要尋找的函數
        jnz     next_

        pop     esi edi

        sub     eax,[ebp+edi+peexc.AddressOfNames]
        shr     eax,1
        add     edx,ebp
        movzx   eax,word [edx+eax]
        add     esi,[ebp+edi+peexc.AddressOfFunctions]
        add     ebp,[esi+eax*4]      ;ebp=Kernel32.GetProcAddress.addr
                                         ;use GetProcAddress and hModule to get other func
        pop     esi                     ;esi=kernel32 Base
        在前面解析匯出函數表擷取API地址的時候,採用的是直接比較字串的方法判斷是不是找到了相應的API,其實還可以計算函數名字的hash,然後同預計算的hash進行比對,現代的PE病毒更多採用的hash的方法,其原因在於一般的函數名字長度都大於4位元組,而用hash只要佔用4個位元組或2個位元組,可以節省空間的,此外還有抗病毒分析的作用,因為hash要比字串名字費解得多。hash演算法的設計只要能保證無衝突即可,可以用crc等成熟演算法,也可以設計自己的簡單演算法。在Elkern中就使用了crc16演算法。

* 檔案搜尋

  檔案搜尋是病毒的重要功能模組之一,也是實現感染和傳播的關鍵。現代Windows和各種移動介質的檔案系統可能採用多種複雜格式,因此象一些Dos病毒一樣試圖直接存取檔案系統(讀寫扇區)是不大現實的。通常利用Win32 API的FindFirstFile和FindNextFile來實目前的目錄下所有目錄和檔案的搜尋,通過判斷搜尋到的檔案屬性,可區分是否為目錄或可執行檔,對於可執行檔則根據預先設計好的感染策略進行感染;對於目前的目錄下的所有子目錄以及特殊的..父目錄,可以使用遞迴或非遞迴的方式利用上述兩個API全部進行遍曆,因此從某個磁碟機或網際網路共用檔案夾的任意一個子目錄開始,都可以遍曆當前磁碟機或網際網路共用檔案夾內的所有檔案和目錄。一般地,搜尋檔案從磁碟機或共用資料夾的根目錄開始,那麼如何得到當前系統中存在的所有磁碟機或所有的共用資料夾列表呢?對於前一個問題,我們知道Windows下可劃分A:~Z:共26個邏輯盤符,因此可以從A:開始遞增搜尋所有的磁碟機,使用Win32 API GetDriveType判斷當前搜尋的盤符是否存在,以及是否是固定硬碟、抽取式存放裝置介質、是否可寫或是網路磁碟機等。一般病毒只感染固定硬碟或網路磁碟機。由於組合語言在表述演算法時顯得過於冗長,因此演算法部分使用C語言描述,當然將C演算法轉換成組合語言是很簡單的過程。
  下面的代碼enumdisk.cpp將顯示A-Z各個磁碟機的相關屬性:

#include
#include

#define MAX_DRIVENAME_LENGTH    64
void __cdecl main(int argc,char *argv[])
{
    char DriveName[MAX_DRIVENAME_LENGTH];
    char *p;
    unsigned int drv_attr;

    p = DriveName;
    strncpy(DriveName,"A:",MAX_DRIVENAME_LENGTH);

    for(;*p<'Z';++*p) {
        drv_attr = GetDriveType(p);
       
        switch(drv_attr)
        {
        case DRIVE_UNKNOWN:  // 未知類型
            printf("drive %s type %s/n",p,"DRIVE_UNKNOWN");break;   
        case DRIVE_NO_ROOT_DIR:  // 該磁碟機不存在
            printf("drive %s type %s/n",p,"DRIVE_NO_ROOT_DIR");break;
        case DRIVE_REMOVABLE: // 可移動盤,磁碟片或隨身碟或移動硬碟等
            printf("drive %s type %s/n",p,"DRIVE_REMOVABLE");break; 
        case DRIVE_FIXED:  // 固定硬碟
            printf("drive %s type %s/n",p,"DRIVE_FIXED");break;     
        case DRIVE_REMOTE:  // 一般是映射網路磁碟機
            printf("drive %s type %s/n",p,"DRIVE_REMOTE");break;    
        case DRIVE_CDROM:  // 光碟片
            printf("drive %s type %s/n",p,"DRIVE_CDROM");break;     
        case DRIVE_RAMDISK:  // RAM DISK
            printf("drive %s type %s/n",p,"DRIVE_RAMDISK");break;   
        }
    }
}
  
  與僅僅顯示一條資訊不同的是,病毒此時將調用檔案枚舉函數(如後面給出的enum_path函數)從當前根目錄開始遍曆DRIVE_FIXED的磁碟機上的所有檔案,根據預定義策略進行檔案感染。
 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.