逆向工程技术——检测开源库及其功能

 2024-02-27 05:03:00  阅读 0

关注天宇实验室官方微信公众号,发送“逆向技巧”即可获取本文完整解题脚本、附加附录及附件~

【逆向工程技术总结】开源函数手动识别

NO.1

在各种逆向工程项目和CTF竞赛题中,都会用到很多开源库,比如集成的Lua解释器,以及zlib、zlib等常用库。

许多库都有非常复杂的逻辑。 逆向过程中的详细分析会增加很多工作量,很多逻辑会用到数学和密码学知识。 如果没有这些理论知识,基本上就没有办法逆转这个过程。 这些程序。

这时,各种库函数的识别就会变得非常重要。 本文主要介绍两种识别开源库函数的方法。

想法

NO.2

首先,我们来谈谈总体思路。 现在我们找到了一个开源库,程序中使用的很多函数的代码逻辑与库中的基本相同。

也就是说,我们得到了程序的部分源代码,但是我们不知道这些源代码对应的是程序的哪一部分。

如果我们能找到对应的函数,我们可以通过查阅开源库的源代码、函数名(符号)、开发文档来了解该函数的作用。 本文要介绍的两种方法就是基于这个思想而发展起来的。

自动分析-

NO.3

它是一个用于比较IDA数据库中功能代码相似度的自动化工具。 识别出的代码相似度越高,实现相同逻辑的可能性就越大。 换句话说,它是一个将程序中函数名与函数体进行匹配的工具。

环境安装

Java SE 套件

首先,您需要安装JDK。 笔者这里使用的是JDK 11,可以从官网下载安装。

您可以根据自己的需求选择对应的版本:

IDA专业版

通过IDA生成的数据库进行匹配。 IDA Pro产品需要购买后才能使用。 它是一个非常强大的逆向工具。 笔者这里使用的是IDA 7.6版本。

官网地址:

官方下载地址在这里,根据你的系统版本和环境选择即可:

注意:安装过程中会要求选择IDA的路径。 这时,你必须选择你的IDA工具(ida.exe/ida64.exe)所在的目录。

指示

要使用该工具,您需要提供另一个idb数据库进行比较。

这里我们首先使用IDA打开被分析程序的数据库,然后在File->中选择要对比的idb数据库

等待一段时间后,即可在 中得到匹配结果。

java字符串相似度算法_字符串相似度c语言_字符串相似度java

左边的未命名函数可以这样找到。

例子

以下是使用 Final 2021 - Barb Metal 的示例。

本题使用库来实现虚拟机,并对虚拟机的代码进行加密。 该题需要找到虚拟机代码中的漏洞来获取flag。

这里我们只匹配相关的库。

第一步是找到库的特征,比如库中的字符串、编译时添加的信息等,通过这些信息来定位使用的是哪个开源库。

直接在IDA中搜索字符串会得到以下结果,其中包含大量代码路径信息。

java字符串相似度算法_字符串相似度java_字符串相似度c语言

直接去搜索,编码路径信息。

获取以下三个开源库,全部托管于:

()

()

()

第二步,将开源库编译成相同架构的二进制库文件。 这里是Linux下的x86架构程序

字符串相似度java_java字符串相似度算法_字符串相似度c语言

这里有两点需要注意。 第一点就是必须在32位环境下编译(如果在64位环境下需要给CFLAG添加-m32参数),因为我们要逆向的文件是32位的。 第二点,如果想在更方便的情况下使用,需要将静态链接库(ar)转换为动态链接库(so),因为静态链接库本质上是一个压缩包,里面有很多编译过的东西其中的库。 文件,那么每个库文件都需要一个idb来存储其代码信息。 动态链接库只需要一个idb,因此我们尝试使用动态链接库生成二进制文件。

修改文件,例如

make后的结果是.so动态链接库文件

以此类推,我们继续编译和建库,最终得到三个so文件

导入IDA分别生成对应的idb,使用进行分析。

但对于他来说,就有点鸡肋了。

通过这个,我们的分析就会变得非常简单,比如下面的函数

字符串相似度c语言_字符串相似度java_java字符串相似度算法

同时也明确

这就是介绍的全部内容。 这个自动化工具非常容易使用,对于识别各种开源库非常有帮助。 当然,开源库版本的选择也很重要。 最好使所有库版本与要逆向的程序保持一致。 版本相同,提高匹配度。

手动分析-对比源码

NO.4

如果这个自动化工具在功能匹配上不是很有效,那么我们就只能通过手动对比源码来分析各个功能的作用了。

以刚才的例子来说,我们能获取到的信息包括源代码所在的目录和文件,甚至是这一行的代码或者信息。 有些函数可能会直接将函数名信息写入其中。 该信息用于定位源代码的函数位置,可用于匹配函数符号。

java字符串相似度算法_字符串相似度java_字符串相似度c语言

不过这种情况在CTF逆向工程中比较少见(除非问题的重点不是逆向工程)。 这时候程序本身的逻辑就会变得非常重要。 无论函数名称或源代码如何更改,函数的逻辑都不会改变。 通过人工直接分析功能逻辑,在信息不足的情况下可以定位到该功能。 接下来我们通过函数本身的特点来演示手动分析的方法。

例子

这道题基本没有什么可查的信息(提问者甚至删除了函数本身的一些可以追溯到源码的功能),所以我们先从main函数开始吧。

字符串相似度c语言_java字符串相似度算法_字符串相似度java

您可以简单浏览一下伪代码。 程序首先打印出密钥,然后加密data.txt并将其存储在enc.dat中,然后用字符串FLAG将其拆分,并写入转换后的密钥。

但进入任何函数时,逻辑都很复杂,没办法理解。

所以我们从能看到的常数值开始,先从上到下看,先进入。 里面有两条数据,但是遗憾的是搜遍了里面的数据后,并没有任何信息表明是哪种算法。

然后输入,可以看到简单的逻辑,再输入看看它做了什么改造。

将a1的类型改为*,因为根据前面的分析,a1是一个整型数组。

java字符串相似度算法_字符串相似度c语言_字符串相似度java

0x312 0x212 0x12 0x112 这些值看起来很特别,所以我们来搜索一下这些值并尝试一下。

我找到了加密算法的来源,是 DS的算法。

根据源码对比,可以得到如下函数映射

->

->

至于上面的两个数据集,如果你仔细分析源码和程序逻辑,你会发现它们只是将自己的key写入到一个sbox中,后续的很多数据甚至根本没有被使用。 这两个数据集实际上是对我们逆向工程的干扰。

返回main函数,找到加密逻辑

字符串相似度java_java字符串相似度算法_字符串相似度c语言

它实际上是什么并没有任何意义,因为v15是我们的加密数据,对其没有任何影响。 但根据逻辑比较,这个函数实际上是将刚刚加密的数据解密回来了。 。 。 我不知道提问者为什么要添加这个功能。

下图中的两个函数交换了int的四个字节的big-和-顺序,这实际上就是上面源码的函数逻辑。

java字符串相似度算法_字符串相似度c语言_字符串相似度java

下一步是非常明显的。 首先想办法解密密钥,然后转储生成的sbox,然后解密。

解密逻辑如下。

def crypt_64bit_down(x, y):    for i in range(0x11, 1, -1):        z = sbox[i] ^ x        x = sbox[0x012 + ((z>>24)&0xff)];        x = sbox[0x112 + ((z>>16)&0xff)] + x;        x = sbox[0x212 + ((z>> 8)&0xff)] ^ x;        x = sbox[0x312 + ((z>> 0)&0xff)] + x;        x = y ^ x        y = z    x = x ^ sbox[1]    y = y ^ sbox[0]    return (x, y)
def byteswap32(a): return (a >> 8) & 0xFF00 | (a << 8) & 0xFF0000 | (a << 24) & 0xFF000000 | (a >> 24) & 0xFF
for i in range(0, len(encbuf), 2): a, b = encbuf[i], encbuf[i+1] a, b = crypt_64bit_down(byteswap32(a), byteswap32(b)) a = byteswap32(a) b = byteswap32(b) encbuf[i: i+2] = [a, b]

接下来回到主函数,trace->,这个函数很大,但是有一些关键的变量可以让我们搜索

放入搜索即可得到图书馆的结果

我还找到一篇文章,专门讲如何识别库函数。 不幸的是,这个问题去掉了文章中提到的函数的特征,因此无法通过本文的思路来识别它们。

然后我们直接编译拖进去,可惜识别率出奇的低,而且没有办法引用。

不过,我们编译出来的so文件也不是没有用的。 用IDA打开它。 首先是搜索键值并获取函数的名称。 接下来我们对比符号表,将so文件对应的函数填入我们的逆向程序中。 中间

字符串相似度c语言_java字符串相似度算法_字符串相似度java

接下来跟进各个函数,根据判断条件、调用函数等函数逻辑继续对函数进行重命名。 比如这个函数用了这个值,就直接搜索,跟踪每个函数,自己看看代码相似度。

java字符串相似度算法_字符串相似度java_字符串相似度c语言

字符串相似度java_java字符串相似度算法_字符串相似度c语言

猜测是一个函数,以此类推,用X进行交叉引用,发现两个函数很相似,尤其是调用参数1

继续往下看,基本可以断定两个函数一模一样,命名,然后copy add sub等函数也识别出来了,以此类推继续识别函数

字符串相似度java_字符串相似度c语言_java字符串相似度算法

java字符串相似度算法_字符串相似度c语言_字符串相似度java

这里有一个小技巧。 现在我们已经确定了该函数,该函数在很多函数中被调用,但是调用次数很少,都在1-5次之间,我们可以通过交叉引用来确定它在程序中的哪个位置被调用。 度数和位置,然后看它的参数(比如这里的3)就可以确定这个函数的名字是什么。

例如,一个非常复杂的函数被调用两次,值分别为10和7。

字符串相似度c语言_字符串相似度java_java字符串相似度算法

查找一个使用参数 10 和 7 调用两次的函数

字符串相似度java_字符串相似度c语言_java字符串相似度算法

通过手动分析,基本可以断定是一个函数。

以此类推,分别恢复符号。

字符串相似度c语言_字符串相似度java_java字符串相似度算法

那么加密逻辑就是下面的结果

很简单的逻辑,然后继续回到main函数

字符串相似度java_字符串相似度c语言_java字符串相似度算法

经过查找和识别很明显算法是,字典改为

9876432*Flag{n0T-EA5y=to+f1Nd}BCDGHJKLMPQRSUVWXYZbcehijkmp

然后用下面的代码就可以得到key的值,只需将整数开3次平方根即可。

(这里使用了gmpy2工具,原文章附录中有解释)

STANDARD_ALPHABET = b'123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'CUSTOM_ALPHABET = b'9876432*Flag{n0T-EA5y=to+f1Nd}BCDGHJKLMPQRSUVWXYZbcehijkmp'
with open("./enc.dat.bak", "rb") as f: buf = f.read()
encrypted, key = buf.split(b"FLAG")
key = base58.b58decode(key.translate(bytes.maketrans(CUSTOM_ALPHABET, STANDARD_ALPHABET)))
print("Key: " + int(gmpy2.iroot(int.from_bytes(key, "big"))[0]).to_bytes(8, "big").decode())
# Key: N5f0cuS_

通过调试,改变key的值,程序生成sbox后dump得到sbox.bin

总结

NO.5

本文主要介绍两种识别开源库的方法。 这种方法在逆向工程中更为重要。 希望对大家有所帮助。

标签: 函数 开源 逻辑

如本站内容信息有侵犯到您的权益请联系我们删除,谢谢!!


Copyright © 2020 All Rights Reserved 京ICP5741267-1号 统计代码