如何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
,第二条指令是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
扫描器,我们需要做的流动:
.text
,找到它的相对地址和它的虚拟大小。 0xff 0xd5
, call 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(§ionHeader, 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