NASM:两个后续文件写入无效

时间:2016-12-01 16:38:56

标签: assembly x86 nasm intel

尝试运行此代码以便我可以创建bmp文件 - 我写标题,然后我想将内容写入文件 - 一切都是单独工作但不能一起工作。 如果重要的话,我会使用hexedit来检查文件。

如果我使用标题编写部分运行代码,则可以正常工作。 如果我使用内容编写部分运行代码,它就可以工作。 当我同时运行它们时,它不会。

有什么想法吗?

以下是代码:

section     .text
global      _start                              

_start:                                         

;#######################################################################
;### main ##############################################################
;#######################################################################

    ; open file
    mov     eax,8                               ;system call number - open/create file
    mov     ebx,msg                             ;file name
    mov     ecx,111111111b                      ;file mode
    int     0x80                                ;call kernel

    ; save file descriptor to r8d
    mov     r8d, eax

    ; write headline to file
    mov     eax, 4                              ;write 54 bytes to file
    mov     ebx, r8d                            ;load file desc
    mov     ecx, bmpheadline                    ;load adress of memory to write
    mov     edx, 54                             ;load number of bytes
    int     0x80                                ;call kernel

    ; write content to file
    mov     eax, 4                              ;number of syscall - write
    mov     ebx, r8d                            ;load file desc
    ;add     ebx, 54                             ;add 54 bytes to location of file location
    mov     ecx, empty_space                    ;load adress of buffer
    mov     edx, 40054                          ;load number of bytes
    int     0x80                                ;call kernel

    ; close file
    mov     eax, 6                              ;load syscall number - close
    mov     ebx, r8d                            ;load file desc
    int     0x80                                ;call kernel

    ; exit program
    mov     eax,1                               ;syscall number - exit     
    int     0x80                                ;call kernel

section     .data

    msg     db  'filename.bmp',0x00                 ;name of out file, 0x00 = end of string                        

    bmpheadline db  0x42,0x4D,0xB6,0xDA,0x01,0x00,0x00,0x00,0x00,0x00,0x7A,0x00,0x00,0x00,0x6C,0x00,0x00,0x00,0xC9,0x00,0x00,0x00,0xC9,0x00,0x00,0x00,0x01,0x00,0x18,0x00,0x00,0x00,0x00,0x00,0x3C,0xDA,0x01,0x00,0x13,0x0B,0x00,0x00,0x13,0x0B,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x42,0x47,0x52,0x73,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00

section .bss

    empty_space: resb 40054

3 个答案:

答案 0 :(得分:2)

您在文件描述符中添加了54而没有说明;我完全不知道你为什么这么做。

我怀疑您误解了文件描述符,并认为您需要将目前为止写入的数据总量添加到描述符中。事实并非如此。描述符不会从打开/创建时间更改为关闭文件句柄的时间。验证您的评论是否与您的代码同步是一个非常好的主意。当您编写详细注释时,没有注释的行会立即被怀疑(例如add指令。)

您从一开始就遇到了一些问题。例如,您的评论说"打开文件"和" sys_write"但是你的代码并不匹配。您的代码目前正在尝试调用sys_creat。您调用文件描述符的实际上是权限模式。 ebx应该包含表示路径的字符串的地址......注释似乎表明它应该是stdout,但它显然不是。 :)

您也不会说明这是针对64位还是32位Linux。您的代码似乎使用r8d并使用int 0x80混合了两者。

答案 1 :(得分:2)

您的代码存在两个重大问题。 R8D R8 )不会在int 0x80之间保留。其次,原始问题中的add ebx, 54不正确。您无需更改文件描述符。

SYSCALL首选64位代码

int 0x80是Linux内核中的IA32兼容性功能。此功能通常在大多数64位Linux内核中打开,但可以关闭。您不能将{64}指针与int 0x80一起使用。这可以防止使用基于堆栈的地址作为int 0x80的参数。出于这些原因,您最好将 SYSCALL 用于64位程序而不是int 0x80

可以在Ryan Chapman's Blog中找到有关在Linux中使用 SYSCALL 的更多信息。请注意,与 SYSCALL 一起使用的系统调用号与int 0x80不同。用于传递参数的寄存器是不同的,并且 SYSCALL 中未保留的唯一寄存器是 RCX R11 RAX < / em>( RAX 是返回值)。系统调用约定在当前64-bit Linux System V ABI中有详细描述。特别是:

  
      
  1. 用户级应用程序用作整数寄存器来传递序列   %rdi,%rsi,%rdx,%rcx,%r8和%r9。 内核界面使用%rdi,   %rsi,%rdx,%r10,%r8和%r9
  2.   
  3. 系统调用通过syscall指令完成。 内核破坏了   注册%rcx和%r11
  4.   
  5. 系统调用的号码必须在寄存器%rax中传递。
  6.   
  7. 系统调用仅限于六个参数,不直接传递参数   堆栈。
  8.   
  9. 从系统调用返回,寄存器%rax包含结果   系统调用即可。介于-4095和-1之间的值表示错误,   它是-errno。
  10.   
  11. 只将类INTEGER或类MEMORY的值传递给内核
  12.   

如果您希望64位代码与INT 0x80

一起使用

INT 0x80 在64位代码中有一些怪癖。它遵循保留 RBX RCX RDX RSI 的32位调用约定RDI RBP 。对于其他64位寄存器,适用64位 C 调用约定。来自ABI:

  

A.2.1呼叫约定

     

...喜欢调用系统调用的应用程序应该使用C库中的函数。 C库和Linux内核之间的接口与用户级应用程序的接口相同

请参阅上面链接的64位Linux ABI中的图3.4:注册表用法 R12 R13 R14 R15 也将被保留。

这意味着 RAX R8 R9 R10 R11 不会被保留。将代码从使用 R8D 更改为保存的其中一个寄存器。 R12D 例如。

为什么你的代码失败了?

由于 R8D 未在int 0x80之间保留,因此 SYS_WRITE 系统调用可能会覆盖 R8D 。第一个写入工作,第二个写入不起作用,因为 R8D 可能被第一个 SYS_WRITE 删除,而 R8D 可能成为无效的文件描述符。使用将保留的其中一个寄存器应该可以解决此问题。如果用完寄存器,您总是可以在堆栈上分配空间以进行临时存储。

答案 2 :(得分:0)

(代表OP发布解决方案)

以下是解决方案的源代码,64位版本:

section     .text
global      _start                              ;must be declared for linker (ld)

_start:                                         ;tell linker entry point

;#######################################################################
;### This program creates empty bmp file - 64 bit version ##############
;#######################################################################
;### main ##############################################################
;#######################################################################

    ; open file
    mov     rax,85                               ;system call number - open/create file
    mov     rdi,msg                             ;file name
                                                ;flags
    mov     rsi,111111111b                      ;mode
    syscall                                     ;call kernel

    ; save file descriptor
    mov     r8, rax

    ; write headline to file
    mov     rax, 1                              ;write to file
    mov     rdi, r8                             ;load file desc
    mov     rsi, bmpheadline                    ;load adress of memory to write
    mov     rdx, 54                             ;load number of bytes
    syscall                                     ;call kernel

    ; write content to file
    mov     rax, 1                              ;write to file
    mov     rdi, r8                             ;load file desc
    mov     rsi, empty_space                    ;load adress of memory to write
    mov     rdx, 40000                          ;load number of bytes
    syscall                                     ;call kernel

    ; close file
    mov     rax, 3                              ;load syscall number - close
    mov     rdi, r8                             ;load file desc
    syscall                                     ;call kernel

    ; exit program
    mov     rax,60                               ;system call number (sys_exit)
    syscall                                     ;call kernel

section     .data

    msg     db  'filename.bmp',0x00                 ;name of out file, 0x00 = end of string
    len     equ $ - msg                             ;length of our dear string

    bmpheadline db  0x42,0x4D,0xB6,0xDA,0x01,0x00,0x00,0x00,0x00,0x00,0x7A,0x00,0x00,0x00,0x6C,0x00,0x00,0x00,0xC9,0x00,0x00,0x00,0xC9,0x00,0x00,0x00,0x01,0x00,0x18,0x00,0x00,0x00,0x00,0x00,0x3C,0xDA,0x01,0x00,0x13,0x0B,0x00,0x00,0x13,0x0B,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x42,0x47,0x52,0x73,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00

section .bss

    empty_space: resb 40000

生成文件:

all: a.out

a.out: main.o
        ld main.o

main.o: main64.asm
        nasm -f elf64 main64.asm -o main.o