【破解作者】 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后返回到输入法程序领空:
跟进00C7378F处的关键CALL后来到这里:代码: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
由上可见:注册码第一节每一位必须存在于固定字串"cbqtefylmx"中,且出现顺序必须为060311,故可得出第一节注册码为:"cyctbb"代码:……
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 ;不等时跳到出错
再后面是分别查找注册码第二、三节在固定字串"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"中;
深入分析得知469DACD8为当前日期后,了解了下这个值的含义:自1970年1月1日8:00到现在所经过的秒数。代码:设:
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
再观察算法中出现的几个常数: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
【版权声明】 本文纯属技术交流, 转载请注明作者并保持文章的完整, 谢谢!