首页
社区
课程
招聘
[原创]WYSIWYG Web Builder v8.0.3 注册保护机制分析
发表于: 2012-1-17 20:18 10403

[原创]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组注册码,因此才进一步研究发现真正的注册码验证流程。


[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

上传的附件:
收藏
免费 6
支持
分享
最新回复 (7)
雪    币: 63
活跃值: (76)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
2
接上面的分析,先自己随意构造一组,比如:WB800-2EF3-V478-7Q65-B1AE-36R7,来到这里:
004F4020 |> \8B4424 1C mov eax, dword ptr [esp+1C] ; 堆栈:(ASCII "56Q7"),输入的注册码中第4组的反序
004F4024 |. 8BCF mov ecx, edi
004F4026 |. 50 push eax
004F4027 |. E8 64FBFFFF call WebBuild.004F3B90 ;转换此组注册码
我们进入此 call 看看:
004F3BDC |. 3BFE cmp edi, esi ; edi=4 为此组注册码的长度,esi 通过 xor esi,esi 初始化为 0
004F3BDE |. C64424 24 02 mov byte ptr [esp+24], 2
004F3BE3 |. 7E 26 jle short WebBuild.004F3C0B ; 跳转则说明此组注册码为空
004F3BE5 |> 8B5424 2C /mov edx, dword ptr [esp+2C] ; 注册码
004F3BE9 |. 8A0416 |mov al, byte ptr [esi+edx] ; 逐位读取注册码
004F3BEC |. 0FBEC8 |movsx ecx, al
004F3BEF |. 83F9 39 |cmp ecx, 39 ; 与 39h(即 '9')比较
004F3BF2 |. 7F 59 |jg short WebBuild.004F3C4D ; 如果此组注册码中含有非数字,则跳转继续验证
004F3BF4 |. 884424 14 |mov byte ptr [esp+14], al ; 如果读完都为实现跳转就 Over,即此组注册码全是数字
004F3BF8 |. 8B4424 14 |mov eax, dword ptr [esp+14]
004F3BFC |. 50 |push eax
004F3BFD |. 8D4C24 14 |lea ecx, dword ptr [esp+14]
004F3C01 |. E8 DA921D00 |call <jmp.&mfc42.#CString::operator+=_940>
004F3C06 |. 46 |inc esi
004F3C07 |. 3BF7 |cmp esi, edi
004F3C09 |.^ 7C DA \jl short WebBuild.004F3BE5 ; 未读取完此组注册码则返回继续读取下一位

004F3C4D |> \3BF7 cmp esi, edi ; esi 的值表示此组注册码非数字字符的位置索引
004F3C4F |. 7F 31 jg short WebBuild.004F3C82
…………………………………………………………省略不重要的代码
004F3CB5 |. 8B4C24 2C mov ecx, dword ptr [esp+2C] ; 此组注册码
004F3CB9 |. 8D4424 14 lea eax, dword ptr [esp+14]
004F3CBD |. C64424 24 05 mov byte ptr [esp+24], 5
004F3CC2 |. 0FBE140E movsx edx, byte ptr [esi+ecx] ; 读取词组注册码中的非数字
004F3CC6 |. 83EA 50 sub edx, 50 ; 非数字-50h('P')
004F3CC9 |. 52 push edx
004F3CCA |. 68 38DD8D00 push WebBuild.008DDD38 ; ASCII "%d"
004F3CCF |. 50 push eax
004F3CD0 |. E8 5B8D1D00 call <jmp.&mfc42.#CString::Format_2818> ; 将相减后的结果转换为 10 进制输出
004F3CD5 |. 83C4 0C add esp, 0C
004F3CD8 |. 8D4C24 14 lea ecx, dword ptr [esp+14]
004F3CDC |. 51 push ecx
004F3CDD |. 8D4C24 0C lea ecx, dword ptr [esp+C]
004F3CE1 |. E8 B6901D00 call <jmp.&mfc42.#CString::operator+=_939>
004F3CE6 |. 8D5424 0C lea edx, dword ptr [esp+C]
004F3CEA |. 8D4C24 08 lea ecx, dword ptr [esp+8]
004F3CEE |. 52 push edx
004F3CEF |. E8 A8901D00 call <jmp.&mfc42.#CString::operator+=_939>
004F3CF4 |. 8B4424 08 mov eax, dword ptr [esp+8] ; 用 10 进制数替换原来非数字的位置的字符
004F3CF8 |. 50 push eax ; /s
004F3CF9 |. FF15 80B78200 call dword ptr [<&msvcrt.atol>] ; \\atol,将替换后的结果转换为 16 进制
004F3CFF |. 83C4 04 add esp, 4 ; eax 中返回十六进制值

最后通过 eax 放回转换后的 16 进制值

总结:此 call 的作用是检查待验证那组的注册码是否全是数字,如果是 Over;如果不是则检测非数字字符是否>=50h('P'),小于则 Over,大于等于则,则将注册码中的非数字字符的 ASCII 码-50h('P')后的 10 进制值替换原始位置的值,因此,如果注册码中含有非数字字符,只能为:[P-Y],-50h后刚好为:[0-9],不能为 Z,因为'Z'-50h后为0Ah,10 进制数为10,成了2位数。最后返回转换后所得的 16 进制值。返回继续看后续的验证,下面这段验证较长,请耐心:
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 ; 跳转说明相等则正确,否则不等于预置注册码的任何一组则进入真正注册码的验证流程
004F4020 |> 8B4424 1C mov eax, dword ptr [esp+1C] ; 堆栈:(ASCII "56Q7"),输入的注册码中第4组的反序
004F4024 |. 8BCF mov ecx, edi
004F4026 |. 50 push eax
004F4027 |. E8 64FBFFFF call WebBuild.004F3B90
004F402C |. 6A 43 push 43 ; 43h=='C'
004F402E |. 6A 58 push 58 ; 58h=='X'
004F4030 |. 8D4C24 18 lea ecx, dword ptr [esp+18]
004F4034 |. 894424 40 mov dword ptr [esp+40], eax
004F4038 |. E8 37911D00 call <jmp.&mfc42.#CString::Replace_6876> ; 如果含有字符 X,则替换为 C
004F403D |. 6A 44 push 44 ; 44h=='D'
004F403F |. 6A 59 push 59 ; 59h=='Y'
004F4041 |. 8D4C24 18 lea ecx, dword ptr [esp+18]
004F4045 |. E8 2A911D00 call <jmp.&mfc42.#CString::Replace_6876> ; 同理
004F404A |. 6A 45 push 45 ; 45h=='E'
004F404C |. 6A 5A push 5A ; 5Ah=='Z'
004F404E |. 8D4C24 18 lea ecx, dword ptr [esp+18]
004F4052 |. E8 1D911D00 call <jmp.&mfc42.#CString::Replace_6876> ; 同理
004F4057 |. 8B5424 10 mov edx, dword ptr [esp+10] ; 输入注册码的第5组
004F405B |. 8B35 CCB68200 mov esi, dword ptr [<&msvcrt.strtol>] ; msvcrt.strtol
004F4061 |. 8D4C24 30 lea ecx, dword ptr [esp+30]
004F4065 |. 6A 10 push 10 ; /radix = 10 (16.)
004F4067 |. 51 push ecx ; |endptr
004F4068 |. 52 push edx ; |s
004F4069 |. FFD6 call esi ; \strtol
004F406B |. 83C4 0C add esp, 0C ; 将输入的注册码的第5组字符串转换为无符号的整数
004F406E |. 8D4C24 2C lea ecx, dword ptr [esp+2C]
004F4072 |. 8BE8 mov ebp, eax ; 然后将转换后的第5组注册码暂存到 ebp 中,等会比较时会用到
004F4074 |. E8 81891D00 call <jmp.&mfc42.#CString::CString_540>
004F4079 |. 8D4424 1C lea eax, dword ptr [esp+1C]
004F407D |. 8D4C24 2C lea ecx, dword ptr [esp+2C]
004F4081 |. 50 push eax
004F4082 |. C64424 5C 07 mov byte ptr [esp+5C], 7
004F4087 |. E8 108D1D00 call <jmp.&mfc42.#CString::operator+=_939>
004F408C |. 8D4C24 20 lea ecx, dword ptr [esp+20]
004F4090 |. 51 push ecx
004F4091 |. 8D4C24 30 lea ecx, dword ptr [esp+30]
004F4095 |. E8 028D1D00 call <jmp.&mfc42.#CString::operator+=_939>
004F409A |. 8D5424 18 lea edx, dword ptr [esp+18]
004F409E |. 8D4C24 2C lea ecx, dword ptr [esp+2C]
004F40A2 |. 52 push edx
004F40A3 |. E8 F48C1D00 call <jmp.&mfc42.#CString::operator+=_939>
004F40A8 |. 8D4424 14 lea eax, dword ptr [esp+14]
004F40AC |. 8D4C24 2C lea ecx, dword ptr [esp+2C]
004F40B0 |. 50 push eax
004F40B1 |. E8 E68C1D00 call <jmp.&mfc42.#CString::operator+=_939>
004F40B6 |. 68 21100000 push 1021 ; 从这里开始计算真正注册码的第5组
004F40BB |. 8D4C24 44 lea ecx, dword ptr [esp+44]
004F40BF |. E8 1C49F3FF call WebBuild.004289E0 ; 将输入的第4组反序+第2组+第3组反序+第6组反序连接为一个新字符串
004F40C4 |. 8B4424 2C mov eax, dword ptr [esp+2C] ; 堆栈 ss:[0012E7A4]=0034E930, (ASCII "56Q72EF3874V7R63")
004F40C8 |. 68 5C110000 push 115C
004F40CD |. C64424 5C 08 mov byte ptr [esp+5C], 8
004F40D2 |. 8B48 F8 mov ecx, dword ptr [eax-8] ; 10h=16,新字符串的长度
004F40D5 |. 51 push ecx
004F40D6 |. 50 push eax
004F40D7 |. 8D4C24 4C lea ecx, dword ptr [esp+4C]
004F40DB |. E8 A049F3FF call WebBuild.00428A80 ; 调用完 call 后计算出真正的第5组注册码
004F40E0 |. 8B5424 20 mov edx, dword ptr [esp+20] ; 输入的第2组注册码
004F40E4 |. 8D4C24 30 lea ecx, dword ptr [esp+30]
004F40E8 |. 6A 10 push 10
004F40EA |. 51 push ecx
004F40EB |. 52 push edx
004F40EC |. 894424 40 mov dword ptr [esp+40], eax
004F40F0 |. FFD6 call esi ; 将输入的第2组注册码字符串转换为无符号的整数
004F40F2 |. 83C4 0C add esp, 0C
004F40F5 |. 894424 3C mov dword ptr [esp+3C], eax
004F40F9 |. 8B4424 18 mov eax, dword ptr [esp+18] ; 数的的第3组注册码反序
004F40FD |. 8BCF mov ecx, edi
004F40FF |. 50 push eax
004F4100 |. E8 8BFAFFFF call WebBuild.004F3B90 ; eax=222A,上面调用过,就是将非数字转换为10进制后替换原始位置并以16进制输出
004F4105 |. 8B4C24 14 mov ecx, dword ptr [esp+14] ; 输入的第6组注册码反序
004F4109 |. 8BF0 mov esi, eax
004F410B |. 51 push ecx
004F410C |. 8BCF mov ecx, edi
004F410E |. E8 7DFAFFFF call WebBuild.004F3B90 ; 同上面的 call 功能,eax=1C5F
004F4113 |. 3B6C24 34 cmp ebp, dword ptr [esp+34] ; ebp 为输入的第5组注册码,[esp+34] 为计算后的真正注册码
004F4117 |. 0F85 19010000 jnz WebBuild.004F4236 ; 不等则 Over
004F411D |. 8B4C24 38 mov ecx, dword ptr [esp+38] ; 输入的第4组注册码反序计算并转换无符号整数的值
004F4121 |. 3BF1 cmp esi, ecx ; esi 是第3组注册码反序转换后的值
004F4123 |. 0F8E 0D010000 jle WebBuild.004F4236 ; 跳转则 Over,即转换后的第3组注册码必须大于第4组反序
004F4129 |. 3BF0 cmp esi, eax ; eax 的值表示第6组反序转后的值
004F412B |. 0F8E 05010000 jle WebBuild.004F4236 ; 同上理,即转换后的第3组注册码必须大于第6组反序
004F4131 |. 8B7C24 3C mov edi, dword ptr [esp+3C] ; 输入的第2组注册码
004F4135 |. 8D3408 lea esi, dword ptr [eax+ecx] ; lea 的技巧用法,相当于 add eax,ecx,mov esi,eax
004F4138 |. 3BF7 cmp esi, edi ; edi 为真正计算后的第2组,即必须满足:第2组==转换后的第4组+转换后第6组
004F413A |. 0F85 F6000000 jnz WebBuild.004F4236
004F4140 |. E8 C7881D00 call <jmp.&mfc42.#AfxGetModuleState_1168>
004F4145 |. 8B40 04 mov eax, dword ptr [eax+4]
004F4148 |. 3BF7 cmp esi, edi ; 再次比较
004F414A |. C780 60010000>mov dword ptr [eax+160], 1
004F4154 |. 0F85 DC000000 jnz WebBuild.004F4236 ; 不能跳
004F415A |. E8 AD881D00 call <jmp.&mfc42.#AfxGetModuleState_1168>
004F415F |. 8B40 04 mov eax, dword ptr [eax+4]
004F4162 |. 8998 64010000 mov dword ptr [eax+164], ebx
004F4168 |. E8 9F881D00 call <jmp.&mfc42.#AfxGetModuleState_1168>
004F416D |. 8B40 04 mov eax, dword ptr [eax+4]
004F4170 |. 8998 64010000 mov dword ptr [eax+164], ebx
004F4176 |. E8 91881D00 call <jmp.&mfc42.#AfxGetModuleState_1168>
004F417B |. 8B40 04 mov eax, dword ptr [eax+4]
004F417E |. 3BF7 cmp esi, edi ; 有一次比较
004F4180 |. 8998 64010000 mov dword ptr [eax+164], ebx
004F4186 |. 0F85 AA000000 jnz WebBuild.004F4236 ; 不能跳

…………………………………………………………………………………
004F4221 |. B8 01000000 mov eax, 1 ; 如果都正确则 eax 返回 1,表示注册码是正确的
004F4226 |. 5B pop ebx
004F4227 |. 8B4C24 40 mov ecx, dword ptr [esp+40]
004F422B |. 64:890D 00000>mov dword ptr fs:[0], ecx
004F4232 |. 83C4 4C add esp, 4C
004F4235 |. C3 retn

总结:必须满足如下的条件:
1、注册码总共为 6 组,每组间用'-'符号分隔;
2、8.x 版本的第一组必须为:WB800,其余5组每组都必须为 4 位;
3、第3、4、6 组的转换规则:
(1)、这3组注册码不能全为数字,必须有1个 [P -Y]范围内的任意字母;
(2)、注册码有效的字符范围:[0-9]以及[P -Y],必须是大写字母;
(3)、先进行反序生成第一次变换的值,为方便叙述,表示为:Transf1;
(4)、Transf1 中如果含有英文字母,则将英文字母的ASCII码-50h('P')后的10
进制值替换原始位置的字符(因此
得到的范围为:[0-9]),'Z' 不能使用,因为'Z'=10(0Ah),替换时以10进制值替
换,10 为2为,超过本为的位数1;记为 Transf2;
(5)、将 Transf2 看作10进制数,然后计算出10进制数对应的16进制数,即
strtol(Transf2),记为:Transf3;
为进一步便于叙述,将某组的 Transf3,记为:Groupx[Transf3],x 为组的序数

4、各组之间应满足:
(1)、Group3[Transf3]>Group4[Transf3];
(2)、Group3[Transf3]>Group6[Transf3];
(3)、Group2=Group4[Transf3]+Group6[Transf3];
(4)、Group4[Transf1]+Group2+Group3[Transf1]+Group6[Transf1]('+' 表示
连接字串,而不是数学运算的 + 号),即:第4组反序+第2组+第3组反序+第6
组反序连接后生成一个计算第5组注册码用到的参数,然后通过 004F33EB |. E8
5052F3FF call 00428640 后生成 Group5(返回到 eax 中的 ax 值作为文本
输出就是第5组的注册码)。

如果输入的注册码完全符合上述要求,则注册成功,并向注册表中写入注册信息,2个地方:
1、HKEY_CURRENT_USER\Software\Netscape\Netscape Navigator\Prefs 下新建字符串类型的值项名 application/www,其值就是明文的后5组注册码;
2、HKEY_CURRENT_USER\Software\Microsoft\GDIPlus\CacheSize 下新建形如:{BQ1PBQ2C-1P6P-2P0C-6C} 的字符串类型的值项名,其值形如:f13279a36447ba11b078053dbf68511f。
另外:在 HKEY_CURRENT_USER\Software\Pablo Software Solutions\WYSIWYG Web Builder 8\Settings 下新建 Name 值项名,其值为你注册时输入的注册 Email

最后构造出一组满足算法的注册码:WB800-1EE0-V389-Q765-A1BF-33R2

本以为分析到这里万事大吉了,没想到成功实际使用时不知什么时候(不定时)将我正在编辑的页面无情地关闭了,再次启动时要求我输入注册信息成为未注册版。经过分析,是官方通过网络验证获知是非法注册用户,将注册信息清除了。注册验证地址:http://extensions.wysiwygwebbuilder.com/system/actions/wwb8.checkin.php(检测是否为正版用户)
2012-1-17 20:18
0
雪    币: 63
活跃值: (76)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
3
那好我就将验证地址重定向到本机应该没问题了吧!晕死,不知又是什么时候提示 "MAJOR PROBLEM DETECTED!,Please clsoe your project and download the latest version from the website.",又把注册信息给清除了。那既然这样,再看看到底软件是如何检测的(直接的有2处调用):

004F37CA  |.  50            push    eax                                       ; /String
004F37CB  |.  C68424 BC0400>mov     byte ptr [esp+4BC], 5                     ; |
004F37D3  |.  8BF0          mov     esi, eax                                  ; |
004F37D5  |.  FF15 CCA28200 call    dword ptr [<&kernel32.lstrlen>]           ; \lstrlenA
004F37DB  |.  50            push    eax
004F37DC  |.  56            push    esi
004F37DD  |.  8D4C24 3C     lea     ecx, dword ptr [esp+3C]
004F37E1  |.  E8 3A54F3FF   call    WebBuild.00428C20
004F37E6  |.  8B5424 24     mov     edx, dword ptr [esp+24]
004F37EA  |.  33C9          xor     ecx, ecx
004F37EC  |.  85D2          test    edx, edx
004F37EE  |.  7E 14         jle     short WebBuild.004F3804
004F37F0  |.  8B7424 20     mov     esi, dword ptr [esp+20]
004F37F4  |>  3906          /cmp     dword ptr [esi], eax                     ;  经分析 eax 的值为注册码经 CRC32 生成一个固定的 dd 值,esi 指向软件中的一个数据表
004F37F6  |.  0F84 95000000 |je      WebBuild.004F3891
004F37FC  |.  41            |inc     ecx
004F37FD  |.  83C6 04       |add     esi, 4
004F3800  |.  3BCA          |cmp     ecx, edx
004F3802  |.^ 7C F0         \jl      short WebBuild.004F37F4
004F3804  |>  8D4C24 30     lea     ecx, dword ptr [esp+30]
004F3808  |.  C68424 B40400>mov     byte ptr [esp+4B4], 4
004F3810  |.  E8 7BB7F6FF   call    WebBuild.0045EF90
004F3815  |.  8D4C24 1C     lea     ecx, dword ptr [esp+1C]
004F3819  |.  C68424 B40400>mov     byte ptr [esp+4B4], 3
004F3821  |.  E8 FCA21D00   call    <jmp.&mfc42.#CDWordArray::~CDWordArray_65>
004F3826  |.  8D4C24 68     lea     ecx, dword ptr [esp+68]
004F382A  |.  C68424 B40400>mov     byte ptr [esp+4B4], 2
004F3832  |.  E8 2B961D00   call    <jmp.&mfc42.#CArchive::~CArchive_603>
004F3837  |.  8D4C24 40     lea     ecx, dword ptr [esp+40]
004F383B  |.  C68424 B40400>mov     byte ptr [esp+4B4], 1
004F3843  |.  E8 3E9F1D00   call    <jmp.&mfc42.#CMemFile::~CMemFile_703>
004F3848  |>  8D4C24 0C     lea     ecx, dword ptr [esp+C]
004F384C  |.  C68424 B40400>mov     byte ptr [esp+4B4], 0
004F3854  |.  E8 83911D00   call    <jmp.&mfc42.#CString::~CString_800>
004F3859  |.  6A 00         push    0
004F385B  |.  6A 10         push    10
004F385D  |.  68 34F78E00   push    WebBuild.008EF734                         ;  ASCII "MAJOR PROBLEM DETECTED!",CR,LF,"Please clsoe your project and download the latest version from the website."

也就是说注册码除了满足上述的要求外,经过 CRC32 计算后的 dd 值必须与 esi 指向的数据中其中的一个 dd 值相等。

继续看看 esi 到底指向一个什么样的数据表,向上翻看追溯发现:

004F3728  |.  8B78 08       mov     edi, dword ptr [eax+8]
004F372B  |.  68 BC748E00   push    WebBuild.008E74BC                         ; /ResourceType = "GIF"
004F3730  |.  68 9CF78E00   push    WebBuild.008EF79C                         ; |ResourceName = "wwb8.gif"
004F3735  |.  57            push    edi                                       ; |hModule
004F3736  |.  FF15 A0A28200 call    dword ptr [<&kernel32.FindResourceA>]     ; \FindResourceA
004F373C  |.  8BE8          mov     ebp, eax
004F373E  |.  55            push    ebp                                       ; /hResource
004F373F  |.  57            push    edi                                       ; |hModule
004F3740  |.  FF15 88A28200 call    dword ptr [<&kernel32.LoadResource>]      ; \LoadResource
004F3746  |.  55            push    ebp                                       ; /hResource
004F3747  |.  57            push    edi                                       ; |hModule
004F3748  |.  8BF0          mov     esi, eax                                  ; |
004F374A  |.  FF15 8CA28200 call    dword ptr [<&kernel32.SizeofResource>]    ; \SizeofResource
004F3750  |.  83CD FF       or      ebp, FFFFFFFF
004F3753  |.  8BF8          mov     edi, eax
004F3755  |.  85F6          test    esi, esi
004F3757  |.  0F84 EB000000 je      WebBuild.004F3848
004F375D  |.  56            push    esi                                       ; /hResource
004F375E  |.  FF15 90A28200 call    dword ptr [<&kernel32.LockResource>]      ; \LockResource

看看上面的 FindResource 函数的2个参数,/ResourceType = "GIF" ,ResourceName = "wwb8.gif",看起来“像”是个图片,一般图片无需如此调用或没那调用方式吧(编程非常少,到底是否可以这样调用),差点又被忽悠了。
用 PE Explorer 打开主程序,找到资源 WWB8.GIF,哪里是图片(坟地里撒花椒面,麻鬼!)。

到这里,真正想写出注册机,如果仅仅是满足第一个条件简单,又要满足此条件到此是否存在逆算法到现在没找到招,我再坛中的“密码学”中发布了求助,坛友发帖说可以采用穷举等,因此,干脆把本软的保护机制分析发布出来,此部分请解密高手看看是否有办法解决同时满足2个条件的算法如何写注册机代码。
2012-1-17 20:19
0
雪    币: 517
活跃值: (35)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
4
可能你分析得不错,但代码部分确实太糟糕了。
2012-1-17 20:30
0
雪    币: 517
活跃值: (35)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
5
简单地整理了一下:

2、进入验证函数初步分析验证机制
然后重新载入(用 Ctrl+F2)被调试的程序,按 F9 运行,弹出注册界面是依然输入试炼码:注册 Email:net0520@163.com,序列号:cracker0520,点击“激活”按钮,断在刚才用 F2 下断的位置,即上图中的 004C3450 处,看下面的分析:
004C3450  PUSH EBP
004C3451  MOV EBP, ESP
004C3453  PUSH -1
004C3455  PUSH 007EAB10 ; SE 处理程序安装
004C345A  MOV EAX, DWORD PTR FS:[0]
004C3460  PUSH EAX
004C3461  MOV DWORD PTR FS:[0], ESP
004C3468  SUB ESP, 70
004C346B  PUSH EBX
004C346C  PUSH ESI
004C346D  PUSH EDI
004C346E  MOV EDI, ECX
004C3470  MOV DWORD PTR [EBP-10], ESP
004C3473  PUSH 1
004C3475  MOV DWORD PTR [EBP-4C], EDI
004C3478  CALL <JMP.&mfc42.#CWnd::UpdateData_6334> ; 更新对话框数据吧
004C347D  PUSH 0                           ; /timer = NULL
004C347F  CALL DWORD PTR [<&msvcrt.time>]  ; \time
004C3485  PUSH EAX                         ; /seed = FFFFFFFF (-1.)
004C3486  CALL DWORD PTR [<&msvcrt.srand>] ; \srand
004C348C  ADD ESP, 8
004C348F  CALL DWORD PTR [<&msvcrt.rand>]  ; [rand
004C3495  LEA EAX, DWORD PTR [EBP-20]      ; 上面被调用的3个函数是典型的通过时间作为随机种子产生随机数的方法
004C3498  MOV ECX, EDI
004C349A  PUSH EAX                         ; /Arg1 = FFFFFFFF
004C349B  CALL 004C3F40                    ; \WebBuild.004C3F40
004C34A0  LEA ECX, DWORD PTR [EBP-28]
004C34A3  MOV DWORD PTR [EBP-4], 0
004C34AA  CALL <JMP.&mfc42.#CString::CString_540>
004C34AF  LEA ECX, DWORD PTR [EBP-1C]
004C34B2  MOV BYTE PTR [EBP-4], 1
004C34B6  CALL <JMP.&mfc42.#CString::CString_540>
004C34BB  LEA ECX, DWORD PTR [EBP-18]
004C34BE  MOV BYTE PTR [EBP-4], 2
004C34C2  CALL <JMP.&mfc42.#CString::CString_540>
004C34C7  LEA ECX, DWORD PTR [EBP-1C]
004C34CA  MOV BYTE PTR [EBP-4], 3
004C34CE  PUSH ECX
004C34CF  LEA ECX, DWORD PTR [EDI+1E8]
004C34D5  CALL <JMP.&mfc42.#CWnd::GetWindowTextA_3874> ; 获取输入的 Email 文本
004C34DA  LEA EDX, DWORD PTR [EBP-18]
004C34DD  LEA ECX, DWORD PTR [EDI+1A8]
004C34E3  PUSH EDX
004C34E4  CALL <JMP.&mfc42.#CWnd::GetWindowTextA_3874> ; 获取输入的注册码
004C34E9  LEA ECX, DWORD PTR [EBP-1C]
004C34EC  CALL <JMP.&mfc42.#CString::TrimLeft_6282> ; 删除用户输入的注册信息的左边的空格(如果有),可能是这个作用吧
004C34F1  LEA ECX, DWORD PTR [EBP-1C]
004C34F4  CALL <JMP.&mfc42.#CString::TrimRight_6283> ; 删除用户输入的注册信息的右边的空格(如果有),可能是这个作用吧
004C34F9  PUSH 2E
004C34FB  LEA ECX, DWORD PTR [EBP-1C]
004C34FE  CALL <JMP.&mfc42.#CString::TrimRight_6929> ; 同理
004C3503  LEA ECX, DWORD PTR [EBP-18]
004C3506  CALL <JMP.&mfc42.#CString::TrimLeft_6282> ; 同理
004C350B  LEA ECX, DWORD PTR [EBP-18]
004C350E  CALL <JMP.&mfc42.#CString::TrimRight_6283> ; 同理
004C3513  PUSH 2E
004C3515  LEA ECX, DWORD PTR [EBP-18]
004C3518  CALL <JMP.&mfc42.#CString::TrimRight_6929> ; 同理
004C351D  MOV EAX, DWORD PTR [EBP-1C]                ; 输入的 Email
004C3520  MOV ECX, DWORD PTR [EAX-8]                 ; Email 长度
004C3523  TEST ECX, ECX                              ; 测试输入输入了 Email
004C3525  JE 004C3DC7                                ; 如果未输入则跳转到出错信息提示处,刚开始分析时调用错误信息的第一处流程
004C352B  PUSH 40                                    ; 40h=='@',像什么,合法的 Email 中就必须含有此字符吧
004C352D  LEA ECX, DWORD PTR [EBP-1C]
004C3530  CALL <JMP.&mfc42.#CString::Find_2763>      ; 顾名思义,知道此函数检查输入的 Email 中是否含有“@”字符
004C3535  OR ESI, FFFFFFFF                           ; 如果含有此字符,则在 EAX 中返回 "@" 字符所在的位置索引,否则返回 0FFFFFFFFh
004C3538  CMP EAX, ESI                               ; EAX==ESI(即 0FFFFFFFFh)?,官方验证 Email 的合法性仅仅验证是否包含 "@" 字符
004C353A  JE 004C3DCA                                ; 跳转则说明输入的 Email 中无"@"字符,当然不是合法的 Email 格式,跳转到错误信息处理处(3处中的第2处)
004C3540  MOV ECX, DWORD PTR [EBP-18]                ; 注册码
004C3543  MOV EAX, DWORD PTR [ECX-8]                 ; 注册码长度,对于长度为什么是在 [ECX-8]这样的内存中,我想这是 C++ 抑或 C++ with MFC 处理的规范吧
004C3546  TEST EAX, EAX                              ; 测试是否输入了注册码
004C3548  JE 004C3DCA
004C354E  PUSH 2D                                    ; 2Dh=="-",像序列号中的分隔符吧
004C3550  LEA ECX, DWORD PTR [EBP-18]
004C3553  CALL <JMP.&mfc42.#CString::Find_2763>      ; 看到此函数就知道用意了,就是检查输入的注册码中是否包含注册码分隔符 "-"
004C3558  CMP EAX, ESI
004C355A  JE 004C3DCA                                ; 不含分隔符 "-",就跳转到错误提示处(3处中的第3处),我没有输入 "-",这下死定了
004C3560  PUSH 008ED5A4                              ; ASCII "You've entered a serial number which can only be used to activate WYSIWYG Web Builder "
004C3565  LEA ECX, DWORD PTR [EBP-24]
004C3568  CALL <JMP.&mfc42.#CString::CString_537>
004C356D  MOV EDX, DWORD PTR [EBP-18]
004C3570  PUSH 008ED584                              ; /s2 = "KFW9K-7J2G8-BQT4Y-V3GXV-QM4RF"
004C3575  PUSH EDX                                   ; |s1 = 00000030 ???
004C3576  MOV BYTE PTR [EBP-4], 4                    ; |
004C357A  CALL DWORD PTR [<&msvcrt._mbsCMP>]         ; \_mbsCMP

呵呵,红色标记处的 s2 = "KFW9K-7J2G8-BQT4Y-V3GXV-QM4RF",提示注册码的长相,嗯!@#等会我重新输入注册码改为与它长相差不多的应该可以通过一部分验证吧。
接上面分析 004C355A 处实现跳转后,直接按 F9 弹出错误提示,好,我们将注册码改为:KFW9K-7J2G8-BQT4Y-V3GXV-QM4RF,然后点击“激活”按钮:
004C3580  ADD ESP, 8
004C3583  TEST EAX, EAX
004C3585  JNZ SHORT 004C35B5
004C3587  LEA EAX, DWORD PTR [EBP-24]
004C358A  PUSH 008ED580
004C358F  LEA ECX, DWORD PTR [EBP-4C]
004C3592  PUSH EAX
004C3593  PUSH ECX
004C3594  CALL <JMP.&mfc42.#operatOR+_924>
004C3599  MOV EAX, DWORD PTR [EAX]
004C359B  PUSH 0
004C359D  PUSH 30
004C359F  PUSH EAX
004C35A0  MOV BYTE PTR [EBP-4], 5
004C35A4  CALL <JMP.&mfc42.#AfxMessageBox_1200>
004C35A9  MOV BYTE PTR [EBP-4], 4
004C35AD  LEA ECX, DWORD PTR [EBP-4C]
004C35B0  JMP 004C36A6

004C3585  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  CALL <JMP.&mfc42.#AfxGetModuleState_1168>
004C36C1  MOV EBX, DWORD PTR [EAX+4]
004C36C4  PUSH 0
004C36C6  PUSH 008ED544                                   ; ASCII "NagCount",经过分析,此注册表值项中的值表示注册时因各种原因未能注册成功的次数,最多给你3次机会
004C36CB  PUSH 008E0288                                   ; ASCII "Settings"
004C36D0  MOV ECX, EBX                                    ; WebBuild.0092A0A0
004C36D2  MOV DWORD PTR [EBP-40], EBX                     ; WebBuild.0092A0A0
004C36D5  CALL <JMP.&mfc42.#CWinApp::GetProfileIntA_3521> ; 读取 NagCount 的值返给 EAX,首次安装并未注册过是其值应该为 0
004C36DA  MOV ESI, EAX ; 即 EAX=0
004C36DC  INC ESI
004C36DD  CMP ESI, 3
004C36E0  JLE SHORT 004C373A                              ; 如果未跳转,说明你尝试注册不成功超过3次了;正常时应该跳转
004C36E2  PUSH -1
004C36E4  PUSH 34
004C36E6  PUSH 82CC
004C36EB  CALL <JMP.&mfc42.#AfxMessageBox_1199>           ; 我们注意到你试图激活本软件遇到问题,你想访问我们的网站查阅解决方案吗?

通过 004C36E0  JLE SHORT 004C373A 地址跳转到如下的代码:
004C373A  MOV ECX, EDI
004C373C  CALL 004C45D0       ; 网络验证 CALL
004C3741  TEST EAX, EAX
004C3743  JNZ SHORT 004C378F  ; 必须跳
004C3745  PUSH -1
004C3747  PUSH 30
004C3749  PUSH 73EF
004C374E  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  MOV EAX, [local.6]                 ; 通过网络验证,局部变量 local.6 中应该返回(ASCII "<WWB8>OK</WWB8>")
004C4729  PUSH 008ED63C                      ; //s2 = "<WWB8>OK</WWB8>",为软件值预置的值
004C472E  PUSH EAX                           ; |s1 = 网络验证的返回值,正确的话应为:<WWB8>OK</WWB8>
004C472F  CALL DWORD PTR [<&msvcrt._mbsCMP>] ; \_mbsCMP
004C4735  ADD ESP, 8
004C4738  MOV BYTE PTR [EBP-4], 4
004C473C  TEST EAX, EAX                      ; 相等则 EAX=0
004C473E  LEA ECX, [local.6]
004C4741  JNZ SHORT 004C4791                 ; 不能跳
004C4743  CALL <JMP.&mfc42.#CString::~CString_800>
004C4748  LEA ECX, [local.16]
004C474B  MOV BYTE PTR [EBP-4], 3
004C474F  CALL <JMP.&mfc42.#CInternetSession::~CInternetS>
004C4754  LEA ECX, [local.9]
004C4757  MOV BYTE PTR [EBP-4], 1
004C475B  CALL <JMP.&mfc42.#CString::~CString_800>
004C4760  LEA ECX, [local.7]
004C4763  MOV BYTE PTR [EBP-4], 0
004C4767  CALL <JMP.&mfc42.#CString::~CString_800>
004C476C  LEA ECX, [local.8]
004C476F  MOV [local.1], -1
004C4776  CALL <JMP.&mfc42.#CString::~CString_800>
004C477B  MOV EAX, 1
004C4780  MOV ECX, [local.3]
004C4783  MOV DWORD PTR FS:[0], ECX
004C478A  POP EDI ; 0012F180
004C478B  POP ESI ; 0012F180
004C478C  POP EBX ; 0012F180
004C478D  MOV ESP, EBP
004C478F  POP EBP ; 0012F180
004C4790  RETN

并返回 EAX==1,好,我们从网络验证模块返回后通过跳转来到下面的代码:
004C378F  PUSH ESI
004C3790  PUSH 008ED544               ; ASCII "NagCount"
004C3795  PUSH 008E0288               ; ASCII "Settings"
004C379A  MOV ECX, EBX                ; WebBuild.0092A0A0
004C379C  CALL <JMP.&mfc42.#CWinApp::WriteProfileInt_6402>
004C37A1  MOV ECX, DWORD PTR [EBP-18]
004C37A4  PUSH ECX
004C37A5  MOV ECX, DWORD PTR [EDI+60]
004C37A8  CALL 004F43B0
004C37AD  MOV EDX, DWORD PTR [EBP-1C]
004C37B0  MOV ECX, EBX                ; WebBuild.0092A0A0
004C37B2  PUSH EDX
004C37B3  PUSH 008E3AE8               ; ASCII "Name"
004C37B8  PUSH 008E0288               ; ASCII "Settings"
004C37BD  CALL <JMP.&mfc42.#CWinApp::WriteProfileStringA_>
004C37C2  MOV EAX, DWORD PTR [EBP-1C] ; 输入的 Email
004C37C5  MOV ECX, DWORD PTR [EDI+60]
004C37C8  PUSH EAX
004C37C9  CALL 004F4770               ; 将输入的 Email 复制到指针 ECX 指向的内存中
004C37CE  MOV ECX, DWORD PTR [EDI+60]
004C37D1  CALL 004F43D0               ; 将注册信息的一部分(因完整的注册信息还有另一部分)写入到注册表中

我们进入 CALL 004F43D0 看看到底做了什么手脚。
004F4350 / MOV EAX, DWORD PTR FS:[0]
004F4356 | PUSH -1
004F4358 | PUSH WebBuild.007EEB88
004F435D | PUSH EAX
004F435E | MOV DWORD PTR FS:[0], ESP
004F4365 | SUB ESP, 10
004F4368 | PUSH ESI
004F4369 | MOV ESI, ECX
004F436B | PUSH EDI
004F436C | PUSH WebBuild.008EF858               ; /String2 = "NotSet"
004F4371 | LEA EDI, DWORD PTR [ESI+8]           ; |
004F4374 | PUSH EDI                             ; |String1
004F4375 | CALL DWORD PTR [<&kernel32.lstrcpy>] ; \lstrcpyA
004F437B | MOV ECX, ESI
004F437D | CALL WebBuild.004F3E10               ; 注册码算法 CALL
004F4382 | TEST EAX, EAX                        ; 乱输入的注册码 EAX=0
004F4384 | JE WebBuild.004F4489                 ; 跳转不会向注册表写入注册信息

经过分析 004F437D | CALL WebBuild.004F3E10 处的 CALL 为注册码算法验证,因此,先进入此 CALL 看看是如何验证的:
仅贴出关键部分:
第一部分:
004F3E7E | LEA ESI, DWORD PTR [EDI+408]         ; 输入的注册码
004F3E84 | PUSH 2D                              ; 很熟悉吧,2Dh="-",注册码分隔符
004F3E86 | PUSH EBX
004F3E87 | LEA EAX, DWORD PTR [ESP+2C]
004F3E8B | PUSH ESI
004F3E8C | PUSH EAX
004F3E8D | MOV BYTE PTR [ESP+68], 5
004F3E92 | CALL <JMP.&mfc42.#AfxExtractSUBString_1140> ; 分割字符串函数,以 "-" 作为分割的依据将注册码分割

第二部分:
004F3EEF | PUSH ESI                             ; //String,输入的注册码
004F3EF0 | CALL DWORD PTR [<&kernel32.lstrlen>] ; \lstrlenA
004F3EF6 | CMP EAX, 1E                          ; 注册码长度23h(即10进制35)与 1Eh(即10进制的 30)
004F3EF9 | JNZ WebBuild.004F4260                ; 不等 1Eh,则注册信息不正确

怎么不等于呢?纳闷,向下翻看发现:

搞这些名堂,为了看清楚它的真面目,把我的脖子都搞歪了,阿拉伯语序式排列(从右到左,反序),这个是不是注册码呢,刚好 30 的长度(包括分隔符),那试试吧!
(Game Over)大侠你的游戏功底不足,请重新来过吧!晕!@!#
将所谓的注册码正序排列后为:WB800-439F-V899-7R89-CDB0-4X47(如果注册码长相确实如此,则应为 5-4-4-4-4-4,即第一组长度 5位,剩余的5组长度为 4 位,加上分隔符 "-",共30位),输入此注册码继续研究(仅贴出关键的代码):
004F3EEF | PUSH ESI                                     ; //String,输入的注册码
004F3EF0 | CALL DWORD PTR [<&kernel32.lstrlen>]         ; \lstrlenA
004F3EF6 | CMP EAX, 1E                                  ; 注册码长度23h(即10进制35)与 1Eh(即10进制的 30)
004F3EF9 | JNZ WebBuild.004F4260                        ; 不等 1Eh,则注册信息不正确
004F3EFF | MOV EAX, DWORD PTR [ESP+24]                  ; 注册码分割后的第一组 WB800
004F3F03 | PUSH WebBuild.008EF850                       ; /s2 = "WB800",软件预置的字符串
004F3F08 | PUSH EAX                                     ; |s1=输入的注册码分割后的第一组注册码
004F3F09 | CALL DWORD PTR [<&msvcrt._mbsCMP>]           ; \_mbsCMP
004F3F0F | ADD ESP, 8                                   ; 如果相等则 EAX 中应该返回 0
004F3F12 | TEST EAX, EAX
004F3F14 | JNZ WebBuild.004F4260                        ; 测试结果不为 0 则跳转 Over,即第一组注册码必须为 WB800
004F3F1A | MOV ECX, DWORD PTR [ESP+1C]                  ; 注册码的第4组
004F3F1E | CMP DWORD PTR [ECX-8], EBP                   ; EBP 的值恒定为 4,即本组注册码的长度与4比较
004F3F21 | JNZ WebBuild.004F4260                        ; 不等于4,则 Over
004F3F27 | MOV EDX, DWORD PTR [ESP+10]                  ; 注册码的第5组
004F3F2B | CMP DWORD PTR [EDX-8], EBP                   ; 同理
004F3F2E | JNZ WebBuild.004F4260                        ; 不等于4,则 Over
004F3F34 | MOV EAX, DWORD PTR [ESP+20]                  ; 注册码的第2组
004F3F38 | CMP DWORD PTR [EAX-8], EBP
004F3F3B | JNZ WebBuild.004F4260                        ; 不等于4,则 Over
004F3F41 | MOV ECX, DWORD PTR [ESP+18]                  ; 注册码的第3组反序
004F3F45 | CMP DWORD PTR [ECX-8], EBP
004F3F48 | JNZ WebBuild.004F4260                        ; 不等于4,则 Over
004F3F4E | MOV EDX, DWORD PTR [ESP+14]
004F3F52 | CMP DWORD PTR [EDX-8], EBP                   ; 注册码的第6组
004F3F55 | JNZ WebBuild.004F4260
004F3F5B | LEA ECX, DWORD PTR [ESP+1C]
004F3F5F | CALL <JMP.&mfc42.#CString::MakeReverse_4203>
004F3F64 | LEA ECX, DWORD PTR [ESP+14]
004F3F68 | CALL <JMP.&mfc42.#CString::MakeReverse_4203>
004F3F6D | LEA ECX, DWORD PTR [ESP+28]
004F3F71 | CALL <JMP.&mfc42.#CString::CString_540>
004F3F76 | PUSH WebBuild.008EF830                       ; ASCII "74X4-0BDC-98R7-998V-F934-008BW",软件中预置的逆序注册码
004F3F7B | LEA ECX, DWORD PTR [ESP+2C]
004F3F7F | MOV BYTE PTR [ESP+5C], 6
004F3F84 | CALL <JMP.&mfc42.#CString::operatOR=_860>
004F3F89 | LEA ECX, DWORD PTR [ESP+28]
004F3F8D | CALL <JMP.&mfc42.#CString::MakeReverse_4203> ; 逆序函数
004F3F92 | MOV EAX, DWORD PTR [ESP+28]                  ; 软件中预置的注册码逆序后的注册码
004F3F96 | PUSH EAX                                     ; //String2,软件中预置的注册码逆序后的注册码
004F3F97 | PUSH ESI                                     ; ||String1,输入的注册码
004F3F98 | CALL DWORD PTR [<&kernel32.lstrCMP>]         ; \lstrCMPA
004F3F9E | TEST EAX, EAX                                ; 相等 EAX 中返回 0;测试 EAX 是否为 0
004F3FA0 | JE WebBuild.004F41BA                         ; 跳转则基本注册成功,有功能限制
004F3FA6 | PUSH ESI
004F3FA7 | CALL WebBuild.004F3D70                       ; 检测输入的注册码是否为软件中预置的第2组注册码,不包括固定的第一组 WB800
004F3FAC | ADD ESP, 4                                   ; 对上面的 CALL 不再分析贴出,有兴趣的自己研究
004F3FAF | TEST EAX, EAX
004F3FB1 | JNZ WebBuild.004F41BA                        ; 不是继续检测是否为第3组,分割后的注册码
004F3FB7 | MOV ECX, DWORD PTR [ESP+20]
004F3FBB | MOV ESI, DWORD PTR [<&msvcrt._mbsCMP>] ; msvcrt._mbsCMP
004F3FC1 | PUSH WebBuild.008EF828                       ; //s2 = "2A98",软件值预置的第2组注册码
004F3FC6 | PUSH ECX                                     ; ||s1=输入注册码的第2组
004F3FC7 | CALL ESI                                     ; \_mbsCMP
004F3FC9 | ADD ESP, 8
004F3FCC | TEST EAX, EAX
004F3FCE | JNZ SHORT WebBuild.004F4020                  ; 不等 Over
004F3FD0 | MOV EDX, DWORD PTR [ESP+18]
004F3FD4 | PUSH WebBuild.008EF820                       ; ASCII "9R67",预置注册码的第3组反序
004F3FD9 | PUSH EDX                                     ; 输入注册码的第3组反序
004F3FDA | CALL ESI
004F3FDC | ADD ESP, 8
004F3FDF | TEST EAX, EAX
004F3FE1 | JNZ SHORT WebBuild.004F4020                  ; 不等 Over
004F3FE3 | MOV EAX, DWORD PTR [ESP+1C]
004F3FE7 | PUSH WebBuild.008EF818                       ; ASCII "156U",预置注册码的第4组反序
004F3FEC | PUSH EAX                                     ; 输入注册码的第4组反序
004F3FED | CALL ESI
004F3FEF | ADD ESP, 8
004F3FF2 | TEST EAX, EAX
004F3FF4 | JNZ SHORT WebBuild.004F4020                  ; 不等 Over
004F3FF6 | MOV ECX, DWORD PTR [ESP+10]
004F3FFA | PUSH WebBuild.008EF810                       ; ASCII "5C17",预置注册码的第5组
004F3FFF | PUSH ECX                                     ; 输入注册码的第5组
004F4000 | CALL ESI
004F4002 | ADD ESP, 8
004F4005 | TEST EAX, EAX
004F4007 | JNZ SHORT WebBuild.004F4020                  ; 不等 Over
004F4009 | MOV EDX, DWORD PTR [ESP+14]
004F400D | PUSH WebBuild.008EF808                       ; ASCII "352U",预置注册码的第6组反序
004F4012 | PUSH EDX                                     ; 输入注册码的第6组反序
004F4013 | CALL ESI
004F4015 | ADD ESP, 8
004F4018 | TEST EAX, EAX
004F401A | JE WebBuild.004F41BA                         ; 跳转说明相等则正确,否则不等于预置注册码的任何一组则进入真正注册码的验证流程
2012-1-17 21:33
0
雪    币: 63
活跃值: (76)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
6
非常感谢帮助格式化代码,确实阅读起来困难,我不擅长格式化处理,本来想通过 FrontPage 弄好后上传,但不知是论坛不支持直接 html 方式编辑还是我为找到,左了半天还得手动一个一个贴上图片,晕!
2012-1-17 21:49
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
我用我自己的email地址跟预置码WB800-439F-V899-7R89-CDB0-4X47注册成功8.0.4版。
2012-2-18 11:53
0
雪    币: 3279
活跃值: (1997)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
虽然这样注册了,但是软件生成的每一个网页会显示 这个网页是用非法的网页构建器制作的。
2012-2-18 14:20
0
游客
登录 | 注册 方可回帖
返回
//