为什么x86-64 Linux系统调用会修改RCX,这个值是什么意思?
Why do x86-64 Linux system calls modify RCX, and what does the value mean?
我正在尝试使用 sys_brk
系统调用在 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 调用约定,我希望 return 值位于 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 ; <---- What does this value mean?
rdx 0x0 0
rsi 0x0 0
rdi 0x8 8
在这种情况下,我不太了解rcx
寄存器中的值。哪一个用作指向我用 sys_brk
分配的 8 个字节开头的指针?
系统调用 return 值一如既往地在 rax
中。参见 What are the calling conventions for UNIX & Linux system calls on i386 and x86-64。
请注意,sys_brk
与 brk
/ sbrk
POSIX 函数的界面略有不同;见C library/kernel differences section of the Linux brk(2)
man page. Specifically, Linux sys_brk
sets the program break; the arg and return value are both pointers. See Assembly x86 brk() call use。该答案需要点赞,因为它是该问题上唯一好的答案。
你的问题中另一个有趣的部分是:
I do not quite understand the value in the rcx register in this case
您正在了解 syscall
/ sysret
指令如何设计以允许内核恢复用户 space 执行但仍然很快的机制。
syscall
不做任何加载或存储,它只修改寄存器。它没有使用特殊寄存器来保存 return 地址,而是使用常规整数寄存器。
RCX=RIP
和 R11=RFLAGS
在内核 return 之后给您的用户 space 代码 并非巧合. 不是的唯一方法是,如果ptrace
系统调用在进程内部修改了进程保存的rcx
或r11
值内核。 (ptrace
是 gdb 使用的系统调用)。在那种情况下,Linux 将使用 iret
而不是 sysret
到 return 到用户 space,因为较慢的一般情况 iret
可以做到那。 (有关 Linux 的系统调用入口点的一些演练,请参阅 。主要是来自 32 位进程的入口点,而不是来自 64 位进程中的 syscall
,不过。)
而不是将 return 地址压入内核堆栈(就像 int 0x80
那样),syscall
:
sets RCX=RIP, R11=RFLAGS(因此在执行 syscall
之前内核甚至不可能看到这些 regs 的原始值)。
masks RFLAGS
使用来自配置寄存器(IA32_FMASK
MSR)的预配置掩码。这让内核禁用中断 (IF) 直到它完成 swapgs
并设置 rsp
指向内核堆栈。即使 cli
作为入口点的第一条指令,也会存在 window 漏洞。您还可以通过屏蔽 DF
免费获得 cld
,因此 rep movs
/ stos
即使用户-space 使用了 std
也可以向上移动。
有趣的事实:AMD 首次提出 syscall
/ swapgs
设计并没有屏蔽 RFLAGS,而是 they changed it after feedback from kernel developers on the amd64 mailing list(大约在 2000 年,比第一个硅产品早几年)。
跳转到配置的syscall
入口点(设置CS:RIP=IA32_LSTAR
)。我认为旧的 CS
值没有保存在任何地方。
它没有做任何其他事情,内核必须使用 swapgs
来访问它保存内核堆栈指针的信息块,因为 rsp
仍然具有来自 user-space.
的值
所以 syscall
的设计需要一个破坏寄存器的系统调用 ABI,这就是为什么值是它们的原因。
我正在尝试使用 sys_brk
系统调用在 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 调用约定,我希望 return 值位于 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 ; <---- What does this value mean?
rdx 0x0 0
rsi 0x0 0
rdi 0x8 8
在这种情况下,我不太了解rcx
寄存器中的值。哪一个用作指向我用 sys_brk
分配的 8 个字节开头的指针?
系统调用 return 值一如既往地在 rax
中。参见 What are the calling conventions for UNIX & Linux system calls on i386 and x86-64。
请注意,sys_brk
与 brk
/ sbrk
POSIX 函数的界面略有不同;见C library/kernel differences section of the Linux brk(2)
man page. Specifically, Linux sys_brk
sets the program break; the arg and return value are both pointers. See Assembly x86 brk() call use。该答案需要点赞,因为它是该问题上唯一好的答案。
你的问题中另一个有趣的部分是:
I do not quite understand the value in the rcx register in this case
您正在了解 syscall
/ sysret
指令如何设计以允许内核恢复用户 space 执行但仍然很快的机制。
syscall
不做任何加载或存储,它只修改寄存器。它没有使用特殊寄存器来保存 return 地址,而是使用常规整数寄存器。
RCX=RIP
和 R11=RFLAGS
在内核 return 之后给您的用户 space 代码 并非巧合. 不是的唯一方法是,如果ptrace
系统调用在进程内部修改了进程保存的rcx
或r11
值内核。 (ptrace
是 gdb 使用的系统调用)。在那种情况下,Linux 将使用 iret
而不是 sysret
到 return 到用户 space,因为较慢的一般情况 iret
可以做到那。 (有关 Linux 的系统调用入口点的一些演练,请参阅 syscall
,不过。)
而不是将 return 地址压入内核堆栈(就像 int 0x80
那样),syscall
:
sets RCX=RIP, R11=RFLAGS(因此在执行
syscall
之前内核甚至不可能看到这些 regs 的原始值)。masks
RFLAGS
使用来自配置寄存器(IA32_FMASK
MSR)的预配置掩码。这让内核禁用中断 (IF) 直到它完成swapgs
并设置rsp
指向内核堆栈。即使cli
作为入口点的第一条指令,也会存在 window 漏洞。您还可以通过屏蔽DF
免费获得cld
,因此rep movs
/stos
即使用户-space 使用了std
也可以向上移动。有趣的事实:AMD 首次提出
syscall
/swapgs
设计并没有屏蔽 RFLAGS,而是 they changed it after feedback from kernel developers on the amd64 mailing list(大约在 2000 年,比第一个硅产品早几年)。跳转到配置的
syscall
入口点(设置CS:RIP=IA32_LSTAR
)。我认为旧的CS
值没有保存在任何地方。它没有做任何其他事情,内核必须使用
的值swapgs
来访问它保存内核堆栈指针的信息块,因为rsp
仍然具有来自 user-space.
所以 syscall
的设计需要一个破坏寄存器的系统调用 ABI,这就是为什么值是它们的原因。