我正在处理一些 x86(32/64 位)ELF 二进制代码。这些二进制文件是从 C 和 C++ 程序代码编译的,我正在尝试检测二进制文件中的循环。
我是这个领域的新手,我想知道在二进制代码中识别循环的标准方法是什么?
我更喜欢使用一些静态方法来检测循环实例,因为我无法生成性能良好的动态测试用例。
我正在处理一些 x86(32/64 位)ELF 二进制代码。这些二进制文件是从 C 和 C++ 程序代码编译的,我正在尝试检测二进制文件中的循环。
我是这个领域的新手,我想知道在二进制代码中识别循环的标准方法是什么?
我更喜欢使用一些静态方法来检测循环实例,因为我无法生成性能良好的动态测试用例。
这个问题的经典编译器理论答案是构建一个控制流图,然后进行图分析以识别自然循环。我相信这方面的算法可以在Dragon Book 中找到,并且在这些幻灯片中给出了总结:
http://www.cs.cmu.edu/afs/cs/academic/class/15745-s03/public/lectures/L7_handouts.pdf
您还可以看到 LLVM 如何实现其循环检测:
http://llvm.org/docs/doxygen/html/LoopInfo_8h_source.html
您要查找更多信息的搜索词是“自然循环”和“后缘”等。
您可能会发现有关查找递归函数的这个问题很有帮助,对此有非常好的答案。在二进制代码级别,恕我直言,检测循环(即一些jmp
到同一目标)和递归调用(即一些call
到同一目标)只有很小的区别。
我认为没有标准的方法,但一种方法可能是使用约翰逊算法之类的东西来检测函数有向图中的循环。
您可以在类似NetworkX simple_cycles 的库中找到它的实现。
从二进制代码中检测循环并不容易。有许多基于多种图数据结构(CFG、SSA 形式、DDG 等)的算法。我提供了一组额外的有用文档link1、link2、link3。您可以检查MAQAO对基于 SSA 的环路检测器的实现。另外,我建议您检查 DynInst 的代码和文档link4。
现在,您可以实现一个汇编代码分析器,它可以通过跟随分支来检测基本块/循环。
如果跳跃点在上面,检查距离(Delta(BRANCH_ADDRESS,BRANCH_TARGET_ADDRESS)),如果距离不是太大(你必须决定一个限制)那么你可以对分支条件进行一些分析(检查是否它包含一个移位的寄存器[递增/递减])。分支条件分析可以是一个简单的模式匹配:
Example 1:
TEST X,X
Jxx X
Example 2:
CMP X,X
Jxx X
Example 3:
DEC X
Jnz X
如果一切顺利,您就会得到一个基本块,它很可能是一个循环。
如果您需要更多毛茸茸的细节,我很乐意提供更多;)