拆解习题

习题十 chap7-10 难度高

名称:CrackMe v3.0a
翻译:看雪(译于2000/2/29)

语言: Visual Basic 6
难度:高
工具:Softice, SmartCheck , 十六进制编辑工具

crack 方法:补丁 , SmartCheck 分析
关于这软件保护方法:它是基于两段不同程序计算你输入的序列号,结果必须相等。
目的:这教程主要对象是初学
者,我把 crack 过程详细列出。我建议你们先看完这篇教学,然后自己独立一步步地练练 ....

    SmartCheck 运行该软件

我不再讲述如何配制 SmartCheck 了,如你不知,请参考本课第一节。在 SmartCheck 下运行这程序,键入任何序列号,我用的是 12121 ,点击“ Check it ”。这时你将注意你的微机不停运转,不会是病毒吧? SmartCheck 也不停运行 ....

OK. 读读该 CrackMe Readme.txt 。你将看到作者告诉你这 CrackMe 里有防 SmartCheck 的程序代码。哦,原来如此,还能防 SmartCheck... ,这时你的计算机正在疯狂运转 ... ,你可用 CTRL ALT+DEL, 只能同时按一下啊,不然就真的要重新启动了,此时在 CrackMe 这一行,选结束任务 ....

最后你的计算机停下来了,你有可能看到一对话框 "You have SmartCheck loaded!...Close it and try again!!!"

因此我们不能用 SmartCheck .. ... ,这也太伤自尊呢,我们会有办法。 ^-^

   Softice 来助一臂之力

这时我们让 SmartCheck 休息下, SOFTICE 上场,我说过了这是 VB6 程序,因此你们需在 Winice.dat 中装载 msvbvm60.dll 。在 SOFTICE 下键入几个常用的 VB 断点函数,我首先就用 "bpx msvbvm60!__vbastrcomp" 试试。

** 注意:因为这是 VB6 运行库,所以要在函数前加 "msvbvm60!"

设好断点后,运行 CrackMe (此时不在 SmartCheck 下运行了),键入任何序列号,点击 "Check it" 。你将被中断。

我们用 SOFTICE 目的是因为该 CrackMe 有一段程序检测 SmartCheck 存在,它的原理是根据这小段程序运行时间的长短来判断的有无 SmartCheck 存在的,如果你运行时间长,意味着 SmartCheck 存在。你们用 SmartCheck 装载任何一 VB 程序与正常运行时对比一下,就会发现运行时间相差明显。

现在你在 SOFTICE 里,我们需到主程序代码里找一找哪里有防 SmartCheck 代码 ....

F10 走出,一直到你看到  BJCM30A!.. 你会在 00404401 这一行。

:00404391  3BC7                CMP     EAX,EDI  <-- 开始一个大循环

:00404393  0F84C8000000        JZ      00404461  <-- 跳出此大循环

:00404399  B801000000          MOV     EAX,00000001

..

..   __________ 剪断 ___________

..

:004043E1  899DF8FEFFFF        MOV     [EBP-0108],EBX

:004043E7  899DE8FEFFFF        MOV     [EBP-0118],EBX

:004043ED  FF1538104000        CALL    [MSVBVM60!__vbaVarForInit]

:004043F3  3BC7                CMP     EAX,EDI  <-- 开始一小循环

:004043F5  744D                JZ      00404444  <-- 跳出此小循环

:004043F7  68342A4000          PUSH    00402A34

:004043FC  68342A4000          PUSH    00402A34

:00404401  FF1568104000        CALL    [MSVBVM60!__vbaStrCmp] <-- 中断在此

:00404407  85C0                TEST    EAX,EAX

:00404409  751F                JNZ     0040442A

..

..   __________ 剪断 __________

..

:0040443B  51                  PUSH    ECX

:0040443C  FF15E8104000        CALL    [MSVBVM60!__vbaVarForNext]

:00404442  EBAF                JMP     004043F3  <-- 返回小循环

:00404444  8D95A4FEFFFF        LEA     EDX,[EBP-015C]

..

..   __________ 剪断 ___________

..

:00404455  51                  PUSH    ECX

:00404456  FF15E8104000        CALL    [MSVBVM60!__vbaVarForNext]

:0040445C  E930FFFFFF          JMP     00404391  <-- 返回大循环

此时你把中断给清掉,如果你按 F10 运行,会发现你在这段代码会一周一周运行很长时间 ....

** 那就是我怎么发现这部分是防 SmartCheck 的代码了。

参考上面的代码,你将注意我标上的两个循环,一个小的,一个大的。小的在大的里面。

如果你在 004043F5 跳出小循环,你将进入大循环,而大循环再次将你带入小循环。因此解决办法是在 00404393 处跳出大循环。

现在你想想,如果我们能在 00404393 处跳出大循环,就能用 SmartCheck 跟踪它了。好让我们将:

:00404393  0F84C8000000        JZ      00404461  <-- 跳出此大循环

改成:      0F85C8000000        JNZ     00404461

这样就不躲过这段循环了吧吗?

哈哈,用十六进制工具,修改 CrackMe 主文件   ,将 0F84C8000000 改成 0F85C8000000 。记得备份一下。

    还我自尊, SmartCheck 再现江湖

SmartCheck 重新装载该软件,键入 12121, 点击 "Check it" 。马上蹦出 "Sorry tyr again" ,这时你的微机不会发疯了 :-

现在点击 Command1_Click  前的 "+" 展开它,你你看到很多很多的 Len Asc Hex$   Mid$ ,这都是些处理你输入的序列号基本命令。

我解释一下:

Len -  得到字符串的长度

Asc -  转换字符为 ascii 的十进制

Hex$ -  转换字符为十六进制

Mid$ - STRING 类型转换 CHAT 类型

UCase$ -  将小写字母转换为大写字母

看看 Command1_Click 里的这些信息,找找感觉 ...

你注意到它们都是些重复内容 ... 因此我们找些不同的,暂时,我们不用 "Show All Events" 此招。

Command1_Click 附近你会发现:

Len(String:"12121") returns LONG:5

...

Mid$(String:"12121",long:?,VARIANT:Integer:1)

Mid$(String:"12121",long:?+1,VARIANT:Integer:1)

哪处?长度由 1 5

** 如果你在这儿选择 show all event ,会发现每个字符同它下一个依次比较。

继续,在几行后(实际重复 3 次)

Text1.Text

Len(String:"12121") returns LONG:5

** 它的作用是取得你输入序列号的长度

为什么会运行 3 次?不管那么多了。

然后在那儿

Left(VARIANT:VT_DISPATCH:....., long:1)

Asc(String:"1") returns Integer:49

** 这里是取你输入的序列号第 1 个字符并转换成十进制。 "1" 字符的十进制是 49 。它会在 Command1_Click 里重复许多次,因此你很容易发现它。

你会看到许多 Hex$ ,其中从上到下,有一处结果为 245 (下面有 5 处的结果都为 245, 我们都要进去分析)。

具体这样:

Hex$(VARIANT:Long:245)

嗯? 245 哪里来的??

Ok. 选中这行,然后选择 "Show All Events"...

你将看到上面许多  __vbaStrCmp

 

Hex$  上一行,你将看到:

__vbaStrCmp(String:"*", String:"*") returns DWORD:0

呵呵 ...  比较 ...

看到字符 "*' 吗,这是乘的表示方法。

这里,有点象什么东西相乘结果为 245...

试试 49*5=245

哪里得到 5 49 呢?

如果你向上滚动一点,你会看到 5 是你输入的序列号的长度, 49 是第一个字符的 Asicii 的十进制。

Hex$ 下面一行,你会看到:

__vbaStrMove(String:"F5",...)...

这里 F5 哪里来?

试试转换一下 245 到十六进制 .... 你会得到 F5!!!!!

** 转换进制可用 windows 下的附件中的计算器完成。

在这里重复了几次,再没有什么有用的东西了 ... 要记住刚才的神奇数字 245 F5.

现在,返回刚才的模式 "Show Errors and Specific Events" 

定位于第二个是 245 Hex$ 的: Hex$(VARIANT:Long:245)

3 行后,你将看到:

Mid$(String:"12121",long:2,VARIANT:Integer:1)

Asc(String:"2") returns Integer:50

点击 Asc 这一行,再次选择 "Show All Events"

Asc 这行下面:

__vbaVarAdd(VARIANT:Integer:49,VARIANT:Integer:50)...

**49 50 ;我希望你们还记得 49 是字符 "1" Ascii 的十进制, 50 是字符 "2" Ascii 的十进制

下面一行:

__vbaVarMove(VARIANT:Integer:99,VARIANT:Integer:49)...

99 替代内存的 49 99 49 + 50 的和。

再次选择 "Show Errors and Specific Events" 

OK. 我们己看了 CrackMe 把第 1 和第 2 个字符转换的过程,它们是把它转换成十进制再相加。

我们用同样方法需要继续寻找第 3 4 5 个字符。

要第 3 个是 245 Hex$ 下面,你会看到:

Mid$(String:"12121",long:3,VARIANT:Integer:1)

它大约在这一行的 Mid$(String:"12121",long:2,VARIANT:Integer:1) 下面 29 行处。

Mid$(String:"12121",long:3,VARIANT:Integer:1)

** 12121 得到第 3 个字符。

Asc(String:"1") returns Integer:49

点击 Asc 这行,并再次选择  "Show All Events" 

两行下面:

__vbaVarAdd(VARIANT:99, VARIANT:Integer:49)...

**49 "1" 字符十进制)加上 50 "2" 字符十进制)

__vbaVarMove(VARIANT:Integer:148, VARIANT:99)...

** 和上面作用一样

再次选择  "Show Errors and Specific Events" 

在第 4 个是 245 Hex$ 下面,也就是 Mid$(String:"12121",long:3,VARIANT:Integer:1)29 行后,你会看到:

Mid$(String:"12121",long:4,VARIANT:Integer:1)

** 得到 12121 的第四字符

Asc(String:"2") returns Integer:50

点击 Asc 这行,并再次选择 "Show All Events" 

两行下面:

__vbaVarAdd(VARIANT:Integer:148,VARIANT:Integer:50)...

__vbaVarMove(VARIANT:Integer:198,VARIANT:Integer:148)...

** 作用和上面类似。

一直这样你会发现:  Mid$(String:"12121",long:5,VARIANT:Integer:1)

大概再向下 29 行,你会看到:

Mid$(String:"12121",long:5,VARIANT:Integer:1)

** 12121 得到第 5 个字符。

Asc(String:"1") returns Integer:49

点击 Asc 这行,并再次选择  "Show All Events" 

__vbaVarAdd(VARIANT:198, VARIANT:Integer:49)...

__vbaVarMove(VARIANT:Integer:247, VARIANT:198)...

** 作用和上面类似。

现在,看完这行,用 "Show Errors and Specific Events" 返回。

你将看到两行:

Mid$(String:"12121",long:5,VARIANT:Integer:1)

Hex$(VARIANT:Integer:247)

如果你点击 Hex$ 这行,并选择  "Show All Events" 

__vbaStrMove(String:"F7",...)...

现在有些东西再次出现 ... (我是指 F7

试着将 247 转换成十六进制,你将得到 F7

下面一行:

__vbaStrCopy(String:"=",...)...

看看是不是很有趣 !!!

点击 Hex$(VARIANT:Integer:247)  并再次  "Show Errors and Specific Events".

在这行和下面几行你将看到 F5 F7, 你们仍旧记得 F5 F7 吗?

滚动字幕往下看:

Hex$(VARIANT:Boolean:False)

点击这行后 "Show All Events"

Ahh...  你将再次看到几个 __vbaStrCmp 

__vbaStrCmp(String:"=",String"=")...

它们在这最后一行  Hex$(VARIANT:Boolean:False) 的上面

因此想想,它们会怎样处理呢?

分析一下 ...

F5 -  输入字符串长度乘以第 1 个字符(十进制)然后转换为十六进制;

F7 -  所有字符(十进制)的和然后转为十六进制。

现在又有  "="  符号 ...

让我们试一试 ...

假设我们不改变值 F5. 我必需输入 5 个数字,第一个是 1. 现在我们需要把 F7 变成 F5.

F7-F5=2, 因此我们的 5 个数字和应为 247-2=245 ,就是 12121 (49 + 50 + 49 + 50 + 49) 12020 (49 + 50 + 48 + 50 + 48) 代替,只要结果是 245 就可以,满足这条件数字很多。 245 的十六进制是 F5.

你用此数字输入,呵, "Good job, tell me how you do that!"

     小结

看到这个计算如此复杂,我想你独自用 SOFTICE 是不可能得出序列号的。另外,你们也应熟悉 SmartCheck 用法了。