收藏 分销(赏)

脱壳的艺术.doc

上传人:s4****5z 文档编号:8741384 上传时间:2025-02-28 格式:DOC 页数:38 大小:693.50KB
下载 相关 举报
脱壳的艺术.doc_第1页
第1页 / 共38页
脱壳的艺术.doc_第2页
第2页 / 共38页
点击查看更多>>
资源描述
脱壳的艺术 Mark Vincent Yason 概述:脱壳是门艺术——脱壳既是一种心理挑战,同时也是逆向领域最为激动人心的智力游戏之一。为了甄别或解决非常难的反逆向技巧,逆向分析人员有时不得不了解操作系统的一些底层知识,聪明和耐心也是成功脱壳的关键。这个挑战既牵涉到壳的创建者,也牵涉到那些决心躲过这些保护的脱壳者。 本文主要目的是介绍壳常用的反逆向技术,同时也探讨了可以用来躲过或禁用这些保护的技术及公开可用的工具。这些信息将使研究人员特别是恶意代码分析人员在分析加壳的恶意代码时能识别出这些技术,当这些反逆向技术阻碍其成功分析时能决定下一步的动作。第二个目的,这里介绍的信息也会被那些计划在软件中添加一些保护措施用来减缓逆向分析人员分析其受保护代码的速度的研究人员用到。当然没有什么能使一个熟练的、消息灵通的、坚定的逆向分析人员止步的。 关键词:逆向工程、壳、保护、反调试、反逆向 1简介 在逆向工程领域,壳是最有趣的谜题之一。在解谜的过程中,逆向分析人员会获得许多关于系统底层、逆向技巧等知识。 壳(这个术语在本文中既指压缩壳也包括加密壳)是用来防止程序被分析的。它们被商业软件合法地用于防止信息披露、篡改及盗版。可惜恶意软件也基于同样的理由在使用壳,只不过动机不良。 由于大量恶意软件存在加壳现象,研究人员和恶意代码分析人员为了分析代码,开始学习脱壳的技巧。但是随着时间的推移,为防止逆向分析人员分析受保护的程序并成功脱壳,新的反逆向技术也被不断地添加到壳中。并且战斗还在继续,新的反逆向技术被开发的同时逆向分析人员也在针锋相对地发掘技巧、研究技术并开发工具来对付它们。 本文主要关注于介绍壳所使用的反逆向技术,同时也探讨了躲过/禁用这些保护措施的工具及技术。可能有些壳通过抓取进程映像(dump)能够轻易被搞定,这时处理反逆向技术似乎没有必要,但是有些情况下加密壳的代码需要加以跟踪和分析,例如: 需要躲过部分加密壳代码以便抓取进程映像、让输入表重建工具正确地工作。 深入分析加密壳代码以便在一个反病毒产品中整合进脱壳支持。 此外,当反逆向技术被恶意程序直接应用,以防止跟踪并分析其恶意行为时,熟悉反逆向技术也是很有价值的。 本文绝不是一个完整的反逆向技术的清单,因为它只涵盖了壳中常用的、有趣的一些技术。建议读者参阅最后一节的链接和图书资料,以了解更多其他逆向及反逆向的技术。 笔者希望您觉得这些材料有用,并能应用其中的技术。脱壳快乐! 2 调试器检测技术 本节列出了壳用来确定进程是否被调试或者系统内是否有调试器正在运行的技术。这些调试器检测技术既有非常简单(明显)的检查,也有涉及到native APIs和内核对象的。 2.1 PEB.BeingDebugged Flag : IsDebuggerPresent() 最基本的调试器检测技术就是检测进程环境块(PEB)1中的BeingDebugged标志。kernel32!IsDebuggerPresent() API检查这个标志以确定进程是否正在被用户模式的调试器调试。 下面显示了IsDebuggerPresent() API的实现代码。首先访问线程环境块(TEB)2得到PEB的地址,然后检查PEB偏移0x02位置的BeingDebugged标志。 mov eax, large fs: 18h mov eax, [eax+30h] movzx eax, byte ptr [eax+2] retn 除了直接调用IsDebuggerPresent(),有些壳会手工检查PEB中的BeingDebugged标志以防逆向分析人员在这个API上设置断点或打补丁。 示例 下面是调用IsDebuggerPresent() API和使用PEB.BeingDebugged标志确定调试器是否存在的示例代码。 ;call kernel32!IsDebuggerPresent() call [IsDebuggerPresent] test eax,eax jnz .debugger_found ;check PEB.BeingDebugged directly Mov eax,dword [fs:0x30] ;EAX = TEB.ProcessEnvironmentBlock movzx eax,byte [eax+0x02] ;AL = PEB.BeingDebugged test eax,eax jnz .debugger_found 由于这些检查很明显,壳一般都会用后面章节将会讨论的垃圾代码或者反—反编译技术进行混淆。 对策 人工将PEB.BeingDebugged标志置0可轻易躲过这个检测。在数据窗口中Ctrl+G(前往表达式)输入fs:[30],可以在OllyDbg中查看PEB数据。 另外Ollyscript命令"dbh"可以补丁这个标志。 dbh 最后,Olly Advanced3 插件有置BeingDebugged标志为0的选项。 2.2 PEB.NtGlobalFlag , Heap.HeapFlags, Heap.ForceFlags PEB.NtGlobalFlag PEB另一个成员被称作NtGlobalFlag(偏移0x68),壳也通过它来检测程序是否用调试器加载。通常程序没有被调试时,NtGlobalFlag成员值为0,如果进程被调试这个成员通常值为0x70(代表下述标志被设置): FLG_HEAP_ENABLE_TAIL_CHECK(0X10) FLG_HEAP_ENABLE_FREE_CHECK(0X20) FLG_HEAP_VALIDATE_PARAMETERS(0X40) 这些标志是在ntdll!LdrpInitializeExecutionOptions()里设置的。请注意PEB.NtGlobalFlag的默认值可以通过gflags.exe工具或者在注册表以下位置创建条目来修改: HKLM\Software\Microsoft\Windows Nt\CurrentVersion\Image File Execution Options Heap Flags 由于NtGlobalFlag标志的设置,堆也会打开几个标志,这个变化可以在ntdll!RtlCreateHeap()里观测到。通常情况下为进程创建的第一个堆会将其Flags和ForceFlags4分别设为0x02(HEAP_GROWABLE)和0 。然而当进程被调试时,这两个标志通常被设为0x50000062(取决于NtGlobalFlag)和0x40000060(等于Flags AND 0x6001007D)。默认情况下当一个被调试的进程创建堆时下列附加的堆标志将被设置: HEAP_TAIL_CHECKING_ENABLED(0X20) HEAP_FREE_CHECKING_ENABLED(0X40) 示例 下面的示例代码检查PEB.NtGlobalFlag是否等于0,为进程创建的第一个堆是否设置了附加标志(PEB.ProcessHeap): ;ebx = PEB Mov ebx,[fs:0x30] ;Check if PEB.NtGlobalFlag != 0 Cmp dword [ebx+0x68],0 jne .debugger_found ;eax = PEB.ProcessHeap Mov eax,[ebx+0x18] ;Check PEB.ProcessHeap.Flags Cmp dword [eax+0x0c],2 jne .debugger_found ;Check PEB.ProcessHeap.ForceFlags Cmp dword [eax+0x10],0 jne .debugger_found 对策 可以将 PEB.NtGlobalFlag和PEB.HeapProcess标志补丁为进程未被调试时的相应值。下面是一个补丁上述标志的ollyscript示例: Var peb var patch_addr var process_heap //retrieve PEB via a hardcoded TEB address( first thread: 0x7ffde000) Mov peb,[7ffde000+30] //patch PEB.NtGlobalFlag Lea patch_addr,[peb+68] mov [patch_addr],0 //patch PEB.ProcessHeap.Flags/ForceFlags Mov process_heap,[peb+18] lea patch_addr,[process_heap+0c] mov [patch_addr],2 lea patch_addr,[process_heap+10] mov [patch_addr],0 同样地Olly Advanced插件有设置PEB.NtGlobalFlag和PEB.ProcessHeap的选项。 2.3 DebugPort: CheckRemoteDebuggerPresent()/NtQueryInformationProcess() Kernel32!CheckRemoteDebuggerPresent()是另一个可以用于确定是否有调试器被附加到进程的API。这个API内部调用了ntdll!NtQueryInformationProcess(),调用时ProcessInformationclass参数为ProcessDebugPort(7)。而NtQueryInformationProcess()检索内核结构EPROCESS5的DebugPort成员。非0的DebugPort成员意味着进程正在被用户模式的调试器调试。如果是这样的话,ProcessInformation 将被置为0xFFFFFFFF ,否则ProcessInformation 将被置为0。 Kernel32!CheckRemoteDebuggerPresent()接受2个参数,第1个参数是进程句柄,第2个参数是一个指向boolean变量的指针,如果进程被调试,该变量将包含TRUE返回值。 BOOL CheckRemoteDebuggerPresent( HANDLE hProcess, PBOOL pbDebuggerPresent ) ntdll!NtQueryInformationProcess()有5个参数。为了检测调试器的存在,需要将ProcessInformationclass参数设为ProcessDebugPort(7): NTSTATUS NTAPI NtQueryInformationProcess( HANDLE ProcessHandle, PROCESSINFOCLASS ProcessInformationClass, PVOID ProcessInformation, ULONG ProcessInformationLength, PULONG ReturnLength ) 示例 下面的例子显示了如何调用CheckRemoteDebuggerPresent()和NtQueryInformationProcess()来检测当前进程是否被调试: ; using Kernel32!CheckRemoteDebuggerPresent() lea eax,[.bDebuggerPresent] push eax ;pbDebuggerPresent push 0xffffffff ;hProcess call [CheckRemoteDebuggerPresent] cmp dword [.bDebuggerPresent],0 jne .debugger_found ; using ntdll!NtQueryInformationProcess(ProcessDebugPort) lea eax,[.dwReturnLen] push eax ;ReturnLength push 4 ;ProcessInformationLength lea eax,[.dwDebugPort] push eax ;ProcessInformation push ProcessDebugPort ;ProcessInformationClass(7) push 0xffffffff ;ProcessHandle call [NtQueryInformationProcess] cmp dword [.dwDebugPort],0 jne .debugger_found 对策 一种方法是在NtQueryInformationProcess()返回的地方设置断点,当这个断点被断下来后,将ProcessInformation 补丁为0。 下面是自动执行这个方法的ollyscript示例: var bp_NtQueryInformationProcess // set a breakpoint handler eob bp_handler_NtQueryInformationProcess // set a breakpoint where NtQueryInformationProcess returns gpa "NtQueryInformationProcess","ntdll.dll" find $RESULT,#C21400# //retn 14 mov bp_NtQueryInformationProcess,$RESULT bphws bp_NtQueryInformationProcess,"X" run bp_handler_NtQueryInformationProcess: //ProcessInformationClass == ProcessDebugPort? cmp [esp+8],7 jne bp_handler_NtQueryInformationProcess_continue //patch ProcessInformation to 0 mov patch_addr,[esp+c] mov [patch_addr],0 // clear breakpoint bphwc bp_NtQueryInformationProcess bp_handler_NtQueryInformationProcess_continue: run Olly Advanced插件有一个patch NtQueryInformationProcess()的选项,这个补丁涉及注入一段代码来操纵NtQueryInformationProcess()的返回值。 2.4 Debugger Interrupts 在调试器中步过INT3和INT1指令的时候,由于调试器通常会处理这些调试中断,所以异常处理例程默认情况下将不会被调用,Debugger Interrupts就利用了这个事实。这样壳可以在异常处理例程中设置标志,通过INT指令后如果这些标志没有被设置则意味着进程正在被调试。另外,kernel32!DebugBreak()内部是调用了INT3来实现的,有些壳也会使用这个API。 示例 这个例子在异常处理例程中设置EAX的值为0xFFFFFFFF(通过CONTEXT6记录)以此来判断异常处理例程是否被调用: ; set exception handler push .exeception_handler push dword [fs:0] mov [fs:0],esp ;reset flag(EAX) invoke int3 xor eax,eax int3 ;restore exception handler pop dword [fs:0] add esp,4 ; check if the flag had been set test eax,eax je .debugger_found ::: .exeception_handler: ;EAX = ContextRecord mov eax,[esp+0xc] ;set flag (ContextRecord.EAX) mov dword [eax+0xb0],0xffffffff ;set ContextRecord.EIP inc dword [eax+0xb8] xor eax,eax retn 对策 由于调试中断而导致执行停止时,在OllyDbg中识别出异常处理例程(通过视图->SEH链)并下断点,然后Shift+F9将调试中断/异常传递给异常处理例程,最终异常处理例程中的断点会断下来,这时就可以跟踪了。 另一个方法是允许调试中断自动地传递给异常处理例程。在OllyDbg中可以通过 选项-> 调试选项 -> 异常 -> 忽略下列异常 选项卡中钩选"INT3中断"和"单步中断"复选框来完成设置。 2.5 Timing Checks 当进程被调试时,调试器事件处理代码、步过指令等将占用CPU循环。如果相邻指令之间所花费的时间如果大大超出常规,就意味着进程很可能是在被调试,而壳正好利用了这一点。 示例 下面是一个简单的时间检查的例子。在某一段指令的前后用RDTSC指令(Read Time-Stamp Counter)并计算相应的增量。增量值0x200取决于两个RDTSC指令之间的代码执行量。 rdtsc mov ecx,eax mov ebx,edx ;...more instructions nop push eax pop eax nop ;...more instructions ;compute delta between RDTSC instructions rdtsc ;Check high order bits cmp edx,ebx ja .debugger_found ;Check low order bits sub eax,ecx cmp eax,0x200 ja .debugger_found 其它的时间检查手段包括使用kernel32!GetTickCount() API, 或者手工检查位于0x7FFE0000地址的SharedUserData7数据结构的TickCountLow 及TickCountMultiplier 成员。 使用垃圾代码或者其它混淆技术进行隐藏以后,这些时间检查手段尤其是使用RDTSC将会变得难于识别。 对策 一种方法就是找出时间检查代码的确切位置,避免步过这些代码。逆向分析人员可以在增量比较代码之前下断然后用 运行 代替 步过 直到断点断下来。另外也可以下GetTickCount()断点以确定这个API在什么地方被调用或者用来修改其返回值。 Olly Advanced采用另一种方法——它安装了一个内核模式驱动程序做以下工作: 1 设置控制寄存器CR48中的时间戳禁止位(TSD),当这个位被设置后如果RDTSC指令在非Ring0下执行将会触发一个通用保护异常(GP)。 2 中断描述表(IDT)被设置以挂钩GP异常并且RTDSC的执行被过滤。如果是由于RDTSC指令引发的GP,那么仅仅将前次调用返回的时间戳加1。 值得注意的是上面讨论的驱动可能会导致系统不稳定,应该始终在非生产机器或虚拟机中进行尝试。 2.6 SeDebugPrivilege 默认情况下进程是没有SeDebugPrivilege权限的。然而进程通过OllyDbg和WinDbg之类的调试器载入的时候,SeDebugPrivilege权限被启用了。这种情况是由于调试器本身会调整并启用SeDebugPrivilege权限,当被调试进程加载时SeDebugPrivilege权限也被继承了。 一些壳通过打开CSRSS.EXE进程间接地使用SeDebugPrivilege确定进程是否被调试。如果能够打开CSRSS.EXE意味着进程启用了SeDebugPrivilege权限,由此可以推断进程正在被调试。这个检查能起作用是因为CSRSS.EXE进程安全描述符只允许SYSTEM访问,但是一旦进程拥有了SeDebugPrivilege权限,就可以忽视安全描述符9而访问其它进程。注意默认情况下这一权限仅仅授予了Administrators组的成员。 示例 下面是SeDebugPrivilege检查的例子: ;query for the PID of CSRSS.EXE call [CsrGetProcessId] ;try to open the CSRSS.EXE process push eax push FALSE push PROCESS_QUERY_INFORMATION call [OpenProcess] ;if OpenProcess() was successful, ;process is probably being debugged test eax,eax jnz .debugger_found 这里使用了ntdll!CsrGetProcessId() API获取CSRSS.EXE的PID,但是壳也可能通过手工枚举进程来得到CSRSS.EXE的PID。如果OpenProcess()成功则意味着SeDebugPrivilege权限被启用,这也意味着进程很可能被调试。 对策 一种方法是在ntdll!NtOpenProcess()返回的地方设断点,一旦断下来后,如果传入的是CSRSS.EXE的PID则修改EAX值为0xC0000022(STATUS_ACCESS_DENIED)。 2.7 Parent Process(检测父进程) 通常进程的父进程是explorer.exe(双击执行的情况下),父进程不是explorer.exe说明程序是由另一个不同的应用程序打开的,这很可能就是程序被调试了。 下面是实现这种检查的一种方法: 1 通过TEB(TEB.ClientId)或者使用GetCurrentProcessId()来检索当前进程的PID 2 用Process32First/Next()得到所有进程的列表,注意explorer.exe的PID(通过PROCESSENTRY32.szExeFile)和通过PROCESSENTRY32.th32ParentProcessID获得的当前进程的父进程PID 3 如果父进程的PID不是explorer.exe的PID,则目标进程很可能被调试 但是请注意当通过命令行提示符或默认外壳非explorer.exe的情况下启动可执行程序时,这个调试器检查会引起误报。 对策 Olly Advanced提供的方法是让Process32Next()总是返回fail,这样壳的进程枚举代码将会失效,由于进程枚举失效PID检查将会被跳过。这些是通过补丁 kernel32!Process32NextW()的入口代码(将EAX值设为0然后直接返回)实现的。 77E8D1C2 > 33C0 xor eax, eax 77E8D1C4 C3 retn 77E8D1C5 83EC 0C sub esp, 0C 2.8 DebugObject: NtQueryObject() 除了识别进程是否被调试之外,其他的调试器检测技术牵涉到检查系统当中是否有调试器正在运行。 逆向论坛中讨论的一个有趣的方法就是检查DebugObject10类型内核对象的数量。这种方法之所以有效是因为每当一个应用程序被调试的时候,将会为调试对话在内核中创建一个DebugObject类型的对象。 DebugObject的数量可以通过ntdll!NtQueryObject()检索所有对象类型的信息而获得。NtQueryObject接受5个参数,为了查询所有的对象类型,ObjectHandle参数被设为NULL,ObjectInformationClass参数设为ObjectAllTypeInformation(3): NTSTATUS NTAPI NtQueryObject( HANDLE ObjectHandle, OBJECT_INFORMATION_CLASS ObjectInformationClass, PVOID ObjectInformation, ULONG Length, PULONG ResultLength ) 这个API返回一个OBJECT_ALL_INFORMATION结构,其中NumberOfObjectsTypes成员为所有的对象类型在ObjectTypeInformation数组中的计数: typedef struct _OBJECT_ALL_INFORMATION{ ULONG NumberOfObjectsTypes; OBJECT_TYPE_INFORMATION ObjectTypeInformation[1]; } 检测例程将遍历拥有如下结构的ObjectTypeInformation数组: typedef struct _OBJECT_TYPE_INFORMATION{ [00] UNICODE_STRING TypeName; [08] ULONG TotalNumberofHandles; [0C] ULONG TotalNumberofObjects; ...more fields... } TypeName成员与UNICODE字符串"DebugObject"比较,然后检查TotalNumberofObjects 或 TotalNumberofHandles 是否为非0值。 对策 与NtQueryInformationProcess()解决方法类似,在NtQueryObject()返回处设断点,然后补丁 返回的OBJECT_ALL_INFORMATION结构,另外NumberOfObjectsTypes成员可以置为0以防止壳遍历ObjectTypeInformation数组。可以通过创建一个类似于NtQueryInformationProcess()解决方法的ollyscript脚本来执行这个操作。 类似地,Olly Advanced插件向NtQueryObject() API中注入代码,如果检索的是ObjectAllTypeInformation类型则用0清空整个返回的缓冲区。 2.9 Debugger Window 调试器窗口的存在标志着有调试器正在系统内运行。由于调试器创建的窗口拥有特定类名(OllyDbg的是OLLYDBG,WinDbg的是WinDbgFrameClass),使用user32!FindWindow()或者user32!FindWindowEx()能很容易地识别这些调试器窗口。 示例 下面的示例代码使用FindWindow()查找OllyDbg或WinDbg创建的窗口来识别他们是否正在系统中运行。 push NULL push .szWindowClassOllyDbg call [FindWindowA] test eax,eax jnz .debugger_found push NULL push .szWindowClassWinDbg call [FindWindowA] test eax,eax jnz .debugger_found .szWindowClassOllyDbg db “OLLYDBG”,0 .szWindowClassWinDbg db “WinDbgFrameClass”,0 对策 一种方法是在FindWindow()/FindWindowEx()的入口处设断点,断下来后,改变lpClassName参数的内容,这样API将会返回fail,另一种方法就是直接将返回值设为NULL。 2.10 Debugger Process 另外一种识别系统内是否有调试器正在运行的方法是列出所有的进程,检查进程名是否与调试器(如 OLLYDBG.EXE,windbg.exe等)的相符。实现很直接,利用Process32First/Next()然后检查映像名称是否与调试器相符就行了。 有些壳也会利用kernel32!ReadProcessMemory()读取进程的内存,然后寻找调试器相关的字符串(如”OLLYDBG”)以防止逆向分析人员修改调试器的可执行文件名。一旦发现调试器的存在,壳要么显示一条错误信息,要么默默地退出或者终止调试器进程。 对策 和父进程检查类似,可以通过补丁 kernel32!Process32NextW() 使其总是返回fail值来防止壳枚举进程。 2.11 Device Drivers 检测内核模式的调试器是否活跃于系统中的典型技术是访问他们的设备驱动程序。该技术相当简单,仅涉及调用kernel32!CreateFile()检测内核模式调试器(如SoftICE)使用的那些众所周知的设备名称。 示例 一个简单的检查如下: push NULL push 0 push OPEN_EXISTING push NULL push FILE_SHARE_READ push GENERIC_READ push .szDeviceNameNtice call [CreateFileA] cmp eax,INVALID_HANDLE_VALUE jne .debugger_found .szDeviceNameNtice db "\\.\NTICE",0 某些版本的SoftICE会在设备名称后附加数字导致这种检查失败,逆向论坛中相关的描述是穷举附加的数字直到发现正确的设备名称。新版壳也用设备驱动检测技术检测诸如Regmon和Filemon之类的系统监视程序的存在。 对策 一种简单的方法就是在kernel32!CreateFileW()内设置断点,断下来后,要么操纵FileName参数要么改变其返回值为INVALID_HANDLE_VALUE(0xFFFFFFFF)。 2.12 OllyDbg:Guard Pages 这个检查是针对OllyDbg的,因为它和OllyDbg的内存访问/写入断点特性相关。 除了硬件断点和软件断点外,OllyDbg允许设置一个内存访问/写入断点,这种类型的断点是通过页面保护11来实现的。简单地说,页面保护提供了当应用程序的某块内存被访问时获得通知这样一个途径。 页面保护是通过PAGE_GUARD页面保护修改符来设置的,如果访问的内存地址是受保护页面的一部分,将会产生一个STATUS_GUARD_PAGE_VIOLATION(0x80000001)异常。如果进程被OllyDbg调试并且受保护的页面被访问,将不会抛出异常,访问将会被当作内存断点来处理,而壳正好利用了这一点。 示例 下面的示例代码中,将会分配一段内存,并将待执行的代码保存在分配的内存中,然后启用页面的PAGE_GUARD属性。接着初始化标设符EAX为0,然后通过执行内存中的代码来引发STATUS_GUARD_PAGE_VIOLATION异常。如果代码在OllyDbg中被调试,因为异常处理例程不会被调用所以标设符将不会改变。 ;set up exception handler push .exception_handle push dword [fs:0] mov [fs:0],esp ;allocate memory push PAGE_READWRITE push MEM_COMMIT push 0x1000 push NULL call [VirtualAlloc] test eax,eax jz .failed mov [.pAllocatedMem],eax ;store a RETN on the allocated memory mov byte [eax],0xC3 ;then set the PAGE_GUARD attribute of the allocated memory lea eax,[.dwOldProtect] push eax push PAGE_EXECUTE_READ | PAGE_GUARD push 0x1000 push dword [.pAllocatedMem] call [VirtualProtect] ;set marker (EAX) as 0 xor eax,eax ;trigger a STATUS_GUARD_PAGE_VIOLATION exception call [.pAllocatedMem] ;check if marker had not been changed (exception handler not called) test eax,eax je .debugger_found .exception_handler ;EAX = CONTEXT record mov eax,[esp+0xC] ;set marker (CONTEXT.EAX) to 0xFFFFFFFF ;to signal that the exception handler was called mov dword [eax+0xb0],0xFFFFFFFF xor eax,eax retn 对策 由于页面保护引发一个异常,逆向分析人员可以故意引发一个异常,这样异常处理例程将会被调用。在示例中,逆向分析人员可以用INT3指令替换掉RETN指令,一旦INT3指令被执行,Shift+F9强制调试器执行异常处理代码。这样当异常处理例程调用后,EAX将被设为正确的值,然后RETN指令将会被执行。 如果异常处理例程里检查异常是否真地是STATUS_GUARD_PAGE_VIOLATION,逆向分析人员可以在异常处理例程中下断点然后修改传入的ExceptionRecord参数,具体来说就是ExceptionC
展开阅读全文

开通  VIP会员、SVIP会员  优惠大
下载10份以上建议开通VIP会员
下载20份以上建议开通SVIP会员


开通VIP      成为共赢上传
相似文档                                   自信AI助手自信AI助手

当前位置:首页 > 包罗万象 > 大杂烩

移动网页_全站_页脚广告1

关于我们      便捷服务       自信AI       AI导航        抽奖活动

©2010-2025 宁波自信网络信息技术有限公司  版权所有

客服电话:4009-655-100  投诉/维权电话:18658249818

gongan.png浙公网安备33021202000488号   

icp.png浙ICP备2021020529号-1  |  浙B2-20240490  

关注我们 :微信公众号    抖音    微博    LOFTER 

客服