【破解作者】 lelfei
【作者邮箱】 lelfei@163.com
【作者主页】 http://www.lelfei.cn
【使用工具】 OD
【破解平台】 Win9x/NT/2000/XP
【软件名称】 数字五笔输入法2007.1逆向研究
【下载地址】 Google一下
【软件简介】 摘自软件简介(帮作者宣传下~):
    十个数字,五种笔画输中文,三分钟掌握,单手输入,速度可达百字/分! 
    完全摒弃传统的汉字拆分、字根理念!笔顺输入,上手就会,不背字根、不拆汉字。
    创新专利技术,完美解决笔画输入慢,不能支持词组输入的问题。
    强大的软件功能,智能记忆,标点自动识别,全鼠标输入,词组连续联想,同音字查询,简体转繁体,支持GBK二万余汉字输入!
【软件大小】 1.9M
【加壳方式】 无
【破解声明】 纯属兴趣研究一下~
--------------------------------------------------------------------------------
【破解内容】

一、前言

领导想学打字,可是既不想背五笔字根又不想学拼音,幸好他平时偶尔用用手机发短信,于是让我找个跟手机上T9差不多的输入法,领导准备买个正版了,我顺便研究了下这个软件,于是乎有了这篇破文。。。

二、观察

输入法程序安装好调出输入法,在“输入法设置”里可以找到输入SN的地方,随便输入一点东西后点“激活”按钮,有“注册码无效”的出错提示。以前一直没有过调试输入法的经验,准备下手时突然傻眼了:输入法没有主程序,该怎么调试?

想了半天突然来了灵感:在记事本里打字的时候输入法程序已经调入内存,这时候应该可以调试吧?测试了下,用OD载入记事本,在记事本里打开数字五笔输入法,再到OD里的Memory窗口里看了下:有szWB.Code段。好!就从这里下手了!

三、调试

用OD载入记事本,再在记事本里调入数字五笔输入法,并打开“输入法设置”,在切换到OD在命令行里下断点:bp GetDlgItemTextA让程序在获取注册码输入框中的内容时中断。输入假注册码:1111-2222-3333,点击“激活”,程序断下来了。经过1个RET后返回到输入法程序领空:

代码:

00C736ED    8B35 3CF3C800 mov     esi, dword ptr [<&USER32.GetDlgItemTextA>] ; USER32.GetDlgItemTextA
00C736F3    BD E8070000   mov     ebp, 7E8
00C736F8    8B00          mov     eax, dword ptr [eax]
00C736FA    BF 04010000   mov     edi, 104
00C736FF    03C5          add     eax, ebp
00C73701    57            push    edi
00C73702    50            push    eax
00C73703    68 F7030000   push    3F7
00C73708    53            push    ebx
00C73709    FFD6          call    esi                                        ;获取注册码第一节内容
00C7370B    A1 50F2C800   mov     eax, dword ptr [<&StrokeMBHandle.sImeG>]   ;<--中断后返回到这里
00C73710    57            push    edi
00C73711    8B00          mov     eax, dword ptr [eax]
00C73713    05 EC080000   add     eax, 8EC
00C73718    50            push    eax
00C73719    68 F8030000   push    3F8
00C7371E    53            push    ebx
00C7371F    FFD6          call    esi                                       ;获取注册码第二节内容
00C73721    A1 50F2C800   mov     eax, dword ptr [<&StrokeMBHandle.sImeG>]
00C73726    57            push    edi
00C73727    BF F0090000   mov     edi, 9F0
00C7372C    8B00          mov     eax, dword ptr [eax]
00C7372E    03C7          add     eax, edi
00C73730    50            push    eax
00C73731    68 F9030000   push    3F9
00C73736    53            push    ebx
00C73737    FFD6          call    esi                                       ;获取注册码第三节内容
00C73739    A1 50F2C800   mov     eax, dword ptr [<&StrokeMBHandle.sImeG>]
00C7373E    BE 4081C900   mov     esi, 00C98140
00C73743    56            push    esi
00C73744    8B00          mov     eax, dword ptr [eax]
00C73746    03C5          add     eax, ebp
00C73748    50            push    eax
00C73749    E8 42C10000   call    00C7F890                                  ;判断第一节内容是否为空
00C7374E    59            pop     ecx
00C7374F    85C0          test    eax, eax
00C73751    59            pop     ecx
00C73752    0F84 7A020000 je      00C739D2
00C73758    A1 50F2C800   mov     eax, dword ptr [<&StrokeMBHandle.sImeG>]
00C7375D    56            push    esi
00C7375E    8B00          mov     eax, dword ptr [eax]
00C73760    05 EC080000   add     eax, 8EC
00C73765    50            push    eax
00C73766    E8 25C10000   call    00C7F890                                  ;判断第二节内容是否为空
00C7376B    59            pop     ecx
00C7376C    85C0          test    eax, eax
00C7376E    59            pop     ecx
00C7376F    0F84 5D020000 je      00C739D2
00C73775    A1 50F2C800   mov     eax, dword ptr [<&StrokeMBHandle.sImeG>]
00C7377A    56            push    esi
00C7377B    8B00          mov     eax, dword ptr [eax]
00C7377D    03C7          add     eax, edi
00C7377F    50            push    eax
00C73780    E8 0BC10000   call    00C7F890                                  ;判断第三节内容是否为空
00C73785    59            pop     ecx
00C73786    85C0          test    eax, eax
00C73788    59            pop     ecx
00C73789    0F84 43020000 je      00C739D2
00C7378F    E8 99370000   call    00C76F2D                                  ;关键!注册算法过程,跟进!
00C73794    85C0          test    eax, eax                                  ;判断是否注册成功
00C73796    0F84 28020000 je      00C739C4

跟进00C7378F处的关键CALL后来到这里:

代码:

……
00C76FF9    8A843D DCFEFF>mov     al, byte ptr [ebp+edi-124]                ;取注册码第一节每一位
00C77000    8DB43D DCFEFF>lea     esi, dword ptr [ebp+edi-124]
00C77007    8365 FC 00    and     dword ptr [ebp-4], 0
00C7700B    0FBEC0        movsx   eax, al
00C7700E    8945 E4       mov     dword ptr [ebp-1C], eax
00C77011    FF75 E4       push    dword ptr [ebp-1C]
00C77014    E8 428B0000   call    00C7FB5B
00C77019    59            pop     ecx
00C7701A    8B4D FC       mov     ecx, dword ptr [ebp-4]
00C7701D    0FBE89 70FEC8>movsx   ecx, byte ptr [ecx+C8FE70]                ;[C8FE70]="cbqtefylmx"
00C77024    3BC1          cmp     eax, ecx                                  ;在固定字串中查找注册码每一位的位置
00C77026    74 0B         je      short 00C77033
00C77028    FF45 FC       inc     dword ptr [ebp-4]
00C7702B    837D FC 0A    cmp     dword ptr [ebp-4], 0A                     ;查找10次
00C7702F  ^ 7C E0         jl      short 00C77011
00C77031    EB 06         jmp     short 00C77039                            ;未找到时设置为固定值 i
00C77033    8B45 FC       mov     eax, dword ptr [ebp-4]
00C77036    8945 F4       mov     dword ptr [ebp-C], eax
00C77039    8A45 F4       mov     al, byte ptr [ebp-C]
00C7703C    04 30         add     al, 30                                    ;把找到的位置数转换为数字字符
00C7703E    47            inc     edi
00C7703F    3BFB          cmp     edi, ebx
00C77041    8806          mov     byte ptr [esi], al                        ;保存转换后的数字字符串
00C77043  ^ 7C B4         jl      short 00C76FF9
00C77045    BE B845C900   mov     esi, 00C945B8                             ; 060311
00C7704A    56            push    esi
00C7704B    E8 D0880000   call    00C7F920
00C77050    80A405 DCFEFF>and     byte ptr [ebp+eax-124], 0
00C77058    56            push    esi
00C77059    E8 6B980000   call    00C808C9
00C7705E    8D85 DCFEFFFF lea     eax, dword ptr [ebp-124]
00C77064    56            push    esi                                       ; 060311
00C77065    50            push    eax                                       ; iiii
00C77066    E8 25880000   call    00C7F890                                  ;把数字字串与固定字串"060311"比较
00C7706B    83C4 10       add     esp, 10
00C7706E    85C0          test    eax, eax
00C77070    0F85 DF010000 jnz     00C77255                                  ;不等时跳到出错

由上可见:注册码第一节每一位必须存在于固定字串"cbqtefylmx"中,且出现顺序必须为060311,故可得出第一节注册码为:"cyctbb"

再后面是分别查找注册码第二、三节在固定字串"pqyuicsjkx"、"kpocetyqah"中的位置,并转换为数字字串,与第一节转换过程完全相同。在此就略过代码了。

再往下就是对第二节第三节转换出来的数字字串进行计算的过程了:

代码:

00C77169    E8 25980000   call    00C80993                    ; 获取当前时间nowdate
00C7716E    59            pop     ecx
00C7716F    8D45 E8       lea     eax, dword ptr [ebp-18]
00C77172    50            push    eax
00C77173    BE B445C900   mov     esi, 00C945B4               ; %u
00C77178    8D85 D8FDFFFF lea     eax, dword ptr [ebp-228]
00C7717E    56            push    esi
00C7717F    50            push    eax
00C77180    E8 DA970000   call    00C8095F                    ; 第2节转换为数值i2
00C77185    83C4 0C       add     esp, 0C
00C77188    8D45 EC       lea     eax, dword ptr [ebp-14]
00C7718B    50            push    eax
00C7718C    8D85 D4FCFFFF lea     eax, dword ptr [ebp-32C]
00C77192    56            push    esi
00C77193    50            push    eax
00C77194    E8 C6970000   call    00C8095F                    ; 第3节转换为数值i3
00C77199    8175 E8 929B0>xor     dword ptr [ebp-18], 0B9B92  ; i2 xor n2(n2=0B9B92)
00C771A0    8B45 E8       mov     eax, dword ptr [ebp-18]     ; i21=i2 xor n2
00C771A3    BE 10270000   mov     esi, 2710
00C771A8    33D2          xor     edx, edx
00C771AA    8BFE          mov     edi, esi
00C771AC    8B4D EC       mov     ecx, dword ptr [ebp-14]     ; ecx=i3
00C771AF    F7F7          div     edi                         ; i22=i21/10000
00C771B1    81F1 0A120300 xor     ecx, 3120A                  ; i31=i3 xor n3(n3=3120A)
00C771B7    33D2          xor     edx, edx
00C771B9    83C4 0C       add     esp, 0C
00C771BC    6A 64         push    64
00C771BE    5B            pop     ebx
00C771BF    8945 EC       mov     dword ptr [ebp-14], eax     ; i22
00C771C2    8BC1          mov     eax, ecx
00C771C4    F7F6          div     esi                         ; i32=i31/10000
00C771C6    33D2          xor     edx, edx
00C771C8    8BF8          mov     edi, eax
00C771CA    8BC1          mov     eax, ecx
00C771CC    8BF7          mov     esi, edi
00C771CE    69F6 10270000 imul    esi, esi, 2710              ; i32*10000
00C771D4    2BC6          sub     eax, esi                    ; i31-i32*10000
00C771D6    F7F3          div     ebx                         ; i33=(i31-i32*10000)/100
00C771D8    8B55 EC       mov     edx, dword ptr [ebp-14]     ; i22
00C771DB    69D2 10270000 imul    edx, edx, 2710              ; i22*10000
00C771E1    8945 F8       mov     dword ptr [ebp-8], eax      ; i33
00C771E4    8B45 E8       mov     eax, dword ptr [ebp-18]     ; i21
00C771E7    2BC2          sub     eax, edx
00C771E9    33D2          xor     edx, edx
00C771EB    F7F3          div     ebx                         ; i23=(i21-i22*10000)/100
00C771ED    8B55 F8       mov     edx, dword ptr [ebp-8]      ; i33
00C771F0    6BD2 64       imul    edx, edx, 64                ; i33*100
00C771F3    2BCA          sub     ecx, edx                    ; i31-i33*100
00C771F5    2BCE          sub     ecx, esi                    ; i34=i31-i33*100-i32*10000
00C771F7    69C0 6D010000 imul    eax, eax, 16D               ; i23*365
00C771FD    05 CE2A0000   add     eax, 2ACE                   ; i24=i23*365+10958
00C77202    8945 F0       mov     dword ptr [ebp-10], eax     ; i24
00C77205    33C0          xor     eax, eax
00C77207    8945 F4       mov     dword ptr [ebp-C], eax
00C7720A    4F            dec     edi                         ; i32-1
00C7720B    DF6D F0       fild    qword ptr [ebp-10]          ; i24
00C7720E    894D F0       mov     dword ptr [ebp-10], ecx     ; i34
00C77211    8945 F4       mov     dword ptr [ebp-C], eax
00C77214    DF6D F0       fild    qword ptr [ebp-10]          ; i34
00C77217    897D F0       mov     dword ptr [ebp-10], edi     ; i32-1
00C7721A    8945 F4       mov     dword ptr [ebp-C], eax
00C7721D    DEC1          faddp   st(1), st                   ; i24+i34
00C7721F    DF6D F0       fild    qword ptr [ebp-10]          ; i32-1
00C77222    DC0D D8FFC800 fmul    qword ptr [C8FFD8]          ; (i32-1)*30.5
00C77228    DEC1          faddp   st(1), st                   ; i4=(i32-1)*30.5+i24+i34
00C7722A    E8 658C0000   call    00C7FE94                    ; eax=int(i4)
00C7722F    69C0 A0050000 imul    eax, eax, 5A0               ; i4*1440
00C77235    0345 EC       add     eax, dword ptr [ebp-14]     ; i4*1440+i22
00C77238    6BC0 3C       imul    eax, eax, 3C                ; (i4*1440+i22)*60
00C7723B    0345 F8       add     eax, dword ptr [ebp-8]      ; i41=(i4*1440+i2)*60+i33
00C7723E    8BC8          mov     ecx, eax
00C77240    2B4D E0       sub     ecx, dword ptr [ebp-20]     ; i41-469DACD8(nowdate)
00C77243    81F9 80E5F300 cmp     ecx, 0F3E580                ; 必须小于0F3E580
00C77249    77 0A         ja      short 00C77255
00C7724B    3B45 E0       cmp     eax, dword ptr [ebp-20]     ; 必须大于469DACD8(nowdate)
00C7724E    7E 05         jle     short 00C77255
00C77250    6A 01         push    1
00C77252    58            pop     eax                         ; 设置注册标志位
00C77253    EB 02         jmp     short 00C77257
00C77255    33C0          xor     eax, eax
00C77257    5F            pop     edi
00C77258    5E            pop     esi
00C77259    5B            pop     ebx
00C7725A    C9            leave
00C7725B    C3            retn

算法比较绕,不过弄懂每部分所代表的含义后流程还是相当清晰的:

注册码第一节必须在字串"cbqtefylmx"中,且出现顺序必须为060311;
注册码第二节必须在字串"pqyuicsjkx"中;
注册码第三节必须在字串"kpocetyqah"中;

代码:

设:
i2=第二节字串出现顺序转换为数值
i3=第三节字串出现顺序转换为数值
n2=0xB9B92=760722(估计是作者的生日吧^_^)
n3=0x3120A=201226(程序最终过期日期?)
i21=i2 xor n2
i22=i21/10000  前2位
i23=(i21-i22*10000)/100  中2位
i24=i23*365+10958

i31=i3 xor n3
i32=i31/10000  前2位
i33=(i31-i32*10000)/100  中2位
i34=i31-i33*100-i32*10000  后2位

i4=int((i32-1)*30.5+i24+i34)
i41=(i4*1440+i22)*60+i33

则必须满足算法:
i41-469DACD8<=F3E580
i41>=469DACD8

深入分析得知469DACD8为当前日期后,了解了下这个值的含义:自1970年1月1日8:00到现在所经过的秒数。

再观察算法中出现的几个常数:365,10958,30.5,1440,60,0F3E580
作出大胆观测:365为一年的天数;30.5为平均每月的天数;1440是一天的分钟数;60是秒数;0F3E580为注册码过期的秒数,折合起来刚好是185天;那10958是什么呢?我费了很大劲才算出来:是1970年1月1日到2000年1月1日之间的天数!

再代入上面的算法过程中可以发现,至此程序算法就非常明朗了:
第二,三节数值意义分别为:
i2:XXXXXX  i3:XXXXXX
   分年       月秒日

这样注册机就非常好写了。

--------------------------------------------------------------------------------
【注册机】
附VB注册机源码:

代码:

Option Explicit

Const sSerialTable As String = "pqyuicsjkxkpocetyqah"

Private Sub cmdGenerate_Click()
On Error GoTo hErr
Dim iYear As Long, iMonth As Long, iDay As Long, i As Long
Dim d As Date, s1 As String, s2 As String, sSN1 As String, sSN2 As String

    d = DateAdd("d", 185, CDate(txtText1.Text))
    iYear = Year(d) - 2000
    iMonth = Month(d)
    iDay = Day(d)
    s1 = Format((iYear * 100) Xor 760722, "000000")
    s2 = Format((iMonth * 10000 + iDay) Xor 201226, "000000")
    For i = 1 To 6
        sSN1 = sSN1 & Mid$(sSerialTable, CInt(Mid$(s1, i, 1)) + 1, 1)
        sSN2 = sSN2 & Mid$(sSerialTable, CInt(Mid$(s2, i, 1)) + 11, 1)
    Next
    txtText2.Text = "cyctbb-" & sSN1 & "-" & sSN2
    Exit Sub
hErr:
    MsgBox "Something Wrong...", vbInformation
End Sub

Private Sub Form_Load()
    txtText1.Text = Format(Now, "yyyy-mm-dd")
End Sub

--------------------------------------------------------------------------------
【版权声明】 本文纯属技术交流, 转载请注明作者并保持文章的完整, 谢谢!