
同学们,这又是一个极佳的新手实战案例,如果说前几个 CrackMe 还是在“玩转字符串”,那么到了 Stage 1-04,我们终于正式踏入了逆向工程的深水区:密码学与算法混淆(内存加密)。
先补一句,所有的案例资源下载都在 www.52xuejishu.com,0基础学逆向,就找小迪老师
这个案例对于新手的最大痛点在于:IDA 还原出来的指针操作非常反人类。 但是别怕,有小迪老师在,让我带大家破除对指针的恐惧。
阶段一:常规清理与“反常点”嗅探
“同学们,拿到代码,轻车熟路的第一步:把熟悉的外围函数全部重命名!sub_401C85 是 printf,sub_4011EF 是 scanf,sub_403CD8 是 system。干完这些脏活累活后,我们来看主逻辑。”
清理后的主函数如下:
strcpy(Str2, "109!4");
printf("Enter password: ");
sub_402004(Str2); // <--- 这里的操作非常可疑!
scanf("%s", Str1);
if ( !strcmp(Str1, Str2) )
printf("OK\n");

思考:
程序先是把 "109!4" 复制给了 Str2。按照之前几关的逻辑,密码应该就是 "109!4"。但是!在让用户输入密码之前,程序悄悄调用了一个 sub_402004(Str2)。
这说明什么?这说明程序在内存里对刚才那个假密码进行了“加工(加密)”! 我们输入的密码最后是和加工后的 Str2 进行比较的。
阶段二:深入敌营(破解恶心的指针运算)
跟进 sub_402004,发现它只是个跳板(thunk),直接来到核心函数 sub_4088B0。


“现在,我们要面对新手最害怕的东西了——IDA 翻译出来的奇葩指针运算 *(_BYTE *)(i + a1)。别怕,小迪老师带你们一行一行把它‘说成人话’!”
展示核心代码并进行拆解:
int __cdecl sub_4088B0(int a1) // a1 其实就是传进来的字符串 Str2 的地址
{
int result;
int i;
for ( i = 0; *(_BYTE *)(i + a1); ++i )
{
*(_BYTE *)(i + a1) ^= 0x55u;
result = i + 1;
}
return result;
}
硬核知识点拆解(重点讲解):
i + a1是什么?a1是字符串的首地址(比如地址是 1000)。当i=0时,它是第一个字符的地址;i=1时,它是第二个字符的地址。(_BYTE *)是什么? 由于 IDA 暂时不知道a1是个字符串(所以给它标了int类型),为了读出一个字符,它必须强行把这个地址转换成字节指针(单字节)。- 最外层的
*是什么? 指针解引用!拿到那个地址里存的具体字符。 - 一句话总结:
*(_BYTE *)(i + a1)这种吓人的写法,其实就是 C 语言里最简单的数组访问:a1[i]!
阶段三:算法揭秘与密码还原
把代码翻译成“人话”后,这个加密算法其实简单得令人发指:
for ( i = 0; a1[i] != '\0'; ++i ) // 当 a1[i] 不是字符串结尾时循环
{
a1[i] = a1[i] ^ 0x55; // 将每个字符与 0x55 进行异或运算
}
“真相大白了!这是一个非常经典的单字节 XOR(异或)加密。在逆向工程中,异或操作有一个极其美妙的特性:它是对称的! 也就是说,如果 A ^ 0x55 = B,那么 B ^ 0x55 必定等于 A。这经常被用来做简单的隐藏。”
** 小迪老师和大家一起算真密码:**
原始假密码是:"109!4"
我们需要将它的每一个字符对应的 ASCII 码,与 0x55 进行异或(你可以打开 Windows 程序员计算器,或者写个极其简单的 Python 脚本):
'1'(十六进制 0x31) ^ 0x55 = 0x64 -> ASCII 字符d'0'(十六进制 0x30) ^ 0x55 = 0x65 -> ASCII 字符e'9'(十六进制 0x39) ^ 0x55 = 0x6C -> ASCII 字符l'!'(十六进制 0x21) ^ 0x55 = 0x74 -> ASCII 字符t'4'(十六进制 0x34) ^ 0x55 = 0x61 -> ASCII 字符a
所以,经过加密函数加工后,内存里的 Str2 变成了 "delta"。
真正的通关密码就是:delta!
阶段四:给 IDA 施加魔法(工具调校展示)
最后,作为大师级的收尾,小迪老师一定要教大家怎么让 IDA 自己把那些难看的代码变漂亮。
- 回到
sub_4088B0的定义处。 - 选中参数
int a1,按下快捷键Y(修改类型)。 - 将类型强行改为
char *a1。 - 按下
F5刷新。
见证奇迹的时刻: 刚才那堆恶心的 *(_BYTE *)(i + a1),会瞬间变成干净清爽的 a1[i]!
给新手的 Takeaway (课后总结):
- 不要相信眼睛看到的硬编码字符串:只要字符串在比对之前被函数碰过,它就一定被加工过了。
- 看穿 IDA 的指针伪装:记住
*(_BYTE *)(变量 + 索引)这个句式,以后看到它,在大脑里自动替换成数组[索引]。 - 掌握异或 (XOR) 特性:遇到
^符号,要知道这是最低成本的加密/解密方式。



没有回复内容