I never realised how much effort it is to get the base address for another process. Your own process base address is equal to the HINSTANCE passed to WinMain, so that's easy enough. You could assume that the other process is at 0x00400000, and almost all of them will be - but that's bound to trip you up at some point.
To get the base address for another process, you need to read the FS segment from a thread in that process, get the Thread Environment Block, get the address of the Process Environment Block from that, and then get the process base address from there.
Now, that wouldn't be quite so bad if the structures were implemented as something other than a series of BYTE and DWORD arrays called dwReserved1 and suchlike.
I can completely understand why that's been done, since it could change at any point (I'll be testing this on Vista when I get home), but it's still annoying.
Anyway, chunk of code to get the base address (Assumes hThread and hProcess are handles to a thread in the process and the process itself respectively):
// Get thread contextCONTEXT theContext;theContext.ContextFlags = CONTEXT_FULL;if(!GetThreadContext(hThread, &theContext)) return false;// Get address of the FS segmentLDT_ENTRY selEntry;if(!GetThreadSelectorEntry(hThread, theContext.SegFs, &selEntry)) return false;DWORD dwFSBase = (selEntry.HighWord.Bits.BaseHi << 24) | (selEntry.HighWord.Bits.BaseMid << 16) | selEntry.BaseLow;// Read thread/process environment block to get image base addressDWORD dwBaseAddress=0;DWORD dwBytes;{ // Read entire TEB TEB theTEB; if(!ReadProcessMemory(hProcess, (void*)dwFSBase, &theTEB, sizeof(theTEB), &dwBytes) || dwBytes!=sizeof(theTEB)) { return false; } // PEB address is in TEB at byte offset 0x30 DWORD dwPEBAddress = *(DWORD*)(((BYTE*)&theTEB)+0x30); // Base address is in the PEB at offset 0x08 if(!ReadProcessMemory(hProcess, (void*)(dwPEBAddress+8), &dwBaseAddress, sizeof(dwBaseAddress), &dwBytes) || dwBytes!=sizeof(dwBaseAddress)) { return false; }}// Read the DOS headerIMAGE_DOS_HEADER dosHeader;if(!ReadProcessMemory(hProcess, (void*)dwBaseAddress, &dosHeader, sizeof(dosHeader), &dwBytes) || dwBytes!=sizeof(dosHeader)){ return false;}// Validate itif(dosHeader.e_magic != IMAGE_DOS_SIGNATURE){ return false;}// Read NT headerIMAGE_NT_HEADERS ntHeader;if(!ReadProcessMemory(hProcess, (void*)(dwBaseAddress+dosHeader.e_lfanew), &ntHeader, sizeof(ntHeader), &dwBytes) || dwBytes!=sizeof(ntHeader)){ return false;}// Validate itif(ntHeader.Signature != IMAGE_NT_SIGNATURE){ return false;}
Well, I'm going to recover from that by doing some Nitro Character stuff (oh god, why me...)