OK, welcome back.
So this third and last part of the session will go over a little bit about optimization, I would say,
and then finish this session with an example about the code and how it all looks like in the stack and
all of that.
And this is the last such that I would say theoretical session before we start jumping into some Hands-On
stuff.
So excuse me.
So, uh, first thing is computers do lots of generation.
They like they don't generate the code just at once.
What they will probably do is they will run the code multiple times and generate uh uh, let's say.
Better code, so they will when they do the compilation, they might generate multiple.
Multiple versions of the code in order at the end, let's say, to reach some optimization so you can
think of it, it's trying to do, let's say, generate some code, then generating code again, generating
code again until it finally can optimize the code to the best the best code, which will be more smaller
and more faster.
So it might take a couple of iterations to get that.
We are not going to discuss this out of the scope of this code to talk about optimization.
But this gives you an idea.
Here are also some instructions or an example, which will also probably give you an example of what
we are talking about.
So look at the instructions, move on, or they can be used to set X registers to zero.
So both Exodus Möller as a code by then the move instruction.
So look at this.
If we want to set, let's say, X with the value zero, we could do move X zero.
But the problem or let's say not the problem, but if we look at the code, it's actually five points.
As you can see here, the code is one, two, three, four, five bytes.
While if I do X or X with X, that should also give me a year.
That should also lead to having X with a value zero.
That's only two bytes.
So we here minimize the code with three bytes.
So it's much smaller.
But again, we're not going to talk a lot about optimization, but just to give you an example of how
things are going to happen.
So let's look at this.
So think of these instructions are going to be executed by the COLLEE.
And let's assume that the call is going to make another call to another function called Foo, which
which Foo will be like requiring one argument or one integer argument.
Now, the COLLY will set its local integer variability to seven and then send double its value to four.
Let's just take that as an example.
Again, we the call is going to call another function.
That function takes one argument and it's an integer.
And then that argument will create local integer variable and we are going to set it to seven.
Just an example, but we are going to also double that value and send it were to full.
Let's start so if you look here, the void Colly, which just again, what we are talking about here,
integer argument one takes one argument, integer V one, which is the here defining that we have one
variable type of integer V one equals seven.
So defining that V1 equals seven.
And then when we wanted to call for we want this and double that value.
So let's look at the code.
We push EBP Move SBP, ESP, and now we push the value zero because we want to create the variable in
the stack.
OK, that variable at the beginning is really it's not going to be really zero, but let's just think
of it like that at the beginning.
It has no value.
So it's going to be zero for the local variable.
Then what's going to happen is SBP copied the value seven into were SBP minus four is actually pointing
to.
So where SBP minus four is pointing to, we will copy the value seven into it then what SBP minus four
is pointing to.
We are going to copy it were into X, so we are copying now the value seven into X.
But before we before that here again EBP if we do were SBP minus four is pointing to.
So we have this location, we stored the value seven in there and then in the next instruction we did
what EBP minus four is pointing to copy Ekso or TBP minus four which is seven.
We copy that now we're into X, so we copied this into X..
Now we did Addicks, which is the double the value, so that made now X 14.
And then as you can see here, we have pushbikes, so we have 14 being pushed on top of the stack.
Then we did the call to full and then everything continues from there.
OK, now, before we continue, let's take a look at the stacks memory reminder.
This is about Indianness.
We talked about this, but this is just a reminder of how the actual bite by bite view will look into
memory.
So as you can see, this is how the bite by bite will.
If we look at the memory, let's say bite by bite, this is how the memory will look like for, let's
say, the value seven, because these are here.
It's four bytes.
This is four bytes.
Again, that's what we mentioned.
But if we look at the byte view, this is how these bytes are going to be represented in memory.
OK, now let's consider this.
This is actually not this is the little Indian and this is big Indian little Indian stores, the values
from the least significant bit first.
So we can see this is how the value is actually being stored.
While the big Indian starts from the most significant bit first.
So systems are processors like Intel will use have like they are multiplied by values are filled starting
from the least significant bit, while in begin again like Sparke processors, they will have there
they are, they will fold or they have their value filled in reverse order, starting from the more
significant bits.
So it's going to start this way, this way, etc..
OK, so that's why you need to keep in mind we are when in our case now, of course, we are talking
all about Intel, so we will be dealing with the little Indian.
It's very important to remember that, OK.
But as you can see here, SBP minus four here points to this valley, while SBP minus four points to
this valley.
But again, this is just a bite by bite level.
At the end, it's all going to be four points at least on on 32 bit system.
So let's go back here and we can see that we can see that the default value zero that was pushed in
the epilogue section was not used.
Why we did that, we just did that to save some space for our local variables.
So compilers like INSEE compilers do not push a default value.
Instead, they reverse the space by moving higher speeds register.
So the compiler will not actually really store of value.
It will reserve that space which we want.
OK, by moving E.S.P.
OK, also, instead of performing the pop to clean local variable space, we can also move to empty
the stack frame so we can do two things are the compiler will will do two things for optimization.
It will move E.S.P to make space for local variables, OK.
And it can also move E.S.P to clean the variables are the arguments on the stack.
OK, so now again, what will happen here is the compiler instead of just a few look, go back and forth.
So here we have pushed zero.
But here what happened is sub E.S.P by four.
So we made four bites of space on the stack now because and we now ESPs pointing also on top of the
stack.
But at that location, E.S.P here will be empty.
So we won't have this value for over here.
OK, A14 sorry now SBP SBP minus four sorry.
Copy seven and two minus four is pointing to.
So we copied that value over here then what.
EBP minus four is pointing to.
Copy it into exile.
We copied 1736 then ADC so we multiplied all with double the value three X now became fourteen.
So easy X has fourteen pushes the X on the stack so that we push now is six on the stack and then we
call the function four to do something.
Popke So if we pop this value it will be removed into X and then here we are cleaning the stack because
this is how the declaration will work.
We are removing this returning and then the solid DC declaration will have if there is another value
here, which is seven, it will be cleaned by the caller.
Now, E.S.P will move to reserve space for local variables, like I mentioned in the previous slide.
But the space is still not initialized, so the space is still not initialized.
Again, memory will not like delete and clear those values.
So this is what happens, by the way, in when if those of you, anyone of you, which is who is listening
now watching this video, when we first create a variable and C and we go and print it, we usually
get this gibberish data.
Why is it gibberish?
Because it's probably some value which was on the stack, which we are in that memory location which
we are now printing.
So again, this value is not really what's happening.
The system will optimize it and will just move.
Espy's much better than storing that value on the stack.
Let's continue now another thing which the compiler can do also to save space and make things faster
is replace both movie movie ASPE, which is when we restore we want to like the epilogue and we want
to like remove, delete that the stack frame and then pop up so we can replace those with one single
instruction, which is the leave instruction.
So what will happen now is instead of having more Vesp and then Pop will just to leave.
So what Live is actually doing is actually doing the movie ASPE and then doing the pop SBP.
OK, so again, like I mentioned at the beginning, the compiler is read the code in many passes before
generating the object code.
OK, now one thing the compiler will do is calculate the needed space for all arguments and like the
local variables.
OK, so if let's say in our case foo needed for bitts so it will allocate four bytes and that's why
we saw sub E.S.P was subbed before it was subtracted by four Y because we only need four bytes for the
function foo.
OK, also the push instruction push is slow instruction.
Therefore the compiler will reserve the argument space in the epilogue section.
OK, keep that in mind.
Push is also not a fast instruction.
It's slow so compilers will you will use something different, which is usually more so here as you
can see what happened.
Now we can see that if you takes two arguments, then SBP minus eight is the first one and SBP minus
12 is the second one.
Right.
Same as performing push for the second then the first argument.
OK, so if we had if, let's say Fule really took two arguments, then we will have E.S.P sub E.S.P
eight because we need one for the first and one for the second.
OK, everything else as you can see here, is actually kind of the same.
OK, except for the move Echikson toward SBP minus eight is pointing to.
Let's continue.
Now, SBP minus eight, what he means is point two is for sure the argument to be passed, but we can
replace it with the spin, this case.
So this is GBP minus four is this one and minus eight is this one, actually.
Why can we replace this in our scenario with with what ESPs pointing to?
Because currently Espy's pointing to the top of the stack and so we can we can do that.
But again, that's not our decision.
That's the.
That's what the compiler will do, so if that was to be replaced, we can do see here X was copied into
what EBP minus eight is pointing to while here.
What we did, the push is X, we did copy X to where E.S.P is actually pointing to.
So again, these are what the compiler or the optimization will will be doing.
Let's look at the example from our code.
Now, this code, by the way, could also be seen here.
So we could see in this one the code if we want to go back so you can see what my function one and then
you can see the function with the code.
We already saw that and function, too, which is going to be printing this message.
And then we have the main function.
And this is what Minyans is doing if you want to look at the assembly code for each one of them.
So this is the code for my function, one after being after disassembling it and saying for my function
to and this is the code for me, let's go back to our presentation.
So here the function, my function one requires 16 bytes for the local array.
So the local array, the buffer is 16 bytes.
OK, now ETR copy requires eight bytes because four bytes for this one and for bytes for this one.
The pointer, this pointer in this pointer each one requires for byte.
So we need 18 bytes.
So what that means and also my function to here in this case, let's just go with the slide requires
four bytes for its argument.
It's taking a pointer to a buffer and that argument is four bytes.
So this means the compiler made the decision to reserve 24 bytes, which is 18 in Hex, which is 16
for the array and eight for the maximum argument space required.
Since this one was used the same.
We didn't need to allocate that space twice.
We just used it once or sixteen and then four, which means twenty.
And then another four, which means 24 four.
OK, so that's how the compiler will decide, oh yeah, this code needs 24 bytes because this one is
16 bytes.
This one is four bytes.
This one is four bytes and so on and so forth.
Now, if you also, let's say, look at the code here.
When we let's say we are executing the IP ATSDR copy as your copy will take two values, which is the
source and the destination.
So we can see that SBP here, which is pointing here, and SBP minus 40, B minus eight, 12 and 16.
This is where the buffer started.
So this is the location where that buffer is located at.
So that's why you can see here we are saying load the effective address of SBP minus 16 into X, OK,
and then.
So this is where our destination is.
And then what we did is here we copied that address into what E.S.P is pointing to.
So that's is pinpointing here.
So that became the destination.
So that's why this code is this way.
That's what's happening over here.
OK, if we continue so by default, TBP points to the saved IP, OK of the caller in this example,
it will be mean and SBP points to the sort of SBP plus four plus four points to the the next instruction
or IP or the position where we want to go back.
In our case it's me, OK, and SBP itself is pointing to the saved EVP of me OK, which is for the previous
function.
So SBP this one is pointing for the the base pointer of the main function.
OK now again as the R takes to arguments destination pastie and the source as RC.
So that's these, all of this is just to prepare all of this on the stack.
OK, now SBP plus eight is the cent value by the caller main to the colly.
My function one OK SBP plus eight, so SBP plus eight is what the value which we sent.
I remember this, that this program, what it does is takes a string and then it sends it to be printed.
So SBP plus eight is our value.
ATSDR, OK, which has been copied where it was being passed to my my function.
OK, my function one.
Let's continue.
So, um, my function to here takes one argument, which is X, which actually if you go back here,
we'll see what it says is the destination.
It's the same thing, destination, which was the buffer because this is the source and this is the
destination.
How that's how ACOR copy works.
So this is a source and this is a destination.
And because the destination is still already on the stack, I just changed it here to see to make sure
you you, you understand that.
So X is still the destination, OK, which we are passing.
Where to my function one.
And now here what happens is when we go to my function to, let's say, hip's at here, what's going
to happen here is we can see is SBP plus eight points to the first argument and or SBP plus eight is
the first argument sent to my function to which is actually, again, what it's a destination.
OK, the pointer to the destination and SBP plus eight points to the second arguments and so on.
But in our case my function too only has one.
So actually this will be irrelevant to that function.
Therefore, SBP plus 12 points to an irrelevant location for my function to.
That's what I was saying because sbp sorry my function to only takes one argument.
So this is still on the stack.
It's still there, but it's irrelevant to my function to.
Okay.
Now can you guess what is currently served in SBP plus 12.
We already saw that this was the destination and this was the source.
So if I just go back a couple of slides, we can see it was actually the source of my strength, the
this one as they are the still value.
OK, so let's continue.
Now, here, this is one quiz I want you to I leave that for you to think about now again, Maine,
Maine by itself is a function.
So when when when we finish executing my function, one, we need to go back to Maine and continue execution
from there.
Again, Maine is just a function, so it will have the frame will be created for me.
A frame will be destroyed after Maine is destroyed or after Maine is finished execution and similar
to any other function.
Now, what do these memory locations contain?
And one and two and three, some basic ways.
If you understood what I was talking about over this when we talked about here, if you understood what
these are, then you will be able if you understood what I mean, the session of the explanation and
the code in here, you will be able to figure out what was an M1, what's in M2 and what's in M3.
OK, so I'm going to leave that as a quiz for you to figure out.
Now, also, these instructions, can you also tell what these instructions do?
I'm also going to leave that for you to figure out.
Just try to trace the program and understand what's happening over here.
OK, just try to trace the program and understand what's actually happening over here.
Now, at the end of of our session, what we can that's a conclusion that we can come up with is what
if we can locate the caller's IP on the stack and change it using, let's say, a move operation or
move instruction or any other instruction.
So if we can locate where there is this.
This.
Value is on the stock, which is the position to where I should be going next after my function finishes.
OK, which is where I should be going next after my function finishes the execution.
So if we can if we can figure this out and what if the new value is a location of another block?
So what if the new value which we put in there is a location of another block?
And what if that other block is actually somehow harmful court?
Again, let's say we managed to locate this where it is on the stack at the end.
What is this is an address.
So if we manage to locate where that address is, so we figured out where the address is, that's one.
Then we managed to override that address with an address to some other location.
And now that other location includes some harmful code or some malicious code that's worth when what
exploitation will happen, because we can then once the function execution finishes, it needs to go
back to the stored position, which in our case, we overloaded with some malicious code.
That's what is going to be bad for the user, good for our exploit and for our exploitation.
Now, these are other references which we use to create this presentation.
I highly recommend you go check them out, especially Xenos intro to Eighty-six, the assembly codes.
And yet that's all for this video.
We now have finished, I would say, the introduction to memory.
And now we will start talking more about hands on stuff and exploiting an application.
OK, so that's all for this video.
See you in the next video.
Thank you and bye bye.