[英]Symbolicating stripped binary using symbols from older debug version (inexact graph matching)
我有二进制A ,这是一个带有附带符号的调试版本 - 很多年前构建的。 我也有二进制B , 没有附带符号的发布版本,并且更新近。 我要寻找从一个二进制符号匹配到二进制B等潜在候选人的最有效的方法。
鉴于调试版本相当大(进行更多的输入验证,向stderr
打印更多内容等)并且函数总是随着时间的推移而变化,我认为尝试指纹各个函数将浪费时间。
因此,我已经决定 - 非常凭空,所以我可能会咆哮错误的树 - 指纹函数的最佳方法是创建两个二进制文件的调用图并尝试匹配顶点(即功能)。
我已经做了一些预处理,所以我有以下数据结构:
# binary A
[[60, 60, 8734], # function 0 is called by functions 60 (twice) and 8734
[193, 441, 505], # function 1 is called by functions 193, 441 and 505
[193, 742],
[23],
[21],
[21],
[26],
[26, 1508, 1509, 1573],
[24],
[25],
...] # (~10k functions)
# binary B
[[8999], # function 0 is called by function 8999
[9016], # function 1 is called by function 9016
[1126],
[7904, 7904, 7913],
[182, 336, 396, 396],
[9010],
[407],
[182, 632],
[20],
[24],
...] # (~10k functions)
需要注意的一个重要注意事项是,二进制A中的函数“0”与二进制B中的函数“0”之间没有对应关系。 这些是我为每个二进制文件中的每个函数分配的任意ID。
下一步是让我感到困惑的一步。 我的算法非常弱,我想不出一个聪明的方法来继续。 我(非常有限)的理解是,为了解决这个问题,我想采用某种形式的不精确图匹配 。 换句话说,哪一组映射Ai - > Bi会最大化两个调用图的相似性?
鉴于二进制A中还有其他调试功能以及程序随时间演变的明显事实,可能没有完全匹配。 理想情况下,我想要输出表单:
[[(37, 0.998), (8432, 0.912), (442, 0.75)], # matching-ness of function "0" in binary A with function "37" in binary B is 0.998, second most likely candidate is function "8432" in binary B with score 0.912, etc.
[(42, 0.973), (7751, 0.788)], # matching-ness of function "1" in binary A with function "42" in binary B is 0.973, second most likely candidate is function "7751" in binary B with score 0.788, etc.
[(4579, 0.996), (123, 0.934)],
...] # around ~10k mappings
实际上,如果我只与一名候选人合作并且没有提供排名,我会很高兴,但是我可以做梦。
任何SO的观众都知道我应该从哪里开始?
当然是一个有趣的问题,但我怀疑它很难解决。 它似乎是有向图上的近似图同构的一个实例。 我没有找到太多的谷歌搜索,但这里有一些软件,用于解决无向一般图形,更一般的情况是NP难。
我认为你可以做的最实际的事情是忘记运行时信息,只需要获取每个版本的可执行代码部分并使用全局对齐算法(例如Needleman-Wunsch ,尽管确实存在更快但不太准确算法)对他们说:
CALL
指令,可能还有其他“可靠”指令序列。 假设函数出现在可执行文件中的顺序没有太大改变(它不会有更改,除非优化版本使用了一些优化,让链接器放置彼此靠近的函数),这应该是给你一个很好的初步近似值。
或者,如果你能找到一种方法(我的直觉表明它需要一种迭代的方法,按照PageRank如何决定网页的价值)来“得分”调试版本中的函数f
对应的可能性对于优化版本中的函数g
,然后是,您可以使用图形匹配技术。 在这种情况下,图形的顶点将是两个版本中的所有函数,并且在调试版本的每个函数和优化版本中的每个函数之间将存在加权边缘,权重由您的评分系统确定。
该图表将是二分图,因为在同一版本中,两个函数之间永远不会存在优势。 这意味着它是分配问题的一个实例,其中存在相当好(并且不太复杂)的算法。
然而,这里缺少的部分是决定每个配对的权重的方法。 这样做的一种近似方式是建立一个向量,计算每个函数的直接子,孙子,曾孙等的数量。 然后,您可以使用您喜欢的任何距离测量来比较这些矢量。 但我希望在这里做这件事不会很好,因为我们已经预计调试版本包含的功能要比优化版本多得多。
如果您可以访问两者的整个调用树,这将为您提供更多信息:在函数内进行的调用序列,以及对调用的确切层次结构的了解。 然后,您可以通过仅使用后者为每个函数构建“签名”:
现在, Levenshtein距离可用于比较2个签名。 为了以更多计算为代价获得更高的精度,您可以使用一种变体,其中允许删除调试版本中最多k个不同的函数,对于一些小k(例如k = 3),并且采用最佳Levenshtein距离所有这些“瘦身”版本,附加一个小的惩罚,与删除的功能数量成比例。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.