研究对象: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
其中0041FCC6处的call 0045D658为加密用户名注册码并保存到注册表的过程,分别将长度信息、用户名和密码加密后保存在HKCU\Software\Microsoft\Basic\Library\Utility中的nd、fx和xf中,然后重启验证注册码是否正确。
得知程序的弹出窗口都是用的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
程序是用CreateProcessA来运行新进程,并用GetExitCodeProcess来监控程序退出,执行到这里时已经可以直接双击目录中的PocketCHM_Pro.exe来打开主程序了。
这里又有一个问题:是什么时候开始可以直接执行主程序的呢?再次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 ; 跳往建立完映像后的位置继续执行
3.直接F9测试一下,程序果然跑起来了!用16进制工具把修改的数据写到程序中。
七、尾声:暴破
程序的注册算法有点复杂,但是做完注册检测后只写了一个注册标志位,暴破起来就相当简单了:
找到注册算法过程中的这个位置并修改:
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
用16进制工具把标为modify的地方改好后试运行一下,主界面上的“Unregistered copy. Available trial days:30”已经没有了,菜单中的注册项也没有了,再测试一下编译CHM文件和“Read CHM on Your PDA”这些有限制的功能,没发现暗桩。至此已大功告成!
后记:
什么时候再静下心来分析一下算法吧。。。
用资源修改工具打开可以发现这个程序很多地方都有中文,是汉化?还是国产软件?迷惑ing...
by lelfei on 2008.4.29 14:30
[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界