脱壳的艺术--2 调试器检测技术

Anti Debug:实战讲解 安卓逆向交流学习:342647370 vx:yijin1108an Anti Debug主要是通过各种函数去确定当前进程是否处于被调试的状态。 一.IDA里面静态分析so文件 1.将文件拖入jdax-gui中,进行静态分析,会发现OnCreate里面没多少内容,并且上面加载了so库,如下图所示。 2.将SO文件拖入,找到JNI_OnLoad,如下图所示。 3.按F5查看伪代码,如下图所示。 4.除了动态注册这个参数外,还有两个参数的传递,其中一个是简单的if判断,它使用的是或运算符,只要其中有一个成立 阅读详情
脱壳的艺术
Mark Vincent Yason
概述:脱壳是门艺术——脱壳既是一种心理挑战,同时也是逆向领域最为激动人心的智力游戏之一。为了甄别或解决非常难的反逆向技巧,逆向分析人员有时不得不了解操作系统的一些底层知识,聪明和耐心也是成功脱壳的关键。这个挑战既牵涉到壳的创建者,也牵涉到那些决心躲过这些保护的脱壳者。
本文主要目的是介绍壳常用的反逆向技术,同时也探讨了可以用来躲过或禁用这些保护的技术及公开可用的工具。这些信息将使研究人员特别是恶意代码分析人员在分析加壳的恶意代码时能识别出这些技术,当这些反逆向技术阻碍其成功分析时能决定下一步的动作。第二个目的,这里介绍的信息也会被那些计划在软件中添加一些保护措施用来减缓逆向分析人员分析其受保护代码的速度的研究人员用到。当然没有什么能使一个熟练的、消息灵通的、坚定的逆向分析人员止步的。
关键词:逆向工程、壳、保护、反调试、反逆向
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参数,具体来说就是ExceptionCode, 手工将ExceptionCode设为STATUS_GUARD_PAGE_VIOLATION 即可。


 
逆向工程中的艺术:从PEiD查壳到VB程序脱壳的实战解析 本文深入解析逆向工程中VB程序脱壳技术,重点介绍PEiD查壳工具的高级应用和UPX脱壳实战流程。通过详细的操作步骤和案例分析,帮助安全研究人员掌握VB程序脱壳的核心技术,包括内存转储、IAT修复等关键环节,提升逆向工程效率。 阅读详情

相关推荐

静态脱壳的艺术:从APK到ELF的完美解密之旅

本文深入探讨了静态脱壳技术在APK和ELF文件解密中的应用与实践。通过解析ELF文件结构、地址转换技巧及实战案例,详细介绍了如何从加密壳中完美提取原始代码,并利用IDA等工具进行自动化处理。文章还提供了对抗反调试的技巧和工具链推荐,帮助逆向工程师提升脱壳效率与成功率。

weixin_29214691的博客 235

探索高效安全的程序调试防御——anti-debug

探索高效安全的程序调试防御——anti-debug 去发现同类优质开源项目:https://gitcode.com/ 1、项目介绍 在软件开发中,防止未经授权的调试是一个重要的安全性需求。anti-debug 是一个开源项目,专为对抗恶意或非法的程序调试设计。它提供了一套跨平台的API和库,可以帮助开发者构建自我保护的应用,有效检测并阻止外部调试器的挂接,保护代码的安全性和知识产权。 2、项目技术...

gitblog_00032的博客 564

脱壳工具全解析:从原理到实战,逆向工程必备技能

在软件安全与逆向工程领域,脱壳是一项基础且关键的技术。其核心原理在于移除软件的保护层,还原原始代码逻辑,以便进行安全分析、漏洞研究或学习借鉴。从技术实现看,脱壳主要分为静态脱壳与动态脱壳两大类:静态脱壳器依赖于已知壳的签名算法进行自动化解包,而动态脱壳则借助调试器在内存中捕获解密后的代码镜像,再通过修复导入表等步骤重建可执行文件。掌握这些技术不仅能提升恶意软件分析效率,也对软件兼容性调试、代码审计等场景具有重要价值。本文以x64dbg、Scylla等主流工具为例,深入剖析在不同实战场景下的工具选型与组合策略

weixin_30680385的博客 308

脱壳的艺术

脱壳的艺术 Mark Vincent Yason 概述:脱壳是门艺术——脱壳既是一种心理挑战,同时也是逆向领域最为激动人心的智力游戏之一。为了甄别或解决非常难的反逆向技巧,逆向分析人员有时不得不了解操作系统的一些底层知识,聪明和耐心也是成功脱壳的关键。这个挑战既牵涉到壳的创建者,也牵涉到那些决心躲过这些保护的脱壳者。 本文主要目的是介绍壳常用的反逆向技术,同时也探讨了可以用来躲过或禁用这些保

小丢的专栏 7937

脱壳的艺术(逆向工程、壳、保护、反调试、反逆向)

网上找到的关于脱壳的介绍,发出来让大家共享

【胡学长 带你学 Global Mapper 】如何将影像地图转换为国家2000坐标系

多部门联合开展工作坐标数据不匹配怎么办?Hi,我是胡学长!今天教大家如何利用Global Mapper将谷歌影像数据转换为国家2000坐标系影像数据 。

一起来交流学习吧 1万+

anti-debug你必须知道的基础篇 All in one

这将是一个进阶的系列,当全部完成的时候笔者会弄成一个git手册供自己以及有需要的人查看。在实际对抗中,可能有些别样的代码的Anti结构似曾相识,或许是一个结构体,或许是一段赋值,但是在找这些结构体的时候经常需要东查西查,所以,here we go~~

jentle8的博客 1485

MinIO vs 华为云OBS:成本、性能与兼容性深度对比(含实测数据)

本文深度对比了开源对象存储MinIO与华为云OBS在成本、性能、兼容性及运维方面的核心差异。通过实测数据与全生命周期成本分析,为技术决策者提供选型依据。文章指出,MinIO在长期成本控制、性能深度优化及S3 API兼容性方面优势显著,而华为云OBS则提供开箱即用的便利性。

kmeans3miner的博客 596

脱壳的艺术(Anti-debug),调试器检测技术

概述:脱壳是门艺术——脱壳既是一种心理挑战,同时也是逆向领域最为激动人心的智力游戏之一。为了甄别或解决非常难的反逆向技巧,逆向分析人员有时不得不了解操作系统的一些底层知识,聪明和耐心也是成功脱壳的关键。这个挑战既牵涉到壳的创建者,也牵涉到那些决心躲过这些保护的脱壳者。 本文主要目的是介绍壳常用的反逆向技术,同时也探讨了可以用来躲过或禁用这些保护的技术及公开可用的工具。这些信息将使研究人员特别是恶

codemanrock 6251

星座SAR动目标检测(GMTI)

参考代码 星座SAR动目标检测 www.youwenfan.com/contentcsg/52399.html。

feifeigo123的博客 550

告别编译噩梦:Vcpkg+Cmake一站式集成Tesseract-OCR C++开发环境(Windows)

本文详细介绍了如何在Windows系统上使用Vcpkg和CMake快速集成Tesseract-OCR C++开发环境,解决传统编译过程中的依赖问题。通过一键安装和自动化配置,开发者可以高效搭建OCR识别系统,支持中英文识别,并提供了常见问题的解决方案和进阶技巧。

weixin_29009339的博客 232

《脱壳艺术》学习笔记--IsDebuggerPresent 检测调试器

  《脱壳艺术》学习笔记 1  时间:2008年2月20日星期三,9时47分26秒 IsDebuggerPresent 检测调试器 新建 vc++ console项目,采用Windbg 调试:(1)  启动 windbg 用户级调试(2)  设置调试符号路径(3)  bp main ,main 函数处下断(4)  F5 ,断点命中(6) 在 wi

FISH 的专栏 2259

小米路由器R1D进阶玩法:MIXBOX工具箱安装与插件管理指南

本文详细介绍了为小米路由器R1D安装MIXBOX工具箱的完整指南。通过开启SSH、执行一键安装脚本,用户可以轻松解锁路由器的进阶功能,如安装离线下载、内网穿透等插件,从而将旧设备转变为强大的家庭网络服务中心。

weixin_29292431的博客 1118

《脱壳艺术》学习笔记2--SEH检测调试器

 《脱壳艺术》学习笔记 2时间:2008年2月22日星期五,10时5分35秒 SEH 检测调试器 新建 vc++ Console 项目, 编写下面代码测试:int main(){ bool bIsInDebugger = true ;  try{   // 非法内存访问,会引发异常     int *p = NULL;   *p = 0;

FISH 的专栏 2081

算法 | 植物根茎生长优化算法(PRGO)原理、公式及应用,matlab代码(2025最强优化算法)

【代码】算法 | 植物根茎生长优化算法(PRGO)原理、公式及应用,matlab代码(2025最强优化算法)

尘世冰封的专栏 1019

微信小程序爬虫终极指南:以电商小程序抓包与逆向分析为例(最新版)

在2026年的移动互联网生态中,微信小程序已经成为电商交易的核心载体之一。据统计,超过78%的头部电商平台都推出了微信小程序版本,其用户活跃度甚至超过原生App。对于数据从业者而言,小程序端往往藏着比Web端更丰富的接口数据——包括但不限于商品详情、价格波动、用户评价、库存变化、秒杀活动信息等。与传统的Web爬虫不同,微信小程序爬虫面临着三大核心挑战无URL地址栏:无法通过浏览器直接查看网络请求wx.request封装:所有请求均通过微信原生API发起,难以直接拦截然而,

2201_76125261的博客 539

恶意软件分析入门:深入“脱壳”与“反混淆”的逆向工程艺术

本文为安全研究人员提供恶意软件分析的技术指南,重点解析加壳和混淆两大对抗技术。文章首先阐明恶意软件使用加密压缩(加壳)和逻辑扰乱(混淆)来逃避检测的基本原理。核心内容包括:1)分析环境的搭建与工具准备(x64dbg、IDA Pro等);2)动态调试技术,通过内存断点、API断点等方法定位原始入口点(OEP)完成脱壳;3)反混淆技巧,包括字符串解密、API哈希解析等。

专注于网络安全攻防技术与安全运营(SecOps)实践。 1661

使用CubeMX配置STM32H7的FreeRTOS

本文将介绍如何使用CubeMX配置STM32H7的FreeRTOS,并提供相应的源代码示例。通过使用CubeMX,你可以轻松地配置FreeRTOS和系统资源,并生成相应的初始化代码和配置文件。在"FreeRTOS"选项卡的"Tasks"部分,可以配置系统中的任务。首先,打开CubeMX软件。在"FreeRTOS"选项卡的"Resources"部分,你可以配置FreeRTOS使用的资源,例如,堆内存和信号量。例如,我们可以创建两个简单的任务,一个任务将在循环中切换LED的状态,另一个任务将通过串口打印消息。

JeeVue的博客 712

脱壳的艺术--7. 工具

  脱壳的艺术Mark Vincent Yason概述:脱壳是门艺术——脱壳既是一种心理挑战,同时也是逆向领域最为激动人心的智力游戏之一。为了甄别或解决非常难的反逆向技巧,逆向分析人员有时不得不了解操作系统的一些底层知识,聪明和耐心也是成功脱壳的关键。这个挑战既牵涉到壳的创建者,也牵涉到那些决心躲过这些保护的脱壳者。本文主要目的是介绍壳常用的反逆向技术,同时也探讨了可以用来躲过或禁

FISH 的专栏 1468

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

UPX脱壳背后的计算机原理:内存解压与PE文件重构的艺术

本文深入剖析了UPX脱壳背后的计算机原理,详细解读了内存解压与PE文件重构的全过程。通过分析UPX如何修改PE结构、利用解压Stub在内存中动态还原程序,并结合LZ77算法模拟,揭示了其欺骗操作系统与静态分析工具的核心机制。文章以CTF逆向实战为例,阐述了手动脱壳的关键步骤与工具使用,为理解软件保护与恶意软件分析提供了底层视角。

h0i1j2k3l的博客 883

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

21种VC反调试器

反调试技术 2 发现OD的处理 2 1. 窗口类名、窗口名 3 2. 检测调试器进程 4 3. 父进程是否是Explorer 5 4. RDTSC/ GetTickCount时间敏感程序段 6 5. StartupInfo结构 7 6. BeingDebugged 8 7. PEB.NtGlobalFlag , Heap.HeapFlags, Heap.ForceFlags 9 8. Deb...

qq_42577465的博客 910

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

手动脱壳实战:从原理到工具链,逆向工程核心技能详解

在软件安全与逆向工程领域,加壳技术是一种常见的代码保护手段,其核心原理是通过加密、压缩或混淆等方式,改变可执行文件的原始形态,以对抗静态分析与逆向工程。理解其工作原理,对于进行安全评估、漏洞分析或学习优秀软件架构至关重要。从技术价值看,掌握手动脱壳能力,意味着能够深入操作系统底层,洞悉程序从加载、解密到执行的全过程,是安全研究员和底层开发者的核心技能之一。其应用场景广泛,包括恶意软件分析、软件漏洞挖掘、第三方组件安全审计以及合法软件的互操作性研究等。本文将聚焦于手动脱壳的通用思路与实战工具链,深入探讨如何动

388
上一篇: 普通恶意代码技术分析与检测
下一篇: mo_jk2 v2.0.2 for Apache 2.0 Remote Buffer Overflow Exploit (win32)
iiprogram
博客等级 码龄25年 1068粉丝 1515原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值