脱壳的艺术.docx

上传人:b****8 文档编号:10204906 上传时间:2023-02-09 格式:DOCX 页数:41 大小:55.16KB
下载 相关 举报
脱壳的艺术.docx_第1页
第1页 / 共41页
脱壳的艺术.docx_第2页
第2页 / 共41页
脱壳的艺术.docx_第3页
第3页 / 共41页
脱壳的艺术.docx_第4页
第4页 / 共41页
脱壳的艺术.docx_第5页
第5页 / 共41页
点击查看更多>>
下载资源
资源描述

脱壳的艺术.docx

《脱壳的艺术.docx》由会员分享,可在线阅读,更多相关《脱壳的艺术.docx(41页珍藏版)》请在冰豆网上搜索。

脱壳的艺术.docx

脱壳的艺术

脱壳的艺术#EzhtuH_xn 

MarkVincentYason|M7C_=z=' 

概述:

脱壳是门艺术——脱壳既是一种心理挑战,同时也是逆向领域最为激动人心的智力游戏之一。

为了甄别或解决非常难的反逆向技巧,逆向分析人员有时不得不了解操作系统的一些底层知识,聪明和耐心也是成功脱壳的关键。

这个挑战既牵涉到壳的创建者,也牵涉到那些决心躲过这些保护的脱壳者。

jsQHg_2Vd 

本文主要目的是介绍壳常用的反逆向技术,同时也探讨了可以用来躲过或禁用这些保护的技术及公开可用的工具。

这些信息将使研究人员特别是恶意代码分析人员在分析加壳的恶意代码时能识别出这些技术,当这些反逆向技术阻碍其成功分析时能决定下一步的动作。

第二个目的,这里介绍的信息也会被那些计划在软件中添加一些保护措施用来减缓逆向分析人员分析其受保护代码的速度的研究人员用到。

当然没有什么能使一个熟练的、消息灵通的、坚定的逆向分析人员止步的。

eP_,bF_c_ 

关键词:

逆向工程、壳、保护、反调试、反逆向ME$_J4

2_ 

1简介                                    RC/45:

_hZZ 

在逆向工程领域,壳是最有趣的谜题之一。

在解谜的过程中,逆向分析人员会获得许多关于系统底层、逆向技巧等知识。

_tYXE$i 

壳(这个术语在本文中既指压缩壳也包括加密壳)是用来防止程序被分析的。

它们被商业软件合法地用于防止信息披露、篡改及盗版。

可惜恶意软件也基于同样的理由在使用壳,只不过动机不良。

[/YuI@C,@ 

由于大量恶意软件存在加壳现象,研究人员和恶意代码分析人员为了分析代码,开始学习脱壳的技巧。

但是随着时间的推移,为防止逆向分析人员分析受保护的程序并成功脱壳,新的反逆向技术也被不断地添加到壳中。

并且战斗还在继续,新的反逆向技术被开发的同时逆向分析人员也在针锋相对地发掘技巧、研究技术并开发工具来对付它们。

R{fJ__"Q5' 

本文主要关注于介绍壳所使用的反逆向技术,同时也探讨了躲过/禁用这些保护措施的工具及技术。

可能有些壳通过抓取进程映像(dump)能够轻易被搞定,这时处理反逆向技术似乎没有必要,但是有些情况下加密壳的代码需要加以跟踪和分析,例如:

^$5__0_[ 

需要躲过部分加密壳代码以便抓取进程映像、让输入表重建工具正确地工作。

_\XPGAuEo 

深入分析加密壳代码以便在一个反病毒产品中整合进脱壳支持。

f4fBUZ^_A 

此外,当反逆向技术被恶意程序直接应用,以防止跟踪并分析其恶意行为时,熟悉反逆向技术也是很有价值的。

_WFiX=@SS_ 

本文绝不是一个完整的反逆向技术的清单,因为它只涵盖了壳中常用的、有趣的一些技术。

建议读者参阅最后一节的链接和图书资料,以了解更多其他逆向及反逆向的技术。

I_H;sVT$M 

笔者希望您觉得这些材料有用,并能应用其中的技术。

脱壳快乐!

J_wB___'B 

58

Ce>_*~_ 

2调试器检测技术                               v_!

8=B21 

本节列出了壳用来确定进程是否被调试或者系统内是否有调试器正在运行的技术。

这些调试器检测技术既有非常简单(明显)的检查,也有涉及到nativeAPIs和内核对象的。

Q_J3#~GYNr 

2.1PEB.BeingDebuggedFlag:

IsDebuggerPresent()g_L_B(A\yG 

最基本的调试器检测技术就是检测进程环境块(PEB)1中的BeingDebugged标志。

kernel32!

IsDebuggerPresent()API检查这个标志以确定进程是否正在被用户模式的调试器调试。

Wk_<_fNHg_ 

下面显示了IsDebuggerPresent()API的实现代码。

首先访问线程环境块(TEB)2得到PEB的地址,然后检查PEB偏移0x02位置的BeingDebugged标志。

VD_$5Djq 

mov        eax,largefs:

18h_W!

w_of-1 

mov        eax,[eax+30h]_=3_.dgtH 

movzx      eax,byteptr[eax+2]3)_y1q>CQf 

retnz_!

IA_]v 

除了直接调用IsDebuggerPresent(),有些壳会手工检查PEB中的BeingDebugged标志以防逆向分析人员在这个API上设置断点或打补丁。

_.;]W_cC<3 

示例nwU],{(Hgr 

下面是调用IsDebuggerPresent()API和使用PEB.BeingDebugged标志确定调试器是否存在的示例代码。

!

fzS'pkk. 

;callkernel32!

IsDebuggerPresent()*_S__a_g 

call      [IsDebuggerPresent]A?

H#_bR_As 

test      eax,eaxgx.\H_3y__ 

jnz      .debugger_found)Ka-_vX)D@ 

_d____d__ 

;checkPEB.BeingDebuggeddirectly_"E\vd_h_k 

Mov      eax,dword[fs:

0x30]  ;EAX= TEB.ProcessEnvironmentBlockk|/VNV(=0 

movzx    eax,byte[eax+0x02]  ;AL = PEB.BeingDebugged5@2_Rl>B$ 

test      eax,eax_z_R<___{z 

jnz      .debugger_foundN__V_*2_ 

由于这些检查很明显,壳一般都会用后面章节将会讨论的GHOFFICE过滤词语代码或者反—反编译技术进行混淆。

gD_9

C_A_* 

对策[c3_!

xHt5O 

人工将PEB.BeingDebugged标志置0可轻易躲过这个检测。

在数据窗口中Ctrl+G(前往表达式)输入fs:

[30],可以在OllyDbg中查看PEB数据。

"d_u(BZw_ 

另外Ollyscript命令"dbh"可以补丁这个标志。

n_nyT,e%_ 

dbh3N__KN__W_ 

最后,OllyAdvanced3插件有置BeingDebugged标志为0的选项。

%unn{92)

2.2 PEB.NtGlobalFlag,Heap.HeapFlags,Heap.ForceFlagsH1k)yax4_ 

PEB.NtGlobalFlag PEB另一个成员被称作NtGlobalFlag(偏移0x68),壳也通过它来检测程序是否用调试器加载。

通常程序没有被调试时,NtGlobalFlag成员值为0,如果进程被调试这个成员通常值为0x70(代表下述标志被设置):

GJ__B+]b- 

FLG_HEAP_ENABLE_TAIL_CHECK(0X10)xRWfZ3E_# 

FLG_HEAP_ENABLE_FREE_CHECK(0X20)7_KlL_%_\ 

FLG_HEAP_VALIDATE_PARAMETERS(0X40)__I0F[Z\U 

这些标志是在ntdll!

LdrpInitializeExecutionOptions()里设置的。

请注意PEB.NtGlobalFlag的默认值可以通过gflags.exe工具或者在注册表以下位置创建条目来修改:

lf(_+]k30 

HKLM\Software\Microsoft\WindowsNt\CurrentVersion\ImageFileExecutionOptionsX?

_haHM#] 

HeapFlags由于NtGlobalFlag标志的设置,堆也会打开几个标志,这个变化可以在ntdll!

RtlCreateHeap()里观测到。

通常情况下为进程创建的第一个堆会将其Flags和ForceFlags4分别设为0x02(HEAP_GROWABLE)和0。

然而当进程被调试时,这两个标志通常被设为0x50000062(取决于NtGlobalFlag)和0x40000060(等于FlagsAND0x6001007D)。

默认情况下当一个被调试的进程创建堆时下列附加的堆标志将被设置:

_Nf4__@m|# 

HEAP_TAIL_CHECKING_ENABLED(0X20)oc"p5Y3,Os 

HEAP_FREE_CHECKING_ENABLED(0X40)w__f_]Wm 

示例aUMiRm-__ 

下面的示例代码检查PEB.NtGlobalFlag是否等于0,为进程创建的第一个堆是否设置了附加标志(PEB.ProcessHeap):

;ebx=PEBH_2],auBY 

Mov      ebx,[fs:

0x30]8;qOsV)UDT 

eV_"_,W_

 

;CheckifPEB.NtGlobalFlag!

=0=4_)8a"7#. 

Cmp      dword[ebx+0x68],0s{_*rBX8N 

jne      .debugger_found/\fR6|tJ 

___k6OO\= 

;eax=PEB.ProcessHeap"Xq$0~c 

Mov      eax,[ebx+0x18]^d>m`*p_x 

m_!

INbI__h 

;CheckPEB.ProcessHeap.FlagsU_ChLWf|_' 

Cmp      dword[eax+0x0c],2*k__\;G?

 

jne      .debugger_foundF`;q9

A__7I0_^_ 

;CheckPEB.ProcessHeap.ForceFlagsP/S,dh_s( 

Cmp      dword[eax+0x10],0_0S_wu]OE 

jne      .debugger_found__

U_s[F@ 

对策_2_*W|s7cc 

可以将PEB.NtGlobalFlag和PEB.HeapProcess标志补丁为进程未被调试时的相应值。

下面是一个补丁上述标志的ollyscript示例:

\0*dKgN__ 

Var      peb$5#DU___F/ 

var      patch_addr_'_0Q_/oU 

var      process_heap(Ms0pm-#t_ 

Y_|#_

//retrievePEBviaahardcodedTEBaddress(firstthread:

0x7ffde000)}Oe4w_EYN) 

Mov      peb,[7ffde000+30]_kB_?

Uw_# 

CszZr>_Z 

//patchPEB.NtGlobalFlag_6_G,_cc 

Lea      patch_addr,[peb+68]F

)aF.'$-/ 

mov      [patch_addr],0:

VR%I;g; 

{H_=<5___ 

//patchPEB.ProcessHeap.Flags/ForceFlagsWxJaE;`Ige 

Mov      process_heap,[peb+18]qP9`p4c8_i 

lea      patch_addr,[process_heap+0c]b-#oE{_(\' 

mov      [patch_addr],2_

0_2[*b__ 

lea      patch_addr,[process_heap+10]_x45F-_w{_ 

mov      [patch_addr],0IR8qF__WDZ 

同样地OllyAdvanced插件有设置PEB.NtGlobalFlag和PEB.ProcessHeap的选项。

5IRUG_)Icr 

2.3DebugPort:

CheckRemoteDebuggerPresent()/NtQueryInformationProcess()Ih5CtcE1'd 

Kernel32!

CheckRemoteDebuggerPresent()是另一个可以用于确定是否有调试器被附加到进程的API。

这个API内部调用了ntdll!

NtQueryInformationProcess(),调用时ProcessInformationclass参数为ProcessDebugPort(7)。

而NtQueryInformationProcess()检索内核结构EPROCESS5的DebugPort成员。

非0的DebugPort成员意味着进程正在被用户模式的调试器调试。

如果是这样的话,ProcessInformation将被置为0xFFFFFFFF,否则ProcessInformation将被置为0。

]_0")i_Y_ 

Kernel32!

CheckRemoteDebuggerPresent()接受2个参数,第1个参数是进程句柄,第2个参数是一个指向boolean变量的指针,如果进程被调试,该变量将包含TRUE返回值。

+WCV"_m_ 

BOOLCheckRemoteDebuggerPresent(\O*W/9__+ 

 HANDLE  hProcess,_0<_R___q 

 PBOOL   pbDebuggerPresent=)!

_~t_/_ 

)IRsyy\[kp8 

ntdll!

NtQueryInformationProcess()有5个参数。

为了检测调试器的存在,需要将ProcessInformationclass参数设为ProcessDebugPort(7):

_fTGVG 

NTSTATUS NTAPI    NtQueryInformationProcess(lgefTTGX) 

HANDLE           ProcessHandle,cZ_,}_1?

!

 

PROCESSINFOCLASS     ProcessInformationClass,MwD8a<_2Dg 

PVOID           ProcessInformation,ZkF_6AF__ 

ULONG           ProcessInformationLength,:

!

wt/Y 

PULONG         ReturnLengthV+$f__h_2t 

)_W_S\Ir-_B 

示例X__JA];9_^ 

下面的例子显示了如何调用CheckRemoteDebuggerPresent()和NtQueryInformationProcess()来检测当前进程是否被调试:

whQJ_Wi=ck 

;usingKernel32!

CheckRemoteDebuggerPresent()4udW__6U 

lea       eax,[.bDebuggerPresent][I_(

Yn_ 

push      eax          ;pbDebuggerPresent_`__c__" 

push  0xffffffff            ;hProcess6_P_02= 

call      [CheckRemoteDebuggerPresent]L_v

cmp     dword[.bDebuggerPresent],0e_IR_F+> 

jne       .debugger_found__pL__,l 

IJDE{__)_ 

;usingntdll!

NtQueryInformationProcess(ProcessDebugPort)*eM_MfxFl 

lea       eax,[.dwReturnLen]EvF_[h_:

C2 

push      eax          ;ReturnLength__=_G`g-E2 

push      4          ;ProcessInformationLengths13__3N_?

lea       eax,[.dwDebugPort]r'MA_$PiS' 

push      eax          ;ProcessInformationLHSbc!

_Y'. 

push      ProcessDebugPort    ;ProcessInformationClass(7)^_U__~QQ 

push      0xffffffff        ;ProcessHandle6i@*L\Dl 

call      [NtQueryInformationProcess]_)T^_x_Dx 

cmp     dword[.dwDebugPort],0%a!

__gN__ 

jne      .debugger_foundSq{@4F}d 

对策t8`_wO+4@_ 

一种方法是在NtQueryInformationProcess()返回的地方设置断点,当这个断点被断下来后,将ProcessInformation补丁为0。

下面是自动执行这个方法的ollyscript示例:

X!

ml_C5_1 

var      bp_NtQueryInformationProcess^

_!

gq_x__ 

t(jE9t|2e6 

//setabreakpointhandleriU

k#hL_LC 

eob      bp_handler_NtQueryInformationProcessSYa_O'_c_ 

n#@Qd!

uzM 

//setabreakpointwhereNtQueryInformationProcessreturnsILqBa_:

_J_ 

gpa      "NtQueryInformationProcess","ntdll.dll"6@eF|_GoP 

find    $RESULT,#C21400# //retn14qH__vU_Bx0 

mov    bp_NtQueryInformationProcess,$RESULT|gIE$rt-~W 

bphws    bp_NtQueryInformationProcess,"X"_{`_,)

run__mx_q__Y 

Dp!

91NgBp 

bp_handler_NtQueryInformationProcess:

1___G_K>&; 

I2Imb9k~B 

//ProcessInformationClass==ProcessDebugPort?

9

\c]I0)3p 

cmp       [esp+8],7_u_x__B`_ 

jne      bp_handler_NtQueryInformationProcess_continue;S_'?

_l_0 

<_fM}_Kk_ 

//patchProcessInformationto0=__*"8N-FU 

mov    patch_addr,[esp+c]l_o(_C3o_' 

mov    [patch_addr],0O^_j*"#_f 

G_g;:

)k\ 

//clearbreakpoint_\2e^_x 

bphwc    bp_NtQueryInformationProcess;RRw-|/W_m 

e6Y>Bk_ 

bp_handler_NtQueryInformationProcess_continue:

IEM_a/[n/ 

runYrB-_n___ 

OllyAdvanced插件有一个patchNtQueryInformationProcess()的选项,这个补丁涉及注入一段代码来操纵NtQueryInformationProcess()的返回值。

w2'q_9_pB+ 

2.4DebuggerInterruptsCj<_8rS4+ 

在调试器中步过INT3和INT1指令的时候,由于调试器通常会处理这些调试中断,所以异常处理例程默认情况下将不会被调用,DebuggerInterrupts就利用了这个事实。

这样壳可以在异常处理例程中设置标志,通过INT指令后如果这些标志没有被设置则意味着进程正在被调试。

另外,kernel32!

DebugBreak()内部是调用了INT3来实现的,有些壳也会使用这个API。

M{{kO@P"9 

示例dN)8_____r 

这个例子在异常处理例程中设置EAX的值为0xFFFFFFFF(通过CONTEXT6记录)以此来判断异常处理例程是否被调用:

$_E.D>5^%7 

;setexceptionhandlerA*?

P_H_`bY 

push    .exeception_handler1o%_#kf_ 

push    dword[fs:

0]ZBK0`7#&EH 

mov    [fs:

0],espd_A__x?

 

r|u[36NmA_ 

;resetflag(EAX)invokeint3#_A__z#_0= 

xor    eax,eax_LS_ou]{R_ 

int3XNWtX-[^@ 

X$w_e\t_ 

;restoreexceptionhandlerCF{_bYf^% 

pop    dword[fs:

0]c_Z_Ncplt8 

add    esp,4_Hyyb0c^

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 高等教育 > 文学

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1