In the table, the call
instruction contains a 32-bit signed relative displacement that is added to the address immediately following the call
instruction in order to calculate the target location. Because the call
instruction shown in the table is located at 0x0040103A, adding the offset value 0xFFFFFFC1 ❶ to the location of the instruction, plus the size of the call
instruction (5 bytes), results in the call target 0x00401000.
The jnz
instruction is very similar to call
, except that it uses only an 8-bit signed relative displacement. The jnz
instruction is located at 0x00401034. Adding together this location, the offset stored in the instruction (0xe
) ❷, and the size of the instruction (2 bytes) results in the jump target 0x00401044.
As you can see, control-flow instructions such as call
and jump
are already position-independent. They calculate target addresses by adding a relative offset stored in the instruction to the current location specified by the EIP register. (Certain forms of call
and jump
allow programmers to use absolute, or nonrelative, addressing that is not position-independent, but they are easily avoided.)
The mov
instruction at ❸ shows an instruction accessing the global data variable dword_407030
. The last 4 bytes in this instruction show the memory location 0x00407030. This particular instruction is not position-independent and must be avoided by shellcode authors.
Compare the mov
instruction at ❸ to the mov
instruction at ❹, which accesses a DWORD
from the stack. This instruction uses the EBP register as a base, and contains a signed relative offset: 0xFC (-4)
. This type of data access is position-independent and is the model that shellcode authors must use for all data access: Calculate a runtime address and refer to data only by using offsets from this location. (The following section discusses finding an appropriate runtime address.)