Secure coding

Overview of common x86 instructions

Srinivas
February 11, 2021 by
Srinivas

In the previous articles, readers were briefly introduced to x86 assembly. We discussed what x86 assembly is, a brief history of it, Instruction Set Architecture and types of x86 assembly syntax with examples, data representation in x86, various addressing modes and various x86 CPU registers. This background has set up a good foundation to understand some common x86 instructions.

So, in this article, we will have an overview of common x86 instructions and how to use them.

Learn Secure Coding

Learn Secure Coding

Build your secure coding skills in C/C++, iOS, Java, .NET, Node.js, PHP and other languages.

MOV instruction

The MOV instruction is used to copy data from the source operand to the destination operand. The following assembly program shows MOV instructions with various different operand types. 

global _start

_start:

mov eax, 8

mov eax, 0xa

mov ebx, eax

mov ecx, [esp]

In the preceding program, the first mov instruction copies the value 8 into register EAX. We can also move hex values into the registers. In the next instruction, we are moving 0xa into eax. We are telling the program that we are moving a hex value that's decimal 10 by prepending 0x. In the third instruction, we are moving the value stored in a register into another register. We have the value hex a (0xa) in the register EAX. We are moving that into the EBX register. There is another type of mov instruction, which will essentially copy the value which is pointed by the address of a register.

So, if we specify the instruction MOV ECX, [ESP] it will try to pick the address of ESP and it will move the value that's pointed by this ESP register into ECX.

Arithmetic instructions

Arithmetic instructions are used to perform simple arithmetic operations. ADD, SUB, INC and DEC are the most commonly used arithmetic instructions.

global _start

_start:

mov eax, 2

add eax, 8

add eax, eax

sub eax, 5

inc eax

inc eax

dec eax

dec eax

In the preceding program, the MOV instruction copies the value 2 into EAX register. Next, ADD EAX, 8 instruction is used to add the value 8 to this value. As we can see, ADD instruction has two arguments. The value in the source is added to the destination and then stored in the destination. This example used an immediate value to be added. It is also possible to use two registers as operands with ADD instruction. This is shown using the instruction ADD EAX, EAX.  When this instruction is executed, the value in EAX will be added with its own value and then it will be saved in EAX itself since the destination operand is EAX register.

So what this means is, the fist instruction moves the value 2 into EAX. The second instruction adds 8 to 2 and stores the value 10 in EAX. The third instruction after its execution stores the value 20 in the EAX register.

Now, let's see how subtraction works. We can use the instruction SUB to perform subtraction. When the instruction SUB EAX, 5 is executed, 5 is subtracted from the value stored in EAX register and the result will in turn be stored in the EAX register. In this case, the register EAX currently contains the value 20 and we are subtracting the value 5. So EAX register will contain the value 15 after the SUB instruction is executed in the preceding program.

Now let's see how INC and DEC instructions work. Currently, the register EAX contains the value 15. Let's assume that you want to increment the value by 1. We can simply use the increment instruction on EAX register. Once INC EAX instruction is executed, the EAX register will contain the value 16. The preceding program contains another INC EAX instruction, which will store the value 17 in EAX register when executed. We can use DEC instruction to decrement the value of the operand by 1. EAX in the preceding program currently holds the value 17. Executing DEC EAX twice will store the value 15 in EAX register.

Logical instructions

The x86 instruction set provides instructions to perform logical operations. These instructions include AND, OR, XOR, TEST and NOT.

Let us begin with the AND instruction. 

global _start

_start:

mov  AL, 7

and  AL, 01H

In the preceding program, the value 7 is first moved to the sub-register AL. The next instruction AND AL, 01H can be used to check if the value stored in AL register is an even number or odd number. If it is an even number, the AND operation will result in a zero. If it is an odd number, the AND operation will result in 1. The resultant value will be stored in the destination operand, which is AL in this case.  Similarly, the OR instruction can be used to set one or more bits. 

Next, let us see the TEST instruction. 

global _start

_start:

mov  AL, 7

test  AL, 01H

This is the same program that we used with AND instruction. TEST instruction is similar to AND instruction with one difference. The result will not be stored in the destination operand once the instruction is executed.

Now, let us know how we can use the XOR instruction.

global _start

_start:

mov eax, 20

xor eax, eax

In the preceding program, the register eax will hold the value 20 after the first instruction is executed. When the second instruction is executed, each bit in the EAX register is going to go through a bitwise exclusive OR operation with each bit in itself. So essentially this is going to produce value zero and then it's going to be stored in EAX. This is usually used to clear registers.

Conditional execution

The next important set of instructions are the ones that help the programmer to achieve conditional execution. CMP and jump family of instruction are two common instructions that facilitate condition execution. Let us consider the following example.

cmp eax, 10

jne valuenotequal

valuenotequal:

; do something

The preceding program has a CMP instruction followed by JNE instruction. Most of the times Jump instructions occur after a comparison instruction such as CMP. In this case, the CMP instruction is checking the value of the register EAX against the value 10. If the value is equal the Zero flag will be set. If the result is negative, Negative flag will be set. When the Jump instructions are executed, they usually rely on one of these flags to decide what action needs to be done. In this case, JNE instruction is used, which checks for the Zero flag. If zero flag is not set, Jump will be taken.

Similarly, other jump instructions such as JE (jump if equal), JG (jump if greater), JL (jump if less), JGE (Jump if greater than or equal), JLE (Jump if less than or equal). Clearly, all these jump instructions are relying on some condition and thus these are known as conditional jumps. The instruction JMP does not rely on any conditions and it always takes the jump. Thus JMP instruction is called an unconditional Jump instruction.

Stack instructions

PUSH and POP are the two most popular instructions when working with the stack. PUSH instruction is used to push a value onto the stack and the POP instruction is used to pop a value off the stack and store it into a register. Let us consider the following example. 

global _start

_start:

mov eax, 20

push eax

push eax

pop ecx

pop ebx

In the preceding program, we are first using the mov rax, 20 instruction to copy the value 20 into EAX register. Next, we are pushing the value stored in EAX register onto the stack by using the instruction PUSH EAX. We are then pushing the value stored in the EAX register onto the stack once again by executing the PUSH EAX instruction. So, the value 20 was pushed twice onto the stack.

Now, let us try to pop this value 20 twice and store it into two different registers. First, the instruction POP ECX can be used to pop the value 20, which is on the top of the stack into the register ECX.  What this instruction will do is, whatever the value that we have recently pushed onto the stack will be popped into the ECX register. Next,  the instruction POP EBX is used, which is going to pop the value on the top of the stack into the register EBX.

Learn Secure Coding

Learn Secure Coding

Build your secure coding skills in C/C++, iOS, Java, .NET, Node.js, PHP and other languages.

Conclusion

This article has provided a quick overview of some of the commonly seen x86 instructions. It should be noted that the instruction set comes with much more instructions that are not covered in this article. We may cover some of the instructions that are not covered here in the upcoming articles as required. 

Sources

Srinivas
Srinivas

Srinivas is an Information Security professional with 4 years of industry experience in Web, Mobile and Infrastructure Penetration Testing. He is currently a security researcher at Infosec Institute Inc. He holds Offensive Security Certified Professional(OSCP) Certification. He blogs atwww.androidpentesting.com. Email: srini0x00@gmail.com