2018-02-10 Nanyi 21CTO
Learning programming is to learn high-level language, that is, those designed for human computer language.
However, the computer does not understand the high-level language and must be translated into binary code by the compiler before it can be run. Learning a high-level language does not mean understanding the actual running steps of a computer.
What the computer really understands is the low-level language, which is designed to control hardware. Assembly language is a low-level language that directly describes/controls the operation of the CPU. If you want to know what the CPU is doing, and how the code runs, you must learn the assembly language.
Assembly language is not easy to learn, even concise introduction is difficult to find. Below I try to write a best-understood assembly language tutorial explaining how the CPU executes the code.
first, what is assembly language?
We know that the CPU is only responsible for computing, and it does not have intelligence. You enter an instruction (instruction), it runs once, then stops and waits for the next instruction.
These instructions are binary, called the opcode (opcode), such as the addition instruction is 00000011. The role of the compiler is to write high-level language programs, translated into a number of operation code.
For humans, the binary program is unreadable, and it is impossible to see what the machine is doing. In order to solve the problem of readability, as well as the occasional editing needs, the birth of assembly language.
assembly language is the textual form of the binary instruction , which corresponds to the one by one of the instruction. For example, the addition instruction 00000011 written in assembly language is ADD. As long as the binary is restored, assembly language can be directly executed by the CPU, so it is the lowest level of the lower language.
second, the Origin
At the earliest, writing the program is handwritten binary instructions, and then through a variety of switches into the computer, such as to do the addition, click on the addition switch. Later, the invention of the paper tape drilling machine, by punching in the tape, the binary instructions to automatically input the computer.
To address the readability of binary instructions, the engineers wrote the instructions into octal. Binary to octal is easy, but the readability of octal is not good either. Naturally, the final word is expressed in words, and the addition instruction is written as Add. The memory address is no longer referenced directly, but is represented by a label.
In this case, one more step, to translate these text instructions into binary, this step is called assembling, the procedure to complete this step is called assembler. It handles the text, which is naturally called aseembly code. After standardization, called Assembly language, abbreviated as ASM, the Chinese is translated into assembly language.
Each CPU machine instruction is different, so the corresponding assembly language is not the same. This article describes the most common x86 assembly language, the one used by Intel's CPU.
Three, register
To learn assembly language, you must first understand two knowledge points: registers and memory models.
Look at the registers first. The CPU itself is only responsible for operations and is not responsible for storing data. Data is generally stored in memory, and the CPU is used to read and write data. However, the CPU is much faster than the memory read-write speed, in order to avoid being slowed down, the CPU comes with a primary cache and a level two cache. Basically, the CPU cache can be seen as a memory that reads and writes faster.
However, the CPU cache is not fast enough, in addition to the data in the cache address is not fixed, the CPU each read and write will be addressed also slow speed. Therefore, in addition to the cache, the CPU has its own register (register), which is used to store the most commonly used data. That is, the most frequently read and write data (such as the loop variable), will be placed in the register, the CPU first read and write registers, and then the register and memory exchange data.
Registers do not rely on addresses to differentiate data, but rely on names. Each register has its own name, and we tell the CPU which register to take the data to, so the speed is the fastest. Some analogy registers are CPU level 0 caches.
Iv. Types of registers
The early x86 CPU had only 8 registers, and each had a different purpose. Now there are more than 100 registers, all become general-purpose registers, not specifically used, but the names of the early registers are preserved.
EAX
EBX
Ecx
EDX
Edi
Esi
Ebp
Esp
Of the above 8 registers, the first seven are universal. ESP registers have a specific purpose, saving the address of the current Stack (see the next section).
We often see 32-bit CPUs, 64-bit CPUs such as the name, in fact, refers to the size of the register. The register size of a 32-bit CPU is 4 bytes.
v. Memory model: HEAP
Registers can only hold a very small amount of data, most of the time, the CPU to command registers, directly with the memory exchange data. So, in addition to registers, you must also understand how memory stores data.
When the program runs, the operating system allocates a memory to it to store the program and the data it produces. There are start and end addresses in this section, such as from 0x1000 to 0x8000, where the starting address is the smaller address, and the ending address is the larger one.
While the program is running, for a dynamic memory footprint request (such as a new object or a malloc command), the system will draw a portion of the pre-allocated memory to the user, with the rule starting from the starting address (in fact, the starting address will have a static data, which is ignored here). For example, the user requests to get 10 bytes of memory, then from the start address 0x1000 to assign him, has been assigned to address 0x100a, if asked to get 22 bytes, then assigned to 0x1020.
This area of memory, which is divided by the user's unsolicited request, is called the heap. It starts with the starting address and grows from the low (address) to the high (address). An important feature of the Heap is that it does not automatically disappear, must be released manually, or reclaimed by a garbage collection mechanism.
VI. Memory model: Stack
In addition to the HEAP, the other memory footprint is called stack (stack). Simply put, a Stack is an area of memory that is temporarily occupied due to function runs.
Take a look at the example below.
int main () {
int a = 2;
int b = 3;
}
In the above code, when the system starts executing the main function, it creates a frame in memory for it, and all of the internal variables of main (such as A and b) are stored in this frame. After the main function is executed, the frame is reclaimed, releasing all internal variables, and no longer occupying space.
What happens if other functions are called inside of the function?
int main () {
int a = 2;
int b = 3;
Return Add_a_and_b (A, b);
}
In the above code, the Add_a_and_b function is called inside the main function. When you do this, the system creates a new frame for add_a_and_b to store its internal variables. In other words, there are two frames at this time: Main and Add_a_and_b. In general, how many layers there are in the call stack, and how many frames there are.
When the add_a_and_b is finished, its frame is recycled, and the system goes back to where the function main just interrupted execution. With this mechanism, the function's layer calls are implemented, and each layer can use its own local variables.
All the frames are stored in the stack, because the frames are stacked layers, so stack is called a stack. Generate a new frame, called "into the stack", English is push, stack recycling is called "Out of the stack", English is pop. The feature of Stack is that the first frame of the last stack (because the most inner function call, the first end of the run), this is called "LIFO" data structure. At the end of each function execution, a frame is automatically freed and all functions are executed and the entire Stack is freed.
The Stack is allocated from the high (address) to the low (address), starting at the end address of the memory area. For example, the end address of the memory area is 0x8000, the first frame is assumed to be 16 bytes, then the next assigned address starts from 0X7FF0, and the second frame assumes 64 bytes, then the address is moved to 0X7FB0.
Seven, CPU instructions
7.1 An instance
Once you know the registers and memory models, you can see what the assembly language is all about. The following is a simple program example.c.
int add_a_and_b (int a, int b) {
return a + B;
}
int main () {
Return Add_a_and_b (2, 3);
}
GCC turns this program into assembly language.
$ gcc-s example.c
After the above command executes, a text file example.s is generated, which is the assembly language, which contains dozens of lines of instructions. Let's just say that the simple operation of a high-level language, the underlying may consist of several or even dozens of CPU instructions. The CPU executes these instructions in turn to complete this step.
EXAMPLE.S after simplification, it probably looks like this.
_add_a_and_b:
Push%EBX
mov%eax, [%esp+8]
mov%ebx, [%esp+12]
Add%eax,%EBX
Pop%EBX
Ret
_main:
Push 3
Push 2
Call _add_a_and_b
Add%esp, 8
Ret
As you can see, the original program's two functions add_a_and_b and main, corresponding to two tags _add_a_and_b and _main. Inside each tag is the CPU run process that the function turns into.
Each line is a single operation performed by the CPU. It is divided into two parts, with one line as an example.
Push%EBX
In this line, push is the CPU instruction, and%EBX is the operator to use for that instruction. A CPU instruction can have 0 to multiple operators.
Here I would like to explain a line of this assembler, it is recommended that the reader is best to put this program, in another window to copy a copy, save the page to scroll when reading.
7.2 Push Command
According to the Convention, the program starts executing from the _main tag, which creates a frame on the stack for main and writes the address pointed to by the stack to the ESP register. If there is data to write to the main frame, it will be written to the address stored in the ESP register.
Then, start executing the first line of code.
Push 3
The push instruction is used to put the operator into a Stack, where 3 is written to the main frame.
Although it looks simple, the push command actually has a predecessor operation. It takes the address inside the ESP register, subtracts 4 bytes, and writes the new address to the ESP register. Using subtraction is because the Stack grows from a high to a low, 4 bytes because 3 is of type int and occupies 4 bytes. After the new address is obtained, 3 will write to the four bytes that the address starts with.
Push 2
The second line is also the same, the push instruction writes 2 to the main frame, the position is close to the previous write 3. At this point, the ESP register will subtract 4 bytes (cumulative minus 8).
7.3 Call Command
The call instruction in the third row is used to invoke the function.
Call _add_a_and_b
The above code indicates that the Add_a_and_b function is called. At this point, the program will look for the _add_a_and_b tag and create a new frame for the function.
The following is the code that executes _add_a_and_b.
Push%EBX
This line indicates that the value inside the EBX register is written to the _add_a_and_b frame. This is because in the back to use this register, first put the value inside out, and then write back.
At this point, the push instructs the address in the ESP register minus 4 bytes (cumulative minus 12).
7.4 mov Instructions
The MOV instruction is used to write a value to a register.
mov%eax, [%esp+8]
This line of code indicates that the address inside the ESP register is added with 8 bytes, a new address is obtained, and then the data is fetched at the Stack at that address. Based on the previous steps, it is possible to figure out that the 2 is taken out here, and then 2 is written to the EAX register.
The next line of code is doing the same thing.
mov%ebx, [%esp+12]
The above code adds the value of the ESP register to 12 bytes, then extracts the data from the Stack at that address, this time fetching 3 and writing it to the EBX register.
7.5 Add Command
The add instruction is used to add two operators and write the result to the first operator.
Add%eax,%EBX
The above code adds the value of the EAX register (that is, 2) with the value of the EBX register (that is, 3), obtains the result 5, and writes the result to the first operator EAX register.
7.6 Pop Instructions
The pop instruction is used to remove the last written value of the Stack (that is, the value of the lowest bit address) and write the value to the location specified by the operator.
Pop%EBX
The code above indicates that the value of the Stack's most recent write (that is, the original value of the EBX register) is removed, and the value is written back to the EBX register (because the addition is done, the EBX register is not available).
Note that the pop instruction also adds 4 to the address inside the ESP register, which means that 4 bytes are reclaimed.
7.7 ret Directive
The RET instruction terminates the execution of the current function, returning the run to the upper function. That is, the frame of the current function is recycled.
Ret
As you can see, the instruction has no operators.
As the Add_a_and_b function terminates execution, the system returns to the point where the main function was interrupted and continues execution.
Add%esp, 8
The above code indicates that the address inside the ESP register is manually added with 8 bytes and then written back to the ESP register. This is because the ESP register is the write start address of the Stack, and the previous pop operation has reclaimed 4 bytes, where it reclaims 8 bytes, which is equal to all collections.
Ret
Finally, the main function ends and the RET instruction exits the program execution.
Eight, reference links
Introduction to reverse engineering and Assembly, by Youness alaoui:http://kakaroto.homelinux.net/2017/11/ introduction-to-reverse-engineering-and-assembly/
x86 Assembly Guide, by University of Virginia computer science:http://www.cs.virginia.edu/~evans/cs216/guides/x86.html
Source: http://www.ruanyifeng.com/blog/2018/01/assembly-language-primer.html
Introduction to assembly language Tutorial