views:

249

answers:

4

How can I convert an offset in text segment of an Win32 executable into a pointer at runtime?

When using a disassembler, I can see the relative addresses. But how can I convert them to an absolute address at runtime?

For example:

.text:402BE620

Which address is that at runtime? How can I convert that number into a pointer?

Some background: I have to fix a bug in a end-of-life DLL and we don't have access to the source code. For that I want to hook a specific function and override it at runtime.

(It contains a call to WaitForSingleObject where MsgWaitForMultipleObjects had to be used, and because of the additional parameters it cannot be fixed with a hex editor)

EDIT: Thank you for your suggestions, but I added that stuff about hooking only to give you some context. I don't need a hooking framework!. I have a good hooking framework which does all the heavy lifting and - at least for now - I don't need any more information about hooking itself.

I just have to provide the address of the function to hook to my hooking framework and for that I have to know how to calculate it and I don't know how to determine it.

EDIT 2: I have used two different disassembler, both show the address as noted above. Somewhat odd: Both reject "402BE620" as an offset if I use their "goto" feature. I have to enter the offset as "2BE620" to make it work...

+1  A: 

I don't know the answer to your specific question, but even if I did, I don't think your plan will work. I take it you are planning to dynamically patch the function call with a call (or jump) to your own code? However, what happens if the page that contains the patch is discarded by the OS and then re-loaded (from the DLLs disk image) sometime later? How are you going to detect this and re-patch it?

Just a thought....

anon
no, this will work. altering the executable page will trigger a copy-on-write, so forevermore that running process will use the patched code.
Jewel S
I know that is how data pages worked, but I didn't realise it was the same for code pages. Makes sense though.
anon
A: 

To do something like this you would usually use detours
It allows you to create a thread in a running process and hook/replace any function in it.
Usually it works by looking up the symbol but there shouldn't be a reason it won't work with an offset.

To specifically answer your question, In the dll file you will usually find relative addresses. relative to the start of the file. When a PE (dll, exe) is loaded into memory all of the relative addresses are replaced with absolute addresses using the base address where the PE is loaded. You can check the base address of a specific DLL using GetModuleHandle()
402BE620 doesn't look like a relative address at all. it really looks like an absolute address so you better be sure what the disassembler shows you. relative addresses can't be larger than the size of the DLL file they are in.
Another hint you might find useful - When you attach a debugger to a process it usually lists all of the DLLs and their base addresses.

shoosh
A: 

This milw0rm paper on API Interception via DLL Redirection (PDF)
discusses a couple of ways that should give you an idea where you are venturing.

While there are several methods which can be used to achieve our goal, this tutorial will examine only DLL redirection. This approach was chosen for several reasons:

  1. It is relatively simple to implement.
  2. It allows us to view and modify parameters passed to an API function, change return values of that function, and run any other code we desire.
  3. While most other methods require code to be injected into the target process or run from an external application, DLL redirection requires only write access to the target application's working directory.
  4. We can intercept any API call without modifying the target (either on disk or in memory) or any system files.

Once we have redirected the program to load our DLL, we need to have all the functions it is looking for. Let's say that we want to intercept every call that Internet Explorer makes to MessageBox. MessageBox is located in user32.dll, so we will need to create a DLL called user32.dll that contains an exportable function named MessageBox, create the iexplore.exe.manifest file, and place our newly created user32.dll and iexplore.exe.manifest files in the C:\Program Files\Internet Explorer directory. Now when IE imports its API functions, it will load MessageBox from our user32.dll file; then, whenever IE calls the MessageBox function, the code we placed on our MessageBox function will be executed.

The catch is that MessageBox is not the only function that will be imported from user32.dll. There could be hundreds of functions that IE will be looking for in user32.dll and if any of these are missing then IE will fail to load. Since we don't want to re-write all of the user32 DLL functions, we will simply forward the rest of the functions to the original user32.dll file

nik
A: 

i don't know about the offset from the .text segment specifically, but in the debugger look at the address you want and subtract the module load offset from it. it may get loaded at a different address every time, even if the base address is explicitly set.

an alternative to runtime patching may be to simply patch the dll itself. OllyDbg is a great free assembly level debugger and code analysis tool that lets you easily patch a binary.

edit:

just for fun here's some sample code i used for using an extension to binary patch out some functionality i didn't like in an app -- at runtime. i made some simplifications and didn't recompile and test, but you should get the idea.

(i had the complication that the binary was periodically updated, hence the additional work to pattern match within a small range from the known offset.)

void MyCrazyPatch()
{
    static bool patched = false;

    if (!patched)
    {
        patched = true;

        DWORD dwPatchOffsetStart = 0x5310;
        DWORD dwPatchLength = 22;

        BYTE rgPatchMatch[] = { 0xab, 0x07, 0x37, 0x56, 0x50, 0xe8, 0x92, 0xfb, 0xff, 0xff, 0x83, 
                                0xf8, 0x01, 0x89, 0xb5, 0xfc, 0x0f, 0x85, 0xb2, 0xaa, 0x00, 0x00, 0x8b };

        HMODULE hmod = GetModuleHandle(L"mybinary.dll");
        if (hmod != NULL
        {
            MODULEINFO modInfo;
            if (GetModuleInformation(GetCurrentProcess(),
                                     hmod,
                                     &modInfo,
                                     sizeof(modInfo)))
            {
                DWORD dwIncrement = 0;
                dwPatchOffsetStart += (DWORD)modInfo.lpBaseOfDll;

                while (dwIncrement < 0x200 &&
                       memcmp((void*)(dwPatchOffsetStart + dwIncrement),
                              rgPatchMatch,
                              sizeof(rgPatchMatch)) != 0)
                {
                    dwIncrement++;
                }

                // Sanity check then add nop's to stomp out the offending code.
                if (dwIncrement < 0x200 &&
                    memcmp((void*)(dwPatchOffsetStart + dwIncrement),
                           rgPatchMatch,
                           sizeof(rgPatchMatch)) == 0)
                {
                    DWORD dwOldProtect = 0;

                    VirtualProtect((void*)(dwPatchOffsetStart + dwIncrement),
                                   dwPatchLength,
                                   PAGE_EXECUTE_READWRITE,
                                   &dwOldProtect);
                    memset((void*)(dwPatchOffsetStart + dwIncrement), 0x90, dwPatchLength);

                    VirtualProtect((void*)(dwPatchOffsetStart + dwIncrement),
                                   dwPatchLength,
                                   dwOldProtect,
                                   NULL);
                }
            }
        }
    }
}
Jewel S