Skip to main content

ASM x86 – Stack operations

· 4 min read
Strider

Hi, in the fifth part of my x86 assembler series, I would like to talk about the stack, because we will need the stack a lot later.

In the last part we learned about loops and delays. Up to now we had only used the X registers. Today we will use the stack.

A stack is a rather simple data structure, which we learned about in one post. The stack works according to the LIFO principle. That means, what is stored as the last, is also taken down as the first. For clarification, I have again packed a picture.

dia1.png

With a stack in x86 assembler, we can also drop things and take them from the stack again, just like in the data structure version. I have built a small example for this.

section .text

global _start:

_start:
push 0x41414141
push 0x42424242
push 0x43434343
push 0x44444444
pop eax
pop ebx
pop ecx
pop edx

In the example we see that we put 4 elements on our stack and take them down again. With the command push, we can put things on the stack. This command has an operand, which can be a register or a value. If it is a register, only the value from the register is put on the stack. The register remains unchanged. With the command pop, we can take things off the stack. This also has an operand, but it can only be a register.

What we have to keep in mind here is that we are working in little-endian format. In addition, the stack does not grow upwards here, as one would assume, but downwards. Because the stack grows downwards, the stack addresses also become smaller and smaller, since the stack starts at the highest address and grows towards the smallest address. It's a bit of a brainfuck but after less time, it's easier to see through it. To the code above, I have a small recording, from the debugger, packed 😄

asciicast

Here we see that what we had put down last is also the first to be fetched from the stack. In this case, it is loaded into the EAX register. The next element that is fetched from the stack is loaded into the EBX register.

I said that the stack works with the little-endian format. We see here a small snippet from the debugger, where we get the first 10 entries from the stack.

dia2.png

Here all values are stored as little-endian. So we have to store or think backwards.

But we can also perform a peek on our stack by using pointers. Instead of a pop operation we execute a mov operation. This way we can read values from the stack without removing them from the stack.

There is also a code example for this:

section .text

global _start:

_start:
push 0x41424344
push 0x45464748
push 0x494a4b4c
push 0x4d4e4f50
mov eax, DWORD[esp + 4]
pop ebx
pop ecx
pop edx

Here we also put things on the stack, but do not directly take the first element from the stack. Here instead, the 2nd element on the stack is read, but not removed. After that, everything is taken off the stack again. For this I have made a small recording 😄

264443

asciicast

We see here that the 2nd element on the stack, is loaded into the EAX register, but the value also remains on the stack. It is only fetched from the stack by a pop operation. With this method, you can read single values at any place in the stack.

There is nothing more to the stack operations in x86 assembler. I hope I could explain you how stacks work in assembler. Then until the next post 😄