汇编 (x86): <label> db 'string',0 除非有跳转指令,否则不会执行

Posted

技术标签:

【中文标题】汇编 (x86): <label> db \'string\',0 除非有跳转指令,否则不会执行【英文标题】:Assembly (x86): <label> db 'string',0 does not get executed unless there's a jump instruction汇编 (x86): <label> db 'string',0 除非有跳转指令,否则不会执行 【发布时间】:2021-11-16 07:47:40 【问题描述】:

我一直在努力理解为什么以下程序集没有正确转储“HELLO_WORLD”的内容。

; Explicitly set 16-bit
[ BITS 16 ]
[ ORG 0x7C00 ]

; Create label for hello world string terminated by null.
HELLO_WORLD db 'hello world', 0

start:
    ; Move address of HELLO_WORLD into si
    mov SI, HELLO_WORLD
    call print_string

    ; Continue until the end of time
    jmp $

print_string:
    loop:
        ; Retrieve value stored in address at si
        mov al, [SI]
        mov ah, 0x0E
        cmp al, 0
        ; Finish execution after hitting null terminator
        je  return
        INT 0x10
        ; Increment contents of si (address)
        inc SI
        jmp loop

    return:
        ret

; boot loader length *must* be 512 bytes.
times 510-($-$$) db 0
dw 0xAA55

最后,我发现如果我们不执行(让它不编码)标签,那么它就可以正常工作。

jmp start
HELLO_WORLD db 'hello world',0    

我发现最令人困惑的部分,查看十六进制转储,HELLO_WORLD 仍然在二进制文件中(在开始时 - 似乎没有其类型的区别)。

cat nojmp_boot.out

00000000  68 65 6c 6c 6f 20 77 6f  72 6c 64 00 be 00 7c e8  |hello world...|.|
00000010  02 00 eb fe 8a 04 b4 0e  3c 00 74 05 cd 10 46 eb  |........<.t...F.|
00000020  f3 c3 eb e8 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000030  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000001f0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 55 aa  |..............U.|
00000200

cat jmpboot.out

00000000  eb 22 68 65 6c 6c 6f 20  77 6f 72 6c 64 00 be 02  |."hello world...|
00000010  7c e8 02 00 eb fe 8a 04  b4 0e 3c 00 74 05 cd 10  ||.........<.t...|
00000020  46 eb f3 c3 eb e8 00 00  00 00 00 00 00 00 00 00  |F...............|
00000030  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000001f0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 55 aa  |..............U.|
00000200

检查前两个字节,我们可以看到 'e8 22' 是到地址 22 (http://net.cs.uni-bonn.de/fileadmin/user_upload/plohmann/x86_opcode_structure_and_instruction_overview.pdf) 的短跳转。

我的问题是:

为什么我们不能将“HELLO_WORLD”作为程序执行的一部分,就我而言,代码和数据之间没有区别?

我正在使用以下内容进行编译:

nasm -f bin -o boot.bin boot.asm && if [ $(stat -c "%s" boot.bin) -ne 512 ]; then x; fi && qemu-system-x86_64 boot.bin

【问题讨论】:

Related Segmentation fault when using DB (define byte) inside a function 在受保护模式下的操作系统下用户空间中的相同错误,而不是引导加载程序,因此症状略有不同。 (并详细说明常见的00 00 字节序列如何解码为内存目标添加)。 【参考方案1】:

执行从顶部开始。如果省略jmp start,那么字符 h 将被 CPU 解释为一条指令。你肯定看到这样不正确吧?

在我看来,代码和数据没有区别?

当我们考虑它们在二进制文件中的位置时,代码和数据之间没有区别。但是代码和数据仍然是两个完全不同的项目。代码是唯一可以由 CPU执行的代码。

【讨论】:

非常感谢 user3144770。出于好奇,我是否有任何理由无法获取 HELLO_WORLD 的地址(因为它是一个标签)并将其放入寄存器中,然后进行迭代? (我在想字节流) mov si, HELLO_WORLD 已经按照您的要求完成了。 (NASM 语法) 问题是,如果我再尝试使用:mov al, [SI] 它在 cmp al, 0 je return 处终止 我认为这没有问题。这正是您向我们展示的引导加载程序中所需要的。文本“hello world”必须在某个时间结束,这里通过附加一个零字节并检查它来完成。【参考方案2】:

由于您正在创建引导扇区,因此从生成文件的第一个字节开始执行。它不会从开始标签或其他任何地方开始。由于字符串“hello world”位于文件的开头,因此这些字节是首先执行的。这些字节被 CPU 解释为指令,而不是字符,并且它们被执行为它们被解码为的任何指令。

这里是被执行的指令:

7c00:   68 65 6c                push   0x6c65
7c03:   6c                      ins    BYTE PTR es:[di],dx
7c04:   6f                      outs   dx,WORD PTR ds:[si]
7c05:   20 77 6f                and    BYTE PTR [bx+0x6f],dh
7c08:   72 6c                   jb     0x7c76
7c0a:   64 00 be 00 7c          add    BYTE PTR fs:[bp+0x7c00],bh
7c0f:   e8 02 00                call   0x7c14
7c12:   eb fe                   jmp    0x7c12
7c14:   8a 04                   mov    al,BYTE PTR [si]
...

【讨论】:

谢谢 - 绝对有道理。还问了上面的以下问题:出于好奇,有什么原因我无法获取 HELLO_WORLD 的地址(因为它是一个标签)并将其放入寄存器中,稍后进行迭代? (我在想字节流)

以上是关于汇编 (x86): <label> db 'string',0 除非有跳转指令,否则不会执行的主要内容,如果未能解决你的问题,请参考以下文章

试图理解这个汇编 x86 代码

帮助理解 x86 内联汇编中的 DIV 指令

“呼叫 0x80482f0 <puts@plt>”?只需要澄清 x86 汇编中“hello world”程序中的一行代码

Android 逆向x86 汇编 ( 使用 IDA 解析 x86 架构的动态库文件 | x86 汇编语言分析 )

Android 逆向x86 汇编 ( 使用 IDA 解析 x86 架构的动态库文件 | x86 汇编语言分析 )

弹出 x86 堆栈时出现分段错误