本帖最后由 星空~夜影 于 2024-03-18 22:19 编辑
一、软件介绍 众所周知,Windows系统自带的截图、录频功能很难用,这么多年了一直没改进。截图在聊天软件中基本都自带了,做得很好而且可以免费使用,这倒不需担心。 但屏幕录制功能大多需要付费,例如WPS截图功能免费,但录频要开通会员才能使用。本文将讲解一款专业录频软件的VIP验证过程,在不充值的情况下,每次只能 录制1分钟且带有水印,几乎等于没法用,就是想让你充钱!在开通会员之后才能解锁全部功能,设置选项里内容丰富,适合用来录频。因为软件带了VMP壳, 不方便修改原可执行文件,可以制作内存补丁来绕过VIP验证。 友情提示:阅读本文需要有X64汇编基础,了解x64dbg调试器的使用方法。
二、查壳+分析目录结构 ExeInfo 查壳显示 *** x64 Unknown Packer-Protector , 10 sections - CRC Set - , Overlay : Signed Die 查壳显示 保护器: VMProtect(2.XX-3.XX) ,签名工具: Windows Authenticode(2.0)[PKCS #7]
由此确定本程序使用VMP加壳,还有两点可以证明: (1)对程序添加补丁后保存打开时出现错误弹窗,这段内容是VMP的特有标记,并不是系统自带的出错提示。 "File corrupted!. This program has been manipulated and maybeit's infected by a Virus or cracked. This file won't work anymore." (2)先把调试器的各种隐藏插件关掉,然后运行此软件出现错误弹窗。 "A debugger has been found running in your system.Please,unload it from memory" 安装目录中发现: (1)sciter.dll , UIBase.dll,说明界面是用Web方式开发的,UIBase中的一些GUI函数尤其值得注意,可以下API断点帮助分析。 (2)log4z.dll, LogServer.dll 说明程序有写入日志的行为,因为调用此类函数时要传入字符串,所以可以从明文中获取一些线索。 (3)libMediaCam64.dll, libFFmpeg64.dll , libcurl.dll 等大量多媒体和网络库,推测业务逻辑是用C++写的,调用这些开源库。
接下来开始调试,以管理员身份运行x64dbg,开启HyperHide插件所有选项,等程序运行后再附加上去。不要使用调试器直接运行程序,因为程序启动时 会执行VMP壳的检测代码,在函数头添加软件断点之类的操作会被发现,导致报错退出。通过后面的分析,我们知道程序并没有对关键验证代码进行混淆, 加密的代码段反而是无关紧要的,说明软件开发者不太会用VMP。存在的干扰项只有VMP自带的反调试和防止修改文件。反调试用插件绕过,防修改用 内存补丁绕过,技术难度不高。经测试,ScyllaHide插件不行,会导致程序操作后卡住。建议使用虚拟机安装Win10系统在上面调试。
接下来有三种方法分析:字符串搜索、暂停法、API断点法。(后两个方法暂时先不写,看看评论区反馈如何)
三、字符串搜索 记录关键字符串:“登录”、“注册”、“试用”、“VIP”、“开通”、“特权”,依次搜索,为了节省文章篇幅,我就不一个个演示了。 有些人不知道如何在指定模块中搜索字符串,这里说一下。如果搜索所有用户模块太费时间,之前分析知道程序使用了很多开源库,这里面的字符串对我们没用。 附加进程后CPU页面停留在ntdll模块,系统模块的字符串我们也不关心。我们要在符号页面搜索模块名,右键在反汇编中转到,然后在新页面中右键搜索,如下图所示: 大部分字符串是界面初始化时加载的内容,并没有关联跳转和比较,对分析没有帮助。看到一处关键提示:“抱歉,您还不是VIP用户.......”,说明软件中有对应功能, 点击后如果不是VIP则弹窗。究竟是哪个功能呢?一番尝试后发现,设置页面中的图片水印功能会触发此弹窗,如下图所示: 在所有"*不是VIP*"处下断点,回到软件设置图片水印,但没有触发任何中断。看来这些字符串也是初始化操作,并不是实际会执行到的代码片段。 字符串下面有个call调用,进入到此函数里面下断点有意外发现。当设置图片水印时,软件中断三次,最后一次中断后会弹窗,中断时分析RIP周围代码没有任何线索。 上面几个函数在UIBase.dll中,功能是设置窗口位置、模态、显示等,如果执行到此处代码就说明用户不是VIP,程序准备显示弹窗。 所以向下找exe模块的函数,跳转到此处,如下图: 先把之前下的断点全部删除,转而在“不是VIP弹窗”的call处下断点,看看程序是如何进入到这一步的。当设置图片水印时,软件只中断一次,说明位置找得很准确! 如下图,在这两处下断点,发现程序没有进行跳转,顺着往下执行,有一处关键比较,即 cmp eax, 4 , eax的值是上面函数的返回值。如果等于4就跳转。 此时我没有登录,eax值为1。当我登录后,eax值为2。这就很有意思了,上面那个函数返回的是个枚举变量,和用户信息密切相关,把这个函数叫做疑似验证函数。 进入疑似验证函数,如下图,解读出以下信息: (1)rdx 作为参数转递局部变量的地址,即 rsp+20 (2)调用真正的验证函数,此函数会改写 rsp+20 的内容 (3)eax 的值就是 rsp+20 的内容(枚举用户状态),判断是否登录 不再深入验证函数了,看看还有哪些地方会调用验证函数,右键函数头地址,查找引用->选定的地址,发现有这么多地方调用了验证函数。到每一处去看看! 很多调用处都会把函数返回值eax与4做比较,我猜测4代表VIP身份的枚举量。修改验证函数头,直接返回4。继续运行,再次设置一遍,发现可以使用了! 屏幕录制时也不会出现最开始的弹窗,说明修改成功! 四、修改方案 代码地址为 hirecmaster.exe+2EA60,把函数头修改为 mov eax, 4 ret
能不能使用补丁保存为新的可执行文件? 答案是不行! VMP壳启动时会校验文件的完整性,如果发现有修改就报错退出,所以可以编写代码制作内存补丁, 等程序初始化完成后再修改代码段。我给出了参考代码,有些地方写得不完善,可能还要改一下。 #include <windows.h> #include <psapi.h> #include <stdio.h> #define HeaderBytesCount 6 int main() { STARTUPINFO si = {0}; PROCESS_INFORMATION pi = {0}; si.cb = sizeof(si); // Start the process. if( !CreateProcess(L"C:/Program Files (x86)/Auntec/HiRecMasterLY/HiRecMaster.exe", // No module name (use command line) NULL, // Command line NULL, // Process handle not inheritable NULL, // Thread handle not inheritable FALSE, // Set handle inheritance to FALSE 0, NULL, // Use parent's environment block NULL, // Use parent's starting directory &si, // Pointer to STARTUPINFO structure &pi ) // Pointer to PROCESS_INFORMATION structure ) { printf( "CreateProcess failed (%lu).\n", GetLastError() ); return 1; } // Wait the program init Sleep(1000); printf( "PID=%lu\n", pi.dwProcessId ); SuspendThread(pi.hThread); // Find EXE module HMODULE hMods[1024]; DWORD cbNeeded; void *lpBase = NULL; if(EnumProcessModulesEx(pi.hProcess, hMods, sizeof(hMods), &cbNeeded, LIST_MODULES_64BIT)) { printf("module: %llu\n", cbNeeded / sizeof(HMODULE)); TCHAR szModName[MAX_PATH]; MODULEINFO modInfo; for (int i = 0; i < (cbNeeded / sizeof(HMODULE)); i++ ) { // Get the full path to the module's file. if (GetModuleBaseName(pi.hProcess, hMods[i], szModName, sizeof(szModName) / sizeof(TCHAR))) { if (wcscmp(szModName, L"HiRecMaster.exe") == 0) { if (GetModuleInformation(pi.hProcess, hMods[i], &modInfo, sizeof(modInfo))) { lpBase = modInfo.lpBaseOfDll; } break; } } } } // Find the verify function if (lpBase) { void *lpFunction = (char*)lpBase + 0x2EA60; printf("lpBase: %p \n lpFunction: %p \n", lpBase, lpFunction); char header[HeaderBytesCount] = {0}; char target[HeaderBytesCount] = {0x40, 0x53, 0x48, 0x83, 0xEC, 0x40}; char replace[HeaderBytesCount] = {0xB8, 0x04, 0x00, 0x00, 0x00, 0xC3}; SIZE_T cbReaded; SIZE_T cbWritten; if (ReadProcessMemory(pi.hProcess, lpFunction, header, sizeof(header), &cbReaded) && cbReaded == HeaderBytesCount) { if (memcmp(header, target, HeaderBytesCount) == 0) { printf("find target \n"); if (WriteProcessMemory(pi.hProcess, lpFunction, replace, HeaderBytesCount, &cbWritten) && cbWritten == HeaderBytesCount) { printf("replaced \n"); } } } } ResumeThread(pi.hThread); CloseHandle( pi.hProcess ); CloseHandle( pi.hThread ); system("pause"); return 0; }
注:若转载请注明大神论坛来源(本贴地址)与作者信息。
|