Build x86 (32-bit), call NR_creat (8) Corrupts Filename Storage

Build x86 (32-bit), call NR_creat (8) Correct the storage file name

That's it, I pulled my hair out trying to figure out how the reserved storage I have for the filename gets corrupted by the file call creat (NR_creat 8)

. Assembler nasm

, and the code is 32-bit, compiled and run in the field x86_64

. A subroutine is a simple bit of code that takes a filename from the arguments of the program argv[1]

, and then uses that name to create a file with octal resolution 0644. The file is written to and then the program exits. Working with files works, the problem is that I lose the filename stored in fnbuf

when I call the file creat

.

The named reserved storage fnbuf

is 32 bytes and is populated with argv[1]

in a simple operation mov [fnbuf], ebx

. The information in fnbuf

is executed until the file is created, after which the information in fnbuf

is damaged and the address is changed. (all other stored information is in order). To keep the filename, I ended up pushing it onto the stack (which works fine after creating the file). I do not understand why the information is fnbuf

corrupted and you need help.

The corresponding code looks the same as the short output of gdb ( complete code follows at the end ).

section    .data
    buflen equ 32

section .bss
    fd_out  resb 1
    fd_in   resb 1
    fnbuf   resb buflen

section    .text

        global  _start
_start:                         ; linker entry point

        ; get the file_name from stdin (argv[1])
        add     esp, 8          ; skip over argc and argv[0] on stack (2 ptr = +8)
        pop     ebx             ; pop argv[1] to ebx
        test    ebx, ebx        ; test if null, jump
        jz      noarg           ; (if pop to ecx, use jecxz - no test required)
        mov     [fnbuf], ebx    ; save the filename in fnbuf (FIXME)
        push    ebx             ; save on stack since fnbuf is getting whacked

        ; output fnbuf to stdout (fnbuf is fine here)
        mov     edi, [fnbuf]    ; load string in edi for length calc & output
        call    strprn          ; calc length and output to stdout
        call    newln           ; output newline to stdout

        ; create the file  (fnbuf is corrupted by this call)
        mov     eax, 8          ; system call number (sys_creat)
        mov     ebx, [fnbuf]    ; set ebx to filename (fine here)
        mov     ecx, 0420       ; 644 octal -rw-r--r--
        int     0x80            ; call kernel
        jc      errcf           ; if carry flag non-zero, jmp errcf
        mov     [fd_out], eax   ; save file descriptor in fd_out

        ; write msg to file
        mov     edi, msg        ; msg address to edi for length
        call    strsz           ; calc length of message to write (ret in edx)
        mov     eax, 4          ; system call number (sys_write)
        mov     ebx, [fd_out]   ; file descriptor
        mov     ecx, msg        ; message to write
        int     0x80            ; call kernel

      

The code is built with the following and provides the following output, using [fnbuf]

to write the filename in stdout

before the call file creat

, but then has to pop the stored one argv[1]

from the stack to pop the filename after file creat

. Usage [fnbuf]

works fine:

nasm -f elf -o ./obj/filecwr_32.o filecwr_32.asm -g
ld -m elf_i386 -o ./bin/filecwr_32 ./obj/filecwr_32.o

$ ./bin/filecwr_32 newfile.txt
newfile.txt
File write complete.
newfile.txt

$ cat newfile.txt
To whom it may concern, this information was written to the file

      

Executing the program with the help gdb

shows that corruption occurs when calling the kernel for file creat

:

gdb ./bin/filecwr_32
(gdb) set args newfile.txt
(gdb) break 1
Breakpoint 1 at 0x8048080: file filecwr_32.asm, line 1.
(gdb) run
(gdb) watch fnbuf
Hardware watchpoint 2: fnbuf
(gdb) step
Single stepping until exit from function strsz,
which has no line number information.
0x08048095 in strprn ()
(gdb)
Single stepping until exit from function strprn,
which has no line number information.
newfile.txt0x080480f2 in _start ()
(gdb) x/s fnbuf
0xffffd2fd:     "newfile.txt"
(gdb) step
...
Hardware watchpoint 2: fnbuf

Old value = -11523
New value = -11776
0x08048120 in _start ()
(gdb) x/s fnbuf
0xffffd200:     "\376\336\377\377\023\337\377\377\036\337\377\377<\337\377\377\227\337\377\377"
...
[Inferior 1 (process 30000) exited normally]
(gdb) quit

      

Looking at the output gdb

above, has the address shown for fnbuf

changed? It was originally in 0xffffd2fd

, but then reported in 0xffffd200

- about 253 bytes further up the stack. This is confusing and where I am stuck. It almost looks like one of the segment addresses is flipping around, but then I would expect the rest of the information to mess up as well. Other thoughts I had was that somehow it was fnbuf

n't explicit NUL-terminated

. I set it to NUL and the issue and problem persist. Other than that, I can't think of anything other than roaming x86 execution when x86_64 is released, but that seems like a stretch.

Full list of codes:

section    .data
    msg db 'To whom it may concern, this information was written to the file', 0xa, 0
    msg_done db 'File write complete.', 0xa, 0
    msg_noarg db 'No argument available for filename.', 0xa, 0
    msg_create_fail db 'File create failed.', 0xa, 0

    buflen equ 32
    nwln db 0xa

section .bss
    fd_out  resb 1
    fd_in   resb 1
    flen    resb 1
    fnbuf   resb buflen

section    .text
            global  _start

    ; szstr computes the length of a string.
    ; edi - string address
    ; edx - contains string length (returned)
    strsz:
            xor     ecx, ecx        ; zero rcx
            not     ecx             ; set rcx = -1 (uses bitwise id: ~x = -x-1)
            xor     al,al           ; zero the al register (initialize to NUL)
            cld                     ; clear the direction flag
            repnz   scasb           ; get the string length (dec ecx through NUL)
            not     ecx             ; rev all bits of negative -> absolute value
            dec     ecx             ; -1 to skip the null-term, ecx contains length
            mov     edx, ecx        ; size returned in edx, ready to call write
            ret

    ; strprn writes a string to the file descriptor.
    ; edi - string address
    ; edx - contains string length
    strprn:
            push    edi             ; push string address onto stack
            call    strsz           ; call strsz to get length
            pop     ecx             ; pop string to ecx esi (source index)
            mov     eax, 0x4        ; write/stdout number in eax (sys_write 4)
            mov     ebx, 0x1        ; set destination index to ebx (stdout 1)
            int     0x80            ; call kernel
            ret

    ; newln writes a newline to the file descriptor.
    newln:
            mov     ecx, nwln       ; set string index in ecx
            mov     ebx, 0x1        ; set destination index to (stdout 1)
            mov     edx, 0x1        ; set length of string in edx
            mov     eax, 0x4        ; mov write syscall number (4) to eax
            int     0x80            ; call kernel
            ret

    ; error function for no argument
    noarg:
            mov     edi, msg_noarg  ; error msg to edi for length calc
            call    strprn          ; calc length and output to stdout
            jmp     exit

    ; error on fail to create file
    errcf:
            mov     edi, msg_create_fail ; error msg to edi for length calc
            call    strprn          ; calc length and output to stdout
            jmp     exit

    _start:                         ; linker entry point

            ; get the file_name from stdin (argv[1])
            add     esp, 8          ; skip over argc and argv[0] on stack (2 ptr = +8)
            pop     ebx             ; pop argv[1] to ebx
            test    ebx, ebx        ; test if null, jump
            jz      noarg           ; (if pop to ecx, use jecxz - no test required)
            mov     [fnbuf], ebx    ; save the filename in fnbuf (FIXME)
            push    ebx             ; save on stack since fnbuf is getting whacked

            ; output fnbuf to stdout (fnbuf is fine here)
            mov     edi, [fnbuf]    ; load string in edi for length calc & output
            call    strprn          ; calc length and output to stdout
            call    newln           ; output newline to stdout

            ; create the file  (fnbuf is corrupted by this call)
            mov     eax, 8          ; system call number (sys_creat)
            mov     ebx, [fnbuf]    ; set ebx to filename (fine here)
            mov     ecx, 0420       ; 644 octal -rw-r--r--
            int     0x80            ; call kernel
            jc      errcf           ; if carry flag non-zero, jmp errcf
            mov     [fd_out], eax   ; save file descriptor in fd_out

            ; write msg to file
            mov     edi, msg        ; msg address to edi for length
            call    strsz           ; calc length of message to write (ret in edx)
            mov     eax, 4          ; system call number (sys_write)
            mov     ebx, [fd_out]   ; file descriptor
            mov     ecx, msg        ; message to write
            int     0x80            ; call kernel

            ; close the file
            mov     eax, 6          ; set eax sys_close
            mov     ebx, [fd_out]   ; file descriptor in ebx 
            int     0x80

            ; print write done to stdout
            mov     edi, msg_done   ; msg_done in ecx
            call    strprn          ; calc length and output to stdout

            ; print file name to stdout
            ; mov     edi, [fnbuf]    ; fnbuf corrupted? Segment smashed?
            pop     edi             ; pop original filename from stack
            push    edi             ; save another copy since fnbuf is messed up
            call    strprn          ; calc length and output to stdout
            call    newln           ; output newline
            jmp     exit

    exit:
            xor     ebx, ebx        ; set exit code to 0
            mov     eax,1           ; system call number (sys_exit)
            int     0x80            ; call kernel

      

I don't understand what caused the damage fnbuf

. What's more, what could affect this address alone when everything else seems to be working as intended. Any help would be appreciated.

+3


source to share


1 answer


First, you put a pointer and the buffer :

fnbuf   resb buflen

      

allocates "buflen" the number of bytes (32) that you can use as a buffer, but

mov     [fnbuf], ebx    ; save the filename in fnbuf (FIXME)

      

stores the address (pointer) contained in ebx

, in the first four bytes fnbuf

- it does not copy the filename or anything else into the buffer, just a pointer to the filename. Dumping your area of ​​memory .bss

produces this output (note that fd_out

this is the first address of your area .bss

):

(gdb) x/32 0x80491ec
0x80491ec <fd_out>: 0x00    0x00    0x00    0xac    0xd2    0xff    0xff    0x00
                                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                                             Pointer retrieved from EBX

      



But the real problem is that you store the file descriptor in fd_out

:

mov     [fd_out], eax   ; save file descriptor in fd_out

      

This writes four (!!) bytes from eax

into memory, starting at fd_out

. Dropping the same memory subsequently results in

                                          Destroyed!
(gdb) x/32 0x80491ec                        ****
0x80491ec <fd_out>: 0x03    0x00    0x00    0x00    0xd2    0xff    0xff    0x00
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                     Four bytes written by mov

      

As you can see, this one mov

destroys the first byte of your pointer - it's set to 0x00

, which results in the changed value you observed.

+1


source







All Articles