7

Typically when I write a game-hacking executable after reversing a game, I go about code-caving to find where I can inject my code as need be. Well, while reversing a few such executables from someone else, I've noticed they choose to always inject their code at Game.exe+100. This has me wondering if it's just easier to start doing that and calling it done.

However, my question here is for those a bit more familiar with the PE format than I am at the moment (to be honest, my brain is swimming at the moment trying to wrap my head around the resources I've Googled up to this point):

Why offset 0x100 from the module base address? Once a PE is mapped into memory, are there documented and/or well-known offsets from the image base that are pretty much always safe to overwrite/write to? If so, which offsets might they be? From there, I'll look to understand what each of those sections represent, as well as how many bytes could be used from each offset.

By all means, instead of laboring an answer, feel free to point me to the right chart or information that details the specific offsets. I'm overwhelmed with information right now and a bit confused by what all I'm looking at (like seeing the same offset number being used for various sections, etc.). Thanks!

2 Answers 2

8

In PE files, sections are generally mapped at addresses that are multiples of 4kb (this is the default value, though it can be overridden). That means that even a section that is a single physical byte long will have the entire 4kb of memory allocated for it. The first section is mapped into memory after the file header, so it will be 4kb away from the file header. It's rare that a file header requires that much space to be used, now that the Bound Import Table is deprecated, so there's a lot of free space. 0x100 is a nice round number that is beyond the documented standard header size, which is why it's used.

2
  • Very awesome explanation. Just to make sure I'm understanding correctly, I then effectively have a range of 0x100 to 0x1000 to work with before getting into any other section, correct? And finally, I'm assuming I'll need to make that region of memory RWX via VirtualProtectEx(). That's something I can try out, but just for the sake of throwing it out there for anyone who might happen upon this thread later.
    – dsasmblr
    Commented Aug 13, 2018 at 17:51
  • 1
    Yes, for the general case, you have 0x0100 to 0x0FFF available; and yes, in the general case you need VirtualProtectEx() to make the header writable. Commented Aug 27, 2018 at 21:16
2

Short answer: Because your average program will never access those values.

Long answer: Peter ferrie's answer states there's usually nothing present at that offset. That is not true. The shortest possible valid header for a Portable Executable is 0xc8 long. But that's for a program with a single section, no imports (or anything else that would require a data directory), no DOS stub and no Rich header (impossible with Visual Studio without patching the linker). The shortest header for a realistic (but still mostly academic) program with imports and two sections (real programs usually have 4–7 sections) will be at least 0x160 long. Add the default DOS stub and you're at 0x1a0. Add the Rich Header and that's typically another 0x30-0x80 bytes. In fact you rarely see PEs whose headers fit into the first 512 bytes. So at 0x100 you will be overwriting data, however, the OS no longer uses that data once the loader is done loading the image into memory, so as long as the program doesn't use that data either(e.g. loading resources), it'll be fine.

It's probably worth noting that all the offsets after 0x40 are variable, so without examining the target binary first you don't even know what you're overwriting at 0x100. Since you'll have to call VirtualProtectEx anyway (whether it's to make a data section executable or a code section writable), you might as well call VirtualAllocEx and not bother with finding code caves at all. If I had to guess why the other guy is always injecting to 0x100, I'd say it's because that way he's running from a known address (unless ASLR is enabled and the target binary is relocatable) so he doesn't have to relocate his code before injecting/make it position independent. It's lazy, sloppy, and unsafe.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.