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.
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.
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
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
(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.