Writing programs in x86-64 ASM and pwncollege embryoasm writeup
Before reading this, it’s an advice to please read previous article of ASM series.
So now that we know some basics of registers and syscalls. We will move onto the part where we use our newfound knowledge to write some assembly code.
The following ASM code is specific to GAS (GNU Assembler)
Step by Step Hello world ASM program breakdown
.section .text
.intel_syntax noprefix
.global _start
_start:
mov rax, 1
lea rsi, [rip+str]
mov rdx, 13
syscall
mov rax, 60
mov rdi, 0
syscall
str:
.string "Hello world\n"
Now in my first article, I explained different sections/mapping of memory. Most important sections among those are .text
section and .data
, because it is .text
where we write code and .data
where we define variables and constants.
In the above hello world program, we are only using the .text
section and keeping the code as minimal and simple as possible.
So in the first line we are defining the section. In order to define a section we do .section <section name>
In our case, as we are only using .text
:
.section .text
Now because we are using GAS, the default ASM sytax is AT&T but we are writing in intel syntax so we need to specify that to the assembler. We do this using :
.intel_syntax noprefix
.global
is GAS directive which helps in defining symbols in object file. Every ASM code must have a _start
symbol. It defines the entry point of ASM code.
In GAS we do all this using :
.global _start # first we define a symbol
_start : # then we mark the entry point
Oh ! by the way, we use #
in GAS for comments.
MOV instruction
The mov
instruction is the very basic and is used extensively in a assembly program. It is used to copy/move data from one register to another. In intel, it works in the following way :
mov <destination register> , <source register>
For example we need to copy number 6263 in register rax
, We’ll do :
mov rax, 6263 # rax = 6263
LEA instruction
The lea
instruction is used to copy some address in some register.
lea <destination register> , <source effective address>
This is also used extensively because we are loading effective address, So we can also do math in one instruction itself and assembler will understand it and assemble it for us. In mov
instruction it’s not possible.
SYSCALL instruction
syscall instruction, as name says, is used for making system calls.
Main Hello world code
Now to write something on screen, we need to write on stdout (it’s file descriptor is 1). So in total we need to do 2 syscalls (write and exit syscalls).
To do write syscall we need to give it some arguments. We give arguments based on calling convention (Discussed in previous article) .
We can either use a good website or if you are a console fan then method used in previous article to get syscall number and to see arguments use 2nd page of man
command.
echo SYS_write | gcc -include sys/syscall.h -E -
So, putting everything together and writing a subroutine for write
syscall
1st argument : fd (File descriptor which in our case is 1 for stdout)
2nd argument : buf (Buffer, Address of info which has to be written on screen ?)
3rd argument : count (Buffer size)
All these arguments have to be set according to calling convention.
mov rax, 1 # syscall code is set through rax register
mov rdi, 1 # set fd to stdout (1)
lea rsi, [rip+str] # use rip (instruction pointer) to access label str
mov rdx, 13 # Hello world\n\0 size = 13
syscall # perform syscall
Label str
is basically a space in memory where “hello world” is stored in form of bytes. lea
instruction will calculate the address of the start of buffer using rip
register (instruction pointer).
I recommend you to write the exit
subroutine without looking at my code.
Exit subroutine :
mov rax, 60 # syscall code for exit is 60
mov rdi, 0 # return address of exit
syscall # perform syscall
So to putting all the pieces together and we get our assembly code :
.section .text
.intel_syntax noprefix
.global _start
_start:
mov rax, 1
lea rsi, [rip+str]
mov rdx, 13
syscall
mov rax, 60
mov rdi, 0
syscall
str:
.string "Hello world\n"
I hope after reading this, you would have understood assembly and basic concepts related to memory. Now like any other language, assembly is just about practice, pratice and practice. Once you master it, I guarantee, assembly and C will become your favorite language.
Pwn.College Embryoasm Writeup
I have already started the instance, so let’ connnect ssh -i ~/.ssh/key.pub hacker@dojo.pwn.college
.
So this is easy. As explained above. We can just do mov rdi, 0x1337
full code :
.section .text
.intel_syntax noprefix
.global _start
_start :
mov rdi, 0x1337
First we assemble it and compile it into an ELF then we will convert copy bytes of that ELF in a different file.
to do so :
gcc -nostdlib -static exp.s -o exp
objcopy --dump-section .text=exp.bin exp
Then we will pipe the bytes into the challenge.
We can also do this using python script through pwntools.
Python script :
#!/usr/bin/env python3
import pwn
pwn.context.log_level = "INFO"
pwn.context.encoding = "latin"
pwn.context.arch = "amd64"
pwn.warnings.simplefilter("ignore")
assembly = """mov rdi, 0x1337"""
proc = pwn.process("/challenge/embryoasm_level1")
print(proc.readrepeat(1).decode())
proc.send(pwn.asm(assembly))
print(proc.readrepeat(1).decode())
In future I’ll write some interesting articles on some more instructions in ASM, file operations through assembly, shellcoding and operating system design.
A wise man👨💻 (check out his blog) once said to me, “its almost like playing lego … you have to put the pieces together …”
On that note,
Signing out