先回答第一个问题。最大的问题是您无法真正将数据与代码分开。基本上有两种拆卸方法:
- 线性扫描
- 递归遍历
使用线性扫描的反汇编器从某个地址开始并一条一条地反汇编指令直到结束,而不会以任何方式跟随跳转或对反汇编代码进行推理。例如 SoftICE 和 Windbg 使用线性扫描。这可能是一个问题,因为您不知道何时停止。你不知道指令在哪里结束,数据从哪里开始。为此,您必须依赖可执行格式元数据,例如节大小。这就是问题所在。
另一方面,递归遍历算法考虑了跳转和调用,反汇编器跟随跳转,只反汇编实际执行的代码。例如 IDA 和 OllyDBG 使用这种方法。它的好处是它本质上知道什么是代码,什么是数据。但很明显,它有它的缺点。首先,通过纯递归遍历,并非所有代码都会被反汇编。例如,在运行时通过计算地址调用的未被直接引用的函数将不会被发现。同样,引擎有一些启发式方法可以绕过这个问题。
举个例子,一个程序向后跳了几条指令,但不是停留在先前反汇编和执行的指令的开头,而是跳转到它的中间。反汇编器应该如何决定显示哪一个?如果这是编码人员的意图,两者都可能是有效的。递归遍历反汇编器可能会显示第一个反汇编的版本,并只标记一个跳转到函数的中间。但是如果不详细检查字节,就很难判断跳转后实际执行了什么。
还有很多其他的例子。
这两种方法的另一个大问题是自修改代码。它只是不能静态完成。
CPU 本身没有任何这些问题,因为它正在执行代码,换句话说,它是动态的。
要回答第二个问题,我认为这不仅仅是 CISC 架构的问题,其中一些也可以应用于 RISC。