-
-
[原创]记一次历史悠久的dymas算法的还原过程
-
2020-4-17 15:19
3976
-
libcms.so中查找Jni_OnLoad函数,

查看off_70004地址,函数的关键注册位置.

用frida打印byte_70086的字符串,最后得到e([B)[B的地址为0x208c9, 点进去,并按f5

这部分是mas算法的初始化步骤,大概流程如下, 传入的as数组扩充5位, 假设ptr = 0x7A218492 那么mas[0] = 0x1,
mas
[1] = 0x92
mas
[2]=84
mas
[3]=0x20 as[4] = 0x02, 仔细观察 sub_ 208c9函数,发现并没有加密逻辑,可以确定是在别的函数进行加密, 经过查找,确定了关键位置为
sub_2087E,

具体流程如下:
1、_rbit mas[1]-mas[16], 然后把结果进行反序存放到mas中
2、调用sub_1EB64函数对16为字符进行加密,还原的代码如下:
uint data_i[4];
for(int i = 0; i < 4; i++){
data_i[i] = encrypt_3(mas + 1 + i * 4);
}
// case 3
for(int i = 0; i < 32; i++){
data_i[i % 4] = sub_20224(data_i[(i + 1) % 4] ^ data_i[(i + 2) % 4] ^ key[i] ^ data_i[(i + 3) % 4]) ^ data_i[i % 4];
}
sub_1EB64 经过了ollvm, 可以在关键块下断点,把结果记录下来,然后根据结果推导出算法,断点的关键块如下:

3:把2的结果,经过case4进行加密,具体算法如下:
uint test(uint r0){
uint r1 = 0xAAAAAAAA;
uint r2 = 0x55555555;
r1 = r1 & (r0 << 1);
r0 = r2 & (r0 >> 1);
r0 = r0 | r1;
r1 = 0xCCCCCCCC;
r2 = 0x33333333;
r1 = r1 & (r0 << 2);
r0 = r2 & (r0 >> 2);
r0 |= r1;
r1 = 0xF0F0F0F0;
r2 = 0x0F0F0F0F;
r1 = r1 & (r0 << 4);
r0 = r2 & (r0 >> 4);
return r0 | r1;
}
4: 把2的结果使用sub_1f6b0函数进行加密, sub_1f6f0算法也是根据动态调试在关键快下断记录过程,然后推到出算法。 其中算法实现为:
uint sub_1F6B0(uint i){
//0xF02962CF
// 0:0x00000000 + 0xcf = 0x000000cf << 4 = 0x00000cf0
// 1:0x00000cf0 + 0x62 = 0x00000d52 << 4 = 0x0000d520
// 2:0x0000d520 + 0x29 = 0x0000D549? << 4 = 0x00xD5490
// 3:0x000D5490 + 0xf0 = 0x000D5580
uchar *c_arr = (uchar *)&i;
uint temp = c_arr[0];
for(int i = 1; i < 4; i++){
temp = (temp << 4) + c_arr[i];
}
return temp;
}
5:同3
6:
把2的结果经过sub_1F4D0加密uint sub_1F4D0(uint i){
// 0x4E781CDB
//
// 0x00000000 << 0x00000007 = 0x00000000
// 0x00000000 ^ 0x000000db = 0x000000db
// 0x00000000 >> 0x00000003 = 0x00000000
// 0x000000db ^ 0x00000000 = 0x000000db
// 0x00000000 ^ 0x000000db = 0x000000db
//
// 0x000000db << 0x0000000B = 0x0006D800
// 0x0006D800 ^ 0x0000001c = 0x0006D81C
// 0x000000db >> 0x00000005 = 0x00000006
// 0x0006D81C ^ 0x00000006 = 0x0006D81A
// 0x0006D81A ^ 0xFFFFFFFF = 0xFFF927E5
// 0x000000DB ^ 0xFFF927E5 = 0xFFF9273E
//
// 0xFFF9273E << 0x00000007 = 0xFC939F00
// 0xFC939F00 ^ 0x00000078 = 0xFC939F78
// 0xFFF9273E >> 0x00000003 = 0x1FFF24E7
// 0xFC939F78 ^ 0x1FFF24E7 = 0xE36CBB9F
// 0xFFF9273E ^ 0xE36CBB9F = 0x1C959CA1
//
// 0x1C959CA1 << 0x0000000B = 0xACE50800
// 0xACE50800 ^ 0x0000004E = 0xACE5084E
// 0x1C959CA1 >> 0x00000005 = 0x00E4ACE5
// 0xACE5084E ^ 0x00E4ACE5 = 0xAC01A4AB
// 0xAC01A4AB ^ 0xFFFFFFFF = 0x53FE5B54
// 0x1C959CA1 ^ 0x53FE5B54 = 0x4F6BC7F5
uchar *c_arr = (uchar *)&i;
uint temp = 0;
uint v1, v2, v3, v4, v5;
for(int i = 0; i < 4; i++){
switch (i & 1){
case 0:
v1 = temp << 7;
v2 = c_arr[i] ^ v1;
v3 = temp >> 3;
v4 = v2 ^ v3;
temp = temp ^ v4;
break;
case 1:
v1 = temp << 0xb;
v2 = v1 ^ c_arr[i];
v3 = temp >> 5;
v4 = v2 ^ v3;
v5 = v4 ^ 0xffffffff;
temp = temp ^ v5;
break;
}
}
return temp & 0x7fffffff;
}
7:同3
8:
把2的结果进行sub_1F2E4加密。uint sub_1F2E4(uint i){
//0x4E67C6A7 << 0x00000005 = 0xCCF8D4E0
//0xCCF8D4E0 + 0x000000i[index] = 0xCCF8D522
//0x4E67C6A7 >> 0x00000002 = 0x1399F1A9
//0xCCF8D522 + 0x1399F1A9 = 0xE092C6CB
//0x4E67C6A7 ^ 0xE092C6CB = 0xAEF5006C
//
//0xAEF5006C << 0x00000005 = xxxxxxxxxx
//0xC51E9B60
uchar *c_arr = (uchar *)&i;
uint temp = 0x4E67C6A7;
for(int i = 0; i < 4; i++){
uint v1 = temp << 5;
uint v2 = v1 + c_arr[i];
uint v3 = temp >> 2;
uint v4 = v2 + v3;
temp ^= v4;
}
return temp;
}
9:同3
10:对3、5、7、9的结果进行异或。
11:3-9进行循环
最后对生成的字符串与as的日志相比较, 就能总结出算法。

后记: 入坑android 逆向差不多一年了吧, 这篇笔记是很久以前根据https://bbs.pediy.com/thread-246360.htm 这位大佬的心得所写的,考虑到mas算法现在已经没用,把它分享出来给需要的人学习吧,文档写的拉闸望各位大佬轻喷,不甚感激。
【公告】 [2022大礼包]《看雪论坛精华22期》发布!收录近1000余篇精华优秀文章!
最后于 2020-4-18 12:59
被hczhong编辑
,原因: 重新上传图片