研究对象:Pocket CHM Pro 5.9
下载地址:自己搜索
软件介绍:这是用于在 CHM 格式创建帮助,电子读物或者手册的工具。袖珍的 CHM 能够直接打开和编辑 CHM 文件,和它能够反编译成批的 CHM。
涉及工具:OD,PEiD,stud_pe,WinHex
一、前言
最近在学习CAD的VBA,在明经通道上看到有在线的《ActiveX 和 VBA 参考》,想要CHM版的还要花30大洋,用OE把在线版的拉下来一看,乐了:居然正是完整的HTM版!自己编译成CHM版的不就OK了么!
但是正是这个编译过程让我郁闷了好半天。先找了个Microsoft HTML Help Workshop汉化版,一编译就出错;再找个国产软件QuickCHM试试看,编译出来的文件很多地方有问题,而且打开工程文件巨慢;最后找到这个PocketCHMPro,用起来感觉不错,但是未注册版只允许编译10个HTM页面。于是打算CRACK之!
二、开始CRACK:开始郁闷
先用PEiD查一下主程序PocketCHMPro.exe:Microsoft Visual C++ 6.0 [Overlay]到底还是2005年的东西,无壳,心中窃喜;进入输入注册码窗口,不输入任何东西直接点“OK”,弹出“User ID can not be empty”,居然还有错误提示,心中狂喜;直接用OD载入,查找字符串参考,没有找到任何有用的东西,字符串加密了?有点郁闷了;下断点“bp MessageBoxA”,没断下来,再下MessageBoxExA,GetDlgItemTextA,GetDlgItemTextExA之类的常用断点,还是没断下来,很郁闷了;直接暂停程序,再切换到程序界面,程序还在正常运行,查看一下任务管理器,居然还是双进程?相当郁闷了;任务管理器上显示新进程路径是“c:\Program Files\Pocket CHM Pro\PocketCHM_Pro.exe”,到目录下找到这个隐藏的主文件,直接运行弹出“程序执行错误”,极其郁闷了居然还是双进程保护!
三、双进程调试:继续郁闷
先用OD分析一下真正的主程序PocketCHM_Pro.exe,果然不出所料,所有关键字符串都已经加密,几乎没有调用标准的API,我所知道的常用断点全部失效,静态分析没有找到有用信息;由于是双进程保护,只好开二个OD,一个调试主程序,另一个附加到PocketCHM_Pro.exe进行调试,却发现动态调试找不到下手的地方。看着程序总是在MFC.dll和pceeuilib.dll中来回转,我却只能一愁莫展,心里这个郁闷呀!
浪费了一二个小时后突然想到:MFC程序处理字符串时都会用到MFC42.#800_CString::~CString,MFC42.#858_CString::operator=等之类的函数,用这个用断点会不会有用呢?于是查找MFC42.#800_CString::~CString参考并在IAT处的JMP直接下断:
0046AB10 jmp dword ptr [<&MFC42.#800_CString::~CString>] ; MFC42.#800_CString::~CString
输入用户ID和注册码后点击“OK”,终于断下来了!
观察堆栈:
0012E9EC 0042E26C 返回到 PocketCH.0042E26C 来自 <jmp.&MFC42.#858_CString::operator=>
在0042E26C处下断,再执行完二个RET后来到注册码处理过程:
0041FA6F push eax 0041FA70 mov byte ptr [ebp-4], 2 0041FA74 call <jmp.&MFC42.#3874_CWnd::GetWindowTextA> ; 获取用户名UN 0041FA79 lea eax, dword ptr [ebp-18] 0041FA7C lea ecx, dword ptr [edi+A8] 0041FA82 push eax 0041FA83 call <jmp.&MFC42.#3874_CWnd::GetWindowTextA> ; 获取用户输入序列号SN 0041FA88 lea ecx, dword ptr [ebp-18] 0041FA8B call <jmp.&MFC42.#6282_CString::TrimLeft> 0041FA90 lea ecx, dword ptr [ebp-18] 0041FA93 call <jmp.&MFC42.#6283_CString::TrimRight> 0041FA98 mov eax, dword ptr [ebp-18] 0041FA9B cmp dword ptr [eax-8], ebx ; len(un)=0? 0041FA9E jnz 0041FB8D 0041FAA4 lea eax, dword ptr [ebp-30] 0041FAA7 mov esi, 004AA940 0041FAAC push 136 0041FAB1 push eax 0041FAB2 mov ecx, esi 0041FAB4 call <DecodeString> ; 字符串解密过程 0041FAB9 push eax ; "User ID can not be empty" 0041FABA lea ecx, dword ptr [ebp-10] 0041FABD mov byte ptr [ebp-4], 3 0041FAC1 call <jmp.&MFC42.#858_CString::operator=> 0041FAC6 lea ecx, dword ptr [ebp-30] 0041FAC9 mov byte ptr [ebp-4], 2 0041FACD call <jmp.&MFC42.#800_CString::~CString> 0041FAD2 lea eax, dword ptr [ebp-30] 0041FAD5 lea ecx, dword ptr [ebp-144] 0041FADB push eax 0041FADC mov dword ptr [ebp-30], ebx 0041FADF call dword ptr [<&fspceelib.CExtBox::CExtBox>] ; fspceeli.CExtBox::CExtBox 0041FAE5 lea ecx, dword ptr [ebp-20] 0041FAE8 mov byte ptr [ebp-4], 4 0041FAEC call <jmp.&MFC42.#540_CString::CString> 0041FAF1 lea eax, dword ptr [ebp-28] 0041FAF4 push 138 0041FAF9 push eax 0041FAFA mov ecx, esi 0041FAFC mov byte ptr [ebp-4], 5 0041FB00 call <DecodeString> ; 字符串解密过程 0041FB05 push eax ; "Pocket CHM Pro" 0041FB06 lea ecx, dword ptr [ebp-20] 0041FB09 mov byte ptr [ebp-4], 6 0041FB0D call <jmp.&MFC42.#858_CString::operator=> 0041FB12 lea ecx, dword ptr [ebp-28] 0041FB15 mov byte ptr [ebp-4], 5 0041FB19 call <jmp.&MFC42.#800_CString::~CString> 0041FB1E lea eax, dword ptr [ebp-28] 0041FB21 lea ecx, dword ptr [ebp-144] 0041FB27 push eax 0041FB28 lea eax, dword ptr [ebp-38] 0041FB2B push eax 0041FB2C lea eax, dword ptr [ebp-34] 0041FB2F push eax 0041FB30 lea eax, dword ptr [ebp-24] 0041FB33 push eax 0041FB34 lea eax, dword ptr [ebp-1C] 0041FB37 push eax 0041FB38 lea eax, dword ptr [edi+20] 0041FB3B push dword ptr [ebp-20] 0041FB3E mov dword ptr [ebp-28], ebx 0041FB41 mov dword ptr [ebp-38], ebx 0041FB44 mov dword ptr [ebp-34], 104 0041FB4B push dword ptr [ebp-10] 0041FB4E mov dword ptr [ebp-24], 4 0041FB55 mov dword ptr [ebp-1C], ebx 0041FB58 push eax ; 弹出窗口 0041FB59 call dword ptr [<&fspceelib.CExtBox::Message>] ; fspceeli.CExtBox::Message 0041FB5F push 440 0041FB64 mov ecx, edi 0041FB66 call <jmp.&MFC42.#3092_CWnd::GetDlgItem> 0041FB6B mov ecx, eax 0041FB6D call <jmp.&MFC42.#5981_CWnd::SetFocus> 0041FB72 lea ecx, dword ptr [ebp-20] 0041FB75 mov byte ptr [ebp-4], 4 0041FB79 call <jmp.&MFC42.#800_CString::~CString> 0041FB7E mov byte ptr [ebp-4], 2 0041FB82 lea ecx, dword ptr [ebp-144] 0041FB88 jmp 0041FDA3 0041FB8D lea ecx, dword ptr [ebp-14] 0041FB90 call <jmp.&MFC42.#6282_CString::TrimLeft> 0041FB95 lea ecx, dword ptr [ebp-14] 0041FB98 call <jmp.&MFC42.#6283_CString::TrimRight> 0041FB9D mov eax, dword ptr [ebp-14] 0041FBA0 cmp dword ptr [eax-8], ebx ; len(sn)=0? 0041FBA3 jnz 0041FC92 0041FBA9 lea eax, dword ptr [ebp-1C] 0041FBAC mov esi, 004AA940 0041FBB1 push 137 0041FBB6 push eax 0041FBB7 mov ecx, esi 0041FBB9 call <DecodeString> ; 字符串解密过程 0041FBBE push eax ; "Serial Number can not be empty" 0041FBBF lea ecx, dword ptr [ebp-10] 0041FBC2 mov byte ptr [ebp-4], 7 0041FBC6 call <jmp.&MFC42.#858_CString::operator=> 0041FBCB lea ecx, dword ptr [ebp-1C] 0041FBCE mov byte ptr [ebp-4], 2 0041FBD2 call <jmp.&MFC42.#800_CString::~CString> 0041FBD7 lea eax, dword ptr [ebp-1C] 0041FBDA lea ecx, dword ptr [ebp-144] 0041FBE0 push eax 0041FBE1 mov dword ptr [ebp-1C], ebx 0041FBE4 call dword ptr [<&fspceelib.CExtBox::CExtBox>] ; fspceeli.CExtBox::CExtBox 0041FBEA lea ecx, dword ptr [ebp-20] 0041FBED mov byte ptr [ebp-4], 8 0041FBF1 call <jmp.&MFC42.#540_CString::CString> 0041FBF6 lea eax, dword ptr [ebp-24] 0041FBF9 push 138 0041FBFE push eax 0041FBFF mov ecx, esi 0041FC01 mov byte ptr [ebp-4], 9 0041FC05 call <DecodeString> 0041FC0A push eax ; "Pocket CHM Pro" 0041FC0B lea ecx, dword ptr [ebp-20] 0041FC0E mov byte ptr [ebp-4], 0A 0041FC12 call <jmp.&MFC42.#858_CString::operator=> 0041FC17 lea ecx, dword ptr [ebp-24] 0041FC1A mov byte ptr [ebp-4], 9 0041FC1E call <jmp.&MFC42.#800_CString::~CString> 0041FC23 lea eax, dword ptr [ebp-24] 0041FC26 lea ecx, dword ptr [ebp-144] 0041FC2C push eax 0041FC2D lea eax, dword ptr [ebp-34] 0041FC30 push eax 0041FC31 lea eax, dword ptr [ebp-38] 0041FC34 push eax 0041FC35 lea eax, dword ptr [ebp-28] 0041FC38 push eax 0041FC39 lea eax, dword ptr [ebp-30] 0041FC3C push eax 0041FC3D lea eax, dword ptr [edi+20] 0041FC40 push dword ptr [ebp-20] 0041FC43 mov dword ptr [ebp-24], ebx 0041FC46 mov dword ptr [ebp-34], ebx 0041FC49 mov dword ptr [ebp-38], 104 0041FC50 push dword ptr [ebp-10] 0041FC53 mov dword ptr [ebp-28], 4 0041FC5A mov dword ptr [ebp-30], ebx 0041FC5D push eax ; 弹出窗口 0041FC5E call dword ptr [<&fspceelib.CExtBox::Message>] ; fspceeli.CExtBox::Message 0041FC64 push 441 0041FC69 mov ecx, edi 0041FC6B call <jmp.&MFC42.#3092_CWnd::GetDlgItem> 0041FC70 mov ecx, eax 0041FC72 call <jmp.&MFC42.#5981_CWnd::SetFocus> 0041FC77 lea ecx, dword ptr [ebp-20] 0041FC7A mov byte ptr [ebp-4], 8 0041FC7E call <jmp.&MFC42.#800_CString::~CString> 0041FC83 mov byte ptr [ebp-4], 2 0041FC87 lea ecx, dword ptr [ebp-144] 0041FC8D jmp 0041FDA3 0041FC92 push ecx 0041FC93 lea eax, dword ptr [ebp-14] 0041FC96 mov ecx, esp 0041FC98 mov dword ptr [ebp-3C], esp 0041FC9B push eax 0041FC9C call <jmp.&MFC42.#535_CString::CString> 0041FCA1 push ecx 0041FCA2 lea eax, dword ptr [ebp-18] 0041FCA5 mov ecx, esp 0041FCA7 mov dword ptr [ebp-40], esp 0041FCAA push eax 0041FCAB mov byte ptr [ebp-4], 0B 0041FCAF call <jmp.&MFC42.#535_CString::CString> 0041FCB4 mov esi, 004A9DD8 0041FCB9 mov byte ptr [ebp-4], 2 0041FCBD mov ecx, esi 0041FCBF call 0045C7DC 0041FCC4 mov ecx, esi 0041FCC6 call 0045D658 ; 加密用户名注册码并保存到注册表 0041FCCB lea eax, dword ptr [ebp-1C] 0041FCCE mov esi, 004AA940 0041FCD3 push 135 0041FCD8 push eax 0041FCD9 mov ecx, esi 0041FCDB call <DecodeString> ; 字符串解密过程 0041FCE0 push eax ; "Thank you!",LF,LF,"Please restart the program, in order to",LF,"activate all functions for you!" 0041FCE1 lea ecx, dword ptr [ebp-10] 0041FCE4 mov byte ptr [ebp-4], 0C 0041FCE8 call <jmp.&MFC42.#858_CString::operator=> 0041FCED lea ecx, dword ptr [ebp-1C] 0041FCF0 mov byte ptr [ebp-4], 2 0041FCF4 call <jmp.&MFC42.#800_CString::~CString> 0041FCF9 lea eax, dword ptr [ebp-1C] 0041FCFC lea ecx, dword ptr [ebp-23C] 0041FD02 push eax 0041FD03 mov dword ptr [ebp-1C], ebx 0041FD06 call dword ptr [<&fspceelib.CExtBox::CExtBox>] ; fspceeli.CExtBox::CExtBox 0041FD0C lea ecx, dword ptr [ebp-2C] 0041FD0F mov byte ptr [ebp-4], 0D 0041FD13 call <jmp.&MFC42.#540_CString::CString> 0041FD18 lea eax, dword ptr [ebp-24] 0041FD1B push 138 0041FD20 push eax 0041FD21 mov ecx, esi 0041FD23 mov byte ptr [ebp-4], 0E 0041FD27 call <DecodeString> ; 字符串解密过程 0041FD2C push eax ; "Pocket CHM Pro" 0041FD2D lea ecx, dword ptr [ebp-2C] 0041FD30 mov byte ptr [ebp-4], 0F 0041FD34 call <jmp.&MFC42.#858_CString::operator=> 0041FD39 lea ecx, dword ptr [ebp-24] 0041FD3C mov byte ptr [ebp-4], 0E 0041FD40 call <jmp.&MFC42.#800_CString::~CString> 0041FD45 lea eax, dword ptr [ebp-48] 0041FD48 lea ecx, dword ptr [ebp-23C] 0041FD4E push eax 0041FD4F lea eax, dword ptr [ebp-4C] 0041FD52 push eax 0041FD53 lea eax, dword ptr [ebp-44] 0041FD56 push eax 0041FD57 lea eax, dword ptr [ebp-40] 0041FD5A push eax 0041FD5B lea eax, dword ptr [ebp-3C] 0041FD5E push eax 0041FD5F lea eax, dword ptr [edi+20] 0041FD62 push dword ptr [ebp-2C] 0041FD65 mov dword ptr [ebp-48], ebx 0041FD68 mov dword ptr [ebp-4C], ebx 0041FD6B mov dword ptr [ebp-44], 106 0041FD72 push dword ptr [ebp-10] 0041FD75 mov dword ptr [ebp-40], 4 0041FD7C mov dword ptr [ebp-3C], ebx 0041FD7F push eax ; 弹出窗口 0041FD80 call dword ptr [<&fspceelib.CExtBox::Message>] ; fspceeli.CExtBox::Message 0041FD86 mov ecx, edi 0041FD88 call <jmp.&MFC42.#4853_CDialog::OnOK> 0041FD8D lea ecx, dword ptr [ebp-2C] 0041FD90 mov byte ptr [ebp-4], 0D 0041FD94 call <jmp.&MFC42.#800_CString::~CString> 0041FD99 mov byte ptr [ebp-4], 2 0041FD9D lea ecx, dword ptr [ebp-23C] 0041FDA3 call dword ptr [<&fspceelib.CExtBox::~CExtBox>] ; fspceeli.CExtBox::~CExtBox 0041FDA9 lea ecx, dword ptr [ebp-14] 0041FDAC mov byte ptr [ebp-4], 1 0041FDB0 call <jmp.&MFC42.#800_CString::~CString> 0041FDB5 lea ecx, dword ptr [ebp-18] 0041FDB8 mov byte ptr [ebp-4], bl 0041FDBB call <jmp.&MFC42.#800_CString::~CString> 0041FDC0 or dword ptr [ebp-4], FFFFFFFF 0041FDC4 lea ecx, dword ptr [ebp-10] 0041FDC7 call <jmp.&MFC42.#800_CString::~CString> 0041FDCC mov ecx, dword ptr [ebp-C] 0041FDCF pop edi 0041FDD0 pop esi 0041FDD1 mov dword ptr fs:[0], ecx 0041FDD8 pop ebx 0041FDD9 leave 0041FDDA retn
得知程序的弹出窗口都是用的fspceeli.CExtBox::Message函数来执行的,就可以爆破很多注册限制的地方了。
但是引导程序每次执行时都会重新生成主程序,这样爆破也就无效了,看来还是要从头分析找到引导程序与主程序之间的关系了。
四、分析引导程序:找到眉目
干脆还是从头开始分析引导程序PocketCHMPro.exe吧:
0042EF18 >push ebp ; 程序入口 0042EF19 mov ebp, esp 0042EF1B push -1 0042EF1D push 00401458 0042EF22 push <jmp.&MSVCRT._except_handler3> ……一直单步往下走 0042F03C push eax 0042F03D push esi 0042F03E push ebx 0042F03F push ebx 0042F040 call dword ptr [<&KERNEL32.GetModuleHandleA>; kernel32.GetModuleHandleA 0042F046 push eax 0042F047 call 0042C667 ; 后面就退出了,这里跟进 0042F04C mov dword ptr [ebp-68], eax 0042F04F push eax 0042F050 call dword ptr [<&MSVCRT.exit>] ; msvcrt.exit 0042C667 push ebp 0042C668 mov ebp, esp 0042C66A sub esp, 20 0042C66D push ebx 0042C66E push esi 0042C66F mov esi, 0042F2A8 0042C674 push edi 0042C675 mov ecx, esi 0042C677 call 0042C39B ; 关联文件 0042C67C push dword ptr [ebp+10] 0042C67F call <jmp.&MSVCRT.strlen> 0042C684 pop ecx 0042C685 mov edi, eax 0042C687 mov ecx, esi 0042C689 call 0042C368 ; 删除旧文件,解压缩数据并写新文件 0042C68E mov ebx, eax 0042C690 test ebx, ebx 0042C692 je short 0042C6D7 ; 写新文件出错时不跳,这里跳过 …… 0042C6D7 test edi, edi 0042C6D9 jnz short 0042C6E4 0042C6DB mov ecx, esi ; PocketCH.0042F2A8 0042C6DD call 0042BB20 ; 执行新建立的文件并监控等待程序退出 0042C6E2 jmp short 0042C6EE 0042C6E4 push dword ptr [ebp+10] 0042C6E7 mov ecx, esi 0042C6E9 call 0042BCA2 0042C6EE mov esi, eax 0042C6F0 cmp esi, -61 0042C6F3 jnz short 0042C706 0042C6F5 push 10 0042C6F7 push 0 0042C6F9 push 004256B0 ; ASCII "Failed to run.",LF,"Please get a new copy of this program." 0042C6FE push 0 0042C700 call dword ptr [<&USER32.MessageBoxA>] ; USER32.MessageBoxA 0042C706 mov eax, esi 0042C708 pop edi 0042C709 pop esi 0042C70A pop ebx 0042C70B leave 0042C70C retn 10
这里又有一个问题:是什么时候开始可以直接执行主程序的呢?再次CTRL+F2重新运行,每执行完一个API后双击一下目录中的PocketCHM_Pro.exe看看是否能运行,终于找到关键所在了:
0042EFD3 push 00402008 ; [402008]=42BDDA 0042EFD8 push 00402000 0042EFDD call <jmp.&MSVCRT._initterm> ; 根据堆栈数据跳转到42BDDA处执行 0042BDDA call 0042BDE4 0042BDE4 mov ecx, 0042F2A8 0042BDE9 jmp 0042BE09 0042BE09 mov eax, 0042F13C 0042BE0E call <jmp.&MSVCRT._EH_prolog> 0042BE13 sub esp, 10C 0042BE19 push ebx 0042BE1A push esi 0042BE1B mov esi, ecx 0042BE1D push edi 0042BE1E mov dword ptr [ebp-14], esi 0042BE21 lea edi, dword ptr [esi+C] 0042BE24 mov ecx, edi 0042BE26 call <jmp.&MFC42.#540_CString::CString> 0042BE2B xor ebx, ebx 0042BE2D push 00423B70 ; ASCII "PCHMPROM",映像名 0042BE32 mov ecx, edi 0042BE34 mov dword ptr [ebp-4], ebx 0042BE37 call <jmp.&MFC42.#860_CString::operator=> 0042BE3C push 5C 0042BE3E call <jmp.&MFC42.#823_operator new> 0042BE43 pop ecx 0042BE44 mov dword ptr [ebp-10], eax 0042BE47 cmp eax, ebx 0042BE49 mov byte ptr [ebp-4], 1 0042BE4D je short 0042BE62 0042BE4F mov edi, dword ptr [edi] 0042BE51 push ebx 0042BE52 push ebx 0042BE53 push 96000 ; 内存映像大小 0042BE58 push edi 0042BE59 mov ecx, eax 0042BE5B call 0042E57D ; 用CreateFileMappingA建立子进程共享数据,关键,跟进 …… 0042E71D push dword ptr [esi+C] 0042E720 call <jmp.&MFC42.#521_CSingleLock::CSingleLock> ; 锁定数据 0042E725 jmp short 0042E729 0042E727 xor eax, eax 0042E729 cmp eax, edi 0042E72B mov byte ptr [ebp-4], 1 0042E72F mov dword ptr [esi+10], eax 0042E732 je 0042E80B 0042E738 push -1 0042E73A mov ecx, eax 0042E73C call <jmp.&MFC42.#4167_CSingleLock::Lock> 0042E741 push dword ptr [ebp+8] ; MapName="PCHMPROM" 0042E744 mov eax, dword ptr [esi+28] 0042E747 neg eax 0042E749 push dword ptr [ebp+C] ; MaximumSizeLow=96000 0042E74C lea ecx, dword ptr [esi+40] 0042E74F sbb eax, eax 0042E751 push edi ; MaximumSizeHigh=0 0042E752 and eax, ecx 0042E754 push 4 ; Protection=PAGE_READWRITE 0042E756 push eax ; pSecurity=00382690 0042E757 push -1 ; hFile=FFFFFFFF 0042E759 call dword ptr [<&KERNEL32.CreateFileMappingA>] ; CreateFileMappingA建立内存映像 0042E75F mov ebx, dword ptr [<&KERNEL32.GetLastError>] ; ntdll.RtlGetLastWin32Error 0042E765 mov dword ptr [esi+24], eax ; 保存hFile 0042E768 call ebx ; 建立映像时是否出错? 0042E76A cmp eax, 0B7 0042E76F jnz short 0042E774 0042E771 mov dword ptr [esi+14], edi 0042E774 mov eax, dword ptr [esi+24] 0042E777 cmp eax, edi 0042E779 je short 0042E7F9 0042E77B push edi 0042E77C push edi 0042E77D push edi 0042E77E push 6 0042E780 push eax ; hFile 0042E781 call dword ptr [<&KERNEL32.MapViewOfFile>] ; MapViewOfFile生成映像 0042E787 cmp eax, edi 0042E789 mov dword ptr [esi+20], eax ; 保存映像地址 0042E78C je short 0042E7F9 0042E78E cmp dword ptr [esi+14], edi ; 生成映像是否出错? 0042E791 je short 0042E7B7 0042E793 mov ebx, dword ptr [ebp+C] 0042E796 push ebx 0042E797 push edi 0042E798 push eax 0042E799 call <jmp.&MSVCRT.memset> 0042E79E lea eax, dword ptr [esi+1C] 0042E7A1 push 4 0042E7A3 push eax 0042E7A4 mov dword ptr [eax], ebx 0042E7A6 mov eax, dword ptr [esi+20] 0042E7A9 add eax, 4 0042E7AC push eax 0042E7AD call <jmp.&MSVCRT.memcpy> ; 写映像数据 0042E7B2 add esp, 18 0042E7B5 jmp short 0042E7C9 0042E80E cmp ecx, edi 0042E810 je short 0042E817 0042E812 call <jmp.&MFC42.#6307_CSingleLock::Unlock> ; 解锁数据 0042E817 mov ecx, dword ptr [ebp-C] 0042E81A mov eax, esi 0042E81C pop edi 0042E81D pop esi 0042E81E pop ebx 0042E81F mov dword ptr fs:[0], ecx 0042E826 leave 0042E827 retn 10 返回后继续用循环写映像数据: 0042BE82 mov edi, 00422024 ; 字符串初始地址 0042BE87 mov eax, dword ptr [edi] ; 循环写映像数据 0042BE89 mov ebx, dword ptr [edi-4] 0042BE8C xor ecx, ecx 0042BE8E cmp dword ptr [edi-8], 00423B6C 0042BE95 push eax 0042BE96 push eax 0042BE97 setne cl 0042BE9A mov dword ptr [ebp-10], ecx 0042BE9D call <jmp.&MSVCRT.strlen> 0042BEA2 pop ecx 0042BEA3 mov ecx, dword ptr [esi+10] 0042BEA6 push eax 0042BEA7 push ebx 0042BEA8 push dword ptr [ebp-10] 0042BEAB call 0042EB58 ; 继续向映像中写数据 0042BEB0 test eax, eax 0042BEB2 jnz short 0042BECF ; 写映像出错时不跳 0042BEB4 mov ecx, dword ptr [esi+10] 0042BEB7 call 0042E8FF 0042BEBC cmp eax, 0E 0042BEBF jnz short 0042BECF 0042BEC1 push 0 0042BEC3 push 0 0042BEC5 push 00423B24 ; ASCII "!!!ERROR_OUTOFMEMORY-1!!!-find 'remark203153' in your dsw's source code" 0042BECA call <jmp.&MFC42.#1200_AfxMessageBox> 0042BECF add edi, 0C ; 取下一字符串 0042BED2 cmp edi, 00423AB8 ; ASCII "ork, we dream......" 0042BED8 jl short 0042BE87 ; 循环结束?
建立名为"PCHMPROM"的内存映像删除已存在的主程序解压缩数据并写入到新建立的主程序中执行新建立的文件并等待退出
五、分析主程序:登堂入室
然后再从头开始分析主程序PocketCHM_Pro.exe:
0046BB10 >push ebp ; 程序入口 0046BB11 mov ebp, esp 0046BB13 push -1 0046BB15 push 004875B8 0046BB1A push <jmp.&MSVCRT._except_handler3> ; SE 处理程序安装 0046BC34 push eax 0046BC35 push esi 0046BC36 push ebx 0046BC37 push ebx ; /pModule 0046BC38 call dword ptr [<&KERNEL32.GetModuleHandleA>] ; \GetModuleHandleA 0046BC3E push eax 0046BC3F call 0046BECA ; 后面就退出了,这里跟进 0046BC44 mov dword ptr [ebp-68], eax 0046BC47 push eax ; /status 0046BC48 call dword ptr [<&MSVCRT.exit>] ; \exit 在0046BC3F |. >call 0046BECA处跟进: 0046BECA /$ >push dword ptr [esp+10] 0046BECE |. >push dword ptr [esp+10] 0046BED2 |. >push dword ptr [esp+10] 0046BED6 |. >push dword ptr [esp+10] 0046BEDA |. >call <jmp.&MFC42.#1576_AfxWinMain> ; 先跟进MFC里看看吧 0046BEDF \. >retn 10 73D3CF2B >mov edi, edi 73D3CF2D push ebx 73D3CF2E push esi 73D3CF2F push edi 73D3CF30 or ebx, FFFFFFFF 73D3CF33 call #1175_AfxGetThread 73D3CF38 mov esi, eax 73D3CF3A call #1168_AfxGetModuleState 73D3CF3F push dword ptr [esp+1C] 73D3CF43 mov edi, dword ptr [eax+4] 73D3CF46 push dword ptr [esp+1C] 73D3CF4A push dword ptr [esp+1C] 73D3CF4E push dword ptr [esp+1C] 73D3CF52 call #1575_AfxWinInit 73D3CF57 test eax, eax 73D3CF59 je short 73D3CF97 73D3CF5B test edi, edi 73D3CF5D je short 73D3CF6D 73D3CF5F mov eax, dword ptr [edi] 73D3CF61 mov ecx, edi 73D3CF63 call dword ptr [eax+8C] ; <jmp.&MFC42.#3922_CWinApp::InitApplication> 73D3CF69 test eax, eax 73D3CF6B je short 73D3CF97 73D3CF6D mov eax, dword ptr [esi] 73D3CF6F mov ecx, esi 73D3CF71 call dword ptr [eax+58] ; PocketCH.0042682D这里又跳回程序了 0042686B push eax 0042686C call 0042E7F7 ; GetUserNameA读取计算机用户名 00426871 push eax 00426872 lea ecx, dword ptr [esi+D5C] 00426878 mov byte ptr [ebp-4], 1 0042687C call <jmp.&MFC42.#858_CString::operator=> 00426881 and byte ptr [ebp-4], 0 00426885 lea ecx, dword ptr [ebp-14] 00426888 call <jmp.&MFC42.#800_CString::~CString> 0042688D lea eax, dword ptr [ebp-14] 00426890 mov ecx, ebx 00426892 push eax 00426893 call 0042E775 ; GetComputerNameA读取计算机名 00426898 push eax 00426899 lea ecx, dword ptr [esi+D60] 0042689F mov byte ptr [ebp-4], 2 004268A3 call <jmp.&MFC42.#858_CString::operator=> 004268A8 and byte ptr [ebp-4], 0 004268AC lea ecx, dword ptr [ebp-14] 004268AF call <jmp.&MFC42.#800_CString::~CString> 004268B4 mov ecx, esi 004268B6 call 0042920B ; 注册算法过程,想研究算法的可以跟进 004268BB mov edi, 004A9DD8 004268C0 mov ecx, edi 004268C2 call 0045CF1D ; 读注册表存的日期,并回写当前日期 004268C7 mov ecx, edi 004268C9 call 0045D6A1 ; 减出试用剩下的秒数 004268CE mov eax, dword ptr [4A9DF4] 004268D3 xor edx, edx 004268D5 mov ecx, 15180 ; 86400 004268DA div ecx ; EAX=试用剩下的天数 004268DC mov ecx, ebx 004268DE mov dword ptr [esi+D2C], eax 004268E4 lea eax, dword ptr [ebp-44] 004268E7 push eax 004268E8 call 0042CE7F ; GetSystemMetrics 004268ED mov ecx, dword ptr [eax] 004268EF mov dword ptr [esi+D20], ecx 004268F5 mov ecx, esi 004268F7 mov eax, dword ptr [eax+4] 004268FA mov dword ptr [esi+D24], eax 00426900 call <jmp.&MFC42.#2621_CWinApp::Enable3dControls> 00426905 push dword ptr [4AA104] 0042690B lea ecx, dword ptr [ebp-24] 0042690E call <jmp.&MFC42.#860_CString::operator=> 00426913 lea ecx, dword ptr [ebp-24] 00426916 call <jmp.&MFC42.#4204_CString::MakeUpper> 0042691B push 004A6460 ; ASCII "PCHMPROM" 00426920 lea ecx, dword ptr [ebp-30] 00426923 call <jmp.&MFC42.#537_CString::CString> 00426928 push ecx 00426929 lea eax, dword ptr [ebp-30] 0042692C mov ecx, esp 0042692E mov dword ptr [ebp-20], esp 00426931 push eax 00426932 mov byte ptr [ebp-4], 3 00426936 call <jmp.&MFC42.#535_CString::CString> 0042693B mov ecx, esi 0042693D call 004276DE ; 用CreateFileMapping读取引导程序的数据 00426942 mov ecx, esi 00426944 call 004290CA ; 如果前面读取失败这里会异常退出
004268B6 call 0042920B; 注册算法过程,想研究算法的可以跟进
004268C2 call 0045CF1D; 读注册表存的日期,并回写当前日期
004268C9 call 0045D6A1; 减出试用剩下的秒数
0042693D call 004276DE; 用CreateFileMapping读取引导程序的数据
其中注册算法过程太烦人了,研究了一下,放弃了;读写注册表和算出试用期很简单,略过;重点看看是如何读取引导程序数据的:
…… 00427711 push 96000 ; 映像大小=96000 00427716 mov ecx, eax 00427718 push dword ptr [ebp+8] ; 映像名="PCHMPROM" 0042771B call 00469E4C ; 读取映像数据 …… 0046A00B push -1 0046A00D mov ecx, eax 0046A00F call <jmp.&MFC42.#4167_CSingleLock::Lock> ; 锁定数据 0046A014 push dword ptr [ebp+8] ; /MapName 0046A017 mov eax, dword ptr [esi+28] ; | 0046A01A neg eax ; | 0046A01C push dword ptr [ebp+C] ; |MaximumSizeLow 0046A01F lea ecx, dword ptr [esi+40] ; | 0046A022 sbb eax, eax ; | 0046A024 push edi ; |MaximumSizeHigh 0046A025 and eax, ecx ; | 0046A027 push ebx ; |Protection 0046A028 push eax ; |pSecurity 0046A029 push -1 ; |hFile = FFFFFFFF 0046A02B call dword ptr [<&KERNEL32.CreateFileMappingA>] ; \建立映像 0046A031 mov edi, dword ptr [<&KERNEL32.GetLastError>] ; ntdll.RtlGetLastWin32Error 0046A037 mov dword ptr [esi+24], eax 0046A03A call edi ; [GetLastError 0046A03C cmp eax, 0B7 ; 映像已经存在? 0046A041 jnz short 0046A047 0046A043 and dword ptr [esi+14], 0 ; 映像已经存在时置标志位 0046A047 mov eax, dword ptr [esi+24] 0046A04A xor ecx, ecx 0046A04C cmp eax, ecx 0046A04E je 0046A0F9 0046A054 push ecx ; /MapSize => 0 0046A055 push ecx ; |OffsetLow => 0 0046A056 push ecx ; |OffsetHigh => 0 0046A057 push 6 ; |AccessMode = 6 0046A059 push eax ; |hMapObject 0046A05A call dword ptr [<&KERNEL32.MapViewOfFile>] ; \读取映像 0046A060 mov edx, eax 0046A062 test edx, edx 0046A064 mov dword ptr [esi+20], edx 0046A067 je 0046A0F9 0046A06D cmp dword ptr [esi+14], 0 ; 映像是否存在? 0046A071 je short 0046A098 0046A073 mov ecx, dword ptr [ebp+C] ; 映像不存在时: 0046A076 mov edi, edx 0046A078 mov edx, ecx 0046A07A xor eax, eax 0046A07C shr ecx, 2 0046A07F rep stos dword ptr es:[edi] ; 把映像数据全部置为0 0046A081 mov ecx, edx 0046A083 push ebx 0046A084 and ecx, 3 0046A087 rep stos byte ptr es:[edi] 0046A089 lea eax, dword ptr [esi+1C] 0046A08C mov ecx, edx 0046A08E push eax 0046A08F mov dword ptr [eax], ecx 0046A091 mov eax, dword ptr [esi+20] 0046A094 add eax, ebx 0046A096 jmp short 0046A0A0 0046A098 add edx, 4 0046A09B push ebx 0046A09C push edx 0046A09D lea eax, dword ptr [esi+1C] 0046A0A0 push eax ; |dest 0046A0A1 call <jmp.&MSVCRT.memcpy> ; \读映像的4-7字节
分析完主程序可以发现,程序从引导程序创建的名为"PCHMPROM"的内存映像中读取数据,如果映像不存在程序就拒绝执行。那么转单进程的方法就是把引导程序创建的内存映像作为一个区段添加到主程序中,在主程序读取映像时直接指向这个区段,即可完全转单进程。
具体方法为:
1.在OD创建好内存映像后,把内存映像另存为mem.bin文件,用PE工具打开主程序PocketCHM_Pro.exe,我这里用的工具是Stud_PE国庆版,选择“新建区段”,区段名为“.mem”,原始大小和虚拟大小都设为96000,选择“区段来自文件”并指定刚才另存出来的内存映像文件mem.bin,保存退出。
2.用OD打开已添加区段的主程序,按CTRL+G,填入46A014来到建立内存映像的位置,修改以下代码:
0046A014 B8 00505800 mov eax, 00585000 ; .mem区段的首地址 0046A019 8366 14 00 and dword ptr [esi+14], 0 ; 置标志位 0046A01D EB 41 jmp short 0046A060 ; 跳往建立完映像后的位置继续执行
七、尾声:暴破
程序的注册算法有点复杂,但是做完注册检测后只写了一个注册标志位,暴破起来就相当简单了:
找到注册算法过程中的这个位置并修改:
00429274 |. 8378 F8 0C cmp dword ptr [eax-8], 0C ; 判断用户名注册码的长度 00429278 33FF xor edi, edi ; //modify 0042927A 47 inc edi ; //modify 0042927B EB 06 jmp short 00429283 ; //modify 0042927D |. 8378 F8 18 cmp dword ptr [eax-8], 18 00429281 |. 74 0B je short 0042928E 00429283 |> 89BB 280D0000 mov dword ptr [ebx+D28], edi ; 置标志位,为1时已注册 00429289 |. E9 ED040000 jmp 0042977B
后记:
什么时候再静下心来分析一下算法吧。。。
用资源修改工具打开可以发现这个程序很多地方都有中文,是汉化?还是国产软件?迷惑ing...
by lelfei on 2008.4.29 14:30