处理器如何知道寄存器值的顺序?

问题描述

在汇编中,可以通过较少易失性寄存器或易失性寄存器传递值。例如,我可以使用 printfedi 将参数传递给 esi 我也可以使用 ebxecx 这个例子是一个非常简单的人为例子。我更好奇这对于从 libc 调用多个函数的更复杂的程序是如何工作的。

例如在Return Oriented Programming攻击中,攻击者可以使用小工具使用与前一个函数相同的寄存器从堆栈中弹出新值,然后返回到另一个{{1 }} 函数使用相同的寄存器,例如 libcwrite 可以在 read 攻击中使用 pop rsi 来使用任何一个函数,如果它们已经泄露全局偏移量表。我的总体问题可以这样问:

如果攻击者从先前对 ROP 的调用中继承寄存器,如下所示:

read

如果传递给 write 的寄存器不同,处理器如何知道要提供写入的参数:

    0x00005555555552d0 <+107>:   lea    rcx,[rbp-0xd0] <- Memory address of buffer "msg"
    0x00005555555552d7 <+114>:   mov    eax,DWORD PTR [rbp-0xe4] <- contains client fd 0x4
    0x00005555555552dd <+120>:   mov    edx,0x400 <- 1024 (size of bytes to write to memory location/buffer)
    0x00005555555552e2 <+125>:   mov    rsi,rcx
    0x00005555555552e5 <+128>:   mov    edi,eax
    0x00005555555552e7 <+130>:   call   0x5555555550d0 <read@plt>

显然 0x00005555555552b1 <+76>: call 0x555555555080 <strlen@plt> 0x00005555555552b6 <+81>: mov rdx,rax <- store return value from strlen into rdx 0x00005555555552b9 <+84>: lea rcx,[rbp-0xe0] <- message to write 0x00005555555552c0 <+91>: mov eax,DWORD PTR [rbp-0xe4] <- client file descriptor 0x00005555555552c6 <+97>: mov rsi,rcx 0x00005555555552c9 <+100>: mov edi,eax 0x00005555555552cb <+102>: call 0x555555555060 <write@plt> 不使用 read 并且 rdx 不使用 write,那么处理器如何知道选择哪个,例如如果攻击者只使用 { {3}}?

我似乎无法理解处理器如何知道要选择哪些寄存器(edxrdx)。一般而言,处理器如何选择要传递给 edx 函数或函数/例程的值?

解决方法

处理器什么都不知道;寄存器不可索引,就 CPU 而言,它们的唯一顺序是机器代码中使用的寄存器编号。 (对于诸如保存多寄存器指令之类的东西,例如传统 32 位模式 pusha / popaxsave 以保存 FPU / SIMD 状态。)

在被调用函数的某些位置寻找 args 的是...更多代码(软件),由编译器生成,该编译器编译了一个以某种方式声明其 args 的函数。请记住,printf 只是更多的软件,而不是内置于 CPU 中。

编译器知道目标平台的标准调用约定(在这种情况下在 x86-64 System V ABI 中定义),因此调用方和被调用方都同意调用约定会导致调用代码将 args 放入被调用者寻找的地方。

标准化这个调用约定是我们如何将来自不同编译器的代码链接到一个程序中,并调用库。

顺便说一句,进行系统调用也是如此;您将呼叫号码放入某个寄存器并运行切换到内核模式的指令(例如syscall)。现在内核正在运行,可以查看仍在寄存器中的值。它使用调用号来索引函数指针表,并使用标准 arg 传递寄存器中的其他 args 调用它。 (或者根据 C 调用约定他们需要去的地方,这通常与系统调用调用约定不同。)

What are the calling conventions for UNIX & Linux system calls (and user-space functions) on i386 and x86-64

,

处理器很笨,什么都不知道。从字面上看,它只执行指令所说的操作,指令最终由程序员直接或间接(通过编译)编写。编译器知道由于编译器作者决定的调用约定,他们可以自由地为该目标选择他们想要的约定,他们不必遵守特定的先前定义的约定。如果他们碰巧这样做,那是他们的自由选择。归根结底,编译器的作者知道并围绕它构建编译器......处理器只做它被告知它不能自己思考的事情。

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...