2014-10-10 70 views
3

大會的x86(32位),調用NR_creat(8)會損壞文件名存儲大會的x86(32位),調用NR_creat(8)會損壞文件名存儲

一切,我已經把我的頭髮試圖確定如何爲文件名保留的存儲器被文件creat (NR_creat 8)的調用破壞。彙編程序爲nasm,代碼爲32位編譯並在x86_64框中運行。該例程是一段簡單的代碼,它從程序參數argv[1]獲取文件名,然後使用該名稱創建一個具有0644八進制權限的文件。將文件寫入,然後程序退出。文件操作起作用,問題是我在調用文件creat時丟失了保存在fnbuf中的文件名。

保留的存儲名爲fnbuf的是32字節,在mov [fnbuf], ebx的簡單操作中填入了argv[1]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]file creat調用之前的文件名stdout下面的輸出,但後來,必須從棧中彈出的保存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,但後來在0xffffd200報告 - 大約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腐敗。此外,單獨影響該地址的其他因素似乎可以按預期工作。任何幫助將不勝感激。

回答

1

首先,你messsing了指針緩衝

fnbuf resb buflen 

分配 「buflen」(32)的字節數,您可能需要作爲緩衝, 但

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

存儲有ADRESS(指針)包含在ebx成的頭四個字節的fnbuf - 它不復制的F ilename本身或其他任何內容到緩衝區,只是指向文件名的指針。傾倒你的.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這在你觀察到的修正值結果 。

+0

安德烈亞斯,謝謝。因此,爲了在'fnbuf'中存儲'argv [1]',我需要在'si'中使用'rep movsb'和''''中的'argv [1]'和'di'中的'fnbuf'。 – 2014-10-10 14:16:39

+0

本質上是 - 不要忘記正確設置ECX和[方向標誌](http://stackoverflow.com/questions/10380076/direction-flag-in-x86) – 2014-10-10 18:53:14