8B EC 56 8B F4 68 00 70 40 00 FF 15 BC 82 40
上面的序列可以通过各种方式进行分段,每个段都可以转换为相应的汇编指令,但每个二进制可执行文件都有唯一的DEFINITE汇编,什么是避免歧义的数学原理?
更新
得票最多的答案实际上并没有回答我的问题。
答案 0 :(得分:13)
了解你的起点。
换句话说,给定指令的特定起始字节,指令结束的位置是明确的,从而为您提供下一条指令的起始字节并允许您继续。给定一个任意的内存块,如果不知道第一条指令的开始位置就不可能将其分解为单个指令。
从更加数学的角度来看,没有有效指令,其字节是另一个有效指令的前缀。因此,如果ab
有效,那么您就知道ab cd
无效,因此ab
必须是一条指令而cd
是下一条指令的开头。
答案 1 :(得分:3)
如果我正确理解你的问题,你就是在试图理解为什么
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为例:如果你想知道每个字节对应的内容,看一下here。
你会看到,例如以00开头的指令是一个add(附加参数在下一个字节中,具有特定的编码)。
您还会看到一些值实际上是修改以下指令的前缀(0F - 前缀以扩展操作码空间,26,2E,36,3E,64,65,66,67,F0,F2,F3 ),其中一些根据以下确切的指示采取不同的含义。那些不是操作码,但它们可以改变操作码参数的编码,或者引入一个全新的操作码空间(例如SSE使用0F)。
总的来说,感谢反汇编程序,x86编码非常复杂。
答案 2 :(得分:2)
首先,您必须区分 RISC 和 CISC 架构。
在RISC架构中,您通常会使用相同大小的指令,因此无法呈现模糊性。你的CPU将为每条指令获取例如4个字节,因为它必须从某个地方开始(你的CPU没有像你提供的那样的序列,它肯定会有一个起点)正确的对齐没有问题。
CISC指令集的情况基本相同:从程序的入口点开始,它将根据您的操作码获取指令。它不需要知道如何在物理上区分歧义,因为它不会发生它只是不知道下一条指令有多长或最后一条指令完成的位置。
因此,询问如何分离每条指令就像是在询问如何分隔
中的每个单词thepenisonthetable
没有数学证明,但你知道哪些字母在一起是正确的,哪些字母没有意义。上一句包含“儿子”,但您知道它是从“正在进行”获得的。如果没有一个有意义的短语,你将无法这么说,但你的CPU只执行有意义的程序,那么重点是什么?
因此,如果CPU可以处理前一句话,它将找到第一个有意义的指令“the”,然后“pen”,“is”,“on”,并且“son”无论如何都不会被识别。 / p>
修改强>:
要清除,在CISC体系结构中,您必须确保不存在歧义的唯一禁令是避免使用作为另一个的前缀的指令。让我们假设一个由字母a-z组成的有限字母,而不是十六进制数字(仅用于实际目的)。
如果程序计数器指向
abbcbcaabdeffabd
你可以认为abb
是一个完整的指令。在这种情况下,ab
将不是有效指令,否则CPU无法知道停止的位置,同时abbc
也不能成为指令,否则可能会产生问题。保持它可以使ca
成为下一条指令,c
不能和cbc
。
您可以将此论证扩展到整个字符串。您将看到,如果CPU发现自己处于二进制的下一个字节指向指令的FIRST字节的状态,并且没有指令是其他指令的前缀,那么在下一个状态下程序计数器将指向下一个正确的指令的第一个字节。
答案 3 :(得分:1)
您列出的序列正好显示1个数字。在二进制文件中,它是
100010111110110001010110100010111111010001101000000000000111000001000000000000001111111100010101101111001000001001000000
。在十进制中,它是726522768938664460674442126658667072
。这些只是编写完全相同值的不同方式。特定架构的ISA将比特分成字段并赋予它们含义。大多数处理器都很容易获得手册,这些手册描述了分配给这些字段中每个位的含义。
答案 4 :(得分:1)
不要线性地混淆尝试用代码的执行顺序进行反汇编。二进制文件以执行顺序解码,从已知位置开始。除了出于各种原因的故意黑客之外,没有歧义。
尝试为可变字长指令集编写反汇编程序。在一天结束时,它必须按执行顺序完成,甚至在那里你只能反汇编一些程序,因为一些分支可以基于运行时计算的地址。现代编译器生成的代码比旧手动编译代码要好得多。例如,在一个旧的站立式街机游戏中,有一个条件分支,前面有一条指令,保证只满足其中一个条件(为什么那里有?我们永远不会知道),条件分支后面的数据类似于一个操作码这种方式会导致与其他操作码发生冲突。
尝试击败反汇编程序的旧dos程序会有自修改代码,一条指令会修改另一条指令前面一条或两条指令,如果单步执行修改会发生,但如果全速运行则指令已经在管道中获取,并没有使用内存中的修改/损坏的。非常巧妙的技巧。
无论如何,你的问题的答案是不要按线性顺序查看字节,从执行顺序查看它们,从复位和向量表中的其他向量定义的地址开始。
答案 5 :(得分:1)
听起来你的问题的答案是有点轻率的“了解你的起点”,但也许你想要一些更冗长的东西。鉴于你的字符串:
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,那么您可以获得该组,但无法获得前面提到的任何一个。
你的记忆布局是非常具体的,起点/跳跃点是准确无误的 - 它们必须与代码本身一样。
答案 6 :(得分:1)
如果在十六进制编辑器中打开二进制文件,复制一部分数据并粘贴到反汇编程序中,则很可能无法复制完整的指令。让我们用你的例子..在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(条件)链,根据十六进制代码(或操作码)的值,它知道“去哪里”或“如何”表现“。
答案 7 :(得分:0)
在其他地方可能还有一些关于什么是有效起始地址的线索。始终存在复位向量地址,并且通常存在中断向量地址,这些地址都必须是代码块的有效起始点。更有用的是,如果您在其他地方遇到跳转或调用指令,该指令引用了您尝试解码的块中的地址,那么这将为您提供另一个起始地址。
我认为我看到了你的担心,据我所知它是正确的 - 如果程序计数器被一个人惹恼并导致无效指令或意外指令被执行,程序可能会崩溃。是的,如果您遇到数据块并尝试执行该操作,也是如此。至少后者可以通过使用哈佛架构来避免,其中代码和数据位于单独的存储空间中,并且可以是不同的位宽。
答案 8 :(得分:0)
也许您觉得考虑另一个方向很有趣:您如何设计代码以便为其他人轻松划分?您可以要求启动序列的字节的最高有效位为零,并且序列中间的那些位为1,就像UTF-8那样。然后,如果你从一个随机位置开始 - 假设你知道字节在哪里 - 很容易找到下一个序列。更进一步,您将如何编码纯比特流,以便很容易找到序列的开头。这种编码浪费了多少比特?
由于您询问了数学,我认为相关主题是“编码理论”,“可变长度代码”或“前缀代码”。
如何在一系列碱基对中找到基因?