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

时间:2013-07-27 00:01:36

标签: windows security dll exploit shellcode

如何找到特定指令的内存地址(用于漏洞利用写入)?

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

2 个答案:

答案 0 :(得分:6)

要查找指令,您需要确定代码,.text,节的开始和结束位置,然后加载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扫描程序进行编码,我们需要进行流程:

  1. 读取PE信息并获取可执行部分信息,通常为.text,以查找其相对地址及其虚拟大小。
  2. 使用LoadLibrary加载DLL,它将返回DLL的基址。
  3. 代码段开头的虚拟地址是:DLL基地址+代码段virtualAddress,它以DLL基址+代码段virtualAddress + VirtualSize结束。
  4. 现在我们已准备好遍历代码并查找0xff 0xd5call ebp的操作码,简单的线性搜索。
  5. 这是一个简单的实现:

    // 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找到一个0x756b49b5。请注意,您希望在使用IsBadReadPtr阅读memcmp之前检查您是否具有读取权限:

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

    所以如果你遇到某些奇怪的访问区域,程序不会失败。

答案 1 :(得分:0)

另一种方法是使用metasploit framework中的msfpescan

msfpescan -j ebp user32.dll