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 _start: ; print on screen mov rax, 1 mov rdi, 1 mov rsi, hello_world mov rdx, length syscall ; exit gracefully mov rax, 60 mov rdi, 11 syscall 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 start: mov rax, 0x2000004 mov rdi, 1 mov rsi, hello_world mov rdx, hello_world.length syscall mov rax, 0x2000001 mov rdi, 0 syscall 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:
all: nasm -f macho64 -g -o hello.o hello.asm ld hello.o -static -o hello
Debugging
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 hello`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 hello`start: -> 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. (gdb)
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.
