一道so文件动态加解密的CrackMe,运行时解密要执行的函数,且在执行后立马加密
拿到FART定制ROM下跑,得到想要的dex文件,数字壳抹去了前八个字节的dex文件魔数,需要填充一下,才能用GDA进行解析
从上图可知,校验函数为libnative-lib.so
文件中的test
函数
首先分析so文件提前加载的三处函数(init、init_array、JNI_OnLoad
)
用readelf -d
查看是否有init、init_array
发现只有init_array
,用IDA查看init_array
数组中的函数
datadiv_decode4192348989750430380
函数的作用是解密字符串
接着分析JNI_OnLoad
函数,导入jni.h
头文件,用于解析JNI函数
接着分析ooxx
函数
sub_8930
函数的内容如下:
其中sub_8A88
函数的作用是获取so文件的加载基址,如下:
获取so文件的加载基址的方法是,通过读取CrackMe进程的内存映射文件maps
,然后通过搜索切割字符串得到的,maps
文件的内容如下:
sub_8930
函数接着调用了sub_8B90
函数用于获取xxoo
函数的相对虚拟地址和大小,如下:
其中步骤5——通过计算,得到xxoo函数在符号表中的索引k
中使用的算法和文章:简单粗暴的so加解密实现中第四部分——基于特定函数的加解密实现介绍的查找函数的算法完全一致,可以导入elf.h
头文件解析ELF文件的结构体
在sub_8930
函数中,根据上面得到的so文件的加载基址、xxoo
函数的相对虚拟地址和大小等信息,接着就是修改内存属性,解密xxoo
函数,还原内存属性,最后刷新指令缓存,分析完成后的sub_8930
函数如下:
其中解密用到的密钥存储在byte_1C180
中,是在bss段,在文件中是未初始化的,所以我们需要在运行时,从内存中dump下来
使用打开文件的方式进行解密,而不是运行时解密,所以需要以下数据
获取xxoo
函数的文件偏移(xxoo_offset
)
xxoo
函数的文件偏移 = .txt
段的文件偏移 + xxoo
函数相对于.txt
段的文件偏移
xxoo
函数相对于.txt
段的文件偏移 = xxoo
函数的相对虚拟地址 - .txt
段的相对虚拟地址
通过上面两个公式可得
xxoo
函数的文件偏移 = .txt
段的文件偏移 + xxoo
函数的相对虚拟地址 - .txt
段的相对虚拟地址
.txt
段的文件偏移和.txt
段的相对虚拟地址在.txt
的区段头中,xxoo
函数的相对虚拟地址在符号表中,如下:
所以xxoo_offset
= 0x8dc5
获取xxoo
函数的大小(xxoo_size
)
如上图,xxoo_size
= 584
获取密钥(xor_array
)
根据上述信息,通过frida脚本dump内存即可得到密钥,脚本如下:
解密过后的ooxx
函数:
flag即是kanxuetest
见附件
/
/
获取用于异或解密的密钥
Java.perform(function () {
var currentApplication
=
Java.use(
"android.app.ActivityThread"
).currentApplication();
/
/
1.
获取app的文件目录files,
/
data
/
user
/
0
/
$package_name
/
files
/
var
dir
=
currentApplication.getApplicationContext().getFilesDir().getPath();
/
/
2.
获取so文件信息
var libso
=
Process.getModuleByName(
"libnative-lib.so"
);
var byte_1C180_addr
=
0x1C180
/
/
3.
获取用于异或解密的密钥的起始地址
var xor_array_base
=
ptr(Number(libso.base)
+
byte_1C180_addr)
/
/
4.
用于异或解密的密钥的大小
var xor_array_size
=
464
;
console.log(
"[xor_array_base]: "
, xor_array_base);
console.log(
"[xor_array_size]: "
, ptr(xor_array_size));
/
/
5.
拼接密钥文件路径
var xor_array_path
=
dir
+
"/"
+
"xor_array"
+
"_"
+
xor_array_base
+
"_"
+
ptr(xor_array_size);
/
/
6.
打开密钥文件
var file_handle
=
new
File
(xor_array_path,
"wb"
);
if
(file_handle && file_handle !
=
null) {
/
/
7.
修改密钥所在内存的属性为可读
Memory.protect(xor_array_base, xor_array_size,
'r'
);
/
/
8.
读取密钥到缓冲区
var xor_array_buffer
=
xor_array_base.readByteArray(xor_array_size);
/
/
9.
把缓冲区内容写入文件
file_handle.write(xor_array_buffer);
/
/
10.
刷新缓存
file_handle.flush();
/
/
11.
关闭文件
file_handle.close();
console.log(
"[dump]: "
, xor_array_path);
}
});
/
/
获取用于异或解密的密钥
Java.perform(function () {
var currentApplication
=
Java.use(
"android.app.ActivityThread"
).currentApplication();
/
/
1.
获取app的文件目录files,
/
data
/
user
/
0
/
$package_name
/
files
/
var
dir
=
currentApplication.getApplicationContext().getFilesDir().getPath();
/
/
2.
获取so文件信息
var libso
=
Process.getModuleByName(
"libnative-lib.so"
);
var byte_1C180_addr
=
0x1C180
/
/
3.
获取用于异或解密的密钥的起始地址
var xor_array_base
=
ptr(Number(libso.base)
+
byte_1C180_addr)
/
/
4.
用于异或解密的密钥的大小
var xor_array_size
=
464
;
console.log(
"[xor_array_base]: "
, xor_array_base);
console.log(
"[xor_array_size]: "
, ptr(xor_array_size));
/
/
5.
拼接密钥文件路径
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
最后于 2021-3-19 20:13
被genliese编辑
,原因: