标签:
系统 : Windows xp
程序 : KeyGenMe 1 by Taliesin
程序下载地址 :http://pan.baidu.com/s/1c2HTuqk
要求 : 注册机编写
使用工具 : OD
可在看雪论坛中查看关于此程序的讨论:传送门
老规矩,先查看下字符串,定位关键代码:
004014DF |. B8 6C304000 mov eax, 0040306C ; great job!
004014E4 |. 8BD8 mov ebx, eax
004014E6 |. 83C3 0B add ebx, 0B
004014E9 |. 6A 00 push 0 ; /Style = MB_OK|MB_APPLMODAL
004014EB |. 50 push eax ; |Title => "Great Job!"
004014EC |. 53 push ebx ; |Text => "You have completed Key Gen Me #1."
004014ED |. 6A 00 push 0 ; |hOwner = NULL
004014EF |. E8 B4000000 call <jmp.&user32.MessageBoxA> ; \MessageBoxA
004014F4 |. C3 retn
004014F5 |> 6A 00 push 0 ; /Style = MB_OK|MB_APPLMODAL
004014F7 |. 68 C2304000 push 004030C2 ; |not this time.
004014FC |. 68 99304000 push 00403099 ; |try again, something did not work right.
00401501 |. 6A 00 push 0 ; |hOwner = NULL
00401503 |. E8 A0000000 call <jmp.&user32.MessageBoxA> ; \MessageBoxA
00401508 |. 6A 00 push 0 ; /ExitCode = 0
0040150A \. E8 A5000000 call <jmp.&kernel32.ExitProcess> ; \ExitProcess
大概就是简单的一个判断逻辑,现在我们下断点看看程序的流程。
输入密码之后发现程序没有如断在API上,反而显示:"Try Again, something did not work right."
看来是遇到反调试了,反复查验发现有一处检测内存是否中断指令的代码:
00401519 $ BF 96124000 mov edi, 00401296 ; Entry address
0040151E . B9 00010000 mov ecx, 100
00401523 . B0 99 mov al, 99
00401525 . 34 55 xor al, 55 ; al = cc
00401527 . F2:AE repne scas byte ptr es:[edi] ; 扫描
00401529 . 85C9 test ecx, ecx
0040152B . 74 06 je short 00401533
0040152D . 5E pop esi
0040152E . 33F6 xor esi, esi
00401530 . 57 push edi
00401531 .^ EB C2 jmp short 004014F5
00401533 > C3 retn
可以粗暴点直接爆破,也可以将ZF为Set为1覆盖判断结果。
接下来还有一处反调试代码:
00401485 . 33DB xor ebx, ebx
00401487 . BF 80144000 mov edi, 00401480
0040148C . 83EF 60 sub edi, 60
0040148F . B8 DE000000 mov eax, 0DE
00401494 . 83F0 12 xor eax, 12
00401497 . B9 59000000 mov ecx, 59
0040149C . F2:AE repne scas byte ptr es:[edi] ; 内存检查
0040149E . 85C9 test ecx, ecx
004014A0 . 74 06 je short 004014A8
004014A2 . 5E pop esi
004014A3 . 33F6 xor esi, esi
004014A5 . 57 push edi
004014A6 . EB 4D jmp short 004014F5
004014A8 > C3 retn
004014A9 $ BE 9C154000 mov esi, <jmp.&user32.GetDlgItemText>; Entry address
004014AE . 8B7E 02 mov edi, dword ptr [esi+2]
004014B1 . 8B3F mov edi, dword ptr [edi] ; API地址
004014B3 . B9 06000000 mov ecx, 6
004014B8 . B0 CC mov al, 0CC
004014BA . F2:AE repne scas byte ptr es:[edi] ; 扫描
004014BC . 85C9 test ecx, ecx
004014BE . 74 06 je short 004014C6
004014C0 . 5E pop esi
004014C1 . 33F6 xor esi, esi
004014C3 . 57 push edi
004014C4 . EB 2F jmp short 004014F5
004014C6 > C3 retn
小心点绕过即可,当然啦,如果会执行太多次还是直接爆破为好。
接下来是算法部分:
00401332 $ 33C0 xor eax, eax
00401334 . B9 00000000 mov ecx, 0
00401339 . BE 23304000 mov esi, 00403023 ; ASCII "TELBJTMPWM"
0040133E . 8A06 mov al, byte ptr [esi]
00401340 . EB 10 jmp short 00401352
00401342 > 0FB6C0 movzx eax, al ; 遍历序列号
00401345 . 80B8 50314000>cmp byte ptr [eax+403150], 2 ; 是否是大写字母
0040134C . 75 0A jnz short 00401358 ; 不是则 输入不合法
0040134E . 41 inc ecx
0040134F . 8A0431 mov al, byte ptr [ecx+esi]
00401352 > 3C 00 cmp al, 0 ; 字符创结尾?
00401354 .^ 77 EC ja short 00401342 ; 是则结束循环
00401356 . EB 07 jmp short 0040135F
00401358 > C605 44304000>mov byte ptr [403044], 40
0040135F > BE 00304000 mov esi, 00403000 ; ASCII "pediy"
00401364 . 33C9 xor ecx, ecx
00401366 . B8 01000000 mov eax, 1
0040136B . 33D2 xor edx, edx
0040136D . C705 45304000>mov dword ptr [403045], 0
00401377 > B9 00000000 mov ecx, 0
0040137C . 8A0C32 mov cl, byte ptr [edx+esi] ; 遍历用户名
0040137F . 80F9 00 cmp cl, 0 ; 字符创结尾?
00401382 . 74 09 je short 0040138D ; 是则结束循环
00401384 . 42 inc edx
00401385 . 000D 45304000 add byte ptr [403045], cl ; 累加
0040138B .^ EB EA jmp short 00401377
0040138D > A1 45304000 mov eax, dword ptr [403045]
00401392 . B9 18000000 mov ecx, 18
00401397 . 99 cdq
00401398 . F7F9 idiv ecx
0040139A . 8815 4F304000 mov byte ptr [40304F], dl ; 保存 累加结果除以 18 的余数
004013A0 . 8A0D 44304000 mov cl, byte ptr [403044]
004013A6 . 80F9 40 cmp cl, 40
004013A9 . 75 05 jnz short 004013B0
004013AB . E9 45010000 jmp 004014F5
004013B0 > E9 CB000000 jmp 00401480
004013B5 . C3 retn
比较序列号:
004013B6 $ 55 push ebp
004013B7 . 8BEC mov ebp, esp
004013B9 . 68 23304000 push 00403023 ; ASCII "TELBJTMPWM"
004013BE . E8 7D010000 call 00401540 ; 算出长度
004013C3 . 83F8 0A cmp eax, 0A
004013C6 . 0F85 29010000 jnz 004014F5
004013CC . BE 23304000 mov esi, 00403023 ; ASCII "TELBJTMPWM"
004013D1 . B8 00000000 mov eax, 0
004013D6 . BB 00000000 mov ebx, 0
004013DB . 33C9 xor ecx, ecx
004013DD . EB 06 jmp short 004013E5
004013DF > 8A0C30 mov cl, byte ptr [eax+esi]
004013E2 . 03D9 add ebx, ecx ; 累加字符串前九位
004013E4 . 40 inc eax
004013E5 > 83F8 09 cmp eax, 9 ; 循环结束?
004013E8 .^ 72 F5 jb short 004013DF
004013EA . 8BC3 mov eax, ebx
004013EC . B9 09000000 mov ecx, 9
004013F1 . 99 cdq
004013F2 . F7F9 idiv ecx ; 累加数除以9
004013F4 . A3 4A304000 mov dword ptr [40304A], eax ; 保存商
004013F9 . 8B7D 08 mov edi, dword ptr [ebp+8]
004013FC . 8A15 4F304000 mov dl, byte ptr [40304F]
00401402 . 8AC2 mov al, dl
00401404 . 3C 18 cmp al, 18 ; 之前的余数大于18?
00401406 . 76 02 jbe short 0040140A
00401408 . 2C 18 sub al, 18
0040140A > A2 4E304000 mov byte ptr [40304E], al ; 保存
0040140F . 33C0 xor eax, eax
00401411 . A0 4E304000 mov al, byte ptr [40304E]
00401416 . 8A2438 mov ah, byte ptr [eax+edi] ; 取字符
00401419 . 8A36 mov dh, byte ptr [esi]
0040141B . 38F4 cmp ah, dh ; 是否一致?
0040141D . 0F85 D2000000 jnz 004014F5
00401423 . 80EE 41 sub dh, 41
00401426 . 8AF2 mov dh, dl
00401428 . B4 00 mov ah, 0
0040142A . A2 4E304000 mov byte ptr [40304E], al
0040142F . 33C0 xor eax, eax
00401431 . A0 4E304000 mov al, byte ptr [40304E]
00401436 . 02C2 add al, dl ; 余数相加
00401438 . 3C 18 cmp al, 18 ; 大于18?
0040143A . 76 02 jbe short 0040143E
0040143C . 2C 18 sub al, 18
0040143E > B9 02000000 mov ecx, 2
00401443 . 8A2438 mov ah, byte ptr [eax+edi] ; 从表中取字符
00401446 . 8A3431 mov dh, byte ptr [ecx+esi]
00401449 . 38F4 cmp ah, dh ; 是否一致?
0040144B . 0F85 A4000000 jnz 004014F5
00401451 . EB 24 jmp short 00401477
00401453 > A2 4E304000 mov byte ptr [40304E], al
00401458 . 33C0 xor eax, eax
0040145A . A0 4E304000 mov al, byte ptr [40304E]
0040145F . 80EE 41 sub dh, 41 ; 对于首字母A的偏移
00401462 . 8AD6 mov dl, dh
00401464 . 41 inc ecx
00401465 . 02C2 add al, dl
00401467 . 3C 18 cmp al, 18 ; 低于等于18?
00401469 . 76 02 jbe short 0040146D
0040146B . 2C 18 sub al, 18
0040146D > 8A2438 mov ah, byte ptr [eax+edi]
00401470 . 8A3431 mov dh, byte ptr [ecx+esi]
00401473 . 38F4 cmp ah, dh
00401475 . 75 7E jnz short 004014F5
00401477 > 83F9 08 cmp ecx, 8 ; 3 - 8
0040147A .^ 72 D7 jb short 00401453
0040147C . C9 leave
0040147D . C2 0400 retn 4
序列号的最后一位字符 和 第二位:
004014CE /$ BE 23304000 mov esi, 00403023 ; ASCII "TELBJTMPWM"
004014D3 |. A1 4A304000 mov eax, dword ptr [40304A] ; 取出商
004014D8 |. 8A5E 09 mov bl, byte ptr [esi+9]
004014DB |. 38D8 cmp al, bl
004014DD |. 75 16 jnz short 004014F5
00401510 $ A0 24304000 mov al, byte ptr [403024]
00401515 . 3C 45 cmp al, 45 ; 序列号第二个字符必须是E
00401517 .^ 75 DC jnz short 004014F5
看得晕了?不要紧,我来总结一下构成序列号的过程:
1.必须全部大写
2.长度为10.
3.第一个字符按余数从表中载入
4.第二个字符为“E”
5.第三个字符按余数从表中载入
6.第4-9个字符按照前一个字符取数,再从表中载入
7.最后一位为前9位的平均数。
整体算法不困难,就是稍微有点复杂,有点耐心就可以搞定啦。
打开http://www.cnblogs.com/ZRBYYXDM/p/5115596.html中搭建的框架,将OnBtnDecrypt函数编辑如下:
void CKengen_TemplateDlg::OnBtnDecrypt()
{
// TODO: Add your control notification handler code here
CString str;
GetDlgItemText( IDC_EDIT_NAME,str ); //获取用户名字串基本信息。
int len = str.GetLength();
if ( len != 0 ){ //格式控制。
char* KeyList = "ZWATRQLCGHPSXYENVBJDFKMU";
char Key[11];
byte sum = 0;
for ( int i = 0 ; i != len ; i++ ){
sum += str[i];
}
byte remainder = sum % 0x18;
Key[0] = KeyList[remainder];
Key[1] = ‘E‘;
remainder *= 2;
if ( remainder > 0x18 )
remainder -= 0x18;
Key[2] = KeyList[remainder];
for ( i = 3 ; i != 9 ; i++ ){
remainder += (Key[i-1] - ‘A‘);
if ( remainder > 0x18 )
remainder -= 0x18;
Key[i] = KeyList[remainder];
}
DWORD KeySum = 0;
for ( i = 0 ; i != 9 ; i++ ){
KeySum += Key[i];
}
Key[9] = KeySum / 9;
Key[10] = ‘\0‘;
SetDlgItemText( IDC_EDIT_PASSWORD,Key );
}
else
MessageBox( "用户名格式错误!" );
}
再在OnInitDialog中添加此代码修改标题:SetWindowText(_T("Keygen"));
运行效果:
本例中,分别对几个程序关键点和API检测断点,在流程中不断则执行这些代码。这就让破解者比较头疼。
我认为比较强大的防护不是无法攻破的城堡,而是尽量拖延时间,消耗对方的精力。能做到 破解成本 > 软件价值,那就是很好的防御措施。
例如,代码开始执行的时候 重复检测断点,启动防调试机制(格盘,关机,等等不可描述的行为),让破解者头疼不已,只能选择爆破程序。
这时,将反调试代码送入MD5函数,生成一段不可逆的子串。
再给子串做操作生成特殊数值,最终跳转到特定地址( 基地址 + 偏移地址/特殊数值 )。
这样,一旦动了反调试代码,程序将执行未知指令,即刻崩溃。
想要逆向这样的程序还是比较痛苦的,特别是对于不熟悉加密算法的骇客来说。就算掌握了整个流程,想要得到真正的地址就得就要默默忍受反调试的折磨:)
标签:
原文地址:http://www.cnblogs.com/ZRBYYXDM/p/5744401.html