我已经这样做了很多次,并继续这样做。在这种情况下,您的主要目标是阅读而不是编写汇编程序,我觉得这适用。
编写自己的反汇编程序。不是为了制造下一个最伟大的反汇编程序,这个完全是为你准备的。目标是学习指令集。无论我是在一个新平台上学习汇编程序,还是在我曾经知道的平台上记住汇编程序。
从几行代码开始,例如添加寄存器,然后在反汇编二进制输出和在输入端添加越来越复杂的指令之间进行乒乓操作:
1) 学习特定处理器的指令集
2) 了解如何为所述处理器编写汇编代码的细微差别,以便您可以摆动每条指令中的每个操作码位
3) 与大多数使用该指令集谋生的工程师相比,您可以更好地学习指令集
在您的情况下,有几个问题,我通常建议从 ARM 指令集开始,今天出货的基于 ARM 的产品比任何其他产品都多(包括 x86 计算机)。但是您现在正在使用 ARM 并且不知道足够的汇编程序来编写启动代码或其他知道 ARM 的例程的可能性可能会或可能不会帮助您尝试做的事情。 ARM 优先的第二个也是更重要的原因是因为指令长度是固定大小且对齐的。作为你的第一个项目,反汇编像 x86 这样的可变长度指令可能是一场噩梦,这里的目标是学习指令集而不是创建一个研究项目。第三个 ARM 是一个做得很好的指令集,寄存器是平等的,没有个别的特殊细微差别。
因此,您必须弄清楚要从哪个处理器开始。我建议先msp430或ARM,然后是ARM,然后是x86的混乱。无论是什么平台,任何值得使用的平台都有供应商免费提供的数据表或程序员参考手册,其中包括指令集以及操作码的编码(机器语言的位和字节)。为了了解编译器的功能以及如何编写编译器不必费力的代码,了解一些指令集并了解每个编译器在每次优化时如何在每个指令集上实现相同的高级代码是很好的环境。您不想在优化您的代码时发现您已经使一个编译器/平台变得更好,但对其他编译器/平台却变得更糟。
哦,用于反汇编可变长度指令集,而不是像使用 ARM 那样简单地从头开始并通过内存线性反汇编每四个字节字或像 msp430 那样每两个字节(msp430 具有可变长度指令,但您可以如果您从中断向量表的入口点开始,仍然可以线性地遍历内存)。对于可变长度,您希望根据向量表或有关处理器如何启动的知识找到一个入口点,并按照执行顺序跟踪代码。您必须完全解码每条指令才能知道使用了多少字节,然后如果该指令不是无条件分支,则假定该指令之后的下一个字节是另一条指令。您还必须存储所有可能的分支地址,并假设这些是更多指令的起始字节地址。有一次我成功了,我多次通过二进制文件。从入口点开始,我将该字节标记为指令的开始,然后通过内存线性解码,直到到达无条件分支。所有分支目标都被标记为指令的起始地址。我多次通过二进制文件,直到没有找到新的分支目标。如果在任何时候你发现一条 3 字节的指令,但由于某种原因你将第二个字节标记为指令的开头,你就有问题了。如果代码是由高级编译器生成的,除非编译器做坏事,否则这不应该发生,如果代码有手写的汇编程序(比如一个旧的街机游戏),很可能会有永远不会发生的条件分支比如 r0=0 如果不为零则跳转。您可能需要手动编辑这些二进制文件才能继续。对于我认为在 x86 上的近期目标,我认为您不会有问题。
我推荐 gcc 工具,如果 x86 是您的目标,mingw32 是在 Windows 上使用 gcc 工具的简单方法。如果不是,mingw32 plus msys 是一个很好的平台,可以从 binutils 和 gcc 源代码生成交叉编译器(通常很容易)。 mingw32 与 cygwin 相比有一些优势,比如明显更快的程序,并且您避免了 cygwin dll 地狱。 gcc 和 binutils 将允许您用 C 或汇编程序编写并反汇编您的代码,并且有比您阅读的更多的网页向您展示如何执行这三个中的任何一个或全部。如果您要使用可变长度指令集执行此操作,我强烈建议您使用包含反汇编程序的工具集。例如,用于 x86 的第三方反汇编程序将是一个使用挑战,因为您永远不知道它是否已正确反汇编。其中一些也取决于操作系统,目标是将模块编译为二进制格式,其中包含来自数据的信息标记指令,以便反汇编程序可以完成更准确的工作。对于这个主要目标,您的另一个选择是拥有一个可以直接编译为汇编器以供您检查的工具,然后希望当它编译为二进制格式时,它会创建相同的指令。
对您的问题的简短回答(好吧,略短一点)。编写反汇编程序来学习指令集。我会从 RISCy 和易于学习的东西开始,比如 ARM。一旦您知道了一个指令集,其他指令集就会变得更容易掌握,通常在几个小时内,通过第三个指令集,您几乎可以立即使用数据表/参考手册的语法开始编写代码。所有值得使用的处理器都有一个数据表或参考手册,其中描述了操作码的位和字节的指令。学习 ARM 之类的 RISC 处理器和 x86 之类的 CISC 足以感受其中的差异,例如必须通过寄存器来处理所有事情,或者能够在更少或没有寄存器的情况下直接在内存上执行操作。三个操作数指令与两个等。当您调整高级代码时,为多个处理器编译并比较输出。您将学到的最重要的事情是,无论编写的高级代码多么好,编译器的质量和所做的优化选择都会对实际指令产生巨大影响。我推荐 llvm 和 gcc(使用 binutils),它们都不会产生 great 代码,但它们是多平台和多目标的,并且都有优化器。两者都是免费的,您可以轻松地从各种目标处理器的源代码构建交叉编译器。