溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

從病毒開始聊聊那些windows下大雜燴

發(fā)布時(shí)間:2020-09-02 20:45:14 來源:網(wǎng)絡(luò) 閱讀:856 作者:長(zhǎng)路慢 欄目:開發(fā)技術(shù)

?豬年送安康,祝大家新一年健康、快樂。愿大家都做一個(gè)勤奮努力、真誠奉獻(xiàn)的人,幸運(yùn)會(huì)永遠(yuǎn)的眷顧你們。
?
引子:
?某一天饒有興趣在卡飯上瀏覽著帖子,故事的相遇就那么簡(jiǎn)單。當(dāng)時(shí)一條評(píng)論勾起我的好奇心,那么好逆向開始。
?根據(jù)我的習(xí)慣,拿到樣本我會(huì)線上惡意代碼分析,直接拉到virustotal之類的網(wǎng)站上,看看是否已經(jīng)被大多數(shù)殺毒軟件所能識(shí)別,看一些有價(jià)值的數(shù)據(jù),如下圖所示:
從病毒開始聊聊那些windows下大雜燴
??????????????????圖片一:基本信息
?當(dāng)看到這個(gè)頁面時(shí)候,看到最后的分析日期是18年11月,又看了一下導(dǎo)出表的函數(shù)信息,是一款老病毒。根據(jù)各大廠商對(duì)這個(gè)病毒行為特性、分析定位為特洛伊、偽裝等,定位不一很正常......,其實(shí)興趣降低了一大半,并不是新鮮品種,但不能這樣侮辱一個(gè)病毒!接著習(xí)慣性拉入到IDA中,當(dāng)我看到熟悉的匯編之后,如下圖所示:
從病毒開始聊聊那些windows下大雜燴
??????????????????圖片二:GetProcAddress實(shí)現(xiàn)
?當(dāng)點(diǎn)進(jìn)去其中的一個(gè)函數(shù),看到了fs寄存器,且一大堆比較復(fù)雜的操作,看到熟悉的匯編指令以后,心中已有定數(shù),這是一個(gè)自己實(shí)現(xiàn)的GetProcAddress函數(shù)。
?
?

理論篇 匯編篇
保護(hù)模式,定時(shí)器,PE雜談 手動(dòng)實(shí)現(xiàn)GetProcAddress函數(shù)及Hash加密字符比對(duì)

?
一、理論篇
?先來看病毒樣本中的一段代碼,如下圖所示:
從病毒開始聊聊那些windows下大雜燴
??????????????????圖片三:CreateTimerQueueTimer
?
?還記著以前分析熊貓燒香時(shí)候的定時(shí)器,如下圖所示:
從病毒開始聊聊那些windows下大雜燴
??????????????????圖片四:SetTimer
?
?惡意代碼大多都會(huì)利用到WinAPI提供的定時(shí)器操作,從而實(shí)現(xiàn)有規(guī)劃、周期性的惡意代碼,既然那么重要,所以我們先來聊聊那些定時(shí)器。
?經(jīng)常用ARK工具的朋友,應(yīng)該都使用過遍歷定時(shí)器相關(guān)的功能,有用戶層定時(shí)器,IO定時(shí)器,DCP定時(shí)器,包括我們的時(shí)鐘中斷機(jī)制,都是具有定時(shí)器相關(guān)操作的。
?我們先從用戶層入手,windbg下深入分析一下上面提到的兩個(gè)定時(shí)器操作,NtSetTimer匯編源碼如下所示:
注:(為什么SetTimer會(huì)調(diào)用NtSetTimer,請(qǐng)看https://blog.51cto.com/13352079/2343452)
函數(shù)原型如下:

UINT_PTR SetTimer(

    HWND hWnd,                        // 窗口句柄

    UINT_PTR nIDEvent,            // 定時(shí)器ID,多個(gè)定時(shí)器時(shí),可以通過該ID判斷是哪個(gè)定時(shí)器

    UINT nElapse,                       // 時(shí)間間隔,單位為毫秒

    TIMERPROC lpTimerFunc  // 回調(diào)函數(shù)

);

?為了更好的理解定時(shí)器的匯編代碼,簡(jiǎn)單分析一下函數(shù)調(diào)用的過程,就是如何獲取當(dāng)前線程。

kd> u PsGetCurrentProcess
nt!PsGetCurrentProcess:
mov     eax,dword ptr fs:[00000124h]
mov     eax,dword ptr [eax+50h]
ret

?
保護(hù)模式:
?那么根據(jù)書籍或者相關(guān)資料,我們知道fs寄存器的值恒定(注意windows7 32位測(cè)試的),內(nèi)核態(tài)是fs = 0x30,用戶態(tài) fs = 0x3B,fs在內(nèi)核態(tài)指向_KPCR,用戶態(tài)指向_TEB.。什么依據(jù)呢?憑什么說fs指向KPCR? 這里屬于保護(hù)模式得內(nèi)容,但是這里還是想與大家一起分享其中的原理,那么先說說段寄存器,為了方便理解做了一個(gè)簡(jiǎn)陋的圖,如下所示:
從病毒開始聊聊那些windows下大雜燴
??????????????????圖片五:段寄存器
?
?其實(shí)段寄存器共96位,只有其中的16位是可見的,剩余部分隱藏,可見的部分就是我們能查詢到的立即數(shù),也叫做選擇子。隱藏部分只可以被CPU操作,不可以使用指令進(jìn)行操作。
?GDT全局描述符表,系統(tǒng)中按照不同的屬性、類型進(jìn)行描述,所以這些描述符統(tǒng)一存儲(chǔ)到內(nèi)存中,并且形成了一個(gè)數(shù)組,這就是GDT。全局描述符的索引保存在了可見部分16位的選擇子中,這就是GDT與段選擇子的關(guān)聯(lián)。如何從選擇子中知道索引呢?如下圖所示:
從病毒開始聊聊那些windows下大雜燴
??????????????????圖片六:選擇子
?
?高13位是索引號(hào),也就是下標(biāo)。TI = 0 代表GDT,TI = 1代表LDT。RPL是當(dāng)前請(qǐng)求特權(quán)級(jí)別,權(quán)限檢查會(huì)用到,這里不對(duì)權(quán)限檢測(cè)做詳細(xì)介紹。
?清楚了上面的知識(shí)后,我們分析一下內(nèi)核態(tài)fs = 30,16位選擇子內(nèi)容,如下圖所示:
從病毒開始聊聊那些windows下大雜燴
??????????????????圖片七:解析fs寄存器
?
?通過上述分解,我們知道了fs在GDT中的第六項(xiàng)(0開始),接著獲取gdtr,并且獲取段描述符的屬性狀態(tài),如下圖所示:
從病毒開始聊聊那些windows下大雜燴
??????????????????圖片八:gdtr寄存器
?段描述符如何來分解?段描述符都有那些屬性呢?如下圖所示:

從病毒開始聊聊那些windows下大雜燴

??????????????????圖片九:通用描述符
?
介紹一些主要屬性:
?

L D/B P S DPL TYPE G
64位代碼段 默認(rèn)操作大小 段有效值 描述符類型 描述符特權(quán)級(jí)別 段類型 粒度

?
?我們按照上圖分解,取Base Address,按照想對(duì)應(yīng)的規(guī)則10101100 01001000 10000100 01000000進(jìn)行地址拼接,其實(shí)這個(gè)就獲取到了KPCR的結(jié)構(gòu)。
?fs寄存器其實(shí)擁有那么的數(shù)據(jù)量,本質(zhì)是是從結(jié)構(gòu)數(shù)據(jù)中獲取,便于操作。推薦一下bochs這款x86硬件平臺(tái)的開源模擬器,學(xué)習(xí)保護(hù)模式,除了書中獲取相關(guān)知識(shí)以外,還可以多多閱讀源碼,才能更深層的學(xué)習(xí)理解。
?
?回到主題,我們既然知道fs在內(nèi)核態(tài)指向的是什么了,我們觀察一下fs:[00000124h]是什么?結(jié)構(gòu)體相關(guān)內(nèi)容以前介紹過,這里不羅嗦,如下圖所示:
從病毒開始聊聊那些windows下大雜燴

??????????????????圖片十:_KPRC
?fs寄存器內(nèi)核態(tài)指向的是_KPRC,fs:[0x124]指向CurrentThread(_EPROCESS),有了這些基礎(chǔ)以后,我們繼續(xù)分析NtSetTimer得調(diào)用過程。
NtSetTimer匯編代碼:(因?yàn)榕虐?所以就上圖了)
從病毒開始聊聊那些windows下大雜燴
??????????????????圖片十一:NtSetTimer解析1
?如上圖所示,先是獲取_ETHREAD,然后獲取了ETHREAD+0x13a(Previous Mode),如下圖所示:
從病毒開始聊聊那些windows下大雜燴
??????????????????圖片十二:

?什么是Previous Mode?,簡(jiǎn)單來說調(diào)用Nt或Zw版本時(shí),系統(tǒng)調(diào)用機(jī)制將調(diào)用線程捕獲到內(nèi)核模式,判定參數(shù)是否來源于用戶模式標(biāo)志。

?The native system services routine checks the PreviousMode field of the calling thread to determine whether the parameters are from a user-mode source.
詳細(xì)得內(nèi)容介紹參考:https://msdn.microsoft.com/zh-cn/windows/desktop/ff559860
PreviousMode其中得兩個(gè)狀態(tài)值
?1、UserMode 狀態(tài)碼是1
?2、KernelMode 狀態(tài)碼是0

定時(shí)函數(shù)分析:
?所以上圖中與0進(jìn)行判斷,判斷當(dāng)前是否內(nèi)核態(tài),是則跳轉(zhuǎn)0x8402fdd。我們先來看看如果是內(nèi)核態(tài),是怎樣一條執(zhí)行路線,如下圖所示:
?從病毒開始聊聊那些windows下大雜燴
??????????????????圖片十三:定時(shí)器ID判定
?第二個(gè)參數(shù)必須大于等于0,否則會(huì)拋出異常,繼續(xù)看,如下圖所示:
從病毒開始聊聊那些windows下大雜燴
??????????????????圖片十四:內(nèi)核態(tài)匯編解析
?OD中我們跟中一下看是否真的追加了第五個(gè)參數(shù),如下圖所示:
從病毒開始聊聊那些windows下大雜燴
??????????????????圖片十五:NtUserSetTimer

?如果為0則跳轉(zhuǎn),跳轉(zhuǎn)位置如下圖所示:
從病毒開始聊聊那些windows下大雜燴
從病毒開始聊聊那些windows下大雜燴
從病毒開始聊聊那些windows下大雜燴
??????????????????圖片十六:ExpSetTimer
?我們會(huì)發(fā)現(xiàn),SetTimer->NtUserSetTimer->Wow64得函數(shù)(如果32位運(yùn)行在64位)-->KiFastSystemCall->ExSetTimer-->ObReferenceObjectByHandle-->..........
?所以SetTimer在內(nèi)核態(tài)得過曾還是比較復(fù)雜得,大家可以通過函數(shù)棧來觀察到底如何運(yùn)作得,這告訴我們一個(gè)道理,誰HOOK得函數(shù)越底層,誰就有可能做更多得事情。
?如果Previous Mode = UserMode呢?如何執(zhí)行?如下圖所示:
從病毒開始聊聊那些windows下大雜燴
??????????????????圖片十七:用戶態(tài)匯編分析
?在做了一些判斷賦值及參數(shù)保存操作以后,又跳回了與內(nèi)核態(tài)執(zhí)行得流程,所以說不論怎樣最終還會(huì)調(diào)用那些函數(shù)。
?關(guān)于SetTimer函數(shù)簡(jiǎn)單得分析到這里,我們下面接著看CreateTimerQueueTimer函數(shù),先來看函數(shù)原型:

BOOL WINAPI CreateTimerQueueTimer(
  _Out_    PHANDLE             phNewTimer,
  _In_opt_ HANDLE              TimerQueue,
  _In_     WAITORTIMERCALLBACK Callback,
  _In_opt_ PVOID               Parameter,
  _In_     DWORD               DueTime,
  _In_     DWORD               Period,
  _In_     ULONG               Flags
);
圖三中已經(jīng)對(duì)參數(shù)進(jìn)行了詳細(xì)得介紹,這里不再做介紹

OD中我們動(dòng)態(tài)觀察一下,如下圖所示:
從病毒開始聊聊那些windows下大雜燴
??????????????????圖片十八:CreateTimerQueueTimer
?函數(shù)內(nèi)部調(diào)用了RtlCreateTimer,我們繼續(xù)動(dòng)態(tài)跟蹤,如下所示:
從病毒開始聊聊那些windows下大雜燴
從病毒開始聊聊那些windows下大雜燴
?內(nèi)部調(diào)用了大量的函數(shù),其中包括TpSetTimer也在其中,基本確定內(nèi)部是調(diào)用TpSetTimer來實(shí)現(xiàn)該函數(shù)功能,在windbg中簡(jiǎn)答了分析一下,內(nèi)部調(diào)用了TppTimerpSet,且使用了Slim讀寫鎖機(jī)制,因?yàn)橛|碰到了盲區(qū),感覺不太準(zhǔn)確,也找不到相關(guān)的參考所以有興趣的朋友可以深入分析一下,這里就不講解了。
從病毒開始聊聊那些windows下大雜燴
??????????????????圖片十九:TppTimerpSet
?這里以上是給大家提供一些函數(shù)分析的思路罷了,有時(shí)間的話寫一篇相關(guān)的話題一起討論一下。
?
PE雜談 :
?關(guān)于PE知識(shí)雖然看起來雜亂,但還是比較有序的。PE涉獵的范圍較廣,PE文件是指一種格式,如可執(zhí)行文件、動(dòng)態(tài)鏈接庫、驅(qū)動(dòng)等等,都屬于PE格式的文件。
?想深入學(xué)習(xí)的朋友,推薦一本書籍《Windows PE權(quán)威指南》,里面內(nèi)容是win32匯編撰寫而成。
?我們這里只對(duì)用到的基本知識(shí)和導(dǎo)出表做介紹,PE結(jié)構(gòu)體大概分為幾個(gè)部分,如下圖所示:
從病毒開始聊聊那些windows下大雜燴
??????????????????圖片二十:PE大體結(jié)構(gòu)
?上面順序是一定的,PE是一個(gè)有序結(jié)構(gòu),標(biāo)準(zhǔn)的PE格式每個(gè)結(jié)構(gòu)體對(duì)應(yīng)的偏移是固定的,當(dāng)然也有很多惡意代碼會(huì)對(duì)PE結(jié)構(gòu)體進(jìn)行數(shù)據(jù)壓縮等技術(shù),達(dá)到隱匿、免殺的目的。
?我們介紹一下DOS頭的數(shù)據(jù)介紹,其實(shí)我們用VS編程的時(shí)候就可以獲取到結(jié)構(gòu)體,這里不再windbg下獲取了,如下所示:

typedef struct _IMAGE_DOS_HEADER {      // DOS .EXE header
    WORD   e_magic;                                           // Magic number
    WORD   e_cblp;                                              // Bytes on last page of file
    WORD   e_cp;                                                 // Pages in file
    WORD   e_crlc;                                               // Relocations
    WORD   e_cparhdr;                                        // Size of header in paragraphs
    WORD   e_minalloc;                                       // Minimum extra paragraphs needed
    WORD   e_maxalloc;                                      // Maximum extra paragraphs needed
    WORD   e_ss;                                                // Initial (relative) SS value
    WORD   e_sp;                                                // Initial SP value
    WORD   e_csum;                                           // Checksum
    WORD   e_ip;                                                 // Initial IP value
    WORD   e_cs;                                               // Initial (relative) CS value
    WORD   e_lfarlc;                                           // File address of relocation table
    WORD   e_ovno;                                           // Overlay number
    WORD   e_res[4];                                          // Reserved words
    WORD   e_oemid;                                         // OEM identifier (for e_oeminfo)
    WORD   e_oeminfo;                                      // OEM information; e_oemid specific
    WORD   e_res2[10];                                     // Reserved words
    LONG   e_lfanew;                                         // File address of new exe header
  } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;

?上面結(jié)構(gòu)體是DOS頭部的全部信息,其中DOS中兩個(gè)重要屬重點(diǎn)介紹一下:

e_magi
“魔術(shù)”標(biāo)志,判斷是否PE格式第一道防線,恒定值為0x4D5A(MZ)
e_lfanew
Dos頭與NT頭之間有一部分Dos Stub的數(shù)據(jù)(Dos的數(shù)據(jù))大小不確定,意味著NT頭偏移不確定,所以 e_lfanew記錄了該模塊NT的偏移

?如何找到NT頭?模塊基址 + e_lfanew = NT的位置。第二部分我們會(huì)用匯編獲取且深入學(xué)習(xí),用C/C++如何實(shí)現(xiàn)呢?如下代碼所示:


// 1.獲取PE格式文件
m_strNamePath = PathName;

// 2.打開文件
HANDLE hFile = CreateFile(PathName, GENERIC_READ | GENERIC_WRITE, FALSE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

if ((int)hFile <= 0){ AfxMessageBox(L"當(dāng)前進(jìn)程有可能被占用或者意外錯(cuò)誤"); return FALSE; }

HANDLE hFile = NULL;

// 3.獲取文件大小
DWORD dwSize = GetFileSize(hFile, NULL);

// 4.申請(qǐng)堆空間
PuPEInfo::m_pFileBase = (void *)malloc(dwSize);

memset(PuPEInfo::m_pFileBase, 0, dwSize);

DWORD dwRead = 0;

OVERLAPPED OverLapped = { 0 };

void* pFileBaseAddress = nullptr;

// 5.讀取文件到內(nèi)存
int nRetCode =  ReadFile(hFile, pFileBaseAddress, dwSize, &dwRead, &OverLapped);

// 6.轉(zhuǎn)換成DOS頭結(jié)構(gòu)體
PIMAGE_DOS_HEADER pDosHander = (PIMAGE_DOS_HEADER)pFileBaseAddress;

// 7.Dos起始地址 + e_lfanew = NT頭
PIMAGE_NT_HEADERS pHeadres = (PIMAGE_NT_HEADERS)(pDosHander->e_lfanew + (LONG)pFileBaseAddress);

?如上述代碼,獲取可執(zhí)文件路徑,創(chuàng)建(獲取文件句柄)、打開文件、讀取文件大小、申請(qǐng)堆空間、讀取文件數(shù)據(jù)到內(nèi)存(加載到了內(nèi)存)、獲取NT頭,第7步正式上述所表達(dá)的 模塊基址 + e_lfanew。
NT頭內(nèi)部是如何?如下所示:
從病毒開始聊聊那些windows下大雜燴
??????????????????圖片二十一:NT結(jié)構(gòu)
如上所示,NT分為三部分,介紹如下:

Signature FileHeader OptionalHeader
標(biāo)記,判斷是否PE格式第二道防線,恒定值為0x4550(PE) 文件頭,存儲(chǔ)這PE文件的基本信息 存儲(chǔ)著關(guān)于PE文件的附加信息

既然已經(jīng)介紹了PE格式兩條應(yīng)規(guī)定,兩道標(biāo)桿,如果判斷是否是一個(gè)PE格式的文件呢?如下代碼所示:

//判定是否是PE文件
BOOL IsPE(char* lpBase)
{
    PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)lpBase;
    if (pDos->e_magic != IMAGE_DOS_SIGNATURE/*0x4D5A*/)
    {
        return FALSE;
    }
    PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + lpBase);
    if (pNt->Signature != IMAGE_NT_SIGNATURE/*0x4550*/)
    {
        return FALSE;
    }
    return TRUE;
}

FileHeader結(jié)構(gòu)體如下:

// File header format.
typedef struct _IMAGE_FILE_HEADER {
    WORD    Machine;
    WORD    NumberOfSections;
    DWORD   TimeDateStamp;
    DWORD   PointerToSymbolTable;
    DWORD   NumberOfSymbols;
    WORD    SizeOfOptionalHeader;
    WORD    Characteristics;
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
Machine NumberOfSections TimeDateStamp NumberOfSymbols
文件運(yùn)行平臺(tái) 區(qū)段的數(shù)量 文件創(chuàng)建時(shí)間 符號(hào)個(gè)數(shù)
SizeOfOptionalHeader PointerToSymbolTable Characteristics
擴(kuò)展頭大小 符號(hào)表偏移 PE文件屬性

補(bǔ)充:
?1、Machine:0x014c代表i386,平時(shí)intel32為平臺(tái),0x0200表示Intel 64為平臺(tái)。
?2、NumberOfSymbols:這個(gè)很重要了,你遍歷節(jié)表先要獲取數(shù)量,這個(gè)就是。
?3、Characteristics:PE的文件屬性值,如下所示:

數(shù)值 介紹 宏定義
0x0001 從文件中刪除重定位信息 IMAGE_FILE_RELOCS_STRIPPED
0x0002 可執(zhí)行文件 IMAGE_FILE_EXECUTABLE_IMAGE
0x0004 行號(hào)信息無 IMAGE_FILE_LINE_NUMS_STRIPPED
0x0008 符號(hào)信息無 IMAGE_FILE_LOCAL_SYMS_STRIPPED
0x0010 強(qiáng)制性縮減工作 IMAGE_FILE_AGGRESIVE_WS_TRIM
0x0020 應(yīng)用程序可以處理> 2GB的地址 IMAGE_FILE_LARGE_ADDRESS_AWARE
0x0080 機(jī)器字的字節(jié)相反的 IMAGE_FILE_BYTES_REVERSED_LO
0x0100 運(yùn)行在32位平臺(tái) IMAGE_FILE_32BIT_MACHINE
0x0200 調(diào)試信息從.DBG文件中的文件中刪除 IMAGE_FILE_DEBUG_STRIPPED
0x0400 如果文件在可移動(dòng)媒體上,則從交換文件復(fù)制并運(yùn)行。 IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP
0x0800 如果在網(wǎng)絡(luò)存儲(chǔ)介質(zhì)中,則從交換文件中復(fù)制并運(yùn)行。 IMAGE_FILE_NET_RUN_FROM_SWAP
0x1000 系統(tǒng)文件 IMAGE_FILE_SYSTEM
0x2000 DLL文件 IMAGE_FILE_DLL
0x4000 單核CPU運(yùn)行 IMAGE_FILE_UP_SYSTEM_ONLY
0x8000 機(jī)器字的字節(jié)相反的 IMAGE_FILE_BYTES_REVERSED_HI

?
OptionalHeader結(jié)構(gòu)體介紹:


typedef struct _IMAGE_OPTIONAL_HEADER {
    //
    // Standard fields.
    //

    WORD    Magic;
    BYTE    MajorLinkerVersion;
    BYTE    MinorLinkerVersion;
    DWORD   SizeOfCode;
    DWORD   SizeOfInitializedData;
    DWORD   SizeOfUninitializedData;
    DWORD   AddressOfEntryPoint;
    DWORD   BaseOfCode;
    DWORD   BaseOfData;

    //
    // NT additional fields.
    //

    DWORD   ImageBase;
    DWORD   SectionAlignment;
    DWORD   FileAlignment;
    WORD    MajorOperatingSystemVersion;
    WORD    MinorOperatingSystemVersion;
    WORD    MajorImageVersion;
    WORD    MinorImageVersion;
    WORD    MajorSubsystemVersion;
    WORD    MinorSubsystemVersion;
    DWORD   Win32VersionValue;
    DWORD   SizeOfImage;
    DWORD   SizeOfHeaders;
    DWORD   CheckSum;
    WORD    Subsystem;
    WORD    DllCharacteristics;
    DWORD   SizeOfStackReserve;
    DWORD   SizeOfStackCommit;
    DWORD   SizeOfHeapReserve;
    DWORD   SizeOfHeapCommit;
    DWORD   LoaderFlags;
    DWORD   NumberOfRvaAndSizes;
    IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

挑重點(diǎn)介紹一下:

Magic AddressOfEntryPoint BaseOfData
標(biāo)志一個(gè)文件什么類型 程序入口點(diǎn)RVA 起始數(shù)據(jù)的相對(duì)虛擬地址(RVA)
ImageBase SizeOfImage SizeOfHeaders
默認(rèn)加載基址0x400000 文件加載到內(nèi)存后大小(對(duì)齊后) 所有頭部大小
NumberOfRvaAndSizes DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES] SizeofStackReserve
數(shù)據(jù)目錄個(gè)數(shù)(一般是0x10) 數(shù)據(jù)目錄表 ??稍鲩L(zhǎng)大小

補(bǔ)充:
?1、文件中的數(shù)據(jù)是0x200對(duì)齊的(FileAlinment),內(nèi)存中是以0x1000對(duì)齊的(SectionAlignment),對(duì)齊什么意思?打個(gè)比方,假如從0開始,數(shù)據(jù)只占用了0x88字節(jié),那么下一段數(shù)據(jù)會(huì)在0x200開始,中間填充0。
?2、DataDirectory這是一個(gè)數(shù)組,IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16。所以共有16項(xiàng),每一項(xiàng)對(duì)于整個(gè)執(zhí)行程序來說都有特殊的意義,當(dāng)然不是每個(gè)程序每一項(xiàng)數(shù)據(jù)表都有內(nèi)容。下面我們介紹的導(dǎo)出表,便是這16項(xiàng)中的第1項(xiàng),下標(biāo)為0。
?那么DataDirectory是什么樣結(jié)構(gòu)呢?如下所示:

typedef struct _IMAGE_DATA_DIRECTORY {
    DWORD   VirtualAddress;
    DWORD   Size;
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;

?每一個(gè)數(shù)組都保存了這樣的一個(gè)結(jié)構(gòu)體指針,VirtualAddress是什么?就是相對(duì)虛擬地址RVA,而Size意味著數(shù)據(jù)的大小。
?
術(shù)語介紹:

**虛擬地址**: 在一個(gè)程序運(yùn)行起來的時(shí)候,會(huì)被加載到內(nèi)存中,并且每個(gè)進(jìn)程都有自己的4GB,這個(gè)4GB叫做**虛擬地址**,由物理地址映射過來的,4GB的空間,并沒有全部被用到。

**物理地址**:在物理內(nèi)存中存在的地址。在windows中是沒有表現(xiàn)出來的,因?yàn)閣indows使用了保護(hù)模式。

**所有的數(shù)據(jù)都存儲(chǔ)在了相應(yīng)的區(qū)段(節(jié))**,rdata存儲(chǔ)只讀數(shù)據(jù),data存儲(chǔ)的全局?jǐn)?shù)據(jù),text存儲(chǔ)的代碼,rsrc存儲(chǔ)的是資源。

**入口點(diǎn)(OEP)**:他保存的是一個(gè) **RVA** ,然后使用 OEP + Imagebase == 入口點(diǎn)的VA,通常情況下,OEP指向的不是main函數(shù),是一個(gè)用于初始化(實(shí)際加載地址)

**加載基址**:默認(rèn)由PE文件指定,但是通常開啟隨機(jī)基址后,它的位置是由系統(tǒng)指定的

**鏡像大小**: 就是exe在文件中展開之后的大小, = 最后一個(gè)區(qū)段的RVA + 最后一個(gè)區(qū)段的size 再按照0x1000對(duì)齊。

**代碼/數(shù)據(jù)基址**:第一個(gè)代碼區(qū)段和第一個(gè)數(shù)據(jù)區(qū)段的RVA

**虛擬地址(VA)**:在進(jìn)程4GB中所處的位置。

**相對(duì)虛擬地址(RVA)**:相對(duì)于內(nèi)存(映像)中<u>加載基址</u>的一個(gè)偏移,

**文件偏移(FOA)**:相對(duì)于文件(鏡像)起始位置的偏移。

**文件塊對(duì)齊:** 0x200(512),一個(gè)區(qū)段在文件的大小必須是0x200的倍數(shù)

**內(nèi)存塊對(duì)齊:**0x1000(4kb),一個(gè)區(qū)段在內(nèi)存中的大小必須是0x1000的倍數(shù)

**關(guān)系:** 數(shù)據(jù)段(有效數(shù)據(jù)長(zhǎng)度是0x100) => 文件對(duì)齊 => (0x200) => 映射到內(nèi)存 => 0x1000

文件對(duì)齊力度和內(nèi)存對(duì)齊力度可以自己改變,但是文件對(duì)齊力度必須不大于內(nèi)存對(duì)齊力度

**標(biāo)志字:**標(biāo)識(shí)可運(yùn)行的平臺(tái),x86,x64

**子系統(tǒng)**:窗口WinMain,控制臺(tái)main

**特征值**: 對(duì)應(yīng)的是文件頭中的Characteristics,標(biāo)識(shí)當(dāng)前模塊有哪些屬性(重定位已分離=>動(dòng)態(tài)基址)

**可選頭的大小**:可選頭有多少個(gè)字節(jié),和操作系統(tǒng)的位數(shù)有關(guān),x86/x64

?節(jié)表就不再這里過多的介紹,說說導(dǎo)出表,也就是數(shù)據(jù)目錄表的第1項(xiàng),下標(biāo)為0。
?導(dǎo)出表是干什么的?PE文件導(dǎo)出的供其他使用的函數(shù)、變量等行為。當(dāng)查找導(dǎo)出函的時(shí)候,能夠方便快捷找到函數(shù)的位置。
?
看一看導(dǎo)出表的結(jié)構(gòu)體,如下所示:

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;

圖片二十一:Export Format

Characteristics TimeDateStamp MajorVersion NumberOfFunctions
保留值, 為0 時(shí)間 主版本號(hào) 函數(shù)數(shù)量
MinorVersion Name Base NumberOfNames
次版本號(hào) PE名稱 序號(hào)基數(shù) 函數(shù)名稱數(shù)量
AddressOfFunctions AddressOfNames AddressOfNameOrdinals
函數(shù)地址表RVA 函數(shù)名稱表RVA 函數(shù)序號(hào)表RVA

補(bǔ)充:
?導(dǎo)出表一般會(huì)被安排到.edata中,一般也都合并到.rdata中。上述中有三個(gè)字段分別是AddressOfFunctions,AddressOfNames和AddressOfNameOrdinals,對(duì)應(yīng)著三張表,上面三個(gè)字段保存了相對(duì)虛擬地址,且有關(guān)聯(lián)性,下面來看一下三個(gè)表的關(guān)聯(lián)性,如下所示:
從病毒開始聊聊那些windows下大雜燴
??????????????????圖片二十二:Table關(guān)聯(lián)
?如上圖所示,序號(hào)表與名稱表一一對(duì)應(yīng),下標(biāo)與下標(biāo)中存儲(chǔ)的值是相關(guān)聯(lián)的,這三張表設(shè)計(jì)巧妙,利用了關(guān)系型數(shù)據(jù)庫的概念。
?需要注意的是,序號(hào)不是有序的,而且會(huì)有空白。地址表中有些沒有函數(shù)名,也就是地址表有地址卻無法關(guān)聯(lián)到名稱表中,這時(shí)候用序號(hào)調(diào)用,序號(hào)內(nèi)容加上Base序號(hào)基址才是真正的調(diào)用號(hào),且注意序號(hào)表是兩個(gè)字節(jié)WORD類型。
?了解這三張表之后,C/C++代碼實(shí)際應(yīng)用獲取一下,代碼如下:

// lpBase就是讀取文件申請(qǐng)的緩沖區(qū)(把文件讀到內(nèi)存后的首地址)
    // 1. 找到導(dǎo)出表
    PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)lpBase;
    PIMAGE_NT_HEADERS pNt =
        (PIMAGE_NT_HEADERS)(pDos->e_lfanew + lpBase);

    PIMAGE_DATA_DIRECTORY pDir = 
        &pNt->OptionalHeader.DataDirectory[0];

    DWORD dwExportFOA = RVAtoFOA(pDir->VirtualAddress);
    // 2. 導(dǎo)出表在文件中的位置
    PIMAGE_EXPORT_DIRECTORY pExportTable = 
                                (PIMAGE_EXPORT_DIRECTORY)
                                (dwExportFOA + lpBase);

    printf("模塊名稱%s\n", (RVAtoFOA(pExportTable->Name) + lpBase));
    // 3. 獲取函數(shù)數(shù)量
    DWORD dwFunCount = pExportTable->NumberOfFunctions;
    // 3.1 獲取函數(shù)名稱數(shù)量
    DWORD dwOrdinalCount = pExportTable->NumberOfNames;
    // 4. 獲取地址表
    DWORD* pFunAddr = 
        (DWORD*)(RVAtoFOA(pExportTable->AddressOfFunctions) + lpBase);
    // 5. 獲取名稱表
    DWORD* pNameAddr =
        (DWORD*)(RVAtoFOA(pExportTable->AddressOfNames) + lpBase);
    // 6. 獲取序號(hào)表
    WORD* pOrdinalAddr =
        (WORD*)(RVAtoFOA(pExportTable->AddressOfNameOrdinals) + lpBase);
    // 7. 循環(huán)遍歷
    for (DWORD i = 0; i < dwFunCount; i++)
    {
        // 7.1 如果為0說明是無效地址,直接跳過
        if (pFunAddr[i] == 0)
        {
            continue;
        }
        // 7.2 遍歷序號(hào)表中是否有此序號(hào),如果有說明此函數(shù)有名字
        BOOL bFlag = FALSE;
        for (DWORD j = 0; j < dwOrdinalCount; j++)
        {
            if (i == pOrdinalAddr[j])
            {
                bFlag = TRUE;
                DWORD dwNameRVA = pNameAddr[j];
                printf("函數(shù)名:%s,函數(shù)序號(hào):%04X,函數(shù)序號(hào):%04X\n",
                    RVAtoFOA(dwNameRVA) + lpBase,
                    i + pExportTable->Base);
            }
        }
        // 7.3 如果序號(hào)表中沒有,說明此函數(shù)只有序號(hào)沒有名字
        if (!bFlag)
        {
            printf("函數(shù)名【NULL】,函數(shù)序號(hào):%04X\n", i + pExportTable->Base);
        }
    }

?上述代碼是對(duì)導(dǎo)出表進(jìn)行的遍歷,上述中也許有一些細(xì)節(jié)性的知識(shí)表達(dá)的不夠到位,如果你能對(duì)以上的知識(shí)都很熟悉且匯編還不錯(cuò),那么用匯編獲取函數(shù)導(dǎo)出表也許對(duì)你來說是一件比較輕松的事情。
?第二部分我們一起學(xué)習(xí)一下如何用匯編手動(dòng)獲取函數(shù)名稱表及對(duì)應(yīng)的函數(shù)地址(上面三張表關(guān)系一定搞清楚),用匯編實(shí)現(xiàn)自己的GetProcAddress,且Hash加密字符串進(jìn)行與名稱表進(jìn)行對(duì)比,理論知識(shí)先告一段落。
?
?
二、匯編篇:
?通過理論篇的閱讀,熟悉了如何使用C/C++(其他語言思路不變)來獲取且遍歷導(dǎo)出表,那么如圖二,當(dāng)分析一段惡意代碼或者正向代碼,我們發(fā)現(xiàn)這些匯編指令如何去做?IDA中轉(zhuǎn)換成C語言?其實(shí)我很少使用IDA中的轉(zhuǎn)換,應(yīng)為看匯編與看c差距并不是特別大,特別對(duì)于算法,想要還原規(guī)則及代碼,匯編最為真實(shí)可靠。當(dāng)然如果說有大量工作需求,沒有太多時(shí)間去研究,只是對(duì)部分規(guī)則,邏輯進(jìn)行分析形成報(bào)告,那么就另說了......
?上面介紹了保護(hù)模式相關(guān)內(nèi)容及fs寄存器,分析了內(nèi)核態(tài)的fs:[0x124],那么用戶態(tài)fs:[0x30]呢?,如下圖所示:
從病毒開始聊聊那些windows下大雜燴
??????????????????圖片二十三:TEB

從病毒開始聊聊那些windows下大雜燴
??????????????????圖片二十四:PEB
?什么是TEB什么是PEB呢?在以前的博客中介紹過一些相關(guān)的內(nèi)容,這里在簡(jiǎn)單的說一說。
?TEB(Thread Environment Block),線程環(huán)境塊,也就是說每一個(gè)線程都會(huì)有TEB,用于保存系統(tǒng)與線程之間的數(shù)據(jù),便于操作控制,通過理論篇述保護(hù)模式知識(shí)可以自己分析一下,用戶態(tài)取fs寄存器的段描述符的BaseAddress拼接后地址為TEB地址,以前的NT類系統(tǒng)上地址是固定的,每4KB是一個(gè)TEB,通過分解的段描述符,內(nèi)存中是向下擴(kuò)展。
?PEB(Process Environment Block),進(jìn)程環(huán)境塊,保存進(jìn)程相關(guān)的信息,同樣每個(gè)進(jìn)程都是由自己的進(jìn)程信息的。
獲取PEB有那些途徑?
1、fs寄存器偏移+0x30 PEB的地址
2、EPROCESS+0x1a8 PEB地址

?以上偏移不是一定的根據(jù)環(huán)境而定,通過以上我們兩種方式我們?cè)诰幊讨芯涂梢暂p易的找到PEB了。
?圖片二十四中,PEB結(jié)構(gòu)體中標(biāo)紅了+0x00c偏移處,指向的是一個(gè)_PEB_LDR_DATA的結(jié)構(gòu)體,如下所示:

kd> dt _PEB_LDR_DATA
nt!_PEB_LDR_DATA
   +0x000 Length           : Uint4B
   +0x004 Initialized      : UChar
   +0x008 SsHandle         : Ptr32 Void
   +0x00c InLoadOrderModuleList : _LIST_ENTRY
   +0x014 InMemoryOrderModuleList : _LIST_ENTRY
   +0x01c InInitializationOrderModuleList : _LIST_ENTRY
   +0x024 EntryInProgress  : Ptr32 Void
   +0x028 ShutdownInProgress : UChar
   +0x02c ShutdownThreadId : Ptr32 Void

?這個(gè)結(jié)構(gòu)意味著什么?其實(shí)就是包含有關(guān)進(jìn)程的已加載模塊的信息。而且微軟給他標(biāo)記了This structure may be altered in future versions of Windows,此結(jié)構(gòu)可能會(huì)在Windows的未來版本中更改。我們?cè)趙indbg下(windwos7 32bit)與官網(wǎng)查詢到的結(jié)構(gòu)體成員數(shù)量不一樣,如下所示:

typedef struct _PEB_LDR_DATA {
  BYTE       Reserved1[8];
  PVOID      Reserved2[3];
  LIST_ENTRY InMemoryOrderModuleList;
} PEB_LDR_DATA, *PPEB_LDR_DATA;

?前兩個(gè)參數(shù)只給了同樣的介紹,Reserved for internal use by the operating system,供系統(tǒng)內(nèi)部使用,而第三個(gè)參數(shù)則是一個(gè)雙向鏈表頭部,包含進(jìn)程的已加載模塊。 列表中的每個(gè)項(xiàng)目都是指向LDR_DATA_TABLE_ENTRY結(jié)構(gòu)的指針。
?在windbg下+0x00c,+0x014,+0x01c三個(gè)都是雙線鏈表有什么不同呢?

InLoadOrderModuleList InMemoryOrderModuleList InInitializationOrderModuleList
模塊加載順序 模塊在內(nèi)存中的順序 模塊初始化裝載順序

LDR_DATA_TABLE_ENTRY是怎樣一個(gè)雙向鏈表呢?如下所示:

從病毒開始聊聊那些windows下大雜燴
??????????????????圖片二十五:關(guān)聯(lián)

LDR_DATA_TABLE_ENTRY結(jié)構(gòu)體,如下所示:

從病毒開始聊聊那些windows下大雜燴

??????????????????圖片二十六:LDR_DATA_TABLE_ENTRY

代碼中會(huì)用到以下屬性,簡(jiǎn)單理解如下,其實(shí)一個(gè)驅(qū)動(dòng)的加載過程這個(gè)結(jié)構(gòu)體很重要:

DLLBase FullDllName BaseDllName
模塊基址 文件路徑 模塊名稱

匯編如何獲取呢?如下圖所示:
從病毒開始聊聊那些windows下大雜燴
??????????????????圖片二十七:獲取DLLBase
?補(bǔ)充:上面一段匯編代碼,我們通過fs獲取了PEB,通過PEB偏移+0x0C獲取_PEB_LDR_DATA,加上偏移+0x1c是InInitializationOrderModuleList為雙向鏈表進(jìn)行的遍歷。
?接著獲取了字符串,然后通過Hash比對(duì),注意模塊名稱存儲(chǔ)是寬字符,比對(duì)成功獲取DLLBase基地址,我們可以遍歷獲取想要的模塊基址如krnel32.dll等。
PE獲?。?/strong>
?PE如何用c++獲取導(dǎo)出表且遍歷,理論篇已給出完整代碼。匯編如何實(shí)現(xiàn)呢?對(duì)于標(biāo)準(zhǔn)的PE來
說,相對(duì)于基址偏移是一定的如下:

0x3c 0x78
PE標(biāo)頭 導(dǎo)出目錄表的相對(duì)虛擬地址(RVA)

如下圖所示:
從病毒開始聊聊那些windows下大雜燴
??????????????????圖片二十八:獲取Export Table

?因?yàn)槭菂R編來實(shí)現(xiàn)操作,關(guān)鍵的步驟都寫到了注釋當(dāng)中,下面貼上完整的匯編代碼,實(shí)現(xiàn)函數(shù)如下:

puGetModule puGetProcAddress
獲取模塊基址,參數(shù)1:Hash值 獲取函數(shù)地址 參數(shù)1:模塊基址,參數(shù)2:Hash值

?關(guān)于Hash值的算法,大家可以逆向一下下面代碼中的匯編代碼,用c語言實(shí)現(xiàn)一下,貼出本代碼中測(cè)試使用的Hash值,如下:

           0xec1c6278;          kernel32.dll
            0xc0d832c7;        LoadlibraryExa
            0x4FD18963;         ExitPorcess
            0x5644673D          User32.dll      
            0x1E380A6A          MessageBoxA     
            0x9EBC86B           RtlExitUserProcess  
            0xF4E2F2C8          GetModuleHandleW
            0xBB7420F9          CreateSolidBrush
            0xBC05E48           RegisterClassW

puGetModule匯編代碼如下:

DWORD puGetModule(const DWORD Hash)
{
    DWORD   nDllBase = 0;
    __asm{
        jmp         start
    /*函數(shù)1:遍歷PEB_LDR_DATA鏈表HASH加密*/
    GetModulVA:
        push        ebp;
        mov         ebp, esp;
        sub         esp, 0x20;
        push        edx;
        push        ebx;
        push        edi;
        push        esi;
        mov         ecx, 8;
        mov         eax, 0CCCCCCCCh;
        lea         edi, dword ptr[ebp - 0x20];
        rep stos    dword ptr es : [edi];
        mov         esi, dword ptr fs : [0x30];
        mov         esi, dword ptr[esi + 0x0C];
        mov         esi, dword ptr[esi + 0x1C];
    tag_Modul:
        mov         dword ptr[ebp - 0x8], esi;  // 保存LDR_DATA_LIST_ENTRY
        mov         ebx, dword ptr[esi + 0x20]; // DLL的名稱指針(應(yīng)該指向一個(gè)字符串)
        mov         eax, dword ptr[ebp + 0x8];
        push        eax;
        push        ebx;                        // +0xC
        call        HashModulVA;
        test        eax, eax;
        jnz         _ModulSucess;
        mov         esi, dword ptr[ebp - 0x8];
        mov         esi, [esi];                 // 遍歷下一個(gè)
        LOOP        tag_Modul
        _ModulSucess :
        mov         esi, dword ptr[ebp - 0x8];
        mov         eax, dword ptr[esi + 0x8];
        pop         esi;
        pop         edi;
        pop         ebx;
        pop         edx;
        mov         esp, ebp;
        pop         ebp;
        ret

        /*函數(shù)2:HASH解密算法(寬字符解密)*/
    HashModulVA :
        push        ebp;
        mov         ebp, esp;
        sub         esp, 0x04;
        mov         dword ptr[ebp - 0x04], 0x00
        push        ebx;
        push        ecx;
        push        edx;
        push        esi;
        // 獲取字符串開始計(jì)算
        mov         esi, [ebp + 0x8];
        test        esi, esi;
        jz          tag_failuers;
        xor         ecx, ecx;
        xor         eax, eax;
    tag_loops:
        mov         al, [esi + ecx];        // 獲取字節(jié)加密
        test        al, al;                 // 0則退出
        jz          tag_ends;
        mov         ebx, [ebp - 0x04];
        shl         ebx, 0x19;
        mov         edx, [ebp - 0x04];
        shr         edx, 0x07;
        or          ebx, edx;
        add         ebx, eax;
        mov[ebp - 0x4], ebx;
        inc         ecx;
        inc         ecx;
        jmp         tag_loops;
    tag_ends:
        mov         ebx, [ebp + 0x0C];      // 獲取HASH
        mov         edx, [ebp - 0x04];
        xor         eax, eax;
        cmp         ebx, edx;
        jne         tag_failuers;
        mov         eax, 1;
        jmp         tag_funends;
    tag_failuers:
        mov         eax, 0;
    tag_funends:
        pop         esi;
        pop         edx;
        pop         ecx;
        pop         ebx;
        mov         esp, ebp;
        pop         ebp;
        ret         0x08

    start:
    /*主模塊*/
        pushad;
        push        Hash;
        call        GetModulVA;
        add         esp, 0x4
        mov         nDllBase, eax;
        popad;
    }
    return nDllBase;
}

puGetProcAddress函數(shù)如下:

DWORD puGetProcAddress(const DWORD dllvalues, const DWORD Hash)
{
    DWORD FunctionAddress = 0;
    __asm{
        jmp         start
        // 自定義函數(shù)計(jì)算Hash且對(duì)比返回正確的函數(shù)
    GetHashFunVA:
        push        ebp;
        mov         ebp, esp;
        sub         esp, 0x30;
        push        edx;
        push        ebx;
        push        esi;
        push        edi;
        lea         edi, dword ptr[ebp - 0x30];
        mov         ecx, 12;
        mov         eax, 0CCCCCCCCh;
        rep stos    dword ptr es : [edi];
        // 以上開辟棧幀操作(Debug版本模式)
        mov         eax, [ebp + 0x8];               // ☆ kernel32.dll(MZ)
        mov         dword ptr[ebp - 0x8], eax;
        mov         ebx, [ebp + 0x0c];              // ☆ GetProcAddress Hash值
        mov         dword ptr[ebp - 0x0c], ebx;
        // 獲取PE頭與RVA及ENT
        mov         edi, [eax + 0x3C];              // e_lfanew
        lea         edi, [edi + eax];               // e_lfanew + MZ = PE
        mov         dword ptr[ebp - 0x10], edi;     // ☆ 保存PE(VA)
        // 獲取ENT
        mov         edi, dword ptr[edi + 0x78];     // 獲取導(dǎo)出表RVA
        lea         edi, dword ptr[edi + eax];      // 導(dǎo)出表VA
        mov[ebp - 0x14], edi;                       // ☆ 保存導(dǎo)出表VA
        // 獲取函數(shù)名稱數(shù)量
        mov         ebx, [edi + 0x18];
        mov         dword ptr[ebp - 0x18], ebx;     // ☆ 保存函數(shù)名稱數(shù)量
        // 獲取ENT
        mov         ebx, [edi + 0x20];              // 獲取ENT(RVA)
        lea         ebx, [eax + ebx];               // 獲取ENT(VA)
        mov         dword ptr[ebp - 0x20], ebx;     // ☆ 保存ENT(VA)
        // 遍歷ENT 解密哈希值對(duì)比字符串
        mov         edi, dword ptr[ebp - 0x18];
        mov         ecx, edi;
        xor         esi, esi;
        mov         edi, dword ptr[ebp - 0x8];
        jmp         _WHILE
        // 外層大循環(huán)
    _WHILE :
        mov         edx, dword ptr[ebp + 0x0c];     // HASH
        push        edx;
        mov         edx, dword ptr[ebx + esi * 4];  // 獲取第一個(gè)函數(shù)名稱的RVA
        lea         edx, [edi + edx];               // 獲取一個(gè)函數(shù)名稱的VA地址
        push        edx;                            // ENT表中第一個(gè)字符串地址
        call        _STRCMP;
        cmp         eax, 0;
        jnz         _SUCESS;
        inc         esi;
        LOOP        _WHILE;
        jmp         _ProgramEnd
        // 對(duì)比成功之后獲取循環(huán)次數(shù)(下標(biāo))cx保存下標(biāo)數(shù)
    _SUCESS :
        // 獲取EOT導(dǎo)出序號(hào)表內(nèi)容
        mov         ecx, esi;
        mov         ebx, dword ptr[ebp - 0x14];
        mov         esi, dword ptr[ebx + 0x24];
        mov         ebx, dword ptr[ebp - 0x8];
        lea         esi, [esi + ebx];               // 獲取EOT的VA
        xor         edx, edx;
        mov         dx, [esi + ecx * 2];            // 注意雙字 獲取序號(hào)
        // 獲取EAT地址表RVA
        mov         esi, dword ptr[ebp - 0x14];     // Export VA
        mov         esi, [esi + 0x1C];
        mov         ebx, dword ptr[ebp - 0x8];
        lea         esi, [esi + ebx];               // 獲取EAT的VA         
        mov         eax, [esi + edx * 4];           // 返回值eax(GetProcess地址)
        lea         eax, [eax + ebx];
        jmp         _ProgramEnd;

    _ProgramEnd:
        pop         edi;
        pop         esi;
        pop         ebx;
        pop         edx;
        mov         esp, ebp;
        pop         ebp;
        ret         0x8;

        // 循環(huán)對(duì)比HASH值
    _STRCMP:
        push        ebp;
        mov         ebp, esp;
        sub         esp, 0x04;
        mov         dword ptr[ebp - 0x04], 0x00;
        push        ebx;
        push        ecx;
        push        edx;
        push        esi;
        // 獲取字符串開始計(jì)算
        mov         esi, [ebp + 0x8];
        xor         ecx, ecx;
        xor         eax, eax;

    tag_loop:
        mov         al, [esi + ecx];        // 獲取字節(jié)加密
        test        al, al;                 // 0則退出
        jz          tag_end;
        mov         ebx, [ebp - 0x04];
        shl         ebx, 0x19;
        mov         edx, [ebp - 0x04];
        shr         edx, 0x07;
        or          ebx, edx;
        add         ebx, eax;
        mov[ebp - 0x4], ebx;
        inc         ecx;
        jmp         tag_loop

        tag_end :
        mov         ebx, [ebp + 0x0C];      // 獲取HASH
        mov         edx, [ebp - 0x04];
        xor         eax, eax;
        cmp         ebx, edx;
        jne         tag_failuer;
        mov         eax, 1;
        jmp         tag_funend;

    tag_failuer:
        mov         eax, 0;

    tag_funend:
        pop         esi;
        pop         edx;
        pop         ecx;
        pop         ebx;
        mov         esp, ebp;
        pop         ebp;
        ret         0x08

    start:
        pushad;
        push        Hash;                       // Hash加密的函數(shù)名稱
        push        dllvalues;                  // 模塊基址.dll
        call        GetHashFunVA;               // GetProcess
        mov         FunctionAddress, eax;       // ☆ 保存地址
        popad;
    }
    return FunctionAddress;
}
向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI