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.
source to share
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.
source to share