8B EC 56 8B F4 68 00 70 40 00 FF 15 BC 82 40
像上面这样的一个序列可以用各种方式进行分段,每个段都可以翻译成相应的汇编指令,但是每个二进制可执行文件都有其唯一的 DEFINITE 汇编,避免歧义的数学原理是什么?
更新
得票最多的答案实际上根本没有回答我的问题。
8B EC 56 8B F4 68 00 70 40 00 FF 15 BC 82 40
像上面这样的一个序列可以用各种方式进行分段,每个段都可以翻译成相应的汇编指令,但是每个二进制可执行文件都有其唯一的 DEFINITE 汇编,避免歧义的数学原理是什么?
更新
得票最多的答案实际上根本没有回答我的问题。
知道你的出发点。
换句话说,给定指令的特定起始字节,指令结束的位置是明确的,从而为您提供下一条指令的起始字节并允许您继续。给定一个任意的内存块,在不知道第一条指令从哪里开始的情况下,不可能将其分解为单独的指令。
从更数学的角度来看,没有有效指令的字节是另一个有效指令的前缀。所以如果ab
是有效的,那么你知道它ab cd
不能是有效的,所以ab
必须是一条指令并且cd
是下一条指令的开始。
如果我正确理解了您的问题,那么您正在尝试理解为什么
8B EC 56 8B F4 68 00 70 40 00 FF 15 BC 82 40
可以拆分为
8BEC 568BF4 68007040 00FF 15BC 8240
不如说,
8B EC568B F4 68007040 00FF 15BC 8240
这完全由您的体系结构的 ISA 指定。该文档准确地描述了指令是如何从一系列字节中唯一构造的。
为了使 ISA 格式良好,一个字节序列最多可以对应一个解码指令序列(如果有无效指令,可能会更少)。
更具体一点,让我们以 x86 为例:如果您想知道每个字节对应的内容,请看这里。
您会看到,例如,以 00 开头的指令是一个加法(附加参数在下一个字节中,具有特定的编码)。
您还将看到一些值实际上是修改以下指令的前缀(0F - 扩展操作码空间的前缀,26、2E、36、3E、64、65、66、67、F0、F2、F3),以及根据确切的以下说明,其中一些具有不同的含义。这些不是操作码,但它们可以改变操作码参数的编码,或者引入一个全新的操作码空间(例如 SSE 使用 0F)。
总的来说,x86 编码非常复杂,感谢反汇编程序。
首先,您必须区分RISC和CISC架构。
在 RISC 架构中,您通常具有相同大小的指令,因此不会出现歧义。例如,您的 CPU 将为每条指令获取 4 个字节,并且由于它必须从某个地方开始(您的 CPU 不只是像您提供的那样的序列,它肯定会有一个起点)一旦它有正确的对齐不会出现问题。
CISC指令集发生的事情本质上是相同的:从程序的入口点开始,它将根据您的操作码获取指令。它不需要知道如何在数学上区分歧义,因为它不会发生它只是不知道下一条指令多长时间或最后一条指令在哪里完成。
所以问如何分隔每条指令就像问如何分隔每个单词
桌子
没有数学证明,但你知道哪些字母是正确的,哪些没有意义。上一句包含“son”,但您知道它是从“is on”获得的。如果没有有意义的短语,您将无法这么说,但是您的 CPU 只执行有意义的程序,那有什么意义呢?
因此,如果 CPU 可以处理前一句,它将找到第一个有意义的指令“the”,然后“pen”、“is”、“on”和“son”无论如何都无法识别。
编辑:
需要澄清的是,在 CISC 体系结构中,您必须确保没有歧义的唯一限制是避免使用作为另一个前缀的指令。让我们假设一个由字母 az 而不是十六进制数字组成的有限字母表(仅出于实际目的)。
如果程序计数器指向
abbcbcaabdeffabd
你可以有abb
一个完整的指令。在那种情况下ab
就不是一条有效的指令,否则CPU不知道在哪里停止,同时abbc
也不能是一条指令,否则可能会产生问题。例如,保持它ca
是下一个指令,c
不能也cbc
不能。
您可以将此论证扩展到整个字符串。你会看到,如果 CPU 发现自己处于二进制的下一个字节指向指令的第一个字节的状态,并且没有指令是其他指令的前缀,那么程序计数器处于下一个状态将指向下一条正确指令的第一个字节。
如果您在十六进制编辑器中打开二进制文件,复制部分数据并粘贴到反汇编程序中,您很可能不会复制完整的指令。让我们用你的例子..在 Windows XP 32bits SP3 英语中,如果我组装8B EC 56 8B F4 68 00 70 40 00 FF 15 BC 82 40
我会得到:
Hex dump Command
8BEC mov ebp,esp
56 push esi
8BF4 mov esi,esp
68 00704000 push 407000
FF15 BC824000 call dword ptr ds:[4082bc]
正如你所看到的,它与下面其他人的答案完全不同......
现在让我们假设您在开始8B EC 56 8B F4 68 00 70 40 00 FF 15 BC 82 40
时添加了操作码,而不是组装C0
C0 8B EC 56 8B F4 68 00 70 40 00 FF 15 BC 82 40
现在检查下面我们的指令中单个字节的作用:
Hex dump Command
C08B EC568BF4 68 ror byte ptr ds:[ebx+f48b56ec],68
0070 40 add byte ptr ds:[eax+40],dh
00FF add bh,bh
15 BC824000 adc eax,4082bc
如您所见,它已完全修改!
处理器知道从哪里开始以及操作码指令要为指令采用哪些参数。在第一个示例中,我们的第一个操作码是8B
它知道它后面可以跟另一个字节。如果这个字节是EC
这样,指令在这里结束,它意味着mov ebp, esp
。
在我们的第二个示例中C0
,它以另一个字节开头,后面可以跟另一个字节,这意味着另一个指令。然后C08B
是指令,EC568BF4 68
是参数。
想象一下,处理器内部有一个巨大的(但纳米级的)电路,其行为类似于 ifs(条件)链,根据十六进制码(或操作码)的值,它知道“去哪里”或“如何表现” .
听起来你的问题的答案是有点轻率的“知道你的起点”,但也许你想要更冗长的东西。给定你的字符串:
8B EC 56 8B F4 68 00 70 40 00 FF 15 BC 82 40
和一个起点(假设 8B 是您的起点)只有一种可能的字节解释。
所以假设一个操作是 8B EC 56 8B (取决于您的操作长度等),那么 NEXT 操作是 F4 68 ......在这种情况下,机器不可能尝试解释操作 56 8B F4 68 因为在那一点上没有任何操作结束。
现在,如果你的起点是 56,那么你可以得到那个组,但不能得到前面提到的任何一个。
您的内存布局非常具体,起点/跳转点是准确且无情的——它们与代码本身一样需要。
不要将线性尝试反汇编与代码的执行顺序混淆。二进制文件按执行顺序解码,从已知位置开始。除了出于各种原因的故意黑客攻击之外,没有任何歧义。
尝试为可变字长指令集编写反汇编程序。归根结底,它必须按执行顺序完成,即使在那里你也只能反汇编一些程序,因为一些分支可以基于运行时计算的地址。现代编译器生成的代码比旧的手工组装代码要好得多。例如,在一个旧的单口街机游戏中,条件分支之前有一条指令,该指令保证只满足一个条件(为什么会出现在其中?我们永远不会知道),条件分支之后的数据类似于这样一来,您就会与其他操作码发生冲突。
试图击败反汇编程序的旧 dos 程序将具有自修改代码,一条指令会提前一到两条指令修改另一条指令,如果单步修改会发生,但如果全速运行,则指令已经在管道中获取,并且未使用内存中修改/损坏的一个。相当巧妙的把戏。
无论如何,您的问题的答案是不要按线性顺序查看字节,而是按执行顺序查看它们,从向量表中的复位和其他向量定义的地址开始。
您列出的序列恰好显示 1 个数字。在二进制中,它是
100010111110110001010110100010111111010001101000000000000111000001000000000000001111111100010101101111001000001001000000
. 在十进制中,它是726522768938664460674442126658667072
. 这些都是编写完全相同的值的不同方式。特定架构的 ISA 会将位划分为字段并赋予它们意义。大多数处理器很容易获得手册,这些手册描述了分配给这些字段中每个位的含义。
其他地方也可能有一些关于什么是有效起始地址的线索。总是有一个复位向量地址,通常还有中断向量地址,它们都必须是代码块的有效起点。更有用的是,如果您在其他地方遇到跳转或调用指令,该指令引用了您尝试解码的块中的地址,那么这将为您提供另一个起始地址。
我想我明白你的担心,据我所知,它是正确的——如果程序计数器被打乱,导致无效指令或意外指令被执行,程序可能会崩溃。是的,如果你遇到一个数据块并尝试执行它。至少后者可以通过使用哈佛架构来避免,其中代码和数据位于单独的存储空间中,并且可能是不同的位宽。
也许你觉得考虑另一个方向很有趣:你将如何设计你的代码以便于其他人分割?您可以要求开始序列的字节的最高有效位为零,而序列中间的字节为 1,就像 UTF-8 一样。然后,如果你从一个随机位置开始——假设你知道字节在哪里——很容易找到下一个序列。更进一步,您将如何编码纯比特流,以便易于找到序列的开头。这样的编码浪费了多少位?
既然你问了数学,我认为相关主题是“编码理论”、“可变长度代码”或“前缀代码”。
如何在碱基对序列中找到基因?