组装的ASM代码可以产生多种可能的方式(偏移值除外)?

时间:2012-05-26 10:07:11

标签: assembly disassembly

我不太了解x86 ASM,但我对SHARP-z80很满意,而且我根据经验知道每条指令(助记符)都有相应的字节/字值,并且通过查看十六进制转储我可以“回读”我使用助记符编写的相同代码。

在另一个SO question中,有人声称​​有些情况下ASM指令没有转换为相应的二进制值,而是由汇编程序以不同的方式重新排列。 / p>

我特别关注拆解二进制文件会导致ASM代码与原始代码不同的情况。

换句话说,是否存在汇编代码与汇编代码的比率不是1:1的情况?

MikeKwan链接到另一个问题,其中GCC将修改内联ASM代码(在C项目中),但是,即使这是一个有趣的主题,它也没有回答这个问题,因为GCC是一个编译器,并始终尝试优化代码和内联ASM中断受到周围C代码的影响。

1 个答案:

答案 0 :(得分:3)

如果汇编程序设计者认为它有用,它可以替换具有其他有用属性的等效指令。

首先,机器具有可变长度值操作数字段。如果值/偏移量适合几种变体中的任何一种,则汇编程序通常用最短的变量替换。 (在这样的装配工中,通常能够强制特定尺寸)。对于涉及立即操作数和索引寻址的指令,情况也是如此。

许多机器都有PC相关偏移的指令,通常用于JMP,有时用于加载/存储/算术指令。在第一次通过期间遇到这样的指令的汇编器可以确定所述寻址的操作数在该insruction之前或者还没有看到该指令。如果在前面,汇编器可以选择短相对形式或长相对形式,因为它知道偏移。如果跟随,则汇编程序不知道大小,并且通常为pass2期间填充的指令选择较大的偏移量。类似地,通常有一些方法可以强制汇编程序选择简短形式。

有些机器没有长跳相关说明。在这种情况下,如果目标在jmp之前并且靠近,则汇编器将向后插入一个短jmp。如果目标在前面但很远,或者目标是前向引用,则汇编器可以在相反的分支条件上插入短相对jmp,其中目标超过下一条指令,然后是长绝对jmp。 (我个人建造了这样的装配工)。这确保了jmps总能达到目标。

关于这些技巧的好消息是,如果您进行反汇编,您仍然可以获得有效的汇编程序。

现在让我们转向那些会让您的反汇编程序混淆的内容。

如果机器具有加载/存储指令的短相对寻址,并且程序员显然指定加载常量或值很长的路径,则可以使用类似于文字操作数跳转的技巧。在这种情况下,汇编器会更改设备,以便在围绕该常量插入的短相对jmp之后引用文字或地址常量。解析器认为指令流中的所有内容都是指令;在这种情况下,字面值不是,这会抛弃反汇编程序。至少在文字周围有一个无条件的jmp来指导反汇编程序。

你可以在成熟的装配工中找到更加狡猾的技巧,在这些装配工中,每个特技都可以想象得到支持。我对8位汇编程序的最爱之一是“伪”指令SKIP1,SKIP2,您可以将其视为极短的相对分支。它们实际上只是“CMP#8bits”和“CMP#16bits”指令的opcoode字节,分别用于跳转8位或16位指令。所以,“一个字节”相对跳跃而不是两个。当你被挤压空间时,每个字节都很重要: - {

      SKIP1
      INC    ; 8 bit instruction
      ...

这在尝试实现循环时也很方便,其中不应对循环条目执行某些步骤,但需要在进一步的循环迭代中完成:

      SKIP2
LOOP: SHLD  ; 16 bit instruction
      ...
      BNE LOOP

这个问题是,如果您反汇编SKIP1或SKIP2指令,您将看不到INC(或相应的16位指令)。

汇编语言程序员用于传递参数的技巧是在调用之后将它们内联,并且条件是被调用的例程适当地调整返回地址:

      CALL   foo
      DC     param1
      DC     param2

或者          CALL打印字符串          DC“可变长度字符串”,0

反汇编程序没有实际的方法可以知道正在使用这样的约定或者约定是什么,所以解析器必然会处理这个错误。