【问题标题】:Assembly x86 brk() call use汇编 x86 brk() 调用使用
【发布时间】:2014-04-30 11:02:01
【问题描述】:

我正在尝试将内存动态分配到堆中,然后在这些内存地址中分配值。我了解如何分配内存,但是我如何将例如寄存器中的值分配给第一个动态内存地址? 这是我目前所拥有的:

    push rbp
    mov rbp, rsp            ;initialize an empy stack to create activation records for the rest of the subroutines                                                                                                                        

    mov rax, 0x2d           ;linux system call for brk()                                                                                                                                                                                  
    mov rbx, 0x0            ;to get the adress of the first adress we are allocating we must have 0 in rbx                                                                                                                                
    int 0x80                ;calls the linux operating system kernel for assistance                                                                                                                                                       
    mov [brk_firstLocation], rax ;the first position in the heap will be returned in rax thus i save the first loaction in a varable called brk_firstLocation                                                                             

    mov rbx, rax            ;the memory adress of the start of the heap is moved in rbx                                                                                                                                                   
    add rbx, 0x14           ;we want 5 bytes worth of data alocated in the heap, so the start adress plus 20 bits                                                                                                                         
    mov rax, 0x2d           ;linux system call for brk()                                                                                                                                                                                  
    int 0x80                ;calls the linux operating system kernel for assistance

我会怎么做,例如,将mov 中的值rax 转换为brk_firstLocation

【问题讨论】:

    标签: linux assembly x86-64 dynamic-memory-allocation brk


    【解决方案1】:

    其他人指出了您的代码有一些问题。我想补充一点,您不会将 20 bits 添加到当前断点(或者像 add rbx, 20 实际上那样添加 20 bytes),您只需添加 5 个字节。

    另外,你的第一个系统调用参数不会在 rbx 中,它会在 rdi 中。 The 64-bit syscall ABI 与 32 位 ABI(在 64 位进程中仍然可用)相比,使用不同的系统调用号、不同的寄存器和不同的指令(syscall 而不是 int 0x80)。另请参阅 标签 wiki 以获取更多 ABI 链接。

    您的代码应如下所示:

    push rbp
    mov rbp, rsp
    
    ;; sys_brk(0)
    mov   rax, 12         ; 12 is SYS_brk (/usr/include/asm/unistd_64.h)
    mov   rdi, 0          ; rdi for first syscall arg in the 64-bit ABI, not rbx
    syscall               ; syscall, not int 0x80, for the 64-bit ABI
    
    mov   qword [brk_firstLocation], rax
    
    ;; sys_brk(old_break + 5)
    lea   rdi, [rax + 5]  ; add 5 bytes to the break point
    mov   rax, 12
    syscall               ; set the new breakpoint
    

    此时,您可以使用 brk_firstLocation 作为指向要存储在堆上的任何 5 字节结构的指针。以下是您将值放入该内存空间的方式:

    mov   rdi, [brk_firstLocation]   ; load the pointer from memory, if you didn't already have it in a register
    
    mov   byte [rdi], 'A'            ; a char in the first byte
    mov   [rdi+1], ecx               ; a 32-bit value in the last 4 bytes.
    

    【讨论】:

    • 最后一个使用64位系统调用ABI的答案,并正确使用。 (stackoverflow.com/questions/2535989/…)。
    • 我修复了几个简单但严重的错误;我认为这现在是完全正确的(也是这个问题的唯一正确答案)。
    【解决方案2】:

    int 80h 仅适用于 32 位系统调用。使用syscall 代替 64 位。

    调用 sys_brk 两次是多余的 - 在汇编中,您始终知道程序数据的结束位置。只需将标签放在那里,您就会得到地址。

    以这种方式分配少于一页的内存是没有意义的,无论如何都会以 4KB 的块分配。

    理解这一点很重要——sys_brk 不是堆管理函数。它是低级内存管理。

    【讨论】:

    • brk内存区域不是紧跟在程序结束之后,而是随机的。
    【解决方案3】:

    我看到了很多问题:

    • 0x2d 是 x86(32 位)上的 brk 系统调用;在 x86_64 上是 0xc
    • brk 设置数据段的结尾;它在成功时返回 0,在失败时返回 -1。它不返回“堆中的第一个位置”。这来自链接器设置为未初始化的预分配数据末尾的符号 _end。

    所以你想要这样的东西:

        mov [brk_firstloaction], _end
        mov rbx, [brk_firstlocation]
        add rbx, 0x14         ; space for 5 dwords (20 bytes)
        mov rax, 12
        int 0x80
    

    【讨论】:

    【解决方案4】:

    正如您所做的那样,调用一次以检索当前的堆底部,然后移动堆顶部(即 brk 值)。但是,您的代码在使用 64 位寄存器集 r.x 时不正确。如果您的 Linux 是 32 位的(正如系统调用号使用 45 所暗示的那样),那么您需要设置 32 位寄存器:

    mov eax, 45                            ; brk                                                                                                                                                                                  
    mov ebx, 0                             ; arg 1: 0 = fail returning brk value in rax                                                                                                                               
    int 0x80                               ; syscall
    mov dword ptr [brk_firstLocation], rax ; save result
    mov eax, 45                            ; brk                                                                                                                                                                                  
    mov ebx, 4                             ; arg 1: allocate 4 bytes for a 32-bit int                                                                                                                                
    int 0x80                               ; syscall
    
    mov eax, dword ptr [brk_firstLocation] ; reload rax with allocated memory address.
    mov dword ptr [eax], 42 ; use result: store 42 in newly allocated storage
    

    当然,您可以根据需要重新加载保存的值以多次重复使用。

    【讨论】:

    • 难道我们还没有在 32 位 int 0x80 和 64 位 syscall 内核调用之间混淆吗?
    • OP 正在编写 64 位代码,因此使用 64 位系统调用 ABI 对于涉及指针的系统调用至关重要。它与 i386 ABI 完全不同(它仍然可用于 64 位进程)。 stackoverflow.com/a/8510471/224132
    猜你喜欢
    • 2015-04-30
    • 2012-11-06
    • 2018-08-11
    • 2012-01-15
    • 1970-01-01
    • 2011-10-27
    • 1970-01-01
    • 2018-10-13
    相关资源
    最近更新 更多