If you’ve found this page… Good for you!
Adding code to a PE
To add code to a PE, you can find “caves” which are 00 byte areas that can fit the code you want to add. This needs to be done in an executable section such as the code/text section. An indicator of room is when the virtual size is smaller than the Raw Size On Disk. This means that the compiler rounded up and padded extra space with zero bytes which is what we want.
To add code, first increase VirtualSize to a suitable size, then add the code.
Portable Executable Format Notes
Jump Thunk Table – Table of JMPs which lead to CALLs of dword ptrs to external functions. This process is much slower than using _declspec(dllimport) which allows for a simple CALL dword ptr [addr of import].
The Import Address Table is actually just FirstThunk’s final stage. FirstThunk starts out as an array of ptrs to IMAGE_THUNK_DATAs (often as IMAGE_IMPORT_BY_NAMEs) and the loader replaces all of these structs with pointers to the actual import functions’ virtual addresses once it has located them. At this stage, FirstThunk is known as the Import Address Table and if the original relative virtual addresses need to be referenced, they are stored in OriginalFirstThunk which is inside of IMAGE_IMPORT_DESCRIPTOR.
Ordinal-Only exports won’t have IMAGE_IMPORT_BY_NAME structs inside of IMAGE_THUNK_DATA. Instead, they will have an Ordinal of the import.
IMAGE_THUNK_DATA contains a union, which allows us to modify the different bits inside of it to set different things. For example, if the high order bit is set, then the rest of the DWORD’s bits are the ordinal. Otherwise if the high order bit is 0, then the system knows that we’re dealing with a relative virtual address to a IMAGE_IMPORT_BY_NAME array. For example, in binary, if we’re dealing with an ordinal, it’ll look like:
10000000000000000101001111111010 which is also 0x800053FA.
Note how high order bit is set to 1 here. If it were zero, then the 53FA would refer to a relative virtual address to a IMAGE_IMPORT_BY_NAME on the heap. This can easily be tested by using bitwise AND:
10000000000000000101001111111010 AND WITH
10000000000000000000000000000000 GIVES US:
Thus, since 0x800053FA AND 0x80000000 == 0x80000000, we know that 0x800053FA is an ordinal. Turns out there’s a macro for 0x80000000: IMAGE_ORDINAL_FLAG32.
The Ordinal is not an RVA.
Missing Import Name Table is not a good idea because then the binder cannot replace all of the names with ordinals at compile-time.
To lookup an imported routine by name, check the Data Directory and locate the RVA of the Import Directory. The Import Directory RVA will point to an array of IMAGE_IMPORT_DESCRIPTORs. Now we grab the IMAGE_IMPORT_DESCRIPTOR with the Name1 that we’re looking for by searching through the Import Directory array. When found, we go to FirstThunk and search through the IMAGE_THUNK_DATA structs for the function with the name we seek.
not – 6-15 cycles
mov – 5-11 cycles
add, sub, cmp, and, or – 8-17 cycles
constants – 0 cycles
registers – 1 cycle
word-aligned memory operand – 2 cycles
unaligned memory operand – 3 cycles
886 – No special features
8286 – Prefetch queue
8486 – Prefetch queue, cache, and pipeline
8686 – All of above but also superscalar operation