Computer Architecture Part 3: Control Flow and Stack
Join over 3 million cybersecurity professionals advancing their career
Sign up with
Required fields are marked with an *
Already have an account? Sign In »
3 hours 41 minutes
Okay, so now that we've looked at our basic assembly instructions in this session, we're going to examine control flow and the stack. Now, as we use high level languages, we tend to write programs based on a number of different behaviors, conditions or states. Now, as an example, we can programmatically print out
any given number of times based on a counter
in a high level language. Our program flow. This is controlled by, if then else, and different looping statements otherwise known as branching.
Now an assembly, we use these instructions to transfer control of execution to end from different memory addresses.
Now the most common branching instruction we've seen assembly is the J. C. C. Family of instructions, otherwise known as jump instructions.
Jump instructions. They come in two flavors. Unconditional, where a jump is just simply taken and conditional, where there's some type of condition that needs to be met before the jump is taken
with an unconditional jump indicated by the JMP pneumonic in our image, the jump instruction will literally jumped the address held in the EA X operandi. Remember, this is known as a director dress. We can also use the jump instruction to branch to an indirect memory address. This is one at a specific memory location
that's either going to be set, retrieved and or calculated somewhere prior to the jump instruction.
Now, when we use the jump instruction, this has an effect on the E I P register. When a jump occurs, it sets the instruction pointer register to the next instruction address. We're gonna take a look at this when we talk about the program stack in a few minutes,
similar to the jump instruction. There are some other ones that modify the execution of our programas well, and these air called the call and read instructions shown on the right.
So when I call happens our program calls or branches to the instructions that are listed in the call instructions operandi. In this case, it's 402000
At the same time, the computer will keep track of the instruction it left off at before it took the branch. In our case, this is 401005
So now that we've branched at the address of hex 402000
the move instruction. This is going to set the EA X registered to beef YB and then execute the right instruction,
the right instruction. This is going to get the return address and set the instruction pointer to 41005 and then the program execution will continue.
Conditional jumps are ones which are taken based on some condition.
They're typically displayed with some combination of a compare instruction
or a logical test utilizing the test instruction. This isn't always the case, however, as the comparisons can happen with any bit wiser arithmetic operation. However, we do get these operations bundle with certain instructions. For instance, the compare instruction
implements a subtracting operation without storing the difference in the destination,
and the test instruction performs a bit wise ad
Now. What these instructions do implement, however, is a change in a specific bit of the E flag register. Based on the results. Now, we haven't really discussed the flags register, but for now, all you really need to know is that it's a register with 32 individual bit positions
that air used to keep track of bullying values
and because our jump instruction this doesn't store the result in an operandi. It uses a bit of the flag register as a switch as to whether or not to take the jump. And so here in our assembly code, we've got some jumps, the first line in our assembly. This compares the value in R B P minus four
The next line, which is J any says that if thesis comparison isn't true, that's what it stands for. Jump not equal j any, then jump to line 402000 and the execution continues by moving one into the memory address our VP minus. For now, we should really take a second to think about what this means
during our explanation of the code. We use the word if and we also use the word then
and one last note. We don't know what the value of r B P minus forests, but we do know that if it's not equal, we jump in, the execution continues. So what we can probably say is that this is comparing some type of variable in an if else statement. So, using the assumptions, we can craft a bit of pseudo code that has RFL statement
something like if X is equal to zero,
then move the value of five into X, making X equal to five.
If it's not, then we jump to address 402000 and set X equal to one.
Now, before we move on on the right here, we've got some comment. Jump instructions. I just want to briefly talk about them and tell you what the flags a register is set. So the first is jump of zero or jump. If equal. This is that if the result of the comparison is equal to zero, the execution will continue
and the ZF flag register is set toe one,
now similar to above the J and Z instruction is the opposite. If the result of the comparison is not zero or not equal, the execution will continue and the ZF flag is set to zero.
The last two instructions are less and greater than comparisons.
If the result of the comparison of the jail instruction is less, the execution continues and the SF flag is set to one.
If the result of the comparison is greater than in RG instruction than execution continues and the Z F and SF flags are set to zero
another control structure that allows us to jump back and forth between code is loops. So loops execute code until some condition is met. There are several types of loops, but the most common ones are the four in wild loops.
So, up to this point, we've seen how jumps move us forward in our code. However, loops allow us to jump backwards
in our image. We have a wild loop
now with Wild Loop has a general form. This consists of four parts Thean Initialization. This is setting our editor to zero
the wild condition. This is going to evaluate the condition while I is less than five. We've got the code now in this code, we really don't do anything but looping through the counter. And lastly, we have an update statement that is incremental. The variable we've set in our loop by one.
Now, from an assembly perspective, we can see that at first we set our initialization variable by moving zero into our VP minus four.
Then we use a jump to branch to our loop test label and do the comparison
Now. This is a bit like in many if like if the variable in R B P minus four is less than four, then jumped to the increment label.
Now the increment label. This adds one toe are variable. This is our looping counter.
Once this happens, we can now continue to the next line in our execution and move to the compare instruction.
This flow of the process execution continues until Iess five in the program exits.
Okay, so let's switch gears for a second and talk about some memory structures. So as we know when a program is stored on disk, it's divided into several sections, either of which contained coder data. The code sections contain instructions to be executed by the processor, whereas the data sections contain variables. Resource is thean port table and so on,
and when we execute a program, the process is loaded into an allocated memory space by the OS. In our simplified image here, you can see that the structure of the XY is loaded into memory, similar to how it's stored on the disk.
Also, as a process is loaded into memory, part of the process context contains a stack and a heap which are also loaded into memory. Thes air space is used to dynamically allocate memory, two variables and the like, and we won't be concerned too much with the heap at this time. But let's talk about the program stack.
A critical component of our programs are functions. Functions contain a block or blocks of code that performs a specific set of tasks. Typically, a program contains many different functions, and when a function is called, the CPU transfers control to a specific memory address which contains the code which is to be executed.
Control is transferred back to the caller
once execution completes.
The called function has different components, such as parameters code within the body of the function in variables, all of which are used within the context of the function. To facilitate the movement of data in and out of the memory allocated for our functions, we use the stack so the stack is a memory space where data is temporarily stored.
This could be anywhere in our main memory.
To use the stack, we add and remove data to and from it using the last in first out method. However, we can read any of the data that's contained in the stack at any memory address at any time to illustrate stack use. Just think of the stack as a group of cells called stack frame, and every time a variable in a function requires memory allocation,
you add a new four bites cell containing the value to the top of the stack.
This is called pushing a value to the stack, and it's implemented using the push instruction.
For example, let's say we want to allocate memory for the value three to the top of the stack and assembly. The push instruction is used to place the value on the top of the stack. As a result of this push, the stack will grow in size. When we push values, the stack grows down towards lower memory addresses.
Now we can put additional values on the top of the stack. For example, we can push four and then four will be added to the top of the stack.
Now, to get data off the top of the stack, we need to pull it using the pop instruction. As a result from this operation, the stack is going to decrease in size. So to keep track of where the stack begins, I e. The top of the stack. We used the stack pointer register
stack pointer register will increase or decrease in size as we push and pop data off the stack.
All right, so I hope that you enjoyed our assembly review. We didn't get to all the topics. But don't worry, because we're going to be filtering them in as we do our static analysis. So let's go ahead and wrap up this module with a brief summary.