[转载] 【转自看雪软件论坛】【翻译】脱壳的艺术
|
最美我中文
My Cat Has Gone.
管理员
目前的守墓人
|
楼主
大
中
小
发表于 2008-8-13 21:27
只看该作者
【转自看雪软件论坛】【翻译】脱壳的艺术
标 题: 【翻译】脱壳的艺术<BR>作 者: hawking<BR>时 间: 2007-08-21,21:25<BR>链 接: <A href="http://bbs.pediy.com/showthread.php?t=50119" target=_blank>http://bbs.pediy.com/showthread.php?t=50119</A><BR><BR>初次翻译文章,加之自己本身水平有限,请多多包涵。有舛误之处还请兄弟们多多指正。<BR><BR>文章本身是很好的,尤其是对于我等刚入门的菜鸟,英文水平稍好些的还是看原版为妙。<BR><BR><BR><BR>
<html> <head> <meta http-equiv=Content-Type content="text/html; charset=gb2312"> <meta name=Generator content="Microsoft Word 12 (filtered)"> <title>脱壳的艺术</title> <style> <!-- /* Font Definitions */ @font-face {font-family:宋体; panose-1:2 1 6 0 3 1 1 1 1 1;} @font-face {font-family:"Cambria Math"; panose-1:2 4 5 3 5 4 6 3 2 4;} @font-face {font-family:"\@宋体"; panose-1:2 1 6 0 3 1 1 1 1 1;} /* Style Definitions */ p.MsoNormal, li.MsoNormal, div.MsoNormal {margin:0cm; margin-bottom:.0001pt; text-align:justify; text-justify:inter-ideograph; font-size:10.5pt; font-family:"Times New Roman","serif";} p.MsoHeader, li.MsoHeader, div.MsoHeader {mso-style-link:"页眉 Char"; margin:0cm; margin-bottom:.0001pt; text-align:center; layout-grid-mode:char; border:none; padding:0cm; font-size:9.0pt; font-family:"Times New Roman","serif";} p.MsoFooter, li.MsoFooter, div.MsoFooter {mso-style-link:"页脚 Char"; margin:0cm; margin-bottom:.0001pt; layout-grid-mode:char; font-size:9.0pt; font-family:"Times New Roman","serif";} a:link, span.MsoHyperlink {color:blue; text-decoration:underline;} a:visited, span.MsoHyperlinkFollowed {color:purple; text-decoration:underline;} span.Char {mso-style-name:"页眉 Char"; mso-style-link:页眉;} span.Char0 {mso-style-name:"页脚 Char"; mso-style-link:页脚;} /* Page Definitions */ @page Section1 {size:595.3pt 841.9pt; margin:72.0pt 90.0pt 72.0pt 90.0pt; layout-grid:15.6pt;} div.Section1 {page:Section1;} --> </style> </head> <body lang=ZH-CN style='text-justify-trim:punctuation'> <div class=Section1 style='layout-grid:15.6pt'> <p class=MsoNormal align=center style='text-align:center'><b><span style='font-size:22.0pt;font-family:宋体'>脱壳的艺术</span></b></p> <p class=MsoNormal align=center style='margin-bottom:15.6pt;text-align:center'><span lang=EN-US>Mark Vincent Yason</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span style='font-family:宋体'>概述</span></b><span style='font-family:宋体'>:脱壳是门艺术——脱壳既是一种心理挑战,同时也是逆向领域最为激动人心的智力游戏之一。为了甄别或解决非常难的反逆向技巧,逆向分析人员有时不得不了解操作系统的一些底层知识,聪明和耐心也是成功脱壳的关键。这个挑战既牵涉到壳的创建者,也牵涉到那些决心躲过这些保护的脱壳者。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>本文主要目的是介绍壳常用的反逆向技术,同时也探讨了可以用来躲过或禁用这些保护的技术及公开可用的工具。这些信息将使研究人员特别是恶意代码分析人员在分析加壳的恶意代码时能识别出这些技术,当这些反逆向技术阻碍其成功分析时能决定下一步的动作。第二个目的,这里介绍的信息也会被那些计划在软件中添加一些保护措施用来减缓逆向分析人员分析其受保护代码的速度的研究人员用到。当然没有什么能使一个熟练的、消息灵通的、坚定的逆向分析人员止步的。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span style='font-family:宋体'>关键词</span></b><span style='font-family:宋体'>:逆向工程、壳、保护、反调试、反逆向</span></p> <p class=MsoNormal style='margin-top:23.4pt;margin-right:0cm;margin-bottom: 23.4pt;margin-left:0cm'><b><span lang=EN-US style='color:white;background:black'>1</span></b><b><span style='font-family:宋体;color:white;background:black'>简介</span><span lang=EN-US style='color:white;background:black'> </span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>在逆向工程领域,壳是最有趣的谜题之一。在解谜的过程中,逆向分析人员会获得许多关于系统底层、逆向技巧等知识。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>壳(这个术语在本文中既指压缩壳也包括加密壳)是用来防止程序被分析的。它们被商业软件合法地用于防止信息披露、篡改及盗版。可惜恶意软件也基于同样的理由在使用壳,只不过动机不良。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>由于大量恶意软件存在加壳现象,研究人员和恶意代码分析人员为了分析代码,开始学习脱壳的技巧。但是随着时间的推移,为防止逆向分析人员分析受保护的程序并成功脱壳,新的反逆向技术也被不断地添加到壳中。并且战斗还在继续,新的反逆向技术被开发的同时逆向分析人员也在针锋相对地发掘技巧、研究技术并开发工具来对付它们。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>本文主要关注于介绍壳所使用的反逆向技术,同时也探讨了躲过</span><span lang=EN-US>/</span><span style='font-family:宋体'>禁用这些保护措施的工具及技术。可能有些壳通过抓取进程映像(</span><span lang=EN-US>dump</span><span style='font-family:宋体'>)能够轻易被搞定,这时处理反逆向技术似乎没有必要,但是有些情况下加密壳的代码需要加以跟踪和分析,例如:</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:21.0pt'><span style='font-family:宋体'>需要躲过部分加密壳代码以便抓取进程映像、让输入表重建工具正确地工作。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:21.0pt'><span style='font-family:宋体'>深入分析加密壳代码以便在一个反病毒产品中整合进脱壳支持。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>此外,当反逆向技术被恶意程序直接应用,以防止跟踪并分析其恶意行为时,熟悉反逆向技术也是很有价值的。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>本文绝不是一个完整的反逆向技术的清单,因为它只涵盖了壳中常用的、有趣的一些技术。建议读者参阅最后一节的链接和图书资料,以了解更多其他逆向及反逆向的技术。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>笔者希望您觉得这些材料有用,并能应用其中的技术。脱壳快乐!</span></p> <b><span lang=EN-US style='font-size:10.5pt;font-family:"Times New Roman","serif"; color:white;background:black'><br clear=all style='page-break-before:always'> </span></b> <p class=MsoNormal style='margin-top:23.4pt;margin-right:0cm;margin-bottom: 23.4pt;margin-left:0cm'><b><span lang=EN-US style='color:white;background:black'>2 </span></b><b><span style='font-family:宋体;color:white;background:black'>调试器检测技术</span><span lang=EN-US style='color:white;background:black'> </span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><span style='font-family:宋体'>本节列出了壳用来确定进程是否被调试或者系统内是否有调试器正在运行的技术。这些调试器检测技术既有非常简单(明显)的检查,也有涉及到</span><span lang=EN-US>native APIs</span><span style='font-family:宋体'>和内核对象的。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span lang=EN-US>2.1 PEB.BeingDebugged Flag : IsDebuggerPresent()</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>最基本的调试器检测技术就是检测进程环境块</span><span lang=EN-US>(PEB)</span><sup><span lang=EN-US>1</span></sup><span style='font-family:宋体'>中的</span><span lang=EN-US>BeingDebugged</span><span style='font-family:宋体'>标志。</span><span lang=EN-US>kernel32!IsDebuggerPresent() API</span><span style='font-family:宋体'>检查这个标志以确定进程是否正在被用户模式的调试器调试。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>下面显示了</span><span lang=EN-US>IsDebuggerPresent() API</span><span style='font-family:宋体'>的实现代码。首先访问线程环境块</span><span lang=EN-US>(TEB)</span><sup><span lang=EN-US>2</span></sup><span style='font-family:宋体'>得到</span><span lang=EN-US>PEB</span><span style='font-family:宋体'>的地址,然后检查</span><span lang=EN-US>PEB</span><span style='font-family:宋体'>偏移</span><span lang=EN-US>0x02</span><span style='font-family:宋体'>位置的</span><span lang=EN-US>BeingDebugged</span><span style='font-family:宋体'>标志。</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>mov eax, large fs: 18h</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>mov eax, [eax+30h]</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>movzx eax, byte ptr [eax+2]</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>retn</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>除了直接调用</span><span lang=EN-US>IsDebuggerPresent()</span><span style='font-family:宋体'>,有些壳会手工检查</span><span lang=EN-US>PEB</span><span style='font-family:宋体'>中的</span><span lang=EN-US>BeingDebugged</span><span style='font-family:宋体'>标志以防逆向分析人员在这个</span><span lang=EN-US>API</span><span style='font-family:宋体'>上设置断点或打补丁。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span style='font-family:宋体'>示例</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>下面是调用</span><span lang=EN-US>IsDebuggerPresent() API</span><span style='font-family:宋体'>和使用</span><span lang=EN-US>PEB.BeingDebugged</span><span style='font-family:宋体'>标志确定调试器是否存在的示例代码。</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>;call kernel32!IsDebuggerPresent()</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>call [IsDebuggerPresent]</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>test eax,eax</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>jnz .debugger_found</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US> </span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>;check PEB.BeingDebugged directly</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>Mov eax,dword [fs:0x30] ;EAX = TEB.ProcessEnvironmentBlock</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>movzx eax,byte [eax+0x02] ;AL = PEB.BeingDebugged</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>test eax,eax</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>jnz .debugger_found</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>由于这些检查很明显,壳一般都会用后面章节将会讨论的垃圾代码或者反—反编译技术进行混淆。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span style='font-family:宋体'>对策</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>人工将</span><span lang=EN-US>PEB.BeingDebugged</span><span style='font-family:宋体'>标志置</span><span lang=EN-US>0</span><span style='font-family:宋体'>可轻易躲过这个检测。在数据窗口中</span><span lang=EN-US>Ctrl+G</span><span style='font-family:宋体'>(前往表达式)输入</span><span lang=EN-US>fs:[30]</span><span style='font-family:宋体'>,可以在</span><span lang=EN-US>OllyDbg</span><span style='font-family:宋体'>中查看</span><span lang=EN-US>PEB</span><span style='font-family:宋体'>数据。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>另外</span><span lang=EN-US>Ollyscript</span><span style='font-family:宋体'>命令</span><span lang=EN-US>"dbh"</span><span style='font-family:宋体'>可以补丁这个标志。</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>dbh</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>最后,</span><span lang=EN-US>Olly Advanced3 </span><span style='font-family:宋体'>插件有置</span><span lang=EN-US>BeingDebugged</span><span style='font-family:宋体'>标志为</span><span lang=EN-US>0</span><span style='font-family:宋体'>的选项。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span lang=EN-US>2.2 PEB.NtGlobalFlag , Heap.HeapFlags, Heap.ForceFlags</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span lang=EN-US>PEB.NtGlobalFlag </span></b><span lang=EN-US> PEB</span><span style='font-family:宋体'>另一个成员被称作</span><span lang=EN-US>NtGlobalFlag</span><span style='font-family:宋体'>(偏移</span><span lang=EN-US>0x68</span><span style='font-family:宋体'>),壳也通过它来检测程序是否用调试器加载。通常程序没有被调试时,</span><span lang=EN-US>NtGlobalFlag</span><span style='font-family:宋体'>成员值为</span><span lang=EN-US>0</span><span style='font-family:宋体'>,如果进程被调试这个成员通常值为</span><span lang=EN-US>0x70</span><span style='font-family:宋体'>(代表下述标志被设置):</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>FLG_HEAP_ENABLE_TAIL_CHECK(0X10)</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>FLG_HEAP_ENABLE_FREE_CHECK(0X20)</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>FLG_HEAP_VALIDATE_PARAMETERS(0X40)</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>这些标志是在</span><span lang=EN-US>ntdll!LdrpInitializeExecutionOptions()</span><span style='font-family: 宋体'>里设置的。请注意</span><span lang=EN-US>PEB.NtGlobalFlag</span><span style='font-family:宋体'>的默认值可以通过</span><span lang=EN-US>gflags.exe</span><span style='font-family:宋体'>工具或者在注册表以下位置创建条目来修改:</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:35.9pt'><span lang=EN-US>HKLM\Software\Microsoft\Windows Nt\CurrentVersion\Image File Execution Options</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span lang=EN-US>Heap Flags</span></b><span lang=EN-US> </span><span style='font-family:宋体'>由于</span><span lang=EN-US>NtGlobalFlag</span><span style='font-family:宋体'>标志的设置,堆也会打开几个标志,这个变化可以在</span><span lang=EN-US>ntdll!RtlCreateHeap()</span><span style='font-family:宋体'>里观测到。通常情况下为进程创建的第一个堆会将其</span><span lang=EN-US>Flags</span><span style='font-family:宋体'>和</span><span lang=EN-US>ForceFlags</span><sup><span lang=EN-US>4</span></sup><span style='font-family:宋体'>分别设为</span><span lang=EN-US>0x02</span><span style='font-family:宋体'>(</span><span lang=EN-US>HEAP_GROWABLE)</span><span style='font-family:宋体'>和</span><span lang=EN-US>0 </span><span style='font-family:宋体'>。然而当进程被调试时,这两个标志通常被设为</span><span lang=EN-US>0x50000062</span><span style='font-family:宋体'>(取决于</span><span lang=EN-US>NtGlobalFlag</span><span style='font-family:宋体'>)和</span><span lang=EN-US>0x40000060</span><span style='font-family:宋体'>(等于</span><span lang=EN-US>Flags AND 0x6001007D</span><span style='font-family:宋体'>)。默认情况下当一个被调试的进程创建堆时下列附加的堆标志将被设置:</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>HEAP_TAIL_CHECKING_ENABLED(0X20)</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>HEAP_FREE_CHECKING_ENABLED(0X40)</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span style='font-family:宋体'>示例</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>下面的示例代码检查</span><span lang=EN-US>PEB.NtGlobalFlag</span><span style='font-family:宋体'>是否等于</span><span lang=EN-US>0</span><span style='font-family:宋体'>,为进程创建的第一个堆是否设置了附加标志</span><span lang=EN-US>(PEB.ProcessHeap)</span><span style='font-family:宋体'>:</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>;ebx = PEB</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>Mov ebx,[fs:0x30]</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US> </span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>;Check if PEB.NtGlobalFlag != 0</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>Cmp dword [ebx+0x68],0</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>jne .debugger_found</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US> </span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>;eax = PEB.ProcessHeap</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>Mov eax,[ebx+0x18]</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US> </span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>;Check PEB.ProcessHeap.Flags</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>Cmp dword [eax+0x0c],2</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>jne .debugger_found</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US> </span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>;Check PEB.ProcessHeap.ForceFlags</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>Cmp dword [eax+0x10],0</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>jne .debugger_found</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span style='font-family:宋体'>对策</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>可以将</span><span lang=EN-US> PEB.NtGlobalFlag</span><span style='font-family:宋体'>和</span><span lang=EN-US>PEB.HeapProcess</span><span style='font-family:宋体'>标志补丁为进程未被调试时的相应值。下面是一个补丁上述标志的</span><span lang=EN-US>ollyscript</span><span style='font-family:宋体'>示例:</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>Var peb</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>var patch_addr</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>var process_heap</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US> </span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>//retrieve PEB via a hardcoded TEB address( first thread: 0x7ffde000)</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>Mov peb,[7ffde000+30]</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US> </span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>//patch PEB.NtGlobalFlag</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>Lea patch_addr,[peb+68]</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>mov [patch_addr],0</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US> </span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>//patch PEB.ProcessHeap.Flags/ForceFlags</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>Mov process_heap,[peb+18]</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>lea patch_addr,[process_heap+0c]</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>mov [patch_addr],2</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>lea patch_addr,[process_heap+10]</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>mov [patch_addr],0</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>同样地</span><span lang=EN-US>Olly Advanced</span><span style='font-family:宋体'>插件有设置</span><span lang=EN-US>PEB.NtGlobalFlag</span><span style='font-family:宋体'>和</span><span lang=EN-US>PEB.ProcessHeap</span><span style='font-family:宋体'>的选项。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span lang=EN-US>2.3 DebugPort: CheckRemoteDebuggerPresent()/NtQueryInformationProcess()</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span lang=EN-US>Kernel32!CheckRemoteDebuggerPresent()</span><span style='font-family:宋体'>是另一个可以用于确定是否有调试器被附加到进程的</span><span lang=EN-US>API</span><span style='font-family:宋体'>。这个</span><span lang=EN-US>API</span><span style='font-family:宋体'>内部调用了</span><span lang=EN-US>ntdll!NtQueryInformationProcess()</span><span style='font-family:宋体'>,调用时</span><span lang=EN-US>ProcessInformationclass</span><span style='font-family:宋体'>参数为</span><span lang=EN-US>ProcessDebugPort(7)</span><span style='font-family:宋体'>。而</span><span lang=EN-US>NtQueryInformationProcess()</span><span style='font-family:宋体'>检索内核结构</span><span lang=EN-US>EPROCESS5</span><span style='font-family:宋体'>的</span><span lang=EN-US>DebugPort</span><span style='font-family:宋体'>成员。非</span><span lang=EN-US>0</span><span style='font-family:宋体'>的</span><span lang=EN-US>DebugPort</span><span style='font-family:宋体'>成员意味着进程正在被用户模式的调试器调试。如果是这样的话,</span><span lang=EN-US>ProcessInformation </span><span style='font-family:宋体'>将被置为</span><span lang=EN-US>0xFFFFFFFF </span><span style='font-family:宋体'>,否则</span><span lang=EN-US>ProcessInformation </span><span style='font-family:宋体'>将被置为</span><span lang=EN-US>0</span><span style='font-family:宋体'>。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span lang=EN-US>Kernel32!CheckRemoteDebuggerPresent()</span><span style='font-family:宋体'>接受</span><span lang=EN-US>2</span><span style='font-family:宋体'>个参数,第</span><span lang=EN-US>1</span><span style='font-family:宋体'>个参数是进程句柄,第</span><span lang=EN-US>2</span><span style='font-family:宋体'>个参数是一个指向</span><span lang=EN-US>boolean</span><span style='font-family:宋体'>变量的指针,如果进程被调试,该变量将包含</span><span lang=EN-US>TRUE</span><span style='font-family:宋体'>返回值。</span></p> <p class=MsoNormal style='margin-left:27.1pt;text-indent:19.95pt'><span lang=EN-US>BOOL CheckRemoteDebuggerPresent(</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US> HANDLE hProcess,</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US> PBOOL pbDebuggerPresent</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>)</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span lang=EN-US>ntdll!NtQueryInformationProcess()</span><span style='font-family:宋体'>有</span><span lang=EN-US>5</span><span style='font-family: 宋体'>个参数。为了检测调试器的存在,需要将</span><span lang=EN-US>ProcessInformationclass</span><span style='font-family:宋体'>参数设为</span><span lang=EN-US>ProcessDebugPort(7)</span><span style='font-family:宋体'>:</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>NTSTATUS NTAPI NtQueryInformationProcess(</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>HANDLE ProcessHandle,</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>PROCESSINFOCLASS ProcessInformationClass,</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>PVOID ProcessInformation,</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>ULONG ProcessInformationLength,</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>PULONG ReturnLength</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>)</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span style='font-family:宋体'>示例</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>下面的例子显示了如何调用</span><span lang=EN-US>CheckRemoteDebuggerPresent()</span><span style='font-family:宋体'>和</span><span lang=EN-US>NtQueryInformationProcess()</span><span style='font-family:宋体'>来检测当前进程是否被调试:</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>; using Kernel32!CheckRemoteDebuggerPresent()</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>lea eax,[.bDebuggerPresent]</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>push eax ;pbDebuggerPresent</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>push 0xffffffff ;hProcess</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>call [CheckRemoteDebuggerPresent]</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>cmp dword [.bDebuggerPresent],0</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>jne .debugger_found</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US> </span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>; using ntdll!NtQueryInformationProcess(ProcessDebugPort)</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>lea eax,[.dwReturnLen]</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>push eax ;ReturnLength</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>push 4 ;ProcessInformationLength</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>lea eax,[.dwDebugPort]</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>push eax ;ProcessInformation</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>push ProcessDebugPort ;ProcessInformationClass(7)</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>push 0xffffffff ;ProcessHandle</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>call [NtQueryInformationProcess]</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>cmp dword [.dwDebugPort],0</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>jne .debugger_found</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span style='font-family:宋体'>对策</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>一种方法是在</span><span lang=EN-US>NtQueryInformationProcess()</span><span style='font-family:宋体'>返回的地方设置断点,当这个断点被断下来后,将</span><span lang=EN-US>ProcessInformation </span><span style='font-family:宋体'>补丁为</span><span lang=EN-US>0</span><span style='font-family:宋体'>。</span> <span style='font-family:宋体'>下面是自动执行这个方法的</span><span lang=EN-US>ollyscript</span><span style='font-family:宋体'>示例:</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>var bp_NtQueryInformationProcess</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US> </span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>// set a breakpoint handler</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>eob bp_handler_NtQueryInformationProcess</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US> </span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>// set a breakpoint where NtQueryInformationProcess returns</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>gpa "NtQueryInformationProcess","ntdll.dll"</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>find $RESULT,#C21400# //retn 14</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>mov bp_NtQueryInformationProcess,$RESULT</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>bphws bp_NtQueryInformationProcess,"X"</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>run</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US> </span></p> <p class=MsoNormal><span lang=EN-US>bp_handler_NtQueryInformationProcess:</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US> </span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>//ProcessInformationClass == ProcessDebugPort?</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>cmp [esp+8],7</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>jne bp_handler_NtQueryInformationProcess_continue</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US> </span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>//patch ProcessInformation to 0</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>mov patch_addr,[esp+c]</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>mov [patch_addr],0</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US> </span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>// clear breakpoint</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>bphwc bp_NtQueryInformationProcess</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US> </span></p> <p class=MsoNormal><span lang=EN-US>bp_handler_NtQueryInformationProcess_continue:</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>run</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span lang=EN-US>Olly Advanced</span><span style='font-family:宋体'>插件有一个</span><span lang=EN-US>patch NtQueryInformationProcess()</span><span style='font-family:宋体'>的选项,这个补丁涉及注入一段代码来操纵</span><span lang=EN-US>NtQueryInformationProcess()</span><span style='font-family:宋体'>的返回值。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span lang=EN-US>2.4 Debugger Interrupts</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>在调试器中步过</span><span lang=EN-US>INT3</span><span style='font-family:宋体'>和</span><span lang=EN-US>INT1</span><span style='font-family:宋体'>指令的时候,由于调试器通常会处理这些调试中断,所以异常处理例程默认情况下将不会被调用,</span><span lang=EN-US>Debugger Interrupts</span><span style='font-family:宋体'>就利用了这个事实。这样壳可以在异常处理例程中设置标志,通过</span><span lang=EN-US>INT</span><span style='font-family:宋体'>指令后如果这些标志没有被设置则意味着进程正在被调试。另外,</span><span lang=EN-US>kernel32!DebugBreak()</span><span style='font-family:宋体'>内部是调用了</span><span lang=EN-US>INT3</span><span style='font-family:宋体'>来实现的,有些壳也会使用这个</span><span lang=EN-US>API</span><span style='font-family:宋体'>。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span style='font-family:宋体'>示例</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>这个例子在异常处理例程中设置</span><span lang=EN-US>EAX</span><span style='font-family:宋体'>的值为</span><span lang=EN-US>0xFFFFFFFF</span><span style='font-family:宋体'>(通过</span><span lang=EN-US>CONTEXT</span><sup><span lang=EN-US>6</span></sup><span style='font-family:宋体'>记录)以此来判断异常处理例程是否被调用:</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>; set exception handler</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>push .exeception_handler</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>push dword [fs:0]</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>mov [fs:0],esp</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US> </span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>;reset flag(EAX) invoke int3</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>xor eax,eax</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>int3</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US> </span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>;restore exception handler</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>pop dword [fs:0]</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>add esp,4</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US> </span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>; check if the flag had been set</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>test eax,eax</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>je .debugger_found</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>:::</span></p> <p class=MsoNormal><span lang=EN-US>.exeception_handler:</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>;EAX = ContextRecord</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>mov eax,[esp+0xc]</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>;set flag (ContextRecord.EAX)</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>mov dword [eax+0xb0],0xffffffff</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>;set ContextRecord.EIP</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>inc dword [eax+0xb8]</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>xor eax,eax</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>retn</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span style='font-family:宋体'>对策</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>由于调试中断而导致执行停止时,在</span><span lang=EN-US>OllyDbg</span><span style='font-family:宋体'>中识别出异常处理例程(通过视图</span><span lang=EN-US>->SEH</span><span style='font-family:宋体'>链)并下断点,然后</span><span lang=EN-US>Shift+F9</span><span style='font-family:宋体'>将调试中断</span><span lang=EN-US>/</span><span style='font-family:宋体'>异常传递给异常处理例程,最终异常处理例程中的断点会断下来,这时就可以跟踪了。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><img width=360 height=188 src="脱壳的艺术.files/image001.jpg" align=left hspace=12><span style='font-family: 宋体'>另一个方法是允许调试中断自动地传递给异常处理例程。在</span><span lang=EN-US>OllyDbg</span><span style='font-family:宋体'>中可以通过</span> <span style='font-family:宋体'>选项</span><span lang=EN-US>-> </span><span style='font-family:宋体'>调试选项</span><span lang=EN-US> -> </span><span style='font-family:宋体'>异常</span><span lang=EN-US> -> </span><span style='font-family:宋体'>忽略下列异常</span> <span style='font-family:宋体'>选项卡中钩选</span><span lang=EN-US>"INT3</span><span style='font-family:宋体'>中断</span><span lang=EN-US>"</span><span style='font-family:宋体'>和</span><span lang=EN-US>"</span><span style='font-family:宋体'>单步中断</span><span lang=EN-US>"</span><span style='font-family:宋体'>复选框来完成设置。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><span lang=EN-US> </span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><span lang=EN-US> </span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span lang=EN-US>2.5 Timing Checks</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>当进程被调试时,调试器事件处理代码、步过指令等将占用</span><span lang=EN-US>CPU</span><span style='font-family:宋体'>循环。如果相邻指令之间所花费的时间如果大大超出常规,就意味着进程很可能是在被调试,而壳正好利用了这一点。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span style='font-family:宋体'>示例</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>下面是一个简单的时间检查的例子。在某一段指令的前后用</span><span lang=EN-US>RDTSC</span><span style='font-family:宋体'>指令(</span><span lang=EN-US>Read Time-Stamp Counter</span><span style='font-family:宋体'>)并计算相应的增量。增量值</span><span lang=EN-US>0x200</span><span style='font-family:宋体'>取决于两个</span><span lang=EN-US>RDTSC</span><span style='font-family:宋体'>指令之间的代码执行量。</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>rdtsc</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>mov ecx,eax</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>mov ebx,edx</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US> </span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>;...more instructions</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>nop</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>push eax</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>pop eax</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>nop</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>;...more instructions</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US> </span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>;compute delta between RDTSC instructions</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>rdtsc</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US> </span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>;Check high order bits</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>cmp edx,ebx</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>ja .debugger_found</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>;Check low order bits</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>sub eax,ecx</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>cmp eax,0x200</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>ja .debugger_found</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>其它的时间检查手段包括使用</span><span lang=EN-US>kernel32!GetTickCount() API</span><span style='font-family:宋体'>,</span> <span style='font-family:宋体'>或者手工检查位于</span><span lang=EN-US>0x7FFE0000</span><span style='font-family:宋体'>地址的</span><span lang=EN-US>SharedUserData</span><sup><span lang=EN-US>7</span></sup><span style='font-family:宋体'>数据结构的</span><span lang=EN-US>TickCountLow </span><span style='font-family:宋体'>及</span><span lang=EN-US>TickCountMultiplier </span><span style='font-family:宋体'>成员。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>使用垃圾代码或者其它混淆技术进行隐藏以后,这些时间检查手段尤其是使用</span><span lang=EN-US>RDTSC</span><span style='font-family:宋体'>将会变得难于识别。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span style='font-family:宋体'>对策</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>一种方法就是找出时间检查代码的确切位置,避免步过这些代码。逆向分析人员可以在增量比较代码之前下断然后用</span> <span style='font-family:宋体'>运行</span> <span style='font-family:宋体'>代替</span> <span style='font-family:宋体'>步过</span> <span style='font-family:宋体'>直到断点断下来。另外也可以下</span><span lang=EN-US>GetTickCount()</span><span style='font-family:宋体'>断点以确定这个</span><span lang=EN-US>API</span><span style='font-family:宋体'>在什么地方被调用或者用来修改其返回值。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span lang=EN-US>Olly Advanced</span><span style='font-family:宋体'>采用另一种方法——它安装了一个内核模式驱动程序做以下工作:</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:17.95pt;text-indent:17.85pt'><span lang=EN-US>1 </span><span style='font-family:宋体'>设置控制寄存器</span><span lang=EN-US>CR4</span><sup><span lang=EN-US>8</span></sup><span style='font-family:宋体'>中的时间戳禁止位(</span><span lang=EN-US>TSD</span><span style='font-family:宋体'>),当这个位被设置后如果</span><span lang=EN-US>RDTSC</span><span style='font-family:宋体'>指令在非</span><span lang=EN-US>Ring0</span><span style='font-family:宋体'>下执行将会触发一个通用保护异常(</span><span lang=EN-US>GP</span><span style='font-family:宋体'>)。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:17.95pt;text-indent:17.85pt'><span lang=EN-US>2 </span><span style='font-family:宋体'>中断描述表(</span><span lang=EN-US>IDT</span><span style='font-family:宋体'>)被设置以挂钩</span><span lang=EN-US>GP</span><span style='font-family:宋体'>异常并且</span><span lang=EN-US>RTDSC</span><span style='font-family:宋体'>的执行被过滤。如果是由于</span><span lang=EN-US>RDTSC</span><span style='font-family:宋体'>指令引发的</span><span lang=EN-US>GP</span><span style='font-family:宋体'>,那么仅仅将前次调用返回的时间戳加</span><span lang=EN-US>1</span><span style='font-family:宋体'>。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.85pt'><span style='font-family:宋体'>值得注意的是上面讨论的驱动可能会导致系统不稳定,应该始终在非生产机器或虚拟机中进行尝试。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span lang=EN-US>2.6 SeDebugPrivilege</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>默认情况下进程是没有</span><span lang=EN-US>SeDebugPrivilege</span><span style='font-family:宋体'>权限的。然而进程通过</span><span lang=EN-US>OllyDbg</span><span style='font-family:宋体'>和</span><span lang=EN-US>WinDbg</span><span style='font-family:宋体'>之类的调试器载入的时候,</span><span lang=EN-US>SeDebugPrivilege</span><span style='font-family:宋体'>权限被启用了。这种情况是由于调试器本身会调整并启用</span><span lang=EN-US>SeDebugPrivilege</span><span style='font-family:宋体'>权限,当被调试进程加载时</span><span lang=EN-US>SeDebugPrivilege</span><span style='font-family:宋体'>权限也被继承了。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>一些壳通过打开</span><span lang=EN-US>CSRSS.EXE</span><span style='font-family:宋体'>进程间接地使用</span><span lang=EN-US>SeDebugPrivilege</span><span style='font-family:宋体'>确定进程是否被调试。如果能够打开</span><span lang=EN-US>CSRSS.EXE</span><span style='font-family:宋体'>意味着进程启用了</span><span lang=EN-US>SeDebugPrivilege</span><span style='font-family:宋体'>权限,由此可以推断进程正在被调试。这个检查能起作用是因为</span><span lang=EN-US>CSRSS.EXE</span><span style='font-family:宋体'>进程安全描述符只允许</span><span lang=EN-US>SYSTEM</span><span style='font-family:宋体'>访问,但是一旦进程拥有了</span><span lang=EN-US>SeDebugPrivilege</span><span style='font-family:宋体'>权限,就可以忽视安全描述符</span><sup><span lang=EN-US>9</span></sup><span style='font-family:宋体'>而访问其它进程。注意默认情况下这一权限仅仅授予了</span><span lang=EN-US>Administrators</span><span style='font-family:宋体'>组的成员。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span style='font-family:宋体'>示例</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>下面是</span><span lang=EN-US>SeDebugPrivilege</span><span style='font-family:宋体'>检查的例子:</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>;query for the PID of CSRSS.EXE</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>call [CsrGetProcessId]</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US> </span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>;try to open the CSRSS.EXE process</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>push eax</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>push FALSE</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>push PROCESS_QUERY_INFORMATION</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>call [OpenProcess]</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US> </span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>;if OpenProcess() was successful,</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>;process is probably being debugged</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>test eax,eax</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>jnz .debugger_found</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>这里使用了</span><span lang=EN-US>ntdll!CsrGetProcessId() API</span><span style='font-family:宋体'>获取</span><span lang=EN-US>CSRSS.EXE</span><span style='font-family:宋体'>的</span><span lang=EN-US>PID</span><span style='font-family:宋体'>,但是壳也可能通过手工枚举进程来得到</span><span lang=EN-US>CSRSS.EXE</span><span style='font-family:宋体'>的</span><span lang=EN-US>PID</span><span style='font-family:宋体'>。如果</span><span lang=EN-US>OpenProcess()</span><span style='font-family:宋体'>成功则意味着</span><span lang=EN-US>SeDebugPrivilege</span><span style='font-family:宋体'>权限被启用,这也意味着进程很可能被调试。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span style='font-family:宋体'>对策</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>一种方法是在</span><span lang=EN-US>ntdll!NtOpenProcess()</span><span style='font-family:宋体'>返回的地方设断点,一旦断下来后,如果传入的是</span><span lang=EN-US>CSRSS.EXE</span><span style='font-family:宋体'>的</span><span lang=EN-US>PID</span><span style='font-family:宋体'>则修改</span><span lang=EN-US>EAX</span><span style='font-family:宋体'>值为</span><span lang=EN-US>0xC0000022</span><span style='font-family:宋体'>(</span><span lang=EN-US>STATUS_ACCESS_DENIED)</span><span style='font-family:宋体'>。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span lang=EN-US>2.7 Parent Process</span></b><b><span style='font-family:宋体'>(检测父进程)</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>通常进程的父进程是</span><span lang=EN-US>explorer.exe</span><span style='font-family:宋体'>(双击执行的情况下),父进程不是</span><span lang=EN-US>explorer.exe</span><span style='font-family:宋体'>说明程序是由另一个不同的应用程序打开的,这很可能就是程序被调试了。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>下面是实现这种检查的一种方法:</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:35.9pt'><span lang=EN-US>1 </span><span style='font-family:宋体'>通过</span><span lang=EN-US>TEB(TEB.ClientId)</span><span style='font-family:宋体'>或者使用</span><span lang=EN-US>GetCurrentProcessId()</span><span style='font-family:宋体'>来检索当前进程的</span><span lang=EN-US>PID</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:17.95pt;text-indent:17.85pt'><span lang=EN-US>2 </span><span style='font-family:宋体'>用</span><span lang=EN-US>Process32First/Next()</span><span style='font-family:宋体'>得到所有进程的列表,注意</span><span lang=EN-US>explorer.exe</span><span style='font-family:宋体'>的</span><span lang=EN-US>PID</span><span style='font-family:宋体'>(通过</span><span lang=EN-US>PROCESSENTRY32.szExeFile</span><span style='font-family:宋体'>)和通过</span><span lang=EN-US>PROCESSENTRY32.th32ParentProcessID</span><span style='font-family:宋体'>获得的当前进程的父进程</span><span lang=EN-US>PID</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:17.95pt;text-indent:17.85pt'><span lang=EN-US>3 </span><span style='font-family:宋体'>如果父进程的</span><span lang=EN-US>PID</span><span style='font-family:宋体'>不是</span><span lang=EN-US>explorer.exe</span><span style='font-family:宋体'>的</span><span lang=EN-US>PID</span><span style='font-family:宋体'>,则目标进程很可能被调试</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>但是请注意当通过命令行提示符或默认外壳非</span><span lang=EN-US>explorer.exe</span><span style='font-family:宋体'>的情况下启动可执行程序时,这个调试器检查会引起误报。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span style='font-family:宋体'>对策</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span lang=EN-US>Olly Advanced</span><span style='font-family:宋体'>提供的方法是让</span><span lang=EN-US>Process32Next()</span><span style='font-family:宋体'>总是返回</span><span lang=EN-US>fail</span><span style='font-family:宋体'>,这样壳的进程枚举代码将会失效,由于进程枚举失效</span><span lang=EN-US>PID</span><span style='font-family:宋体'>检查将会被跳过。这些是通过补丁</span><span lang=EN-US> kernel32!Process32NextW()</span><span style='font-family:宋体'>的入口代码(将</span><span lang=EN-US>EAX</span><span style='font-family:宋体'>值设为</span><span lang=EN-US>0</span><span style='font-family:宋体'>然后直接返回)实现的。</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>77E8D1C2 > 33C0 xor eax, eax</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>77E8D1C4 C3 retn</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>77E8D1C5 83EC 0C sub esp, 0C</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span lang=EN-US>2.8 DebugObject: NtQueryObject()</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>除了识别进程是否被调试之外,其他的调试器检测技术牵涉到检查系统当中是否有调试器正在运行。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>逆向论坛中讨论的一个有趣的方法就是检查</span><span lang=EN-US>DebugObject</span><sup><span lang=EN-US>10</span></sup><span style='font-family:宋体'>类型内核对象的数量。这种方法之所以有效是因为每当一个应用程序被调试的时候,将会为调试对话在内核中创建一个</span><span lang=EN-US>DebugObject</span><span style='font-family:宋体'>类型的对象。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span lang=EN-US>DebugObject</span><span style='font-family:宋体'>的数量可以通过</span><span lang=EN-US>ntdll!NtQueryObject()</span><span style='font-family:宋体'>检索所有对象类型的信息而获得。</span><span lang=EN-US>NtQueryObject</span><span style='font-family:宋体'>接受</span><span lang=EN-US>5</span><span style='font-family:宋体'>个参数,为了查询所有的对象类型,</span><span lang=EN-US>ObjectHandle</span><span style='font-family:宋体'>参数被设为</span><span lang=EN-US>NULL</span><span style='font-family:宋体'>,</span><span lang=EN-US>ObjectInformationClass</span><span style='font-family:宋体'>参数设为</span><span lang=EN-US>ObjectAllTypeInformation(3)</span><span style='font-family:宋体'>:</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>NTSTATUS NTAPI NtQueryObject(</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>HANDLE ObjectHandle,</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>OBJECT_INFORMATION_CLASS ObjectInformationClass,</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>PVOID ObjectInformation,</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>ULONG Length,</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>PULONG ResultLength</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>)</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>这个</span><span lang=EN-US>API</span><span style='font-family:宋体'>返回一个</span><span lang=EN-US>OBJECT_ALL_INFORMATION</span><span style='font-family:宋体'>结构,其中</span><span lang=EN-US>NumberOfObjectsTypes</span><span style='font-family:宋体'>成员为所有的对象类型在</span><span lang=EN-US>ObjectTypeInformation</span><span style='font-family:宋体'>数组中的计数:</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>typedef struct _OBJECT_ALL_INFORMATION{</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>ULONG NumberOfObjectsTypes;</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>OBJECT_TYPE_INFORMATION ObjectTypeInformation[1];</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>}</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>检测例程将遍历拥有如下结构的</span><span lang=EN-US>ObjectTypeInformation</span><span style='font-family:宋体'>数组:</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>typedef struct _OBJECT_TYPE_INFORMATION{</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>[00] UNICODE_STRING TypeName;</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>[08] ULONG TotalNumberofHandles;</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>[0C] ULONG TotalNumberofObjects;</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>...more fields...</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>}</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span lang=EN-US>TypeName</span><span style='font-family:宋体'>成员与</span><span lang=EN-US>UNICODE</span><span style='font-family:宋体'>字符串</span><span lang=EN-US>"DebugObject"</span><span style='font-family:宋体'>比较,然后检查</span><span lang=EN-US>TotalNumberofObjects </span><span style='font-family:宋体'>或</span><span lang=EN-US> TotalNumberofHandles </span><span style='font-family:宋体'>是否为非</span><span lang=EN-US>0</span><span style='font-family:宋体'>值。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span style='font-family:宋体'>对策</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>与</span><span lang=EN-US>NtQueryInformationProcess()</span><span style='font-family:宋体'>解决方法类似,在</span><span lang=EN-US>NtQueryObject()</span><span style='font-family:宋体'>返回处设断点,然后补丁</span> <span style='font-family:宋体'>返回的</span><span lang=EN-US>OBJECT_ALL_INFORMATION</span><span style='font-family:宋体'>结构,另外</span><span lang=EN-US>NumberOfObjectsTypes</span><span style='font-family:宋体'>成员可以置为</span><span lang=EN-US>0</span><span style='font-family:宋体'>以防止壳遍历</span><span lang=EN-US>ObjectTypeInformation</span><span style='font-family:宋体'>数组。可以通过创建一个类似于</span><span lang=EN-US>NtQueryInformationProcess()</span><span style='font-family:宋体'>解决方法的</span><span lang=EN-US>ollyscript</span><span style='font-family:宋体'>脚本来执行这个操作。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>类似地,</span><span lang=EN-US>Olly Advanced</span><span style='font-family:宋体'>插件向</span><span lang=EN-US>NtQueryObject() API</span><span style='font-family:宋体'>中注入代码,如果检索的是</span><span lang=EN-US>ObjectAllTypeInformation</span><span style='font-family:宋体'>类型则用</span><span lang=EN-US>0</span><span style='font-family:宋体'>清空整个返回的缓冲区。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span lang=EN-US>2.9 Debugger Window</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>调试器窗口的存在标志着有调试器正在系统内运行。由于调试器创建的窗口拥有特定类名(</span><span lang=EN-US>OllyDbg</span><span style='font-family:宋体'>的是</span><span lang=EN-US>OLLYDBG</span><span style='font-family:宋体'>,</span><span lang=EN-US>WinDbg</span><span style='font-family:宋体'>的是</span><span lang=EN-US>WinDbgFrameClass</span><span style='font-family:宋体'>),使用</span><span lang=EN-US>user32!FindWindow()</span><span style='font-family:宋体'>或者</span><span lang=EN-US>user32!FindWindowEx()</span><span style='font-family:宋体'>能很容易地识别这些调试器窗口。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span style='font-family:宋体'>示例</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>下面的示例代码使用</span><span lang=EN-US>FindWindow()</span><span style='font-family:宋体'>查找</span><span lang=EN-US>OllyDbg</span><span style='font-family:宋体'>或</span><span lang=EN-US>WinDbg</span><span style='font-family:宋体'>创建的窗口来识别他们是否正在系统中运行。</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>push NULL</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>push .szWindowClassOllyDbg</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>call [FindWindowA]</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>test eax,eax</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>jnz .debugger_found</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US> </span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>push NULL</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>push .szWindowClassWinDbg</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>call [FindWindowA]</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>test eax,eax</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>jnz .debugger_found</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US> </span></p> <p class=MsoNormal><span lang=EN-US>.szWindowClassOllyDbg db “OLLYDBG”,0</span></p> <p class=MsoNormal><span lang=EN-US>.szWindowClassWinDbg db “WinDbgFrameClass”,0</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span style='font-family:宋体'>对策</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>一种方法是在</span><span lang=EN-US>FindWindow</span><span style='font-family:宋体'>()</span><span lang=EN-US>/FindWindowEx</span><span style='font-family:宋体'>()的入口处设断点,断下来后,改变</span><span lang=EN-US>lpClassName</span><span style='font-family:宋体'>参数的内容,这样</span><span lang=EN-US>API</span><span style='font-family:宋体'>将会返回</span><span lang=EN-US>fail</span><span style='font-family:宋体'>,另一种方法就是直接将返回值设为</span><span lang=EN-US>NULL</span><span style='font-family:宋体'>。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span lang=EN-US>2.10 Debugger Process</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>另外一种识别系统内是否有调试器正在运行的方法是列出所有的进程,检查进程名是否与调试器(如</span><span lang=EN-US> OLLYDBG.EXE,windbg.exe</span><span style='font-family:宋体'>等)的相符。实现很直接,利用</span><span lang=EN-US>Process32First/Next()</span><span style='font-family:宋体'>然后检查映像名称是否与调试器相符就行了。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>有些壳也会利用</span><span lang=EN-US>kernel32!ReadProcessMemory()</span><span style='font-family:宋体'>读取进程的内存,然后寻找调试器相关的字符串(如</span><span lang=EN-US>”OLLYDBG”</span><span style='font-family:宋体'>)以防止逆向分析人员修改调试器的可执行文件名。一旦发现调试器的存在,壳要么显示一条错误信息,要么默默地退出或者终止调试器进程。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span style='font-family:宋体'>对策</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>和父进程检查类似,可以通过补丁</span><span lang=EN-US> kernel32!Process32NextW() </span><span style='font-family:宋体'>使其总是返回</span><span lang=EN-US>fail</span><span style='font-family:宋体'>值来防止壳枚举进程。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span lang=EN-US>2.11 Device Drivers</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>检测内核模式的调试器是否活跃于系统中的典型技术是访问他们的设备驱动程序。该技术相当简单,仅涉及调用</span><span lang=EN-US>kernel32!CreateFile()</span><span style='font-family:宋体'>检测内核模式调试器(如</span><span lang=EN-US>SoftICE</span><span style='font-family:宋体'>)使用的那些众所周知的设备名称。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span style='font-family:宋体'>示例</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>一个简单的检查如下:</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>push NULL</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>push 0</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>push OPEN_EXISTING</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>push NULL</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>push FILE_SHARE_READ</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>push GENERIC_READ</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>push .szDeviceNameNtice</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>call [CreateFileA]</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>cmp eax,INVALID_HANDLE_VALUE</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>jne .debugger_found</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US> </span></p> <p class=MsoNormal><span lang=EN-US>.szDeviceNameNtice db "\\.\NTICE",0</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>某些版本的</span><span lang=EN-US>SoftICE</span><span style='font-family:宋体'>会在设备名称后附加数字导致这种检查失败,逆向论坛中相关的描述是穷举附加的数字直到发现正确的设备名称。新版壳也用设备驱动检测技术检测诸如</span><span lang=EN-US>Regmon</span><span style='font-family:宋体'>和</span><span lang=EN-US>Filemon</span><span style='font-family:宋体'>之类的系统监视程序的存在。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span style='font-family:宋体'>对策</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>一种简单的方法就是在</span><span lang=EN-US>kernel32!CreateFileW()</span><span style='font-family:宋体'>内设置断点,断下来后,要么操纵</span><span lang=EN-US>FileName</span><span style='font-family:宋体'>参数要么改变其返回值为</span><span lang=EN-US>INVALID_HANDLE_VALUE</span><span style='font-family:宋体'>(</span><span lang=EN-US>0xFFFFFFFF</span><span style='font-family:宋体'>)。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span lang=EN-US>2.12 OllyDbg</span></b><b><span style='font-family:宋体'>:</span><span lang=EN-US>Guard Pages</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>这个检查是针对</span><span lang=EN-US>OllyDbg</span><span style='font-family:宋体'>的,因为它和</span><span lang=EN-US>OllyDbg</span><span style='font-family:宋体'>的内存访问</span><span lang=EN-US>/</span><span style='font-family:宋体'>写入断点特性相关。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>除了硬件断点和软件断点外,</span><span lang=EN-US>OllyDbg</span><span style='font-family:宋体'>允许设置一个内存访问</span><span lang=EN-US>/</span><span style='font-family:宋体'>写入断点,这种类型的断点是通过页面保护</span><sup><span lang=EN-US>11</span></sup><span style='font-family:宋体'>来实现的。简单地说,页面保护提供了当应用程序的某块内存被访问时获得通知这样一个途径。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>页面保护是通过</span><span lang=EN-US>PAGE_GUARD</span><span style='font-family:宋体'>页面保护修改符来设置的,如果访问的内存地址是受保护页面的一部分,将会产生一个</span><span lang=EN-US>STATUS_GUARD_PAGE_VIOLATION(0x80000001)</span><span style='font-family:宋体'>异常。如果进程被</span><span lang=EN-US>OllyDbg</span><span style='font-family:宋体'>调试并且受保护的页面被访问,将不会抛出异常,访问将会被当作内存断点来处理,而壳正好利用了这一点。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span style='font-family:宋体'>示例</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>下面的示例代码中,将会分配一段内存,并将待执行的代码保存在分配的内存中,然后启用页面的</span><span lang=EN-US>PAGE_GUARD</span><span style='font-family:宋体'>属性。接着初始化标设符</span><span lang=EN-US>EAX</span><span style='font-family:宋体'>为</span><span lang=EN-US>0</span><span style='font-family:宋体'>,然后通过执行内存中的代码来引发</span><span lang=EN-US>STATUS_GUARD_PAGE_VIOLATION</span><span style='font-family:宋体'>异常。如果代码在</span><span lang=EN-US>OllyDbg</span><span style='font-family:宋体'>中被调试,因为异常处理例程不会被调用所以标设符将不会改变。</span></p> <p class=MsoNormal style='margin-left:.1pt;text-indent:35.9pt'><span lang=EN-US>;set up exception handler</span></p> <p class=MsoNormal style='margin-left:.1pt;text-indent:35.9pt'><span lang=EN-US>push .exception_handle</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>push dword [fs:0]</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>mov [fs:0],esp</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US> </span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>;allocate memory</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>push PAGE_READWRITE</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>push MEM_COMMIT</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>push 0x1000</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>push NULL</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>call [VirtualAlloc]</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>test eax,eax</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>jz .failed</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>mov [.pAllocatedMem],eax</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US> </span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>;store a RETN on the allocated memory</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>mov byte [eax],0xC3</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>;then set the PAGE_GUARD attribute of the allocated memory</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>lea eax,[.dwOldProtect]</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>push eax</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>push PAGE_EXECUTE_READ | PAGE_GUARD</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>push 0x1000</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>push dword [.pAllocatedMem]</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>call [VirtualProtect]</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US> </span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>;set marker (EAX) as 0</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>xor eax,eax</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>;trigger a STATUS_GUARD_PAGE_VIOLATION exception</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>call [.pAllocatedMem]</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>;check if marker had not been changed (exception handler not called)</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>test eax,eax</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>je .debugger_found</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US> </span></p> <p class=MsoNormal><span lang=EN-US>.exception_handler</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>;EAX = CONTEXT record</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>mov eax,[esp+0xC]</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>;set marker (CONTEXT.EAX) to 0xFFFFFFFF</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>;to signal that the exception handler was called</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>mov dword [eax+0xb0],0xFFFFFFFF</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>xor eax,eax</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>retn</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span style='font-family:宋体'>对策</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>由于页面保护引发一个异常,逆向分析人员可以故意引发一个异常,这样异常处理例程将会被调用。在示例中,逆向分析人员可以用</span><span lang=EN-US>INT3</span><span style='font-family:宋体'>指令替换掉</span><span lang=EN-US>RETN</span><span style='font-family:宋体'>指令,一旦</span><span lang=EN-US>INT3</span><span style='font-family:宋体'>指令被执行,</span><span lang=EN-US>Shift+F9</span><span style='font-family:宋体'>强制调试器执行异常处理代码。这样当异常处理例程调用后,</span><span lang=EN-US>EAX</span><span style='font-family:宋体'>将被设为正确的值,然后</span><span lang=EN-US>RETN</span><span style='font-family:宋体'>指令将会被执行。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>如果异常处理例程里检查异常是否真地是</span><span lang=EN-US>STATUS_GUARD_PAGE_VIOLATION</span><span style='font-family:宋体'>,逆向分析人员可以在异常处理例程中下断点然后修改传入的</span><span lang=EN-US>ExceptionRecord</span><span style='font-family:宋体'>参数,具体来说就是</span><span lang=EN-US>ExceptionCode</span><span style='font-family:宋体'>,</span> <span style='font-family:宋体'>手工将</span><span lang=EN-US>ExceptionCode</span><span style='font-family:宋体'>设为</span><span lang=EN-US>STATUS_GUARD_PAGE_VIOLATION</span><span style='font-family:宋体'>即可。</span></p> <p class=MsoNormal style='margin-top:23.4pt;margin-right:0cm;margin-bottom: 15.6pt;margin-left:0cm'><b><span lang=EN-US style='color:white;background:black'>3 </span></b><b><span style='font-family:宋体;color:white;background:black'>断点和补丁检测技术</span><span lang=EN-US style='color:white;background:black'> </span></b></p> <p class=MsoNormal style='margin-bottom:7.8pt;text-indent:17.95pt'><span style='font-family:宋体'>本节列举了壳最常用的识别软件断点、硬件断点和补丁的方法。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span lang=EN-US>3.1 Software Breakpoint Detection </span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>软件断点是通过修改目标地址代码为</span><span lang=EN-US>0xCC</span><span style='font-family:宋体'>(</span><span lang=EN-US>INT3/Breakpoint Interrupt</span><span style='font-family:宋体'>)来设置的断点。壳通过在受保护的代码段和(或)</span><span lang=EN-US>API</span><span style='font-family:宋体'>函数中扫描字节</span><span lang=EN-US>0xCC</span><span style='font-family:宋体'>来识别软件断点。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span style='font-family:宋体'>示例</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>检测可能和下面一样简单:</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>cld</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>mov edi,Protected_Code_Start</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>mov ecx,Protected_Code_End - Protected_Code_Start</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>mov al,0xcc</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>repne scasb</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>jz .breakpoint_found</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>有些壳对比较的字节值作了些运算使得检测变得不明显,例如:</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>if ( byte XOR 0x55 == 0x99 ) then breakpoint found</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>Where: 0x99 == 0xCC XOR 0x55</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span style='font-family:宋体'>对策</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>如果软件断点被发现了逆向分析人员可以使用硬件断点来代替。如果需要在</span><span lang=EN-US>API</span><span style='font-family:宋体'>内部下断,但是壳又检测</span><span lang=EN-US>API</span><span style='font-family:宋体'>内部的断点,逆向分析人员可以在最终被</span><span lang=EN-US>ANSI</span><span style='font-family:宋体'>版</span><span lang=EN-US>API</span><span style='font-family:宋体'>调用的</span><span lang=EN-US>UNICODE</span><span style='font-family:宋体'>版的</span><span lang=EN-US>API</span><span style='font-family:宋体'>下断(如:用</span><span lang=EN-US>LoadLibraryExW</span><span style='font-family:宋体'>代替</span><span lang=EN-US>LoadLibraryA</span><span style='font-family:宋体'>),或者用相应的</span><span lang=EN-US>native API</span><span style='font-family:宋体'>来代替。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span lang=EN-US>3.2 Hardware Breakpoint Detection</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>另一种断点称之为硬件断点,硬件断点是通过设置名为</span><span lang=EN-US>Dr0</span><span style='font-family:宋体'>到</span><span lang=EN-US>Dr7</span><span style='font-family:宋体'>的调试寄存器</span><sup><span lang=EN-US>12</span></sup><span style='font-family:宋体'>来实现的。</span><span lang=EN-US>Dr0-Dr3</span><span style='font-family:宋体'>包含至多</span><span lang=EN-US>4</span><span style='font-family:宋体'>个断点的地址,</span><span lang=EN-US>Dr6</span><span style='font-family:宋体'>是个标志,它指示哪个断点被触发了,</span><span lang=EN-US>Dr7</span><span style='font-family:宋体'>包含了控制</span><span lang=EN-US>4</span><span style='font-family:宋体'>个硬件断点诸如启用</span><span lang=EN-US>/</span><span style='font-family:宋体'>禁用或者中断于读</span><span lang=EN-US>/</span><span style='font-family:宋体'>写的标志。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>由于调试寄存器无法在</span><span lang=EN-US>Ring3</span><span style='font-family:宋体'>下访问,硬件断点的检测需要执行一小段代码。壳利用了含有调试寄存器值的</span><span lang=EN-US>CONTEXT</span><span style='font-family:宋体'>结构,</span><span lang=EN-US>CONTEXT</span><span style='font-family:宋体'>结构可以通过传递给异常处理例程的</span><span lang=EN-US>ContextRecord</span><span style='font-family:宋体'>参数来访问。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><span style='font-family:宋体'>示例</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>这是一段查询调试寄存器的示例代码:</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>; set up exception handler</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>push .exception_handler</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>push dword [fs:0]</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>mov [fs:0],esp</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US> </span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>;eax will be 0xFFFFFFFF if hardware breakpoints are identified</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>xor eax,eax</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US> </span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>;throw an exception</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>mov dword [eax],0</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US> </span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>;restore exception handler</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>pop dword [fs:0]</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>add esp,4</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US> </span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>;test if EAX was updated (breakpoint identified) </span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>test eax,eax</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>jnz .breakpoint_found</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>:::</span></p> <p class=MsoNormal><span lang=EN-US>.exception_handler</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>;EAX = CONTEXT record</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>mov eax,[esp+0xc]</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US> </span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>;check if Debug Registers Context.Dr0-Dr3 is not zero</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>cmp dword [eax+0x04],0</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>jne .hardware_bp_found</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>cmp dword [eax+0x08],0</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>jne .hardware_bp_found</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>cmp dword [eax+0x0c],0</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>jne .hardware_bp_found</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>cmp dword [eax+0x10],0</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>jne .hardware_bp_found</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>jmp .exception_ret</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US> </span></p> <p class=MsoNormal><span lang=EN-US>.hardware_bp_found</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>;set Context.EAX to signal breakpoint found</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>mov dword [eax+0xb0],0xFFFFFFFF</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US> </span></p> <p class=MsoNormal><span lang=EN-US>.exception_ret</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>;set Context.EIP upon return</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>add dword [eax+0xb8],6</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>xor eax,eax</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>retn</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>有些壳也利用调试寄存器的值作为解密密钥的一部分。这些调试寄存器要么初始化为一个特定值要么为</span><span lang=EN-US>0</span><span style='font-family:宋体'>。因此,如果这些调试寄存器被修改,解密将会失败。当解密的代码是受保护的程序或者脱壳代码的一部分的时候,将导致无效指令并造成程序一些意想不到的终止。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span style='font-family:宋体'>对策</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>如果壳没检测软件断点,逆向分析人员可以尝试使用软件断点,同样</span><span lang=EN-US>OllyDbg</span><span style='font-family:宋体'>的内存读</span><span lang=EN-US>/</span><span style='font-family:宋体'>写断点也可以使用。当逆向分析人员需要设置</span><span lang=EN-US>API</span><span style='font-family:宋体'>断点的时候在</span><span lang=EN-US>native</span><span style='font-family:宋体'>或者是</span><span lang=EN-US>UNICODE</span><span style='font-family:宋体'>版的</span><span lang=EN-US>API</span><span style='font-family:宋体'>内部设软件断点也是可行的。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span lang=EN-US>3.3 Patching Detection via Code Checksum Calculation</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>补丁检测技术能识别壳的代码是否被修改(代码被修改则意味着反调试例程已经被禁用了),其次也能识别是否设置了软件断点。补丁检测是通过代码校验来实现的,校验计算包括从简单到复杂的校验和</span><span lang=EN-US>/</span><span style='font-family:宋体'>哈希算法。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span style='font-family:宋体'>示例</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>下面是一个比较简单的校验和计算的例子:</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>mov esi,Protected_Code_Start</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>mov ecx,Protected_Code_End - Protected_Code_Start</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>xor eax,eax</span></p> <p class=MsoNormal><span lang=EN-US>.checksum_loop</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>movzx ebx,byte [esi]</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>add eax,ebx</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>rol eax,1</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>inc esi</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>loop .checksum_loop</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US> </span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>cmp eax,dword [.dwCorrectChecksum]</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>jne .patch_found</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span style='font-family:宋体'>对策</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>如果代码校验例程识别出了软件断点,可以用硬件断点来代替。如果校验例程识别出了代码补丁,逆向分析人员可以通过在补丁地址设置内存访问断点来定位校验例程所在,一旦发现了校验例程,可以修改校验和为预期的值或者在比较失败后修改适当的标志。</span></p> <p class=MsoNormal style='margin-top:23.4pt;margin-right:0cm;margin-bottom: 15.6pt;margin-left:0cm'><span lang=EN-US style='color:white;background:black'>4</span><span style='font-family:宋体;color:white;background:black'>反分析技术</span><span lang=EN-US style='color:white;background:black'> </span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>反分析技术的目标是减缓逆向分析人员对受保护代码和(或)加壳后的程序分析和理解的速度。我们将讨论诸如加密</span><span lang=EN-US>/</span><span style='font-family:宋体'>压缩、垃圾代码、代码变形、反</span><span lang=EN-US>-</span><span style='font-family:宋体'>反编译等技术,这些技术的目的是为了混淆代码、考验耐心、浪费逆向分析人员的时间,解决这些问题需要逆向分析人员拥有耐心、聪慧等品质。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span lang=EN-US>4.1 Encryption and Compression</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>加密和压缩是最基本的反分析形式。它们初步设防,防止逆向分析人员直接在反编译器内加载受保护的程序然后没有任何困难地开始分析。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span style='font-family:宋体'>加密</span></b> <span style='font-family:宋体'>壳通常都既加密本身代码也加密受保护的程序。不同的壳所采用的加密算法大不相同,有非常简单的</span><span lang=EN-US>XOR</span><span style='font-family:宋体'>循环,也有执行数次运算的非常复杂的循环。对于某些多态变形壳,为了防止查壳工具正确地识别壳,每次加壳所采用的加密算法都不同,解密代码也通过变形显得很不一样。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>解密例程作为一个取数、计算、存诸操作的循环很容易辨认。下面是一个对加密过的</span><span lang=EN-US>DWORD</span><span style='font-family:宋体'>值执行数次</span><span lang=EN-US>XOR</span><span style='font-family:宋体'>操作的简单的解密例程。</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0040A</span><span lang=EN-US>07C</span><span lang=EN-US> LODS DWORD PTR DS:[ESI]</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0040A</span><span lang=EN-US>07D XOR EAX,EBX</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0040A</span><span lang=EN-US>07F</span><span lang=EN-US> SUB EAX,12338CC3</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0040A</span><span lang=EN-US>084 ROL EAX,10</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0040A</span><span lang=EN-US>087 XOR EAX,799F82D0</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0040A</span><span lang=EN-US>08C</span><span lang=EN-US> STOS DWORD PTR ES:[EDI]</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0040A</span><span lang=EN-US>08D INC EBX</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0040A</span><span lang=EN-US>08E LOOPD SHORT 0040A07C ;decryption loop</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>这里是另一个多态变形壳的解密例程:</span></p> <p class=MsoNormal style='text-indent:35.95pt'><b><span lang=EN-US>00476056 MOV BH,BYTE PTR DS:[EAX]</span></b></p> <p class=MsoNormal style='text-indent:35.8pt'><span lang=EN-US>00476058 INC ESI</span></p> <p class=MsoNormal style='text-indent:35.8pt'><span lang=EN-US>00476059 ADD BH,0BD</span></p> <p class=MsoNormal style='text-indent:35.8pt'><span lang=EN-US>0047605C</span><span lang=EN-US> XOR BH,CL</span></p> <p class=MsoNormal style='text-indent:35.8pt'><span lang=EN-US>0047605E INC ESI</span></p> <p class=MsoNormal style='text-indent:35.8pt'><span lang=EN-US>0047605F</span><span lang=EN-US> DEC EDX</span></p> <p class=MsoNormal style='text-indent:35.95pt'><b><span lang=EN-US>00476060 MOV BYTE PTR DS:[EAX],BH</span></b></p> <p class=MsoNormal style='text-indent:35.8pt'><span lang=EN-US>00476062 CLC</span></p> <p class=MsoNormal style='text-indent:35.8pt'><span lang=EN-US>00476063 SHL EDI,CL</span></p> <p class=MsoNormal style='text-indent:35.8pt'><span lang=EN-US>:::More garbage code</span></p> <p class=MsoNormal style='text-indent:35.8pt'><span lang=EN-US>00476079 INC EDX</span></p> <p class=MsoNormal style='text-indent:35.8pt'><span lang=EN-US>0047607A</span><span lang=EN-US> DEC EDX</span></p> <p class=MsoNormal style='text-indent:35.95pt'><b><span lang=EN-US>0047607B DEC EAX</span></b></p> <p class=MsoNormal style='text-indent:35.8pt'><span lang=EN-US>0047607C</span><span lang=EN-US> JMP SHORT 0047607E</span></p> <p class=MsoNormal style='text-indent:35.8pt'><span lang=EN-US>0047607E DEC ECX</span></p> <p class=MsoNormal style='text-indent:35.95pt'><b><span lang=EN-US>0047607F</span></b><b><span lang=EN-US> JNZ 00476056 ;decryption loop </span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>下面是由同一个多态壳生成的另一段解密例程:</span></p> <p class=MsoNormal style='text-indent:35.95pt'><b><span lang=EN-US>0040C</span></b><b><span lang=EN-US>045 MOV CH,BYTE PTR DS:[EDI]</span></b></p> <p class=MsoNormal style='text-indent:35.8pt'><span lang=EN-US>0040C</span><span lang=EN-US>047 ADD EDX,EBX</span></p> <p class=MsoNormal style='text-indent:35.8pt'><span lang=EN-US>0040C</span><span lang=EN-US>049 XOR CH,AL</span></p> <p class=MsoNormal style='text-indent:35.8pt'><span lang=EN-US>0040C</span><span lang=EN-US>04B XOR CH,0D9</span></p> <p class=MsoNormal style='text-indent:35.8pt'><span lang=EN-US>0040C</span><span lang=EN-US>04E CLC</span></p> <p class=MsoNormal style='text-indent:35.95pt'><b><span lang=EN-US>0040C</span></b><b><span lang=EN-US>04F</span></b><b><span lang=EN-US> MOV BYTE PTR DS:[EDI],CH</span></b></p> <p class=MsoNormal style='text-indent:35.8pt'><span lang=EN-US>0040C</span><span lang=EN-US>051 XCHG AH,AH</span></p> <p class=MsoNormal style='text-indent:35.8pt'><span lang=EN-US>0040C</span><span lang=EN-US>053 BTR EDX,EDX</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0040C</span><span lang=EN-US>056 MOVSX EBX,CL</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>::: More garbage code</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0040C</span><span lang=EN-US>067 SAR EDX,CL</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0040C</span><span lang=EN-US>06C</span><span lang=EN-US> NOP</span></p> <p class=MsoNormal style='text-indent:36.05pt'><b><span lang=EN-US>0040C</span></b><b><span lang=EN-US>06D DEC EDI</span></b></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0040C</span><span lang=EN-US>06E DEC EAX</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0040C</span><span lang=EN-US>06F</span><span lang=EN-US> JMP SHORT 0040C071</span></p> <p class=MsoNormal style='text-indent:36.05pt'><b><span lang=EN-US>0040C</span></b><b><span lang=EN-US>071 JNZ 0040C045 ;decryption loop</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>上面两个示例中高亮的行是主要的解密指令,其余的指令都是用来迷惑逆向分析人员的垃圾代码。注意寄存器是如何交换的,还有两个示例之间解密方法是如何改变的。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span lang=EN-US>Compression</span></b><span lang=EN-US> </span><span style='font-family:宋体'>压缩的主要目的是为了缩小可执行文件代码和数据的大小,但是由于原始的包含可读字符串的可执行文件变成了压缩数据,因此也有那么一些混淆的作用。看看几款壳所使用的压缩引擎:</span><span lang=EN-US>UPX</span><span style='font-family:宋体'>使用</span><span lang=EN-US>NRV</span><span style='font-family:宋体'>(</span><span lang=EN-US>Not Really Vanished)</span><span style='font-family:宋体'>和</span><span lang=EN-US>LZMA(Lempel-Ziv-Markov chain-Algorithm)</span><span style='font-family:宋体'>,</span><span lang=EN-US>FSG</span><span style='font-family:宋体'>使用</span><span lang=EN-US>aPLib</span><span style='font-family:宋体'>,</span><span lang=EN-US>Upack</span><span style='font-family:宋体'>使用</span><span lang=EN-US>LZMA</span><span style='font-family:宋体'>,</span><span lang=EN-US>yoda</span><span style='font-family:宋体'>加密壳使用</span><span lang=EN-US>LZO</span><span style='font-family:宋体'>。这其中有些压缩引擎可以自由地使用于非商业应用,但是商业应用需要许可</span><span lang=EN-US>/</span><span style='font-family:宋体'>注册。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span style='font-family:宋体'>对策</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>解密和解压缩循环很容易就能被躲过,逆向分析人员只需要知道解密和解压缩循环何时结束,然后在循环结束后面的指令上下断点。记住,有些壳会在解密循环中检测断点。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span lang=EN-US>4.2 Garbage Code and Code Permutation</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span lang=EN-US>Garbage Code</span></b><span lang=EN-US> </span><span style='font-family:宋体'>在脱壳的例程中插入垃圾代码是另一种有效地迷惑逆向分析人员的方法。它的目的是在加密例程或者诸如调试器检测这样的反逆向例程中掩盖真正目的的代码。通过将本文描述过的调试器</span><span lang=EN-US>/</span><span style='font-family:宋体'>断点</span><span lang=EN-US>/</span><span style='font-family:宋体'>补丁检测技术隐藏在一大堆无关的、不起作用的、混乱的指令中,垃圾代码可以增加这些检测的效果。此外,有效的垃圾代码是那些看似合法</span><span lang=EN-US>/</span><span style='font-family:宋体'>有用的代码。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span style='font-family:宋体'>示例</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>下面是一段在相关的指令中插入了垃圾代码的解密例程:</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0044A</span><span lang=EN-US>21A</span><span lang=EN-US> JMP SHORT sample.0044A21F</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0044A</span><span lang=EN-US>21C</span><span lang=EN-US> XOR DWORD PTR SS:[EBP],6E4858D</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0044A</span><span lang=EN-US>223 INT 23</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0044A</span><span lang=EN-US>225 MOV ESI,DWORD PTR SS:[ESP]</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0044A</span><span lang=EN-US>228 MOV EBX,2C322FF0</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0044A</span><span lang=EN-US>22D LEA EAX,DWORD PTR SS:[EBP+6EE5B321]</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0044A</span><span lang=EN-US>233 LEA ECX DWORD PTR DS:[ESI+543D583E]</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0044A</span><span lang=EN-US>239 ADD EBP,742C0F15</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0044A</span><span lang=EN-US>23F</span><span lang=EN-US> ADD DWORD PTR DS:[ESI],3CB3AA25</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0044A</span><span lang=EN-US>245 XOR EDI,7DAC77E3</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0044A</span><span lang=EN-US>24B CMP EAX,ECX</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0044A</span><span lang=EN-US>24D MOV EAX,5ACAC514</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0044A</span><span lang=EN-US>252 JMP SHORT sample.0044A257</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0044A</span><span lang=EN-US>254 XOR DWORD PTR SS:[EBP],AAE47425</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0044A</span><span lang=EN-US>25B PUSH ES</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0044A</span><span lang=EN-US>25C</span><span lang=EN-US> ADD EBP,5BAC5C22</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0044A</span><span lang=EN-US>262 ADC ECX,3D71198C</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0044A</span><span lang=EN-US>268 SUB ESI,-4</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0044A</span><span lang=EN-US>26B ADC ECX,3795A210</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0044A</span><span lang=EN-US>271 DEC EDI</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0044A</span><span lang=EN-US>272 MOV EAX,2F57113F</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0044A</span><span lang=EN-US>277 PUSH ECX</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0044A</span><span lang=EN-US>278 POP ECX</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0044A</span><span lang=EN-US>279 LEA EAX,DWORD PTR SS:[EBP+3402713D]</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0044A</span><span lang=EN-US>27F</span><span lang=EN-US> EDC EDI</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0044A</span><span lang=EN-US>280 XOR DWORD PTR DS:[ESI],33B568E3</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0044A</span><span lang=EN-US>286 LEA EBX,DWORD PTR DS:[EDI+57DEFEE2]</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0044A</span><span lang=EN-US>28C</span><span lang=EN-US> DEC EDI</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0044A</span><span lang=EN-US>28D SUB EBX,7ECDAE21</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0044A</span><span lang=EN-US>293 MOV EDI,185C5C6C</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0044A</span><span lang=EN-US>298 MOV EAX,4713E635</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0044A</span><span lang=EN-US>29D MOV EAX,4</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0044A</span><span lang=EN-US>2A</span><span lang=EN-US>2 ADD ESI,EAX</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0044A</span><span lang=EN-US>2A</span><span lang=EN-US>4 MOV ECX,1010272F</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0044A</span><span lang=EN-US>2A</span><span lang=EN-US>9 MOV ECX,7A49B614</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0044A</span><span lang=EN-US>2AE CMP EAX,ECX</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0044A</span><span lang=EN-US>2B0 NOT DWORD PTR DS:[ESI]</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>示例中相关的解密指令是:</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0044A</span><span lang=EN-US>225 MOV ESI,DWORD PTR SS:[ESP]</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0044A</span><span lang=EN-US>23F</span><span lang=EN-US> ADD DWORD PTR DS:[ESI],3CB3AA25</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0044A</span><span lang=EN-US>268 SUB ESI,-4</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0044A</span><span lang=EN-US>280 XOR DWORD PTR DS:[ESI],33B568E3</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0044A</span><span lang=EN-US>29D MOV EAX,4</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0044A</span><span lang=EN-US>2A</span><span lang=EN-US>2 ADD ESI,EAX</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0044A</span><span lang=EN-US>2B0 NOT DWORD PTR DS:[ESI]</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span lang=EN-US>Code Permutation</span></b><span lang=EN-US> </span><span style='font-family:宋体'>代码变形是更高级壳使用的另一种技术。通过代码变形,简单的指令变成了复杂的指令序列。这要求壳理解原有的指令并能生成新的执行相同操作的指令序列。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>一个简单的指令置换示例:</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>mov eax,ebx</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>test eax,eax</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>转换成下列等价的指令:</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>push ebx</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>pop eax</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>or eax,eax</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>结合垃圾代码使用,代码变形是一种有效地减缓逆向分析人员理解受保护代码速度的技术。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span style='font-family:宋体'>示例</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>为了说明,下面是一个通过代码变形并在置换后的代码间插入了垃圾代码的调试器检测例程:</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>004018A</span><span lang=EN-US>8 MOV ECX,A104B412</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>004018AD PUSH 004018C1</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>004018B2 RETN</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>004018B3 SHR EDX,5</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>004018B6 ADD ESI,EDX</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>004018B8 JMP SHORT 004018BA</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>004018BA XOR EDX,EDX</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>004018BC MOV EAX,DWORD PTR DS:[ESI]</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>004018BE STC</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>004018BF JB SHORT 004018DE</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>004018C</span><span lang=EN-US>1 SUB ECX,EBX</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>004018C</span><span lang=EN-US>3 MOV EDX,9A01AB1F</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>004018C</span><span lang=EN-US>8 MOV ESI,DWORD PTR FS:[ECX]</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>004018CB LEA ECX DWORD PTR DS:[EDX+FFFF7FF7]</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>004018D1 MOV EDX,600</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>004018D6 TEST ECX,2B73</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>004018DC JMP SHORT 004018B3</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>004018DE MOV ESI,EAX</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>004018E0 MOV EAX,A35ABDE4</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>004018E5 MOV ECX,FAD1203A</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>004018EA MOV EBX,51AD5EF2</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>004018EF DIV EBX</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>004018F</span><span lang=EN-US>1 ADD BX,44A5</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>004018F</span><span lang=EN-US>6 ADD ESI,EAX</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>004018F</span><span lang=EN-US>8 MOVZX EDI,BYTE PTR DS:[ESI]</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>004018FB OR EDI,EDI</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>004018FD JNZ SHORT 00401906</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>其实这是一个很简单的调试器检测例程:</span></p> <p class=MsoNormal style='text-indent:17.95pt'><span lang=EN-US>00401081 MOV EAX,DWORD PTR FS:[18]</span></p> <p class=MsoNormal style='text-indent:17.95pt'><span lang=EN-US>00401087 MOV EAX,DWORD PTR DS:[EAX+30]</span></p> <p class=MsoNormal style='text-indent:17.95pt'><span lang=EN-US>0040108A</span><span lang=EN-US> MOVZX EAX,BYTE PTR DS:[EAX+2]</span></p> <p class=MsoNormal style='text-indent:17.95pt'><span lang=EN-US>0040108E TEST EAX,EAX</span></p> <p class=MsoNormal style='text-indent:17.95pt'><span lang=EN-US>00401090 JNZ SHORT 00401099</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span style='font-family:宋体'>对策</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>垃圾代码和代码变形是一种用来考验耐心和浪费逆向分析人员的时间的方式。因此,重要的是知道这些混淆技术背后隐藏的指令是否值得去理解(是不是仅仅执行解密、壳的初始化等动作)。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>避免跟踪进入这些难懂的指令的方法之一是在壳最常用的</span><span lang=EN-US>API</span><span style='font-family:宋体'>下断点(如:</span><span lang=EN-US>VirtualAlloc,VitualProtect,LoadLibrary,GetProcAddress</span><span style='font-family:宋体'>等)并把这些</span><span lang=EN-US>API</span><span style='font-family:宋体'>当作跟踪的标志。如果在这些跟踪标志之间出了错,这时候就对这一段代码进行详细的跟踪。另外,设置内存访问</span><span lang=EN-US>/</span><span style='font-family:宋体'>写入断点也让逆向分析人员能有针对性地分析那些修改</span><span lang=EN-US>/</span><span style='font-family:宋体'>访问受保护进程最有趣的部分的代码,而不是跟踪大量的代码最终却(很可能)发现是一个确定的例程。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>最后,在</span><span lang=EN-US>VMWare</span><span style='font-family:宋体'>中运行</span><span lang=EN-US>OllyDbg</span><span style='font-family:宋体'>并不时地保存调试会话快照,这样一来逆向分析人员就可以回到某一个特定的跟踪状态。如果出了错,可以返回到某一特定的跟踪状态继续跟踪分析。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span lang=EN-US>4.3 Anti-Disassembly</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>用来困惑逆向分析人员的另一种方法就是混乱反编译输出。反</span><span lang=EN-US>-</span><span style='font-family:宋体'>反编译是使通过静态分析理解二进制代码的过程大大复杂化的有效方式。如果结合垃圾代码和代码变形一起使用将会更具效果。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>反</span><span lang=EN-US>-</span><span style='font-family:宋体'>反编译技术的一个具体的例子是插入一个垃圾字节然后增加一个条件分支使执行跳转到垃圾字节(译者注:即我们常说的花指令)。但是这个分支的条件永远为</span><span lang=EN-US>FALSE</span><span style='font-family:宋体'>。这样垃圾代码将永远不会被执行,但是反编译引擎会开始反编译垃圾字节的地址,最终导致不正确的反编译输出。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span style='font-family:宋体'>示例</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>这是一个加了一些反</span><span lang=EN-US>-</span><span style='font-family:宋体'>反编译代码的简单</span><span lang=EN-US>PEB.BeingDebugged</span><span style='font-family:宋体'>标志检查例子。高亮的行是主要指令,其余的是反</span><span lang=EN-US>-</span><span style='font-family:宋体'>反编译代码。它用到了垃圾字节</span><span lang=EN-US>0xff</span><span style='font-family:宋体'>并增加了用来迷惑反编译引擎的跳到垃圾字节的假的条件跳转。</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>;Anti-disassembly sequence #1</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>push .jmp_real_01</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>stc</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>jnc .jmp_fake_01</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>retn</span></p> <p class=MsoNormal><span lang=EN-US>.jmp_fake_01:</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>db 0xff</span></p> <p class=MsoNormal><span lang=EN-US>.jmp_real_01:</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>;--------------------------------</span></p> <p class=MsoNormal style='text-indent:36.05pt'><b><span lang=EN-US>mov eax,dword [fs:0x18]</span></b></p> <p class=MsoNormal style='text-indent:36.05pt'><b><span lang=EN-US> </span></b></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>;Anti-disassembly sequence #2</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>push .jmp_real_02</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>clc</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>jc .jmp_fake_02</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>retn</span></p> <p class=MsoNormal><span lang=EN-US>.jmp_fake_02:</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>db 0xff</span></p> <p class=MsoNormal><span lang=EN-US>.jmp_real_02:</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>;--------------------------------</span></p> <p class=MsoNormal style='text-indent:36.05pt'><b><span lang=EN-US>mov eax,dword [eax+0x30]</span></b></p> <p class=MsoNormal style='text-indent:36.05pt'><b><span lang=EN-US>movzx eax,byte [eax+0x02]</span></b></p> <p class=MsoNormal style='text-indent:36.05pt'><b><span lang=EN-US>test eax,eax</span></b></p> <p class=MsoNormal style='text-indent:36.05pt'><b><span lang=EN-US>jnz .debugger_found</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>下面是</span><span lang=EN-US>WinDbg</span><span style='font-family:宋体'>中的反汇编输出:</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0040194A</span><span lang=EN-US> 6854194000 PUSH 0X401954</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0040194F</span><span lang=EN-US> F9 STC</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>00401950 7301 JNB image00400000+0x1953(00401953)</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>00401952 C</span><span lang=EN-US>3 RET</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>00401953 FF64A118 JMP DWORD PTR [ECX+0X18]</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>00401957 0000 ADD [EAX],AL</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>00401959 006864 ADD [EAX+0X64],CH</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0040195C</span><span lang=EN-US> 194000 SBB [EAX],EAX</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0040195F</span><span lang=EN-US> F8 CLC</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>00401960 7201 JB image00400000+0x1963 (00401963)</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>00401962 C</span><span lang=EN-US>3 RET</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>00401963 FF8B40300FB6 DEC DWORD PTR [EBX+0XB60F3040]</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>00401969 40 INC EAX</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0040196A</span><span lang=EN-US> 0285C0750731 ADD AL,[EBP+0X310775C0]</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span lang=EN-US>OllyDbg</span><span style='font-family:宋体'>中的反汇编输出:</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0040194A</span><span lang=EN-US> 6854194000 PUSH 00401954</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0040194F</span><span lang=EN-US> F9 STC</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>00401950 7301 JNB SHORT 00401953</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>00401952 C</span><span lang=EN-US>3 RETN</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>00401953 FF64A118 JMP DWORD PTR DS:[ECX+18]</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>00401957 0000 ADD BYTE PTR DS:[EAX],AL</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>00401959 006864 ADD BYTE PTR DS:[EAX+0X64],CH</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0040195C</span><span lang=EN-US> 194000 SBB DWORD PTR DS:[EAX],EAX</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0040195F</span><span lang=EN-US> F8 CLC</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>00401960 7201 JB SHORT 00401963</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>00401962 C</span><span lang=EN-US>3 RETN</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>00401963 FF8B40300FB6 DEC DWORD PTR DS:[EBX+B60F3040]</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>00401969 40 INC EAX</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0040196A</span><span lang=EN-US> 0285C0750731 ADD AL,BYTE PTR SS:[EBP+310775C0]</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>最后</span><span lang=EN-US>IDAPro</span><span style='font-family:宋体'>中的反汇编输出:</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0040194A</span> <span lang=EN-US> push (offset loc_401953+1)</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0040194F</span> <span lang=EN-US> stc</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>00401950 jnb short loc_401953</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>00401952 retn</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>00401953 ;------------------------------------------------------------------</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>00401953</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>00401953 loc-401953: ;CODE XREF: sub_401946+A</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>00401953 ;DATA XREF: sub_401946+4</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>00401953 jmp dword ptr [ecx+18h]</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>00401953 sub_401946 endp</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>00401953</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>00401953 ;------------------------------------------------------------------</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>00401957 db 0</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>00401958 db 0</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>00401959 db 0</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0040195A</span> <span lang=EN-US> db 68h; h</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0040195B dd offset unk_401964</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0040195F</span> <span lang=EN-US> db 0F8h;</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>00401960 db 72h; r</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>00401961 db 1</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>00401962 db 0C3h;+</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>00401963 db 0FFh</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>00401964 unk_401964 db 8Bh; i ;DATA XREF: text:0040195B</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>00401965 db 40h; @</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>00401966 db 30h; 0</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>00401967 db 0Fh</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>00401968 db 0B6h;|</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>00401969 db 40h; @</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0040196A</span> <span lang=EN-US> db 2</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0040196B db 85h; </span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0040196C</span> <span lang=EN-US> db 0C0h;+</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>0040196D db 75h; u</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>注意所有这三个反编译引擎</span><span lang=EN-US>/</span><span style='font-family:宋体'>调试器是如何落入反</span><span lang=EN-US>-</span><span style='font-family:宋体'>反编译陷阱的,分析这样的反汇编代码对于逆向分析人员来说是很不容易的。还有其它的几种干扰反编译引擎的手段,这只是一个例子。另外这些反</span><span lang=EN-US>-</span><span style='font-family:宋体'>反编译代码可以编码成一个宏,这样汇编源码就清晰多了。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>建议读者参考</span><span lang=EN-US>Eldad Eliam</span><sup><span lang=EN-US>13</span></sup><span style='font-family:宋体'>的一本精彩的逆向书籍,里面包含了反</span><span lang=EN-US>-</span><span style='font-family:宋体'>反编译的详细信息和其它一些逆向话题。</span></p> <p class=MsoNormal style='margin-top:23.4pt;margin-right:0cm;margin-bottom: 15.6pt;margin-left:0cm'><b><span lang=EN-US style='color:white;background:black'>5 </span></b><b><span style='font-family:宋体;color:white;background:black'>调试器攻击技术</span><span lang=EN-US style='color:white;background:black'> </span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>本节罗列了壳用来主动攻击调试器的技术,如果进程正在被调试那么执行会突然停止、断点将被禁用。和前面描述的技术类似,结合反</span><span lang=EN-US>-</span><span style='font-family:宋体'>反编译技术隐藏起来使用效果会更佳。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span lang=EN-US>5.1 Misdirection and Stopping Execution via Exceptions</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>线性地跟踪能够让逆向分析人员容易理解并掌握代码的真正目的。因此壳使用一些技术使得跟踪代码不再是线性的且更加费时。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>一个普遍使用的技巧是在脱壳的过程中抛出一些异常,通过抛出一些可捕获的异常,逆向分析人员必需熟悉异常发生的时候</span><span lang=EN-US>EIP</span><span style='font-family:宋体'>指向何处,当异常处理例程执行完之后</span><span lang=EN-US>EIP</span><span style='font-family:宋体'>又指向何处。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>另外异常是壳用来反复停止脱壳代码执行的手段之一,因为当进程被调试时抛出异常,调试器会暂停脱壳代码的执行。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>壳通常使用结构化异常处理(</span><span lang=EN-US>SEH</span><span style='font-family:宋体'>)</span><sup><span lang=EN-US>14</span></sup><span style='font-family:宋体'>作为异常处理的机制,然而新壳也开始使用向量化异常</span><sup><span lang=EN-US>15</span></sup><span style='font-family:宋体'>。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span style='font-family:宋体'>示例</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>下面示例代码抛出溢出异常(通过</span><span lang=EN-US>INTO</span><span style='font-family:宋体'>)产生错误,通过数轮循环后由</span><span lang=EN-US>ROL</span><span style='font-family:宋体'>指令来修改溢出标志。但是由于溢出异常是一个陷阱异常,</span><span lang=EN-US>EIP</span><span style='font-family:宋体'>将指向</span><span lang=EN-US>JMP</span><span style='font-family:宋体'>指令。如果逆向分析人员使用</span><span lang=EN-US>OllyDbg</span><span style='font-family:宋体'>并且没有将异常传递给进程(通过</span><span lang=EN-US>Shift+F7/F8/F9</span><span style='font-family:宋体'>)而是继续步进,进程将会进入一个死循环。</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>;set up exception handler</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>push .exception_handler</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>push dword [fs:0]</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>mov [fs:0],esp</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US> </span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>;throw an exception</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>mov ecx,1</span></p> <p class=MsoNormal><span lang=EN-US>.loop:</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>rol ecx,1</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>into</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>jmp .loop</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US> </span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>;restore exception handler</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>pop dword [fs:0]</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>add esp,4</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>:::</span></p> <p class=MsoNormal><span lang=EN-US>.exception_handler</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>;EAX = CONTEXT record</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>mov eax,[esp+0xc]</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>;set Context.EIP upon return</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>add dword [eax+0xb8],2</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>xor eax,eax</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>retn</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>壳通常会抛出违规访问(</span><span lang=EN-US>0xC0000005</span><span style='font-family:宋体'>)、断点(</span><span lang=EN-US>0x80000003</span><span style='font-family:宋体'>)和单步(</span><span lang=EN-US>0x80000004</span><span style='font-family:宋体'>)异常。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span style='font-family:宋体'>对策</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>当壳使用可捕获的异常仅仅是为了执行不同的代码时,可以通过选项</span><span lang=EN-US>-> </span><span style='font-family:宋体'>调试选项</span><span lang=EN-US> -> </span><span style='font-family:宋体'>异常选项卡配置</span><span lang=EN-US>OllyDbg</span><span style='font-family:宋体'>使得异常处理例程自动被调用。下面是异常处理配置对话框的屏幕截图。逆向分析人员也可以添加那些不能通过复选框选择的自定义的异常。</span><span lang=EN-US><img width=436 height=319 src="脱壳的艺术.files/image002.jpg"></span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>当壳在异常处理例程内部执行重要操作时,逆向分析人员可以在异常处理例程中下断,其地址可以在</span><span lang=EN-US>OllyDbg</span><span style='font-family:宋体'>中通过视图</span><span lang=EN-US>->SEH</span><span style='font-family:宋体'>链看到。然后</span><span lang=EN-US>Shift+F7/F8/F9</span><span style='font-family:宋体'>将控制移交给异常处理例程。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span lang=EN-US>5.2 Blocking Input </span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>为了防止逆向分析人员控制调试器,当脱壳主例程运行的时候,壳可以通过调用</span><span lang=EN-US>user32!BlockInput() API </span><span style='font-family:宋体'>来阻断键盘和鼠标的输入。通过垃圾代码和反</span><span lang=EN-US>-</span><span style='font-family:宋体'>反编译技术进行隐藏使用这种方法,如果逆向分析人员没有识别出来的话是很有效的。一旦生效系统看上去没有反应,只剩下逆向分析人员在那里莫名其妙。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>典型的场景可能是逆向分析人员在</span><span lang=EN-US>GetProcAddress()</span><span style='font-family:宋体'>内下断,然后运行脱壳代码直到被断下。但是跳过一段垃圾代码之后壳调用</span><span lang=EN-US>BlockInput()</span><span style='font-family:宋体'>。当</span><span lang=EN-US>GetProcAddress()</span><span style='font-family:宋体'>断点断下来后,逆向分析人员会突然困惑地发现无法控制调试器了,不知究竟发生了什么。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span style='font-family:宋体'>示例</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span lang=EN-US>BlockInput()</span><span style='font-family:宋体'>需要一个</span><span lang=EN-US>boolean</span><span style='font-family:宋体'>型的参数</span><span lang=EN-US>fBlockIt</span><span style='font-family:宋体'>。如果这个参数是</span><span lang=EN-US>true</span><span style='font-family:宋体'>,键盘和鼠标事件被阻断;如果是</span><span lang=EN-US>false</span><span style='font-family:宋体'>,键盘和鼠标事件被解除阻断:</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>; Block input</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>push TRUE</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>call [BlockInput]</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US> </span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>;...Unpacking code...</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US> </span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>;Unblock input</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>push FALSE</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>call [BlockInput]</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span style='font-family:宋体'>对策</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>幸好最简单的方法就是补丁</span><span lang=EN-US> BlockInput()</span><span style='font-family:宋体'>使它直接返回。这是补丁</span><span lang=EN-US>user32!BlockInput()</span><span style='font-family:宋体'>入口的</span><span lang=EN-US>ollyscript</span><span style='font-family:宋体'>脚本:</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>gpa "BlockInput","user32.dll"</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>mov [$RESULT],#C20400# //retn 4</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span lang=EN-US>Olly Advanced</span><span style='font-family:宋体'>插件同样有补</span><span lang=EN-US>BlockInput()</span><span style='font-family:宋体'>的选项。另外,可以同时按</span><span lang=EN-US>CTRL+ALT+DELETE</span><span style='font-family:宋体'>键手工解除阻断。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span lang=EN-US>5.3 ThreadHideFromDebugger</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>这项技术用到了常常被用来设置线程优先级的</span><span lang=EN-US>API ntdll!NtSetInformationThread()</span><span style='font-family: 宋体'>,不过这个</span><span lang=EN-US>API</span><span style='font-family:宋体'>也能够用来防止调试事件被发往调试器。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span lang=EN-US>NtSetInformationThread()</span><span style='font-family:宋体'>的参数列表如下。要实现这一功能,</span><span lang=EN-US>ThreadHideFromDebugger(0x11)</span><span style='font-family:宋体'>被当作</span><span lang=EN-US>ThreadInformationClass</span><span style='font-family:宋体'>参数传递,</span><span lang=EN-US>ThreadHandle</span><span style='font-family:宋体'>通常设为当前线程的句柄</span><span lang=EN-US>(0xFFFFFFFE)</span><span style='font-family:宋体'>:</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>NTSTATUS NTAPI NtSetInformationThread(</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>HANDLE ThreadHandle,</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>THREAD_INFORMATION_CLASS ThreadInformaitonClass,</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>PVOID ThreadInformation,</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>ULONG ThreadInformationLength</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>);</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span lang=EN-US>ThreadHideFromDebugger</span><span style='font-family:宋体'>内部设置内核结构</span><span lang=EN-US>ETHREAD</span><sup><span lang=EN-US>16</span></sup><span style='font-family:宋体'>的</span><span lang=EN-US>HideThreadFromDebugger</span><span style='font-family:宋体'>成员。一旦这个成员设置以后,主要用来向调试器发送事件的内核函数</span><span lang=EN-US>_DbgkpSendApiMessage()</span><span style='font-family:宋体'>将不再被调用。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span style='font-family:宋体'>示例</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>调用</span><span lang=EN-US>NtSetInformationThread()</span><span style='font-family:宋体'>的一个典型示例:</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>push 0 ;InformationLength</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>push NULL ;ThreadInformation</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>push ThreadHideFromDebugger ;0x11</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>push 0xfffffffe ;GetCurrentThread()</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>call [NtSetInformationThread]</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span style='font-family:宋体'>对策</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>可以在</span><span lang=EN-US>ntdll!NtSetInformationThread()</span><span style='font-family:宋体'>里下断,断下来后,逆向分析人员可以操纵</span><span lang=EN-US>EIP</span><span style='font-family:宋体'>防止</span><span lang=EN-US>API</span><span style='font-family:宋体'>调用到达内核,这些都可以通过</span><span lang=EN-US>ollyscript</span><span style='font-family:宋体'>来自动完成。另外,</span><span lang=EN-US>Olly Advanced</span><span style='font-family:宋体'>插件也有补这个</span><span lang=EN-US>API</span><span style='font-family:宋体'>的选项。补过之后一旦</span><span lang=EN-US>ThreadInformaitonClass</span><span style='font-family:宋体'>参数为</span><span lang=EN-US>HideThreadFromDebugger</span><span style='font-family:宋体'>,</span><span lang=EN-US>API</span><span style='font-family:宋体'>将不再深入内核仅仅执行一个简单的返回。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span lang=EN-US>5.4 Disabling Breakpoints</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>另外一种攻击调试器的方法就是禁用断点。壳通过</span><span lang=EN-US>CONTEXT</span><span style='font-family:宋体'>结构修改调试寄存器来禁用硬件断点。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span style='font-family:宋体'>示例</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>在这个示例中,通过传入异常处理例程的</span><span lang=EN-US>CONTEXT</span><span style='font-family:宋体'>记录,调试寄存器被清空了。</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>;set up exception handler</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>push .exception_handler</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>push dword [fs:0]</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>mov [fs:0],esp</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US> </span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>;throw an exception</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>xor eax,eax</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>mov dword [eax],0</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US> </span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>;restore exception handler</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>pop dword [fs:0]</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>add esp,4</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>:::</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US> </span></p> <p class=MsoNormal><span lang=EN-US>.exception_handler</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>;EAX = CONTEXT record</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>mov eax,[esp+0xc]</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US> </span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>;Clear Debug Registers: Context.Dr0-Dr3,Dr6,Dr7</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>mov dword [eax+0x04],0</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>mov dword [eax+0x08],0</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>mov dword [eax+0x0C],0</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>mov dword [eax+0x10],0</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>mov dword [eax+0x14],0</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>mov dword [eax+0x18],0</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US> </span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>;set Context.EIP upon return</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>add dword [eax+0xb8],6</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>xor eax,eax</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>retn</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>对于软件断点,壳可以直接搜索</span><span lang=EN-US>INT3</span><span style='font-family:宋体'>(</span><span lang=EN-US>0xCC</span><span style='font-family:宋体'>)并用任意</span><span lang=EN-US>/</span><span style='font-family:宋体'>随机的操作码加以替换。这样做以后,软件断点失效并且原始的指令将会被破坏。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span style='font-family:宋体'>对策</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>显然当硬件断点被检测以后可以用软件断点来代替,反之亦然。如果两者都被检测,可以试试</span><span lang=EN-US>OllyDbg</span><span style='font-family:宋体'>的内存访问</span><span lang=EN-US>/</span><span style='font-family:宋体'>写入断点功能。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span lang=EN-US>5.5 Unhandled Exception Filter</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span lang=EN-US>MSDN</span><span style='font-family:宋体'>文档声明当一个异常到达</span><span lang=EN-US>Unhandled Exception Filter</span><span style='font-family:宋体'>(</span><span lang=EN-US>kernel32!UnhandledExceptionFilter)</span><span style='font-family:宋体'>并且程序没有被调试时,</span><span lang=EN-US>Unhandled Exception Filter</span><span style='font-family:宋体'>将会调用在</span><span lang=EN-US>kernel32!SetUnhandledExceptionFilter()API</span><span style='font-family:宋体'>作为参数指定的高层</span><span lang=EN-US>exception Filter</span><span style='font-family:宋体'>。壳利用了这一点,通过设置</span><span lang=EN-US>exception Filter</span><span style='font-family:宋体'>然后抛出异常,如果程序被调试那么这个异常将会被调试器接收,否则,控制被移交到</span><span lang=EN-US>exception Filter</span><span style='font-family:宋体'>运行得以继续。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span style='font-family:宋体'>示例</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>下面的示例中通过</span><span lang=EN-US>SetUnhandledExceptionFilter()</span><span style='font-family:宋体'>设置了一个高层的</span><span lang=EN-US>exception Filter</span><span style='font-family:宋体'>,然后抛出一个违规访问异常。如果进程被调试,调试器将收到两次异常通知,否则</span><span lang=EN-US>exception Filter</span><span style='font-family:宋体'>将修改</span><span lang=EN-US>CONTEXT.EIP</span><span style='font-family:宋体'>并继续执行。</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>;set the exception filter</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>push .exception_filter</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>call [SetUnhandledExceptionFilter]</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>mov [.original_filter],eax</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US> </span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>;throw an exception</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>xor eax,eax</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>mov dword [eax],0 </span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US> </span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>;restore exception filter</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>push dword [.original_filter]</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>call [SetUnhandledExceptionFilter]</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US> </span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>:::</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US> </span></p> <p class=MsoNormal><span lang=EN-US>.exception_filter:</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>;EAX = ExceptionInfo.ContextRecord</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>mov eax,[esp+4]</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>mov eax,[eax+4]</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US> </span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>;set return EIP upon return</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>add dword [eax+0xb8],6</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US> </span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>;return EXCEPTION_CONTINUE_EXECUTION</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>mov eax,0xffffffff</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>retn</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>有些壳并不调用</span><span lang=EN-US>SetUnhandledExceptionFilter()</span><span style='font-family:宋体'>而是直接通过</span><span lang=EN-US>kernel32!_BasepCurrentTopLevelFilter</span><span style='font-family: 宋体'>手工设置</span><span lang=EN-US>exception Filter</span><span style='font-family: 宋体'>,以防逆向分析人员在那个</span><span lang=EN-US>API</span><span style='font-family: 宋体'>上下断。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span style='font-family:宋体'>对策</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>有意思的是</span><span lang=EN-US>kernel32!UnhandledExceptionFilter()</span><span style='font-family: 宋体'>内部实现代码是使用</span><span lang=EN-US>ntdll!NtQueryInformationProcess(ProcessDebugPort)</span><span style='font-family:宋体'>来确定进程是否被调试,从而决定是否调用已注册的</span><span lang=EN-US>exception Filter</span><span style='font-family:宋体'>。因此,处理方法和</span><span lang=EN-US>DebugPort</span><span style='font-family:宋体'>调试器检测技术相同。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span lang=EN-US>5.6 OllyDbg:OutputDebugString() Format String Bug</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>这个调试器攻击手段只对</span><span lang=EN-US>OllyDbg</span><span style='font-family:宋体'>有效。已知</span><span lang=EN-US>OllyDbg</span><span style='font-family:宋体'>面对能导致崩溃或执行任意代码的格式化字符串漏洞是脆弱的,这个漏洞是由于向</span><span lang=EN-US>kernel32!OutputDebugString()</span><span style='font-family:宋体'>传递了不当的字符串参数引起的。这个漏洞在当前</span><span lang=EN-US>OllyDbg(1.10)</span><span style='font-family:宋体'>依然存在并且仍然没有打补丁。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span style='font-family:宋体'>示例</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>下面这个简单的示例将导致</span><span lang=EN-US>OllyDbg</span><span style='font-family:宋体'>抛出违规访问异常或不可预期的终止。</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>push .szFormatString</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>call [OutputDebugStringA]</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>:::</span></p> <p class=MsoNormal><span lang=EN-US>.szFormatString db "%s%s",0</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span style='font-family:宋体'>对策</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>可以通过补丁</span><span lang=EN-US> kernel32!OutputDebugStringA()</span><span style='font-family:宋体'>入口使之直接返回来加以解决。</span></p> <p class=MsoNormal style='margin-top:23.4pt;margin-right:0cm;margin-bottom: 15.6pt;margin-left:0cm'><b><span lang=EN-US style='color:white;background:black'>6. </span></b><b><span style='font-family:宋体;color:white;background:black'>高级及其它技术</span><span lang=EN-US style='color:white;background:black'> </span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>本节罗列了不属于前面任一分类的一些高级和其它的反逆向技术。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span lang=EN-US>6.1 Process Injection</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>进程注入已经成为某些壳的一个特点。脱壳代码打开一个选定的宿主进程(自身、</span><span lang=EN-US>explorer.exe</span><span style='font-family:宋体'>、</span><span lang=EN-US>iexplorer.exe</span><span style='font-family:宋体'>等)然后将脱壳后的程序注入到这个宿主进程。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><span lang=EN-US><img width=551 height=93 src="脱壳的艺术.files/image003.jpg"></span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>下面是一个支持进程注入的壳的屏幕截图。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><span lang=EN-US><img width=296 height=226 src="脱壳的艺术.files/image004.jpg"></span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>恶意代码利用壳的这个特点使它们能躲过一些防火墙,这些防火墙通过检查进程是否在获准进行外部网络连接的应用程序列表中而决定是否放行。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>壳所采用的执行进程注入的一种方法如下:</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:17.95pt;text-indent:17.85pt'><span lang=EN-US>1. </span><span style='font-family:宋体'>向</span><span lang=EN-US>kernel32!CreateProcess()</span><span style='font-family:宋体'>传递</span><span lang=EN-US>CREATE_SUSPENDED</span><span style='font-family:宋体'>进程创建标志,将宿主进程作为一个挂起的子进程打开。这时一个初始化了的线程被创建并挂起,由于</span><span lang=EN-US>loader</span><span style='font-family:宋体'>例程(</span><span lang=EN-US>ntdll!LrdInitializeThunk)</span><span style='font-family:宋体'>还没有被调用,</span><span lang=EN-US>DLL</span><span style='font-family:宋体'>还没有被载入。这个线程的上下文中包含</span><span lang=EN-US>PEB</span><span style='font-family:宋体'>地址、宿主进程入口点信息的寄存器值被设置。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:17.95pt;text-indent:17.85pt'><span lang=EN-US>2. </span><span style='font-family:宋体'>使用</span><span lang=EN-US>kernel32!GetThreadContext()</span><span style='font-family:宋体'>获取子进程初始化线程的上下文。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:17.95pt;text-indent:17.85pt'><span lang=EN-US>3. </span><span style='font-family:宋体'>通过</span><span lang=EN-US>CONTEXT.EBX</span><span style='font-family:宋体'>获取子进程的</span><span lang=EN-US>PEB</span><span style='font-family:宋体'>地址。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:17.95pt;text-indent:17.85pt'><span lang=EN-US>4. </span><span style='font-family:宋体'>读</span><span lang=EN-US>PEB.ImageBase(PEB+0x8)</span><span style='font-family:宋体'>获取子进程的映像基址。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:17.95pt;text-indent:17.85pt'><span lang=EN-US>5. </span><span style='font-family:宋体'>将</span><span lang=EN-US>BaseAddress</span><span style='font-family:宋体'>参数指向检索到的映像基址,调用</span><span lang=EN-US>ntdll!NtUnmapViewOfSection()</span><span style='font-family:宋体'>来</span><span lang=EN-US>unmap</span><span style='font-family:宋体'>子进程中的原始宿主映像。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:17.95pt;text-indent:17.85pt'><span lang=EN-US>6. </span><span style='font-family:宋体'>脱壳代码使用</span><span lang=EN-US>kernel32!VirtualAllocEx()</span><span style='font-family:宋体'>在子进程中分配一段内存,</span><span lang=EN-US>dwSize</span><span style='font-family:宋体'>参数等于脱壳后程序的映像大小。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:17.95pt;text-indent:17.85pt'><span lang=EN-US>7. </span><span style='font-family:宋体'>使用</span><span lang=EN-US>kernel32!WriteProcessMemory()</span><span style='font-family:宋体'>将脱壳后的程序的</span><span lang=EN-US>PE</span><span style='font-family:宋体'>头和每个节写入子进程。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:17.95pt;text-indent:17.85pt'><span lang=EN-US>8. </span><span style='font-family:宋体'>将子进程的</span><span lang=EN-US>PEB.ImageBase</span><span style='font-family:宋体'>更新以匹配脱壳后的程序映像基址。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:17.95pt;text-indent:17.85pt'><span lang=EN-US>9. </span><span style='font-family:宋体'>通过</span><span lang=EN-US>kernel32!SetThreadContext()</span><span style='font-family:宋体'>更新子进程初始化线程的上下文,将其中的</span><span lang=EN-US>CONTEXT.EAX</span><span style='font-family:宋体'>设置为脱壳后程序的入口点。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:17.95pt;text-indent:17.85pt'><span lang=EN-US>10. </span><span style='font-family:宋体'>通过</span><span lang=EN-US>kernel32!ResumeThread()</span><span style='font-family:宋体'>恢复子进程的执行。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>为了从入口点开始调试打开的子进程,逆向分析人员可以在</span><span lang=EN-US>WriteProcessMemory()</span><span style='font-family:宋体'>中设置断点,当包含入口点的节被写入子进程的时候,将入口点代码补丁为</span><span lang=EN-US>”</span><span style='font-family:宋体'>跳往自身</span><span lang=EN-US>”</span><span style='font-family:宋体'>指令(</span><span lang=EN-US>0xEB0xFE</span><span style='font-family:宋体'>)。当子进程的主线程被恢复,子进程将在入口点进入一个死循环。这时逆向分析人员就可以附加一个调试器到子进程,恢复被修改的指令,继续正常的调试。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span lang=EN-US>6.2 Debugger Blocker</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span lang=EN-US>Armadillo</span><span style='font-family:宋体'>壳引入了称之为</span><span lang=EN-US>Debugger Blocker</span><span style='font-family:宋体'>的功能,它可以阻止逆向分析人员将调试器附加到一个受保护的进程。这个保护是通过调用</span><span lang=EN-US>Windows</span><span style='font-family:宋体'>提供的调试函数来实现的。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>具体来说就是脱壳代码扮演一个调试器的角色(父进程),通过它打开、调试</span><span lang=EN-US>/</span><span style='font-family:宋体'>控制包含脱壳后程序的子进程。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><span lang=EN-US><img width=553 height=118 src="脱壳的艺术.files/image005.jpg"></span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>由于受保护的进程已经被调试,通过</span><span lang=EN-US>kernel32!DebugActiveProcess()</span><span style='font-family:宋体'>来附加调试器将会失败,原因是相应的</span><span lang=EN-US>native API ntdll!NtDebugActiveProcess()</span><span style='font-family:宋体'>将返回</span><span lang=EN-US>STATUS_PORT_ALREADY_SET</span><span style='font-family:宋体'>。</span><span lang=EN-US> NtDebugActiveProcess()</span><span style='font-family:宋体'>的失败的根本原因在于内核结构</span><span lang=EN-US>EPROCESS</span><span style='font-family:宋体'>的</span><span lang=EN-US>DebugPort</span><span style='font-family:宋体'>成员已经被设置过了。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>为了附加调试器到受保护的进程,好几个逆向论坛发布的解决方法是在父进程的上下文里调用</span><span lang=EN-US>dernel32!DebugActiveProcessStop()</span><span style='font-family: 宋体'>。可以通过附加调试器到父进程,在</span><span lang=EN-US>kernel32!WaitForDebugEvent()</span><span style='font-family:宋体'>内部下断,断下来后,注入一段调用</span><span lang=EN-US>DebugActiveProcessStop(childProcessID)</span><span style='font-family:宋体'>的代码并执行,一旦调用成功,这时就可以附加调试器到受保护的进程了。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span lang=EN-US>6.3 TLS Callbacks</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>另一个被壳使用的技术就是在实际的入口点代码执行之前执行代码,这是通过使用</span><span lang=EN-US>Thread Local Storage (TLS)</span><span style='font-family:宋体'>回调函数来实现的。壳通过这些回调函数执行调试器检测及解密例程,这样逆向分析人员将无法跟踪这些例程。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span lang=EN-US>TLS</span><span style='font-family:宋体'>回调可以使用诸如</span><span lang=EN-US>pedump</span><span style='font-family:宋体'>之类的</span><span lang=EN-US>PE</span><span style='font-family:宋体'>文件分析工具来识别。如果可执行文件中存在</span><span lang=EN-US>TLS</span><span style='font-family:宋体'>条目,数据条目将会显示出来。</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>Data directory</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>EXPORT rva:00000000 size:00000000</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>IMPORT rva:00061000 size:000000E0</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>:::</span></p> <p class=MsoNormal style='text-indent:36.05pt'><b><span lang=EN-US>TLS rva:000610E0 size:00000018</span></b></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>:::</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>IAT rva:00000000 size:00000000</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>DELAY_IMPORT rva:00000000 size:00000000</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>COM_DESCRPTR rva:00000000 size:00000000</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>unused rva:00000000 size:00000000</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>接着显示</span><span lang=EN-US>TLS</span><span style='font-family:宋体'>条目的实际内容。</span><span lang=EN-US>AddressOfCallBacks</span><span style='font-family:宋体'>成员指向一个以</span><span lang=EN-US>null</span><span style='font-family:宋体'>结尾的回调函数数组。</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>TLS directory</span><span style='font-family:宋体'>:</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>StartAddressOfRawData: 00000000</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>EndAddressOfRawData: 00000000</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>AddressOfIndex: 004610F8</span></p> <p class=MsoNormal style='text-indent:36.05pt'><b><span lang=EN-US>AddressOfCallBacks: 004610FC</span></b></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>SizeOfZeroFill: 00000000</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>Characteristics: 00000000</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>在这个例子中,</span><span lang=EN-US>RVA 0x4610fc</span><span style='font-family:宋体'>指向回调函数指针(</span><span lang=EN-US>0x490f43</span><span style='font-family:宋体'>和</span><span lang=EN-US>0x44654e</span><span style='font-family:宋体'>):</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><span lang=EN-US><img width=554 height=97 src="脱壳的艺术.files/image006.jpg"></span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>默认情况下</span><span lang=EN-US>OllyDbg</span><span style='font-family:宋体'>载入这个例子将会暂停在入口点。由于</span><span lang=EN-US>TLS</span><span style='font-family:宋体'>回调函数是在实际的入口点执行之前被调用的,</span><span lang=EN-US>OllyDbg</span><span style='font-family:宋体'>应该配置一下使其在</span><span lang=EN-US>TLS</span><span style='font-family:宋体'>回调被调用之前中断在实际的</span><span lang=EN-US>loader</span><span style='font-family:宋体'>。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>可以通过选择选项</span><span lang=EN-US>-></span><span style='font-family:宋体'>调试选项</span><span lang=EN-US>-></span><span style='font-family:宋体'>事件</span><span lang=EN-US>-></span><span style='font-family:宋体'>第一次中断于</span><span lang=EN-US>-></span><span style='font-family:宋体'>系统断点来设置中断于</span><span lang=EN-US>ntdll.dll</span><span style='font-family:宋体'>内的实际</span><span lang=EN-US>loader</span><span style='font-family:宋体'>代码。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><span lang=EN-US><img width=493 height=176 src="脱壳的艺术.files/image007.jpg"></span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>这样设置以后,</span><span lang=EN-US>OllyDbg</span><span style='font-family:宋体'>将会中断在位于执行</span><span lang=EN-US>TLS</span><span style='font-family:宋体'>回调的</span><span lang=EN-US>ntdll!LdrpRunInitializeRoutines()</span><span style='font-family:宋体'>之前的</span><span lang=EN-US>ntdll!_LdrpInitializeProcess()</span><span style='font-family:宋体'>,这时就可以在回调例程中下断并跟踪了。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>关于</span><span lang=EN-US>PE</span><span style='font-family:宋体'>文件格式的更多信息及包括</span><span lang=EN-US>pedump</span><span style='font-family:宋体'>的二进制</span><span lang=EN-US>/</span><span style='font-family:宋体'>源码可以在如下的链接获得:</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span lang=EN-US>An In-Depth Look into the Win32 Portable Executable File Format by Matt Pietrek</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span lang=EN-US>http://msdn.microsoft.com/msdnmag/issues/02/02/PE/default.aspx</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span lang=EN-US>An In-Depth Look into the Win32 Portable Executable File Format,Part 2 by Matt Pietrek</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span lang=EN-US>http://msdn.microsoft.com/msdnmag/issues/02/03/PE2/</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>最新版本的微软</span><span lang=EN-US>PE</span><span style='font-family:宋体'>文件格式可以通过如下链接获得:</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span lang=EN-US>Microsoft Portable Executable and Common Object File Format Specification</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span lang=EN-US>http://www.microsoft.com/whdc/system/platform/firmware/PECOFF.mspx</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span lang=EN-US>6.4 Stolen Bytes</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>代码抽取基本上就是壳移走受保护程序的一部分(通常是入口点的少量指令),这部分指令被复制并在分配的内存中执行。这在某种程度上保护了程序,因为如果从内存中</span><span lang=EN-US>dump</span><span style='font-family:宋体'>受保护进程,被抽取的指令将不会被恢复。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>这是一个可执行文件的原始入口点代码:</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>004011CB MOV EAX,DWORD PTR FS:[0]</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>004011D1 PUSH EBP</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>004011D2 MOV EBP,ESP</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>004011D4 PUSH -1</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>004011D6 PUSH 0047401C</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>004011DB PUSH 0040109A</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>004011E0 PUSH EAX</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>004011E1 MOV DWORD PTR FS:[0],ESP</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>004011E8 SUB ESP,10</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>004011EB PUSH EBX</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>004011EC PUSH ESI</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>004011ED PUSH EDI</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><span style='font-family:宋体'>下面是被</span><span lang=EN-US>Enigma</span><span style='font-family:宋体'>加密壳偷取了前两个指令的同一段代码:</span></p> <p class=MsoNormal style='text-indent:35.95pt'><b><span lang=EN-US>004011CB POP EBX</span></b></p> <p class=MsoNormal style='text-indent:35.95pt'><b><span lang=EN-US>004011CC CMP EBX,EBX</span></b></p> <p class=MsoNormal style='text-indent:35.95pt'><b><span lang=EN-US>004011CE DEC ESP</span></b></p> <p class=MsoNormal style='text-indent:35.95pt'><b><span lang=EN-US>004011CF POP ES</span></b></p> <p class=MsoNormal style='text-indent:35.8pt'><span lang=EN-US>004011D0 JECXZ SHORT 00401169</span></p> <p class=MsoNormal style='text-indent:35.8pt'><span lang=EN-US>004011D2 MOV EBP,ESP</span></p> <p class=MsoNormal style='text-indent:35.8pt'><span lang=EN-US>004011D4 PUSH -1</span></p> <p class=MsoNormal style='text-indent:35.8pt'><span lang=EN-US>004011D6 PUSH 0047401C</span></p> <p class=MsoNormal style='text-indent:35.8pt'><span lang=EN-US>004011DB PUSH 0040109A</span></p> <p class=MsoNormal style='text-indent:35.8pt'><span lang=EN-US>004011E0 PUSH EAX</span></p> <p class=MsoNormal style='text-indent:35.8pt'><span lang=EN-US>004011E1 MOV DWORD PTR FS:[0],ESP</span></p> <p class=MsoNormal style='text-indent:35.8pt'><span lang=EN-US>004011E8 SUB ESP,10</span></p> <p class=MsoNormal style='text-indent:35.8pt'><span lang=EN-US>004011EB PUSH EBX</span></p> <p class=MsoNormal style='text-indent:35.8pt'><span lang=EN-US>004011EC PUSH ESI</span></p> <p class=MsoNormal style='text-indent:35.8pt'><span lang=EN-US>004011ED PUSH EDI</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>这是被</span><span lang=EN-US>ASProtect</span><span style='font-family:宋体'>壳偷取了数条指令的相同例子。它增加了一条</span><span lang=EN-US>jump</span><span style='font-family:宋体'>指令,指向内存中一段执行被偷代码的过程,被偷的指令和垃圾代码搀杂在一起,想要恢复被偷的代码困难重重。</span></p> <p class=MsoNormal style='text-indent:35.95pt'><b><span lang=EN-US>004011CB JMP 00B70361</span></b></p> <p class=MsoNormal style='text-indent:35.8pt'><span lang=EN-US>004011D0 JNO SHORT 00401198</span></p> <p class=MsoNormal style='text-indent:35.8pt'><span lang=EN-US>004011D3 INC EBX</span></p> <p class=MsoNormal style='text-indent:35.8pt'><span lang=EN-US>004011D4 ADC AL,0B3</span></p> <p class=MsoNormal style='text-indent:35.8pt'><span lang=EN-US>004011D6 JL SHORT 00401196</span></p> <p class=MsoNormal style='text-indent:35.8pt'><span lang=EN-US>004011D8 INT1</span></p> <p class=MsoNormal style='text-indent:35.8pt'><span lang=EN-US>004011D9 LAHF</span></p> <p class=MsoNormal style='text-indent:35.8pt'><span lang=EN-US>004011DA PUSHFD</span></p> <p class=MsoNormal style='text-indent:35.8pt'><span lang=EN-US>004011DB MOV EBX,1D0F0294</span></p> <p class=MsoNormal style='text-indent:35.8pt'><span lang=EN-US>004011E0 PUSH ES</span></p> <p class=MsoNormal style='text-indent:35.8pt'><span lang=EN-US>004011E1 MOV EBX,A732F973</span></p> <p class=MsoNormal style='text-indent:35.8pt'><span lang=EN-US>004011E6 ADC BYTE PTR DS:[EDX-E],CH</span></p> <p class=MsoNormal style='text-indent:35.8pt'><span lang=EN-US>004011E9 MOV ECX,EBP</span></p> <p class=MsoNormal style='text-indent:35.8pt'><span lang=EN-US>004011EB DAS</span></p> <p class=MsoNormal style='text-indent:35.8pt'><span lang=EN-US>004011EC DAA</span></p> <p class=MsoNormal style='text-indent:35.8pt'><span lang=EN-US>004011ED AND DWORD PTR DS:[EBX+58BA76D7],ECX</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span lang=EN-US>6.5 API Redirection</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span lang=EN-US>API</span><span style='font-family:宋体'>重定向是用来防止逆向分析人员轻易重建受保护程序输入表的一种方法。原始的输入表被销毁,对</span><span lang=EN-US>API</span><span style='font-family:宋体'>的调用被重定向到位于内存中的例程,然后由这些例程负责调用实际的</span><span lang=EN-US>API</span><span style='font-family:宋体'>。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>在这个例子中代码调用了</span><span lang=EN-US>kernel32!CopyFileA() API</span><span style='font-family:宋体'>:</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>00404F</span><span lang=EN-US>05 LEA EDI,DWORD PTR SS:[EBP-20C]</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>00404FOB PUSH EDI</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>00404FOC PUSH DWORD PTR SS:[EBP-210]</span></p> <p class=MsoNormal style='text-indent:35.9pt'><span lang=EN-US>00404F</span><span lang=EN-US>12 CALL <JMP.&KERNEL32.CopyFileA></span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>被调用的代码是一个</span><span lang=EN-US>JMP</span><span style='font-family:宋体'>指令,跳转到输入表中的函数地址。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:35.9pt'><span lang=EN-US>004056B8 JMP DWORD PTR DS:[<&KERNEL32.CopyFileA>]</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>然而当</span><span lang=EN-US>ASProtect</span><span style='font-family:宋体'>壳重定向</span><span lang=EN-US>KERNEL32!CopyFileA() API</span><span style='font-family:宋体'>时,这段代码被修改为一个</span><span lang=EN-US>call</span><span style='font-family:宋体'>指令,调用壳自己分配的内存中的过程。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:35.9pt'><span lang=EN-US>004056B8 CALL 00D90000</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>下图说明了被偷的指令是如何被安置的。前</span><span lang=EN-US>7</span><span style='font-family:宋体'>条</span><span lang=EN-US>KERNEL32!CopyFileA()</span><span style='font-family:宋体'>代码中的指令被复制过来,另外</span><span lang=EN-US>0x7C83005E Call</span><span style='font-family:宋体'>指令指向的代码也被复制过来。通过一个</span><span lang=EN-US>RETN</span><span style='font-family:宋体'>指令,将控制移交回</span><span lang=EN-US>kernel32.dll</span><span style='font-family:宋体'>领空</span><span lang=EN-US>KERNEL32!CopyFileA()</span><span style='font-family:宋体'>中间的</span><span lang=EN-US>0x7C830063</span><span style='font-family:宋体'>地址处:</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><span lang=EN-US><img width=554 height=289 src="脱壳的艺术.files/image008.jpg"></span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>有些壳则更进一步将整个</span><span lang=EN-US>DLL</span><span style='font-family:宋体'>映像载入到一段分配的内存中,然后重定向</span><span lang=EN-US>API</span><span style='font-family:宋体'>调用到这些</span><span lang=EN-US>DLL</span><span style='font-family:宋体'>映像的拷贝。</span> <span style='font-family:宋体'>这个技术使得在实际的</span><span lang=EN-US>API</span><span style='font-family:宋体'>中下断点变难了。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span lang=EN-US>6.6 Multi-Threaded Packers</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>对于多线程壳,另一个线程常常用于执行一些诸如解密受保护程序这样必需的操作。多线程壳复杂度增加了,由于跟踪代码变得复杂,理解代码的难度也大大增加了。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span lang=EN-US>PECrypt</span><span style='font-family:宋体'>是一款多线程壳壳,它用第</span><span lang=EN-US>2</span><span style='font-family:宋体'>个线程来解密数据,然后这些数据被主线程使用,这些线程之间通过事件对象进行同步。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span lang=EN-US>PECrypt</span><span style='font-family:宋体'>壳操作并同步线程:</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><span lang=EN-US><img width=553 height=108 src="脱壳的艺术.files/image009.jpg"></span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span lang=EN-US>6.7 Virtual Machines</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>使用虚拟机的概念很简单:逆向分析人员最终会想出如何躲过</span><span lang=EN-US>/</span><span style='font-family:宋体'>解决反调试和反逆向技术,当受保护的程序最终需要在内存中解密并执行时,面对静态分析就显得脆弱不堪了。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>随着虚拟机的出现,受保护部分的代码被转换成了</span><span lang=EN-US>p-code</span><span style='font-family:宋体'>,</span><span lang=EN-US>p-code</span><span style='font-family:宋体'>在执行时可以转换成机器码。原始的机器指令被替换,理解代码所作所为的复杂度成指数上升。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>下面是这个概念的简单图示:</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><span lang=EN-US><img width=553 height=148 src="脱壳的艺术.files/image010.jpg"></span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>像</span><span lang=EN-US>Oreans technologies</span><span style='font-family:宋体'>的</span><span lang=EN-US>CodeVirtualizer</span><span style='font-family:宋体'>和</span><span lang=EN-US>StraForce</span><span style='font-family:宋体'>这些最新的壳都应用了虚拟机的概念来保护程序。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>对付虚拟机需要分析</span><span lang=EN-US>p-code</span><span style='font-family:宋体'>是如果组织并被虚拟机转换的,尽管这一切并不简单。获得足够的信息之后,就可以开发一款反编译引擎来分析</span><span lang=EN-US>P-code</span><span style='font-family:宋体'>并将它们转换成机器码或者是可理解的指令。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>一个开发</span><span lang=EN-US>p-code</span><span style='font-family:宋体'>反编译引擎的例子和虚拟机实现的详细信息可以通过如下链接获得:</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span lang=EN-US>Defeating HyperUnpackMe2 With an IDA Processor Module, Rolf Rolles III</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span lang=EN-US>http://www.openrce.org/articles/full_view/28</span></p> <p class=MsoNormal style='margin-top:23.4pt;margin-right:0cm;margin-bottom: 15.6pt;margin-left:0cm'><b><span lang=EN-US style='color:white;background:black'>7. </span></b><b><span style='font-family:宋体;color:white;background:black'>工具</span><span lang=EN-US style='color:white;background:black'> </span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>本节列举了逆向分析人员和恶意代码分析人员可以用来分析、脱壳的公开可用的工具。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>免责声明:这些都是第三方工具,笔者对这些工具可能导致的系统不稳定和可能影响系统的其他问题不负任何责任。建议总是在测试或恶意代码分析环境中运行这些工具。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><b><span lang=EN-US>7.1 OllyDbg</span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><span lang=EN-US>http://www.ollydbg.de/</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>逆向分析人员和恶意代码分析人员使用的一款强大</span><span lang=EN-US>Ring3</span><span style='font-family:宋体'>调试器。它的插件功能允许其他的逆向分析人员创建更多的插件,使得逆向和脱壳变得越来越简单。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><span lang=EN-US><img width=553 height=161 src="脱壳的艺术.files/image011.jpg"></span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><span lang=EN-US>7.2 Ollyscript</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><span lang=EN-US>http://www.openrce.org/downloads/details/106/OllyScript</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>一个</span><span lang=EN-US>OllyDbg</span><span style='font-family:宋体'>的插件,允许通过使用类似于汇编语言的脚本实现自动设置</span><span lang=EN-US>/</span><span style='font-family:宋体'>处理断点、补丁代码</span><span lang=EN-US>/</span><span style='font-family:宋体'>数据等。在执行重复性的工作或者是自动脱壳是尤其有用。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><span lang=EN-US><img width=202 height=145 src="脱壳的艺术.files/image012.jpg"></span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><span lang=EN-US>7.3 Olly Advanced</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><span lang=EN-US>http://www.openrce.org/downloads/details/241/Olly_Advanced</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>针对逆向分析人员如果说壳有盔甲的话,那么这个</span><span lang=EN-US>OllyDbg</span><span style='font-family:宋体'>的插件就是逆向分析人员调试器的盔甲。它有很多选项用来躲过反调试技术,隐藏</span><span lang=EN-US>OllyDbg</span><span style='font-family:宋体'>使其不被壳检测到。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><span lang=EN-US><img width=242 height=149 src="脱壳的艺术.files/image013.jpg"></span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><span lang=EN-US>7.4 OllyDump</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><span lang=EN-US>http://www.openrce.org/downloads/details/108/OllyDump</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>成功脱壳后,这个</span><span lang=EN-US>OllyDbg</span><span style='font-family:宋体'>插件可以用来</span><span lang=EN-US>dump</span><span style='font-family:宋体'>进程并且重建输入表。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><span lang=EN-US><img width=354 height=94 src="脱壳的艺术.files/image014.jpg"></span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><span lang=EN-US>7.5 ImpRec</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><span lang=EN-US>http://www.woodmann.com/crackz/Unpackers/Imprec16.zip</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span style='font-family:宋体'>最后,这是另一款</span><span lang=EN-US>dump</span><span style='font-family:宋体'>进程和重建输入表的工具。它是一款独立的工具,它提供了最出色的输入表重建能力。</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><span lang=EN-US><img width=321 height=129 src="脱壳的艺术.files/image015.jpg"></span></p> <p class=MsoNormal style='margin-top:23.4pt;margin-right:0cm;margin-bottom: 15.6pt;margin-left:5.15pt;text-indent:-5.15pt'><b><span lang=EN-US style='color:white;background:black'>8 </span></b><b><span style='font-family: 宋体;color:white;background:black'>参考</span><span lang=EN-US style='color:white; background:black'> </span></b></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><span style='font-family:宋体'>书籍:逆向工程,软件保护</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span lang=EN-US>Reversing: Secrets of Reverse Engineering. E.Eilam.Wiley, 2005</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span lang=EN-US>Crackproof Your Software , P.Cerven.No Starch Press, 2002</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><span style='font-family:宋体'>书籍:</span><span lang=EN-US>Windows</span><span style='font-family:宋体'>和处理器底层</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span lang=EN-US>Microsoft Windows Internal, 4</span><sup><span lang=EN-US>th</span></sup><span lang=EN-US> Edition . M. Russinovich, D. Solomon, Microsoft Press,</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span lang=EN-US>IA-32 Intel Architecture Software Developer’s Manual. Volume 1-3, Intel Corporation, 2006</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><span style='font-family:宋体'>链接:</span><span lang=EN-US>Windows</span><span style='font-family:宋体'>底层</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span lang=EN-US>ReactOS Project</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span lang=EN-US>http://www.reactos.org/en/index.html</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span lang=EN-US>Source Search: http://www.reactos.org/generated/doxygen/</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span lang=EN-US>Wine Project</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span lang=EN-US>http://www.winehq.org/</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span lang=EN-US>Source Search: http://source.winehq.org/source/</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span lang=EN-US>The Undocumented Functions</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span lang=EN-US>http://undocumented.ntinternals.net</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span lang=EN-US>MSDN</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span lang=EN-US>http://msdn2.microsoft.com/en-us/default.aspx</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm'><span style='font-family:宋体'>链接:逆向工程,软件保护,脱壳</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span lang=EN-US>OpenRCE</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span lang=EN-US>http://www.openrce.org</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span lang=EN-US>OpenRCE Anti Reverse Engineering Techniques Database</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span lang=EN-US>http://www.openrce.org/reference_library/anti_reversing</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span lang=EN-US>RCE Forums</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span lang=EN-US>http://www.woodmann.com/forum/index.php</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span lang=EN-US>EXETOOLS Forums</span></p> <p class=MsoNormal style='margin-top:7.8pt;margin-right:0cm;margin-bottom:7.8pt; margin-left:0cm;text-indent:17.95pt'><span lang=EN-US>http://forum.exetools.com</span></p> </div> </body> </html>
附件: 您所在的用户组无法下载或查看附件
|
||||
|
TOP
|


发送到消息盒子