本帖最后由 lhy9443 于 2019-3-10 08:08 编辑
0 更新记录
2019/3/10 更新附件地址。本人由于忙于学业,6月8日前恐怕再无机会接触电脑。今天偷得半日闲,发现分享链接早已失效,故重发。
另最近在研究另一款软件,既有进展又有瓶颈,预计6月8日后开始整理,之后不久就会发新贴,到时还请各位多多讨论,共同进步。
2018/10/1 更新补丁地址的定位方式,不再采用硬编码(即固定偏移量)的形式,改用GetProcAddress定位。(感谢 苏紫方璇 大大提出)
删除“2.6.2.4 补丁代码”一节,请自行查看源代码。
更新附件。“补丁功能分析”一节添加思维导图。
1 简介
最近在研究一款64位软件,琢磨许久后终于成功破解,现写此贴以记录成果。
2 破解2.1 探究软件需要破解的地方
软件是一款象棋引擎,使用时总有Nag(弹窗、弹网页),如图所示。
2.2 查壳
软件为64位,VMP壳。
2.3 确定破解思路
考虑到VMP的代码虚拟化功能,很难从程序代码入手,因而想到从系统代码入手。
弹窗的API为MessageBoxA/W,弹网页的API是ShellExecuteA/W(有可能不是,但总要先试试)。
2.4 调试
用x64dbg,加载好反反调试插件,启动程序,在上述API下断并触发它,成功断下:
将两个API的段首改为retn,从而移除Nag。
经测试,Nag成功被移除。
2.5 保存修改
由于是VMP,很自然的想到了补丁。但对于此软件却有几处问题:
2.5.1 现有的补丁工具大部分无法使用- 樱花补丁已经过时,故不予讨论。
- XH补丁只支持x86软件,经测试无法使用。
- PYG的exe补丁只支持x86软件,而dll劫持补丁则支持x64位,值得一试。
2.5.2 PYG的dll劫持补丁x64不满足使用要求
虽然经测试,该补丁确实可行,但是只能由其专用的启动工具启动目标文件后,对其进行补丁。而该软件需要由象棋界面启动以保持通信。也就是说,必须让界面启动象棋引擎后,对象棋引擎的进程进行内存补丁,才能达到效果。该功能只有XH补丁才能实现,故不满足要求。
2.5.3 解决方案:自行编写内存补丁
既然没有现成的工具可用,那只好自行编写程序,对目标进行补丁了。
2.6 自制内存补丁
2.6.1 选择语言
易语言只支持编译32位软件,故不考虑,使用以Visual Studio作为IDE的C++编译x64位内存补丁。
2.6.2 补丁功能分析&代码实现
该补丁需要实现的功能如下:- 根据进程名获得其PID
- 根据PID获得其对应的模块基址。MessageBoxA属于USER32.dll,ShellExecuteA属于SHELL32.dll,即需要获得这两个dll的基址,加上偏移量来确定补丁的地址
- 根据得到的进程PID、补丁址和补丁命令,对目标代码进行补丁
2.6.2.1 根据进程名获得其PID
需要引用头文件<tlhelp32.h>。
CreateToolhelp32Snapshot(DWORD dwFlags, DWORD th32ProcessId)
当参数dwFlags为TH32CS_SNAPPROCESS时,能够给当前系统的所有进程拍一个快照,并返回该快照的句柄。该快照中存有进程名和与其对应的PID。此时th32ProcessId被忽略。 Process32FirstW(HANDLE hSnapshot, LPPROCESSENTRY32W lppe)
Process32NextW(HANDLE hSnapshot, LPPROCESSENTRY32W lppe)
为这两个函数提供快照句柄和一个PROCESSENTRY32类型变量的地址,可以将快照里的每一个进程信息赋给形参变量lppe。随后,使用lppe.sz32ExeFile返回当前进程名(WCHAR),使用lppe.th32ProcessID返回当前进程PID(DWORD)。使用Process32FirstW来获取第一个进程的信息,使用Process32NextW获取相对于当前进程的下一个进程的信息。如此,使用do-while循环便可遍历进程名,使用wcscmp函数对返回的进程名进行比较,若找到则进行下一步的操作。注意:在使用这两个函数之前,必须执行lppe.dwSize = sizeof(PROCESSENTRY32),否则将出错。
2.6.2.2 根据PID获得其对应的模块基址
CreateToolhelp32Snapshot(DWORD dwFlags, DWORD th32ProcessId)
当参数dwFlags为TH32CS_SNAPMODULE时,能够给以th32ProcessId作为PID所指定的进程所调用的模块拍一个快照,并返回该快照的句柄。该快照中存有模块名和与其对应的基址。
Module32FirstW(HANDLE hSnapshot, LPMODULEENTRY32W lpme)
Module32NextW(HANDLE hSnapshot, LPMODULEENTRY32W lpme)
这两个函数与Process32FirstW/NextW的使用方法类似,也需要执行lpme.dwSize = sizeof(MODULEENTRY32)。不同的是,使用这两个函数后,使用lpme.szModule返回当前模块名(WCHAR),使用lpme.hModule返回当前模块基址(HMODULE),使用unsigned long long进行转换即可。 2.6.2.3 对目标代码进行补丁
OpenProcess( _In_ DWORD dwDesiredAccess, _In_ BOOL bInheritHandle, _In_ DWORD dwProcessId)
该函数用来打开一个已存在的进程对象,并返回进程的句柄。该句柄将作为下一个命令的参数。dwDesiredAccess 渴望得到的访问权限(标志),此处使用PROCESS_ALL_ACCESS,bInheritHandle 是否继承句柄,此处填写false,dwProcessId 进程标示符,即PID,使用上述提到的lppe.th32ProcessID即可。
GtProcAddress(_In_ HMODULE hModule, _In_ LPCSTR lpProcName); 此函数获得指定模块中的函数对应的地址,模块句柄由LoadLibrary给出。用这个API能计算偏移量。
WriteProcessMemory(_In_ HANDLE hProcess, _In_ LPVOID lpBaseAddress, _In_reads_bytes_(nSize) LPCVOID lpBuffer, _In_ SIZE_T nSize, _Out_opt_ SIZE_T* lpNumberOfBytesWritten)
此函数能写入某一进程的内存区域,故需此函数入口区必须可以访问,否则操作将失败。
hProcess 由OpenProcess返回的进程句柄。
lpBaseAddress 要写的内存首地址。在写入之前,此函数将先检查目标地址是否可用,并能容纳待写入的数据。此处填入lpme.hModule得到的基址加上偏移量即为需要补丁的地方。
lpBuffer 指向要写的数据的指针。这里使用一个变量data储存补丁数据,然后填入&data即可。
nSize 要写入的字节数。此处填1,因为retn的机器码为C3,只有1个字节。 3 总结 本程序的破解有以下特点:- 不修改程序代码,而修改系统代码,避开了VMP的干扰
- 自写程序完成内存补丁
通过本次的破解之旅,我学习到了不同的破解方法,也学会了如何在没有现成的补丁制作工具时自行制作内存补丁。也许这些对大牛来说都是小菜一碟,但对我来说,这些都是一些很好的经验,本人从中受益匪浅,故写下此帖记录。当然也希望大牛们能制作出64位的内存补丁制作工具,这样在日后就可以更方便地爆破64位程序了。本次破解用时将近2个星期,时间主要都花费在C++上了,因为对C++的语言不甚熟悉,为了WIN API的使用又花费了大量的时间去查阅资料,因而锻炼了我的C++编程能力和WIN API使用能力。此帖作为52破解的新手第一帖,也就到此结束。欢迎各位提出评论,我有时间的话就会去查看的。
4 附件
附件包括源代码(sln文件,请用Visual Studio打开)、补丁成品,已经打包为1个文件,需要学习的朋友可自行下载。补丁使用方法是用象棋界面(附件不提供)加载引擎后执行补丁,成功则会看到11,补丁完成后按任意键退出。
附件地址(百度网盘)链接: https://pan.baidu.com/s/1fALTQjTkVaXadwWNeoaDgA 密码: 2s1h |