【问题标题】:NASM: Two subsequent file writes not workingNASM:两个后续文件写入不起作用
【发布时间】:2017-04-16 09:39:04
【问题描述】:

尝试运行此代码,以便我可以创建 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

【问题讨论】:

  • r8d 来看,这似乎是 64 位代码。不要使用int 0x80,因为那是 32 位兼容性中断。 PS:您可以使用strace 来跟踪系统调用,并使用调试器来单步调试您的代码。
  • 您应该考虑先返回并清理您的 cmets 和代码。它可能会使问题对您更加明显。比如第一节的cmets与代码不符。

标签: linux assembly x86 nasm system-calls


【解决方案1】:

你在文件描述符上加了 54 而没有解释;我完全不知道你为什么要这样做。

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

您似乎从一开始就遇到了一些问题。例如,您的 cmets 说“打开文件”和“sys_write”,但您的代码不匹配。您的代码当前所做的是尝试调用sys_creat。您所说的文件描述符实际上是权限模式。 ebx 应该包含代表路径的字符串的地址... cmets 似乎表明它应该是标准输出,但显然不是。 :)

您也没有说明这是针对 64 位还是 32 位 Linux。您的代码似乎混合了两者,使用r8dint 0x80

【讨论】:

  • 64 位或 32 位 Linux - 假设 int 0x80 适用于它们仍然,删除“添加 54”指令我不明白为什么我:1. 将 54 字节写入文件 2。将 40054 字节写入文件 3. 关闭文件 4. 文件中只有 54 字节
  • @vanqyard:像 Jester 建议的那样使用 strace,并阅读系统调用的手册页。
  • r8d 可能会被覆盖。在您的情况下,分配一个内存位置来存储 FD 并从那里检索它会更安全。
【解决方案2】:

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


64 位代码首选 SYSCALL

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

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

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

如果您希望您的 64 位代码使用 INT 0x80

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

A.2.1 调用约定

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

参见上面链接的 64 位 Linux ABI 中的图 3.4:寄存器用法R12R13R14R15也会被保留。

这意味着 RAXR8R9R10R11 不会被保留。将您的代码从使用 R8D 更改为已保存的寄存器之一。 R12D 例如。


为什么你的代码会失败?

由于 R8D 未在 int 0x80 中保留,它可能会被 SYS_WRITE 系统调用覆盖。第一次写入有效,第二次无效,因为 R8D 可能已被第一次 SYS_WRITE 破坏,而 R8D 可能成为无效的文件描述符.使用将保留的寄存器之一应该可以解决此问题。如果您的寄存器用完了,您可以随时在堆栈上分配空间用于临时存储。

【讨论】:

    【解决方案3】:

    (代表 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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-08-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-01-31
      • 1970-01-01
      • 2013-04-18
      • 2018-03-24
      相关资源
      最近更新 更多