可以将机器代码转换为其他体系结构吗?


11

因此,这与有关在ARM上运行Windows服务器的问题有关。因此,我的问题的前提是,可以将机器代码从一种体系结构转换为另一种体系结构,以便在与编译后运行的体系结构不同的体系结构上执行二进制文件

QEMU和其他仿真器可以即时翻译指令,因此可以在未为其编译的计算机上运行可执行文件。为什么不提前翻译而不是即时翻译以加快翻译过程呢?从我比较有限的装配知识,大部分的指令一样MOVADD和其他人应该是跨架构移植。

因为所有机器都是图灵完备的,所以没有直接映射的任何内容都可以映射到其他指令集。这样做会太复杂吗?由于我不熟悉的某些原因,它根本无法工作吗?它可以工作,但没有比使用模拟器更好的结果吗?


该技术可能不受欢迎,因为(除了脆弱)还不需要太多。如今,可移植性/标准化(略有改善)(如果仅是因为Wintel已经占领了世界),并且在确实需要跨机器仿真的地方(例如,对于应用程序开发环境中的电话仿真器),直接仿真提供了结果更可靠,更准确。另外,处理器足够快,以至于仿真成本不会像过去那样严重。
Daniel R Hicks

Answers:


6

简短的答案:您无法翻译已编译的链接可执行文件。尽管在技术上可行,但极不可能实现(请参见下文)。 但是,如果您具有汇编源文件(包含说明和标签),则很可能会这样做(尽管以某种方式获得了汇编源,除非程序是用汇编编写的,否则应将原始程序源代码保存为好吧,因此最好先针对不同的架构进行编译)。


长答案

QEMU和其他仿真器可以即时翻译指令,因此可以在未为其编译的计算机上运行可执行文件。为什么不提前翻译而不是即时翻译以加快翻译过程呢?

我知道原则上看起来似乎很容易,但是在实践中,出于几个主要原因,这几乎是不可能的。首先,不同的指令集使用非常不同的寻址模式,不同的操作码结构,不同的字长,有些甚至没有您需要的指令。

假设您需要将指令替换XYZ为另外两个指令ABCDEF。现在,您已从该点开始有效地转移了整个程序中的所有相对/偏移地址,因此您将需要分析并遍历整个程序并更新偏移量(在更改之前和之后)。现在,假设偏移量发生了很大的变化-现在您需要更改寻址模式,这可能会更改地址的大小。这将再次迫使您重新扫描整个文件并重新计算所有地址,依此类推。

编写汇编程序时,可能会使用标签,但CPU不会使用-汇编文件时,所有标签的计算位置都是相对,绝对或偏移位置。您会看到为什么这很快变成一项艰巨的任务,并且几乎是不可能的。更换一个单一的指令可能需要您在移动之前,整个程序轻车熟路通过。

据我对汇编的了解有限,大多数指令(如MOV,ADD和其他指令)应可跨体系结构移植。

是的,但请看我上面概述的问题。机器的字号如何?地址长度?它甚至具有相同的寻址模式吗?同样,您不能只是“查找并替换”说明。程序的每个段都有一个专门定义的地址。汇编程序时,跳转到其他标签的内容将用文字或偏移量内存地址替换。

因为所有机器都是图灵完备的,所以没有直接映射的任何内容都可以映射到其他指令集。这样做会太复杂吗?由于我不熟悉的某些原因,它根本无法工作吗?它可以工作,但没有比使用模拟器更好的结果吗?

您100%正确地认为这两者都有可能,而且速度很多。但是,编写程序来完成此任务非常困难且极不可能,除非除了我上面概述的问题以外,什么都不做。

如果您有实际的汇编源代码,那么将机器代码转换为另一种指令集体系结构将是微不足道的。但是,机器代码本身是经过汇编的,因此如果没有汇编源(其中包含用于计算内存地址的各种标签),它将变得异常困难。同样,更改一条指令可能会更改整个程序中的内存偏移,并需要进行数百次传递才能重新计算地址。

对于具有几千个指令的程序执行此操作将需要数万甚至数十万次的传递。对于相对较小的程序,这是可能的,但是请记住,通过次数将随着程序中机器指令的数量成倍增加。对于足够大的程序,几乎是不可能的。


本质上,人们要做的就是“反编译”或“反汇编”源对象代码。对于相对简单的代码(尤其是由存在已知“样式”的某些编译器或代码生成包生成的代码),标签等的重新插入相当简单。但是,当然,新的高度优化的编译器将生成很难以这种方式“掌握”的代码。
Daniel R Hicks

@DanH如果您有源目标代码,则几乎有汇编源代码(而不是机器代码)。目标文件包含要链接在一起的已命名(读取:标记)的机器代码序列。当您将目标代码文件链接到可执行文件时,就会出现问题。与整个链接的可执行文件相比,可以更轻松地处理(或反向工程)这些较小的段。
突破

当然,某些目标文件格式会使工作变得容易一些。有些甚至可能包含调试信息,使您可以还原大多数标签。其他人的帮助较小。在某些情况下,甚至以链接文件格式保留了许多此类信息,而在其他情况下则不会。有大量不同的文件格式。
Daniel R Hicks

2

是的,您的建议可以并且已经完成。这不太常见,我不知道当前有任何使用该技术的系统,但这绝对在技术可行性范围内。

在任何人都没有实现我们现在所拥有的甚至简陋的“可移植性”之前,做很多工作就可以将代码从一个系统移植到另一个系统。它需要对“源”进行复杂的分析,并且可能会被代码修改和其他奇怪的做法所困扰,但是仍然可以做到。

最近,诸如IBM System / 38-iSeries-System i之类的系统已利用与已编译程序一起存储的中间代码(类似于Java字节码)的可移植性来实现不兼容指令集体系结构之间的可移植性。


同意这样做,通常使用更旧(更简单)的指令集。在1970年代,有一个IBM项目将旧的7xx二进制程序转换为System / 360。
木屑

1

机器代码本身是特定于体系结构的。

允许在多种体系结构之间轻松移植的语言(Java可能是最著名的)通常是非常高级的,需要将解释器或框架安装在计算机上才能工作。

这些框架或解释器是针对它们将要运行的每种特定系统体系结构编写的,因此,它们本身并不比“常规”程序具有更高的可移植性。


2
编译语言也具有可移植性,而不仅仅是解释语言,它是特定于体系结构的编译器,因为它最终会将代码转换为所识别的平台。唯一的区别是,已编译的语言会在编译时进行翻译,解释的语言会根据需要逐行进行翻译。
2011年

1

绝对有可能。什么是机器码?它只是语言特定计算机可以理解的。将自己想象成一台计算机,并且您正在尝试阅读一本用德语写的书。您做不到,因为您不懂该语言。现在,如果您要阅读德语词典并查找单词“ Kopf”,您会看到它翻译成英语单词“ head”。您使用的字典在计算机世界中被称为仿真层。容易吧?好吧,这变得更加困难。使用德语单词“ Schadenfruede”,并将其翻译成英语。您会看到英语没有单词,但是有一个定义。在计算机世界中存在相同的问题,翻译没有等效词的事物。这使得直接端口很困难,因为仿真层的开发人员必须对该词的含义进行解释,并使主机计算机理解它。有时它只是无法按预期的那样工作。我们都在互联网上看到书籍,短语等的有趣翻译,对吗?


1

您描述的过程称为“静态重新编译”,并且已经完成了,只是没有以通常适用的方式进行。意味着它是不可能的,它已经完成了很多次,但是需要手动操作。

有许多历史例子值得研究,但它们却不足以说明现代问题。我发现了两个例子,从根本上应该使任何完全怀疑的人都质疑那些声称一切艰辛的人都是不可能的。

首先,这个家伙为NES ROM做了一个完整的静态架构和平台。 http://andrewkelley.me/post/jamulator.html

他提出了一些非常好的观点,但得出的结论是JIT仍然更加实用。我实际上不确定他为什么不知道这种情况,这可能是大多数人考虑的那种情况。不走捷径,要求完整的循环精度,并且根本不使用ABI。如果一切都到了,我们可以把这个概念丢进垃圾桶里,然后称之为一天,但这还不是全部,而且从来没有....我们怎么知道的?因为所有成功的项目都没有使用这种方法。

现在,对于不太明显的可能性,要利用您已经拥有的平台... Linux ARM手持设备上的Starcraft?是的,这种方法在您不将任务限制为完全要动态执行时有效。通过使用Winlib,Windows平台调用都是本地的,我们只需要担心体系结构。

http://www.geek.com/games/starcraft-has-been-reverse-engineered-to-run-on-arm-1587277/

考虑到ARM手持式pandora仅比Pi强一点,因此我会花钱给甜甜圈,以使减速几乎可以忽略不计。他使用的工具在此存储库中。

https://github.com/notaz/ia32rtools

那个家伙非常手工地反编译,我相信该过程可以通过减少工作量来实现显着自动化……但是目前仍然是费力的工作。不要让任何人告诉你一些不可能的事情,甚至不要让我告诉你这是不现实的……只要你想出一种新的方法,它就可以成为现实。


0

从理论上讲,是可以做到的。出现的更大问题是将一个操作系统(或内核)的应用程序转换为另一个操作系统。Windows,Linux,OSX和iOS内核低级操作之间存在显着差异,这些设备的所有应用程序都必须使用。

从理论上讲,人们可以再次编写一个应用程序,该应用程序可以分解应用程序以及与被编译为在其上运行的操作系统相关的所有机器代码,然后为另一台设备重新编译所有机器代码。但是,在几乎每种情况下,这都是高度违法的,并且极难编写。事实是,我脑子里的齿轮开始思考。

更新

以下几条评论似乎与我的回应不同意,但是,我认为它们是我的观点。据我所知,没有一种应用程序可以采用一种体系结构的可执行字节序列,在字节码级别将其分解,包括对外部库的所有必要调用,包括对底层OS内核的调用,然后将其重新组装为另一个系统并保存。产生的可执行字节码。换句话说,没有应用程序可以像Notepad.exe那样简单,可以分解它的190k小文件,并且100%将其重新组装成可以在Linux或OSX上运行的应用程序。

据我了解,提问者想知道,如果我们可以通过Wine或Parallels等程序对软件进行虚拟化或运行应用程序,为什么我们不能简单地为不同系统重新转换字节码。原因是,如果您想为另一种体系结构完全重组一个应用程序,则必须在重组之前分解运行该程序所需的所有字节码。例如,对于Windows机器,每个应用程序不仅有exe文件,而且还有更多。所有Windows应用程序都使用低级Windows内核对象和函数来创建菜单,文本区域,窗口大小调整方法,绘制到显示画面,发送/接收OS消息等等...

如果要重新组合为应用程序并使它在不同的体系结构上运行,则必须分解所有字节代码。

Wine之类的应用程序以字节级别解释Windows二进制文件。他们识别对内核的调用,并将这些调用转换为相关的Linux函数,或者它们模拟Windows环境。但是,这不是逐字节(或针对操作码的操作码)重新翻译。它更多的是一个函数到函数的转换,并且有很多不同。


这根本不是理论上的。并且有许多应用程序可以在不同的操作系统上运行其他二进制文件。你听说过酒吗?它可以在不同的操作系统(例如Linux,Solaris,Mac OSX,BSD等)上运行Windows二进制文件。
凯尔塔里(Keltari)2011年

通过使用管理程序运行多个操作系统(或在一个系统上运行“层”,例如Wine在一个系统上模拟另一个系统),可以轻松地在大多数系统上弥补操作系统中的差异。AFAIK,所有“现代”非嵌入式处理器都是“虚拟化的”,因此不需要指令集仿真/转换。
Daniel R Hicks

0

似乎所有专家都忽略了这一点:“翻译”很复杂,但非常适合计算机(没有智能,只是费力)。但是翻译后,程序需要操作系统支持,例如:Linux中不存在GetWindowVersion。通常由仿真器提供(非常大)。因此,您可以“预翻译”一个简单的程序,但必须链接到一个庞大的库才能独立运行。映像每个Windows程序均带有其自己的kernel.dll + user.dll + shell.dll ...


这不仅费劲,还需要智力。例如,假设您看到一些计算,其结果决定了您要跳转的地址,该计算可能位于看起来像是一条指令的中间。
David Schwartz
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.