【问题标题】:Open file, delete zeros, sort it - NASM打开文件,删除零,排序 - NASM
【发布时间】:2019-02-25 22:01:22
【问题描述】:

我目前正在解决一些问题,这是我遇到的问题。为了清楚起见,我是初学者,因此非常欢迎任何帮助。

问题:

按降序对二进制文件的内容进行排序。文件名作为命令行参数传递。文件内容被解释为四字节正整数,其中值 0 在找到时不会写入文件。结果必须写入已读取的同一文件中。

我理解的方式是我必须有一个二进制文件。打开它。获取其内容。找出所有字符,同时记住那些是正的四字节整数,找到零,去除零,对其余数字进行排序。

我们被允许使用 glibc,所以这是我的尝试:

section .data
    warning db 'File does not exist!', 10, 0
    argument db 'Enter your argument.', 10, 0

    mode dd 'r+'
    opened db 'File is open. Time to read.', 10, 0


section .bss
    content resd 10
    counter resb 1

section .text

    extern printf, fopen, fgets, fputc

global main
main:
    push rbp
    mov rbp, rsp
    push rsi
    push rdi
    push rbx

    ;location of argument's address
    push rsi 

    cmp rdi, 2
    je .openfile
    mov rdi, argument
    mov rax, 0
    call printf
    jmp .end

.openfile:
    pop rbx
    ;First real argument of command line
    mov rdi, [rbx + 8]
    mov rsi, mode
    mov rax, 0
    call fopen
    cmp al, 0
    je .end

    push rax

    mov rdi, opened
    mov rax, 0
    call printf

.readfromfile:
    mov rdi, content
    mov rsi, 12 ;I wrote 10 numbers in my file
    pop rdx
    mov rax, 0
    call fgets
    cmp al, 0
    je .end

    push rax


    mov rsi, tekst
    pop rdi
.loop:
    lodsd
    inc byte[counter]
    cmp eax, '0'
    jne .loop

    ;this is the part where I am not sure what to do. 
    ;I am trying to delete the zero with backspace, then use space and 
    ;backspace again - I saw it here somewhere as a solution
    mov esi, 0x08
    call fputc  
    mov esi, 0x20
    call fputc
    mov esi, 0x08
    call fputc
    cmp eax, 0
    je .end
    jmp .loop

.end:
    pop rdi
    pop rsi
    pop rbx
    mov rsp, rbp
    pop rbp
    ret

所以,我的想法是打开文件,找到零,使用退格和空格将其删除,然后再次退格;继续直到我到达文件的末尾,然后对其进行排序。可以看出,我没有尝试对内容进行排序,因为我无法让程序为我完成第一部分。我已经尝试了几天了,一切都变得模糊不清。

如果有人可以帮助我,我将非常感激。如果有类似的问题,请随时链接给我。任何可以帮助的东西,我都准备好阅读和学习。

我也不确定我必须提供多少信息。如果有不清楚的地方,请指出来。

谢谢

【问题讨论】:

  • 您不能使用退格从文件中删除。在任何情况下,只需将所有内容读入内存,对其进行排序,截断文件并写入不带零的整个内容(这将在最后,因此您可以使用较小的长度)。您可能想先用 C 语言编写它。
  • 要打开二进制文件,你应该对fopen使用“r+b”模式。二进制文件中的“四字节正整数”也意味着简单的dword,即从 0 到 2^32-1 的值。在二进制文件上使用fgets 没有什么意义,与ASCII '0' 字符进行比较也没有什么意义,因为内容已经是二进制的,而不是ASCII 编码的......任务是随机混合不同的术语。根据文件中零的数量,“读取、排序、写入直到第一个零”或“读取、折叠零、排序、写入”可能会更快
  • 你还不如mmap文件,按降序排序,然后截断,去掉最后的零。您不想使用 fputc 一次读取一个字符,您正在处理 dword 元素。
  • fputc 破坏 RDI,因此您的重复调用需要在每个 call 之前设置 RDI。这也是fputc 很烂的另一个原因。

标签: sorting assembly nasm glibc


【解决方案1】:

为了我自己的私心,当检测到 dword 零值时,内存区域被“折叠”的示例:

使用 NASM 在 linux 中构建目标 ELF64 可执行文件:

nasm -f elf64 so_64b_collapseZeroDword.asm -l so_64b_collapseZeroDword.lst -w+all
ld -b elf64-x86-64 -o so_64b_collapseZeroDword so_64b_collapseZeroDword.o

对于调试器,我正在使用edb(从源代码构建)(可执行文件不执行任何用户可观察到的操作,当它正常工作时,它应该在调试器中运行,单步执行指令并拥有内存查看.data 段以了解值是如何在内存中移动的)。


源文件so_64b_collapseZeroDword.asm

    segment .text

collapseZeroDwords:
; input (custom calling convention, suitable only for calls from assembly):
;   rsi - address of first element
;   rdx - address beyond last element ("vector::end()" pointer)
; return: rdi - new "beyond last element" address
; modifies: rax, rsi, rdi
; the memory after new end() is not cleared (the zeroes are just thrown away)!

; search for first zero (up till that point the memory content will remain same)
    cmp     rsi, rdx
    jae     .noZeroFound    ; if the (rsi >= end()), no zero was in the memory
    lodsd                   ; eax = [rsi], rsi += 4
    test    eax, eax        ; check for zero
    jne     collapseZeroDwords
; first zero found, from here on, the non-zero values will be copied to earlier area
    lea     rdi, [rsi-4]    ; address where the non-zero values should be written
.moveNonZeroValues:
    cmp     rsi, rdx
    jae     .wholeArrayCollapsed    ; if (rsi >= end()), whole array is collapsed
    lodsd                   ; eax = [rsi], rsi += 4
    test    eax, eax        ; check for zero
    jz      .moveNonZeroValues      ; zero detected, skip the "store" value part
    stosd                   ; [rdi] = eax, rdi += 4 (pointing beyond last element)
    jmp     .moveNonZeroValues

.noZeroFound:
    mov     rdi, rdx        ; just return the original "end()" pointer
.wholeArrayCollapsed:       ; or just return when rdi is already set as new end()
    ret

global _start
_start:     ; run some hardcoded simple tests, verify in debugger
    lea     rsi, [test1]
    lea     rdx, [test1+4*4]
    call    collapseZeroDwords
    cmp     rdi, test1+4*4      ; no zero collapsed

    lea     rsi, [test2]
    lea     rdx, [test2+4*4]
    call    collapseZeroDwords
    cmp     rdi, test2+3*4      ; one zero

    lea     rsi, [test3]
    lea     rdx, [test3+4*4]
    call    collapseZeroDwords
    cmp     rdi, test3+3*4      ; one zero

    lea     rsi, [test4]
    lea     rdx, [test4+4*4]
    call    collapseZeroDwords
    cmp     rdi, test4+2*4      ; two zeros

    lea     rsi, [test5]
    lea     rdx, [test5+4*4]
    call    collapseZeroDwords
    cmp     rdi, test5+2*4      ; two zeros

    lea     rsi, [test6]
    lea     rdx, [test6+4*4]
    call    collapseZeroDwords
    cmp     rdi, test6+0*4      ; four zeros

    ; exit back to linux
    mov     eax, 60
    xor     edi, edi
    syscall

    segment .data
    ; all test arrays are 4 elements long for simplicity
        dd 0xCCCCCCCC       ; debug canary value to detect any over-read or over-write
test1   dd 71, 72, 73, 74, 0xCCCCCCCC
test2   dd 71, 72, 73,  0, 0xCCCCCCCC
test3   dd  0, 71, 72, 73, 0xCCCCCCCC
test4   dd  0, 71,  0, 72, 0xCCCCCCCC
test5   dd 71,  0, 72,  0, 0xCCCCCCCC
test6   dd  0,  0,  0,  0, 0xCCCCCCCC

我试图广泛评论它以展示它在做什么/为什么/如何做,但请随时询问任何特定部分。代码的编写考虑到了简单性,因此它没有使用任何激进的性能优化(如矢量化搜索第一个零值等)。

【讨论】:

  • in-place 解决方案的有趣实现。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-05-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多