通过一些操作,我已将其缩小到ret
操作的问题。我知道call
将返回地址推送到堆栈;弹出并将其推回是违法的吗?
format ELF64 executable 3
entry start
segment readable executable
start:
pop rcx ; argc
mov [argc],cl ; int -> ASCII
add [argc],'0'
push 1 argc 1
call sys_write
mov rdi,0
mov rax,60
syscall
sys_write: ; (fd,*buf,count)
pop r11
pop rdx rsi rdi
mov rax,1
syscall
push r11
ret
segment readable writable
argc rb 1
输出是:
$ ./prog
1Segmentation fault
$ _
答案 0 :(得分:1)
syscall
指令使用R11
注册表隐含了RFLAGS
的内容,这意味着在syscall
R11
之后存储的值是syscall
覆盖。
解决方案可能只是选择一个未被系统调用修改的适当寄存器。根据英特尔的说明参考手册(http://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-software-developer-manual-325462.pdf,第4-668页第2B卷)RCX
覆盖RIP
,R11
,RFLAGS
和{{1}作为其操作的一部分,但操作系统当然能够恢复RIP和RFLAGS。
这留下了很多其他选择。一个很好的选择是一个寄存器,允许函数调用在标准的用户空间调用约定中被破坏,但Linux系统调用将保持不变。 R8
符合要求。由于您未使用标准x86-64 System V function-calling convention,RBX
或RBP
将是更好的选择(较小的机器代码大小,因为它们不需要REX前缀)。