【文章标题】: “以彼之道,还施彼身”——对付CrackMe之小蜜蜂v1
【文章作者】: petnt
【作者邮箱】: petnt@sohu.com
【软件名称】: 小蜜蜂V1
【下载地址】: 见附件
【使用工具】: OllyDBG; Stud_PE; UltraEdit_32
【操作平台】: XP SP2
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
--------------------------------------------------------------------------------
【详细过程】
为了便于总结和以后复习,下面将整个过程分为三部分:下断、分析 和编写注册机。
第一部分 下断
其实,如何下断一直是困扰小菜我的一个问题,可能很多新手也会被同样的问题困扰着。无奈好多大侠根本不屑提及此类问题,也只好抱块石头,慢慢品尝其中滋味。真心希望各位大侠在写破文的时候,能顺便提及一下如何下断,小菜在此将不胜感激。
这个CrackMe的下断确实让我费了不少功夫,在我有限的知识水平内根本断不下来,字符串、API都无从参考。从头跟踪一下,发现小蜜蜂把所有重要的操作都放在了一个附属文件krnln.fnr中。这个文件像是Dll,在被释放后就被加载到进程中了。即使在这个文件中,容易被下断了函数好像也没有直接调用,相关功能小菜我也分析不出他是如何实现的。我虽然用下面的方法断下了程序,但整个过程显得过分的笨拙,希望路过的大侠能从中指点一二。
OllyDBG载入,F9,Alt+W,我们会发现只有两个 EDIT是保持原貌,其他的控件都已经罩上了马甲。点出小蜜蜂,输入假的用户名和注册码,回到OllyDbg的 windows ,Afx类的行上右键,在ClassProc上切换断点。再次点击小蜜蜂,程序被断在系统领空。观察堆栈我们发现这是个默认的消息处理函数,当然我们关心的并不是这些默认的消息处理,这样做的目的只是为了让我们在点击注册按钮后程序被断下。按住F9,界面会被慢慢的刷出来。鼠标移动到注册按钮之上,按住F9到程序运行,按下左键。程序被断下,Alt+M,选择小蜜蜂的Tls段,右键,设置内存访问断点,双击取消系统领空的断点,F9,程序被断下。(上述过程中省略了数万字的无用过程)
第二部分 分析
由于下面过程中涉及到了我不熟悉的指令(好像是浮点操作,更大的原因是跟踪krnln.fnr里面函数的复杂性),下面的分析(特别是对Call的分析)包含了大量的个人猜测成分,仅供参考。
0041E89C 55 push ebp ; 程序被我们断在了这里
0041E89D 8BEC mov ebp, esp
0041E89F 81EC 44000000 sub esp, 44
0041E8A5 C745 FC 0000000>mov dword ptr [ebp-4], 0
0041E8AC C745 F8 0000000>mov dword ptr [ebp-8], 0
0041E8B3 C745 F4 0000000>mov dword ptr [ebp-C], 0
0041E8BA 6A FF push -1
0041E8BC 6A 08 push 8
0041E8BE 68 10000116 push 16010010
0041E8C3 68 01000152 push 52010001
0041E8C8 E8 FC0A0000 call 0041F3C9 ; 读取用户名
0041E8CD 83C4 10 add esp, 10
0041E8D0 8945 F0 mov [ebp-10], eax
0041E8D3 68 04000080 push 80000004
0041E8D8 6A 00 push 0
0041E8DA 8B45 F0 mov eax, [ebp-10]
0041E8DD 85C0 test eax, eax
0041E8DF 75 05 jnz short 0041E8E6 ; 用户名为空不跳
0041E8E1 B8 8A014100 mov eax, 0041018A
0041E8E6 50 push eax
0041E8E7 68 01000000 push 1
0041E8EC BB 30010000 mov ebx, 130
0041E8F1 E8 B50A0000 call 0041F3AB ; 获取用户名长度
0041E8F6 83C4 10 add esp, 10
0041E8F9 8945 EC mov [ebp-14], eax
0041E8FC 8B5D F0 mov ebx, [ebp-10]
0041E8FF 85DB test ebx, ebx ;
0041E901 74 09 je short 0041E90C
0041E903 53 push ebx
0041E904 E8 AE0A0000 call 0041F3B7 ; 好像是清理工作
0041E909 83C4 04 add esp, 4
0041E90C 8B45 EC mov eax, [ebp-14]
0041E90F 33C9 xor ecx, ecx
0041E911 50 push eax
0041E912 8D45 FC lea eax, [ebp-4]
0041E915 8BD8 mov ebx, eax
0041E917 58 pop eax
0041E918 41 inc ecx
0041E919 51 push ecx
0041E91A 53 push ebx
0041E91B 890B mov [ebx], ecx
0041E91D 50 push eax
0041E91E 3BC8 cmp ecx, eax ; 用用户名长度构造循环
0041E920 0F8F 17010000 jg 0041EA3D ; 不小于 转移
0041E926 6A FF push -1
0041E928 6A 08 push 8
0041E92A 68 10000116 push 16010010
0041E92F 68 01000152 push 52010001
0041E934 E8 900A0000 call 0041F3C9 ; 获取用户名
0041E939 83C4 10 add esp, 10
0041E93C 8945 F0 mov [ebp-10], eax
0041E93F 68 01030080 push 80000301
0041E944 6A 00 push 0
0041E946 FF75 FC push dword ptr [ebp-4] ; 循环数
0041E949 68 04000080 push 80000004
0041E94E 6A 00 push 0
0041E950 8B45 F0 mov eax, [ebp-10]
0041E953 85C0 test eax, eax ; 判断用户名是否为空
0041E955 75 05 jnz short 0041E95C
0041E957 B8 8A014100 mov eax, 0041018A
0041E95C 50 push eax
0041E95D 68 02000000 push 2
0041E962 BB 44010000 mov ebx, 144
0041E967 E8 3F0A0000 call 0041F3AB ; 取用户名第循环数位
0041E96C 83C4 1C add esp, 1C
0041E96F 8945 EC mov [ebp-14], eax
0041E972 8B5D F0 mov ebx, [ebp-10]
0041E975 85DB test ebx, ebx ; 用户名是否为空
0041E977 74 09 je short 0041E982
0041E979 53 push ebx
0041E97A E8 380A0000 call 0041F3B7
0041E97F 83C4 04 add esp, 4
0041E982 DB45 EC fild dword ptr [ebp-14]
0041E985 DD5D E4 fstp qword ptr [ebp-1C]
0041E988 DD45 E4 fld qword ptr [ebp-1C]
0041E98B DC05 8B014100 fadd qword ptr [41018B] ; 公用变量 27.000000000
0041E991 DD5D DC fstp qword ptr [ebp-24]
0041E994 DD45 DC fld qword ptr [ebp-24]
0041E997 DC0D 93014100 fmul qword ptr [410193] ; 公用变量 4.0000000000
0041E99D DD5D D4 fstp qword ptr [ebp-2C]
0041E9A0 68 01060080 push 80000601
0041E9A5 68 00003B40 push 403B0000
0041E9AA 68 00000000 push 0
0041E9AF 68 01060080 push 80000601
0041E9B4 FF75 D8 push dword ptr [ebp-28]
0041E9B7 FF75 D4 push dword ptr [ebp-2C]
0041E9BA 68 02000000 push 2
0041E9BF BB 48000000 mov ebx, 48
0041E9C4 E8 E2090000 call 0041F3AB ; 注意 Eax and Edx
0041E9C9 83C4 1C add esp, 1C
0041E9CC 8945 C4 mov [ebp-3C], eax
0041E9CF 8955 C8 mov [ebp-38], edx
0041E9D2 DD45 C4 fld qword ptr [ebp-3C]
0041E9D5 E8 71FAFFFF call 0041E44B ; 变换 Eax 正确注册码
0041E9DA 68 01030080 push 80000301
0041E9DF 6A 00 push 0
0041E9E1 50 push eax
0041E9E2 68 01000000 push 1
0041E9E7 BB D4010000 mov ebx, 1D4
0041E9EC E8 BA090000 call 0041F3AB ; 是将Eax 转换成 ASCII
0041E9F1 83C4 10 add esp, 10
0041E9F4 8945 C0 mov [ebp-40], eax
0041E9F7 FF75 C0 push dword ptr [ebp-40]
0041E9FA FF75 F8 push dword ptr [ebp-8]
0041E9FD B9 02000000 mov ecx, 2
0041EA02 E8 9CFDFFFF call 0041E7A3 ; 合并 字符串
0041EA07 83C4 08 add esp, 8
0041EA0A 8945 BC mov [ebp-44], eax
0041EA0D 8B5D C0 mov ebx, [ebp-40]
0041EA10 85DB test ebx, ebx
0041EA12 74 09 je short 0041EA1D
0041EA14 53 push ebx
0041EA15 E8 9D090000 call 0041F3B7
0041EA1A 83C4 04 add esp, 4
0041EA1D 8B45 BC mov eax, [ebp-44]
0041EA20 50 push eax
0041EA21 8B5D F8 mov ebx, [ebp-8]
0041EA24 85DB test ebx, ebx
0041EA26 74 09 je short 0041EA31
0041EA28 53 push ebx
0041EA29 E8 89090000 call 0041F3B7
0041EA2E 83C4 04 add esp, 4
0041EA31 58 pop eax
0041EA32 8945 F8 mov [ebp-8], eax
0041EA35 58 pop eax
0041EA36 5B pop ebx
0041EA37 59 pop ecx
0041EA38 ^ E9 DBFEFFFF jmp 0041E918 ; 循环完毕将产生注册码中间部分
0041EA3D 83C4 0C add esp, 0C
0041EA40 837D FC 02 cmp dword ptr [ebp-4], 2 ; cmp 用户名长度+1,2
0041EA44 0F8E D5000000 jle 0041EB1F
0041EA4A 6A FF push -1
0041EA4C 6A 08 push 8
0041EA4E 68 11000116 push 16010011
0041EA53 68 01000152 push 52010001
0041EA58 E8 6C090000 call 0041F3C9 ; 读取注册码
0041EA5D 83C4 10 add esp, 10
0041EA60 8945 EC mov [ebp-14], eax
0041EA63 68 01030080 push 80000301
0041EA68 6A 00 push 0
0041EA6A 68 04000000 push 4
0041EA6F 68 04000080 push 80000004
0041EA74 6A 00 push 0
0041EA76 8B45 EC mov eax, [ebp-14]
0041EA79 85C0 test eax, eax
0041EA7B 75 05 jnz short 0041EA82 ; 注册码是否为空
0041EA7D B8 8A014100 mov eax, 0041018A
0041EA82 50 push eax
0041EA83 68 02000000 push 2
0041EA88 BB 38010000 mov ebx, 138
0041EA8D E8 19090000 call 0041F3AB ; 取 注册码 后四位
0041EA92 83C4 1C add esp, 1C
0041EA95 8945 E8 mov [ebp-18], eax
0041EA98 8B5D EC mov ebx, [ebp-14]
0041EA9B 85DB test ebx, ebx
0041EA9D 74 09 je short 0041EAA8 ; 注册码是否为空
0041EA9F 53 push ebx
0041EAA0 E8 12090000 call 0041F3B7
0041EAA5 83C4 04 add esp, 4
0041EAA8 68 6A000000 push 6A ;
0041EAAD B8 9B014100 mov eax, 0041019B ;
0041EAB2 8945 E4 mov [ebp-1C], eax
0041EAB5 8D45 E4 lea eax, [ebp-1C]
0041EAB8 50 push eax
0041EAB9 E8 98060000 call 0041F156 ; 产生注册码 后四位
0041EABE 8945 E0 mov [ebp-20], eax
0041EAC1 8B5D E4 mov ebx, [ebp-1C]
0041EAC4 85DB test ebx, ebx
0041EAC6 74 09 je short 0041EAD1
0041EAC8 53 push ebx
0041EAC9 E8 E9080000 call 0041F3B7
0041EACE 83C4 04 add esp, 4
0041EAD1 8B45 E0 mov eax, [ebp-20]
0041EAD4 50 push eax
0041EAD5 FF75 E8 push dword ptr [ebp-18]
0041EAD8 E8 22FDFFFF call 0041E7FF ;比较 注册码后四位
0041EADD 83C4 08 add esp, 8
0041EAE0 83F8 00 cmp eax, 0
0041EAE3 B8 00000000 mov eax, 0
0041EAE8 0F94C0 sete al
0041EAEB 8945 DC mov [ebp-24], eax
0041EAEE 8B5D E8 mov ebx, [ebp-18]
0041EAF1 85DB test ebx, ebx
0041EAF3 74 09 je short 0041EAFE
0041EAF5 53 push ebx
0041EAF6 E8 BC080000 call 0041F3B7
0041EAFB 83C4 04 add esp, 4
0041EAFE 8B5D E0 mov ebx, [ebp-20]
0041EB01 85DB test ebx, ebx
0041EB03 74 09 je short 0041EB0E
0041EB05 53 push ebx
0041EB06 E8 AC080000 call 0041F3B7
0041EB0B 83C4 04 add esp, 4
0041EB0E 837D DC 00 cmp dword ptr [ebp-24], 0 ; 判断比较结果
0041EB12 0F84 07000000 je 0041EB1F ; 关键跳转
0041EB18 B8 01000000 mov eax, 1
0041EB1D EB 02 jmp short 0041EB21
0041EB1F 33C0 xor eax, eax ; 用户名<2跳入点
0041EB21 85C0 test eax, eax
0041EB23 0F84 CD050000 je 0041F0F6
0041EB29 68 01030080 push 80000301
0041EB2E 6A 00 push 0
0041EB30 68 04000000 push 4
0041EB35 68 01030080 push 80000301
0041EB3A 6A 00 push 0
0041EB3C 68 01000000 push 1
0041EB41 68 02000000 push 2
0041EB46 BB 94000000 mov ebx, 94
0041EB4B E8 5B080000 call 0041F3AB ;
0041EB50 83C4 1C add esp, 1C
0041EB53 8945 F0 mov [ebp-10], eax
0041EB56 68 01030080 push 80000301
0041EB5B 6A 00 push 0
0041EB5D 68 F2000000 push 0F2
0041EB62 68 01030080 push 80000301
0041EB67 6A 00 push 0
0041EB69 FF75 F0 push dword ptr [ebp-10]
0041EB6C 68 01030080 push 80000301
0041EB71 6A 00 push 0
0041EB73 68 0A000000 push 0A
0041EB78 68 03000000 push 3
0041EB7D BB 10000000 mov ebx, 10
0041EB82 B8 01000000 mov eax, 1
0041EB87 E8 25080000 call 0041F3B1
0041EB8C 83C4 28 add esp, 28
0041EB8F 6A FF push -1
0041EB91 6A 08 push 8
0041EB93 68 11000116 push 16010011
0041EB98 68 01000152 push 52010001
0041EB9D E8 27080000 call 0041F3C9 ; 取注册码
0041EBA2 83C4 10 add esp, 10
0041EBA5 8945 F0 mov [ebp-10], eax
0041EBA8 68 01030080 push 80000301
0041EBAD 6A 00 push 0
0041EBAF 68 13000000 push 13
0041EBB4 68 04000080 push 80000004
0041EBB9 6A 00 push 0
0041EBBB 8B45 F0 mov eax, [ebp-10]
0041EBBE 85C0 test eax, eax
0041EBC0 75 05 jnz short 0041EBC7 ; 是否为空
0041EBC2 B8 8A014100 mov eax, 0041018A
0041EBC7 50 push eax
0041EBC8 68 02000000 push 2
0041EBCD BB 34010000 mov ebx, 134
0041EBD2 E8 D4070000 call 0041F3AB ; 取注册码前19位
0041EBD7 83C4 1C add esp, 1C
0041EBDA 8945 EC mov [ebp-14], eax
0041EBDD 8B5D F0 mov ebx, [ebp-10]
0041EBE0 85DB test ebx, ebx
0041EBE2 74 09 je short 0041EBED
0041EBE4 53 push ebx
0041EBE5 E8 CD070000 call 0041F3B7
0041EBEA 83C4 04 add esp, 4
0041EBED 68 4F000000 push 4F
0041EBF2 B8 A0014100 mov eax, 004101A0
0041EBF7 8945 E8 mov [ebp-18], eax
0041EBFA 8D45 E8 lea eax, [ebp-18]
0041EBFD 50 push eax
0041EBFE E8 53050000 call 0041F156 ; 产生注册码前19位
0041EC03 8945 E4 mov [ebp-1C], eax
0041EC06 8B5D E8 mov ebx, [ebp-18]
0041EC09 85DB test ebx, ebx
0041EC0B 74 09 je short 0041EC16
0041EC0D 53 push ebx
0041EC0E E8 A4070000 call 0041F3B7
0041EC13 83C4 04 add esp, 4
0041EC16 8B45 E4 mov eax, [ebp-1C]
0041EC19 50 push eax
0041EC1A FF75 EC push dword ptr [ebp-14]
0041EC1D E8 DDFBFFFF call 0041E7FF ; 比较前19位
0041EC22 83C4 08 add esp, 8
0041EC25 83F8 00 cmp eax, 0
0041EC28 B8 00000000 mov eax, 0
0041EC2D 0F94C0 sete al
0041EC30 8945 E0 mov [ebp-20], eax
0041EC33 8B5D EC mov ebx, [ebp-14]
0041EC36 85DB test ebx, ebx
0041EC38 74 09 je short 0041EC43
0041EC3A 53 push ebx
0041EC3B E8 77070000 call 0041F3B7
0041EC40 83C4 04 add esp, 4
0041EC43 8B5D E4 mov ebx, [ebp-1C]
0041EC46 85DB test ebx, ebx
0041EC48 74 09 je short 0041EC53
0041EC4A 53 push ebx
0041EC4B E8 67070000 call 0041F3B7
0041EC50 83C4 04 add esp, 4
0041EC53 837D E0 00 cmp dword ptr [ebp-20], 0 ; 测试比较结果
0041EC57 0F84 58040000 je 0041F0B5 ; 第二处关键跳转
0041EC5D 6A FF push -1
0041EC5F 6A 08 push 8
0041EC61 68 11000116 push 16010011
0041EC66 68 01000152 push 52010001
0041EC6B E8 59070000 call 0041F3C9 ; 读入注册码
;...;中间省略代码用读入的注册码通过变换得到另一组
;...;中加码,由于此段代码过长且与我们的分析关系
;...;不大,故省略。
0041EF1E E8 DCF8FFFF call 0041E7FF ; 比较 用户名 和 中间码
0041EF23 83C4 08 add esp, 8
0041EF26 83F8 00 cmp eax, 0
0041EF29 B8 00000000 mov eax, 0
0041EF2E 0F95C0 setne al
0041EF31 8945 E4 mov [ebp-1C], eax
0041EF34 8B5D E8 mov ebx, [ebp-18]
0041EF37 85DB test ebx, ebx
0041EF39 74 09 je short 0041EF44
0041EF3B 53 push ebx
0041EF3C E8 76040000 call 0041F3B7
0041EF41 83C4 04 add esp, 4
0041EF44 837D E4 00 cmp dword ptr [ebp-1C], 0 ; 测试上面比较结果
0041EF48 0F84 62010000 je 0041F0B0 ; 跳转的话 将没有任何提示信息返回
0041EF4E 68 04000080 push 80000004
0041EF53 6A 00 push 0
0041EF55 8B45 F8 mov eax, [ebp-8]
0041EF58 85C0 test eax, eax
0041EF5A 75 05 jnz short 0041EF61
0041EF5C B8 8A014100 mov eax, 0041018A
0041EF61 50 push eax
0041EF62 68 01000000 push 1
0041EF67 BB 30010000 mov ebx, 130
0041EF6C E8 3A040000 call 0041F3AB ; 取得注册码中间部分长度
0041EF71 83C4 10 add esp, 10
0041EF74 8945 F0 mov [ebp-10], eax
0041EF77 6A FF push -1
0041EF79 6A 08 push 8
0041EF7B 68 11000116 push 16010011
0041EF80 68 01000152 push 52010001
0041EF85 E8 3F040000 call 0041F3C9 ; 读入注册码
0041EF8A 83C4 10 add esp, 10
0041EF8D 8945 EC mov [ebp-14], eax
0041EF90 68 01030080 push 80000301
0041EF95 6A 00 push 0
0041EF97 FF75 F0 push dword ptr [ebp-10]
0041EF9A 68 01030080 push 80000301
0041EF9F 6A 00 push 0
0041EFA1 68 14000000 push 14
0041EFA6 68 04000080 push 80000004
0041EFAB 6A 00 push 0
0041EFAD 8B45 EC mov eax, [ebp-14]
0041EFB0 85C0 test eax, eax
0041EFB2 75 05 jnz short 0041EFB9
0041EFB4 B8 8A014100 mov eax, 0041018A
0041EFB9 50 push eax
0041EFBA 68 03000000 push 3
0041EFBF BB 3C010000 mov ebx, 13C
0041EFC4 E8 E2030000 call 0041F3AB ; 取输入注册码的中间部分
0041EFC9 83C4 28 add esp, 28
0041EFCC 8945 E8 mov [ebp-18], eax
0041EFCF 8B5D EC mov ebx, [ebp-14]
0041EFD2 85DB test ebx, ebx
0041EFD4 74 09 je short 0041EFDF
0041EFD6 53 push ebx
0041EFD7 E8 DB030000 call 0041F3B7
0041EFDC 83C4 04 add esp, 4
0041EFDF 8B45 F8 mov eax, [ebp-8]
0041EFE2 50 push eax
0041EFE3 FF75 E8 push dword ptr [ebp-18]
0041EFE6 E8 14F8FFFF call 0041E7FF ; 比较注册码 中间部分
0041EFEB 83C4 08 add esp, 8
0041EFEE 83F8 00 cmp eax, 0
0041EFF1 B8 00000000 mov eax, 0
0041EFF6 0F95C0 setne al
0041EFF9 8945 E4 mov [ebp-1C], eax
0041EFFC 8B5D E8 mov ebx, [ebp-18]
0041EFFF 85DB test ebx, ebx
0041F001 74 09 je short 0041F00C
0041F003 53 push ebx
0041F004 E8 AE030000 call 0041F3B7
0041F009 83C4 04 add esp, 4
0041F00C 837D E4 00 cmp dword ptr [ebp-1C], 0 ; 测试比较结果
0041F010 0F84 41000000 je 0041F057 ; 第三处关键点 跳向成功
0041F016 68 02000080 push 80000002 ; 注册码中间部分 比较失败跳入点
0041F01B 6A 00 push 0
0041F01D 68 01000000 push 1
0041F022 68 01000100 push 10001
0041F027 68 00000106 push 6010000
0041F02C 68 01000152 push 52010001
0041F031 68 01000100 push 10001
0041F036 68 23000106 push 6010023
0041F03B 68 24000152 push 52010024
0041F040 68 03000000 push 3
0041F045 BB 20030000 mov ebx, 320
0041F04A E8 5C030000 call 0041F3AB ; 提示失败
0041F04F 83C4 28 add esp, 28
0041F052 E9 59000000 jmp 0041F0B0
0041F057 6A 00 push 0 ; 成功跳入点
0041F059 68 01000000 push 1
0041F05E 6A FF push -1
0041F060 6A 0E push 0E
0041F062 68 00000106 push 6010000
0041F067 68 01000152 push 52010001
0041F06C E8 52030000 call 0041F3C3
0041F071 83C4 18 add esp, 18
0041F074 68 02000080 push 80000002
0041F079 6A 00 push 0
0041F07B 68 01000000 push 1
0041F080 68 01000100 push 10001
0041F085 68 00000106 push 6010000
0041F08A 68 01000152 push 52010001
0041F08F 68 01000100 push 10001
0041F094 68 21000106 push 6010021
0041F099 68 22000152 push 52010022
0041F09E 68 03000000 push 3
0041F0A3 BB 20030000 mov ebx, 320
0041F0A8 E8 FE020000 call 0041F3AB ; 提示成功
0041F0AD 83C4 28 add esp, 28
0041F0B0 E9 3C000000 jmp 0041F0F1 ; 成功返回路径
0041F0B5 68 02000080 push 80000002 ; 前19为比较失败 跳入点
0041F0BA 6A 00 push 0
0041F0BC 68 01000000 push 1
0041F0C1 68 01000100 push 10001
0041F0C6 68 00000106 push 6010000
0041F0CB 68 01000152 push 52010001
0041F0D0 68 01000100 push 10001
0041F0D5 68 23000106 push 6010023
0041F0DA 68 24000152 push 52010024
0041F0DF 68 03000000 push 3
0041F0E4 BB 20030000 mov ebx, 320
0041F0E9 E8 BD020000 call 0041F3AB ; 提示 失败
0041F0EE 83C4 28 add esp, 28
0041F0F1 E9 3C000000 jmp 0041F132 ; 成功返回路径
0041F0F6 68 02000080 push 80000002 ; 后四 比较失败跳入点 用户名<2跳入点
0041F0FB 6A 00 push 0
0041F0FD 68 01000000 push 1
0041F102 68 01000100 push 10001
0041F107 68 00000106 push 6010000
0041F10C 68 01000152 push 52010001
0041F111 68 01000100 push 10001
0041F116 68 23000106 push 6010023
0041F11B 68 24000152 push 52010024
0041F120 68 03000000 push 3
0041F125 BB 20030000 mov ebx, 320
0041F12A E8 7C020000 call 0041F3AB ; 提示 失败
0041F12F 83C4 28 add esp, 28
0041F132 8B5D F8 mov ebx, [ebp-8] ; 成功返回路径
0041F135 85DB test ebx, ebx
0041F137 74 09 je short 0041F142
0041F139 53 push ebx
0041F13A E8 78020000 call 0041F3B7 ; 清理工作
0041F13F 83C4 04 add esp, 4
0041F142 8B5D F4 mov ebx, [ebp-C]
0041F145 85DB test ebx, ebx
0041F147 74 09 je short 0041F152
0041F149 53 push ebx
0041F14A E8 68020000 call 0041F3B7 ; 清理工作
0041F14F 83C4 04 add esp, 4
0041F152 8BE5 mov esp, ebp
0041F154 5D pop ebp
0041F155 C3 retn ; 返回
整理分析思路:(顺序即程序流程)
1. 利用用户名经过算法变换,得到注册码中间部分。
2. 比较用户名是否<2,是则跳向失败。
3. 计算出注册码后4位,并与输入的注册码后4位进行比较,不符则跳向失败。
4. 计算出注册码前19位,并与输入的注册码前19位进行比较,不符则跳向失败。
5. 利用输入的假注册码,变换得到一中间码,与输入的用户名比较,相符则无提示返回。(省略部分)
6. 取输入的假注册码的中间部分,与计算所得的注册码中间部分相比,不符则跳向失败。
7. 前面的2,3,4,6部分均走向成功的话,表明注册成功。
注: 前面3,4中提到注册码及注册码位数与输入的用户名无关,是否与个人机器码相关我没有分析。前面6中的取假注册码中间部分的取法是从前面取,意即是越过4中的位数取的。
我们先不谈算法,先来看看我们发现的有趣的事情,即小蜜蜂只关心注册码的后4位,前19位,以及紧跟在前19后面的n位,并没有审核注册码的总长度。也就是说我们可以在得到注册码后,在注册码的第19+n位后,后4位前加入任何字符,都同样可以注册成功。哪怕你加入的是斗大的汉字:小蜜蜂,嗡嗡翁!
同样的道理,我们会发现注册码的中间部分是根据用户名按位计算取得的,意即同样的字符产生同样的注册码。结合上面的分析很容易知道:如果name1仅仅是比name2短几位(其余对应位字符相同)的话,name1用name2的注册码同样会注册成功。例如: 我用petnt得到的注册码,同样适用于 pe、 pet、 petn。
一句话概括我们的发现:用户名和注册码并不一一对应。
第三部分 编写注册机
其实,破文之所以取上面的名字,就源于这一部分。经过上面的分析,我本想再跟踪一下算法写出注册机。但是我不得不把责任再一次推向那个可恶的krnln.fnr,那里面的函数一层层call的我头晕脑涨,而且它的参数是大都是一些莫名其妙的数字,并且不断的使用公用变量。这些使我不得不放弃了跟踪算法的想法。难道就这样眼睁睁的放走他?
“授权任意人士可随时随地的随意对本软件进行反汇编,反编译,修改或分解等另类操作。”(引自小蜜蜂《免责声明》中程序授权第5条)
哈哈,看来我们的权利还真不小。经过算法分析我们看出,虽然注册码比较分了三部分,但每一部分都用的明码比较,也就是说在程序中存在着我们想要的东西,只是我们看不到他。呵呵,可能你我已经想到了一起:把程序搬上手术台,取出我们想得到的东西,并且把它挂在脸上。(好象有些不人道,总感觉对不起小蜜蜂。)
请出Stud_PE,对我们的想法进行一下可行性分析:
No | Name | VSize | VOffset | RSize | ROffset | Charact. |
01 | .text | 00005A77 | 00001000 | 00006000 | 00001000 | 60000020 |
02 | .rdata | 00000AB4 | 00007000 | 00001000 | 00007000 | 40000040 |
03 | .data | 00003FA0 | 00008000 | 00004000 | 00008000 | C0000040 |
04 | .tls | 00014000 | 0000C000 | 00014000 | 0000C000 | E0000040 |
05 | .rsrc | 00000D60 | 00020000 | 00001000 | 00020000 | 40000040 |
* | ExtraDat | | | 0005BAC1 | 00021000 | |
代码段、数据段都有足够的空间给我们用,可以实施手术。就连各段的VOffset和ROffset都相等,真是天公作美啊。准备动手!
别急,还是理一下思路吧。
1. 改变程序流程,让我们想看到的都出现。
2. 寻找合适的地方,引走程序,完成任务后再引回来。
3. 清理现场,美化程序。
第一步应该比较简单,只要把关键跳转部分改动以下,程序就会乖乖的给我们计算注册码。改动如下:
第一处 0041eb12 0f 84 07 00 00 00 -> 90 90 90 90 90 90
第二处 0041ec57 0f 84 58 04 00 00 -> 90 90 90 90 90 90
第三处 0041f010 0f 84 41 00 00 00 -> eb 45 90 90 90 90
其实上面的工作相当于一次爆破。经过上面改动后,除注册名长度<2回注册失败外,其余各种情况均会注册成功。
第二步要复杂一些,我们同样要选择合适的改动地方。我选的两处是:0041ead8、0041ec1d。
之所以选这两处,最重要的原因当然是程序运行到这里的时候,正确的注册码刚好在堆栈或寄存器中。其二jmp 和 call 同样是条5字节指令,而且在这个例子中,这两条指令的最后一个字节都是FF,所以,我们只相当于修改了4字节,这样我们就可以很方便的进行恢复了。在代码段的空白处选两个合适的位置,准备放我们的处理指令。具体修改如下:
第一处 0041ead8 e8 22 fd ff -> e9 9a 7f fe
补丁程序内存中地址 00406a77 (此段程序我们将获得注册码后4位),具体代码如下:
00406A77 60 pushad ;保存现场
00406A78 8BF0 mov esi, eax ;Eax中即注册码后四位的地址
00406A7A BF 00984000 mov edi, 00409800 ;注册码后4位存放地址
00406A7F 33C9 xor ecx, ecx
00406A81 33DB xor ebx, ebx
00406A83 0FB61418 movzx edx, byte ptr [eax+ebx]
00406A87 41 inc ecx
00406A88 43 inc ebx
00406A89 83FA 00 cmp edx, 0
00406A8C ^ 75 F5 jnz short 00406A83
00406A8E F3:A4 rep movs byte ptr es:[edi], byte ptr [esi]
00406A90 B8 E822FDFF mov eax, FFFD22E8 ;恢复原指令
00406A95 A3 D8EA4100 mov [41EAD8], eax
00406A9A 61 popad ;恢复现场
00406A9B - E9 38800100 jmp 0041EAD8
第二处 0041ec1d e8 dd fb ff -> e9 8b 7e fe
补丁程序地址 00406aad (此段程序我们将获得注册码的前19位 和 中间n位),具体代码如下:
00406AAD 60 pushad
00406AAE 8BF0 mov esi, eax ;Eax 为注册码前19位
00406AB0 BF 80984000 mov edi, 00409880 ;存放地址
00406AB5 33C9 xor ecx, ecx
00406AB7 33DB xor ebx, ebx
00406AB9 0FB61418 movzx edx, byte ptr [eax+ebx]
00406ABD 41 inc ecx
00406ABE 43 inc ebx
00406ABF 83FA 00 cmp edx, 0
00406AC2 ^ 75 F5 jnz short 00406AB9
00406AC4 F3:A4 rep movs byte ptr es:[edi], byte ptr [esi]
00406AC6 8BEC mov ebp, esp
00406AC8 8B45 28 mov eax, [ebp+28] ;这个栈址中放着注册码中间部分
00406ACB 8BF0 mov esi, eax
00406ACD BF 40984000 mov edi, 00409840 ;存放地址
00406AD2 33C9 xor ecx, ecx
00406AD4 33DB xor ebx, ebx
00406AD6 0FB61418 movzx edx, byte ptr [eax+ebx]
00406ADA 41 inc ecx
00406ADB 43 inc ebx
00406ADC 83FA 00 cmp edx, 0
00406ADF ^ 75 F5 jnz short 00406AD6
00406AE1 F3:A4 rep movs byte ptr es:[edi], byte ptr [esi]
00406AE3 B8 E8DDFBFF mov eax, FFFBDDE8
00406AE8 A3 1DEC4100 mov [41EC1D], eax
00406AED 61 popad
00406AEE - E9 2A810100 jmp 0041EC1D
好了,经过以上修改,我们程序会在每次的注册码验证过程中,将正确的注册码存放在固定的3个位置上。下面的任务上如何想办法将正确的注册码合并并显示在编辑框中。
在这之前,我们先来美化下程序吧。我们总不能让注册码显示在编辑框的同时还弹出正确或者错误的提示框吧,我们先把这些没用的指令nop掉。根据分析我们应该nop的两处如下:
0001f057---------0001f0af nop(提示注册成功的地方)
0001f0f6---------0001f131 nop(由于用户名太短而提示失败的地方)
我们nop出了好多空间,这里可以很方便的加入我们的代码了,不用到处找了。好了,该是我们考虑如何实现想法的时候了。幸运的是程序导入表中有两个重要的函数,LoadLibrary 和 GetProcAddress。有了他们两个我们还有什么事不能做呢?为了方便灵活的写我们的代码,同样只是在空空的nop处用jmp指令跳出。
注册成功的地方,应该放我们合并并显示注册码的代码。具体修改及代码如下:
0041f060 处 90 90 90 90 90 -> e9 9b 7a fe ff
00406b00 处 存放处理程序 :
00406B00 60 pushad
00406B01 B8 E99A7FFE mov eax, FE7F9AE9
00406B06 A3 D8EA4100 mov [41EAD8], eax
00406B0B B8 E98B7EFE mov eax, FE7E8BE9
00406B10 A3 1DEC4100 mov [41EC1D], eax
00406B15 68 3C964000 push 0040963C ; ASCII "User32.dll"
00406B1A FF15 04704000 call [407004] ; LoadLibray
00406B20 50 push eax
00406B21 68 30964000 push 00409630 ; ASCII "FindWindowA"
00406B26 50 push eax
00406B27 FF15 00704000 call [407000] ; GetProcAddress
00406B2D A3 20974000 mov [409720], eax
00406B32 58 pop eax
00406B33 68 53964000 push 00409653 ; ASCII "SetDlgItemTextA"
00406B38 50 push eax
00406B39 FF15 00704000 call [407000]
00406B3F A3 28974000 mov [409728], eax
00406B44 68 40984000 push 00409840
00406B49 68 80984000 push 00409880
00406B4E E8 CD000000 call 00406C20 ; 自定义字符串合并程序
00406B53 68 00984000 push 00409800
00406B58 68 80984000 push 00409880
00406B5D E8 BE000000 call 00406C20
00406B62 68 29964000 push 00409629 ; 存放窗口名
00406B67 68 6B964000 push 0040966B ; ASCII "Afx:10000000:b:10011:1900015:0"
00406B6C FF15 20974000 call [409720] ; USER32.FindWindowA
00406B72 68 80984000 push 00409880 ;
00406B77 68 AA000000 push 0AA
00406B7C 50 push eax ; 窗口句柄
00406B7D FF15 28974000 call [409728] ; USER32.SetDlgItemTextA
00406B83 61 popad
00406B84 - E9 DC840100 jmp 0041F065
值得注意的是,上面的处理过程中,我们用到了很多预定义的字符串,请事先加入到数据段的固定位置。另外不得不说两句的是我居然没有找到ASCII字符串合并函数,不知是MS根本没写还是我没找到(有StrCatW,却没有A的),郁闷的我只能自己写一个来实现了。代码如下:
00406C20 55 push ebp
00406C21 8BEC mov ebp, esp
00406C23 60 pushad
00406C24 FC cld
00406C25 8B75 0C mov esi, [ebp+C]
00406C28 8B7D 08 mov edi, [ebp+8]
00406C2B 33C9 xor ecx, ecx
00406C2D 33DB xor ebx, ebx
00406C2F 0FB6143B movzx edx, byte ptr [ebx+edi]
00406C33 41 inc ecx
00406C34 43 inc ebx
00406C35 83FA 00 cmp edx, 0
00406C38 ^ 75 F5 jnz short 00406C2F
00406C3A 49 dec ecx
00406C3B 03F9 add edi, ecx
00406C3D 33C9 xor ecx, ecx
00406C3F 33DB xor ebx, ebx
00406C41 0FB61433 movzx edx, byte ptr [ebx+esi]
00406C45 41 inc ecx
00406C46 43 inc ebx
00406C47 83FA 00 cmp edx, 0
00406C4A ^ 75 F5 jnz short 00406C41
00406C4C F3:A4 rep movs byte ptr es:[edi], byte ptr>
00406C4E 61 popad
00406C4F 5D pop ebp
00406C50 C2 0800 retn 8
还有一种情况要考虑一下,用户名不符合条件我们总得给个提示吧。
0041F0FE 90 90 90 90 90 -> E9 8D 7A FE FF jmp 00406B90
00406b90 处存放处理程序如下:
00406B90 60 pushad
00406B91 68 3C964000 push 0040963C ; ASCII "User32.dll"
00406B96 FF15 04704000 call [<&KERNEL32.LoadLibraryA>] ; kernel32.LoadLibraryA
00406B9C 50 push eax
00406B9D 68 30964000 push 00409630 ; ASCII "FindWindowA"
00406BA2 50 push eax
00406BA3 FF15 00704000 call [<&KERNEL32.GetProcAddress>] ; kernel32.GetProcAddress
00406BA9 A3 20974000 mov [409720], eax
00406BAE 58 pop eax
00406BAF 68 53964000 push 00409653 ; ASCII "SetDlgItemTextA"
00406BB4 50 push eax
00406BB5 FF15 00704000 call [<&KERNEL32.GetProcAddress>] ; kernel32.GetProcAddress
00406BBB A3 28974000 mov [409728], eax
00406BC0 68 29964000 push 00409629
00406BC5 68 6B964000 push 0040966B ;
00406BCA FF15 20974000 call [409720]
00406BD0 68 10964000 push 00409610 ; 预定义的错误提示字符串
00406BD5 68 AA000000 push 0AA
00406BDA 50 push eax
00406BDB FF15 28974000 call [409728]
00406BE1 61 popad
00406BE2 - E9 1F850100 jmp 0041F106
好了,把所有改动应用于文件,一个CrackMe就这样被打造成了注册机。“以彼之道,还施彼身”的形容算不算恰当呢?反正用了小蜜蜂的算法,作了小蜜蜂的注册机。一切皆怪小蜜蜂让我分析不出算法,:-),希望小蜜蜂看了被小菜我改得乱七八糟的CrackMe不要生气。如有分析有误之处,敬请批评指正,收工!
另附一个点了没有反应的用户名和注册码:(真想知道小蜜蜂这葫芦里装得什么药)
注册名:NAK-19EA1A6194A06198111A63018713F13281483F20.oN-GCD
注册码:[DCG][OCN][PYG]-No.101A585-KAN
--------------------------------------------------------------------------------
【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!
2007年12月11日 01:09:10
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!