例如,
blob1
CMP A, B
BLE L1
blob2
CMP C, D
BEQ L2
L1:
blob3
CMP E, F
BGT L3
L2:
blob4
...
B L4
L3:
blob5
L4:
blob6
如果
blob2
和blob3
太大,可能会变成blob1;
if ( A > B && { blob2; C == D } || {blob3; E <= F} ) {
blob4;
} else {
blob5;
}
blob6;
或常规ifs树。
此外,变量A到F不必遵循任何寻址方式约定,它们可以是任意字符串,可以在输出中按原样复制。除了标签的外观以及比较运算符和分支的样式外,您什么都不知道。(一个比较指令产生由各种分支指令消耗的条件代码,而各种比较指令在寄存器中产生一个真/假值,由“如果为true,则分支;如果为false,则分支,等等)。
嵌套这些结构,重用标签,在源代码中使用了eak / continue,可能会发生偶然的非结构化控制转移,依此类推。为了充分认识它们,需要对底层ISA进行更深入的了解。
我需要的是基本DREAM反编译算法的实现;我找不到直接将文本格式的CFG作为输入的实现。
#1 楼
初步评论:问题中描述的过程未反编译
与“反编译”标签关联的定义不正确
否现有的工具执行上述过程的原因有几个:
任何给定的汇编语言都必然与目标处理器的机器语言密切相关
,因为汇编代码(文本)和目标代码(二进制机器语言)是其符号表示,对于通用汇编语言转换器的功能等效项是否可能首先存在的疑问,似乎是个疑问。语言规范。这排除了词法分析的能力,这是从文本到文本的转换中必不可少的步骤。
反编译器不接受任意文本作为输入,因为它们从解析的机器代码创建CFG。此外,反编译器之间CFG构造的方法也不同,因为CFG用于创建在反编译器之间不同的中间表示。在某种程度上无法解析任意伪汇编
尽管汇编源文本指令通常与目标机器语言指令具有一对一的关系,但是中间表示可以是语言和体系结构-独立。这取决于对中间表示形式的选择
反编译
1。重要的是,能够引用反编译的正确定义,以便对其含义有一定了解。
如前所述,与“反编译”标签关联的定义不正确。这是:
用结构化编程语言(例如C)转换从二进制文件中提取的汇编代码的过程。
我们可以将此定义与学术文献中提供的正确定义进行比较:
反编译程序是一种程序,它读取以机器语言(源语言)编写的程序,并将其翻译为目标语言(高级语言)中的等效程序。反编译器或反向编译器尝试使编译器的过程反向,该编译器将高级语言程序转换为二进制程序或可执行程序。1
更直接:
从表面上看,反编译是仅给出二进制文件即可恢复程序源代码的过程。在底层,反编译由一系列抽象恢复机制组成,例如间接跳转解决,控制流结构和数据类型重构,它们可以恢复二进制形式不易获得的高级抽象。2
可以根据正确的定义进行某些推断:
反编译器的输入应为目标代码,而不是ASCII文本或ASCII码的形式。目标代码通过助记符。建议-错误地-汇编代码(机器代码的符号语言表示)是预期的输入,这是一个严重的概念错误。这就是为什么需要更改标签定义的原因,因为此错误可能导致混淆或误解
反编译的目的是通过以二进制形式编码为目标代码的操作的高级语言来创建语义近似。为了使这种近似值达到可接受的准确度,需要多种方法来补偿在编译过程中发生的信息丢失。换句话说,关于抽象恢复的原则越少,反编译源文本作为二进制2的目标代码的信息编码近似值就越不准确。反编译使用中间表示而非汇编语言文本来创建HLL输出。
以下是通常涉及的内容的概述:
另一个示例:控制流程图恢复第一阶段分析输入二进制文件的文件格式,反汇编二进制文件,并为每个函数创建控制流程图(CFG)。从高层次上讲,控制流程图是一种程序表示,其中顶点表示基本块,而边缘
表示块之间可能的控制流过渡。(有关详细信息,请参见第2.1节。)众所周知,在一般情况下,可执行文件很难执行,事实证明,当前算法在实践中效果很好[4、5、24、25]。已经有成熟的平台实现了此步骤。我们使用CMU二进制分析平台(BAP)[10]。 BAP将CFG中的顺序x86汇编指令提升为称为BIL的中间语言,其语法如表1所示(请参见[10])。如我们所见,Phoenix的最终目标是将这种语言反编译为表2.2中所示的高级语言。
CMU的Phoenix反编译器采取的步骤顺序与Cristina Cifuentes的“反向编译技术”(其反编译器称为dcc)中的图示顺序稍有不同:Phoenix为二进制文件创建了CFG,然后将其转换为中间文件语言而不是其他方法。
再举一个例子,摘自No More Gotos:使用与模式无关的控制流结构和保留语义的转换进行反编译: br />
从第一句话可以看出,DREAM的输入是二进制的。与使用BAP中间语言作为中间表示形式的Phoenix不同,DREAM使用其自己的未指定形式的中间表示形式。除白皮书外,一般的反编译过程如下所示:
微代码生成
局部优化
全局优化
局部变量分配
结构分析
初始伪代码
伪代码转换
类型分析
简而言之,Hex-Rays反编译器将目标代码从二进制转换为微代码,创建CFG ,它使用CFG创建伪代码(一些未指定的,可能是专有的中间表示形式),然后输出转换后的伪代码。 >编译器和反编译器都使用中间表示作为转换过程的一部分有几个充分的理由。主要原因是IR可以独立于语言和体系结构,同时保留以源语言编码的信息。 IR的这一方面完全消除了对等效汇编语言到伪代码转换器的需求。从CFG到IR的二进制文件具有极大的优越性和良好的建立性。评论
看来,我需要的是基本DREAM反编译算法的实现;我找不到直接将文本格式的CFG作为输入的简单实现。
不存在这样的实现。如前所述,DREAM从包含在二进制文件中的已解析目标代码构造CFG。
我已经有了自己的反汇编程序。使用以二进制为输入的工具会很浪费精力。重新发明轮子会浪费很多精力。顺便提及,所有上述反编译器都将二进制文件作为输入。
我的观点是,重构控制流结构实际上与体系结构无关。我可以将输入提高一级:如果通过“ if(condition)goto”和“ goto”连接了一张不透明的基本块图,则应该可以恢复原始的if-then-else语句和循环,并且一个可以做到的工具,不需要任何反汇编功能
CFG是通过目标代码分析生成的,以便保留尽可能多的信息。保存更多的信息=更准确的反编译。目标代码格式直接取决于体系结构。
1。反向编译技术
2。使用保留语义的结构分析和迭代控制流结构进行本机x86反编译
#2 楼
IDA Pro是我所知道的最近的一家。尽管对于我认为x86和PPC而言,它仅具有反编译器,但它可以处理更多的CPU,并且似乎对控制流的了解有限,如以图形方式查看功能所示。使用IDA Python,您可以解析函数,遵循控制流并轻松输出文本文件,但这只会使您进入由true / false分支链接的Blob树。我得出的结论是,好的反编译器需要有关编译器和CPU的特定信息。使用指针的结构,间接寻址和有限的类型信息可能很难进行手动跟踪。评论
我已经有自己的反汇编程序。使用以二进制作为输入的工具会浪费很多精力。
– Leo B.
17年7月17日在16:58
显然,我需要来自Dream Decompiler的控制流结构化算法,但是一些声称可以使用它的工具声称可以将x86 ELF作为输入。
– Leo B.
17年7月18日在2:01
#3 楼
在不知道要定位的CPU体系结构的情况下很难说是否存在这样的工具。但是,我推荐radare2的原因有两个:它支持很多体系结构,甚至是异国情调的体系结构
它是开源的并且可扩展的—如果不支持您的CPU ,您可以集成现有的反汇编程序实现,也可以自己编写一个。
但是请记住,它只会产生流控制图。如果反汇编程序实现某些数据流分析,则可能可用。
评论
我的观点是,重构控制流结构实际上与体系结构无关。我可以将输入提高一级:如果通过“ if(condition)goto”和“ goto”连接了一张不透明的基本块图,则应该可以还原原始的if-then-else语句和循环,一个可以做到的工具将不需要任何反汇编功能。
– Leo B.
17年7月17日在16:56
@LeoB。 Radar2具有类似C的输出,但远没有可用。 “ if condition goto”的问题在于恢复原始条件非常困难,并且保持if(eax = 3)之类的条件并不是真正有用。您所要求的可能是具有与架构无关的数据流分析的更通用的反编译器框架。 Retargetable Decompiler就是这样一种工具,但它只能在线使用,而且似乎并未得到积极开发。
–John Doe
17年7月17日在17:03
我的问题更加狭窄:我不需要框架,我需要一个带有文本输入和文本输出的工具。它的复杂性可能只是研究论文的水平,而不是工业产品。
– Leo B.
17年7月17日在17:19
看起来需要实现DREAM算法中与体系结构无关的部分。
– Leo B.
17年7月18日在1:58
它们对于我的目的将是有用的:我正在逆转一个复古软件(一个Pascal编译器),它实际上没有优化就可以自行编译。
– Leo B.
17年7月18日在7:39
#4 楼
我将把我的抽象汇编代码表示为一个C程序,其基本块作为外部定义的过程,条件中的变量作为外部定义的函数等,然后将其编译为优化的x86二进制文件,然后将其传递给现有的x86反编译器。 br />使用问题中的示例:
extern void blob1(), blob2(), blob3(), blob4(), blob5(), blob6();
extern int A(), B(), C(), D(), E(), F();
main() {
blob1();
if (A() < B()) goto L1;
blob2();
if (C() == D()) goto L2;
L1: blob3();
if (E() > F()) goto L3;
L2: blob4();
goto L4;
L3: blob5();
L4: blob6();
}
反编译器产生:
_blob1();
int32_t v2 = _A();
int32_t v3;
if (v2 < _B()) {
_blob3();
v3 = _E();
if (v3 > _F()) {
_blob5();
_blob6();
return 0;
}
_blob4();
_blob6();
return 0;
}
_blob2();
int32_t v4 = _C();
if (v4 != _D()) {
_blob3();
v3 = _E();
if (v3 > _F()) {
_blob5();
_blob6();
return 0;
}
}
_blob4();
_blob6();
那不是完美的,但是会做到的。
评论
我要的是反编译器执行的操作的重要子集,因此该标签是合适的;明确定义了所需的功能:给定CFG作为输入,使用break将其简化为循环和条件,并在必要时继续执行,如果原始CFG无法还原,则将剩余的非结构化对象降至最低。您的答案打算提供什么服务?
– Leo B.
17年7月19日在4:35
@LeoB。 “有没有能够从任意汇编代码中重构结构化代码的工具?” -不
– julian♦
17年7月19日在4:37
一个更有用的答案可能是短了两个数量级:“将抽象程序集代码表示为C程序,其中基本块作为外部定义的过程,条件中的变量作为外部定义的函数,并且尽可能多的getos编译为优化的x86二进制文件,然后将其传递给现有的x86反编译器。”这有点circuit回,但很可能达到目的。
– Leo B.
17年7月19日在5:00
@LeoB。此“解决方案”涉及从某种汇编语言开始,将其表示为HLL,然后进行编译和反编译。经过如此多的转换后,如果反编译的代码能够很好地表示原始机器代码以至于有用,那将令人惊讶。毫不奇怪,鉴于存在着更有效,更准确的公认方法,因此永远不会采用这种二进制分析方法。除此之外,我无话可说。
– julian♦
17年7月19日在5:57
@SYS_V您描述的反编译过程向二进制倾斜,成为一种更静态的类型和/或编译的语言。对于字节码到动态语言,地形有些不同。有关我对后者的看法,请参见rocky.github.io/Deparsing-Paper.pdf。
–洛基
18年11月12日,0:32