osusec
monday, 1/13
imagine a program
adds 3 and 4
how tho?
our programs (3 4 +) are just ways of manipulating a
stack
and a stack is just kind of like scratch space to put numbers for later
use.
and digits are just arguments to functions
we can write pseudo-asm:
push 4 [4]
push 3 [4, 3]
push 4 [4, 3, 4]
add [4, 7]
multiply [28]
push 3 [28, 3]
add [31]
as code gets more complex, we like to write functions/subroutines. reusable, flexible pieces of code.
this might look something like
push 3 [3]
call square [9]
call square [81]
(inside square we’ve got some code that squares a number)
how does our program get back to the next line of code to where
it was once square completes?
square might need to use the stack too - how do we
make sure the function that calls it will know where the stack
is?
how are we keeping track of all this stack stuff in the first place
i tried really hard to cleanly connect this intro to what we’re about to do but the best I can come up with is asking these questions
so keep these in mind as we move into a C program
the answer to all of these questions is registers, by the way.
saving registers. restoring registers. registers registers registers.
what’s it like to hold the hand of someone you love? registers. have they left a place for you where you can dream? registers.
int main() {
0x00001178 <+0>: push ebp
0x00001179 <+1>: mov ebp,esp
0x0000117b <+3>: sub esp,0x10
int my_fun_variable = 3;
0x0000117e <+6>: mov DWORD PTR [ebp-0x4],0x3
square(my_fun_variable)
0x00001185 <+13>: push DWORD PTR [ebp-0x4]
0x00001188 <+16>: call 0x116d <square>
0x0000118d <+21>: add esp,0x4 // for alignment
square(square(my_fun_variable))
0x00001190 <+24>: push eax
0x00001191 <+25>: call 0x116d <square>
int even_funner = square(square(my_fun_variable))
0x00001196 <+30>: add esp,0x4 // alignment
0x00001199 <+33>: mov DWORD PTR [ebp-0x8],eax
}
0x0000119c <+36>: mov eax,0x0
0x000011a1 <+41>: leave
0x000011a2 <+42>: retidiot-proofing:
ebp, esp, DWORD PTR, eax, call, ret
but it’s solving all our problems!
what do call and leave and ret
do?
we mark where our stack was when we entered a function, and it gives us a stable peg to base new stack entries off of.
(this is because it’s nice to have access beyond push/pop. from now on, when I say stack, i really mean The Stack, which is not exactly a stack.)
push ebp
mov ebp,esp
saves the parent function’s “base pointer” to the stack
and copies the current stack pointer into the base pointer register
main is kinda weird because the thing that calls
main has a base pointer of 0. anyways we’ll get back into
it in a second.`
next up is
mov loads 3 into a local variable, located 4 above
ebp
parameters are pushed to the stack
and then we call 0x8049156
call moves our instruction pointer eip to a
new place in memory… and something else >:3
eip is a little guy that walk around memory and tells
the CPU what instruction to fetch and execute
square just has pop ebp, no leave this
works because the stack isn’t really used in square
leave is shorthand for
mov esp,ebp , pop ebp
there are random blank/unused spots on the stack yeah, compilers like to do that.
call pushes $eip and jumps to a new
addressleave pops the saved $ebp into
$espret pops a return address into $eip$eaxtemplate in description, but we here to help p32() is ur
friend!
sudo dpkg –add-architecture i386 sudo apt-get update