


其中的第一个参数是变化的byte数组
第二三个参数是固定的值

找到了so中注册的具体位置
unidbg中固定参数,运行一下,没有什么问题,配合frida打印一下相同参数下真机的返回值,发现是相同的
这里生成一份trace文件,留待备用
返回值(分成32个字符一组,方便查看):
9b332a80a8edcc723e9dbf64c13e24c2
975451929dc2085c3a8249dd01820eae
fe4d8c7b7861bf24b98819c5f46e0878
已经拿到了trace文件,根据返回值,从后往前找,很快能找到关键点0x07024

直接进入这个pc指针0x07024中查看,就能大概找到算法主函数 Bangcle_WB_LAES_encrypt 了

不过我们unidbg中运行中看到的入口地址是 0x9e98
直接上ida中进行查找,内部没有什么严重的混淆,
那么入口的0x9e98是怎么一步步运行到0x07024内部的呢,我们简单分析ida中函数的调用逻辑
Java_com_bangcle_CryptoTool_laesEncryptByteArr一番看下来,就是下图中标记的地方比较有问题

进入sub_8B2C中,结合之前分析到的Bangcle_WB_LAES_encrypt算法主函数一起观察,发现如下:

所以上述函数调用的整体逻辑就完全清晰了。
so内部函数算法调用逻辑大概清晰了,我们现在来分析一下aes的这个主要算法函数Bangcle_WB_LAES_encrypt的大致逻辑:
函数内部非常符合aes的标准算法流程:字节代换,行位移,列混淆,轮密钥加
可以很清晰的找到一个循环:

正常的话这里的v52的值应该是10,
这里有多种方式可以查看这个v52的值
找到了aes的主要逻辑位置,以及分组加密中9轮加密的循环过程,其实就可以进行dfa差分攻击获取aes key了
差分攻击,需要修改第9轮的state值,所以现在需要找到循环中的state值
这里简单分析一下

循环的第一个过程是 字节代换, 将一个4*4的矩阵,通过查表,一个个替换成另外的数值
图中很明显,代码是将4*4的矩阵,切分成了v36到v51的一个连续存储地址的变量中,
所以进入循环后,这个v36地址对应的长度16个字节的值,就是state,
ida汇编分析如下

所以,第二行LDRB W0,[X0] 这个时候的x0的值,就是state,
差分攻击的时候,修改这里就行
但是攻击后没有密钥返回
这里就怀疑是魔改的aes加密了
最开始参数(分成32个字符,也就是16个字节一组):
实际加密参数:
838d8f808f89212888212a8a22288f8f
8a8b22212a8d8b23222289218121818b
iv参数
99303a3a32343a3992923a3b3a999292
返回值:
9b332a80a8edcc723e9dbf64c13e24c2
975451929dc2085c3a8249dd01820eae
fe4d8c7b7861bf24b98819c5f46e0878

观察到的参数a4 : 0x99303a3a32343a3992923a3b3a999292, 这个就是iv参数

a1:这个就是原始需要加密的参数 (原始输入是32个字节,所以aes算法会补齐16个字节,变成三组,标准算法这里刚好32个字节应该是补0, 但是这里可以观察到补充的是0x9b)

所以这里魔改的aes算法中的补位不是标准的PKCS7补位,具体什么时候补充什么,比较简单的方式是一个个尝试出来,这样就不用分析代码
a1 / a2: 根据Bangcle_CRYPTO_cbc128_decrypt中代码可以知道,这个a1参数实际就是和 iv值 异或 第一组原始待加密值

a3: 猜测可能是key,或者和key相关的值, 且调整iv, 和planttext,这个值都不改变

a3是函数的参数a3, 而在进入的时候,a3的打印是

所以,我错误的以为这里的v53他就是a3值,但是其实不是这样的,
这里(_QWORD *)不仅有强转的意思,还有指针取地址的意思
所以*(_QWORD *)a3的实际含义是:
相当于是双层指针获取内容
所以unidbg中拿到的a3的值,并不是v53的实际值,而是要再进行一层指针获取内容
也就是 : 0x123600c0地址下的内容,才是实际的v53的值
上ida中去观察一下a3的传入逻辑,发现,最早出现在Bangcle_internal_crypto函数的这个v18中

往上继续观察,发现v18只有在这个sub_3BA8函数中操作了

进入后,发现逻辑也很清晰
这里大概就是最开始的key的赋值相关操作了

打印一下输入输出值
ida中伪代码:

这段代码执行完后,&v36对应值如下
其中存在一些参数:
unk_B248:这是一张表,直接通过unidbg进行打印
a1: 就是参数a1
v53: 这个上述分析过了
反编译后的代码没有多少混淆,几乎是可读的状态,这样直接手动转换为python代码就很简单,重要的是每一步数据的转换,需要配合实际调试(Frida, trace文件,unidbg动态调试等)
上述第一段代码转成py代码如下
实际输出,和debug调试内容相同
剩下的其他部分伪代码,比如9轮循环等等,也是一样的操作流程
因为没有什么混淆所以转换起来很简单,就不多介绍了
如果这段函数代码存在vmp, 就会变得比较难以还原,估计要一点点细节分析才知道代码做了什么
整体分析流程涉及了jadx + unidbg + frida + ida + trace分析 + aes算法,
侧重点在 so算法分析的
整体蛮适合新手入门
Find native function Java_com_bangcle_CryptoTool_laesEncryptByteArr => RX@0x12009e98[libbangcle_crypto_tool.so]0x9e98
Find native function Java_com_bangcle_CryptoTool_laesEncryptByteArr => RX@0x12009e98[libbangcle_crypto_tool.so]0x9e98
Find native function Java_com_bangcle_CryptoTool_laesEncryptByteArr => RX@0x12009e98[libbangcle_crypto_tool.so]0x9e98
Find native function Java_com_bangcle_CryptoTool_laesEncryptByteArr => RX@0x12009e98[libbangcle_crypto_tool.so]0x9e98
public void hook_so_dfa_attach() {
Debugger attach = androidEmulator.attach();
int[] counts = {0};
attach.addBreakPoint(cryptoDDmodule.base + 0x5EB0, new BreakPointCallback() {
@Override
public boolean onHit(Emulator<?> emulator, long address) {
counts[0]++;
if (counts[0] == 9) {
Random random = new Random();
UnidbgPointer pointerArg = emulator.getContext().getPointerArg(0);
pointerArg.setByte(setByteInt, (byte) random.nextInt(255));
}
return true;
}
});
}
public void hook_so_dfa_attach() {
Debugger attach = androidEmulator.attach();
int[] counts = {0};
attach.addBreakPoint(cryptoDDmodule.base + 0x5EB0, new BreakPointCallback() {
@Override
public boolean onHit(Emulator<?> emulator, long address) {
counts[0]++;
if (counts[0] == 9) {
Random random = new Random();
UnidbgPointer pointerArg = emulator.getContext().getPointerArg(0);
pointerArg.setByte(setByteInt, (byte) random.nextInt(255));
}
return true;
}
});
}
with open("tracefile", "wb") as file:
file.write(
.encode())
phoenixAES.crack_file("tracefile")
with open("tracefile", "wb") as file:
file.write(
传播安全知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2025-11-20 04:26
被卡卡骨编辑
,原因: