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

时间:2014-10-10 09:03:02

标签: linux assembly nasm

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

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

名为fnbuf的保留存储空间为32字节,在简单argv[1]操作中填充mov [fnbuf], ebxfnbuf中的信息在创建文件之前已经很好,此后,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 腐败的原因感到茫然。此外,在翻译其他地方时可能会影响该地址的内容似乎正在按预期工作。任何帮助将不胜感激。

1 个答案:

答案 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}} 在您观察到的修改后的值中。