RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 1571619
Accepted
nx4n
nx4n
Asked:2024-03-15 01:55:17 +0000 UTC2024-03-15 01:55:17 +0000 UTC 2024-03-15 01:55:17 +0000 UTC

为什么我们需要 .org 0x7C00 指令来从头开始运行代码?从磁盘的第一个扇区运行代码

  • 772

许多消息来源写道,有必要在汇编代码中插入一个指示地址的指令.org 0x7C00,据说代码从该地址开始执行(更准确地说,BIOS从磁盘的第一个扇区进入那里)。该指令存在于 Linux 内核源代码和许多其他源代码中,例如https://github.com/fffaraz/bootloader/blob/master/bootloader.asm(顺便说一句,nasm 立即生成一个文件512字节,也不清楚为什么,只有一个猜测:它被自动“trunked”。这个“顺便说一下”指的是链接后的文本)。

理论上说:需要设置16位模式,BIOS读取第一个扇区,分别检查511和512字节是否存在0x55和0xAA,如果检查成功,则将代码放置在地址处0x0000:0x7C00 并开始执行。

我只是想在启动时在屏幕上显示“X”,这是代码和后续步骤:

.org 0x7C00
.code16
start:
    mov $'X', %al
    mov $0xE, %ah
    int $0x10
    cli
    hlt
    . = start + 510
    .byte = 0x55
    .byte = 0xAA

我编译:

as -o boot.o boot.S

我得到 objdump -D

boot.o:     file format elf64-x86-64


Disassembly of section .text:

0000000000000000 <_start-0x7c00>:
    ...

0000000000007c00 <_start>:
    7c00:   b0 58                   mov    $0x58,%al
    7c02:   b4 0e                   mov    $0xe,%ah
    7c04:   cd 10                   int    $0x10
    7c06:   fa                      cli
    7c07:   f4                      hlt
    ...
    7dfc:   00 00                   add    %al,(%rax)
    7dfe:   55                      push   %rbp
    7dff:   aa                      stos   %al,%es:(%rdi)

Disassembly of section .note.gnu.property:

0000000000000000 <.note.gnu.property>:
   0:   04 00                   add    $0x0,%al
   2:   00 00                   add    %al,(%rax)
   4:   20 00                   and    %al,(%rax)
   6:   00 00                   add    %al,(%rax)
   8:   05 00 00 00 47          add    $0x47000000,%eax
   d:   4e 55                   rex.WRX push %rbp
   f:   00 02                   add    %al,(%rdx)
  11:   00 01                   add    %al,(%rcx)
  13:   c0 04 00 00             rolb   $0x0,(%rax,%rax,1)
    ...
  1f:   00 01                   add    %al,(%rcx)
  21:   00 01                   add    %al,(%rcx)
  23:   c0 04 00 00             rolb   $0x0,(%rax,%rax,1)
  27:   00 01                   add    %al,(%rcx)
  29:   00 00                   add    %al,(%rax)
  2b:   00 00                   add    %al,(%rax)
  2d:   00 00                   add    %al,(%rax)

我删除 .note.gnu.property

strip --remove-section=.note.gnu.property boot.o

关联:

ld --oformat binary -o boot boot.o

输出是一个大小为 32256 字节的文件。呵呵,当然,代码是从0x7C00开始的!查看该文件的十六进制转储,您可以看到该“地址”之前有零。那么问题来了,人们如何将这个文件写入第一个 512 字节扇区呢?当然没办法,有人不告诉什么,qemu-system-i386 -drive format=raw,file=boot他们甚至不会运行这个文件,前512字节中不会有代码

如果我们删除.org 0x7C00,生成的文件大小将恰好为 512 字节。这个文件已经可以启动qemu了。

如何在计算机启动时将控制权从 USB 转移到代码。我这样做:

为此,我使用 dd 实用程序。团队:

dd if=boot of=/dev/sdb bs=512 count=1 conv=notrunc

一切都被记录下来,hexdump也适用于/dev/sdb,前512的内容是相同的。qemu可以运行/dev/sdb

在BIOS中,我禁用安全模式,启用CSM支持(毕竟,uefi已经无处不在),准备好,转到启动选项卡,BIOS看到这个闪存驱动器(顺便说一下,KingstonDatatraveler 3.0),选择它,启动它,打印出令人垂涎的“X”符号。该代码无需 .org 0x7C00 即可运行。

问题:为什么需要这个指令?

更新:

现在我将0xAA更改为0xAF,代码仍然可以读取并运行。我是这样理解的:BIOS(没有uefi)仅将第一个扇区的前440字节复制到RAM中(https://wiki.archlinux.org/title/Arch_boot_process),这意味着它不会读取后续扇区和0x55和 0xAA 并不重要,它们是 uefi 所需要的。

问:我理解的对吗,或者是怎么回事?

ассемблер
  • 1 1 个回答
  • 50 Views

1 个回答

  • Voted
  1. Best Answer
    PRODIGY
    2024-03-15T15:14:20Z2024-03-15T15:14:20Z

    问题:为什么需要这个指令?

    汇编指令ORG将跳转指令代码中的基址偏移设置为“绝对地址”,例如call和jmp。如果代码中没有,则不必为二进制文件指定它。要了解细节,您需要记住编译器实际上如何编码转换?

    x16/32 模式下的处理器使用 2 种形式的跳转 - 通过绝对地址和相对地址。对于 x64,不再有绝对转换,所有转换都是 RIP 相对的,具有 32 位目标操作数。Intel对该指令的描述jmp如下:

      1.近(near)——转换到当前CS段内的指令(段内)。
      2. Short(短)——与Near相同,但跳转范围限制为距离当前(E)IP值+/-127字节。
      3.远(far)——转移到位于与当前CS不同的段中的指令,有时称为段间。
      4. 任务切换(任务切换)——转换到位于另一个任务中的指令。

    这里Near(操作码E9h)和Short(操作码EBh)是“相对转换”。它们之间的区别在于,Near 具有 2 字节操作数,而 Short 具有 1 字节有符号值的形式。如果该字节为负值,则从当前字节向后跳转(E)IP,如果为正值,则向前跳转。至于FAR(操作码 EAh),这始终是到“绝对地址”的转换。该操作数在 x16 模式下具有 2 字节值,在 x32 模式下具有 4 字节值。

    我们来看下面的加载器代码,并立即将其送入反汇编器(这里主要是无条件跳转jmp和条件跳转loop):

    org  0x7c00
    jmp  start
    
    var1   db  0
    var2   db  0
    
    ;//---------- Точка входа 
    start:  xor   ax,ax
            mov   ds,ax
            mov   ss,ax
            mov   sp,0x7c00
    
    ;//---------- Создадим цикл
            mov   cx,10
    @@:     mov   ax,word[var1]
            loop  @b
    
    ;//---------- Переход типа Short 
            jmp   @next
    @next:  nop
            nop
    
    ;//---------- Переход типа Far
            db    0xEA
            dw    @absolute
    @absolute:
            xor   ax,ax
            ret
    

    这是他的拆解外观:

    * Entry Point:
    
    00007C00:  EB02     jmp   00007C04    ;// Short, операнд(02) положительное значение - вперёд!
    00007C02:  0000     add   [bx+si], al
    
    00007C04:  31C0     xor   ax, ax
    00007C06:  8ED8     mov   ds, ax
    00007C08:  8ED0     mov   ss, ax
    00007C0A:  BC007C   mov   sp, 7C00
    
    00007C0D:  B90A00   mov   cx, 000A
    00007C10:  A1027C   mov   ax, [7C02]
    00007C13:  E2FB     loop  00007C10    ;// Операнд(FB) отрицательное значение - назад!
    
    00007C15:  EB00     jmp   00007C17    ;// Short, операнд положительное значение - вперёд!
    00007C17:  90       nop 
    00007C18:  90       nop 
    
    00007C19:  EA1C7C   jmp   00007C1C    ;// FAR, операнд абсолютный адрес!
    00007C1С:  31C0     xor   ax, ax
    00007C1E:  C3       ret
    

    通过相对寻址,操作数值存储相对于当前值的 +/- 127 范围内的偏移量EIP,该偏移量始终指向代码中的下一条指令,如上面反汇编中的 Short 操作码所示。但是在长距离 FAR 过渡的情况下我们看到了什么?编译器神奇地计算出正确的转换值0x7C1C。他从哪里得到的?

    这要归功于ORG指令- 多亏了它,编译器才能收到物理内存的有效“绝对地址” 。如果我们从源代码中删除 ORG,我们会得到下面的二进制文件,其中转换指向 space CS:001C,而 RAM 中的引导加载程序基址 = CS:7С00。

    00000010:  A10200        mov ax, [0002]
    00000013:  E2FB          loop  00000010
    
    00000015:  EB00          jmp   00000017
    00000017:  90            nop 
    00000018:  90            nop 
    
    00000019:  EA1C00        jmp   0000001C
    0000001С:  31C0          xor   ax,ax
    0000001E:  C3            ret
    
    • 2

相关问题

  • 难以在大会中创建 (for) 语句

  • call 如何使用堆栈?

  • STM32 中的中断/异常向量,汇编器

  • 寄存器和数据大小

  • 请帮助 turbo pascal 程序中的汇编程序

  • 举例说明将字符串输出到屏幕的过程 [关闭]

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    我看不懂措辞

    • 1 个回答
  • Marko Smith

    请求的模块“del”不提供名为“default”的导出

    • 3 个回答
  • Marko Smith

    "!+tab" 在 HTML 的 vs 代码中不起作用

    • 5 个回答
  • Marko Smith

    我正在尝试解决“猜词”的问题。Python

    • 2 个回答
  • Marko Smith

    可以使用哪些命令将当前指针移动到指定的提交而不更改工作目录中的文件?

    • 1 个回答
  • Marko Smith

    Python解析野莓

    • 1 个回答
  • Marko Smith

    问题:“警告:检查最新版本的 pip 时出错。”

    • 2 个回答
  • Marko Smith

    帮助编写一个用值填充变量的循环。解决这个问题

    • 2 个回答
  • Marko Smith

    尽管依赖数组为空,但在渲染上调用了 2 次 useEffect

    • 2 个回答
  • Marko Smith

    数据不通过 Telegram.WebApp.sendData 发送

    • 1 个回答
  • Martin Hope
    Alexandr_TT 2020年新年大赛! 2020-12-20 18:20:21 +0000 UTC
  • Martin Hope
    Alexandr_TT 圣诞树动画 2020-12-23 00:38:08 +0000 UTC
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5