use*_*857 6 architecture binary cpu emulation
我理解模拟器的作用,将一种机器语言改为另一种机器语言,通常是"即时".这样的程序是否可以构建为读取为一个体系结构编写的二进制文件并为另一个体系结构保存新的二进制文件.在该过程完成之后,它将使用户准备好二进制文件以在给定体系结构上进行本机执行.这对于那些拥有昂贵的传统架构专有应用程序的人来说尤其有用.
是否有可能提出这样的申请?二进制重新编译不是一个新概念,但我还没有找到任何有用的实现.
在其他一些人的帮助下,我很高兴能开始编写这样一个程序的开源实现,如果编程可能的话.
静态重新编译是将二进制文件从外部体系结构转换为另一个目标体系结构的有前途的方法.它比Just-In-Time(JIT)更快,因为它不必在运行之前编译代码,并且因为它可能需要额外的编译时间来优化生成代码.
但是,JIT编译使用动态程序分析,而静态重新编译依赖于静态程序分析(因此名称).
在静态分析中,您没有关于执行的运行时信息.
这是一个主要问题是间接跳跃.该术语涵盖了可能从某些switch语句,使用函数指针或运行时多态(生成虚拟表)生成的代码.这一切都归结为以下形式的指示:
JMP reg_A
Run Code Online (Sandbox Code Playgroud)
假设您知道程序的起始地址,并且您决定从这一点开始重新编译指令.遇到直接跳转时,转到目标地址,然后从那里继续重新编译.当你遇到间接跳跃时,你会陷入困境.在该汇编指令中,reg_A静态地不知道内容.因此,我们不知道下一条指令的地址.请注意,在动态重新编译中,我们没有这个问题,因为我们模拟寄存器的虚拟状态,并且我们知道当前的内容reg_A.此外,在静态重新编译中,您有兴趣找到所有可能的值reg_A此时,因为您希望编译所有可能的路径.在动态分析中,您只需要当前值来生成当前正在执行的路径,如果要reg_A更改其值,您仍然可以生成其他路径.在某些情况下,静态分析可以找到一个候选列表(如果它是某个switch地方必须有一个可能的偏移表),但在一般情况下我们根本就不知道.
好吧,你说,让我们重新编译二进制文件中的所有指令吧!
这里的问题是大多数二进制文件都包含代码和数据.根据架构,您可能无法分辨哪个是哪个.
更糟糕的是,在某些体系结构中没有对齐约束和可变宽度指令,您可能会在某些时候开始解组,只是发现您已经开始使用偏移重新编译.
我们来看一个包含两条指令和一个寄存器的简化指令集A:
41 xx (size 2): Add xx to `A`.
42 (size 1): Increment `A` by one.
Run Code Online (Sandbox Code Playgroud)
我们来看下面的二进制程序:
41 42
Run Code Online (Sandbox Code Playgroud)
假设起点是第一个字节41.你做:
41 42 (size 2): Add 42 to `A`.
Run Code Online (Sandbox Code Playgroud)
但如果41是一个数据怎么办?然后你的程序变成:
42 (size 1): Increment `A` by one.
Run Code Online (Sandbox Code Playgroud)
这个问题在旧游戏中被放大,旧游戏通常直接在汇编中进行优化,而程序员可能会故意期望某些字节被解释为代码和数据,具体取决于上下文!
更糟糕的是,重新编译的程序本身可以生成代码!想象一下重新编译JIT编译器.结果仍然会输出源架构的代码并尝试跳转到它,很可能导致程序很快死掉.静态重新编译仅在运行时可用的代码需要无限的诡计!
静态二进制分析是一个非常活跃的研究领域(主要是在安全领域,寻找源不可用的系统中的漏洞),实际上我知道尝试生成试图静态重新编译程序的NES模拟器.这篇文章非常有趣.
JIT和静态重新编译之间的折衷方案是静态地重新编译尽可能多的代码,只保留不能静态转换的位.
您必须首先确保所有引用的库也使用它重新编译。
这是可能的,但这是一项艰巨的任务。
还要考虑这样做可能会出现许可问题;您正在基于原始软件创建衍生作品。大多数允许您执行此操作的许可证也会让您拥有源代码,因此您可以重新编译或移植源代码,这要容易得多。