Position-independent code (PIC) is code that uses no hard-coded addresses for either code or data. Shellcode is PIC. It cannot assume that it will be located at a particular memory location when it executes, because at runtime, different versions of a vulnerable program may load the shellcode into different memory locations. The shellcode must ensure that all memory access for both code and data uses PIC techniques.
Table 19-1 shows several common types of x86 code and data access, and whether they are PIC.
Table 19-1. Different Types of x86 Code and Data Access
Instruction mnemonics | Instruction bytes | Position-independent? | |
|---|---|---|---|
|
|
| Yes |
|
|
| Yes |
|
|
| No |
|
|
| Yes |
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.)