假设我们有一个汇编代码,可以使用哪些已知的技术来恢复原始高级代码中使用的变量?

编辑:通过恢复变量,我并不意味着要恢复变量名。 ,但尝试标识用于存储临时结果的内存位置,这些结果可以由高级代码中的变量替换。另外,我不是在谈论字节码,而是真正的二进制代码,没有类型信息,也没有内嵌完整的名称。

#1 楼

(我原本打算发表评论,但结果却相当长,只能自己做出回答)。

有些评论提到了Hex-Rays反编译器。它的基本思想不是商业秘密,实际上在Ilfak Guilfanov的白皮书中进行了描述,该白皮书是他在2008年发表的演讲中附带的内容。

局部变量分配

此阶段使用数据流分析来连接来自不同基本块的寄存器,以便将它们转换为局部变量。如果一个寄存器是由一个块定义的,并且被另一个块使用,那么我们将创建一个局部变量,覆盖定义和用途。换句话说,局部变量由所有定义和可以连接在一起的所有用途组成。尽管基本概念很简单,但是由于字节/字/双字寄存器的存在,事情变得复杂了。


表面上很简单,但是实现当然有来说明许多细节。而且总有改进的余地。这段内容是:


目前,我们不分析堆栈变量的有效范围
(这首先需要进行良好的别名分析:我们必须能够< br证明没有在两个位置之间修改堆栈变量)。我
怀疑在不久的将来是否可以进行完整的实时范围分析。


因此,对于堆栈变量,目前的方法是简单:每个堆栈插槽都被视为整个功能的单个变量(有一些小例外)。反编译器在此依赖于IDA在反汇编期间完成的工作,在该工作中,一条指令为每次访问创建了一个堆栈插槽。

当前的一个问题是同一个变量的多个名称。例如,编译器可以将堆栈var缓存在一个寄存器中,将其传递给某个函数,然后再将其重新加载到另一个寄存器中。反编译器在这里必须是悲观的。如果无法在两个时间点证明同一位置包含相同的值,则无法合并变量。例如,每当代码将变量的地址传递给调用时,反编译器都必须假定该调用可能破坏该地址之后的任何内容。因此,即使寄存器仍包含与堆栈var相同的值,我们也不能100%确定。因此多余的变量名。但是,用户可以使用手动映射覆盖它。

关于引入函数注释的一些想法可以准确地指定函数如何使用和/或更改其参数(类似于Microsoft的SAL),从而减轻了这种情况。问题,但那里存在一些技术实施问题。

评论


正是我想要的答案类型,谢谢!

–恐怖
2013年3月27日10:26



评论不作进一步讨论;此对话已移至聊天。

–伊戈尔·斯科钦斯基♦
19年5月10日在12:29

#2 楼

您所描述的正是Gogul Balakrishnan在其关于价值集分析的博士论文中所解决的问题[1]。特别是,他根据诸如“抽象位置”之类的概念定义了x86的内存模型。这是他对该概念的描述:如前所述,可执行文件没有内在实体
,就像可用于分析的源代码变量一样。因此,
下一步是从
可执行文件中恢复类似变量的实体。我们将类似变量的实体称为a-locs(用于
“抽象位置”)。您应该阅读这篇论文,尽管会被警告-与大多数有关抽象解释的文档一样-它是简洁且不友好的阅读。

[1] http://pages.cs.wisc.edu/ 〜bgogul / Research / Thesis / thesis.html

#3 楼

太.....这是二进制分析困难,语义信息丢失的原因之一。变量不是计算机体系结构中已知的概念,它让人想起更高层次的理解。 ,您可以查找该编译器用来存储变量的约定,可能是将寄存器和变量“溢出”组合在一起存储到堆栈帧上的位置。好消息是大多数编译器或多或少都差不多。

您可以通过观察影响值的条件操作来尝试确定符号(假设开发人员没有犯错)例如比较有符号和无符号值。

评论


您给出了很好的调查路径,但是必须有一些现有的“临时”技术。例如,在十六进制射线反编译器或回旋镖中用于识别堆栈帧中变量的技术是什么?

–恐怖
13年3月26日在17:15

Hex-Rays反编译器实际上在理解可变边界方面很差。似乎只是假设任何可能是变量的东西都是。这可能导致对变量数量的高估。为了获得清晰的反编译,通常必须将很多变量作为别名进行映射。它仍然是一个很棒的产品。伊戈尔可能知道更多,但这可能与商业机密或其他某些东西有关。

–彼得·安德森(Peter Andersson)
13年3月26日在18:14



努力:但是,不仅依赖于编译器。考虑调用约定,它们由体系结构或平台决定。

– 0xC0000022L♦
13 Mar 26 '13 at 20:01

@PeterAndersson:可能不会透露任何信息。我想在我们所有人都可以想到的事情之上,Hex-Rays(该公司)可能想出了一堆试探法来识别事物。我同意,在测试了反编译器插件的Beta版本后,我一点也不相信。 IDA从来没有去过,这在很大程度上误导了我。不过,已经过去了几年,但是作为一个私人我现在不想负担得起;)

– 0xC0000022L♦
13年3月26日在20:04

@ 0xC0000022L,反编译器很棒。它为我们节省了很多时间。您只需输入和映射所有内容都非常周到。它仍然会犯错误,有时甚至会误导他人,但这是一个积极的结果。

–彼得·安德森(Peter Andersson)
13年3月26日在20:21

#4 楼

关于二进制文件中的字符串的一个很好的技巧是命令行工具strings。值得一提的是,它不搜索“变量”。它只是寻找连续的有效字符并打印出来。因此,这也有助于从任何类型的文件中提取字符串(当以明文形式存储时)。

示例程序:

int main(int argc, char* argv[]) {
    char pw[]="SecretPW";
    if(!strcmp(pw,argv[1])){
        printf("Correct!\n");
    } else {
        printf("False...\n");
    }
    return 0;
}


使用字符串提取字符串:

$ ./test FalsePW
False...
$ strings test
SecretPW
Correct!
False...
$ ./test SecretPW                                                           139 ↵
Correct!