Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
592 views
in Technique[技术] by (71.8m points)

segmentation fault - 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. 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?

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

question from:https://stackoverflow.com/questions/65943894/arm64-assembly-convert-int-to-string

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

I would assume it's because you're trying to access data that is in the .text section. Usually you're not allowed to write to code segment for security. Modifiable data should be in the .data section. (Or .bss if zero-initialized.)

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.


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.

This is a common trick in shellcode (which must be position-independent); 32-bit mode needs a call to get EIP somehow. 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.

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.

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. (Or read-only data in .rodata.) This way you're not trying execute code next to data, or put data next to code.

(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.)


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...