关于汇编和计算机程序的问题



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

它说:

以告诉x86/IA-32处理器的指令为例将一个直接的8位值移到寄存器中。的二进制代码这个指令是10110,后面跟着一个3位的标识符注册使用。AL寄存器的标识符是000,所以下面的机器码将数据装入人工智能寄存器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++中这样做吗?

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

是的,像图像,视频和其他数据。

当使用像OllyDbg这样的反汇编器查看时,它只是试图将那些0和1还原为一些汇编(英特尔?)语言,输出大多是正确的?

是的,在这种确切的情况下,它总是正确的,因为mov al, 61h总是在16位,32位和64位模式下组装为0xB0 0x61(在Intel 64和IA-32架构软件开发人员手册和其他通常写成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上有这个1011000001100001程序,并且我写了一个c#/PHP/wtvr应用程序,读取文件的内容并将它们作为位输出,我会看到这些确切的1011000001100001数字吗?

是的,从某种意义上说,如果应用程序包含mov al, 61h指令,则文件将包含0xB00x61字节。

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

在将代码加载到内存(并且内存的权限设置正确)之后,它可以跳转到或调用它并让它运行。有一件事你必须意识到,尽管操作系统只是一个程序,但它是一个特殊的程序,因为它首先到达处理器!它以一种特殊的管理程序(或管理程序)模式运行,这种模式允许它做普通(用户)程序不允许做的事情。比如设置抢占式多任务,确保进程自动退出。

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

调用直接在c++中加载的代码(我不认为在c#中不诉诸不安全/本机代码是可能的)需要特定于平台的技巧。对于Windows,您可能想要查看VirtualProtect,在linux下查看mprotect(2)。或者可能更现实地从一个文件,它是使用这个进程的Windows或mmap(2)的linux映射。

有很多问题:

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

是的,反汇编程序试图理解0/1…它还使用了关于文件格式的额外知识(EXE通常遵循PE规范,COM是不同的规范等),二进制文件应该运行的操作系统和可用的api等。

这两个字节(一条带参数的指令)读起来就像这样…虽然这取决于它们是程序的一部分,但正如前面提到的,不同的文件类型遵循不同的规范。

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

在c#中,您不将汇编作为一种语言处理,而是使用"字节码"(IL指令)…你可以发出或通过框架方法加载这些。在c++中,如果你真的想,你可以直接处理汇编,但这是不可移植的,可能会变得复杂……因此,您通常只在获得真正值得的情况下才这样做(例如需要将性能提升10倍)。

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

是的。

当使用像OllyDbg这样的反汇编器查看时,它只是试图恢复这些0和1返回到一些汇编语言(Intel?)和输出大部分是正确的?

是的。除非二进制数据表示反汇编器所设计的cpu的代码,否则输出将是完全正确的,而不仅仅是"大部分"正确。

如果我的SSD上有这个10110000 01100001程序,我写一个c#/PHP/wtvr应用程序,读取文件的内容并输出我能看到这些精确的10110000 01100001数字吗?

是的

操作系统是如何进行实际的"执行"的?它是怎么做到的?告诉处理器"嘿,把这些比特取下来,然后运行它们"?

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

我可以直接在c#/c++中做吗?

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

最新更新