逆向分析CrackMe系列——CrackMe004之注册码算法分析

逆向分析CrackMe系列——CrackMe004注册码算法分析

本文内容承接前面的工作,记录了自己每一步的分析过程和思路,由于内容较长,故单独写一篇。
(本系列的CrackMe资源均来自我爱破解网).

18.通过前面的分析,初步认为生成算法就藏在刚刚的两个死循环中,我们一个一个来分析:

在这里插入图片描述
在这里插入图片描述
19. 从00457FDC开始单步执行,在00457FDC处将edx指向了 [ebp-C],我们对[ebp-C]进行监控,F8继续执行,发现在call 00423348函数调用后,栈中的数据被改变了,[ebp-C] 的数据变成了用户输入的用户名“wwwwwwwwwwwwww

在这里插入图片描述
20. 继续往下执行,发现对 [ebp-C] 又被当作参数传递给了下面一个函数调用中 call 00403B7C ,但是我们并部知道该函数到底是做什么的;别急,继续往下分析,调用完函数后立马对eax进行了操作,也就是说eax应该的存放了上面函数的返回值,我们对eax进行查看,eax的值为0x0000000C,数值为12,正好是我们输入用户名的长度;那么上面函数的作用就被我们猜出来了:计算输入用户名的字符串长度。接下来再做加法:eax=eax+1E, 此处的结果为0x0C+0x1E=0x2A。

在这里插入图片描述
21. 继续往下执行,edx指向 [ebp-8],对 [ebp-8] 和寄存器进行观察,发现[ebp-8]的内容发生变化,存放的是一个字符串“42”,怎么会跑出个“42”来呢?正准确F7进去看个仔细,突然发现刚刚eax计算得到的结果“0x2A”的十进制不就是: 2*16+10=42吗。原来如此,这个函数就是一个16进制到10进制转换的函数(其中操作的参数是eax=”0x2A”,edx用于存放结果)。

在这里插入图片描述
22. 继续往下执行,发现又有新的变量出现,edx指向 [ebp-10] 同时被当作参数传入下面的函数调用中(call 00423348),这个函数怎么这么眼熟;往上看,在00457FE5同样被调用,用户获取用户输入的用户名;继续执行,发现还是进行同样的操作,只不过是将用户输入的用户名存放到 [ebp-10] 中了。

在这里插入图片描述
23. 继续往下分析,将用户名入栈,然后再此调用16进制转换10进制的函数,只不过参数变化了,eax=ebx(ebx的初始值为0),edx指向[ebp-14],由于eax=0,所以[ebp-14]指向的内存地址中什么也没有:

在这里插入图片描述
24. 继续执行,[ebp-14] 入栈,新的变量出现,eax 指向 [ebp-4],对其进行观察并且继续往下执行,发现在下面的函数调用后(call 00403C3C),[ebp-4]的值发生了改变,其内容变成了“42wwwwwwwwwwww0”;看起来好像进行了一个字符串拼接,刚刚计算出的“42”,输入的用户名“wwwwwwwwwwww”和一个“0”。

在这里插入图片描述
25. 继续往下执行,ebx=ebx+1,然后拿 ebx 与0x13(数值19)做比较,如果小于 0x13,则跳回去继续执行。Σ(っ °Д ° ;)っ 原来刚刚的循环不是死循环!!!(我明明按着F8就没松,看着一直循环就觉得是死循环了) 尴尬!!!,ԾㅂԾ,
 
26. 不过,问题不大,哈哈哈哈… 继续往下分析。既然是拿ebx进行判断,所以我们主要关注与ebx有关的部分,也就是 [ebp-14] 和 [ebp-4] ,观察这两个内存的数据变化,重复进行循环。
 
27. 发现 [ebp-14] 指向的内存中依然没有数据,但是 [ebp-4] 中的数据发生了变化,由“42wwwwwwwwwwww0” 变成了“42wwwwwwwwwwww1”;再循环一次,又变成了“42wwwwwwwwwwww2”;再循环一次,又变成了“42wwwwwwwwwwww3”;我好像明白了什么,猜测跳出循环时[ebp-4]指向的内存数据应该是“42wwwwwwwwwwww18”,我们在紧跟循环后的一条语句下断(也就是地址00458031处)

在这里插入图片描述
28. 下断后,F9继续执行,断下来发现 [ebp-4] 指向的内存数据果然是“42wwwwwwwwwwww18”,同时 [ebp-14] 的指向的数据变为了“18”。

在这里插入图片描述
29. 此处的cmp进行了一个比较,为什么刚刚计算的值一个都没有用到?╰(°▽°)╯;cmp中 [esi+30C] 指向的内存数据与 0x85 进行比较 (此时的esi=010DB1970,[esi+30C] 中 (地址01DB1C7C) 什么也没有 (;′⌒`) )

在这里插入图片描述
30. 那么esi到底是哪来的呢?只好回去监控 esi 了,发现刚刚的循环里面并没有 esi 的相关操作,往上再看,找到了犯罪嫌疑人地址00457FCA,正好在我们刚刚下断的不远处,由 eax 赋值得到:

在这里插入图片描述
31. 当我们执行到下断处(00457FB8),发现此时的eax就是01DB1970,也就是说在运行到此处前eax已经被赋值了,这并不是真正的犯罪嫌疑人,这可咋整?
 
32. 别方!!既然是在这个模块调用前就赋值了,那么我们找到调用前模块即可:
      一直单步(F8)运行到retn,直到返回到一个新的代码块,首先遇到第一个retn:(一般情况下用 ctrl+F9 即可,但是在这里程序并不会停下来,就是这么妖(⊙﹏⊙) )

在这里插入图片描述
33. 执行下去,发现跳到了下面的不远处,有点奇怪:

在这里插入图片描述
34. 发现并没有修改eax的操作,继续跟踪,直到 retn,查看堆栈,发现回跳转到地址00424658处:

在这里插入图片描述
35. 跟踪到00424658:

在这里插入图片描述
36. 发现前面是一个函数调用,虽然此处还有一个跳转,但是由于是从栈中返回的地址,则只可能是由函数调用这个分支往下进行的,也就是说我们刚刚分析的一大堆都是在前面这个函数中实现的。好了,我们离真正的犯罪嫌疑人更进一步了,继续往前分析,发现了一个eax的操作(mov eax, dword ptr [ebx+10C]),二话不说,在地址0042464C下断:

在这里插入图片描述
37. 发现,此时的 [ebx+0C] 为一个函数的入口地址。函数的入口地址是不会改变的,也就是说此处的跟踪是没有意义的。咋整?内存断点,看看到底是谁改变了内存中的值。继续点击灰色框,发现程序并不会断下来,说明此时的序列号检测过程已经结束;试试修改序列号,发现程序断下来:

在这里插入图片描述
38. 对所有的内存内容和堆栈观察,然后单步进行调试,发现在00457D69 处调用函数call 00423348函数时 [ebp-28] 的值由00000000变成了输入的用户名“wwwwwwwww”,同时出现了很多与用户名和输入序列号相关的信息:

在这里插入图片描述
在这里插入图片描述
39. 继续执行,发现用户名被赋值到 eax 中,经00457D76处的函数调用后,eax=0x0C(数字12),很眼熟,我们输入的用户名成都正好是12;那么该函数的功能是计算输入用户名的长度;这个部分的代码怎么这么眼熟,看看笔记发现,此处和之前 步骤21 中分析的代码块功能一样,应该是同一个函数,但是其中的某些局部变量和常量发生了变化;分析完这段代码后,发现与之前的代码块一样,还是没有什么实质性的操作。

  • 步骤21处:
    在这里插入图片描述
  • 现在
    在这里插入图片描述

 
 
40. 有点伤脑筋,这个内存断点先不管了,看下一个内存断点,在00457EF5处发现与步骤18处有代码相似的部分,但是其中多了一个 cmp 和跳转,此处的代码块和步骤18处的代码块几乎完全一样,也没什么实质性的操作:

在这里插入图片描述
41. 有点无从下手了,看看我们最初下内存断点的地方,有一个其他的字符串:
在这里插入图片描述
42. 我们来对它下内存断点跟跟看,在修改注册码后断下来:

在这里插入图片描述
43. 继续分析,在[ebx+318]中发现了另一个字符串 “黑头Sun Bird17dseloffc-012-OK wwwwwwwwwwww";其中**”黑头Sun Bird“为程序界面提示的字符串,“17“在前面出现过,” dseloffc-012-OK “为我们跟踪的字符串,也是直接初始化的,”wwwwwwwwww“**为输入的用户名,我们猜测它为正确的序列号:

在这里插入图片描述
44. 用刚刚得到的新字符串进行测试,成功!:

在这里插入图片描述
45. 那么注册码为几个字符的拼接: “黑头Sun Bird“+“17”+“dseloffc-012-OK”+“wwwwwwwwwwww”。其中只有“17“这个数字不知道怎么得到的,我们往回看,发现是由 [ebx+2F8]内存中的数据 我们称之为 L 加上0x05后转换成10进制而成;即:

  • 注册码 = "黑头Sun Bird“+”L+5“+“dseloffc-012-OK”+用户名

在这里插入图片描述
46. 但是在改代码部分并没有 [ebx+2F8] (内存[01D81C68]) 数据的修改,我们对它下内存断点(write)

在这里插入图片描述
47. 发现[01D81C68]的值是由地址00457E59的函数计算得到,下断,然后进去分析。还有函数调用,继续进去分析,记住此时ecx=0, edx=0E:

在这里插入图片描述
48. 继续跟,发现00424308处的函数对变量4进行了修改,而eax由变量4赋值的得到:

在这里插入图片描述
49. 跟进去继续看,发现此部分的代码块很长,我们直接对变量4下内存断点(write)

在这里插入图片描述
50. 修改用户输入名后断到0042705F处:

在这里插入图片描述
51. 发现是由一个user32.dll库中的函数返回的eax的值;

在这里插入图片描述
52. 放弃了,虽然一开始看到数字就怀疑是用户名的长度,通过多组的测试也能确定,比如,

  • 输入123,L变为3;
    输入1234,L变为4;
    输入12345;L变为5;
    输入wwwwww,L变为6;
    输入wwwwwww,L变为7

可以推断出 L 是输入用户名的长度。
所以整个序列号的生成算法为:
注册码 = "黑头Sun Bird“+”输入用户名长度+5“+“dseloffc-012-OK”+输入的用户名
 
53. 总结:

  • 程序作者设计了很多干扰代码断,也就是前面分析的步骤18—步骤28部分,其实在整个分析过程中还有其他地方有相似的干扰代码断。
  • 整个分析过程比较冗余,并不简洁,主要是记录整个分析过程中的思路和分析流程
  • 看了看其他人的分析,直接用Delphi语言的反编译器分析起来会快得多,直接从几个特定函数的入口地址开始分析即可。

猜你喜欢

转载自blog.csdn.net/weixin_39561364/article/details/108246298