【问题标题】:Question regarding Assembly and computer programs关于装配和计算机程序的问题
【发布时间】:2011-08-14 13:46:22
【问题描述】:

我看了这篇文章:http://en.wikipedia.org/wiki/Assembly_language

上面写着:

以告诉 x86/IA-32 处理器的指令为例 将立即数 8 位值移动到寄存器中。二进制代码为 该指令是 10110 后跟一个 3 位标识符 注册使用。 AL 寄存器的标识符是 000,所以 以下机器代码将数据加载到 AL 寄存器中 01100001.[4]

10110000 01100001

它解释了如何更容易将其编写为:

MOV AL, 61h       ; Load AL with 97 decimal (61 hex)

现在这是我的问题。

那么,计算机程序/可执行文件只是二进制数据(0 和 1)?

当使用 OllyDbg 之类的反汇编程序查看时,它只是试图将那些 0 和 1 恢复为某种汇编(英特尔?)语言,并且输出大部分是正确的?

如果我的 SSD 上有这个 10110000 01100001 程序并且我编写了一个 C#/PHP/wtvr 应用程序来读取文件的内容并将它们输出为位,我会看到这些确切的 10110000 01100001 数字吗?

操作系统如何进行实际的“执行”?它如何告诉处理器“嘿,获取这些位并运行它们”?我可以直接在 C#/C++ 中这样做吗?

【问题讨论】:

标签: assembly linker executable


【解决方案1】:

那么,计算机程序/可执行文件只是二进制数据(0 和 1)?

是的,比如图片、视频和其他数据。

当使用 OllyDbg 之类的反汇编程序查看时,它只是试图将那些 0 和 1 恢复为某种汇编(英特尔?)语言,并且输出大部分是正确的?

是的,在这种情况下,它总是正确的,因为在 16 位、32 位和 64 位中,mov al, 61h 总是组装到 0xB0 0x61(在 Intel 64 and IA-32 Architectures Software Developer's Manuals 和其他通常写为 B0 61 的地方)模式。注意0xB0 0x61 = 0b10110000 0b01100001

您可以在第 2A 卷中找到不同指令的编码。例如这里是“B0+ rb MOV r8, imm8 E Valid Valid Move imm8 to r8”。在第 3-644 页。

其他指令有不同的含义取决于它们是在 16/32 还是 64 位模式下解释的。考虑一下这个短字节序列:66 83 C0 04 41 80 C0 05

在 16 位模式下,它们的意思是:

00000000  6683C004          add eax,byte +0x4
00000004  41                inc cx
00000005  80C005            add al,0x5

在 32 位模式下,它们的意思是:

00000000  6683C004          add ax,byte +0x4
00000004  41                inc ecx
00000005  80C005            add al,0x5

最后是 64 位模式:

00000000  6683C004          add ax,byte +0x4
00000004  4180C005          add r8b,0x5

因此,在不知道上下文的情况下,不能总是正确地反汇编指令(这甚至没有考虑到除了代码之外的其他内容可以驻留在文本段中,并且代码可以做一些令人讨厌的事情,例如动态生成代码或自行生成代码。修改)。

如果我的 SSD 上有这个 10110000 01100001 程序,并且我编写了一个 C#/PHP/wtvr 应用程序来读取文件的内容并将它们输出为位,我会看到这些确切的 10110000 01100001 数字吗?

是的,如果应用程序包含mov al, 61h 指令,则文件将包含字节0xB00x61

操作系统如何进行实际的“执行”?它如何告诉处理器“嘿,获取这些位并运行它们”?我可以直接在 C#/C++ 中这样做吗?

将代码加载到内存后(并且内存已正确设置权限),它可以跳转到或调用它并让它运行。即使操作系统只是另一个程序,您也必须意识到它是一个特殊程序,因为它首先到达处理器!它以特殊的主管(或管理程序)模式运行,允许它处理普通(用户)程序不允许的事情。就像设置 preemptive multitasking 一样,它可以确保自动生成流程。

第一个处理器还负责唤醒多核/多处理器机器上的其他内核/处理器。请参阅this SO 问题。

要调用您自己直接在 C++ 中加载的代码(我认为在 C# 中不使用不安全/本机代码是不可能的)需要特定于平台的技巧。对于 Windows,您可能想查看 VirtualProtect,在 linux 下查看 mprotect(2)。或者更实际地来自使用this process for Windowsmmap(2) 映射的文件。

【讨论】:

    【解决方案2】:

    这是很多问题:

    是的,计算机程序/可执行文件只是二进制数据 0/1。

    是的,反汇编程序试图理解 0/1s...并且它使用有关文件格式的附加知识(EXE 通常遵循 PE 规范,COM 是不同的规范等)和二进制文件应该使用的操作系统运行和可用的 API 等。

    这两个字节(一个带参数的指令)的读取方式与此完全相同……尽管这取决于它们所属的程序 - 正如所提到的,不同的文件类型遵循不同的规范。

    通常操作系统会根据规范加载文件并处理其内容 - 例如重新排列一些内存区域等。然后它将包含可执行代码的内存区域标记为可执行,并对所谓的入口点的第一条指令的地址执行 JMP 或 CALL(这再次取决于手头的文件格式/规范)。

    在 C# 中,您不会将汇编作为一种语言处理,而是使用“字节码”(IL 指令)...您可以通过框架方法等发出或加载 thos。 在 c++ 中,如果你真的想要,你可以直接处理汇编,但这不是可移植的,并且可能会变得复杂......所以你通常只在获得真正值得的时候才这样做(比如所需的性能提升 10 倍)。

    【讨论】:

    • 非常有趣。 Unix OS 使用的可执行格式规范是什么?因此,当操作系统执行文件时,JPMCALL 会不会阻塞所有其他进程?或者其他进程的可执行文件yield 处理器时间?
    • Unix 操作系统中使用的格式不是 100% 相同的,因为有几种风格(以前是 COFF,最近是 ELF -seee en.wikipedia.org/wiki/Executable_and_Linkable_Format)... 没有操作系统有所谓的调度程序,它需要为每个进程提供一段处理器时间......并且这些进程在不同的线程上启动,因此操作系统不会被阻塞等。处理器架构有助于实现这一点,例如通过让操作系统获得更多特权“ ring”(x86 32 和 64 位处理器...)。
    【解决方案3】:

    那么,计算机程序/可执行文件只是二进制数据(0 和 1)?

    是的。

    当使用 OllyDbg 之类的反汇编程序查看时,它只是试图还原 那些 0 和 1 回到了一些汇编(英特尔?)语言和 输出基本正确?

    是的。除了如果二进制数据代表反汇编程序设计的 cpu 代码,输出将完全正确,而不仅仅是“大部分”正确。

    如果我的 SSD 上有这个 10110000 01100001 程序,我写了一个 读取文件内容并输出的 C#/PHP/wtvr 应用程序 它们是位,我会看到这些确切的 10110000 01100001 数字吗?

    是的

    操作系统如何进行实际的“执行”?怎么做的 告诉处理器“嘿,获取这些位并运行它们”?

    操作系统只是一个程序,就像其他程序一样,它是在处理器上执行的指令。简单地说,当操作系统执行代码时,它所做的只是跳转到代码所在位置的起始地址,因此处理器现在开始执行该位置的任何代码。

    我可以直接在 C#/C++ 中执行此操作吗?

    不要忘记,C 在执行时会被编译为汇编语言,并且在执行时,它与可以在给定 CPU 上运行的任何其他程序没有什么不同。是的,例如,您可以使用内联汇编跳转到给定的内存位置并执行代码。

    【讨论】:

    • 谢谢,我对 Yahia 有同样的问题:当操作系统执行文件时,JPMCALL 会不会阻塞所有其他进程?或者其他进程的可执行文件yield 处理器时间?而且由于操作系统只是另一个程序(1和0),操作系统不会也阻塞吗?多核 CPU 有何不同?
    • 具体情况取决于您所运行的硬件的确切性质,对于简单的堆栈溢出响应来说,这个问题实在是太宽泛了。
    猜你喜欢
    • 1970-01-01
    • 2010-12-08
    • 2014-06-22
    • 2021-07-06
    • 2011-05-24
    • 1970-01-01
    • 1970-01-01
    • 2010-12-31
    • 2021-06-18
    相关资源
    最近更新 更多