Simple assembly program on MacOS

While going through shellcoding course on Pantester Academy I though I would like to check how the code in the course would look like on MacOS. On Linux this was pretty straightforward:

cat 007_Module_1_Hello_World_in_64_bit_Assembly/HelloWorld.nasm
global _start

section .text


        ; print on screen

        mov rax, 1
        mov rdi, 1
        mov rsi, hello_world
        mov rdx, length

        ; exit gracefully

        mov rax, 60
        mov rdi, 11

section .data

        hello_world: db 'Hello World to the SLAE-64 Course'
        length: equ $-hello_world

Set one syscall (mov rax, 1), to write to standard output (mov rdi, 1), enter hello_world string, enter its length, call, set another syscall – exit (mov rax, 60), set exit code to 11 (mov rdi, 11). Syscall number goes to eax, 1st argument to edi and so on. But on MacOS everything is thought different.

Syscall numbers on MacOS

By different I do not mean that numbering of syscalls do not match, since I would be surprised if it did. Rather that there are some nuances that made my work more troublesome. On syscall table available on Apple’s website you can see that write has code 4 (instead of 1) and exit has number 1 (instead of 60). The arguments are the same due to POSIX standard and go to same registers (because this depends on the architecture of the processor). But in order to get a syscall right you have to add class identifier to the syscall number. For BSD syscalls this is 0x200000 to the syscall.

Static linking

Next issue I had was with on code linking. Trying to link it simply with ld hello.o -o hello gave errors regarding undefined symbol _main which I did not even include. Maybe this was due to improper text section naming ? It should have been handled by nasm (check it here) but I do not have any other clue why this happened.

Final code

As always you can find solution on my gitlab, here are the contents:

global start

section .text

	mov	rax, 0x2000004
	mov	rdi, 1
	mov	rsi, hello_world
	mov	rdx, hello_world.length

	mov	rax, 0x2000001
	mov	rdi, 0

section .data
	hello_world:	db "Hello World!", 0xa
	.length:		equ $-hello_world

	var1: db 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88

And the Makefile:

	nasm -f macho64 -g -o hello.o hello.asm
	ld hello.o -static -o hello


Since this can also be tricky using lldb I’ll add a few words. Unfortunately it turned out that the lldb debugger simply wouldn’t stop at start symbol:

(lldb) br s -n start
Breakpoint 1: where = hello`start, address = 0x0000000000001fd9
(lldb) run
Process 1179 launched: '/Users/wgonczaronek/Projects/blog/simple-macos-assembly/hello' (x86_64)
Hello World!
Process 1179 exited with status = 0 (0x00000000)

As you can see after typing run program immediately exited without hitting any breakpoints. What I needed to do was to set a breakpoint one instruction after main.

(lldb) dis --name start
0x1fd9 <+0>:  mov    eax, 0x2000004
0x1fde <+5>:  mov    edi, 0x1
0x1fe3 <+10>: movabs rsi, 0x2000
0x1fed <+20>: mov    edx, 0xd
0x1ff2 <+25>: syscall
0x1ff4 <+27>: mov    eax, 0x2000001
0x1ff9 <+32>: mov    edi, 0x0
0x1ffe <+37>: syscall

(lldb) br s -a 0x1fde
Breakpoint 3: where = hello`start + 5, address = 0x0000000000001fde
(lldb) run
Process 1199 launched: '/Users/wgonczaronek/Projects/blog/simple-macos-assembly/hello' (x86_64)
Process 1199 stopped
* thread #1, stop reason = breakpoint 3.1
    frame #0: 0x0000000000001fde hello`start + 5
->  0x1fde <+5>:  mov    edi, 0x1
    0x1fe3 <+10>: movabs rsi, 0x2000
    0x1fed <+20>: mov    edx, 0xd
    0x1ff2 <+25>: syscall
Target 0: (hello) stopped.

That’s something I found odd compared to gdb, where setting a breakpoint for symbol allowed me to stop at it.

(gdb) break start
Breakpoint 1 at 0x1fd9
(gdb) run
Starting program: /Users/wgonczaronek/Projects/blog/simple-macos-assembly/hello
[New Thread 0xc03 of process 37769]
[New Thread 0xd03 of process 37769]

Thread 2 hit Breakpoint 1, 0x0000000000001fd9 in start ()
(gdb) disassemble
Dump of assembler code for function start:
=> 0x0000000000001fd9 <+0>:	mov    $0x2000004,%eax
   0x0000000000001fde <+5>:	mov    $0x1,%edi
   0x0000000000001fe3 <+10>:	movabs $0x2000,%rsi
   0x0000000000001fed <+20>:	mov    $0xd,%edx
   0x0000000000001ff2 <+25>:	syscall
   0x0000000000001ff4 <+27>:	mov    $0x2000001,%eax
   0x0000000000001ff9 <+32>:	mov    $0x0,%edi
   0x0000000000001ffe <+37>:	syscall
End of assembler dump.

BTW. I have struggled for several days before I finally manged to codesign this properly. Many thanks to the author of this guide that allowed me to finally run gdb.