魔改md5样本算法分析
1. 前置
- 明文:1651480960074
- 加密结果:76B5CEBECD2484F78CDF7A468C133EFB
- 目标:还原魔改的md5算法,主要是unidbg辅助算法还原;
- 此文章是早期所写,具体写了些什么我也不记得了,有问题请留言或自行解决;
样本链接:
通过网盘分享的文件:md5-release.apk 链接: https://pan.baidu.com/s/1VdfzarU3JbpflIvAMOWtQA?pwd=sana 提取码: sana --来自百度网盘超级会员v6的分享
2. 分析
2.1 静态分析
- 使用ida打开so文件,函数是静态注册的,直接搜即可,大致定位到关键md5函数位置如下:

- 这里可以看到初始化魔数,看看大致流程,这里的so文件是32位的;
- 首先需要有一份标准的源码,然后对比着分析,这里的常数看着比较标准,但后续发现顺序其实是变化的,所以也是有进行魔改的,先用unidbg把流程跑起来,这里不需要补环境,直接跑就行;

- 跑通了之后就可以去看反编译的代码了;
2.2 初始化常量
- 首先肯定先看看常量是不是被魔改了,众所周知这是最常见的魔改点,先看看和标准的有什么区别;
标准:
context->state[0] = 0x67452301;
context->state[1] = 0xEFCDAB89;
context->state[2] = 0x98BADCFE;
context->state[3] = 0x10325476;
ida:
v5 = 0x10325476;
v68 = 0x98BADCFE;
v67 = 0xEFCDAB89;
v66 = 0x67452301;- 大致看好像是没有魔改的,数据是对的,但是顺序也是对的吗?我们去分析一下;
- 我们来看第一轮的主函数这里;
v15 = __ROR4__(v13 + (v9 & v14 | v12 & ~v9) + *(v10 - 3) + *(v11 - 4), 25);- 与标准的对比;
FF(a, b, c, d, x[0], 7, 0x8A51407D);
#define FF(a,b,c,d,x,s,ac) \
{ \
a += F(b,c,d) + x + ac; \
a = ROTATE_LEFT(a,s); \
a += b; \
}
#define F(x,y,z) ((x & y) | (~x & z))- 这个FF函数对应标准的也是大概能看出来一些东西的,(v9 & v14 | v12 & ~v9)应该就对应的F函数里的((x & y) | (~x & z));
(v9 & v14 | v12 & ~v9)
((x & y) | (~x & z))
F(b,c,d) -->> ((b & c) | (~b & d))
所以:
(v9 & v14 | v12 & ~v9) == ((b & c) | (~b & d))- 那么v9就是b,v14就是c,v12就应该是d,剩下一个就应该是a,我们去对比一下;
标准:
a = 0x67452301
b = 0xEFCDAB89
c = 0x98BADCFE
d = 0x10325476
ida:
a = 0x67452301
b = 0x10325476
c = 0x98BADCFE
d = 0xEFCDAB89- 所以这里应该是这样的顺序,表面看没有修改常量,实际上是有变化的;
2.3 第1轮
- 接下来看看标准的代码第一轮应该是怎样的;
FF(a, b, c, d, x[0], 7, 0x8A51407D); // 这里的结果应该是给了a
#define FF(a,b,c,d,x,s,ac) \
{ \
a += F(b,c,d) + x + ac; \
a = ROTATE_LEFT(a,s); \
a += b; \
}
#define F(x,y,z) ((x & y) | (~x & z))- 上述代码就是第一轮涉及到的内容,我们看看ida反编译的是什么样子;
v15 = __ROR4__(md5_a + (md5_b & md5_c | md5_d & ~md5_b) + *(v10 - 3) + *(v11 - 4), 25);
md5_a = md5_d + v15;- 对比着分析,v10肯定就是明文了,v11应该就是k表了,这里需要hook验证一下也是可以的,当然直接盲猜也行;

- v10在这里赋值,那么我们应该看它等于多少,按下tab看看汇编代码;

- 这条汇编指令的作用是从堆栈中加载一个值到寄存器
R6,那么R6里面存的应当就是v10的值了,我们hook下一条指令,也就是A24的位置,此时上一条指令刚刚结束,去看看R6寄存器的值即可;
private void hook_A24() {
// 首先hook明文 看看明文是否正确
emulator.attach().addBreakPoint(module.base + 0xA24 + 1, new BreakPointCallback() {
@Override
public boolean onHit(Emulator<?> emulator, long address) {
// 读取R6寄存器
Number number1 = emulator.getBackend().reg_read(ArmConst.UC_ARM_REG_R6);
// *(v10 - 3) 这里是int指针 一个int是占4个字节 所以这里减去12就应该是明文
byte[] data_str = emulator.getMemory().pointer(number1.longValue() - 12).getByteArray(0, 64);
Inspector.inspect(data_str, "[+] 明文");
return true;
}
});
}- 看看情况如何,是否是我们期许的;

- 这正是我们输入的明文,那么证明我们上述的观点是没有问题的,剩下的自然就是k值了,那它是多少呢?

- 按照刚刚的理论,这个&dword_2C00想必就是k值了,我们去看看它应该是多少;

- 但它代码显示是v11-4,按照刚刚明文的理论,这里也应当减去四个int大小,也就是16个字节,去设置一下,让它显示具体的字节;

- 那么真正的k值就应该是0x8A51407D,这是我们的推论,去看一下正不正确,这里就需要借助unidbg了,看看第一轮结束的值是不是经过我们分析魔改点之后的值,所以本地也应该根据它的修改进行修改;
- 依据我们hook明文时的理论,同样的去hook第一轮结束后的位置,看看此时寄存器状况是怎样的;
private void hook_A66() {
// A66
emulator.attach().addBreakPoint(module.base + 0xA66 + 1, new BreakPointCallback() {
@Override
public boolean onHit(Emulator<?> emulator, long address) {
Number number2 = emulator.getBackend().reg_read(ArmConst.UC_ARM_REG_R3);
System.out.println("[+] hook_A66结果-->>" + Integer.toHexString(number2.intValue()));
return true;
}
});
}- 这里同时去打印一下本地同步修改的第一轮结果;
FF(a, b, c, d, x[0], 7, 0x8A51407D); /* k表改了 */
printf("[+] 第一轮a-->>%x\n", a);
printf("[+] 第一轮x0-->>%x\n", x[0]);- 对比一下结果发现unidbg结果为:[+] hook_A66结果-->>559a829a;
- 本地的结果为:[+] 第一轮a-->>75ff2b87;
- 结果是不一样的,那就证明可能还有地方是被魔改了的,那就去看看F 函数的运算过程,因为这一轮我们已经没有可以修改的了,忘记说了,左移7位和右移25位是同一个意思,因为在c里面好像是没有循环左移这个说法的,这里的左移位数是没问题的;
#define F(x,y,z) ((x & y) | (~x & z))
v15 = __ROR4__(md5_a + (md5_b & md5_c | md5_d & ~md5_b) + *(v10 - 3) + *(v11 - 4), 25);
md5_a = md5_d + v15;- 这里发现了蛛丝马迹,我们发现ida里面最后加的是md5_d,而标准的加的是b,那我们根据它的理论修改一下再测试;

此时我们发现结果对上了,那么第一轮就是魔改了这些位置,稍微汇总一下:
- k表;
- FF函数;
2.4 第2轮
- 接下来解析第二轮;
FF(d, a, b, c, x[1], 12, 0xE8C7B756); /* 2 */
v17 = __ROR4__(md5_b + *(v10 - 2) + *(v11 - 2) + ((md5_d + v15) & ~md5_c | md5_c & md5_d), 20);
md5_b = md5_a + v17;- 第二轮很显然应该是给d赋值才对,ida这里是给b赋值,说明这里也是有修改,那一会分析的时候就需要注意;
v17 = __ROR4__(md5_b + *(v10 - 2) + *(v11 - 2) + ((md5_d + v15) & ~md5_c | md5_c & md5_d), 20);
md5_b = md5_a + v17;
FF(b, c, d, a, x[3], 22, 0xc1bdceee);
#define FF(a,b,c,d,x,s,ac) \
{ \
a += F(b,c,d) + x + ac; \
a = ROTATE_LEFT(a,s); \
a += b; \
}
#define F(x,y,z) ((x & y) | (~x & z))
#define F(b,c,d) ((b & c) | (~b & d))- 首先看看正确的流程,这里为了方便看我会把FF函数做一下调整,根据第二轮传入的参数修改;
FF(d, a, b, c, x[1], 12, 0xE8C7B756); /* 2 */
#define FF(a,b,c,d,x,s,ac) \
#define FF(d, a, b, c,x,s,ac) \
{ \
d += F(a, b, c) + x + ac; \
d = ROTATE_LEFT(c,s); \
d += a; \
}
看看ida的流程:
v17 = __ROR4__(md5_b + *(v10 - 2) + *(v11 - 2) + ((md5_d + v15) & ~md5_c | md5_c & md5_d), 20);
md5_b = md5_a + v17;
d += F(a, b, c) + x + ac; ==>> md5_b + *(v10 - 2) + *(v11 - 2) + ((md5_d + v15) & ~md5_c | md5_c & md5_d)
F(a, b, c) ==>> ((md5_d + v15) & ~md5_c | md5_c & md5_d)
d = ROTATE_LEFT(c,s); ==>> __ROR4__(...,20);
d += a; ==>> md5_b = md5_a + v17;- 可以看到其实修改的点也是很多,那么首先看看明文,第一轮是-3,这里-2,应该是没有问题的,那再看k值,这里的k值应该-2也就是八个字节,往上看两行;

- 这里的k值就是:0x6A88995D,再看左移位数,这里原本是12,也就是左移12位,ida里是20,也就是右移20,这里的左移位数是没有问题的,那就看其他的位置;
- 根据对比,正常传入FF函数的顺序是与这里对不上的,那根据它的逻辑我们进行修复;
FF(d, a, b, c, x[1], 12, 0xE8C7B756); /* 2标准 */
FF(b, a, b, c, x[1], 12, 0x6A88995D); /* 2ida */
// 这里除开b剩下的顺序应该是这样:
// md5_c -> 1
// md5_d -> 2
// (md5_d + v15) -> 3 (md5_d + v15)就是第一步得到的a
FF(b, c, d, a, x[1], 12, 0x6A88995D); /* 2ida */- 那么这里就分析结束了,逻辑是说得通的,最终修改成这样的数据;
FF(b, c, d, a, x[1], 12, 0x6A88995D); /* 2 */
printf("[+] 第2轮b-->>%x\n", b);
printf("[+] 第2轮x1-->>%x\n", x[1]);- 根据上面的同样做法,去看看unidbg里第二轮值是不是对应的;
private void hook_A7A() {
// A7A
emulator.attach().addBreakPoint(module.base + 0xA7A + 1, new BreakPointCallback() {
@Override
public boolean onHit(Emulator<?> emulator, long address) {
Number number2 = emulator.getBackend().reg_read(ArmConst.UC_ARM_REG_R2);
System.out.println("[+] hook_A7A 第2轮结果-->>" + Integer.toHexString(number2.intValue()));
return true;
}
});
}- 看看结果如何;

第二轮也就还原完成了,再总结一下第二轮修改了什么:
- k值;
- 修改了 b, c, d, a 的位置;
- FF函数;
2.5 第3-4轮
- 先看第三轮,分析如下:
md5_a = md5_d + v15;
md5_b = md5_a + v17;
v16 = md5_a & md5_d;
md5_c = md5_b + __ROR4__(md5_c + *(v10 - 1) + *v11 + (md5_b & ~md5_d | md5_a & md5_d), 15);
标准:md5_c = FF(c, d, a, b, x[2], 17, 0x242070DB); /* 3 */
#define FF(a,b,c,d,x,s,ac) \
{ \
a += F(b,c,d) + x + ac; \
a = ROTATE_LEFT(a,s); \
a += b; \
}
#define F(x,y,z) ((x & y) | (~x & z))
ida:md5_c = FF(c, d, a, b, x[2], 17, 0xFD7025F4); /* 3 */- 这个比较简单,非常好分析,最终只是魔改了k值,看看结果对比;
- unidbg结果:
[+] hook_A90 第3轮结果ccc-->>9c63c0bd- 本地结果:
[+] 第3轮x2-->>37303036
[+] 第3轮c-->>9c63c0bd- 可以看到第三轮就只是修改了k值;
- 再看第四轮;
ida:md5_d = md5_c + __ROR4__(v19 + v20 + (md5_c & ~md5_a | (md5_a + v17) & md5_a), 10);
标准:FF(b, c, d, a, x[3], 22, 0xc1bdceee); /* 4 */
#define FF(a,b,c,d,x,s,ac) \
{ \
a += F(b,c,d) + x + ac; \
a = ROTATE_LEFT(a,s); \
a += d; \
}
F(x,y,z) ((x & y) | (~x & z))
v19 = md5_d + v18;
v20 = v11[2];- 分析也是比较容易的,这里需要看一下k值,根据上面的分析,v20应该是k值,那么它等于v11的第二个数据,那就是往后推八个字节,看看是什么数据;

- 那就是0xA7553036,再看具体的abcd,这里肯定是有变化的,因为b的值在前面已经被计算了,所以这里肯定是d的值,具体的修改如下:
FF(d, a, b, c, x[3], 22, 0xA7553036); /* 4 */
printf("[+] 第4轮x3-->>%x\n", x[3]);
printf("[+] 第4轮d-->>%x\n", d);- 对比看看,unidbg结果:
[+] hook_AAC 第4轮结果ddd-->>466fc264- 本地结果:
[+] 第4轮x3-->>8034
[+] 第4轮d-->>466fc264那么第3-4轮也还原完毕,大致魔改位置汇总:
- k值;
- a,b,c,d顺序;
- FF函数;
2.6 第5-16轮
- 根据伪代码分析,会把我们上述的步骤重复四次,但是每次的明文和k值是在往后移的,这里我们最好的选择肯定是和它一样写一个循环,因为具体的步骤都是一样的,只需要改变明文和k值;
for (int i = 0; i < 4; ++i) {
// k表改了 修改了FF函数 此后这16轮都会修改
FF(a, b, c, d, x[i * 4 + 0], 7, k_table[i * 4 + 0]); /* 1 */
// printf("[+] 第1轮x0-->>%x\n", x[0]);
printf("[+] 第1轮a-->>%x\n", a);
// 修改了k值 b, c, d, a的位置
FF(b, c, d, a, x[i * 4 + 1], 12, k_table[i * 4 + 1]); /* 2 */
// printf("[+] 第2轮x1-->>%x\n", x[1]);
printf("[+] 第2轮b-->>%x\n", b);
// 修改了k值
FF(c, d, a, b, x[i * 4 + 2], 17, k_table[i * 4 + 2]); /* 3 */
// printf("[+] 第3轮x2-->>%x\n", x[2]);
printf("[+] 第3轮c-->>%x\n", c);
// 修改了k值
FF(d, a, b, c, x[i * 4 + 3], 22, k_table[i * 4 + 3]); /* 4 */
// printf("[+] 第4轮x3-->>%x\n", x[3]);
printf("[+] 第4轮d-->>%x\n", d);
printf("[+++++++++++++++++++++++++++]\n");
}- 这里的k表我们就需要腾出来了,并且分析一下代码;

- 它把v11+8,也就是向后移32个字节,去看看;

- 原始位置是2c00,加上32的十六进制也就是20,会定位到2c20,它的k值是从上往下取的,这是根据前四轮的分析得到的,我们就可以将具体的k值拿下来放在一个数组里,这里若不一样可以按下D改变数据的类型;
- 因此,前十六轮就可以还原为如下代码:
int k_table[16] = {
0x8A51407D, 0x6A88995D, 0xFD7025F4, 0xA7553036,
0x489E15C1,0xF5CDB84B, 0xC0FFBCF6, 0x253F7D7E,
0xE93FD535, 0xD6CD6448,0x1220AE4, 0xD806D023,
0xE84E6EA9, 0x230135D8, 0xC27AE834,0xF5292BF4
};
for (int i = 0; i < 4; ++i) {
FF(a, b, c, d, x[i * 4 + 0], 7, k_table[i * 4 + 0]); /* 1 */
printf("[+] 第1轮a-->>%x\n", a);
FF(b, c, d, a, x[i * 4 + 1], 12, k_table[i * 4 + 1]); /* 2 */
printf("[+] 第2轮b-->>%x\n", b);
FF(c, d, a, b, x[i * 4 + 2], 17, k_table[i * 4 + 2]); /* 3 */
printf("[+] 第3轮c-->>%x\n", c);
FF(d, a, b, c, x[i * 4 + 3], 22, k_table[i * 4 + 3]); /* 4 */
printf("[+] 第4轮d-->>%x\n", d);
printf("[+++++++++++++++++++++++++++]\n");
}- unidbg前16轮结果:
[+] hook_A66 第1轮结果aaa-->>559a829a
[+] hook_A7A 第2轮结果bbb-->>90a37ab1
[+] hook_A90 第3轮结果ccc-->>9c63c0bd
[+] hook_AAC 第4轮结果ddd-->>466fc264
[++++++++++++++++++++++++++++++++++++++]
[+] hook_A66 第1轮结果aaa-->>9a9c6a9e
[+] hook_A7A 第2轮结果bbb-->>ac6e9375
[+] hook_A90 第3轮结果ccc-->>4dfea254
[+] hook_AAC 第4轮结果ddd-->>db8ce9c4
[++++++++++++++++++++++++++++++++++++++]
[+] hook_A66 第1轮结果aaa-->>c1223db5
[+] hook_A7A 第2轮结果bbb-->>4c7c6281
[+] hook_A90 第3轮结果ccc-->>fdf68ba2
[+] hook_AAC 第4轮结果ddd-->>1882adb9
[++++++++++++++++++++++++++++++++++++++]
[+] hook_A66 第1轮结果aaa-->>4c20b8bc
[+] hook_A7A 第2轮结果bbb-->>5142113c
[+] hook_A90 第3轮结果ccc-->>ab3624a0
[+] hook_AAC 第4轮结果ddd-->>25b2551b
[++++++++++++++++++++++++++++++++++++++]
76B5CEBECD2484F78CDF7A468C133EFB- 本地前16轮结果:
[+] 第1轮a-->>559a829a
[+] 第2轮b-->>90a37ab1
[+] 第3轮c-->>9c63c0bd
[+] 第4轮d-->>466fc264
[+++++++++++++++++++++++++++]
[+] 第1轮a-->>9a9c6a9e
[+] 第2轮b-->>ac6e9375
[+] 第3轮c-->>4dfea254
[+] 第4轮d-->>db8ce9c4
[+++++++++++++++++++++++++++]
[+] 第1轮a-->>c1223db5
[+] 第2轮b-->>4c7c6281
[+] 第3轮c-->>fdf68ba2
[+] 第4轮d-->>1882adb9
[+++++++++++++++++++++++++++]
[+] 第1轮a-->>4c20b8bc
[+] 第2轮b-->>5142113c
[+] 第3轮c-->>ab3624a0
[+] 第4轮d-->>25b2551b
[+++++++++++++++++++++++++++]
b47e51f64bac1c36d6b65dbc2ab2728c- 可以发现前16轮也是还原出来了,但是最终结果依旧是不一样,所以需要继续还原;
2.7 第17-32轮
- 与上面的逻辑其实无异了,先看第一轮;
v28 = __ROR4__(md5_a + (md5_c & ~md5_d | md5_b & md5_d) + v77[(v22 - 15) & 0xF] + v27, 27);
md5_a = md5_d + v28;
// 标准:
GG(a, b, c, d, x[1], 5, 0xf61e2562); /* 17 */
#define GG(a,b,c,d,x,s,ac) \
{ \
a += G(b,c,d) + x + ac; \
a = ROTATE_LEFT(a,s); \
a += b; \
}
// a += b -->> a += d
#define G(x,y,z) ((x & z) | (y & ~z))
// 依旧是一样的分析方式,这里最终其实就是修改了k值,然后同样修改了GG函数;
GG(a, b, c, d, x[1], 5, 0x46711AC1); /* 17 */
printf("[+] Round 2 第1轮明文-->>%x\n", x[1]);
printf("[+] Round 2 第1轮a-->>%x\n", a);- 对比一下,先看unidbg的结果:
[+] hook_AFA Round 2 第1轮结果aaa-->>feba0245- 本地模拟结果:
[+] Round 2 第1轮明文-->>39303834
[+] Round 2 第1轮a-->>feba0245- 再看第二轮;
v29 = __ROR4__(md5_b + v77[(v22 - 10) & 0xF] + v24 + (md5_d & ~(md5_d + v28) | (md5_d + v28) & md5_c), 23);
md5_b = md5_a + v29;
md5_a = md5_d + v28;
//所以
v29 = __ROR4__(md5_b + v77[(v22 - 10) & 0xF] + v24 + (md5_d & ~(md5_a) | (md5_a) & md5_c), 23);
md5_b = md5_a + v29;
// 标准:
GG(d, a, b, c, x[6], 9, 0xc040b340); /* 18 */
#define GG(a,b,c,d,x,s,ac) \
{ \
a += G(b,c,d) + x + ac; \
a = ROTATE_LEFT(a,s); \
a += b; \
}
// a += b -->> a += d
#define G(x,y,z) ((x & z) | (y & ~z))
// 依旧分析 这里原本赋值应该是d 但是顺序又不一样了 那么这里是改了;
//
GG(b, c, d, a, x[6], 9, 0xA90A840A); /* 18 */- unidbg结果:
[+] hook_B1A Round 2 第1轮结果bbb-->>fc8ec38f- 本地模拟结果:
[+] Round 2 第2轮明文-->>0
[+] Round 2 第2轮b-->>fc8ec38f- 再看第三轮;
v30 = __ROR4__(md5_c + v77[(v22 - 5) & 0xF] + v25 + (md5_a & ~(md5_a + v29) | (md5_a + v29) & md5_d), 18);
md5_c = md5_b + v30;
md5_b = md5_a + v29;
// 所以:
v30 = __ROR4__(md5_c + v77[(v22 - 5) & 0xF] + v25 + (md5_a & ~(md5_b) | (md5_b) & md5_d), 18);
md5_c = md5_b + v30;
// 标准:
GG(c, d, a, b, x[11], 14, 0x265e5a51); /* 19 */
#define GG(a,b,c,d,x,s,ac) \
{ \
a += G(b,c,d) + x + ac; \
a = ROTATE_LEFT(a,s); \
a += b; \
}
// a += b -->> a += d
#define G(x,y,z) ((x & z) | (y & ~z))
// 改了k值;
//
GG(c, d, a, b, x[11], 14, 0xFD1BBEF0); /* 19 */- 结果依旧是没问题,看第四轮;
md5_d = md5_b + v30 + __ROR4__(md5_d + v77[v31] + v26 + (md5_b & ~(md5_b + v30) | (md5_b + v30) & md5_a), 12);
md5_c = md5_b + v30;
// 所以:
md5_d = md5_c + __ROR4__(md5_d + v77[v31] + v26 + (md5_b & ~(md5_c) | (md5_c) & md5_a), 12);
// 这里c肯定就是最后一个
// 标准:
GG(b, c, d, a, x[0], 20, 0xe9b6c7aa); /* 20 */
#define GG(a,b,c,d,x,s,ac) \
{ \
a += G(b,c,d) + x + ac; \
a = ROTATE_LEFT(a,s); \
a += b; \
}
// a += b -->> a += d
#define G(x,y,z) ((x & z) | (y & ~z))
// k abcd顺序
GG(d, a, b, c, x[0], 20, 0x687810E5); /* 20 */- 最后这四轮的结果都是对的上的,那么这里还能像之前一样写成循环吗;这里就是很方便了,因为明文的下表不再有规律,这里按照它的流程把k表修改就行,后续的流程也是一样的,实际上就是k值还在变,最终效果如下:
/* Round 2 */
// 修改了k GG函数
GG(a, b, c, d, x[1], 5, 0x46711AC1); /* 17 */
printf("[+] Round 2 第17轮aaa-->>%x\n", a);
// 修改了k abcd顺序 GG函数
GG(b, c, d, a, x[6], 9, 0xA90A840A); /* 18 */
printf("[+] Round 2 第18轮bbb-->>%x\n", b);
// 修改了k GG函数
GG(c, d, a, b, x[11], 14, 0xFD1BBEF0); /* 19 */
printf("[+] Round 2 第19轮ccc-->>%x\n", c);
// 修改了k abcd顺序 GG函数
GG(d, a, b, c, x[0], 20, 0x687810E5); /* 20 */
printf("[+] Round 2 第20轮ddd-->>%x\n", d);
GG(a, b, c, d, x[5], 5, 0x8C37FC1B); /* 21 */
GG(b, c, d, a, x[10], 9, 0xFFFD6EC6); /* 22 */
GG(c, d, a, b, x[15], 14, 0x8867BEAC); /* 23 */
GG(d, a, b, c, x[4], 20, 0x6C96FED4); /* 24 */
GG(a, b, c, d, x[9], 5, 0xFDBF77AC); /* 25 */
GG(b, c, d, a, x[14], 9, 0xA59C8134); /* 26 */
GG(c, d, a, b, x[3], 14, 0x4AC99BE5); /* 27 */
GG(d, a, b, c, x[8], 20, 0xF66D568A); /* 28 */
GG(a, b, c, d, x[13], 5, 0xBF80B2C1); /* 29 */
printf("[+] Round 2 第29轮aaa-->>%x\n", a);
GG(b, c, d, a, x[2], 9, 0x277D05E4); /* 30 */
printf("[+] Round 2 第30轮bbb-->>%x\n", b);
GG(c, d, a, b, x[7], 14, 0xEA2C8E1F); /* 31 */
printf("[+] Round 2 第31轮ccc-->>%x\n", c);
GG(d, a, b, c, x[12], 20, 0xD58FA982); /* 32 */
printf("[+] Round 2 第32轮ddd-->>%x\n", d);- 对比一下是否一致,unidbg结果:
[+] hook_AFA Round 2 第1轮结果aaa-->>feba0245
[+] hook_B1A Round 2 第2轮结果bbb-->>fc8ec38f
[+] hook_B3A Round 2 第3轮结果ccc-->>5c5b750
[+] hook_B5A Round 2 第4轮结果ddd-->>f5d175ed
[++++++++++++++++++++++++++++++++++++++]
[+] hook_AFA Round 2 第1轮结果aaa-->>e4a9b59c
[+] hook_B1A Round 2 第2轮结果bbb-->>a0f941c0
[+] hook_B3A Round 2 第3轮结果ccc-->>7bef5e7f
[+] hook_B5A Round 2 第4轮结果ddd-->>21c39097
[++++++++++++++++++++++++++++++++++++++]
[+] hook_AFA Round 2 第1轮结果aaa-->>cc9316a2
[+] hook_B1A Round 2 第2轮结果bbb-->>7f463e02
[+] hook_B3A Round 2 第3轮结果ccc-->>a214d825
[+] hook_B5A Round 2 第4轮结果ddd-->>76543056
[++++++++++++++++++++++++++++++++++++++]
[+] hook_AFA Round 2 第1轮结果aaa-->>c16c6126
[+] hook_B1A Round 2 第2轮结果bbb-->>d0f5824e
[+] hook_B3A Round 2 第3轮结果ccc-->>42e039b5
[+] hook_B5A Round 2 第4轮结果ddd-->>1741ff4c
[++++++++++++++++++++++++++++++++++++++]
76B5CEBECD2484F78CDF7A468C133EFB- 本地模拟结果:
[+] Round 2 第17轮aaa-->>feba0245
[+] Round 2 第18轮bbb-->>fc8ec38f
[+] Round 2 第19轮ccc-->>5c5b750
[+] Round 2 第20轮ddd-->>f5d175ed
[+] Round 2 第29轮aaa-->>c16c6126
[+] Round 2 第30轮bbb-->>d0f5824e
[+] Round 2 第31轮ccc-->>42e039b5
[+] Round 2 第32轮ddd-->>1741ff4c
3aaca05518f8c10484ae242306dbabf5- 是对的上的,那么第17-32轮也就还原结束了;
2.8 第33-64轮
- 后续其实所有的步骤都是一致的,主要就是k值和运算函数,最后加的都是d,直接给出最终结果;
/* Round 3 */
// k HH函数
HH(a, b, c, d, x[5], 4, 0x3661ADB); /* 33 */
printf("[+] Round 3 第33轮aaa-->>%x\n", a);
HH(b, c, d, a, x[8], 11, 0xD93BE6CA); /* 34 */
printf("[+] Round 3 第34轮bbb-->>%x\n", b);
HH(c, d, a, b, x[11], 16, 0xE7585F52); /* 35 */
printf("[+] Round 3 第35轮ccc-->>%x\n", c);
HH(d, a, b, c, x[14], 23, 0x20C23A76); /* 36 */
printf("[+] Round 3 第36轮ddd-->>%x\n", d);
printf("[+++++++++++++++++++++++++++]\n");
HH(a, b, c, d, x[1], 4, 0xC3F22CE1); /* 37 */
HH(b, c, d, a, x[4], 11, 0xF47FB4D2); /* 38 */
HH(c, d, a, b, x[7], 16, 0x4442B612); /* 39 */
HH(d, a, b, c, x[10], 23, 0xAABC73EB); /* 40 */
HH(a, b, c, d, x[13], 4, 0xFCC24453); /* 41 */
HH(b, c, d, a, x[0], 11, 0x66657006); /* 42 */
HH(c, d, a, b, x[3], 16, 0x8E1BE7C3); /* 43 */
HH(d, a, b, c, x[6], 23, 0xFFF5BB27); /* 44 */
HH(a, b, c, d, x[9], 4, 0x867B8079); /* 45 */
printf("[+] Round 3 第45轮aaa-->>%x\n", a);
HH(b, c, d, a, x[12], 11, 0x6EA336BB); /* 46 */
printf("[+] Round 3 第46轮aaa-->>%x\n", a);
HH(c, d, a, b, x[15], 16, 0xFE09B282); /* 47 */
printf("[+] Round 3 第47轮aaa-->>%x\n", a);
HH(d, a, b, c, x[2], 23, 0xA3E07FDA); /* 48 */
printf("[+] Round 3 第48轮ddd-->>%x\n", d);
printf("[+++++++++++++++++++++++++++]\n");
/* Round 4 */
II(a, b, c, d, x[0], 6, 0x4CF3A208); /* 49 */
II(b, c, d, a, x[7], 10, 0xF708037E); /* 50 */
II(c, d, a, b, x[14], 15, 0xBDFDD143); /* 51 */
II(d, a, b, c, x[5], 21, 0x29B9C389); /* 52 */
II(a, b, c, d, x[12], 6, 0xEB1494A7); /* 53 */
II(b, c, d, a, x[3], 10, 0xD44DA632); /* 54 */
II(c, d, a, b, x[10], 15, 0x5AA195E); /* 55 */
II(d, a, b, c, x[1], 21, 0xDA6CA20A); /* 56 */
II(a, b, c, d, x[8], 6, 0xE65DAC20); /* 57 */
II(b, c, d, a, x[15], 10, 0x1E8296E0); /* 58 */
II(c, d, a, b, x[6], 15, 0xC5658374); /* 59 */
II(d, a, b, c, x[13], 21, 0xF3D1564B); /* 60 */
II(a, b, c, d, x[4], 6, 0x4212F2E5); /* 61 */
printf("[+] Round 4 第61轮aaa-->>%x\n", a);
II(b, c, d, a, x[11], 10, 0xAC6AF723); /* 62 */
II(c, d, a, b, x[2], 15, 0xFC63B7E7); /* 63 */
II(d, a, b, c, x[9], 21, 0x6450C165); /* 64 */
printf("[+] Round 4 第64轮ddd-->>%x\n", d);- 对比一下,发现结果有些不同;
本地:76b5cebebacde8178cdf7a469f6ad9da
正确:76B5CEBECD2484F78CDF7A468C133EFB- 有一些部分是一样的,其实是第二个和第四个换了一下位置;
76b5cebe 8c133efb 8cdf7a46 cd2484f7
76B5CEBE CD2484F7 8CDF7A46 8C133EFB- 最终结果如下:

- 这样就还原了这个算法,魔改点还是比较多的,但大多也是重复性的工作,且样本并未做过多阻拦,特征十分明显,还原第一大轮之后也就是重复性的工作罢了;
3. 总结
魔改点总结:
- 明文处理
- 初始化常量
- K表
- 左移次数
- abcd的顺序
- 运算函数
- 感谢大佬提供的样本(我不记得是谁了),在此致敬;
大佬带带![$[经典表情]::(左亲亲)](data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==)