Loading... # 如何快速定位关键点并爆破 什么是关键点:程序进行判断注册码是否正确的位置 什么是爆破:通过修改程序的流程,让程序判断失误 VM壳被称为代码层的最后一层防御。 --- > 理解简单程序验证流程: 1. 点击注册按钮 2. 读取读取注册码 `GetWindowTextA` 3. 判断注册码是否合法 4. 提示用户是否成功 `MessageBoxA` `scanf函数无法直接在VS中编译,由于有栈溢出漏洞,要编译需要在属性配置中关闭SDL检查。` 关键位置:jcc指令跳转到jmp下。 方法: > 交叉引用查找法IDA > > 调用堆栈--ALT+K 暂停法 > > 搜索字符串 > > 访问断点法 > > 自动跟踪法-控制台程序(什么地方卡住就在什么地方下断点F7跟踪,继续自动步过) > > Ctrl+G搜索MessageBoxA 在OD中快捷键Shift+F2打开**条件断点**!破解易语言程序,由于按钮时间常常被其他事件干扰,若以经常要用到条件断点。(但是汉化版可能会有bug,用原版英文即可) 条件断点--举例子:`[esp+4]!=00290642` 调试+自动步过的快捷键Ctrl+F8可以自动跟踪执行。 OD的插件(ImmLabel)Im.004011C1-->Edit Label可以给call函数打标签哦,不是注释。 减号键 可以退回到上面的指令显示 修改后右键可以撤销选择处修改,要保存可以右键保存到复制可执行文件--全部复制--右键保存文件 逆向破解随手搜索字符串 IDA如果搜索字符串的时候不支持中文则需要在快捷键加个参数空格`-dCULTURE=all` OD搜索不出的字符串可以用IDA找出并交叉引用跟踪到地址,记得交叉使用。 (PIE)aslr地址随机化:程序模块基地址OD中找E键:CFF Exlporer可以在可选头--DllCharacteristics--Click here-去掉DLL can move就不会随机改变了。在vs2017里项目属性--链接器--所有选项--随机基址也可以改成否。而且基址在PE结构中ImageBase是可以改的,写多少基址就是多少(因此以后PE加载不一定是在00401000,而是想在什么位置就在什么位置)。 OD为了打印程序调用的堆栈,调用了微软提供的**dbghelp.dll**,OD的字母Alt+K就是**调用堆栈**,通过这个方法往上走同样可以点位关键点,但是记得暂停程序,才能在调用堆栈中查看到还在堆栈没被释放的东西。 OD的字母Alt+M同样可以在内存中-->右键-->查找 字符串(经验所说有可能在提示框信息旁边放的就是注册码),内存断点也可以定位关键点,只要内存访问断点可以断,那么硬件访问断点就可以断。(但是有时候呢,OD的的访问断点可能会失效,这是很多插件影响导致的bug,x96dbg就不会) xdbg要想保存可以鼠标右键--补丁--修补文件 增量链接:快速编译功能,在vs中-属性-链接器-所有选项-启用增量链接可以修改。启用后会在DBG中生成很多的jmp。(在有些大程序可能用到很多函数,不能把所有函数打包到程序,所以使用链接的方式,用到再说) 自动跟踪法-控制台程序(什么地方卡住就在什么地方下断点F7跟踪,继续自动步过),OD的字母Alt+T是线程,有时候OD会假死就是线程被暂停住了,所以可以右键Resume All Threads修复,继续自动步过,卡住就F7步入,继续跟...。 在OD中如果分析出了call的作用,那么就可以使用插件(ImmLabel)Im.00463FB6--Edit Label取标签(其实就是给一个地址取了个别名)。其实他的原理就是用的OD打标签功能,即使进入call第一行右键--标签(逆VM常用)。其实这个插件的原理也非常的简单,就是双击call复制后面call的地址,Ctrl+G追踪跳转到那个地址,第一行右键--标签,这是OD自带的功能,只不过插件更方便,优化掉了我们追踪的这一步,直接可以打标签。 --- # 破解补丁工具的使用 OD中搜索字符串的原理: > 该插件是由一个吾爱的大佬开发的叫ustrref的插件,论坛可以下到源码分析。 > > 原理是取得显示在当前反汇编窗口中内存块区域地址,也即是整个PE文件的内存地址,在OD中按Ctrl+E即可查看到范围。循环读取每行的指令,将读取到的当前行指令进行反汇编,循环判断,寻找push mov lea指令,对操作数进行读取,判断操作数是否以\0结尾,如果是的话就判定为一段字符串(像结构体里面的字符串有的时候就会找不到,因为插件读到前面的00就会结束了)。这就是该插件的设计缺陷。 IDA中搜索字符串的原理: > 总所周知,IDA是一款静态反汇编工具,是直接查找PE文件中能读取到的字符串,有点类似于strings吧。 以上就是有的字符串OD搜不到但是IDA却能搜到的原因,那有没什么方式能让两种方式都搜不到呢 > //这段的代码能正常执行输出字符串“注册失败”,但是无法搜到了。可以在VS下断然后右键转到反汇编串口,看汇编的代码发现都是传值16进制。but但是放在全局变量里面这个方法就失效了。又能找到。 > > #include <stdio.h> > > int main() > { > char str[] = {D7, A2, B2, E1, CA, A7, B0, DC, 00};//注册失败 > printf(str); > } > >  OD中分析代码的功能:看到local.1是局部变量1,参数1是【arg-1】,可以从右键--分析--删除模块分析就是显示ebp的加减,右键--分析--分析代码就是。  1. 记住线程的特征? > (易语言)push过后马上call后马上pop--可能就是线程,要找到从哪里启动的线程就要右键--查找参考--选定命令--双击**绿色**的行就能找到启动线程的call > >  > > 实例代码: > >  > > OD找到从哪里启动的线程 > > 如果易语言多线程编译不成功请在--工具--》支持库配置--》全选确认!!! > >  2. IDA里保存后的exe文件? > **IDA-->选项-->常规-->操作码字节数(16)才能显示操作码字节,因为IDA是不会自动nop填充的所以需要一个个字节码改* > > 首先如果在OD中找到了关键跳,那么在IDA中需要快速定位到地址要按键盘g,输入地址--OK > **选中地址Edit--Patch program--Assemble--修改后OK;再选中地址Edit--Patch program--Patched bytes(或者/修补程序应用到输入文件/)** 3. 为什么要使用破解补丁工具? > 总结就是牛逼,所以使用 4. 怎么让IDA和OD都搜不到字符串? > OD搜索字符串的插件源码是寻找push mov lea指令--对操作数读取--判断操作数是否以\0结尾。所以当字符串前有\0是就搜不到了。 > IDA是通过PE文件静态搜索字符串的,所以有时用记事本也可以看到 > 可以在将字符串改变为十六进制的数保存在局部变量数组中,这样就搜不到了,编译时候保存到了代码段;但是全局变量还是能搜到,这是因为数据被编译到了.data节。 5. 加壳后修改数据在保存时OD报错在可执行文件中无法定位数据。 > 此时破解补丁工具就派上用场了。远程读取进程内存**WriteProcessMemoy**。 > > 例如:XH 补丁制作工具1.3.6,PYG内存破解补丁制作工具,PYGDLL内存破解补丁制作工具。DLL劫持就是程序**就近加载原则**需要的dll文件名,有时是必须要用HOOKAPI的(比如程序加壳了,程序还没执行的时候关键位置的硬编码都是00,如果此时修改为90,当壳把代码吐出来的时候又会把原来改的给覆盖掉成程序原本应该的硬编码,因此HOOKAPI的作用,就可以检测原始代码是否正[原理埋坑,后面再讲])。 > --- # 编写第一个破解补丁 1. 为什么吧字符数组放在全局变量里面OD又能找到? · 原来是编译器它把我们的数组以字符串的形式写到了.data段里面,放在局部变量里面就被编译在了.text段。 直接跟踪main函数的新方法:**OD调试选项(调试设置--地址)记得勾选--解码修饰符号名称** > 一个Debug的x86程序放入OD:跟踪call ConsoleA.__scrt_common_main --> call ConsoleA.__scrt_common_main_seh --> 往下找ConsoleA.invoke_main --> call ConsoleA.main --> 这就跟踪到了我们的main函数 OD调试选项记得勾选--解码修饰符号名称 2. 思考,实现dll劫持补丁,万一调用到真正的dll函数的功能怎么办? > 为什么我的破解补丁能用,但是dll劫持做出的补丁不能用啊,一样的数据? > (NCK老师说曾经应用这个技术破解过Dnguard和MaxtoCode,这里埋坑,以后学到来填坑) 解答:实现dll劫持补丁时,万一用到真正的dll需要使用API转发,对加载的DLL动手脚,需要什么还给它转发出去。`Loadlibrary` `GetProProcess` 3. 认识壳 > 就是把原始代码加密存储在PE文件中,在程序运行时候再一个字节一个字节的吐出来到原始的位置。才能正常执行。 > 4. 编写第一个内存破解补丁 ``` // 内存破解补丁.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。 // #include <windows.h> #include <iostream> int main() { int pId; printf("请输入要破解的程序进程ID:"); scanf("%d", &pId); byte buff[] = { 0x90, 0x90, 0x90, 0x90, 0x90, 0x90 }; HANDLE hHandle=OpenProcess(PROCESS_ALL_ACCESS, TRUE, pId); WriteProcessMemory(hHandle, (LPVOID)0x004010FD, buff, 6, NULL); } ``` `OpenProcess`打开一个进程,返回一个进程句柄 `WriteProcessMemory`将数据写入指定进程中的内存区域。要写入的整个区域必须可访问,否则操作将失败。 使用VS编译时,属性--关闭SDL检查,就不会函数报错了,就不用添加预处理器了。 易语言写内存破解补丁需要将填入的数据都转成十进制。 5. 编写第一个dll劫持补丁 > 易语言在编写dll劫持补丁的时候如果遇到加壳程序,可能会造成时机不对(就是壳还没把代码吐出来就改了,后面吐出来就又覆盖了前面的修改,所以等于没改)---可以使用--(创建线程)延迟技术、HOOK API、判断解密是否完成。 > 此劫持补丁代码远不止这些,运用到了易语言的神器精益模块,可以去网上下载模块或源码,该模块已经帮助我们封装好了所有的常用功能,如果不想调用它的模块还课可以直接复制他的源码到你自己的源码里实现该功能,避免重复造轮子。 C语言的劫持补丁 主要部分代码如下,采用的是winspool.drv(Windows的打印客户端 (winspool.drv)把打印的APl暴露给用户应用程序, 用户应用程序用打印API来查询打印机、打印任务、改变打印机设置、查询打印机设置、加载打印机驱动程序用户界面DLL来显示打印机具体设置属性页和做一些其他的事情。Windows的打印客户端 (winspool.drv)帮助GDI决定打印任务应该如何处理。对于一般的打印任务,GDI生成EMF文件并把它送到打印池客户,然后打印客户用远程进程调用把打印任务发送给打印系统服务进程) [Windows打印体系结构之Print Spooler概念与架构 - 特洛伊-Micro - 博客园 (cnblogs.com)](https://www.cnblogs.com/micro-chen/p/6015739.html) [打印管理库函数Winspool.drv - 邓春光 - 博客园 (cnblogs.com)](https://www.cnblogs.com/deng02/archive/2012/06/21/2558204.html) 前面后面还有大量代码没有写出: ```C DWORD WINAPI ThreadProc(_In_ LPVOID lpParameter) { byte byteRead = 0; while (1) { ReadProcessMemory((HANDLE)-1,(LPVOID)0x004010A9,&byteRead,1,NULL); if (byteRead==0x55) { byte writeBytes[] = {0x90,0x90, 0x90, 0x90, 0x90, 0x90}; WriteProcessMemory((HANDLE)-1, (LPVOID)0x004010FD, writeBytes, 6, NULL); break; } } MessageBoxA(NULL,"unique-elven","Elven",NULL); return TRUE; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // 入口函数 BOOL WINAPI DllMain(HMODULE hModule, DWORD dwReason, PVOID pvReserved) { if (dwReason == DLL_PROCESS_ATTACH) { //::MessageBoxA(0, "今天的作业来了\n\n根据我提供的劫持补丁模板\n\n给此模板增加代码,实现劫持补丁\n\n", "恭喜你领到作业一份", 0); CreateThread(NULL,NULL,ThreadProc,NULL,NULL,NULL); DisableThreadLibraryCalls(hModule); return Load(); } else if (dwReason == DLL_PROCESS_DETACH) { Free(); } return TRUE; } ``` .NET程序(采用C#,面向对象)很容易就会被工具dnSpy给反编译出源码来,所以一般都会加壳保护。 --- # 不修改代码实现破解01 1. 如何调试自己写的dll文件??? > VS在属性中--》调试--》命令--》添加调用到这个文件的exe程序路径 > > 这样就能开始通过先执行exe再调用我们的dll进行愉快的调试啦. > 补充: DLL劫持的其他用途(劫持注入技术 - 过游戏保护) 其实在同一个进程里的地址空间,直接指针访问就行,不建议使用读内存API,因为这样可以防止别人偷补丁数据的时候直接下断点这个API,就什么都知道了。 ``` 待补充上一节课遗留,这一课编写的C代码的内存破解补丁。 ``` 硬件HOOK用途强大: > 国外强壳(ILProtector)通杀脱壳演示 > > 游戏辅助 (经典的过游戏检测技术之一) > > 反调试 2. 什么是线程??? (进程就是4GB, 线程就是EIP)(进程是死的是不会执行的,必须要有线程)(宿主,寄宿者) **进程32位4G,64位16EG**。 ST0~7浮点型寄存器,X87FPU。埋坑! 线程上下文CONTEXT。(因为CPU给线程分配时间片的特性,切换作用) ```C int main() { CONTEXT ctx;//监视这个变量就可以看到所有的寄存器的值,这就是线程上下文,这样线程才知道下一步往哪里跑 GetThreadContext(GetCurrentThread(),&ctx);//读取线程上下文 system("pause"); } `` ``` 3. 科普--初识异常之VEH **初识异常之VEH**--`AddVectoredExceptionHandler`,以下代码在vs中会卡住是因为异常处理被vs接管了,而用OD来执行却能正常执行是因为OD有反反调试插件。 优先级高于seh,高于try catch ```C #include <stdio.h> #include <windows.h> LONG CALLBACK VectoredHandler(PEXCEPTION_POINTERS pExceptionInfo) { printf("hello world"); printf("%08x", pExceptionInfo->ContextRecord->Eip); return EXCEPTION_EXECUTE_HANDLER; } int main() { //注册了一个异常处理,当后面的代码发生异常,就会进入这个函数进行处理 AddVectoredContinueHandler(NULL, VectoredHandler); __asm int 3; system("pause"); } ``` OD的**硬件断点**的原理:调试寄存器DR7是最大的老大(DR7配置DR0--DR的属性,不管他们有没有值,都由DR7说了算)的,到程序地址执行到硬件断点地址就会送入DR寄存器判断,如果存在该地址就会出发异常,OD就会接管异常,程序就停下了。 OD的F2下的断点是把改内存地址的数据改为CC,称为**CC断点**=====这在OD内存中可能看着没变,但其实它就是有变cc,可以使用CE修改器附加被调试程序的进程,添加地址就能看到字节数据十六进制cc. 4. 了解crc检测 **crc检测**: 用途,防破解 实例: 校验总和 写一个简单的防CC断点,示例代码,原理就是检测这0x00401000+0x3000一段内存是否有改变,由于OD断点的原理会导致字节变成CC,所以检测到断点就会退出。如下: ``` #include <iostream> #include <windows.h> int gTotal = 0; DWORD WINAPI ThreadProc(_In_ LPVOID lpParameter) { while (true) { int address = 0x00401000; int byteToal = 0; for (int Index = 0; Index < 0x3000; Index++) { byte byteRead = 0; ReadProcessMemory((HANDLE)-1, (LPVOID)address, &byteRead, 1, NULL); byteToal += byteRead; address++; } if (byteToal != gTotal) { exit(0); } } return TRUE; } int main() { int address = 0x00401000; for (int Index = 0; Index < 0x3000; Index++) { byte byteRead = 0; ReadProcessMemory((HANDLE)-1, (LPVOID)address, &byteRead, 1, NULL); gTotal += byteRead; address++; } CreateThread(NULL, NULL, ThreadProc, NULL, NULL, NULL); while (1) { Sleep(1500); printf("%d 正常执行中....\n",i); i++; } //由于直接pause导致调试效果不明显,所以加上上面的while system("pause"); return 0; } ``` --- # 不修改代码实现破解2 `AddVectoredExceptionHandler`函数用于注册异常,只要产生异常就会进入异常处理。 硬件断点的原理(通过对地址调试寄存器DR写值构造异常中断,然后调试器接管) --- 1. 公开课内容补充,DLL劫持补丁的生成(就是上节课C代码里没我没展示的一堆导入导出函数) 使用工具AheadLib.exe可以预生成dll劫持代码额,然后再在vs建立dll项目调试。 (但是像有些系统dll是不能劫持的,如ntdll) 出现C1010报错遇到意外预编译头#include "stdafx.h",可以在属性--C/C++--预编译头--不使用预编译头。 出现E1067实参与形参的类型不兼容,因为vs默认的是unicode字节集,所以在属性--常规--使用多字节符集。 最后源文件只能留下一个包含DLLMain函数的文件,在入口函数DLLMain编写函数。 工具--代码片段管理器--导入--画眉专用代码模板yyds-------比番茄助手还nb,模板如下,以后自己做内容替换即可,用好以后就是纯中文编程: ```C <?xml version="1.0" encoding="utf-8"?> <CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet"> <CodeSnippet Format="1.0.0"> <Header> <Title>信息框</Title> <Shortcut>信息框</Shortcut> <Description>信息框</Description> <Author>Microsoft Corporation</Author> <SnippetTypes> <SnippetType>Expansion</SnippetType> <SnippetType>SurroundsWith</SnippetType> </SnippetTypes> </Header> <Snippet> <Code Language="cpp"><![CDATA[::MessageBox(NULL, "内容", "标题", NULL);]]> </Code> </Snippet> </CodeSnippet> </CodeSnippets> ``` 2. 作业讲解 > 加了壳的程序不能拖进去就搜索字符串,必须在全局内存中找到代码段才能进去搜,不然是搜不出来的。 > > 遇上CRC检测的程序,请在Ctrl+T的线程查看中暂停掉线程。挂起! > > 遇上有个输入框的Windows程序,可以在setWindowsTextA的API函数下断点。 > 最后修改:2024 年 12 月 14 日 © 允许规范转载 打赏 赞赏作者 支付宝微信 赞 3 如果觉得我的文章对你有用,请随意赞赏
1 条评论
对生命本质的追问充满哲学思辨。