封心锁爱 发表于 2013-12-14 17:33

【吾爱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
[版权声明]: 本文原创于封心锁爱, 转载请注明作者并保持文章的完整, 谢谢!

















Bds1r 发表于 2013-12-14 17:41

膜拜算法! 真的佩服!

Ylca 发表于 2013-12-14 17:50

ZZ1196326205 发表于 2013-12-14 17:57

膜拜大牛太详细了·····

vipcrack 发表于 2013-12-14 18:17

精彩,认真学习一下,一会自己走一遍看看.

840870819 发表于 2013-12-14 18:20

去反调试
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

cui3030 发表于 2013-12-14 18:20

- - 大神在上...
页: [1]
查看完整版本: 【吾爱2013CM大赛解答】--LoveKido Update-- JoyChou追码分析