【问题标题】:How to split / truncate a string variable value in Assembly?如何在Assembly中拆分/截断字符串变量值?
【发布时间】:2017-01-26 05:16:30
【问题描述】:

我目前正在做一个项目,为了存储,我想在汇编中删除一个变量,并(可选)将其设为寄存器的值,例如 eax。

我需要使用 Intel 语法与 NASM 一起工作的代码。

例如,如果变量“msg”设置为“29ak49”,我想取其中的一部分,例如“9ak4”,并将其放入寄存器或类似的东西中。

【问题讨论】:

  • 通过添加空格和评论进行编辑,因为我在主页上看不到我的条目,即使在最近的页面上也是如此。
  • 它没有出现...
  • 刚刚浏览并找到了这个。我猜你运气不错。
  • 你能定义切断是什么意思吗?
  • 是的,所以我错过了从 4B 保存到 199kiB 保存的飞跃。要么你在数据中有一些非常重复的模式(并且使用像 LZMA 这样的压缩可能会“足够好”,但更通用),或者我看不出将部分值存储在寄存器中对你有什么帮助。

标签: string assembly x86 nasm


【解决方案1】:

正如 cmets 中提到的Peter Cordes,您始终可以将空终止符 (0) 添加到现有字符串的缓冲区中以右截断;如果您不介意修改原始字符串数据。

下面的示例将在不修改原始字符串的情况下检索子字符串。


如果你有一个变量的地址,并且你知道要在哪里截断它,你可以取数据的起始位置的地址,并在左截断处添加一个偏移量。要右截断,您可以从新偏移量中读取任意数量的字符。

例如在x86:

    msg db '29ak49'         ; create a string (1 byte per char)


    ;; left truncate
    mov esi, msg            ; get the address of the start of the string
    add esi, OFFSET_INTO_DATA ; offset into the string (1 byte per char)

    ;; right truncate
    mov edi, NUM_CHARS      ; number of characters to take

    .loop:                  
    movzx eax, byte [esi]   ; get the value of the next character
    ;; do something with the character in eax   
    inc esi
    dec edi
    jnz .loop

    ;; end loop

编辑:

以下是作为 32 位 Linux 应用程序的可运行测试实现,它打印出基于 OFFSET_INTO_DATANUM_CHARS 选择的子字符串(注意:算法相同,但寄存器已更改):

        section .text
        global _start


_start:

        ;; left truncate
        mov esi, msg            ; get the address of the start of the string
        add esi, OFFSET_INTO_DATA ; offset into the string (1 byte per char)

        ;; right truncate
        mov edi, NUM_CHARS      ; number of characters to take

        .loop:                  
        mov ecx, esi            ; get the address of the next character
        call print_char_32        
        inc esi
        dec edi
        jnz .loop

        jmp halt


;;; input:      ecx      -> character to display
print_char_32:
        mov edx, 1              ; PRINT
        mov ebx, 1              ;
        mov eax, 4              ;
        int 0x80                ;
        ret


halt:
        mov eax, 1              ; EXIT
        int 0x80                ;
        jmp halt


section .data

       msg db '29ak49'         ; create a string (1 byte per char)


       OFFSET_INTO_DATA EQU 1
       NUM_CHARS EQU 3

编译:

nasm -f elf substring.asm ; ld -m elf_i386 -s -o substring substring.o

【讨论】:

  • 小改进:不要写jz .end / jmp .loop,而是写jnz .loop,这样当分支未被采用时,你会以失败退出。这样效率更高,并且是循环的常用习语。
  • 另外,dec ecx 已经根据结果设置了 ZF。所以dec ecx / jnz 是循环的惯用语(see this answer)。 mov eax, [ebx] 加载 4 个字节。也许你的意思是movzx eax, byte [ebx]。您也可以使用inc ebx 代替add ebx, 1
  • 还值得一提的是,您可以通过存储一个零字节来右截断来标记字符串的新结尾,假设它是一个隐式长度的 C 样式字符串,您可以修改。
  • 哦,nvm,你使用了 pusha/popa。这太可怕了,至少对于效率而言,尽管一次调用 write() 一个字符甚至会相形见绌。我想这是循环部分字符串的一个很好的例子,但对于其他事情来说是一个不好的例子。
  • 顺便说一句,Linux int 0x80 系统调用 ABI 保留了除 EAX 之外的所有寄存器,因此您可以设置循环以将 ESI 和 ESI 用于计数器或其他内容。或者将 ECX 中的指针递增到另一个寄存器中的限制。然后,您可以在循环中进行系统调用,而无需进行大量的寄存器改组。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-03-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-24
  • 2015-12-27
  • 1970-01-01
相关资源
最近更新 更多