程序集x86(32位),调用NR_creat(8)破坏文件名存储
所有,我已经拉了我的头发试图确定我对文件名的保留存储如何被文件creat (NR_creat 8)
的调用破坏。汇编程序为nasm
,代码为32位编译并在x86_64
框上运行。该例程是一段简单的代码,它从程序参数argv[1]
获取文件名,然后使用该名称创建一个八进制权限为0644的文件。该文件被写入然后程序退出。文件操作有效,问题是我在调用文件fnbuf
时丢失了存储在creat
中的文件名。
名为fnbuf
的保留存储空间为32字节,在简单argv[1]
操作中填充mov [fnbuf], ebx
。 fnbuf
中的信息在创建文件之前已经很好,此后,fnbuf
中的信息已损坏且地址已更改。 (所有其他存储的信息都很好)。为了保留文件名,我最终将其推入堆栈(在创建文件后工作正常)我不明白为什么fnbuf
中的信息已损坏并需要帮助。
相关代码如下,以及简短的gdb输出(最后的完整代码)。
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
代码使用以下内容构建,并使用[fnbuf]
提供以下输出,以便在stdout
调用之前将文件名写入file creat
,但之后必须弹出已保存的{{1}从堆栈输出argv[1]
后面的文件名。使用file creat
也可以正常工作:
[fnbuf]
使用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
逐步执行程序会显示内核调用gdb
时发生损坏:
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
输出,gdb
报告的地址已更改?它最初位于fnbuf
,但随后在0xffffd2fd
报告 - 在堆栈下方大约253个字节。这令人困惑,我陷入了困境。它几乎就像其中一个段地址变换一样,但我希望其余的信息也会被破坏。我的其他想法是某种方式0xffffd200
没有明确地fnbuf
。我已将其设置为NUL,问题和问题仍然存在。除此之外,除了x86_64问题上的杂散x86执行之外,我无法想到任何其他事情,但这似乎是一个延伸。
完整的代码清单:
NUL-terminated
我对导致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
腐败的原因感到茫然。此外,在翻译其他地方时可能会影响该地址的内容似乎正在按预期工作。任何帮助将不胜感激。
答案 0 :(得分:1)
首先,您正在搞乱指针和缓冲区:
fnbuf resb buflen
分配&#34; buflen&#34;您可能希望用作缓冲区的字节数(32), 但是
mov [fnbuf], ebx ; save the filename in fnbuf (FIXME)
将ebx
中包含的地址(指针)存储到fnbuf
的前四个字节中 - 它不会将文件名本身或其他任何内容复制到缓冲区,只是指向文件名的指针。转储.bss
内存区域后会显示此输出(请注意fd_out
是.bss
区域的第一个地址):
(gdb) x/32 0x80491ec
0x80491ec <fd_out>: 0x00 0x00 0x00 0xac 0xd2 0xff 0xff 0x00
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Pointer retrieved from EBX
但是,真正的问题是将文件描述符存储到fd_out
:
mov [fd_out], eax ; save file descriptor in fd_out
这会将eax
的四个(!!)字节写入从fd_out
开始的内存。倾倒相同的记忆
之后结果
Destroyed!
(gdb) x/32 0x80491ec ****
0x80491ec <fd_out>: 0x03 0x00 0x00 0x00 0xd2 0xff 0xff 0x00
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Four bytes written by mov
如您所见,此mov
会破坏指针的第一个字节 - 它设置为0x00
,结果为{{1}}
在您观察到的修改后的值中。