我正在尝试使用sys_brk syscall在linux中分配一些内存.这是我尝试过的:
BYTES_TO_ALLOCATE equ 0x08
section .text
global _start
_start:
mov rax, 12
mov rdi, BYTES_TO_ALLOCATE
syscall
mov rax, 60
syscall
根据linux调用约定,我希望返回值在rax寄存器中(指向已分配内存的指针).我在gdb中运行它,在进行sys_brk系统调用之后,我注意到以下寄存器内容
在系统调用之前
rax 0xc 12
rbx 0x0 0
rcx 0x0 0
rdx 0x0 0
rsi 0x0 0
rdi 0x8 8
在系统调用之后
rax 0x401000 4198400
rbx 0x0 0
rcx 0x40008c 4194444 ;
rdx 0x0 0
rsi 0x0 0
rdi 0x8 8
在这种情况下,我不太了解rcx寄存器中的值.哪一个用作指向我用sys_brk分配的8个字节的开头的指针?
解决方法:
请注意,sys_brk的界面与brk / sbrk POSIX函数略有不同;请参阅C library/kernel differences section of the Linux brk(2) man page.具体来说,Linux sys_brk设置程序中断; arg和返回值都是指针.见Assembly x86 brk() call use.这个答案需要upvotes,因为它是该问题上唯一的好处.
你问题的另一个有趣的部分是:
I do not quite understand the value in the rcx register in this case
您正在看到syscall/sysret指令如何设计为允许内核恢复用户空间执行但仍然很快的机制.
系统调用不执行任何加载或存储,它只修改寄存器.它不使用特殊寄存器来保存返回地址,而只使用常规整数寄存器.
在内核返回到用户空间代码之后,RCX = RIP和R11 = RFLAGS并不是巧合.这种情况的唯一方法是,如果ptrace系统调用在内核中修改了进程保存的rcx或r11值. (ptrace是gdb使用的系统调用).在这种情况下,Linux将使用iret而不是sysret返回用户空间,因为较慢的通用情况iret可以做到这一点. (有关Linux系统调用入口点的一些演练,请参阅What happens if you use the 32-bit int 0x80 Linux ABI in 64-bit code?.但是,大多数情况下,32位进程的入口点,而不是64位进程中的系统调用.)
而不是将返回地址推送到内核堆栈(如int 0x80),系统调用:
>设置RCX = RIP,R11 = RFLAGS(因此在执行系统调用之前,内核甚至无法查看这些寄存器的原始值).
>使用配置寄存器(IA32_FMASK MSR)中的预配置掩码屏蔽RFLAGS.这让内核禁用中断(IF),直到它完成swapgs并将rsp设置为指向内核堆栈.即使将cli作为入口点的第一条指令,也会有一个漏洞的窗口.你也可以通过屏蔽DF来免费获得cld,所以即使用户空间使用了std,rep movs / stos也会向上移动.
有趣的事实:AMD首次提出的系统调用/交换设计并未掩盖RFLAGS,而是掩盖了they changed it after feedback from kernel developers on the amd64 mailing list(在〜2000年,比第一个芯片早几年).
>跳转到配置的系统调用入口点(设置CS:RIP = IA32_LSTAR).我想,旧的CS值不会保存在任何地方.
>它没有做任何其他事情,内核必须使用swapgs来访问保存内核堆栈指针的信息块,因为rsp仍然有来自用户空间的值.
因此,系统调用的设计需要一个系统调用ABI,其中注册符号,这就是为什么这些值是它们的原因.
标签:linux,assembly,x86-64,system-calls
来源: https://codeday.me/bug/20190828/1749140.html