没有合适的资源?快使用搜索试试~ 我知道了~
本文主要目的是介绍壳常用的反逆向技术,同时也探讨了可以用来躲过或者禁用这些保护的技术及公开可用的工具。
资源推荐
资源详情
资源评论
脱壳的艺术
Mark Vincent Yason
概述 : 脱壳是门艺术 —— 脱壳既是一种心理挑战, 同时也是逆向领域最为激动人心的智力 游
戏之一。 为了甄别或解决非常难的反逆向技巧, 逆向分析人员有时不得不了解操作系统的 一
些底层知识, 聪明和耐心也是成功脱壳的关键。 这个挑战既牵涉到壳的创建者,也牵涉到 那
些决心躲过这些保护的脱壳者。
本文主要目的是介绍壳常用的反逆向技术, 同时也探讨了可以用来躲过或禁用这些保 护
的技术及公开可用的工具。 这些信息将使研究人员特别是恶意代码分析人员在分析加壳的 恶
意代码时能识别出这些技术, 当这些反逆向技术阻碍其成功分析时能决定下一步的动作。 第
二个目的, 这里介绍的信息也会被那些计划在软件中添加一些保护措施用来减缓逆向分析 人
员分析其受保护代码的速度的研究人员用到。当然没有什么能使一个熟练的、消息灵通的、
坚定的逆向分析人员止步的。
关键词 :逆向工程、壳、保护、反调试、反逆向
1
1
1
1
简介
在逆向工程领域,壳是最有趣的谜题之一。在解谜的过程中,逆向分析人员会获得许 多
关于系统底层、逆向技巧等知识。
壳(这个术语在本文中既指压缩壳也包括加密壳)是用来防止程序被分析的。它们被 商
业软件合法地用于防止信息披露、篡改及盗版。可惜恶意软件也基于同样的理由在使用壳,
只不过动机不良。
由于大量恶意软件存在加壳现象,研究人员和恶意代码分析人员为了分析代码,开始 学
习脱壳的技巧。但是随着时间的推移,为防止逆向分析人员分析受保护的程序并成功脱壳,
新的反逆向技术也被不断地添加到壳中。 并且战斗还在继续, 新的反逆向技术被开发的同 时
逆向分析人员也在针锋相对地发掘技巧、研究技术并开发工具来对付它们。
本文主要关注于介绍壳所使用的反逆向技术, 同时也探讨了躲过 / 禁用这些保护措施的 工
具及技术。可能有些壳通过抓取进程映像( dump )能够轻易被搞定,这时处理反逆向技术
似乎没有必要,但是有些情况下加密壳的代码需要加以跟踪和分析,例如:
需要躲过部分加密壳代码以便抓取进程映像、让输入表重建工具正确地工作。
深入分析加密壳代码以便在一个反病毒产品中整合进脱壳支持。
此外,当反逆向技术被恶意程序直接应用,以防止跟踪并分析其恶意行为时,熟悉反 逆
向技术也是很有价值的。
本文绝不是一个完整的反逆向技术的清单,因为它只涵盖了壳中常用的、有趣的一些 技
术。建议读者参阅最后一节的链接和图书资料,以了解更多其他逆向及反逆向的技术。
笔者希望您觉得这些材料有用,并能应用其中的技术。脱壳快乐!
2
2
2
2
调试器检测技术
本节列出了壳用来确定进程是否被调试或者系统内是否有调试器正在运行的技术。 这些调 试
器检测技术既有非常简单(明显)的检查,也有涉及到 native APIs 和内核对象的。
2.1
2.1
2.1
2.1
PEB.BeingDe
PEB.BeingDe
PEB.BeingDe
PEB.BeingDe
b
b
b
b
ugged
ugged
ugged
ugged
Flag
Flag
Flag
Flag
:
:
:
:
IsDebuggerPresent()
IsDebuggerPresent()
IsDebuggerPresent()
IsDebuggerPresent()
最基本的调试器检测技术就是检测进程环境块 ( PEB )
1
中的 BeingDebugged 标志。
kernel32!IsDebuggerPresent() API 检查这个标志以确定进程是否正在被用户模式的调试器调
试。
下面显示了 IsDebuggerPresent() API 的实现代码。首先访问线程环境块 ( TEB )
2
得到 PEB
的地址,然后检查 PEB 偏移 0x02 位置的 BeingDebugged 标志。
mov eax, large fs: 18 h
mov eax, [eax+30 h ]
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
2.2
2.2
2.2
PEB.NtGlobalFlag
PEB.NtGlobalFlag
PEB.NtGlobalFlag
PEB.NtGlobalFlag
,
,
,
,
Heap.HeapFlags,
Heap.HeapFlags,
Heap.HeapFlags,
Heap.HeapFlags,
Heap.ForceFlags
Heap.ForceFlags
Heap.ForceFlags
Heap.ForceFlags
PEB.NtGlobalFlag
PEB.NtGlobalFlag
PEB.NtGlobalFlag
PEB.NtGlobalFlag
PEB 另一个成员被称作 NtGlobalFlag (偏移 0x68
) ,壳也通过它来检
测
程序是否用调试器加载。通常程序没有被调试时, NtGlobalFlag 成员值为 0 ,如果进程被调
试这个成员通常值为 0x70 (代表下述标志被设置) :
FLG_HEAP_ENABLE_TAIL_CHECK(0X10)
FLG_HEAP_ENABLE_FREE_CHECK(0X20)
FLG_HEAP_V ALIDATE_PARAMETERS(0X40)
这些标志是在 ntdll!LdrpInitializeExecutionOptions() 里设置的。请注意 PEB.NtGlobalFlag
的默认值可以通过 gflags.exe 工具或者在注册表以下位置创建条目来修改:
HKLM\Software\Microsoft\Windows Nt\CurrentV ersion\Image File Execution Options
Heap
Heap
Heap
Heap
Flags
Flags
Flags
Flags
由于 NtGlobalFlag 标志的设置,堆也会打开几个标志,这个变化可以在
ntdll!RtlCreateHeap() 里观测到。通常情况下为进程创建的第一个堆会将其 Flags 和
ForceFlags
4
分别设为 0x02 ( HEAP_GROW ABLE) 和 0 。然而当进程被调试时,这两个标志
通常被设为
0x50000062
(取决于 NtGlobalFlag
) 和
0x40000060
(等于 Flags AND 0x600100 7D
)。
默认情况下当一个被调试的进程创建堆时下列附加的堆标志将被设置:
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
2.3
2.3
2.3
DebugPort:
DebugPort:
DebugPort:
DebugPort:
CheckRemoteDebuggerPresent()/NtQueryInformationProcess()
CheckRemoteDebuggerPresent()/NtQueryInformationProcess()
CheckRemoteDebuggerPresent()/NtQueryInformationProcess()
CheckRemoteDebuggerPresent()/NtQueryInformationProcess()
Kernel32!CheckRemoteDebuggerPresent() 是另一个可以用于确定是否有调试器被附加到
进程的 API 。这个 API 内部调用了 ntdll!NtQueryInformationProcess() ,调用时
ProcessInformationclass 参数为 ProcessDebugPort(7) 。而 NtQueryInformationProcess() 检索内
核结构 EPROCESS 5 的 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:
剩余37页未读,继续阅读
资源评论
viecks_wang
- 粉丝: 3
- 资源: 18
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
最新资源
- Screenshot_20240427_031602.jpg
- 网页PDF_2024年04月26日 23-46-14_QQ浏览器网页保存_QQ浏览器转格式(6).docx
- 直接插入排序,冒泡排序,直接选择排序.zip
- 在排序2的基础上,再次对快排进行优化,其次增加快排非递归,归并排序,归并排序非递归版.zip
- 实现了7种排序算法.三种复杂度排序.三种nlogn复杂度排序(堆排序,归并排序,快速排序)一种线性复杂度的排序.zip
- 冒泡排序 直接选择排序 直接插入排序 随机快速排序 归并排序 堆排序.zip
- 课设-内部排序算法比较 包括冒泡排序、直接插入排序、简单选择排序、快速排序、希尔排序、归并排序和堆排序.zip
- Python排序算法.zip
- C语言实现直接插入排序、希尔排序、选择排序、冒泡排序、堆排序、快速排序、归并排序、计数排序,并带图详解.zip
- 常用工具集参考用于图像等数据处理
资源上传下载、课程学习等过程中有任何疑问或建议,欢迎提出宝贵意见哦~我们会及时处理!
点击此处反馈
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功