程序集x86(32位),调用NR_creat(8)损坏文件名存储

程序集x86(32位),调用NR_creat(8)损坏文件名存储

所有的,我已经拉出我的头发,试图确定如何为文件名保留的存储空间被调用文件creat (NR_creat 8)损坏。 汇编程序是nasm ,代码是在x86_64框中编译并运行的32位代码。 例程是一个简单的代码,它从程序参数argv[1]中获取文件名,然后使用该名称创build一个具有0644八进制许可权的文件。将文件写入,然后程序退出。 文件操作工作,问题是我失去了调用文件fnbuf时存储在fnbuf的文件名。

名为fnbuf的保留存储是32字节,在简单的mov [fnbuf], ebx操作中用argv[1]填充。 fnbuf的信息fnbuf是正常的,直到文件被创build,之后, fnbuf的信息被破坏,地址被改变。 (所有其他存储的信息都很好)。 要保留文件名,我已经把它推到了堆栈上(这在文件创build后工作正常)我不明白为什么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 

代码使用以下内容构build,并使用[fnbuf]file creat调用之前将file creat名写入stdout ,但之后必须从堆栈中popup保存的argv[1]以输出文件创build后的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 ,但是在0xffffd2000xffffd200 – 大约253字节。 这令人眼花缭乱,我陷入困境。 这几乎就像是其中一个分段地址在移动,但是我希望剩下的信息也会被破坏。 我的其他想法是, fnbuf不是明确地以NUL-terminated 。 我已经把它设置为NUL,问题和问题依然存在。 除此之外,除了x86_64问题上的x86执行外,我想不出任何其他的东西,但这似乎是一个延伸。

完整的代码清单:

 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 

我不知道是什么导致了fnbuf的腐败。 而且,什么可能会影响这个地址,而其他地方似乎正在按预期工作。 任何帮助将不胜感激。

首先,你搞乱了一个指针和一个缓冲区

 fnbuf resb buflen 

分配“buflen”字节数(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 ,这会导致你观察到的修改值。