Assembly + C - Part #5

During my journey of learning Assembly, i was taking some notes .. and now, I'm sharing it openly with you <3 ...

New Instructions: call, ret, mov, add, sub

Let's look at the following simple C code:

int func(){
	return 0xbeef;
}

int main(){
	func();
	return 0xf00d;
}

The generated assembly code for the previous c code is a as the following:

CALL - Call Procedure

  • CALL's job is to transfer control to a different function; in a way that control can later be resumed where it left off.

  • First it pushes the address of the next instruction onto the stack.

    • For use by RET for when the procedure is done

  • Then it changes RIP to the address given in the instruction.

  • Destination address for the target function can be specified in multiple ways:

    • Absolute address

    • Relative address (relative to the end of the end instruction, or some other register)

RET - Retrun from Procedure

  • Two forms:

    • Pop the top of the stack into RIP (remember, pop implicitly increments stack point, RSP)

      • In this form, the instruction is just written as "ret".

    • Pop the top of the stack into RIP also add a constant number of bytes to RSP

      • In this form, the instruction is written as "ret 0x8" or "ret 0x20" etc.

How to read two-operand instructions: Intel vs. AT&T Syntax

Intel : Destination <-- Source(s)

  • Windows: Think algebra or C: y = 2x +1;

mov rbp, rsp
add rsp, 0x14; (rsp = rsp + 0x14)

ATT&T: Destination --> Source(s)

  • *nix/GNU: Think elementary school: 1 + 2 = 3

mov %rsp, %rbp
add $0x14, %rsp
  • so registers get a % prefix and immediate get a $

During my notes i'll use Intel syntax It's important to know both, so you can read documents in either format.

MOV - Move

  • Can move:

    • register to register

    • memory to register, register to memory

    • immediate to register, immediate to memory

  • But ! ... Never memory to memory

  • Memory addresses are given in "r/mX" form.

"r/mX" Addressing Example

ADD and SUB

  • Adds or Substracts, just as expected

  • Destination operand can be r/mX or register

  • Source operand can be r/mX or register or immedaite

  • No source and destination as r/mXs, because that could allow for memory to memory transfer, which isn't allowed on x86.

add rsp, 8 --> (rsp = rsp + 8)
sub rax, [rax*2] --> (rax = rax - memorypointedtoby(rbx*2))

Stack frame single-step slideware wlakthrough

RIP = 00000001`40001010, but no instruction yet executed
  1. You can't tell, but it "zero extended" the rax reg (meaning it filled in the upper 32 bits of raw with zeros)

    From section 3.4.1.1 in the Nov 2020 Intel Manual:

NOTE: This only applies to writing to registers, not memory ! If you write a 32 bit value to what you're imagining as a "64-bit" memory location (such as for a 64 bit local variable), still only 32 bits will be changed !

STACK FRAME TIME OUT

Let's back to the execution

5.

6.

7.

8.

Notes

  • func() is dead code - its return value is not used for anything, and main() always returns 0xF00D. If optimizations were turned on in the compiler, it would remove func().

  • We don't yet understand why main() does sub rsp, 28h and add rsp,28h

Mystery Lister

With .. another mystery lister :D (will come to it later)

Old: Why do the GCC/Clang HelloWorlds have balanced Push/Pop instruction but Visual Studio doesn't ?

  • Wha'ts up with the sub/add 0x28 in main()

Simple Stack Diagram 2

Let's take the following C code:

#include <stdio.h>

int bar(int y) {
    int a = 3 * y;
    printf("bar returned %d", a);
    return a;
}

int foo(int x) {
    int b = 5 * x;
    printf("foo passed %d", b);
    return bar(b);
}

int main() {
    int c = foo(7);
    printf("main passed %d", c);
}

The stack will be as the following:

Instruction we now know

NOP (6%) PUSH/POP (17%) CALL/RET (9%) MOV (23%) ADD/SUB (5%)

Now, we know probably 60% from all of assembly instructions !! <3

Last updated