250 likes | 408 Views
Position Independent Code. self sufficiency of combining programs. Producing a listing file.
E N D
Position Independent Code self sufficiency of combining programs
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
Example of a listing file (main) 1 section .rodata 2 00000000 0500000006000000 data_strat: dd 5,6 3 00000008 6D795F66756E632072- fmt: db "my_func result is: %d", 10, 0 4 00000011 6573756C742069733A- 5 0000001A 202025640A00 6 %define num1 8 7 %define num2 7 8 9 section .text 10 global my_func 11 global main 12 extern printf 13 14 main: 15 00000000 55 push ebp 16 00000001 89E5 mov ebp, esp 17 18 00000003 6808000000 push dword num1 19 00000008 6807000000 push dword num2 20 0000000D E81B000000 call my_func 21 00000012 81C408000000 add esp, 8 22 23 00000018 50 push eax 24 00000019 68[08000000] push fmt 25 0000001E E8(00000000) call printf 26 00000023 81C408000000 add esp, 8 27 28 00000029 89EC mov esp, ebp 29 0000002B 5D pop ebp 30 0000002C C3 ret 31
Example of a listing file (my_func) 32 my_func: 33 0000002D 55 push ebp 34 0000002E 89E5 mov ebp, esp 35 36 00000030 8B4D08 mov ecx, [ebp+8] ; get n 37 00000033 B800000000 mov eax, 0 38 39 func_loop: 40 00000038 8B5D0C mov ebx, [ebp+12] ; get m 41 0000003B 01D8 add eax, ebx 42 0000003D 49 dec ecx 43 0000003E 7405 jz func_fin 44 00000040 E9F3FFFFFF jmp func_loop 45 46 func_fin: 47 00000045 89EC mov esp, ebp 48 00000047 5D pop ebp 49 00000048 C3 ret
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 everything it needs internally.
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 resolved by the assembler and linker at compile time to an absolute address. So if the code is moved the number wont becorrect any more. Remember ”label’s address doesn’t change at run-time though the code address may change.
No direct use of labels 1 section .data 2 00000000 48656C6C6F20776F72- hello: db 'Hello world!',10 3 00000009 6C64210A 4 helloLen: equ $ - hello 5 6 section .text 7 global _start 8 9 _start: 10 00000000 B9[00000000] mov ecx,hello 11 00000005 BA0D000000 mov edx,helloLen 12 13 0000000A B804000000 mov eax,4 14 0000000F BB01000000 mov ebx,1 15 16 00000014 CD80 int 80h 17 18 00000016 B801000000 mov eax,1 19 0000001B BB00000000 mov ebx,0 20 00000020 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 changes 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 pushes the current EIP at run-time. Thus if we know where “call” was, we now possess an anchor address at run-time.
Using strings – PIC example • section .text • 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 .text 2 3 00000000 4D6F7368650A00 name db "Moshe",10,0 4 nameLen equ $ - name 5 6 global _start 7 get_my_loc: 8 00000007 E801000000 call next_i 9 0000000C C3 ret 10 next_i: 11 0000000D 5A pop edx 12 _start: 13 0000000F E8F3FFFFFF call get_my_loc 14 00000014 81EA0D000000 sub edx, next_i - name 15 0000001A 89D1 mov ecx,edx 16 0000001C BA07000000 mov edx,nameLen 17 00000021 B804000000 mov eax,4 18 00000026 BB01000000 mov ebx,1 19 0000002B CD80 int 80h 20 0000002D B801000000 mov eax,1 21 00000032 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 changes it’s position . So if we find the address of “next_i” using the listing file, we can find the address of “name” at run time.
Jump table applications • To implement a “switch” or a “case”. • To select a function to be evoked.
How to implement a “switch” ora “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 label_1: push str0 call printf add esp, 4 jmp end label_2: push str1 call printf add esp, 4 jmp end out_of: push str2 call printf add esp, 4 jmp end end: popa pop ebp ret 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 add esp, 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
Output num = 0 Got the number 0 num = 1 Got the number 1 num = 2 Out of bound
shl ebx,2 call get_my_loc add edx, jt – next_i mov ebx, [edx + ebx] call get_my_loc add edx, ebx jmp edx label_1: push str0 call printf add esp, 4 jmp end label_2: push str1 call printf add esp, 4 jmp end out_of: push str2 call printf add esp, 4 jmp end end: popa pop ebp ret jump_pic.s section .text 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 movebp,esp pusha movebx,dword [ebp+8] push ebx push str3 call printf; Print num add esp, 8 cmp ebx,0 ; Check if num is in bounds jbout_of cmpebx ,1 jaout_of