【吾爱2013CM大赛解答】--LoveKido Update-- JoyChou追码分析
本帖最后由 Chief 于 2013-12-14 17:47 编辑[文章标题]: [吾爱2013CM大赛解答]--LoveKido Update-- JoyChou追码分析
[作者信息]: 封心锁爱
[操作平台]: Win7 Sp1
[使用工具]: OD
[软件名称]: LoveKido Update-- JoyChou
[下载地址]: http://www.52pojie.cn/thread-228422-1-1.html
第一关:
1> 激活按钮,下断点 bp EnableWindow, f9运行,Alt+F9返回,会返回到mfc90u模块中,继续单步几下走到程序模块,网上翻一点, 找到push 0, 改为push 1
01241A3F 6A 00 push 0x0 ;改为TRUE启用按钮
01241A41 .68 EB030000 push 0x3EB
01241A46 .8BCE mov ecx, esi
01241A48 .E8 310B0000 call <jmp.&mfc90u.#2904>
01241A4D .8BC8 mov ecx, eax
01241A4F .E8 240B0000 call <jmp.&mfc90u.#2360> ; 禁用按钮
01241A54 .E8 37FAFFFF call 01241490 ;从mfc90u返回到这里
2> 反调试,从01241A9E - 01241ADA中的JNZ全部不能调,NOP掉
01241A54 .E8 37FAFFFF call 01241490 ;┓
01241A59 .85C0 test eax, eax ;┃
01241A5B .0F85 AF000000 jnz 01241B10 ;┃
01241A61 .E8 CAFAFFFF call 01241530 ;┃
01241A66 .85C0 test eax, eax ;┃
01241A68 .0F85 A2000000 jnz 01241B10 ;┣ 这几个call为检测虚拟机的几个方法
01241A6E .E8 FDFBFFFF call 01241670 ;┃
01241A73 .85C0 test eax, eax ;┃
01241A75 .0F85 95000000 jnz 01241B10 ;┃
01241A7B .E8 50FCFFFF call 012416D0 ;┃
01241A80 .85C0 test eax, eax ;┃
01241A82 .0F85 88000000 jnz 01241B10 ;┛
01241A88 .E8 83F9FFFF call 01241410 ;清除硬件断点
01241A8D .E8 2EF9FFFF call 012413C0 ;获取MessageBoxA函数,并将MessageBoxA保护属性改为PAGE_EXECUTE_READWRITE
01241A92 .E8 A9F8FFFF call 01241340 ;查找MessageBoxA中是否存在内存断点
01241A97 .E8 64F5FFFF call 01241000 ;查找是否存在调试器进程 "OLLYDBG.EXE" "吾爱破解.EXE" "ICEYOD.EXE" "OLLYICE.EXE" "WINGUARD.EXE"
01241A9C .85C0 test eax, eax
01241A9E 75 54 jnz short 01241AF4
01241AA0 .E8 9BF7FFFF call 01241240 ;查找窗口类"OLLYXE"
01241AA5 .85C0 test eax, eax
01241AA7 75 4B jnz short 01241AF4
01241AA9 .E8 F2F7FFFF call 012412A0 ;判断父进程是不是explorer来反调试
01241AAE .85C0 test eax, eax
01241AB0 75 42 jnz short 01241AF4
01241AB2 .FF15 6C402401 call dword ptr [<&KERNEL32.GetCurrent>; [GetCurrentProcess
01241AB8 .8D4C24 10 lea ecx, dword ptr
01241ABC .51 push ecx
01241ABD .50 push eax
01241ABE .C74424 18 000>mov dword ptr , 0x0
01241AC6 .FF15 68402401 call dword ptr [<&KERNEL32.CheckRemot>;kernel32.CheckRemoteDebuggerPresent
01241ACC .837C24 10 01cmp dword ptr , 0x1 ;调用CheckRemoteDebuggerPresent检查是否存在调试器
01241AD1 74 21 je short 01241AF4
01241AD3 .E8 B8F8FFFF call 01241390 ;根据执行时间反调试
01241AD8 .85C0 test eax, eax
01241ADA 75 18 jnz short 01241AF4
3> 按钮激活后,开始找Key,要比较Key是否正确,当然需要获取Key的内容,这里F9运行,随便输入Key,然后下断点
bp GetWindowTextW, 返回到程序模块
00091D18 .8D4424 0C lea eax, dword ptr
00091D1C .50 push eax
00091D1D .68 E9030000 push 0x3E9
00091D22 .8BCE mov ecx, esi
00091D24 .C68424 B00000>mov byte ptr , 0x1
00091D2C .E8 6B080000 call <jmp.&mfc90u.#2909> ;获取Key
00091D31 .8B4C24 0C mov ecx, dword ptr ;ecx = Key -->到了程序模块后在这里
00091D35 .8B41 F4 mov eax, dword ptr ;eax = StrLen
下面单步继续往下走,寻找比较Key的地方, 下面是一些除法运算(还原除数原理参见C++反汇编与逆向分析技术揭秘),还原后可知下面几个条件, 下面的Key(10)代表Key的10进制值:
Key(10) - ((Key(10) / 3) * 3) == 1 &&
Key(10) - ((Key(10) / 5) * 5) == 2 &&
Key(10) - ((Key(10) / 7) * 7) == 4 &&
Key(10) - ((Key(10) / 13) * 13) == 6 &&
Key(10) - ((Key(10) / 17) * 17) == 8
这5个条件必须全部为TRUE,这里用了枚举方法算出Key,为18232,C代码见Code<1>
00091DA1 .FFD6 call esi ;将Key转换为10进制
00091DA3 .8BC8 mov ecx, eax ;除法运算 Key(10进制) / 3 除数的计算方法: 2^32 / 0x55555556四舍五入
00091DA5 .B8 56555555 mov eax, 0x55555556
00091DAA .F7E9 imul ecx
00091DAC .8BC2 mov eax, edx
00091DAE .C1E8 1F shr eax, 0x1F
00091DB1 .03C2 add eax, edx ;eax = 商
00091DB3 .8D1440 lea edx, dword ptr ;商 * 3
00091DB6 .8BC1 mov eax, ecx ;eax = Key10进制
00091DB8 .2BC2 sub eax, edx
00091DBA .83C4 08 add esp, 0x8
00091DBD .83F8 01 cmp eax, 0x1 ;Key(10进制) - (商 * 3) == 1
00091DC0 .0F85 FD000000 jnz 00091EC3
00091DC6 .B8 67666666 mov eax, 0x66666667 ;除法运算 Key(10) / 5 除数=2^33 / 0x66666667四舍五入
00091DCB .F7E9 imul ecx ;ecx = Key(10)
00091DCD .D1FA sar edx, 1 ;edx为乘法的高32位,相当于已经右移的32位,再右移1位为33位
00091DCF .8BC2 mov eax, edx
00091DD1 .C1E8 1F shr eax, 0x1F
00091DD4 .03C2 add eax, edx ;eax = 商
00091DD6 .8D1480 lea edx, dword ptr ;商 * 5
00091DD9 .8BC1 mov eax, ecx
00091DDB .2BC2 sub eax, edx ;Key(10) - (商 * 5) == 2
00091DDD .83F8 02 cmp eax, 0x2
00091DE0 .0F85 DD000000 jnz 00091EC3
00091DE6 .B8 93244992 mov eax, 0x92492493 ;除法运算 Key(10) / 7 除数=2^34 / 0x92492493四舍五入
00091DEB .F7E9 imul ecx
00091DED .03D1 add edx, ecx
00091DEF .C1FA 02 sar edx, 0x2
00091DF2 .8BC2 mov eax, edx
00091DF4 .C1E8 1F shr eax, 0x1F
00091DF7 .03C2 add eax, edx ;eax = 商
00091DF9 .8D14C5 000000>lea edx, dword ptr ;商 * 8
00091E00 .2BD0 sub edx, eax ;edx = 商 * 7
00091E02 .8BC1 mov eax, ecx ;eax = Key(10)
00091E04 .2BC2 sub eax, edx
00091E06 .83F8 04 cmp eax, 0x4 ;Key(10) - (商 * 7) == 4
00091E09 .0F85 B4000000 jnz 00091EC3
00091E0F .B8 4FECC44E mov eax, 0x4EC4EC4F ;除法运算 Key(10) / 13 除数:2^34 / 0x4ec4ec4f四舍五入
00091E14 .F7E9 imul ecx
00091E16 .C1FA 02 sar edx, 0x2
00091E19 .8BC2 mov eax, edx
00091E1B .C1E8 1F shr eax, 0x1F
00091E1E .03C2 add eax, edx ;eax = 商
00091E20 .6BC0 0D imul eax, eax, 0xD ;商 * 13
00091E23 .8BD1 mov edx, ecx
00091E25 .2BD0 sub edx, eax ;Key(10) - (商 * 13) == 6
00091E27 .83FA 06 cmp edx, 0x6
00091E2A .0F85 93000000 jnz 00091EC3
00091E30 .B8 79787878 mov eax, 0x78787879 ;除法运算 Key(10) / 17 除数:2^35 / 0x78787879四舍五入
00091E35 .F7E9 imul ecx
00091E37 .C1FA 03 sar edx, 0x3
00091E3A .8BC2 mov eax, edx
00091E3C .C1E8 1F shr eax, 0x1F
00091E3F .03C2 add eax, edx ;eax = 商
00091E41 .8BD0 mov edx, eax
00091E43 .C1E2 04 shl edx, 0x4
00091E46 .03D0 add edx, eax ;商 * 17
00091E48 .2BCA sub ecx, edx ;Key(10) - (商 * 17) == 8
00091E4A .83F9 08 cmp ecx, 0x8
00091E4D .75 74 jnz short 00091EC3
第二关:
和上面同理,要判断Key,就必须获取Key,同样下断点bp GetWindowTextW,输入Key点按钮断下,返回到程序模块
0009207D .8D4424 18 lea eax, dword ptr
00092081 .50 push eax
00092082 .68 EE030000 push 0x3EE
00092087 .8BCE mov ecx, esi
00092089 .C74424 6C 000>mov dword ptr , 0x0
00092091 .E8 06050000 call <jmp.&mfc90u.#2909> ;获取Key
00092096 .8B3D 40650900 mov edi, dword ptr ;edi = 机器码 ->> 返回到程序模块后在这里
下面根据机器码计算Key, 下面说的字符长度和字节长度在Unicode环境下才有区别,便于理解,这里举个例子:
String: "Hello"
上面这个字符串的字符长度不管是Unicode还是非Unicode都是5,而字节长度在Ansi下是5,在Unicode就是10,因为一个Unicode字符2字节
000920C0 > /8D444D 00 lea eax, dword ptr
000920C4 . |66:8B5404 30mov dx, word ptr ;取机器码一个字符
000920C9 . |66:42 inc dx ;加1
000920CB . |66:83F2 1F xor dx, 0x1F ;和0x1f异或
000920CF . |8BC7 mov eax, edi
000920D1 . |66:89544C 30mov word ptr , dx ;保存算出的Key
000920D6 . |41 inc ecx
000920D7 . |8D70 02 lea esi, dword ptr
000920DA . |8D9B 00000000 lea ebx, dword ptr
000920E0 > |66:8B10 mov dx, word ptr
000920E3 . |83C0 02 add eax, 0x2
000920E6 . |66:85D2 test dx, dx
000920E9 .^|75 F5 jnz short 000920E0
000920EB . |2BC6 sub eax, esi ; 机器码字节长度
000920ED . |D1F8 sar eax, 1 ;机器码字符长度
000920EF . |3BC8 cmp ecx, eax
000920F1 .^\72 CD jb short 000920C0
继续, 下面这段将机器码字节长度与0x11异或然后输出成为一个字符串
00092109 .8B0D 40650900 mov ecx, dword ptr ;机器码
0009210F .8B41 F4 mov eax, dword ptr ;机器码字符长度
00092112 .8B35 30420900 mov esi, dword ptr [<&mfc90u.#2537>] ;mfc90u.#2537
00092118 .8D1400 lea edx, dword ptr ;edx = 机器码字节长度
0009211B .83F2 11 xor edx, 0x11 ;机器码字节长度与0x11异或
0009211E .52 push edx
0009211F .8D4424 18 lea eax, dword ptr
00092123 .68 B04C0900 push 00094CB0 ;UNICODE "%d"
00092128 .50 push eax
00092129 .FFD6 call esi ;mfc90u.#2537; <&mfc90u.#2537>
下面将上面根据机器码计算出的Key与刚才输出的字符串连接
0009214E .8D4424 20 lea eax, dword ptr
00092152 .50 push eax
00092153 .8D4C24 2C lea ecx, dword ptr
00092157 .51 push ecx
00092158 .8D4C24 24 lea ecx, dword ptr
0009215C .E8 2F020000 call 00092390 ;将Key字节长度异或0x11后的值接入串后面
此时eax指向的内存保存着Key的地址
下面就是比较Key和我们的输入的Key了
00092189 .52 push edx ;我们输入的Key
0009218A .8D4C24 14 lea ecx, dword ptr
0009218E .884424 24 mov byte ptr , al ;这些mov是成功提示字符串
00092192 .C64424 25 A7mov byte ptr , 0xA7
00092197 .C64424 26 CFmov byte ptr , 0xCF
0009219C .C64424 27 B2mov byte ptr , 0xB2
000921A1 .C64424 28 00mov byte ptr , 0x0
000921A6 .C64424 2C B3mov byte ptr , 0xB3
000921AB .C64424 2D C9mov byte ptr , 0xC9
000921B0 .884424 2E mov byte ptr , al
000921B4 .C64424 2F A6mov byte ptr , 0xA6
000921B9 .C64424 30 00mov byte ptr , 0x0
000921BE .FF15 3C420900 call dword ptr [<&mfc90u.#1599>] ;比较Key
000921C4 .85C0 test eax, eax
000921C6 .75 13 jnz short 000921DB
好了,计算Key已经看完了,现在来整理下:
机器码的每个字符 + 1 ^ 0x1f组成字符串和机器码的字节长度 ^ 0x11后的字符串连接
根据上面这个我们来写KeyGen,见Code<2>
Code<1>
#include "stdafx.h"
#include <windows.h>
int _tmain(int argc, _TCHAR* argv[])
{
BOOL bFound = FALSE;
unsigned int nValue = 0;
do
{
if (nValue - ((nValue / 3) * 3) == 1 &&
nValue - ((nValue / 5) * 5) == 2 &&
nValue - ((nValue / 7) * 7) == 4 &&
nValue - ((nValue / 13) * 13) == 6 &&
nValue - ((nValue / 17) * 17) == 8)
{
bFound = TRUE;
break;
}
} while (++nValue <= 100000);
if (bFound)
_tprintf(TEXT("%d\r\n"), nValue);
else
_tprintf(TEXT("Not Found!\r\n"));
return 0;
}
Code<2>
C代码:
#include "stdafx.h"
#include <windows.h>
int _tmain(int argc, _TCHAR* argv[])
{
TCHAR szMac;
_tscanf_s(TEXT("%s"), szMac, _countof(szMac));
TCHAR szSN;
int nLen = _tcslen(szMac);
int nIndex;
for (nIndex = 0; nIndex < nLen; nIndex++)
{
szSN = szMac + 1 ^ 0x1f;
}
szSN = (nLen * 2 ^ 0x11) + 0x30;
szSN = TEXT('\0');
_tprintf(TEXT("%s\r\n"), szSN);
return 0;
}
吾爱破解注册机源码:
Function GetMC(X)
GetMC=X
MC=GetMC
End Function
Function GetName(X)
End Function
Function GetSN()
mcLen = Len(MC)
Key=""
n = 1
do while n <= mcLen
tmp = Asc(Mid(MC, n, 1)) + 1
tmp = tmp xor &H1f
Key = Key & Chr(tmp)
n = n + 1
loop
mcLen = mcLen * 2
mcLen = mcLen xor &H11
Key = Key & mcLen
GetSN=Key
End Function
[版权声明]: 本文原创于封心锁爱, 转载请注明作者并保持文章的完整, 谢谢!
膜拜算法! 真的佩服! 膜拜大牛太详细了····· 精彩,认真学习一下,一会自己走一遍看看. 去反调试
00401970 6A FF push -0x1 --------------------改为retn
00401972 68 E9324000 push CrackMe.004032E9
00401977 64:A1 0000000>mov eax,dword ptr fs:
0040197D .50 push eax
- - 大神在上...
页:
[1]