如何查找DLL中特定指令的内存地址

如何find特定指令的内存地址(用于利用写入)?

具体来说,我正在寻找在Windows XP的user32.dll call ebp指令,没有Service Pack的地址我可以指向EIP 。 我在目标上都安装了Immunity Debugger和OllyDBG 。

要找到一个指令,你需要找出代码,.text,section开始和结束的地方,然后加载DLL,直到你找到指令。

这里我们有一个测试DLL,它有两个call ebp指令:

 // 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 ,第二条指令是0x6bec1268call 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 > 

我只在0x756b49b5找到一个call ebp 。 请注意,在使用IsBadReadPtr读取memcmp之前,您需要检查是否具有读取访问权限:

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

所以程序不会失败,如果你碰到一些怪异的访问区域。

另一种方法是使用metasploit框架中的msfpescan :

 msfpescan -j ebp user32.dll