Position Independent Code self sufficiency of combining program
Producing a listing file If you supply the -l option to NASM, followed (with the usual optional space) by a file name, NASM will generate a source-listing file for you, in which addresses and generated code are listed on the left, and the actual source code, with expansions of multi-line macros For example: nasm -f elf asm0.s -l asm.lst
1 section.rodata data_strat: dd 5, D795F66756E fmt: db "my_func result is: %d", 10, C A A A00 6 %define num1 8 7 %define num section.text 10 global my_func 11 global main 12 extern printf main: push ebp E5 mov ebp, esp push dword num push dword num D E81B call my_func C add esp, push eax [ ] push fmt E E8( ) call printf C add esp, EC mov esp, ebp B 5D pop ebp C C3 ret 31 Example of a listing file (main)
32 my_func: D 55 push ebp E 89E5 mov ebp, esp B4D08 mov ecx, [ebp+8] ; get n B mov eax, func_loop: B5D0C mov ebx, [ebp+12] ; get m B 01D8 add eax, ebx D 49 dec ecx E 7405 jz func_fin E9F3FFFFFF jmp func_loop func_fin: EC mov esp, ebp D pop ebp C3 ret Example of a listing file (my_func)
self sufficiency of combining program If we have one program which is PIC we can add this program to any other program without fear that it might not work because the PIC program has every this it needs interiorly.
Position Independent Code No direct use of labels Only relative jumps. “call” is relative. No global variables (no data, rodata or bss). No library functions Only system calls.
No direct use of labels Labels are solved by the assembler and linker at compile time to an absolute number. So if the code is moved the number wont be correct any more. Remember” label’s address don’t change at run-time though the code address may change.
No direct use of labels 1 section.data C6C6F20776F72- hello: db 'Hello world!', C64210A 4 helloLen: equ $ - hello 5 6 section.text 7 global _start 8 9 _start: B9[ ] mov ecx,hello BA0D mov edx,helloLen A B mov eax, F BB mov ebx, CD80 int 80h B mov eax, B BB mov ebx, CD80 int 80h
Only relative jumps We can use only relative jumps because the code we wish to jump to may change its position. If all the code change its position relative jumps are still valid because the address difference is preserved.
“call” is relative We can still use “call” instruction because it makes a relative jump to the procedure which means that the new IP (after “call”) will be the old IP (before “call”) plus a number (may be negative) with was the address difference of the two places calculated by the assembler.
No global variables There are no global variables because we can’t assume that DATA, RODATA or BSS sections exists. If they do exists we don’t know their address and we can’t allocate memory there. We can use constant variables (mainly strings) as a part of our code.
No library functions We don’t know if and where the library functions are. Thus there are no “printf” “gets” “open” “strlen” and so on…
Only system calls To perform I/O operation we have to use the Linux system calls because INT 0x80 isn’t a regular procedure it is called via the interrupt table which is static.
Finding your code address at run-time get_my_loc: call next_i next_i: pop edx ret
Finding your code address at run-time Since we can use the “call” instruction because it makes a relative jump we can call a function that we write in our code. The call instruction push the current IP at run-time. Thus if we know where “call” was, we now posses an anchor address at run-time.
Using strings – PIC example section.data name:db "Moshe",10,0 nameLen equ $ - name global _start get_my_loc: call next_i next_i: pop edx ret _start: call get_my_loc sub edx, next_i - name mov ecx,edx mov edx,nameLen mov eax,4 mov ebx,1 int 80h mov eax,1 int 80h
Using strings – PIC example 1 section.data D6F A00 name db "Moshe",10,0 4 nameLen equ $ - name 5 6 global _start 7 get_my_loc: E call next_i C C3 ret 10 next_i: D 5A pop edx 12 _start: F E8F3FFFFFF call get_my_loc EA0D sub edx, next_i - name A 89D1 mov ecx,edx C BA mov edx,nameLen B mov eax, BB mov ebx, B CD80 int 80h D B mov eax, CD80 int 80h
Using strings – PIC example Get_my_loc function gets the address of “next_i “ at run-time the address difference between “next_i” and “name” is constant even if the code will change it’s position. So when we find the address of “next_i” using the listing file we can find the address of “name” at run time.
Jump tables
Jump table applications To implement a “switch” or a “case”. To select a function to be evoked.
How to implement a “switch” or a “case”. We will construct a array of the jump addresses. For each number will jump to the corresponding entry in the jump table
Main.c extern void jumper(int); int main (int argc, char* argv) { jumper (0); jumper (1); jumper (2); return 0; }
jump.s section.data jt: dd label_1 dd label_2 str0: db "Got the number 0",10,0 str1: db "Got the number 1",10,0 str2: db "Out of bound",10,0 str3: db "num = %d",10,0 section.text align 16 global jumper extern printf jumper: push ebp mov ebp,esp pusha mov ebx,dword [ebp+8] push ebx push str3 call printf; Print num addesp, 8 cmp ebx,0; Check if num is in bounds jb out_of cmp ebx,1 ja out_of shl ebx,2; num = num * 4 jmp dword [ebx+jt]; Jump according to address in table label_1: push str0 call printf addesp, 4 jmp end label_2: push str1 call printf addesp, 4 jmp end out_of: push str2 call printf addesp, 4 jmp end end: popa pop ebp ret
Output num = 0 Got the number 0 num = 1 Got the number 1 num = 2 Out of bound
jump_pic.s section.data jt: dd label_1 - next_i ; For PIC, this is an offset dd label_2 - next_i str0: db "Got the number 0",10,0 str1: db "Got the number 1",10,0 str2: db "Out of bound",10,0 str3: db "num = %d",10,0 section.text align 16 global jumper extern printf get_my_loc: call next_i next_i: pop edx ret jumper: push ebp mov ebp,esp pusha mov ebx,dword [ebp+8] push ebx push str3 call printf; Print num addesp, 8 cmp ebx,0; Check if num is in bounds jb out_of cmp ebx,1 ja out_of shl ebx,2 movebx, dword [ebx + jt] callget_my_loc addedx, ebx jmp dword edx label_1: push str0 call printf addesp, 4 jmp end label_2: push str1 call printf addesp, 4 jmp end out_of: push str2 call printf addesp, 4 jmp end end: popa pop ebp ret