Assembly Basics
Fetch, decode, and execute. What a computer does best.
I think there are two beautiful things about cybersecurity above everything else in it. Two would be that you're always learning something. And the other one would be seeing the bare basics come to life, and that's what assembly is. Assembly is the bare basics of your computer. Well, how does it work?
The CPU
It's a machine that does one thing. It executes instructions one at a time, billions of instructions per second. Everything on your computer is nothing but a very organized list of instructions being fed into your CPU.
But what does an instruction look like? Well, an instruction is just a number that just sits in your computer's memory, as raw bytes. Assembly language is just a human readable version of those numbers. So you don't have to memorize all these abstract or large numbers.
Raw bytes: 48 89 d8
Assembly: mov rax, rbx
RAM
Every byte in RAM has an address, the same way a house number identifies a house on a street. So do these bytes in RAM. RAM really doesn't have a structure either. A byte doesn't know if it's a part of an instruction, a pixel, an image, a number, a letter, or anything else that you can imagine. It's just eight bits. The meaning is entirely created by the program reading it. The byte 0x41 could literally be the letter A or the number sixty-five.
Byte in RAM: 0x41
Interpreted as an integer: 65
Interpreted as ASCII: 'A'
Interpreted as machine code: part of an instruction
Registers
Now registers are very tiny storage slots inside of the CPU. On x86-64, the general-purpose registers are: rax, rbx, rcx, rdx, rsi, rdi, rbp, rsp, plus r8-r15.
They exist because RAM is very physically far away from the CPU and slow to access. So registers sit right inside of the CPU core and can be read and written in a single cycle. Any actual computation, like comparing any values or subtracting numbers or multiplying numbers, these have to happen in the register, and data gets pulled from RAM into the register, worked on, and sometimes sent back.
mov
Now we have to put all of this information together, with the command mov. It doesn't actually move anything. It's misleading. What mov actually does is it copies the value in one register or location in RAM and moves it into another.
Here's how to use it!
mov rax, rbx ; copy the value in rbx into rax
mov rax, [0x1234] ; copy the value FROM RAM at address 0x1234 INTO rax
mov [0x1234], rax ; copy the value in rax INTO RAM at address 0x1234
There's a few other commands, and they're all super easy to remember. Mainly because they're named properly. I'm looking at you, mov.
add rax, rbx ; rax = rax + rbx
sub rax, 1 ; rax = rax - 1
cmp rax, rbx ; compare rax and rbx, set flags based on result
jmp 0x4011a0 ; jump execution to that address
Why might someone in a reverse engineering aspect care?
Well, because of disassemblers. A disassembler is a program that reverses data. It takes raw instructions in bytes, then turns them into readable names that we can understand. And you can try this for yourself by using this following command on any compiled binary on your machine.
objdump -d /bin/ls | less
You might say, that's really interesting and all. And great that we know that it runs billions of times and has all these instructions and these ways to move around and play with data and etcetera, etcetera. But why do we care? I'll never use this in my life. So why should I give this any thought?
A program in Python or C or any other language may be three hundred lines of code, could even compile down to probably millions of these tiny instructions. The CPU doesn't know what a variable is or a function or a loop, it only knows its registers. Its RAM. And the endless cycle of fetching, decoding, and executing. Knowing the roots of what you're learning is more important than anything else, in my opinion.