3

I am trying to implement the known method of "Dynamic Forking of Win32 EXE", which is knows as RunPE. My problem is that i am can't get the right result of the "base address" as it mentioned in the 3rd point at http://www.security.org.sg/code/loadexe.html

This is my code:

DWORD* peb;
DWORD* baseAddress;
...snip...

GetThreadContext(hTarget, &contx)

peb = (DWORD *) contx.Ebx;
baseAddress = (DWORD *) contx.Ebx+8;

_tprintf(_T("The EBX [PEB] is: 0x%08X\nThe base address is: 0x%08X\nThe Entry Point is: 0x%08X\n"), peb, baseAddress, contx.Eax);

and the output is as follwos:

The EBX [PEB] is: 0x7FFD4000

The base address is: 0x7FFD4020

The Entry Point is: 0x00401000

I think that my problem is with the implementation of my baseAddress pointer, but i can't figure out exactly what is the issue. Or could be that i havn't understand the above article correctly and baseAddress isn't ImageBase, if so what is baseAddress ?

I have tried to run it under Win 7 64b and Win-XP and on both i am get the same incorrect results.

Hanan
  • 1,169
  • 3
  • 23
  • 40

3 Answers3

2

You're just doing pointer arithmetic, you're not actually dereferencing the memory, in this line:

baseAddress = (DWORD *) contx.Ebx+8;

You're just adding 8*sizeof(DWORD) = 32 to the value of contx.Ebx. What you really want to do is read the data at the address of contx.Ebx+8 in the new process's address space. In order to do that, you need to use ReadProcessMemory, and don't bother to cast -- you want to use raw offsets, not offsets multiplied by sizeof(DWORD), which is what happens when you do pointer arithmetic with DWORD* values.

However, I'd strongly caution you against digging into implementation details like this, which can and do change between different versions of Windows. Keep in mind that the article you linked to was written in 2004, and it was just a proof-of-concept, so there are likely to be lots of hidden gotchas and unexpected problems in Vista, Windows 7, Windows 8, and future versions.

The Windows API does not have a function that behaves like Unix's fork(2) function, and as a result, you should try as much as possible to avoid needing to fork -- use CreateProcess instead of fork+exec etc. The Cygwin implementation of fork is ugly and slow, and it can fail unexpectedly due to DLL memory mapping issues.

Adam Rosenfield
  • 390,455
  • 97
  • 512
  • 589
  • I can't understand how the (DWORD *) become a times operation? I know about the order of arithmetic operations, but why a cast become arithmetic? – Hanan Oct 09 '12 at 21:41
  • @Hanan: Do you understand how pointer arithmetic works? `((DWORD *)anything)` is a pointer, and when you add a pointer and an integer, the compiler always multiplies the integer by the size of the pointed-to element, which in this case is `DWORD` with a size of 4. The `*(contx.Ebx+8)` can't be done using pointers because the data you want lies in a different process's [virtual address space](http://en.wikipedia.org/wiki/Virtual_memory). – Adam Rosenfield Oct 09 '12 at 21:46
2

Note that the instructions say "at [EBX+8]". The brackets mean the value at that address location. There are several problems with

baseAddress = (DWORD *) contx.Ebx+8;

First, the compiler doesn't pay attention spacing, only to parenthesizing, so this means

baseAddress = ((DWORD *)contx.Ebx) + 8;

which is wrong because the 8 is counting DWORDs, rather than bytes. You want

baseAddress = (DWORD *)(contx.Ebx + 8);

but this just gets you the address where the baseAddress is stored, not the value of the baseAddress. For that you need

baseAddress = *(DWORD *)(contx.Ebx + 8);

However, this only works if contx.Ebx refers to an address in your process, but every process has its own address space, and you need to access the address space of the suspended process; for that you need to use ReadProcessMemory ( http://msdn.microsoft.com/en-us/library/windows/desktop/ms680553%28v=vs.85%29.aspx ):

ok = ReadProcessMemory(hTarget, (LPCVOID)(contx.Ebx + 8), (LPVOID)&baseAddress, sizeof baseAddress, NULL);
Jim Balter
  • 16,163
  • 3
  • 43
  • 66
  • I am just need the suspended thread's base address, which i think i will be able to get that even without ReadProcessMemory isn't? – Hanan Oct 09 '12 at 22:42
  • 1
    @HananN. No, you can't get it without ReadProcessMemory, as I just explained and as Adam Rosenfield explained. If you want to be helped, please put in the effort to read and understand the explanations. Have you tried using ReadProcessMemory as I suggested and did it work for you? – Jim Balter Oct 09 '12 at 23:14
  • just because i am trying to learn and understand i am asking again, since i didn't quite understand what you and Adam said. – Hanan Oct 10 '12 at 07:09
  • why am i should add the `(LPCVOID)` in front of `(contx.Ebx + 8)` at the ReadProcessMemroy function call? – Hanan Oct 10 '12 at 20:01
  • 1
    @HananN. Because, from the MS doc I linked to, that parameter is declared `_In_ LPCVOID lpBaseAddress`. If you don't cast it, you will hae a type mismatch (`contx.Ebx + 8` has type DWORD) the compiler is likely to produce a warning. – Jim Balter Oct 10 '12 at 20:27
2

In addition to what others have said, there is a much simplier way to retreive the base address of the process's PEB structure. Use NtQueryInformationProcess() instead, setting its ProcessInformationClass parameter set to ProcessBasicInformation. The output will be a PROCESS_BASIC_INFORMATION structure:

typedef struct _PROCESS_BASIC_INFORMATION {
    PVOID Reserved1;
    PPEB PebBaseAddress;
    PVOID Reserved2[2];
    ULONG_PTR UniqueProcessId;
    PVOID Reserved3;
} PROCESS_BASIC_INFORMATION;

The second member is what you are looking for.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770