IDA Pro 实战逆向破解入门到大师03:动态字符串拼接 · 变量重构

file-20260717004944298

这个案例适合大家用作实战进阶。今天我们将引入一个逆向工程中非常核心的概念:动态字符串拼接(字符串混淆的雏形)

上两个 CrackMe 中,密码(”ixiaodi668″ 和 “123456”)是完整地硬编码在程序里的,通过字符串搜索就能瞬间秒杀。而这个程序,开发者学会了“把密码拆碎了再组装”。

今天我们逐步给大家进行拆解讲解:

阶段一:破除迷雾(依然是熟悉的配方)

“同学们,拿到这段代码,第一步永远是把我们认识的老朋友还原出来。通过前面两节课的训练,大家应该能做到‘秒认’了。”

我们一起快速做一遍重命名:

  1. sub_401C85 -> 里面有提示语,显然是 printf。那个多余的 Str1[0] 依然是 IDA 分析可变参数时的瑕疵,直接忽略。
  2. sub_4011EF -> 带有 %s 格式化符号,显然是获取用户输入的 scanf
  3. sub_403CD8 -> 带有 “pause”,显然是执行系统命令的 system

清理完外围后,程序的输入输出骨架就已经非常清晰了,直接进入核心密码生成区。

阶段二:解析关键函数(C语言函数小课堂)

在这里,我们给没有 C 语言基础的学员科普两个极其常用的标准库函数,这是解开本题的关键:

  1. strcpy(目标, 源):String Copy,字符串拷贝。意思是把后面的内容,原封不动地复制给前面的变量。
  2. strcat(目标, 追加内容):String Concatenate,字符串拼接。意思是把后面的内容,接在前面字符串的尾巴上。

“开发者为了防止我们一眼看出密码,没有直接写 strcmp(输入, "密码"),而是准备了几个零件:Sourcev8v6,并且准备了一个空的大盒子 Destination(通过 memset 清零了)。接下来,程序就像玩乐高积木一样,开始组装真正的密码了。”

阶段三:沙盘推演(大脑模拟执行)

这时候,大家可以一行一行在大脑中模拟变量的变化,这种“推演感”能让新手村的各位朋友迅速建立起逆向的成就感:

  • 准备零件:
    • strcpy(Source, "gam"); -> Source 变成了 "gam"
    • strcpy(v8, "ma"); -> v8 变成了 "ma"
    • strcpy(v6, "789"); -> v6 变成了 "789"
  • 开始拼接(核心逻辑):
    • j__strcat(Destination, Source);
      • 大盒子刚开始是空的,拼接后,Destination = "gam"
    • j__strcat(Destination, v8);
      • 在尾巴上追加 v8Destination = "gam" + "ma" = "gamma"
    • j__strcat(Destination, v6);
      • 继续在尾巴上追加 v6Destination = "gamma" + "789" = "gamma789"

推演完毕,最终进入判断逻辑:

if ( !j__strcmp(Str1, Destination) )

到这里自然就恍然大悟了:程序实际上是在拿用户的输入,和拼接好的 "gamma789" 进行比对。通关密码就是 "gamma789"

阶段四:底层拔高与变量重命名(大师课)

为了更有深度的理解逆向,我们可以观察一下这三个“零件”变量在栈上的定义:

  char v6[4]; // [esp+80h] [ebp-Ch]
  char Source[4]; // [esp+84h] [ebp-8h]
  char v8[4]; // [esp+88h] [ebp-4h]

为什么偏偏都是长度为 4 的数组?

问题与答案:“比如 gam 明明只有 3 个字母,为什么要用 4 个字节存?因为 C 语言的字符串必须以 \0(空字符,占 1 个字节)作为结束标志。所以 g-a-m-\0 刚好占满 4 个字节。这也侧面印证了开发者的用心设计。”

最后,大家可以实践一下变量重构的终极形态,按 N 键重命名变量,让伪代码变得完美:

  • Str1 -> user_input
  • Destination -> real_password
  • Source -> part1
  • v8 -> part2
  • v6 -> part3

经过小迪老师的这番拆解,原本带有混淆性质的代码,就会变成极具观赏性的艺术品,大家的逆向思维也会随之完成一次升华。

file-20260717171116309

请登录后发表评论

    没有回复内容