简体   繁体   English

x86_64 程序集 - 尝试在 x64 程序集中编辑数组中的字节时出现段错误

[英]x86_64 Assembly - Segfault when trying to edit a byte within an array in x64 assembly

The tutorial I am following is for x86 and was written using 32-bit assembly, I'm trying to follow along while learning x64 assembly in the process.我正在关注的教程是针对 x86 并使用 32 位汇编编写的,我在学习 x64 汇编的过程中尝试跟随。 This has been going very well up until this lesson where I have the following simple program which simply tries to modify a single character in a string;这一直进展顺利,直到本课我有以下简单的程序,它只是尝试修改字符串中的单个字符; it compiles fine but segfaults when ran.它编译得很好,但运行时会出现段错误。

section .text

global _start ; Declare global entry oint for ld
_start:

    jmp short message ; Jump to where or message is at so we can do a call to push the address onto the stack

    code:   
    xor rax, rax    ; Clean up the registers
    xor rbx, rbx
    xor rcx, rcx
    xor rdx, rdx

    ; Try to change the N to a space
    pop rsi ; Get address from stack
    mov al, 0x20 ; Load 0x20 into RAX
    mov [rsi], al; Why segfault?
    xor rax, rax; Clear again

    ; write(rdi, rsi, rdx) = write(file_descriptor, buffer, length)
    mov al, 0x01    ; write the command for 64bit Syscall Write (0x01) into the lower 8 bits of RAX
    mov rdi, rax    ; First Paramter, RDI = 0x01 which is STDOUT, we move rax to ensure the upper 56 bits of RDI are zero
    ;pop rsi        ; Second Parameter, RSI = Popped address of message from stack
    mov dl, 25  ; Third Parameter, RDX = Length of message
    syscall     ; Call Write

    ; exit(rdi) = exit(return value)    
    xor rax, rax    ; write returns # of bytes written in rax, need to clean it up again
    add rax, 0x3C   ; 64bit syscall exit is 0x3C
    xor rdi, rdi    ; Return value is in rdi (First parameter), zero it to return 0
    syscall     ; Call Exit

    message:
    call code   ; Pushes the address of the string onto the stack
    db 'AAAABBBNAAAAAAAABBBBBBBB',0x0A

This culprit is this line:罪魁祸首是这一行:

mov [rsi], al; Why segfault?

If I comment it out, then the program runs fine, outputting the message 'AAAABBBNAAAAAAAABBBBBBBB', why can't I modify the string?如果我注释掉它,那么程序运行正常,输出消息'AAAABBBNAAAAAAABBBBBBBB',为什么我不能修改字符串?

The authors code is the following:作者代码如下:

global _start


_start:
        jmp short ender

        starter:

        pop ebx                 ;get the address of the string
        xor eax, eax

        mov al, 0x20
        mov [ebx+7], al        ;put a NULL where the N is in the string

        mov al, 4       ;syscall write
        mov bl, 1       ;stdout is 1
        pop ecx         ;get the address of the string from the stack
        mov dl, 25       ;length of the string
        int 0x80

        xor eax, eax
        mov al, 1       ;exit the shellcode
        xor ebx,ebx
        int 0x80

        ender:
        call starter
        db 'AAAABBBNAAAAAAAABBBBBBBB'0x0A

And I've compiled that using:我已经使用以下方法编译了它:

nasm -f elf <infile> -o <outfile>
ld -m elf_i386 <infile> -o <outfile>

But even that causes a segfault, images on the page show it working properly and changing the N into a space, however I seem to be stuck in segfault land:( Google isn't really being helpful in this case, and so I turn to you stackoverflow, any pointers (no pun intended!) would be appreciated但即使这会导致段错误,页面上的图像显示它正常工作并将 N 更改为空格,但是我似乎被困在段错误领域:(谷歌在这种情况下并没有真正提供帮助,所以我转向你stackoverflow,任何指针(没有双关语!)将不胜感激

I would assume it's because you're trying to access data that is in the .text section.我认为这是因为您正在尝试访问.text部分中的数据。 Usually you're not allowed to write to code segment for security.通常,为了安全起见,您不允许写入代码段。 Modifiable data should be in the .data section.可修改的数据应该在.data部分。 (Or .bss if zero-initialized.) (或.bss如果零初始化。)

For actual shellcode, where you don't want to use a separate section, see Segfault when writing to string allocated by db [assembly] for alternate workarounds.对于不希望使用单独部分的实际 shellcode,请参阅Segfault 当写入由 db [assembly] 分配的字符串时的替代解决方法。


Also I would never suggest using the side effects of call pushing the address after it to the stack to get a pointer to data following it, except for shellcode.此外,我永远不会建议使用call将地址之后的地址推送到堆栈的副作用来获取指向其后数据的指针,除了 shellcode。

This is a common trick in shellcode (which must be position-independent);这是 shellcode 中的一个常见技巧(必须与位置无关); 32-bit mode needs a call to get EIP somehow. 32 位模式需要调用以某种方式获取 EIP。 The call must have a backwards displacement to avoid 00 bytes in the machine code, so putting the call somewhere that creates a "return" address you specifically want saves an add or lea . call必须具有向后位移以避免机器代码中的00字节,因此将调用放置在创建您特别想要的“返回”地址的地方可以节省addlea

Even in 64-bit code where RIP-relative addressing is possible, jmp / call / pop is about as compact as jumping over the string for a RIP-relative LEA with a negative displacement.即使在可以使用 RIP 相对寻址的 64 位代码中,jmp / call / pop 也与跳过具有负位移的 RIP 相对 LEA 的字符串一样紧凑。

Outside of the shellcode / constrained-machine-code use case, it's a terrible idea and you should just lea reg, [rel buf] like a normal person with the data in .data and the code in .text .shellcode / 受限机器代码用例之外,这是一个糟糕的主意,您应该像普通人一样使用.data的数据和.text的代码lea reg, [rel buf] (Or read-only data in .rodata .) This way you're not trying execute code next to data, or put data next to code. (或.rodata只读数据。)这样您就不会尝试在数据旁边执行代码,或将数据放在代码旁边。

(Code-injection vulnerabilities that allow shellcode already imply the existence of a page with write and exec permission, but normal processes from modern toolchains don't have any W+X pages unless you do something to make that happen. W^X is a good security feature for this reason, so normal toolchain security features / defaults must be defeated to test shellcode.) (允许 shellcode 的代码注入漏洞已经暗示存在具有 write 和 exec 权限的页面,但是现代工具链中的正常进程没有任何 W+X 页面,除非你做一些事情来实现它。W ^X是一个出于这个原因,良好的安全功能,因此必须击败正常的工具链安全功能/默认值才能测试 shellcode。)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM