Question

How can I find the in-memory address (for exploit writing) of a specific instruction?

Specifically, I'm looking for a call ebp instruction in user32.dll on Windows XP with no Service Pack whose address I can point EIP to. I have both Immunity Debugger and OllyDBG installed on the target.

Was it helpful?

Solution

To find an instruction, you need to figure out where the code, .text, section starts and ends, then load the DLL and just do liner search until you find the instruction.

Here we have a test DLL that has two call ebp instructions:

// 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");
}

Compile it and load the DLL in ollydbg and click CTRL+F and search for 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

you see the address of the first instruction is at 0x6bec125a the second at 0x6bec1268. The opcode of call ebp is 0xff 0xd5, remember this.

Now we need to find the boundaries of the code, you can use objdump with -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
  ....

>

the code starts at VMA, virtual memory address, 0x6bec1000 and its size is 0x984, so it ends at 0x6bec1000 + 0x984 = 0x6bec1984 as :

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

I hope that was clear so far.

If we want to code our call ebp scanner, we need to do the flowing:

  1. Read the PE information and get the executable section information, usually .text, to find its relative address and its virtual size.
  2. Load the DLL using LoadLibrary, it will return the base address of the DLL.
  3. The virtual address of the beginning of the code section is: DLL base address + code section virtualAddress and it ends at DLL base address + code section virtualAddress + VirtualSize.
  4. Now we are ready to loop through the code and look for 0xff 0xd5, call ebp's opcode, simple liner search.

Here is a simple implementation:

// 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;
}

Compile it and test it with that 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

>

It works pretty well, let's try 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

> 

I only found one call ebp at 0x756b49b5. Note, you way want to check if you have a read access before you read with memcmp using IsBadReadPtr:

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

so the program won't fail if you hit some area with some weird access.

OTHER TIPS

An alternative way is to use the msfpescan from the metasploit framework:

msfpescan -j ebp user32.dll
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top