反编译 ARM64 并了解分支目标边界

Decompiling ARM64 and understanding branch targets bounderies

提问人:Jorayen 提问时间:10/21/2023 更新时间:10/21/2023 访问量:53

问:

从 arm64 反编译代码时,如何知道无条件分支指令是否是同一函数中标签的分支,而不是其他函数的分支?b

最先进的反编译器如何识别分支目标是仍在函数中还是新函数?他们是否依赖于分支目标的值,并查看它是否落在细分市场中的不同部分?
如果分支目标位于同一部分中,但仍被视为新功能,该怎么办?ARM64 是否有经验法则说,如果分支目标离当前地址太远,则将其视为新边界,从而被视为新函数?就像在 x86 中一样,远跳和短跳有不同的编码,其中短跳可能被视为函数中的标签,而远跳可能不是。
TEXT

我可能会补充一点,我现在正在检查的目标二进制文件是用 Objective-C 编写的 Machos,并且我尝试使用 Ghidra 验证我的发现,因此它可能会使用一些更启发式的方法,例如查看跳转目标是否在该部分或该部分中,甚至分析块结构以识别更多过程(尽管从 Ghidra 反编译来看,最后一点似乎没有识别这些结构)?__stubs__objc_stubs

objective-c arm64 反编译 mach-o

评论


答:

3赞 Siguza 10/21/2023 #1

从 arm64 反编译代码时,如何知道无条件分支指令 b 是否是同一函数中标签的分支,而不是其他函数的分支?

真的不知道。你充其量只能做出一个有根据的猜测。根据我的经验,即使是最先进的反编译器也非常不擅长这一点。好的允许用户手动覆盖此类检测。

核心问题是在程序集级别没有“功能”的概念。导出到更高级别的语言(如 C)的标签应该符合一定的 ABI,但对于其他任何事情,所有的赌注都是失败的。即使使用导出的内容,如果您遇到恶意作者(考虑:恶意软件),那么他们也可能会选择破坏此合同,以掩盖二进制文件的工作原理。但即使使用非恶意二进制文件,如果有人将 Apple 支持的 arm64 目标用于 arm64 目标,你也会受到伤害。-O3 -flto -moutline

不过,您可以使用一些启发式方法:

  • x30 是否保存在某个地方?
    通常,这将通过指令发生,并随着减少而发生。如果是这种情况,那么您可能会查看同一函数中的分支。如果不是这种情况,那么您不知道这是尾部调用还是同一函数中的跳转。
    stp x29, x30, [sp, ...]spb

    假设:x30 用作链路寄存器。混淆器可以使用其他东西,例如通过函数调用,然后是链接寄存器。这也可以按功能随机分配。adr x17, ...; b _funcx17

  • 当前地址和跳转目标之间是否有任何函数?
    也许你的二进制文件有符号,也许是LC_FUNCTION_STARTS,也许你的反编译已经识别了函数单元,等等。

    假设:函数的所有基本块彼此相邻,并且不是碎片化和分散的。通常情况就是这样,但同样,作为混淆技术的一部分,这个假设可能会被打破。

  • 跳跃目标是否从任何其他函数跳转到?
    如果代码中的其他调用站点已确定属于其他函数,则跳转目标不能属于其中任何一个函数。

    注意:功能概述,见下一点。

  • 调用的代码是否保持您期望从函数中获得的 ABI 输入和输出?
    ABI 违规应该很容易确定。ABI 一致性需要显式寄存器溢出之类的东西来给你信心。但是,如果它符合预期的ABI,那么它很可能是它自己的功能。如果不是,那么可能是跳转到它的函数的一部分,或者是概述的代码。

但这些都不能保证会成功,而且总会有这两种选择都可能的情况。

评论

0赞 Jorayen 10/21/2023
感谢您的见解。所以现在我正在使用符号表和 prolouge 分析来查找一组入口点。我不介意混淆技术,因为我正在处理的二进制文件是系统二进制文件,不具有恶意软件性质。之后,我获取所有这些条目并将它们传递给分支分析器,以查找我们分支并链接到 () 的更多函数。所以至于你的第二个启发式点,我觉得我可以做那个检查,但除非我已经知道之前的所有函数,否则我无法判断当前地址和跳转目标之间的数据是否是另一个函数,它可能是,也可能是bl
0赞 Jorayen 10/21/2023
除非我找到所有函数,否则它可能会给我误报。第三点也一样,我觉得好像我没有首先找到所有函数,我无法确定是否有超过 2 个调用站点调用相同的跳转目标。例如,如果我有一个我们确定知道它是一个函数的函数,并且它有一个要定位的分支,它可能是一个新函数,也可能不是,后来我们有另一个分支,它让我们到达另一个相同的目标“B”,你能肯定地说这是一个函数而不是同一个函数中的标签,因为它有 2 个调用站点?我觉得我们首先ABB
0赞 Jorayen 10/21/2023
需要确定 2 个调用站点是不同的函数还是只是我们原始函数中的标签,因此对于第一个分支,它可能是函数中的本地分支,但对于第二个分支,因为它跳转到另一个位置,然后分支到它很难确定跳转到之前的第二个跳转是否本身就是一个函数, 计入 的 2 个不同的调用站点。我觉得看着我正在处理的二进制文件,Ghidra 将他们的跳跃目标识别为新功能,跳跃目标驻留在某个存根部分ABBB
0赞 Jorayen 10/21/2023
所以我想知道这是否是一个很好的假设,可以检查跳跃目标是否在不同的部分,或者更具体地说,完全是一个存根部分?
0赞 Siguza 10/22/2023
你是说进口存根吗?在这种情况下,您可以确定它是一个不同的函数,但在任何其他情况下,所有代码都将位于同一部分(通常)。是的,相当多的启发式方法要求你已经掌握了一些信息,但另一方面,随着反编译的进行,你将获得很多信息 - 毕竟,这就是重点。这意味着反编译应该是一个迭代过程,因为期望事先获得您需要的所有信息是......委婉地说,非常不切实际。__TEXT.__text