sohd 发表于 2010-6-1 17:30:10

【9小组】【中华灯谜】【算法分析】

本帖最后由 月之精灵 于 2010-6-2 18:13 编辑

【破文标题】【PYG学员】【9小组】【中华灯谜】【算法分析】
【破文作者】sohd
【作者邮箱】[email protected]
【作者主页】
【破解工具】OD
【破解平台】win2003
【软件名称】中华灯谜 2008 build 12.15
【软件大小】2.87M
【原版下载】http://www.onlinedown.net/softdown/863_2.htm
【保护方式】aspack
【软件简介】〖中华灯谜〗软件是目前国产最好的灯谜软件,软件受到数十家刊物的特别撰文推荐。
【破解声明】只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
------------------------------------------------------------------------
【破解过程】第一次完整分析这个注册代码,有误之处还请见谅。我简单说一下几个重要的代码。

首先来到关键处
0054EF06   |.8B85 6CFFFFFF mov   eax, dword ptr
0054EF0C   |.50            push    eax
0054EF0D   |.8D95 64FFFFFF lea   edx, dword ptr
0054EF13   |.8B83 C0030000 mov   eax, dword ptr
0054EF19   |.E8 6A8CEFFF   call    <GetRegInfo>   ;获取定单号GetRegInfo 这个是我做的标签。
0054EF1E   |.8B85 64FFFFFF mov   eax, dword ptr
0054EF24   |.E8 DBA6EBFF   call    <C_Compute1>                     ; 把序号的数*10再相加这里进去看看看下面的C_Compute1处代码
0054EF29   |.B9 3A000000   mov   ecx, 3A
0054EF2E   |.99            cdq
0054EF2F   |.F7F9          idiv    ecx                              ; 把上面算出来的数,除3A 取余数给 eax
0054EF31   |.8BC2          mov   eax, edx
0054EF33   |.8D95 68FFFFFF lea   edx, dword ptr           ; 一个局部地址 一会会把算到的注册码前几个字符串地址写到这里。
0054EF39   |.E8 62A6EBFF   call    004095A0                         ; 获取注册码的前几个字符。。这个函数有点绕这里进去看看看下面的004095A0 处代码
0054EF3E   |.8D85 68FFFFFF lea   eax, dword ptr
0054EF44   |.50            push    eax
0054EF45   |.8D95 58FFFFFF lea   edx, dword ptr
0054EF4B   |.8B83 C0030000 mov   eax, dword ptr
0054EF51   |.E8 328CEFFF   call    <GetRegInfo>                     ;获取定单号GetRegInfo 这个是我做的标签。
0054EF56   |.8B85 58FFFFFF mov   eax, dword ptr
0054EF5C   |.E8 A3A6EBFF   call    <C_Compute1>                     ;把序号的数*10再相加这里进去看看看下面的C_Compute1处代码
0054EF61   |.8D95 5CFFFFFF lea   edx, dword ptr
0054EF67   |.E8 80DCFFFF   call    0054CBEC                         ;进去一个计算。这里进去看看看下面的0054CBEC 处代码
0054EF6C   |.8B85 5CFFFFFF mov   eax, dword ptr
0054EF72   |.E8 8DA6EBFF   call    <C_Compute1>
0054EF77   |.8D95 60FFFFFF lea   edx, dword ptr
0054EF7D   |.E8 4ADDFFFF   call    <Compute2>                     ;出来一串注册码   进去看看   看下面的Compute2 处代码
0054EF82   |.8B95 60FFFFFF mov   edx, dword ptr           ; |
0054EF88   |.58            pop   eax                              ; |
0054EF89   |.E8 E25CEBFF   call    <strcat>                         ; \strcat
0054EF8E   |.8B95 68FFFFFF mov   edx, dword ptr
0054EF94   |.58            pop   eax
0054EF95   |.E8 125EEBFF   call    00404DAC
0054EF9A   |.0F85 8F000000 jnz   0054F02F
0054EFA0   |.B8 F4F05400   mov   eax, 0054F0F4                  ;注册成功,谢谢你的注册!
0054EFA5   |.E8 D21DEFFF   call    00440D7C
0054EFAA   |.BA 18F15400   mov   edx, 0054F118                  ;本软件已注册
0054EFAF   |.8B83 B0030000 mov   eax, dword ptr
0054EFB5   |.E8 FE8BEFFF   call    00447BB8
0054EFBA   |.8D85 54FFFFFF lea   eax, dword ptr
0054EFC0   |.B9 30F15400   mov   ecx, 0054F130                  ;\dc0n.dll
0054EFC5   |.8B55 FC       mov   edx, dword ptr
0054EFC8   |.E8 E75CEBFF   call    00404CB4
0054EFCD   |.8B8D 54FFFFFF mov   ecx, dword ptr
0054EFD3   |.B2 01         mov   dl, 1
0054EFD5   |.A1 B8184700   mov   eax, dword ptr
0054EFDA   |.E8 8929F2FF   call    00471968
0054EFDF   |.8BF0          mov   esi, eax
0054EFE1   |.8D95 50FFFFFF lea   edx, dword ptr
0054EFE7   |.8B83 C0030000 mov   eax, dword ptr
0054EFED   |.E8 968BEFFF   call    <GetRegInfo>
0054EFF2   |.8B85 50FFFFFF mov   eax, dword ptr
0054EFF8   |.50            push    eax
0054EFF9   |.B9 44F15400   mov   ecx, 0054F144                  ;sepop
0054EFFE   |.BA 54F15400   mov   edx, 0054F154                  ;syssetup
0054F003   |.8BC6          mov   eax, esi
0054F005   |.8B18          mov   ebx, dword ptr
0054F007   |.FF53 04       call    dword ptr
0054F00A   |.8D85 4CFFFFFF lea   eax, dword ptr
0054F010   |.B9 30F15400   mov   ecx, 0054F130                  ;\dc0n.dll
0054F015   |.8B55 FC       mov   edx, dword ptr
0054F018   |.E8 975CEBFF   call    00404CB4
0054F01D   |.8B85 4CFFFFFF mov   eax, dword ptr
0054F023   |.BA 02000000   mov   edx, 2
0054F028   |.E8 03A9EBFF   call    00409930
0054F02D   |.EB 17         jmp   short 0054F046
0054F02F   |>B8 68F15400   mov   eax, 0054F168                  ;注册码错误,请重新输入!
0054F034   |.E8 431DEFFF   call    00440D7C



C_Compute1 代码如下(只贴关键代码):

004033B6   |>80EB 30       /sub   bl, 30
004033B9   |. |80FB 09       |cmp   bl, 9                           ;必须为数字。
004033BC   |. |77 25         |ja      short 004033E3                  ;不是数字就跳走了。
004033BE   |. |39F8          |cmp   eax, edi
004033C0   |. |77 21         |ja      short 004033E3
004033C2   |. |8D0480      |lea   eax, dword ptr       ;EAX第一次是0 ------------------------
004033C5   |. |01C0          |add   eax, eax                        ;   这断在循环的把数字相加
004033C7   |. |01D8          |add   eax, ebx
004033C9   |. |8A1E          |mov   bl, byte ptr             ; eai为字符串首地址
004033CB   |. |46            |inc   esi
004033CC   |. |84DB          |test    bl, bl
004033CE   |.^\75 E6         \jnz   short 004033B6                  ;-------------------------------------

转换成C++代码为:
        char* pszOrderID = "358588";   // 假如这个就是定单号。
        DWORD dwNum = 0;
        while (*pszOrderID != 0)
        {
                dwNum = dwNum*10;
                dwNum += (*pszOrderID-0x30);
                pszOrderID++;
        }
        return dwNum;
以上就是 C_Compute1 函数大概作用。。当然函数的其它代码可能还有别的作用。暂不深入看了。。



0054CBEC 代码如下(只贴关键代码):

0054CC09   |.81F3 F1250B00 xor   ebx, 0B25F1                      ;ebx就是前面算出来的数和 0B25F1 xor
0054CC0F   |.8BC3          mov   eax, ebx
0054CC11   |.33D2          xor   edx, edx
0054CC13   |.52            push    edx                              ; |format => NULL
0054CC14   |.50            push    eax                              ; |s
0054CC15   |.8D45 FC       lea   eax, dword ptr          ; |
0054CC18   |.E8 B3C9EBFF   call    <sprintf>                        ; \%d 到一个内存里。 sprintf我定义的标签 只能说是近似函数
0054CC1D   |.8B45 FC       mov   eax, dword ptr
0054CC20   |.0FB600      movzx   eax, byte ptr             ;10进制数的第一个
0054CC23   |.8B55 FC       mov   edx, dword ptr
0054CC26   |.0FB652 01   movzx   edx, byte ptr             ;10进制数的第2个
0054CC2A   |.03C2          add   eax, edx                         ;相加
0054CC2C   |.B9 05000000   mov   ecx, 5
0054CC31   |.99            cdq
0054CC32   |.F7F9          idiv    ecx                              ;除5
0054CC34   |.80C2 34       add   dl, 34                           ;余数+34
0054CC37   |.8855 F8       mov   byte ptr , dl             ; 第一个数值 保存
0054CC3A   |.8B45 FC       mov   eax, dword ptr
0054CC3D   |.0FB640 02   movzx   eax, byte ptr
0054CC41   |.8B55 FC       mov   edx, dword ptr
0054CC44   |.0FB652 03   movzx   edx, byte ptr
0054CC48   |.03C2          add   eax, edx
0054CC4A   |.B9 05000000   mov   ecx, 5
0054CC4F   |.99            cdq
0054CC50   |.F7F9          idiv    ecx
0054CC52   |.8BDA          mov   ebx, edx
0054CC54   |.80C3 33       add   bl, 33
0054CC57   |.885D F9       mov   byte ptr , bl          ; 第2个数值 保存
0054CC5A   |.8D45 F4       lea   eax, dword ptr
0054CC5D   |.8A55 F8       mov   dl, byte ptr
0054CC60   |.E8 2B7FEBFF   call    <new_copy>                     ;这函数会先new一个空间,再把数值copy到新空间。
0054CC65   |.8B45 F4       mov   eax, dword ptr
0054CC68   |.8D55 FC       lea   edx, dword ptr
0054CC6B   |.B9 1B000000   mov   ecx, 1B
0054CC70   |.E8 D382EBFF   call    <strInsert>                   ; 这个就是字符串插入函数1B是要插入的位置一般我们的定单号算出来的不会有1B长。所以 都是直接把数值往后插
0054CC75   |.8D45 F0       lea   eax, dword ptr
0054CC78   |.8BD3          mov   edx, ebx
0054CC7A   |.E8 117FEBFF   call    <new_copy>                  ; 同上
0054CC7F   |.8B45 F0       mov   eax, dword ptr
0054CC82   |.8D55 FC       lea   edx, dword ptr
0054CC85   |.B9 19000000   mov   ecx, 19
0054CC8A   |.E8 B982EBFF   call    <strInsert>                  ; 同上


转换成C++代码为:

        dwNum ^= 0x0B25F1;
        char szNum = {0};
        sprintf(szNum, "%d", dwNum);

        char szTemp = {0};
        szTemp = (*(szNum) + *(szNum+1))%5 + 0x34;
        szTemp = (*(szNum+2) + *(szNum+3))%5 + 0x33;

        strcat(szNum, szTemp);






004095A0 代码如下(只贴关键代码):

下面这段代码并非 004095A0 函数的是子函数内的。调用过程中的代码就不贴了,

00409E80      > \895D DC       mov   dword ptr , ebx
00409E83      .8975 D8       mov   dword ptr , esi
00409E86      .51            push    ecx
00409E87      .52            push    edx
00409E88      .E8 95000000   call    00409F22                         ;这里改变了ESI   进去



00409F22 代码如下(只贴关键代码):

0040A076   |$B9 0A000000   mov   ecx, 0A                        ;Case 55 ('U') of switch 0040A04A
0040A07B   |>8D75 9F       lea   esi, dword ptr
0040A07E   |>31D2          /xor   edx, edx
0040A080   |.F7F1          |div   ecx
0040A082   |.80C2 30       |add   dl, 30                        ;这块是算注册码 前面的几个字串
0040A085   |.80FA 3A       |cmp   dl, 3A
0040A088   |.72 03         |jb      short 0040A08D
0040A08A   |.80C2 07       |add   dl, 7
0040A08D   |>4E            |dec   esi
0040A08E   |.8816          |mov   byte ptr , dl
0040A090   |.09C0          |or      eax, eax                        ; 商不为0 就继续。
0040A092   |.^ 75 EA         \jnz   short 0040A07E

上面这一块就是核心代码了。算出来前N位字符

转换成C++代码为:
        CString strRegCode;
        int nNum = 0;
       
        nNum = dwNum%0x3A;

        int n1, n2, nTemp;

        n1 = nNum;
        do
        {
                nTemp = n1;
                n1 = n1/0x0A;   // 商
                n2 = nTemp%0x0A;   // 余数
                n2 += 0x30;
                strRegCode.Insert(0, (char*)&n2);
        } while (n1);
       
       
       
Compute2 代码如下(只贴关键代码):

0054CCEB   |.81F3 8776FBDD xor   ebx, DDFB7687                  ;ebx前面代码计算出来的值 和DDFB7687 xor
0054CCF1   |.8BC3          mov   eax, ebx
0054CCF3   |.33D2          xor   edx, edx
0054CCF5   |.52            push    edx                              ; |format => NULL
0054CCF6   |.50            push    eax                              ; |s
0054CCF7   |.8D45 FC       lea   eax, dword ptr          ; |
0054CCFA   |.E8 D1C8EBFF   call    <sprintf>                        ; \sprintf    格式化为十进制数
0054CCFF   |.8B45 FC       mov   eax, dword ptr
0054CD02   |.0FB600      movzx   eax, byte ptr             ;第一个数
0054CD05   |.8B55 FC       mov   edx, dword ptr
0054CD08   |.0FB652 01   movzx   edx, byte ptr             ;第2个数
0054CD0C   |.03C2          add   eax, edx
0054CD0E   |.B9 05000000   mov   ecx, 5
0054CD13   |.99            cdq
0054CD14   |.F7F9          idiv    ecx                              ;除5
0054CD16   |.80C2 66       add   dl, 66                           ;余数+66
0054CD19   |.8855 F8       mov   byte ptr , dl             ; 保存第一个值。
0054CD1C   |.8B45 FC       mov   eax, dword ptr
0054CD1F   |.0FB640 02   movzx   eax, byte ptr
0054CD23   |.8B55 FC       mov   edx, dword ptr
0054CD26   |.0FB652 03   movzx   edx, byte ptr
0054CD2A   |.03C2          add   eax, edx
0054CD2C   |.B9 05000000   mov   ecx, 5
0054CD31   |.99            cdq
0054CD32   |.F7F9          idiv    ecx
0054CD34   |.80C2 75       add   dl, 75
0054CD37   |.8855 F9       mov   byte ptr , dl      ; 保存第2个值。
0054CD3A   |.8B45 FC       mov   eax, dword ptr
0054CD3D   |.0FB640 04   movzx   eax, byte ptr
0054CD41   |.8B55 FC       mov   edx, dword ptr
0054CD44   |.0FB652 05   movzx   edx, byte ptr
0054CD48   |.03C2          add   eax, edx
0054CD4A   |.B9 05000000   mov   ecx, 5
0054CD4F   |.99            cdq
0054CD50   |.F7F9          idiv    ecx
0054CD52   |.80C2 7A       add   dl, 7A
0054CD55   |.8855 FA       mov   byte ptr , dl   ; 保存第3个值。
0054CD58   |.8B45 FC       mov   eax, dword ptr
0054CD5B   |.0FB640 06   movzx   eax, byte ptr
0054CD5F   |.8B55 FC       mov   edx, dword ptr
0054CD62   |.0FB652 07   movzx   edx, byte ptr
0054CD66   |.03C2          add   eax, edx
0054CD68   |.8B55 FC       mov   edx, dword ptr
0054CD6B   |.0FB652 08   movzx   edx, byte ptr
0054CD6F   |.03C2          add   eax, edx
0054CD71   |.B9 05000000   mov   ecx, 5
0054CD76   |.99            cdq
0054CD77   |.F7F9          idiv    ecx
0054CD79   |.80C2 69       add   dl, 69
0054CD7C   |.8855 FB       mov   byte ptr , dl; 保存第4个值。
0054CD7F   |.8D45 F4       lea   eax, dword ptr
0054CD82   |.8A55 F8       mov   dl, byte ptr
0054CD85   |.E8 067EEBFF   call    <new_copy>                  ;这函数会先new一个空间,再把数值copy到新空间。
0054CD8A   |.8B45 F4       mov   eax, dword ptr
0054CD8D   |.8D55 FC       lea   edx, dword ptr
0054CD90   |.B9 07000000   mov   ecx, 7
0054CD95   |.E8 AE81EBFF   call    <strInsert>                  ; 这个就是字符串插入函数7是要插入的位置
0054CD9A   |.8D45 F0       lea   eax, dword ptr
0054CD9D   |.8A55 FB       mov   dl, byte ptr
0054CDA0   |.E8 EB7DEBFF   call    <new_copy>               ;同上
0054CDA5   |.8B45 F0       mov   eax, dword ptr
0054CDA8   |.8D55 FC       lea   edx, dword ptr
0054CDAB   |.B9 03000000   mov   ecx, 3
0054CDB0   |.E8 9381EBFF   call    <strInsert>               ;同上
0054CDB5   |.8D45 EC       lea   eax, dword ptr
0054CDB8   |.8A55 F9       mov   dl, byte ptr
0054CDBB   |.E8 D07DEBFF   call    <new_copy>             ;同上
0054CDC0   |.8B45 EC       mov   eax, dword ptr
0054CDC3   |.8D55 FC       lea   edx, dword ptr
0054CDC6   |.B9 05000000   mov   ecx, 5
0054CDCB   |.E8 7881EBFF   call    <strInsert>            ;同上
0054CDD0   |.8D45 E8       lea   eax, dword ptr
0054CDD3   |.8A55 FA       mov   dl, byte ptr
0054CDD6   |.E8 B57DEBFF   call    <new_copy>               ;同上
0054CDDB   |.8B45 E8       mov   eax, dword ptr
0054CDDE   |.8D55 FC       lea   edx, dword ptr
0054CDE1   |.B9 09000000   mov   ecx, 9
0054CDE6   |.E8 5D81EBFF   call    <strInsert>               ;同上

转换成C++代码为:

        dwNum ^= 0xDDFB7687;
        memset(szNum, 0, sizeof(szNum));
        sprintf(szNum, "%lu", dwNum);

        szTemp = (*(szNum) + *(szNum+1))%5 + 0x66;
        szTemp = (*(szNum+2) + *(szNum+3))%5 + 0x75;
        szTemp = (*(szNum+4) + *(szNum+5))%5 + 0x7A;
        szTemp = (*(szNum+6) + *(szNum+7) + *(szNum+8))%5 + 0x69;
       
        CString strTemp(szNum);
        strTemp.Insert(7-1, szTemp);
        strTemp.Insert(3-1, szTemp);
        strTemp.Insert(5-1, szTemp);
        strTemp.Insert(9-1, szTemp);
       
       
下面是注册的整体代码:

        CString strOrderID;
        GetDlgItemText(EDT_DDH, strOrderID);         // 获取到定单号
        char* pszOrderID = strOrderID.GetBuffer(0);
        strOrderID.ReleaseBuffer();

        DWORD dwNum = 0;
        while (*pszOrderID != 0)
        {
                dwNum = dwNum*10;
                dwNum += (*pszOrderID-0x30);
                pszOrderID++;
        }

        CString strRegCode;
        int nNum = 0;
       
        nNum = dwNum%0x3A;

        int n1, n2, nTemp;

        n1 = nNum;
        do
        {
                nTemp = n1;
                n1 = n1/0x0A;   // 商
                n2 = nTemp%0x0A;   // 余数
                n2 += 0x30;
                strRegCode.Insert(0, (char*)&n2);
        } while (n1);

        dwNum ^= 0x0B25F1;
        char szNum = {0};
        sprintf(szNum, "%d", dwNum);

        char szTemp = {0};
        szTemp = (*(szNum) + *(szNum+1))%5 + 0x34;
        szTemp = (*(szNum+2) + *(szNum+3))%5 + 0x33;

        strcat(szNum, szTemp);

        char* pszNum = szNum;
        dwNum = 0;
        while (*pszNum != 0)
        {
                dwNum = dwNum*10;
                dwNum += (*pszNum-0x30);
                pszNum++;
        }

        dwNum ^= 0xDDFB7687;
        memset(szNum, 0, sizeof(szNum));
        sprintf(szNum, "%lu", dwNum);

        szTemp = (*(szNum) + *(szNum+1))%5 + 0x66;
        szTemp = (*(szNum+2) + *(szNum+3))%5 + 0x75;
        szTemp = (*(szNum+4) + *(szNum+5))%5 + 0x7A;
        szTemp = (*(szNum+6) + *(szNum+7) + *(szNum+8))%5 + 0x69;
       
        CString strTemp(szNum);
        strTemp.Insert(7-1, szTemp);
        strTemp.Insert(3-1, szTemp);
        strTemp.Insert(5-1, szTemp);
        strTemp.Insert(9-1, szTemp);

        strRegCode += strTemp;

        SetDlgItemText(EDT_REGCODE, strRegCode);// 最终注册码。
------------------------------------------------------------------------
【破解总结】
------------------------------------------------------------------------
【版权声明】

老万 发表于 2010-6-1 19:29:30

学习了,谢谢楼主分享

xinjian185 发表于 2010-6-1 19:38:21

学习了露珠的大作

abbsabbs 发表于 2010-6-1 20:06:55

本来想破这个,结果没搞定,进来学习下哈

kghong 发表于 2010-6-1 20:43:17

我是来学习下的

jjfsxz 发表于 2010-6-2 07:32:38

牛人呀,谢谢

xZeI 发表于 2010-6-3 16:06:11

宋宋好强

zhtech 发表于 2011-5-19 14:43:57

这个不错,对着学习,我就不信学不会
页: [1]
查看完整版本: 【9小组】【中华灯谜】【算法分析】