从x86-64程序集(NASM)执行系统命令的一般结构?

时间:2014-12-22 18:41:34

标签: macos assembly nasm x86-64

我正在尝试在程序集中进行一些基本的系统调用(OSX上的NASM中的x86-64),但到目前为止还没有成功。

到目前为止,我在网上看到的唯一例子是从stdin读取或写入stdout,例如:

global main
section .text

main:
  call write

write:
  mov rax, 0x2000004
  mov rdi, 1
  mov rsi, message
  mov rdx, length
  syscall

section .data
message: db 'Hello, world!', 0xa
length: equ $ - message

但是,当我尝试使用相同的模式进行另一个系统调用时,它不起作用(它说的是Bus error: 10):

global main
section .text

main:
  call mkdir

mkdir:
  mov rax, 0x2000136 ; mkdir system command number
  mov rdi, rax ; point destination to system command
  mov rsi, directory ; first argument
  mov rdx, 755 ; second argument
  syscall

section .data
directory: db 'tmp', 0xa

调用系统命令的一般结构是什么(理想情况下在NASM中的OSX上)?

基本上你应该做的就是在这里找到你想要的系统调用:http://www.opensource.apple.com/source/xnu/xnu-1504.3.12/bsd/kern/syscalls.master。所以“写”一个看起来像这样:

4   AUE_NULL    ALL { user_ssize_t write(int fd, user_addr_t cbuf, user_size_t nbyte); } 

这就是说:

  • 系统电话号码:4
  • 参数个数:3(文件描述符,字符串/缓冲区的内存地址,缓冲区长度)

所以我开始认为一般模式是这样的:

rax: system call number
rdi: maybe? point to system call ("destination index"), but why the `1` in the write example?
rsi: first argument to system call ("source index", the string in this case)
rdx: second argument to system call
rcx: third argument (if necessary, but not in the system write case)

那么就像你可以直接映射任何系统命令一样。所以mkdir

136 AUE_MKDIR ALL { int mkdir(user_addr_t path, int mode); } 

将被翻译为:

rax: 0x20000136 ; 136 + 20000000
rdi: i dunno, maybe `rax`?
rsi: directory (first argument)
rdx: 755 (mode, second argument)

但是,这不起作用。

我做错了什么?如何执行此操作的一般模式是什么,以便我可以在syscalls.master中的任何其他系统命令上进行测试?你能描述不同的寄存器在这里扮演的角色吗?这有助于澄清我的想法。

3 个答案:

答案 0 :(得分:3)

我相信OSX遵循标准的SYSV ABI调用约定,至少你的例子肯定是这样的。参数按顺序进入寄存器RDIRSIRDXR10R8R9。系统电话号码进入RAX

让我们看一下writeint fd, user_addr_t cbuf, user_size_t nbyte 大会:

mov rdi, 1       ; fd = 1 = stdout 
mov rsi, message ; cbuf
mov rdx, length  ; nbyte

现在,对于mkdiruser_addr_t path, int mode 显然,您需要将path加入rdimode加入rsi

mkdir:
  mov rax, 0x2000136 ; mkdir system command number
  mov rdi, directory ; first argument
  mov rsi, 0x1ED     ; second argument, 0x1ED = 755 octal
  syscall
  ret

请注意,您需要retmkdir子例程的结尾,并且您还需要一个,因此main不会进入mkdir。此外,您应该使用lea加载directory参数,并使用RIP - 相对寻址,例如lea rdi, [rel directory]

答案 1 :(得分:2)

你已经得到了几乎正确的结果:系统调用号需要0x88(dec 136)。 syscall.master中的系统调用是十进制的。你最终调用了getsid(这是系统调用310)。

对于参数,不要使用syscalls.master,因为这会给你一个有点倾斜的内核视角(当涉及到参数名称时)。您应该使用/usr/include/unistd.h作为原型,并使用usr / inclunde / sys / syscall.h作为数字。只有在系统调用未导出到这些文件的情况下,syscalls.master才会派上用场,而且那些主文件是NO_SYSCALL_STUB的情况。

对于ABI,它与System V AMD64 ABI相同。 http://people.freebsd.org/~obrien/amd64-elf-abi.pdf

你可以看到系统调用就像libsystem那样:

otool -tV /usr/lib/system/libsystem_kernel.dylib  | more 
# seek to /^_mkdir:
_mkdir:
0000000000012dfc        movl    $0x2000088, %eax
0000000000012e01        movq    %rcx, %r10
0000000000012e04        syscall
0000000000012e06        jae     0x12e0d
0000000000012e08        jmpq    cerror_nocancel
0000000000012e0d        ret
0000000000012e0e        nop
0000000000012e0f        nop

所有系统调用基本上都具有以下结构:

  1. 此时的论点已经放在RDI,RSI ......中,如上所述 ABI
  2. 系统调用#被加载到EAX中。 0x2意味着POSIX 系统调用。 0x1将是一个Mach Trap,0x3 - 特定于arch,0x4 - 诊断系统调用
  3. rcx保存到r10
  4. 系统调用被执行

    <<内核部分发生,其中执行通过陷阱进入内核模式,         并且eax的值用于i)到达系统调用表和ii)分支以纠正         系统调用>>

    1. 内核模式返回用户模式,超过系统调用指令
    2. EAX现在保存系统调用返回值,所以 那" jae"表示系统调用返回值是否> = 0 - 即确定 - 继续" ret"并返回给用户
    3. 如果没有,跳转到 cerror_nocancel,它加载errno的值并返回-1 用户。

答案 2 :(得分:0)

Bus error: 10错误似乎是由错误的系统调用号码引起的,并且没有退出系统调用。

; nasm -f macho64 mkdir.asm && ld -o mkdir mkdir.o && ./mkdir

%define SYSCALL_MKDIR 0x2000088
%define SYSCALL_EXIT  0x2000001

global start

section .text
start:
  call mkdir
  call exit
  ret

mkdir:
  mov rax, SYSCALL_MKDIR
  mov rdi, directory
  mov rsi, 0x1ED
  syscall

exit:
  mov rax, SYSCALL_EXIT
  mov rdi, 0
  syscall

section .data
directory: db 'tmp', 0

原始代码更改摘要:

  • main符号重命名为start
  • 将mkdir系统调用号码从0x2000136更改为0x2000088
  • 更改注册表分配
  • 将目录变量中的0xa字符更改为0(无效,但文件名不正确)

NASM

我还必须安装nasm的版本2.10.09:

brew install https://raw.githubusercontent.com/Homebrew/homebrew/c1616860c8697ffed8887cae8088ab39141f0308/Library/Formula/nasm.rb
brew switch nasm 2.10.09

这是由于:

  • nacho64
  • 中没有/usr/bin/nasm支持
  • 最新的brew版本(2.11.08)会导致此错误:fatal: No section for index 2 offset 0 found