2013-07-27 66 views
2

如何查找特定指令的內存地址(用於利用寫入)?如何查找DLL中特定指令的內存地址

具體而言,我正在尋找在Windows XP上user32.dllcall ebp指令,沒有Service Pack,其地址我可以指向EIP。我在目標上安裝了Immunity DebuggerOllyDBG

+0

好,DLL是重定位(和_are being_搬遷),這樣你就永遠只能找到一個地址相對於模塊的基本地址在這樣的可靠的方式。 – Damon

+0

@Damon對於所有現代操作系統都是如此,但對於Windows XP,沒有ASLR,因此DLL的內存中位置在操作系統版本和SP版本之間非常一致。 –

+0

我甚至沒有想到ASLR,但這當然是另一個問題。我正在考慮常規發生的正常DLL重新綁定。所有的DLL通常都有相同的基地址(雖然可以告訴鏈接器選擇一個隨機的地址,但是這會使地址空間更加分裂),並且在加載時重新分頁。第一個DLL通常是kernel32,加載到它的原始地址。然後它通常是NLS,然後是所有其他DLL(包括user32),所有這些都被重新定位。他們可能每次都會以相同的地址結束,但誰知道。 – Damon

回答

6

要找到一條指令,您需要找出代碼,.text,節開始和結束的位置,然後加載DLL並執行班輪搜索,直到找到指令。

這裏我們有兩個call ebp指令的測試DLL:

// test.c 
// gcc -Wall -shared test.c -o test.dll 
#include <stdio.h> 

__declspec(dllexport) void test(void) { 
    asm("call *%ebp"); 
    puts("test"); 
    asm("call *%ebp"); 
} 

編譯並在OllyDbg中加載DLL,然後單擊CTRL + ˚F和搜索CALL EBP

6BEC125A |. FFD5   CALL EBP 
6BEC125C |. C70424 6430EC6> MOV DWORD PTR SS:[ESP],test.6BEC3064 ; |ASCII "test" 
6BEC1263 |. E8 74060000  CALL <JMP.&msvcrt.puts>    ; \puts 
6BEC1268 |. FFD5   CALL EBP 

你看到第一條指令的地址是0x6bec125a第二條在0x6bec1268。請記住這個,call ebp的操作碼是0xff 0xd5

現在我們需要找到的代碼的邊界,你可以使用objdump的使用-h

> objdump --headers test.dll 

test.dll:  file format pei-i386 

Sections: 
Idx Name   Size  VMA  LMA  File off Algn 
    0 .text   00000984 6bec1000 6bec1000 00000600 2**2 
        CONTENTS, ALLOC, LOAD, READONLY, CODE, DATA 

    1 .data   00000008 6bec2000 6bec2000 00001000 2**2 
        CONTENTS, ALLOC, LOAD, DATA 

    2 .rdata  0000011c 6bec3000 6bec3000 00001200 2**2 
        CONTENTS, ALLOC, LOAD, READONLY, DATA 
    .... 

> 

的代碼開始在VMA,虛擬內存地址,0x6bec1000其尺寸爲0x984,所以它在結束0x6bec1000 + 0x984 = 0x6bec1984爲:

0x6bec1000 
.... 
what is between are the DLL instructions 
.... 
0x6bec1984 

我希望是清楚爲止。

如果我們要編寫我們call ebp掃描儀,我們需要做的流動:

  1. 閱讀PE的信息,並得到執行部分信息,通常.text,找到它的相對地址和它的虛擬大小。
  2. 使用LoadLibrary加載DLL,它將返回DLL的基地址。
  3. 代碼段開頭的虛擬地址是:DLL基地址+代碼段virtualAddress,它在DLL基地址+代碼段virtualAddress + VirtualSize結束。
  4. 現在我們準備遍歷代碼並尋找0xff 0xd5call ebp的操作碼,簡單的班輪搜索。

下面是一個簡單的實現:

// findopcode.c 
// gcc -Wall findopcode.c -o findopcode 

#include <windows.h> 
#include <stdio.h> 
#include <string.h> 

int main(int argc, char **argv) { 
    const char opcode[] = {0xff, 0xd5}; // The opcode of `call ebp' 
    FILE *dllFile; 
    HMODULE dllHandle; 

    IMAGE_DOS_HEADER dosHeader; 
    IMAGE_NT_HEADERS NtHeaders; 
    IMAGE_SECTION_HEADER sectionHeader; 

    unsigned int i; 
    unsigned char *starAddr; 
    unsigned char *endAddr; 

    if(argc < 2) { 
     printf("usage: %s [DLL]\n", argv[0]); 
     return -1; 
    } 

    if((dllFile = fopen(argv[1], "rb")) == NULL) { 
     perror("[!] Error"); 
     return -1; 
    } 

    // Read the basic PE headers 
    fread(&dosHeader, sizeof(dosHeader), 1, dllFile); 
    fseek(dllFile, dosHeader.e_lfanew, SEEK_SET); 
    fread(&NtHeaders, sizeof(NtHeaders), 1, dllFile); 

    // Search for the executable section, .text section. 
    for(i = 0 ; i < NtHeaders.FileHeader.NumberOfSections ; i++) { 
     fread(&sectionHeader, sizeof(sectionHeader), 1, dllFile); 
     // If we found a section that contains executable code, 
     // we found our code setion. 
     if((sectionHeader.Characteristics & IMAGE_SCN_CNT_CODE) != 0) { 
      printf("[*] Code section: `%s'\n", sectionHeader.Name); 
      break; 
     } 
    } 

    fclose(dllFile); 

    // Load the DLL to get it's base address 
    if((dllHandle = LoadLibraryA(argv[1])) == NULL) { 
     printf("[!] Error: loading the DLL, 0x%.8x\n", (unsigned int) GetLastError()); 
     return -1; 
    } 

    // The code start at : base address + code virtual address 
    starAddr = (unsigned char *) dllHandle + sectionHeader.VirtualAddress; 
    // It ends at : base address + code virtual address + virtual size 
    endAddr = (unsigned char *) starAddr + sectionHeader.Misc.VirtualSize; 

    printf("[*] Base address : 0x%.8x\n", (unsigned int) dllHandle); 
    printf("[*] Start address: 0x%.8x\n", (unsigned int) starAddr); 
    printf("[*] End address : 0x%.8x\n", (unsigned int) endAddr); 

    // Simple liner search, when ever we find `0xff 0xd5' we print that address 
    for(endAddr -= sizeof(opcode) ; starAddr < endAddr ; starAddr++) { 
     if(memcmp(&opcode, (void *) starAddr, sizeof(opcode)) == 0) { 
      printf("[*] Found `call ebp` at: 0x%.8x\n", (unsigned int) starAddr); 
     } 
    } 

    FreeLibrary(dllHandle); 
    return 0; 
} 

編譯並與該DLL測試:

> gcc -Wall findopcode.c -o findopcode 
> findopcode.exe test.dll 
[*] Code section: `.text' 
[*] Base address : 0x6bec0000 
[*] Start address: 0x6bec1000 
[*] End address : 0x6bec1984 
[*] Found `call ebp` at: 0x6bec125a 
[*] Found `call ebp` at: 0x6bec1268 

> 

它工作得很好,讓我們嘗試user32.dll

> findopcode.exe \Windows\System32\user32.dll 
[*] Code section: `.text' 
[*] Base address : 0x75680000 
[*] Start address: 0x75681000 
[*] End address : 0x756e86ef 
[*] Found `call ebp` at: 0x756b49b5 

> 

我只找到一個call ebp a t 0x756b49b5。請注意,你的方式要檢查,如果你有一個讀訪問您使用IsBadReadPtrmemcmp閱讀前:

 if(IsBadReadPtr(starAddr, sizeof(opcode)) == 0 && 
      memcmp(&opcode, (void *) starAddr, sizeof(opcode)) == 0) { 

所以如果你擊出了一些面積一些奇怪的訪問計劃不會失敗。

+2

哇!優秀的答案。非常有用和信息豐富。非常感謝! –