使用OllyDbg从零开始Cracking 第四十六章-Patrick的CrackMe-Part1
第四十六章-Patrick的CrackMe-Part1本章我们继续加深难度,实验程序名称为Patrick.exe,该程序的保护较强。大家会发现之前介绍的一些方法针对于该保护并不奏效。所有很多时候我们不得不适当变通,特定的保护特定解。这里要提一句,一些商业壳不可能将所有的保护手段都运用到,因为商业壳的宗旨是要保证目标程序为任意软件。但是恰恰有些保护手段只针对于特定的程序。好了,这里我们打开OD,还记得之前介绍那款修改过的Patched 4这款OD吧,就它了,配置好反反调试插件。这个CrackMe,作者的要求不仅仅是脱壳,还必须将DLL剥离,让EXE单独正常运行。那么就是说Patrick主程序目录下的AntiDebugDll.dll这是一个核心DLL,如果将这个DLL删除掉的话,主程序将无法正常运行。好了,现在我们用Patched4这款OD加载Patrick.exe。我们可以看到还没有到达入口点,程序就终止了。好,下面我们来尝试将入口点修改为系统断点处,系统断点会在到达主程序入口点之前断下。打开主菜单中的Debugging options-Events,选中System breakpoint。这样就可以让程序首次中断在系统断点处,这里要提一句,有些DLL会在到达入口点之前被加载,而这些DLL会检测主程序是否正在被调试,如果正在被调试的话就立即结束进程。有一点必须明确,就是系统动态库并不会对OD进行检测。下面我们在数据窗口中单击鼠标右键选择Goto Expression,输入400000定位到PE头。接着在数据窗口中单击鼠标右键选择Special-PE Header,切换到PE结构解析模式。往下拉。这里我们可以看到Import Table(缩写:IT,俗称:导入表)的RVA(相对虚拟地址)(PS:不要将Import Table与IAT搞混淆了,IAT是Import Address Table(输入函数地址表)的缩写,嘿嘿).这里6F3C + 映像基地址(400000)就可以定位到Import Table了。将数据窗口的显示模式由PE解析模式切换回十六进制模式。不知道大家是否还记得导入表的格式,每个DLL项占5个DWORD。(PS:关于PE结构不了解的童鞋,可以参看传说中的小黄书,你懂得!嘿嘿 Windows PE权威指南)每个DLL项中的第4个DWORD指向了DLL的名称字符串,那么40712E这个地址就指向了第一个DLL的名称字符串,我们一起来看一看。这里我们可以看到第一个DLL为WINMM.DLL,我们假设在到达入口点之前如果该DLL被执行了的话,OD就会终止。(PS:这是个系统DLL,并不会进行反调试的处理,这里只是举个例子)我们来一探究竟,首先OD断在系统断点处。断在了这里,此时该CrackMe还没有到达入口点处,因为当某个DLL加载并执行以后就会终止进程,而当前进程并没有终止。也就是说此时存在反调试的DLL还没有得到执行,我们单击工具栏中的M按钮打开区段列表窗口,对该DLL的代码段设置内存访问断点。这里断点就设置好了,我们直接运行起来。我们看到断了下来,这里是由于读取76B0D8B8地址处的内容导致的中断。我们继续运行,直到触发内存执行断点为止。断在了这里,这里就要开始执行该DLL的代码了。跟踪反调试DLL的流程就是这样的,接下来,我们重启OD,再次重复一遍上面的步骤,这次我们直接对AntiDebugdll.dll的代码段设置内存访问断点。我们运行起来,会发现首次就触发了执行断点。在运行之前我们首先删除掉之前设置的内存访问断点:这里提示一下该程序既不是检测OD的进程名,也是不是检测OD窗口名,也不是HideOD插件中的涉及的那些检测方法,它是检测该进程是由谁创建的,通过调用Process32Next等API函数来遍历进程,判断当前进程的父进程是否为桌面进程来达到反调试的目的。也就说该CrackMe会判断当前进程是否由explorer.exe启动,如果是的话,那么就说明是用户双击运行的。如果不是的话,那么就说明正在被调试,直接终止进程。这里我们给Process32Next这个API函数设置一个断点。接着运行起来。断了下来,这里可以看到第二个参数pProcessentry的值为12EEC8。这里我们用PUPE这个小工具查看一下进程的PID。这里我们可以看到第一个进程的PID为零,我们结合MSDN来看。这里我们可以看到关于Process32Next这个API函数的说明。我们看下关于PROCESSENTRY32这个结构体的解释。这里我们可以看到第三个字段为th32ProcessID即进程PID,第七个字段为th32ParentProcessID即父进程ID。这里我用粉红色标注出了PID,绿色标注出了PPID。这里我们可以看到当前遍历到的这个进程的PID(进程ID)和PPID(父进程ID)都为零,代表是这个进程,这个进程并不是我们要定位的,我们直接运行起来。 嘿嘿,程序终止了。怎么这样就终止了呢?难道这个系统进程有问题?不太可能吧,那么会不会是HideOD插件冲突了的原因呢?我们去掉HideOD中Process32Next这一项的对勾试试。现在我们重启OD,这里我们不需要再给该DLL的代码段设置内存访问断点了,我们直接给Process32Next下断。断了下来,我们可以看到PID和PPID都为零。这里就是刚刚出问题的地方,如果勾选了HideOD上的Process32Next这个选项的话,当该函数执行完毕,EAX等于零,说明出错了,程序就直接退出了。这里我们不勾选这一项,EAX非零。函数执行成功,这样程序就不会直接退出了。我们运行起来,这次程序并没有退出,这里遍历到了进程快照中的第二个进程。即SYSTEM进程,PID为4,我用粉红色标注出来了。PPID为0,我用绿色标注出来了。其实我们还可以对删除掉Process32Next入口处的断点,将断点下在该函数的返回处。我们继续运行直到出现Patrick.exe为止。这里我们可以看到此时定位到了Patrick.exe这个进程。PID为0x0AE4,PPID为0x8AC,这里我们对比着PUPE里面的进程信息来看。我们单击PUPE中的Actualizar(刷新)按钮。Patrick.exe的PID为0xAE4,而Parcheado4.exe(Patched 4)的PID为0x8AC。也就是说Patrick.exe的父进程并非explorer.exe,而是OD。所以Patrick.exe会直接退出。这里我们直接对0x8AC这4个字节设置内存访问断点,看什么地方会获取该PPID。我们运行起来,断在了这里。这里我们可以看到该指令将PPID读取出来存放到了另外一个地方。我们按F7单步,可以看到PPID被保存到了12EEB8地址处。我们对12EEB8地址处的内容设置硬件访问断点,来定位何处会获取PPID的值。我们运行起来,会发现PPID最初保存的地址处的值已经被覆盖掉了。我们删除掉之前设置的内存访问断点。再次运行,断了下来,这里就是进行比较的地方。这个将parcheado4(Patched 4)的PID与另一个PID 0xC74进行比较。正如所料,是explorer.exe的PID。这里我们可以看到,判断Patrick.exe父进程的PID是否与explorer.exe的PID相同,相同的话,继续往下运行,不同的话则调用ExitProcess退出进程。这是其中一处反调试,如果我们将此处修改并保存到文件的话,那么如果还其他其他反调试的话,程序还是无法正常运行。所以这里我们不进行修改,我们删除掉之前设置的硬件访问断点。给进行比较的这一行指令设置硬件执行断点。当程序断在这里的时候,我们可以手动修改Patrick.exe父进程的PID,将其修改为explorer.exe的PID值。更加方便的方法是断在这一行的时候修改EAX的值。现在我们删除之前设置的Process32Next这个函数的断点。重启OD。断在了这里,12EEB8地址处保存的PID为0x8AC。而另一个与之比较的PID为0xC7C。这里我们将12EEB8地址处的值修改为0xC7C。这样两个PID就相等了,程序也就不会直接退出了。我们按F7键单步。这样第一处反调试就绕过了。我们继续。这里往下跟一点,就可以看到该CrackMe会获取PID为0xC7C进程(即explorer.exe)的模块快照。进而检查模块的一些字段信息,用于判断explorer是不是重命名过的。我们继续往下跟,就会到达Module32First函数调用处。这个API函数可以配合Module32Next这个API函数来遍历进程模块信息。我们来看看MSDN中的关于该函数的说明。这里模块信息依次是:MODULEENTRY32结构大小,模块标示符,进程ID,全局模块引用计数,所在进程范围内模块引用计数,模块基地址,模块大小,模块句柄,模块名称,模块全路径,预留标志。这里我们可以看到模块基地址为0x10000000。这里我们可以看到关于模块的相关信息,我们来一起看看通过该CrackMe通过模块信息会干什么事情。往下跟一点就可以看到读取0xC7C与模块信息中的进程PID进行比较。判断该模块是不是explorer.exe。我们继续往下跟。这里我们可以看到调用GetWindowsDirectory这个API函数获取Windows的目录,也就是explorer.exe所在目录。也就是说该CrackMe会检测父进程的路劲。这里我们可以看到Windows所在目录字符串将要保存的这个缓冲区中。这里我们可以看到保存到了这里。下面就进行比较路径是否一致了。这里路径是一致的,我们继续跟。我们继续往下,可以看到该程序将C:\WINDOWS与explorer.exe进行字符串拼接。这里就得到了explorer.exe的全路径。好了,我们来看看接下来要干什么。下面将开始与快照中的进程路径进行比较。这里我们到了CharUpperBuffA这个API函数的调用处。这里是将目标缓冲区中的字符串由小写转大写。这里没看出什么特别的,就是将遍历到的进程全路径小写转大写。然后通过GetWindowsDirectory获取windows目录,接着与explorer.exe进行字符串连接,然后进行比较。这里我们就到了字符串比较函数处,这里待比较的两个字符串明显是相等的,所以返回值会为零。我们单击回车键进去看看这个字符串比较函数的实现。这里我们就可以看到该字符串比较函数的实现了,我们单击减号键返回。我们直接按F8键执行这个字符串比较函数。这里我们可以看到EAX的值为零,下面的JE跳转就成立。继续往下跟,这里可以看到该CrackMe还要进行进一步的判断,继续遍历下一个模块。这里我们可以看到接下来这个模块是ntdll.dll。其模块标示符为0x01,PID为0xC7C,以及其他相关的模块信息。接下来继续进行比较,依次判断explorer.exe的所有模块。这里再次到了GetWindowsDirectoryA的调用处。我们继续往下。这里又是获取Windows的所在目录,重复前面的比较步骤,这里我们就不再赘述了,继续跟踪。我们再次到了字符串比较函数处。这里两个字符串明显不相等。EAX返回1,说明比较的两个字符串不相等。下面的跳转不成立。下面的代码是经过混淆了的。(PS:也就是常说的花指令)这里我们可以看到,首先将一个常量赋值给EAX,然后与另一个常量进行比较,这里由于两个常量并不相等,所有下面JE条件跳转永远不会成立。接下来又是将一个常量赋值给EAX,大家注意到了我标红了的地址没?紧接着下面通过JMP NEAR EAX来跳转的刚刚标红的常量所表示地址处。这里可以看到这部分代码被混淆了,我们如何看到定位其真正要执行的代码处呢?我们选中标红了地址,单击鼠标右键选择Follow immediate constant(跟随立即数)。这样我们将能够不执行混淆过的代码,直接定位到实际的功能代码了。这里我们将就可以看到其实际要执行的代码了。下一个MOV指令处同样通过这样方式来查看。我们再次定位到了实际代码处,通过这样方式就可以不执行混淆过的代码直接定位实际代码了,比较方便。我们继续跟踪。这里我们可以看到拼接字符串得到C:\WINDOWS\SYSTEM32\NTDLL.DLL,我们继续。这里再次看到了explorer.exe这个名称。下面就将EXPLORER.EXE的全路径与NTDLL.DLL的全路径进行比较。两者并不相等,并且会跳过了中间的ExitProcess,继续。我们看到这里继续遍历其他模块。说实话这部分我没看出有多大意义。我们继续耐心跟吧。我们可以看到继续一个模块一个模块的遍历,都与EXPLORER.EXE的全路径进行比较。只有首次比较是相等的,其余的比较都不相等。那么为了解决时间,我们直接给进行字符串比较的CALL处设置硬件执行断点。这里我们可以通过PUPE查看explorer.exe进程的模块列表信息对照的看。我们只需要定位最后一个模块即可。我这里最后一个模块是idle.dll。不符合的我们直接放过。还没有到,我们再继续。这里我们到了最后一个DLL了,我们继续跟踪看看会发生什么。这里依然跳过了ExitProcess,再次调用Module32Next。我们可以看到这里ExitProcess又被跳过了。继续往下跟。这里调用GetModuleFileNameA。这里就得到了该CrackMe的全路径。接下来就到了CreateMutexA的调用处,该函数用于创建互斥体,可用于防止多个实例运行。(PS:最简单的防多开)这里是该函数的参数。这里我们直接按F8执行该函数就得到了句柄50,OD中LastErr(PS:也就是调用GetLastError得到的值)的返回值为ERROR_SUCCESS,也就是创建互斥体成功。我们单击工具栏中的H按钮查看句柄列表窗口,可以看到其中有两个互斥体的句柄,一个叫做MYFIRSTINSTANCE,另一个叫做WAIT。MYFIRSTINSTANCE这个互斥体是之前创建的,这里我就不跟踪了。接下来调用RtlGetLastWin32Error这个函数获取API函数执行的错误码,这里我们可以对照着OD来看。这里EAX的值为0,也就是OD中显示的LastErr的值。这里我们可以看到这个WAIT互斥体是第一次创建,所以跳过了ExitProcess,如果WAIT不是第一次创建的话,那么就会调用ExitProcess结束进程。这样就可以防止多个程序实例运行。继续。这里可以看到该程序将调用CreateProcessA创建第二个进程。这里我们可以看到该函数的参数,如果我们按F8键执行该函数的话,那么第二个进程将会被创建,但是WAIT互斥体已经存在了,所以被创建的进程会检测是否存在WAIT这个互斥体,显示这个互斥体是存在的,所有程序将调用ExitProcess退出。这里如果我们按F8键创建该进程的话,就算被创建了,该进程也会立即结束掉,所以这里我们可以尝试将进程挂起,我们可以通过修改CreateFlags这个参数将进程挂起,这里我们将CreateFlags的值修改0x4。我们来看看会发生什么。这里我们按F8键执行该函数,EAX返回的是0,也就是说创建进程失败,这是为什么呢?好,我们直接给CreateProcessA这个函数调用处设置硬件执行断点,来看看为什么创建进程失败。也就是说此时有两个硬件访问断点,一个是父进程PID与explorer.exePID进行比较处,另一个就是该CreateProcessA的调用处。这里有可能是HideOD插件的影响,可能是该插件某些与权限相关的选项导致不能创建其他进程,我们尝试去掉这些选项。这里去掉上图中的这些与权限有关的选项以后,接着将CreatetionFlags标志修改为0x4。我们按F8键创建进程,我们可以看到EAX的值为1,说明创建进程成功,证明我们的猜想是正确的。好,现在我们再将HideOD的反反调试选项都勾选上。我们用PUPE查看一下进程。这里我们可以看到有两个patrick.exe,第二个patrick.exe是挂起的,嘿嘿。现在有个比较棘手的问题,就是如何附加以及中断新创建的进程呢?只有附加了新的进程,我们才能进一步跟踪新进程中是如何检测多开的。我们可以通过以下方式:重启OD,其断在系统断点处,我们给AntiDebugDll.dll这个模块的代码段设置内存访问断点,我们运行起来,就会断在该DLL的入口点处。现在我们需要寻找一个点,这个点需要满足如下条件:新进程被创建,AntiDebugDll.dll的代码还未执行。我们可以选一个调用AntiDebugDll.dll入口点之前的地址,然后让其一直在该地址处循环,不往下执行。我们来看一看此时的堆栈情况。这里我们可以看到返回地址为7C9111A7,我们定位到该返回地址处。这里7C9111A4这个CALL就是跳往AntiDebugDll.dll的入口点,当前程序的领空属于NTDLL.DLL,也就是说当新创建的进程由挂起状态恢复为运行状态后,就会运行到该CALL处,我们要想办法让其在这里一直循环而不进去。这里我们需要做的就是替换7C9111A4地址处的原始字节,该地址处的原始字节为FF55。好了思路就是这样的啊,我们之前的两个硬件断点还没有删除吧?我们删除掉内存访问断点,接着直接运行起来,断在了PID的比较处。这里我们将父进程PID修改为explorer.exe的PID,继续运行随即就断在了CreateProcessA的调用处。这里我们将CreateFlags的值修改为0x4。按F8键,这样新进程就创建了。这里我们可以在PUPE中看到这两个进程。这里我们选择新创建的进程进行Patch,我们选中新创建的进程,单击鼠标右键选择Parchear(Patch)按钮。这里我们将7C9111A4地址处的前两个字节修改为EB FE。这里我们填写上7C9111A4(PS:注意你本机的地址),单击Buscar(获取原始字节)按钮,接着填写上要替换的字节码EBFE,最后单击PARCHEAR(Patch)按钮。这样就Patch完毕了。现在我们需要将该进程由挂起状态恢复到运行状态。下面我要用到Estricnina_v0.12这款工具。这里我们定位到新创建的进程,单击鼠标右键选择Info Threads。单击Reanudar(Resume)按钮。这样挂起的线程就被恢复了,并且一直在7C9111A4地址处循环。接下来我们来附加该进程。首先我们打开任务管理器,将显示PID的列勾选上。这里将任务管理器的PID选项勾选上,显示的是10进制,我们转换为16进制即可,这样我们就定位到新创建的进程了。接下来我们将OD设置为即时调试器(JIT),我们选择新创建的Patrick.exe这个进程单击鼠标右键选择Depurar(调试)。这样OD的即时调试功能就生效了。这里我们可以看到OD中是空白的,但是并没有发生警告,不用担心,我们直接单击工具栏中的T按钮查看下线程。这里我们可以看到有两个线程,其中一个是由于我们Patch的循环引起的,我们任选一个双击,如果不是循环,那么就证明是另一个。我们可以看到该处处于循环中。我们给7C9111A4这一行设置一个断点,接着将该地址处的前两个字节恢复为原始字节FF55.这样我们将附加成功了,我们可以查看下模块列表窗口。此时并没有模块列表中并没有出现AntiDebugDll.dll这个模块,我们会断在7C9111A4处,如果还是没有出现AntiDebugDll.dll,我继续运行,直到AntiDebugDll.dll这个模块为止。现在我们可以看到模块列表中出现了AntiDebugDll.dll,好,现在我们对其代码段设置内存访问断点。现在删除掉7C9111A4处的断点,运行起来。这里我们就断在了AntiDebugDll.dll的入口点处了。下面我们给CreateMutexA设置一个断点。运行起来。这里我们可以看到将要创建的互斥体为MYFIRSTINSTANCE,这里由于该互斥体已经存在了,所以我们直接运行到函数返回处。 这里我们可以看到LastErr显示为0xB7,即ERROR_ALREADY_EXISTS,表示互斥体已经存在。接下来这里就调用GetLastError这个API函数获取错误码,判断错误码是否为0xB7。如果为0xB7的话表示互斥体已经存在,该进程是并非第一次创建,如果不为0xB7的话,就表示该进程是首次创建。这里错误码为0xB7,表示该进程并非首次创建。好了,本章就这里。本系列文章汉化版转载看雪论坛
感谢原作者:RicardoNarvaja(西班牙人) 原作者个人主页:http://www.ricardonarvaja.info/
感谢热心翻译的朋友:1~3章译者:BGCoder4~58章译者:安于此生
全集配套程序下载地址:链接: http://pan.baidu.com/s/1eQzTWfo 密码: vytv
老大你好,检查过 全集配套程序下载地址 都已经失效 [吾爱汇编论坛52HB.COM]-感谢楼主分享,支持一下! [吾爱汇编论坛52HB.COM]-支持一下,希望楼主做的更好,加油!感谢楼主分享,支持一下!!做的不错哦,楼主加油,期待更好的作品! 好东西,谢谢分享,收藏了{:5_116:}支持一下! 多谢分享 大佬牛批6666 感谢发布原创作品,吾爱破解论坛因你更精彩! 好好学习下看看!
页:
[1]
2