将值存储在寄存器中如何导致程序正常运行?

逆向工程 部件 x86 linux
2021-06-29 10:13:29

我目前正在学习汇编,但我似乎无法理解如何将值存储到寄存器中并操作它们会产生一个工作程序。

我想知道你们是否可以提供一个非常容易理解的解释,说明为什么在汇编代码中完成某些事情。

以汇编代码中的 Hello World 程序为例:

section     .text
global      _start                              ;must be declared for linker (ld)

_start:                                         ;tell linker entry point
  1. 为什么需要将消息长度放入EDX寄存器?为什么选择 EDX 寄存器而不是 DX 寄存器或 EAX 寄存器?

    mov     edx,len                             ;message length
    mov     ecx,msg                             ;message to write
    
  2. 我也不明白为什么我们要将 1 移入 EBX 寄存器?就此而言,将 4 移入 EAX 寄存器?

    mov     ebx,1                               ;file descriptor (stdout)
    mov     eax,4                               ;system call number (sys_write)
    int     0x80                                ;call kernel
    

    为什么我们将 1 移到 EAX 中?与其他寄存器相比,EAX 有什么重要意义?之前存储在 EAX 中的 4 发生了什么?

    mov     eax,1                               ;system call number (sys_exit)
    int     0x80                                ;call kernel
    
    section     .data
    msg     db  'Hello, world!',0xa                 ;string
    len     equ $ - msg                             ;length of string
    
  3. 个人寄存器中的数据如何相互交互?他们如何知道数据存储在其中?

TL DR:如何将 Helloworld 合并到屏幕上?

3个回答

提供完整的示例程序而不是零碎的片段会更有帮助您可以使用;-preprended 行内联您的问题

1)为什么需要将报文长度放入EDX寄存器?为什么选择 EDX 寄存器而不是 DX 寄存器或 EAX 寄存器?

    mov     edx,len                             ;message length
    mov     ecx,msg                             ;message to write

每个参数的位置(在哪个寄存器中)实际上是架构问题(似乎是 x86_64):ABI该顺序通常与作为系统调用前端的 C 运行时函数相同,此处为write这是内核模式和用户模式之间的约定。

2)我也不明白为什么我们要将 1 移入 EBX 寄存器?就此而言,将 4 移入 EAX 寄存器?

    mov     ebx,1                               ;file descriptor (stdout)
    mov     eax,4                               ;system call number (sys_write)
    int     0x80                                ;call kernel

一个字面上是名为标准流的文件号stdout

是系统调用号(本质上是表的索引),int 0x80是内核的调用门。即执行转换到内核,相应的系统调用(此处sys_write)知道从哪里获取参数。

为什么我们将 1 移到 EAX 中?与其他寄存器相比,EAX 有什么重要意义?之前存储在 EAX 中的 4 发生了什么?

通常返回值也以 EAX 或 EDX:EAX 或类似的形式结束。所以 4 会被覆盖。这取决于返回类型。

其中在这种情况下的系统调用号sys_exit,在libc中的内核端函数exit功能。

    mov     eax,1                               ;system call number (sys_exit)
    int     0x80                                ;call kernel

section     .data

msg     db  'Hello, world!',0xa                 ;string
len     equ $ - msg                             ;length of string

3) 个人寄存器中的数据如何相互交互?他们如何知道数据存储在其中?

他们没有。内核从这些寄存器中选择它们,(用户模式)开发人员只需知道将这些数据放在哪里。

然而,在正常情况下,用户模式开发人员将使用 C 运行时,如 glibc。

int 80 用于执行系统调用

每个系统调用都有一个索引

索引总是在eax寄存器中传递

函数调用可能需要参数

所述第一五个参数经由传递EBX,ECX EDX,ESI,EDI寄存器

如果参数超过五个,则使用数组指针采用特殊方法

sys_write 的原型如下

ssize_t sys_write(unsigned int fd, const char * buf, size_t count)

sys_write 的索引 = 4

所以

eax = index == 4,    
ebx = fd    == 1,stdout    
ecx = char* == msg   
edx = count == len   

sys_exit 也一样

查看下面的链接,了解简明的系统调用索引、参数、原型和源详细信息

http://docs.cs.up.ac.za/programming/asm/derick_tut/syscalls.html#note117 http://asm.sourceforge.net/syscall.html#4

Int 80 是一种对 Linux 内核执行系统调用的方法。此方法特定于 Linux。

看看 arch/x86/include/asm/unistd_32.h 它有关于这个“函数”的文档。

通常在汇编中,您将输入推送到您以与堆栈相反的顺序调用的任何函数。然而,这是一个特殊命令,其中功能输入存储在寄存器中。

也看看https://en.m.wikibooks.org/wiki/X86_Assembly/Interface_with_Linux