-
-
[原创]WYSIWYG Web Builder v8.0.3 注册保护机制分析
-
发表于: 2012-1-17 20:18 10403
-
【破解作者】网络浪子
【作者邮箱】netprodiag@163.com
【使用工具】PEiD0.95、LordPE、ImportREC v1.7e、OllyICE v1.10、CFF Explorer 以及 IDA v6.1
【破解平台】Windows 7 Ultimate
【破解日期】2011.12.25 - 2011.12.29
【软件名称】WYSIWYG Web Builder 8 (具体版本:8.0.3)
【软件大小】原版大小:2.27 MB,脱壳后大小:7.69 MB(不知能不能继续减肥优化未深入研究)
【加壳方式】PECompact 2.x -> Jeremy Collake
【保护机制】网络验证、本地验证、不定时(定时器+随机数)检测注册用户的合法性
【授权方式】共享软件,提供 30 天的试用期
【下载地址】http://www.wysiwygwebbuilder.com/webbuilder8.zip
【软件简介】
WYSIWYG Web Builder 是一款以所见即所得的方式创建完整网站的工具,你甚至无需学习 HTML,就可以创建自己的网页。
【破解声明】本文仅供技术交流、学习之用,失误之处敬请大虾指教!
一、破解前期准备工作(基础部分高手飘过)
查壳。用 PEiD v0.95 查壳,显示 PECompact 2.x -> Jeremy Collake,用 PEiD 自带的通用脱壳插件可以直接脱壳并能正常
运行,不过脱壳后的程序比较大,本人采用了手动脱壳修复的方法。即用 OllyICE(为便于描述,以下简称 OD)载入
WYSIWYG Web Builder 8 UNICODE 的主程序文件 WebBuilder.exe(快速统计报告提示“被压缩、加密或包含了大量的嵌入式
数据”之类的信息,点击“否”按钮),利用 ESP 定律(单步执行直到寄存器窗格中的 ESP 被着色为红色时),在命令行直
接输入 hr esp 下硬件访问断点,然后按 F9(或 shift + F9,忽略异常继续执行)继续运行程序,5 次 F9 后,断在 jmp eax
处,注意:此时 eax 的值就是 OEP 了,按 F8 单步一下,停在程序的 OEP 处,上下翻动看看 IAT 和入口代码,立即可知是
用标准的 Visual C++ 6.0 with MFC 编写的。用鼠标点选 OEP 那行,然后右击在弹出的菜单中选择“用 OllyDump 脱壳调试
的进程”,并在弹出的对话框中复制“修正为:”右侧编辑框中的 16 进制值(暂存一下,待会用到,暂时别关闭 OD),启动
LordPE 在进程列表中找到 WebBuilder.exe,右击在弹出的菜单中依次选择“修正镜像大小”和“完整转储”,转存位置定位
到本软件安装文件夹下,生成 dumped.exe,继续运行 ImportREC,在“附加一个活动进程”下面的组合框的列表中选中 WebBuilder.exe,
在 OPE 处粘贴刚才在 OD 中复制的 16 进制格式的 OEP 值,点击“自动查找 IAT”按钮,再点“获取引入表”按钮,
点击“显示无效的”按钮,经检测,没有无效的引入表(再好不多了),点击“修复转储文件”,从打开的对话框中选择 dumped.exe,
最后生成脱壳修复后的文件 dumped_.exe,双击 dumped_.exe 试运行,没有任何问题,用 CFF Explorer 检查一下能否可以减肥,粗略
看了一下,好像无需减肥优化了。鉴于本软件界面语言采用了外挂式提供的方法,并且界面原始语言另行在软件中采用 hardcode 的方式
又提供了一套,因此即使替换标准资源中的原始语言进行本地化,软件设计思路是如果无法找到外挂式语言文件,将调用 hardcode 方式
的界面语言字符串,因而不能实现本地化的目的。所以,对脱壳后的主程序的资源节没有进行移到节最后的处理,没有必要也没有意义了。
二、注册保护机制初步分析
1、查找"激活"按钮的事件处理函数入口
用 OD 载入主程序 WebBuilder.exe,直接按 F9 运行,弹出注册对话框,如下图:
要求输入注册 Email 和序列号,输入试炼码,注册 Email:net0520@163.com,序列号:cracker0520(字母会自动转换为大写),点击“激活”按钮,弹出如下图所示的消息框:
乱输入的注册信息肯定无效(如果谁乱输就能正确注册,那你还不赶快买**,至少中奖 100000000 元以上,呵呵!),暂时别关闭此消息框,我们采用 F12 暂停+调用堆栈法来直接定位调用提示信息的位置,如下如图:
双击红色标记的那行(当然也可以是截图中提示信息与截图相同的其它位置,不过其它位置不是 MFC42.dll 就是 user32.dll 的模块领空,需要单步几步才能返回主模块空间,如果截图列表中存在主模块(被调试的程序)空间,那可以快速定位错误信息的调用位置。),返回到反汇编窗口并乖乖地定位到错误信息提示的调用位置:
查看信息窗口提示:
esi=00DDDF50
跳转来自 004C353A, 004C3548, 004C355A(有3处跳转到这里显示错误信息),3处地址非常接近并且从左到右依次增大,因此,直接在信息窗口将鼠标指向“跳转来自……”这项点右键,然后从弹出的菜单中选择第一个跳转返回到如下如下的位置:
然后回到函数的开始位置下普通断点(用 F2),就像下图所示:
2、进入验证函数初步分析验证机制
然后重新载入(用 Ctrl+F2)被调试的程序,按 F9 运行,弹出注册界面是依然输入试炼码:注册 Email:net0520@163.com,序列号:cracker0520,点击“激活”按钮,断在刚才用 F2 下断的位置,即上图中的 004C3450 处,看下面的分析:
004C3450 . 55 push ebp
004C3451 . 8BEC mov ebp, esp
004C3453 . 6A FF push -1
004C3455 . 68 10AB7E00 push 007EAB10 ; SE 处理程序安装
004C345A . 64:A1 0000000>mov eax, dword ptr fs:[0]
004C3460 . 50 push eax
004C3461 . 64:8925 00000>mov dword ptr fs:[0], esp
004C3468 . 83EC 70 sub esp, 70
004C346B . 53 push ebx
004C346C . 56 push esi
004C346D . 57 push edi
004C346E . 8BF9 mov edi, ecx
004C3470 . 8965 F0 mov dword ptr [ebp-10], esp
004C3473 . 6A 01 push 1
004C3475 . 897D B4 mov dword ptr [ebp-4C], edi
004C3478 . E8 6D992000 call <jmp.&mfc42.#CWnd::UpdateData_6334> ; 更新对话框数据吧
004C347D . 6A 00 push 0 ; /timer = NULL
004C347F . FF15 E416BB00 call dword ptr [<&msvcrt.time>] ; \time
004C3485 . 50 push eax ; /seed = FFFFFFFF (-1.)
004C3486 . FF15 E016BB00 call dword ptr [<&msvcrt.srand>] ; \srand
004C348C . 83C4 08 add esp, 8
004C348F . FF15 DC16BB00 call dword ptr [<&msvcrt.rand>] ; [rand
004C3495 . 8D45 E0 lea eax, dword ptr [ebp-20] ; 上面被调用的3个函数是典型的通过时间作为随机种子产生随机数的方法
004C3498 . 8BCF mov ecx, edi
004C349A . 50 push eax ; /Arg1 = FFFFFFFF
004C349B . E8 A00A0000 call 004C3F40 ; \WebBuild.004C3F40
004C34A0 . 8D4D D8 lea ecx, dword ptr [ebp-28]
004C34A3 . C745 FC 00000>mov dword ptr [ebp-4], 0
004C34AA . E8 8B972000 call <jmp.&mfc42.#CString::CString_540>
004C34AF . 8D4D E4 lea ecx, dword ptr [ebp-1C]
004C34B2 . C645 FC 01 mov byte ptr [ebp-4], 1
004C34B6 . E8 7F972000 call <jmp.&mfc42.#CString::CString_540>
004C34BB . 8D4D E8 lea ecx, dword ptr [ebp-18]
004C34BE . C645 FC 02 mov byte ptr [ebp-4], 2
004C34C2 . E8 73972000 call <jmp.&mfc42.#CString::CString_540>
004C34C7 . 8D4D E4 lea ecx, dword ptr [ebp-1C]
004C34CA . C645 FC 03 mov byte ptr [ebp-4], 3
004C34CE . 51 push ecx
004C34CF . 8D8F E8010000 lea ecx, dword ptr [edi+1E8]
004C34D5 . E8 5A972000 call <jmp.&mfc42.#CWnd::GetWindowTextA_3874> ; 获取输入的 Email 文本
004C34DA . 8D55 E8 lea edx, dword ptr [ebp-18]
004C34DD . 8D8F A8010000 lea ecx, dword ptr [edi+1A8]
004C34E3 . 52 push edx
004C34E4 . E8 4B972000 call <jmp.&mfc42.#CWnd::GetWindowTextA_3874> ; 获取输入的注册码
004C34E9 . 8D4D E4 lea ecx, dword ptr [ebp-1C]
004C34EC . E8 199A2000 call <jmp.&mfc42.#CString::TrimLeft_6282> ; 删除用户输入的注册信息的左边的空格(如果有),可能是这个作用吧
004C34F1 . 8D4D E4 lea ecx, dword ptr [ebp-1C]
004C34F4 . E8 0B9A2000 call <jmp.&mfc42.#CString::TrimRight_6283> ; 删除用户输入的注册信息的右边的空格(如果有),可能是这个作用吧
004C34F9 . 6A 2E push 2E
004C34FB . 8D4D E4 lea ecx, dword ptr [ebp-1C]
004C34FE . E8 239C2000 call <jmp.&mfc42.#CString::TrimRight_6929> ; 同理
004C3503 . 8D4D E8 lea ecx, dword ptr [ebp-18]
004C3506 . E8 FF992000 call <jmp.&mfc42.#CString::TrimLeft_6282> ; 同理
004C350B . 8D4D E8 lea ecx, dword ptr [ebp-18]
004C350E . E8 F1992000 call <jmp.&mfc42.#CString::TrimRight_6283> ; 同理
004C3513 . 6A 2E push 2E
004C3515 . 8D4D E8 lea ecx, dword ptr [ebp-18]
004C3518 . E8 099C2000 call <jmp.&mfc42.#CString::TrimRight_6929> ; 同理
004C351D . 8B45 E4 mov eax, dword ptr [ebp-1C] ; 输入的 Email
004C3520 . 8B48 F8 mov ecx, dword ptr [eax-8] ; Email 长度
004C3523 . 85C9 test ecx, ecx ; 测试输入输入了 Email
004C3525 . 0F84 9C080000 je 004C3DC7 ; 如果未输入则跳转到出错信息提示处,刚开始分析时调用错误信息的第一处流程
004C352B . 6A 40 push 40 ; 40h=='@',像什么,合法的 Email 中就必须含有此字符吧
004C352D . 8D4D E4 lea ecx, dword ptr [ebp-1C]
004C3530 . E8 A9982000 call <jmp.&mfc42.#CString::Find_2763> ; 顾名思义,知道此函数检查输入的 Email 中是否含有“@”字符
004C3535 . 83CE FF or esi, FFFFFFFF ; 如果含有此字符,则在 eax 中返回 "@" 字符所在的位置索引,否则返回 0FFFFFFFFh
004C3538 . 3BC6 cmp eax, esi ; eax==esi(即 0FFFFFFFFh)?,官方验证 Email 的合法性仅仅验证是否包含 "@" 字符
004C353A . 0F84 8A080000 je 004C3DCA ; 跳转则说明输入的 Email 中无"@"字符,当然不是合法的 Email 格式,跳转到错误信息处理处(3处中的第2处)
004C3540 . 8B4D E8 mov ecx, dword ptr [ebp-18] ; 注册码
004C3543 . 8B41 F8 mov eax, dword ptr [ecx-8] ; 注册码长度,对于长度为什么是在 [ecx-8]这样的内存中,我想这是 C++ 抑或 C++ with MFC 处理的规范吧
004C3546 . 85C0 test eax, eax ; 测试是否输入了注册码
004C3548 . 0F84 7C080000 je 004C3DCA
004C354E . 6A 2D push 2D ; 2Dh=="-",像序列号中的分隔符吧
004C3550 . 8D4D E8 lea ecx, dword ptr [ebp-18]
004C3553 . E8 86982000 call <jmp.&mfc42.#CString::Find_2763> ; 看到此函数就知道用意了,就是检查输入的注册码中是否包含注册码分隔符 "-"
004C3558 . 3BC6 cmp eax, esi
004C355A . 0F84 6A080000 je 004C3DCA ; 不含分隔符 "-",就跳转到错误提示处(3处中的第3处),我没有输入 "-",这下死定了
004C3560 . 68 A4D58E00 push 008ED5A4 ; ASCII "You've entered a serial number which can only be used to activate WYSIWYG Web Builder "
004C3565 . 8D4D DC lea ecx, dword ptr [ebp-24]
004C3568 . E8 D9962000 call <jmp.&mfc42.#CString::CString_537>
004C356D . 8B55 E8 mov edx, dword ptr [ebp-18]
004C3570 . 68 84D58E00 push 008ED584 ; /s2 = "KFW9K-7J2G8-BQT4Y-V3GXV-QM4RF"
004C3575 . 52 push edx ; |s1 = 00000030 ???
004C3576 . C645 FC 04 mov byte ptr [ebp-4], 4 ; |
004C357A . FF15 0017BB00 call dword ptr [<&msvcrt._mbscmp>] ; \_mbscmp
呵呵,红色标记处的 s2 = "KFW9K-7J2G8-BQT4Y-V3GXV-QM4RF",提示注册码的长相,嗯!@#等会我重新输入注册码改为与它长相差不多的应该可以通过一部分验证吧。
接上面分析 004C355A 处实现跳转后,直接按 F9 弹出错误提示,好,我们将注册码改为:KFW9K-7J2G8-BQT4Y-V3GXV-QM4RF,然后点击“激活”按钮:
004C3580 . 83C4 08 add esp, 8
004C3583 . 85C0 test eax, eax
004C3585 . 75 2E jnz short 004C35B5
004C3587 . 8D45 DC lea eax, dword ptr [ebp-24]
004C358A . 68 80D58E00 push 008ED580
004C358F . 8D4D B4 lea ecx, dword ptr [ebp-4C]
004C3592 . 50 push eax
004C3593 . 51 push ecx
004C3594 . E8 2D9B2000 call <jmp.&mfc42.#operator+_924>
004C3599 . 8B00 mov eax, dword ptr [eax]
004C359B . 6A 00 push 0
004C359D . 6A 30 push 30
004C359F . 50 push eax
004C35A0 . C645 FC 05 mov byte ptr [ebp-4], 5
004C35A4 . E8 179B2000 call <jmp.&mfc42.#AfxMessageBox_1200>
004C35A9 . C645 FC 04 mov byte ptr [ebp-4], 4
004C35AD . 8D4D B4 lea ecx, dword ptr [ebp-4C]
004C35B0 . E9 F1000000 jmp 004C36A6
004C3585 . 75 2E jnz short 004C35B5 处的跳转未实现,提示如下图:
也就是说此注册码只能激活 3.x 版本。
向下翻看代码,为了节约空间,仅仅截了部分图:
看上图中的 ASCII"WB400-",截图的下面还有雷同的的 WB500-、WB600-、WB700-,猜想,WB 不是软件名称 Web Bulder 的每个单词的首字母吗(缩写),400、500、600、700,不就是版本号 4.x、5.x、6.x 以及 7.x 吗?也就是说,注册码的格式必须以软件名缩写 WB+软件版本开头,进一步大胆猜想,既然此次研究的是8.x版本,注册码应该以 WB800- 这样开始,OK,我们就随便构造一组吧,为了简化,我就在 3.x 注册码的基础上冠以 WB800-来构造,即等会输入:WB800-KFW9K-7J2G8-BQT4Y-V3GXV-QM4RF。直接按 F9 运行让它弹出错误信息,然后输入:WB800-KFW9K-7J2G8-BQT4Y-V3GXV-QM4RF(随意构造的一组,当然只能通过一部分验证,不可能完全正确,谁都没那手气吧,随便输入就注册成功!)
经验证 WB400-、WB500-、WB600-、WB700-,如果注册码是以这些开头的,则提示“提示此注册码只能激活 [4-7].x 版”的,因此,分析代码就不贴出了。直接进入 8.x 的验证流程如下:
004C36BC > \E8 8B952000 call <jmp.&mfc42.#AfxGetModuleState_1168>
004C36C1 . 8B58 04 mov ebx, dword ptr [eax+4]
004C36C4 . 6A 00 push 0
004C36C6 . 68 44D58E00 push 008ED544 ; ASCII "NagCount",经过分析,此注册表值项中的值表示注册时因各种原因未能注册成功的次数,最多给你3次机会
004C36CB . 68 88028E00 push 008E0288 ; ASCII "Settings"
004C36D0 . 8BCB mov ecx, ebx ; WebBuild.0092A0A0
004C36D2 . 895D C0 mov dword ptr [ebp-40], ebx ; WebBuild.0092A0A0
004C36D5 . E8 7C9A2000 call <jmp.&mfc42.#CWinApp::GetProfileIntA_3521> ; 读取 NagCount 的值返给 eax,首次安装并未注册过是其值应该为 0
004C36DA . 8BF0 mov esi, eax ; 即 eax=0
004C36DC . 46 inc esi
004C36DD . 83FE 03 cmp esi, 3
004C36E0 . 7E 58 jle short 004C373A ; 如果未跳转,说明你尝试注册不成功超过3次了;正常时应该跳转
004C36E2 . 6A FF push -1
004C36E4 . 6A 34 push 34
004C36E6 . 68 CC820000 push 82CC
004C36EB . E8 F69A2000 call <jmp.&mfc42.#AfxMessageBox_1199> ; 我们注意到你试图激活本软件遇到问题,你想访问我们的网站查阅解决方案吗?
通过 004C36E0 . 7E 58 jle short 004C373A 地址跳转到如下的代码:
004C373A > \8BCF mov ecx, edi
004C373C . E8 8F0E0000 call 004C45D0 ; 网络验证 call
004C3741 . 85C0 test eax, eax
004C3743 . 75 4A jnz short 004C378F ; 必须跳
004C3745 . 6A FF push -1
004C3747 . 6A 30 push 30
004C3749 . 68 EF730000 push 73EF
004C374E . E8 939A2000 call <jmp.&mfc42.#AfxMessageBox_1199> ; 提示网络不通
好了,我们进入网络验证 call 中看看(为了进入网络验证模块,我们在它的上一行地址处下段(F2)),就像下图那样:
然后输入构造的注册码:WB800-KFW9K-7J2G8-BQT4Y-V3GXV-QM4RF,直到停在上图中的 004C373A 处,F8 和 F7 各一次就进入网络验证 call 中。我在分析此款软件时不知道坛中 playboysen 已经发布有关于“WYSIWYG Web Build V5.5.4 算法+网络验证分析”,之后我上传了本版的注册机后通过帖子中的“相似主题”才发现,如果早发现,对于此款软件的分析本人就不需要走很多弯路了,晕死!基于节约版面和尽可能缩短帖子的长度,仅将关键部分贴出(爱好者想进一步了解本款软件的网络验证机制请查看 playboysen 的帖子或自行研究),关键部分如下:
004C4726 |. 8B45 E8 mov eax, [local.6] ; 通过网络验证,局部变量 local.6 中应该返回(ASCII "<WWB8>OK</WWB8>")
004C4729 |. 68 3CD68E00 push 008ED63C ; //s2 = "<WWB8>OK</WWB8>",为软件值预置的值
004C472E |. 50 push eax ; |s1 = 网络验证的返回值,正确的话应为:<WWB8>OK</WWB8>
004C472F |. FF15 0017BB00 call dword ptr [<&msvcrt._mbscmp>] ; \_mbscmp
004C4735 |. 83C4 08 add esp, 8
004C4738 |. C645 FC 04 mov byte ptr [ebp-4], 4
004C473C |. 85C0 test eax, eax ; 相等则 eax=0
004C473E |. 8D4D E8 lea ecx, [local.6]
004C4741 |. 75 4E jnz short 004C4791 ; 不能跳
004C4743 |. E8 D4842000 call <jmp.&mfc42.#CString::~CString_800>
004C4748 |. 8D4D C0 lea ecx, [local.16]
004C474B |. C645 FC 03 mov byte ptr [ebp-4], 3
004C474F |. E8 328D2000 call <jmp.&mfc42.#CInternetSession::~CInternetS>
004C4754 |. 8D4D DC lea ecx, [local.9]
004C4757 |. C645 FC 01 mov byte ptr [ebp-4], 1
004C475B |. E8 BC842000 call <jmp.&mfc42.#CString::~CString_800>
004C4760 |. 8D4D E4 lea ecx, [local.7]
004C4763 |. C645 FC 00 mov byte ptr [ebp-4], 0
004C4767 |. E8 B0842000 call <jmp.&mfc42.#CString::~CString_800>
004C476C |. 8D4D E0 lea ecx, [local.8]
004C476F |. C745 FC FFFFF>mov [local.1], -1
004C4776 |. E8 A1842000 call <jmp.&mfc42.#CString::~CString_800>
004C477B |. B8 01000000 mov eax, 1
004C4780 |. 8B4D F4 mov ecx, [local.3]
004C4783 |. 64:890D 00000>mov dword ptr fs:[0], ecx
004C478A |. 5F pop edi ; 0012F180
004C478B |. 5E pop esi ; 0012F180
004C478C |. 5B pop ebx ; 0012F180
004C478D |. 8BE5 mov esp, ebp
004C478F |. 5D pop ebp ; 0012F180
004C4790 |. C3 retn
并返回 eax==1,好,我们从网络验证模块返回后通过跳转来到下面的代码:
004C378F > \56 push esi
004C3790 . 68 44D58E00 push 008ED544 ; ASCII "NagCount"
004C3795 . 68 88028E00 push 008E0288 ; ASCII "Settings"
004C379A . 8BCB mov ecx, ebx ; WebBuild.0092A0A0
004C379C . E8 BB992000 call <jmp.&mfc42.#CWinApp::WriteProfileInt_6402>
004C37A1 . 8B4D E8 mov ecx, dword ptr [ebp-18]
004C37A4 . 51 push ecx
004C37A5 . 8B4F 60 mov ecx, dword ptr [edi+60]
004C37A8 . E8 030C0300 call 004F43B0
004C37AD . 8B55 E4 mov edx, dword ptr [ebp-1C]
004C37B0 . 8BCB mov ecx, ebx ; WebBuild.0092A0A0
004C37B2 . 52 push edx
004C37B3 . 68 E83A8E00 push 008E3AE8 ; ASCII "Name"
004C37B8 . 68 88028E00 push 008E0288 ; ASCII "Settings"
004C37BD . E8 A8942000 call <jmp.&mfc42.#CWinApp::WriteProfileStringA_>
004C37C2 . 8B45 E4 mov eax, dword ptr [ebp-1C] ; 输入的 Email
004C37C5 . 8B4F 60 mov ecx, dword ptr [edi+60]
004C37C8 . 50 push eax
004C37C9 . E8 A20F0300 call 004F4770 ; 将输入的 Email 复制到指针 ecx 指向的内存中
004C37CE . 8B4F 60 mov ecx, dword ptr [edi+60]
004C37D1 . E8 FA0B0300 call 004F43D0 ; 将注册信息的一部分(因完整的注册信息还有另一部分)写入到注册表中
我们进入 call 004F43D0 看看到底做了什么手脚。
004F4350 /$ 64:A1 0000000>mov eax, dword ptr fs:[0]
004F4356 |. 6A FF push -1
004F4358 |. 68 88EB7E00 push WebBuild.007EEB88
004F435D |. 50 push eax
004F435E |. 64:8925 00000>mov dword ptr fs:[0], esp
004F4365 |. 83EC 10 sub esp, 10
004F4368 |. 56 push esi
004F4369 |. 8BF1 mov esi, ecx
004F436B |. 57 push edi
004F436C |. 68 58F88E00 push WebBuild.008EF858 ; /String2 = "NotSet"
004F4371 |. 8D7E 08 lea edi, dword ptr [esi+8] ; |
004F4374 |. 57 push edi ; |String1
004F4375 |. FF15 D8A28200 call dword ptr [<&kernel32.lstrcpy>] ; \lstrcpyA
004F437B |. 8BCE mov ecx, esi
004F437D |. E8 8EFAFFFF call WebBuild.004F3E10 ; 注册码算法 call
004F4382 |. 85C0 test eax, eax ; 乱输入的注册码 eax=0
004F4384 |. 0F84 FF000000 je WebBuild.004F4489 ; 跳转不会向注册表写入注册信息
经过分析 004F437D |. E8 8EFAFFFF call WebBuild.004F3E10 处的 call 为注册码算法验证,因此,先进入此 call 看看是如何验证的:
仅贴出关键部分:
第一部分:
004F3E7E |. 8DB7 08040000 lea esi, dword ptr [edi+408] ; 输入的注册码
004F3E84 |. 6A 2D push 2D ; 很熟悉吧,2Dh="-",注册码分隔符
004F3E86 |. 53 push ebx
004F3E87 |. 8D4424 2C lea eax, dword ptr [esp+2C]
004F3E8B |. 56 push esi
004F3E8C |. 50 push eax
004F3E8D |. C64424 68 05 mov byte ptr [esp+68], 5
004F3E92 |. E8 178F1D00 call <jmp.&mfc42.#AfxExtractSubString_1140> ; 分割字符串函数,以 "-" 作为分割的依据将注册码分割
第二部分:
004F3EEF |. 56 push esi ; //String,输入的注册码
004F3EF0 |. FF15 CCA28200 call dword ptr [<&kernel32.lstrlen>] ; \lstrlenA
004F3EF6 |. 83F8 1E cmp eax, 1E ; 注册码长度23h(即10进制35)与 1Eh(即10进制的 30)
004F3EF9 |. 0F85 61030000 jnz WebBuild.004F4260 ; 不等 1Eh,则注册信息不正确
怎么不等于呢?纳闷,向下翻看发现:
搞这些名堂,为了看清楚它的真面目,把我的脖子都搞歪了,阿拉伯语序式排列(从右到左,反序),这个是不是注册码呢,刚好 30 的长度(包括分隔符),那试试吧!
(Game Over)大侠你的游戏功底不足,请重新来过吧!晕!@!#
将所谓的注册码正序排列后为:WB800-439F-V899-7R89-CDB0-4X47(如果注册码长相确实如此,则应为 5-4-4-4-4-4,即第一组长度 5位,剩余的5组长度为 4 位,加上分隔符 "-",共30位),输入此注册码继续研究(仅贴出关键的代码):
004F3EEF |. 56 push esi ; //String,输入的注册码
004F3EF0 |. FF15 CCA28200 call dword ptr [<&kernel32.lstrlen>] ; \lstrlenA
004F3EF6 |. 83F8 1E cmp eax, 1E ; 注册码长度23h(即10进制35)与 1Eh(即10进制的 30)
004F3EF9 |. 0F85 61030000 jnz WebBuild.004F4260 ; 不等 1Eh,则注册信息不正确
004F3EFF |. 8B4424 24 mov eax, dword ptr [esp+24] ; 注册码分割后的第一组 WB800
004F3F03 |. 68 50F88E00 push WebBuild.008EF850 ; /s2 = "WB800",软件预置的字符串
004F3F08 |. 50 push eax ; |s1=输入的注册码分割后的第一组注册码
004F3F09 |. FF15 00B78200 call dword ptr [<&msvcrt._mbscmp>] ; \_mbscmp
004F3F0F |. 83C4 08 add esp, 8 ; 如果相等则 eax 中应该返回 0
004F3F12 |. 85C0 test eax, eax
004F3F14 |. 0F85 46030000 jnz WebBuild.004F4260 ; 测试结果不为 0 则跳转 Over,即第一组注册码必须为 WB800
004F3F1A |. 8B4C24 1C mov ecx, dword ptr [esp+1C] ; 注册码的第4组
004F3F1E |. 3969 F8 cmp dword ptr [ecx-8], ebp ; ebp 的值恒定为 4,即本组注册码的长度与4比较
004F3F21 |. 0F85 39030000 jnz WebBuild.004F4260 ; 不等于4,则 Over
004F3F27 |. 8B5424 10 mov edx, dword ptr [esp+10] ; 注册码的第5组
004F3F2B |. 396A F8 cmp dword ptr [edx-8], ebp ; 同理
004F3F2E |. 0F85 2C030000 jnz WebBuild.004F4260 ; 不等于4,则 Over
004F3F34 |. 8B4424 20 mov eax, dword ptr [esp+20] ; 注册码的第2组
004F3F38 |. 3968 F8 cmp dword ptr [eax-8], ebp
004F3F3B |. 0F85 1F030000 jnz WebBuild.004F4260 ; 不等于4,则 Over
004F3F41 |. 8B4C24 18 mov ecx, dword ptr [esp+18] ; 注册码的第3组反序
004F3F45 |. 3969 F8 cmp dword ptr [ecx-8], ebp
004F3F48 |. 0F85 12030000 jnz WebBuild.004F4260 ; 不等于4,则 Over
004F3F4E |. 8B5424 14 mov edx, dword ptr [esp+14]
004F3F52 |. 396A F8 cmp dword ptr [edx-8], ebp ; 注册码的第6组
004F3F55 |. 0F85 05030000 jnz WebBuild.004F4260
004F3F5B |. 8D4C24 1C lea ecx, dword ptr [esp+1C]
004F3F5F |. E8 7C9B1D00 call <jmp.&mfc42.#CString::MakeReverse_4203>
004F3F64 |. 8D4C24 14 lea ecx, dword ptr [esp+14]
004F3F68 |. E8 739B1D00 call <jmp.&mfc42.#CString::MakeReverse_4203>
004F3F6D |. 8D4C24 28 lea ecx, dword ptr [esp+28]
004F3F71 |. E8 848A1D00 call <jmp.&mfc42.#CString::CString_540>
004F3F76 |. 68 30F88E00 push WebBuild.008EF830 ; ASCII "74X4-0BDC-98R7-998V-F934-008BW",软件中预置的逆序注册码
004F3F7B |. 8D4C24 2C lea ecx, dword ptr [esp+2C]
004F3F7F |. C64424 5C 06 mov byte ptr [esp+5C], 6
004F3F84 |. E8 C58A1D00 call <jmp.&mfc42.#CString::operator=_860>
004F3F89 |. 8D4C24 28 lea ecx, dword ptr [esp+28]
004F3F8D |. E8 4E9B1D00 call <jmp.&mfc42.#CString::MakeReverse_4203> ; 逆序函数
004F3F92 |. 8B4424 28 mov eax, dword ptr [esp+28] ; 软件中预置的注册码逆序后的注册码
004F3F96 |. 50 push eax ; //String2,软件中预置的注册码逆序后的注册码
004F3F97 |. 56 push esi ; ||String1,输入的注册码
004F3F98 |. FF15 74A28200 call dword ptr [<&kernel32.lstrcmp>] ; \lstrcmpA
004F3F9E |. 85C0 test eax, eax ; 相等 eax 中返回 0;测试 eax 是否为 0
004F3FA0 |. 0F84 14020000 je WebBuild.004F41BA ; 跳转则基本注册成功,有功能限制
004F3FA6 |. 56 push esi
004F3FA7 |. E8 C4FDFFFF call WebBuild.004F3D70 ; 检测输入的注册码是否为软件中预置的第2组注册码,不包括固定的第一组 WB800
004F3FAC |. 83C4 04 add esp, 4 ; 对上面的 call 不再分析贴出,有兴趣的自己研究
004F3FAF |. 85C0 test eax, eax
004F3FB1 |. 0F85 03020000 jnz WebBuild.004F41BA ; 不是继续检测是否为第3组,分割后的注册码
004F3FB7 |. 8B4C24 20 mov ecx, dword ptr [esp+20]
004F3FBB |. 8B35 00B78200 mov esi, dword ptr [<&msvcrt._mbscmp>] ; msvcrt._mbscmp
004F3FC1 |. 68 28F88E00 push WebBuild.008EF828 ; //s2 = "2A98",软件值预置的第2组注册码
004F3FC6 |. 51 push ecx ; ||s1=输入注册码的第2组
004F3FC7 |. FFD6 call esi ; \_mbscmp
004F3FC9 |. 83C4 08 add esp, 8
004F3FCC |. 85C0 test eax, eax
004F3FCE |. 75 50 jnz short WebBuild.004F4020 ; 不等 Over
004F3FD0 |. 8B5424 18 mov edx, dword ptr [esp+18]
004F3FD4 |. 68 20F88E00 push WebBuild.008EF820 ; ASCII "9R67",预置注册码的第3组反序
004F3FD9 |. 52 push edx ; 输入注册码的第3组反序
004F3FDA |. FFD6 call esi
004F3FDC |. 83C4 08 add esp, 8
004F3FDF |. 85C0 test eax, eax
004F3FE1 |. 75 3D jnz short WebBuild.004F4020 ; 不等 Over
004F3FE3 |. 8B4424 1C mov eax, dword ptr [esp+1C]
004F3FE7 |. 68 18F88E00 push WebBuild.008EF818 ; ASCII "156U",预置注册码的第4组反序
004F3FEC |. 50 push eax ; 输入注册码的第4组反序
004F3FED |. FFD6 call esi
004F3FEF |. 83C4 08 add esp, 8
004F3FF2 |. 85C0 test eax, eax
004F3FF4 |. 75 2A jnz short WebBuild.004F4020 ; 不等 Over
004F3FF6 |. 8B4C24 10 mov ecx, dword ptr [esp+10]
004F3FFA |. 68 10F88E00 push WebBuild.008EF810 ; ASCII "5C17",预置注册码的第5组
004F3FFF |. 51 push ecx ; 输入注册码的第5组
004F4000 |. FFD6 call esi
004F4002 |. 83C4 08 add esp, 8
004F4005 |. 85C0 test eax, eax
004F4007 |. 75 17 jnz short WebBuild.004F4020 ; 不等 Over
004F4009 |. 8B5424 14 mov edx, dword ptr [esp+14]
004F400D |. 68 08F88E00 push WebBuild.008EF808 ; ASCII "352U",预置注册码的第6组反序
004F4012 |. 52 push edx ; 输入注册码的第6组反序
004F4013 |. FFD6 call esi
004F4015 |. 83C4 08 add esp, 8
004F4018 |. 85C0 test eax, eax
004F401A |. 0F84 9A010000 je WebBuild.004F41BA ; 跳转说明相等则正确,否则不等于预置注册码的任何一组则进入真正注册码的验证流程
至到,注册码的验证的一小部分完成
总结:8.x 注册码包括分隔符在内总共为 30 个字符的长度,第一组注册码必须为: WB800,其余5组每组长度为 4,注册码形如:WB800-XXXX-XXXX-XXXX-XXXX-XXXX,验证时对整个注册码或其中的某些组进行反序显示比较干扰破解者分析(不过这不会增强难度)。
特别说明:当时我以为这样就注册成功了,确实在启动时不再显示注册对话框,关于对话框中显示为“已注册”,后来在软件中实际操作时,比如新建网页或对某些对象进行复制-粘贴时(这取决于你所使用的预置3组注册码中的哪一组,不过都有功能限制),发现在每个网页中嵌入令人厌烦的 徽标,后来本地化时发现官方有提示,诸如“如果你不想在你的网页的每个页面中嵌入此徽标,请注册购买 log free……”。当时我也纳闷和怀疑,一个如此专业的软件不至于只有3组注册码,因此才进一步研究发现真正的注册码验证流程。
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!