mac os X 下用nasm大杂烩

时间:2021-08-27 08:59:48
section .data
msg db 'This is a test', 10, 0 ; something stupid here
ft db 'addr is %x',10,0

section .text
global _main
extern _printf
extern _exit

_main:
push rbp
mov rbp, rsp

;xor al, al
mov rdi, ft
;lea rdi,[rel ft]
mov rsi,rdi
call _printf

;xor rax,rax
;mov [rax],rax

xor rdi,rdi
call _exit
;mov rsp, rbp
;pop rbp
ret

mac OS X 10.8.3:

nasm -f macho x.asm

gcc -o x x.o


会出现警告:

ld: warning: PIE disabled. Absolute addressing (perhaps -mdynamic-no-pic) not allowed in code signed PIE, but used in _main from a.o. To fix this warning, don't compile with -mdynamic-no-pic or link with -Wl,-no_pie

由于安全问题,解释如下:

所谓的PIE是指position independent executable,即地址无关exe,换句话说也就是生成的机器码中不能出现lea rax, some_symbol_name这样的绝对寻址,而只能以:lea rax, [rel some_symbol_name]这种形式出现,目的是为了提高安全性,这样OS可以以随机的地址加载exe。

采用相对地址定位即可:使用第16行指令代替。

x64 API接口有用的提示:

Mac OS X complies to the System V ABI - AMD64 Architecture Processor Supplement. It mandates that the fist 6 integer/pointer arguments are passed in RDIRSIRDXRCXR8 and R9, exactly in that order. The first 8 floating-point or vector arguments go into XMM0XMM1, ..., XMM7. Only after all the available registers are depleted or there are arguments that cannot fit in any of those registers (e.g. a 80-bit long
double
 value) the stack is used. 64-bit pushes are performed using MOV (the QWORDvariant) and not PUSH. Simple return values are passed back in the RAX register. The caller must also provide stack space for the callee to save some of the registers.

printf is a special function because it takes variable number of arguments. When calling such functions RAX should be set to the number of floating-point arguments, passed in the vector registers. Also note that RIP-relative addressing is preferred for data that lies within 2 GiB of the code.


关于mac OS X系统调用号有用提示:

Mac OS X 64 bit Assembly System Calls

Newegg.com |  Amazon.com |  ThinkGeek |  eBay |  HP |  Dell |  Barnes & Noble |  BestBuy  |  Adobe |  Logitech

After reading about shellcode in Chapter 5 of Hacking: The Art of Exploitation, I wanted to go back through some of the examples and try them out. The first example was a simple Hello World program in Intel assembly. I followed along in the book and had no problems reproducing results on a 32 bit Linux VM using nasm with elf file format and ld for linking.

Then I decided I wanted to try something similar but with a little bit of a challenge: write a Mac OS X 64 bit “hello world” program using the new fast ‘syscall’ instruction instead of the software interrupt based (int 0×80) system call, this is where things got interesting.

First and foremost, the version of Nasm that comes with Mac OS X is a really old version. If you want to assemble macho64 code, you’ll need to download the lastest version.

nobody@nobody:~$ nasm -v
NASM version 2.09.03 compiled on Oct 27 2010

I figured I could replace the extended registers with the 64 bit registers and the int 0×80 call with a syscall instruction so my first attempt was something like this

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
section
.data
hello_world    
db      "Hello World!", 0x0a
 
section
.text
global
_start
 
_start:
mov
rax, 4              ; System call write = 4
mov
rbx, 1              ; Write to standard out = 1
mov
rcx, hello_world    ; The address of hello_world string
mov
rdx, 14             ; The size to write
syscall                
; Invoke the kernel
mov
rax, 1              ; System call number for exit = 1
mov
rbx, 0              ; Exit success = 0
syscall                
; Invoke the kernel

After assembling and linking, I got this

nobody@nobody:~$ nasm -f macho64 helloworld.s
nobody@nobody:~$ ld helloworld.o
ld: could not find entry point "start" (perhaps missing crt1.o) for inferred architecture x86_64

Apparently Mac OS X doesn’t use ‘_start’ for linking, instead it just uses ‘start’. After removing the underscore prefix from start, I was able to link but after running, I got this

nobody@nobody:~$ ./a.out
Bus error

I was pretty stumped at this point so I headed off to Google to figure out how I was supposed to use the ‘syscall’ instruction. After a bunch of confusion, I stumbled upon the documentation and realized that x86_64 uses entirely different registers for passing arguments. From the documentation:

The number of the syscall has to be passed in register rax.

rdi - used to pass 1st argument to functions
rsi - used to pass 2nd argument to functions
rdx - used to pass 3rd argument to functions
rcx - used to pass 4th argument to functions
r8 - used to pass 5th argument to functions
r9 - used to pass 6th argument to functions

A system-call is done via the syscall instruction. The kernel destroys registers rcx and r11.

So I tweaked the code with this new information

...
mov rax, 4 ; System call write = 4
mov rdi, 1 ; Write to standard out = 1
mov rsi, hello_world ; The address of hello_world string
mov rdx, 14 ; The size to write
syscall ; Invoke the kernel
mov rax, 1 ; System call number for exit = 1
mov rdi, 0 ; Exit success = 0
syscall ; Invoke the kernel
...

And with high hopes that I’d see “Hello World!” on the console, I still got the exact same ‘Bus error’ after assembling and linking. Back to Google to see if others had tried a write syscall on Mac OS X. I found a few posts of people having success with the syscall number 0×2000004 so I thought I’d give it a try. Similarly, the exit syscall number was 0×2000001. I tweaked the code and BINGO! I was now able to see “Hello World” output on my console but I was seriously confused at this point; what was this magic number 0×200000 that is being added to the standard syscall numbers?

I looked in syscall.h to see if this was some sort of padding (for security?) I greped all of /usr/include for 0×2000000 with no hints what-so-ever. I looked into the Mach-o file format to see if it was related to that with no luck.

After about an hour and a half of looking, I spotted what I was looking for in ‘syscall_sw.h’

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/*
  * Syscall classes for 64-bit system call entry.
  * For 64-bit users, the 32-bit syscall number is partitioned
  * with the high-order bits representing the class and low-order
  * bits being the syscall number within that class.
  * The high-order 32-bits of the 64-bit syscall number are unused.
  * All system classes enter the kernel via the syscall instruction.
  *
  * These are not #ifdef'd for x86-64 because they might be used for
  * 32-bit someday and so the 64-bit comm page in a 32-bit kernel
  * can use them.
  */
#define
SYSCALL_CLASS_SHIFT 24
#define
SYSCALL_CLASS_MASK  (0xFF << SYSCALL_CLASS_SHIFT)
#define
SYSCALL_NUMBER_MASK (~SYSCALL_CLASS_MASK)
 
#define
SYSCALL_CLASS_NONE  0   /* Invalid */
#define
SYSCALL_CLASS_MACH  1   /* Mach */ 
#define
SYSCALL_CLASS_UNIX  2   /* Unix/BSD */
#define
SYSCALL_CLASS_MDEP  3   /* Machine-dependent */
#define
SYSCALL_CLASS_DIAG  4   /* Diagnostics */

Mac OS X or likely BSD has split up the system call numbers into several different “classes.” The upper order bits of the syscall number represent the class of the system call, in the case of write and exit, it’s SYSCALL_CLASS_UNIX and hence the upper order bits are 2! Thus, every Unix system call will be (0×2000000 + unix syscall #).

Armed with this information, here’s the final x86_64 Mach-o “Hello World”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
section
.data
hello_world    
db      "Hello World!", 0x0a
 
section
.text
global
start
 
start:
mov
rax, 0x2000004      ; System call write = 4
mov
rdi, 1              ; Write to standard out = 1
mov
rsi, hello_world    ; The address of hello_world string
mov
rdx, 14             ; The size to write
syscall                
; Invoke the kernel
mov
rax, 0x2000001      ; System call number for exit = 1
mov
rdi, 0              ; Exit success = 0
syscall                
; Invoke the kernel
nobody@nobody:~$ nasm -f macho64 helloworld.s
nobody@nobody:~$ ld helloworld.o
nobody@nobody:~$ ./a.out
Hello World!

总结如下:

因为mac OS X 10.8.3是64位系统,如果想要写汇编代码可有2种方式:

1 32位方式 macho32 ,但是要有32位的C库,你可以按照老的API ABI接口写程序,即

push xxx,push xxx,call xxx。

2 64位方式 macho64 ,使用新的64位ABI调用C库,或者你直接用syscall调用。

最后你可以用mac OS X自带的as汇编器来搞:

gcc -S -o x.s x.c

as -o x.o x.s

gcc -o x x.o