中国IT动力,最新最全的IT技术教程
最新100篇 | 推荐100篇 | 专题100篇 | 排行榜 | 搜索 | 在线API文档
首 页 | 程序开发 | 操作系统 | 软件应用 | 图形图象 | 网络应用 | 精文荟萃 | 教育认证 | 硬件维护 | 未整理篇 | 站长教程
ASP JS PHP工程 ASP.NET 网站建设 UML J2EESUN .NET VC VB VFP 网络维护 数据库 DB2 SQL2000 Oracle Mysql
服务器 Win2000 Office C DreamWeaver FireWorks Flash PhotoShop 上网宝典 CorelDraw 协议大全 网络安全 微软认证
硬件维护  CPU  主板  硬盘  内存  显卡  显示器  键盘鼠标  声卡音箱  打印机  机箱电源  BIOS  网卡  C#  Java  Delphi  vs.net2005
  当前位置:> 看雪学院专区 > 外文翻译
【翻译】反逆向工程揭密(试炼篇)-全文完
作者:佚名 时间:2006-12-15 12:49 出处:pediy.com 责编:月夜寒箫
              摘要:【翻译】反逆向工程揭密(试炼篇)-全文完
Anti Reverse Engineering Uncovered
作者:Nicolas Brulez*
E-Mail: 0x90@Rstack.org

Abstract 
Rather than doing another complete analysis of the binary, i will rather present the techniques i have used in the challenge, and how i have implemented them. The Scan of the Month 33 was released by the Honeynet Project in November 2004. I invite everyone to read the excellent submissions we received this month once they have read my paper. I am presenting the binary from the protection author point of view, while they presented it from the analyst point of view. You will learn the methods and techniques used to Protect / Unprotect a binary with this month's challenge. A lot of weaknesses were left on purpose in this binary and they will be presented here. 
Keywords: Software Protection; Reverse Code Engineering; Linux; Anti-Debugging; Anti-Anti-Debugging  
摘要:
有人偏爱详细的分析过程,而我却喜欢从技术和实现方法的层面上去探讨。2004年的11月,The Honeynet Project发布了The scan of the month 33。我推荐大家好好的研读一下。他们研究的就是前者。我除了对技术和实现方法进行研究外,大量的漏洞在我的文中也有披露。
关键词:软件保护;逆向工程;Linux;反调试;调试。

1. Introduction 
This month's challenge is to analyze an unknown binary, in an effort to reinforce the value of reverse engineering, and improve (by learning from the security community) the methods, tools and procedures used to do it. This challenge is similar to SotM 32. However, this binary has mechanisms implemented to make the binary much harder to analyze, to protect against reverse engineering. 
1.简介:
最新的month 33挑战了一段未知的二进制代码,这对于我们加深逆向印象、改进方法、了解工具的使用及其整个过程具有积极的作用。这与先前发布的The scan of the month 32是相同的。唯一不同的是,此二进制代码更加难于分析。

Skill Level: Advanced/Expert 
All we are going to tell you about the binary is that it was 'found' on a WinXP system and has now be sent to you for analysis. You will have to analyse it in-depth and get as much information as possible about its inner working, and what is the goal of the binary. The main goal of this challenge is to teach people how to analyse heavily armored binaries. Such techniques could be used in the future, and its time to get used to them. 
2. Identify and explain any techniques in the binary that protect it from being analyzed or reverse engineered 
技术含量:中/高级
你将自己独立分析这些基于WinXP环境下的二进制代码。想完成挑战,你必须深刻的分析问题,搜集更多的信息去了解内部原理以及体会代码中包含的目的。最终使你能够独立的分析一层又一层的代码。
2.鉴别并解释阻碍分析和逆向的技术。

Many techniques have been used in order to slow down analysis and break reverse engineers tools: 
• PE Header Modifications 
Many fields of the PE header were modified in order to disturb analysing tools, and thus, the Reverse Engineer. I will quickly cover the most important changes: 
->Optional Header 
Magic: 0x010B (HDR32_MAGIC) 
MajorLinkerVersion: 0x02 
MinorLinkerVersion: 0x19 -> 2.25 
SizeOfCode: 0x00000200 
SizeOfInitializedData: 0x00045400 
SizeOfUninitializedData: 0x00000000 
AddressOfEntryPoint: 0x00002000 
BaseOfCode: 0x00001000 
BaseOfData: 0x00002000 
ImageBase: 0x00DE0000 <--- "Non Standard" ImageBase 
SectionAlignment: 0x00001000 
FileAlignment: 0x00001000 
MajorOperatingSystemVersion: 0x0001 
MinorOperatingSystemVersion: 0x0000 -> 1.00 
MajorImageVersion: 0x0000 
MinorImageVersion: 0x0000 -> 0.00 
MajorSubsystemVersion: 0x0004 
MinorSubsystemVersion: 0x0000 -> 4.00 
Win32VersionValue: 0x00000000 
SizeOfImage: 0x00049000 
SizeOfHeaders: 0x00001000 
CheckSum: 0x00000000 
Subsystem: 0x0003 (WINDOWS_CUI) 
DllCharacteristics: 0x0000 
SizeOfStackReserve: 0x00100000 
SizeOfStackCommit: 0x00002000 
SizeOfHeapReserve: 0x00100000 
SizeOfHeapCommit: 0x00001000 
LoaderFlags: 0xABDBFFDE <--- Bogus Value 
NumberOfRvaAndSizes: 0xDFFFDDDE <--- Bogus Value 
The "standard" ImageBase usually is 400000 for Win32 applications and Reverse Engineers are used to analyse programs with such an ImageBase. While it isn't a protection by itself, this simple modification will confuse some Reverse Engineers, because they aren't used to such memory addresses. 
包括以下这些:
(1)修改PE文件头:
目的:干扰工具的分析。附上重要的修改:
->Optional Header 
Magic: 0x010B (HDR32_MAGIC) 
MajorLinkerVersion: 0x02 
MinorLinkerVersion: 0x19 -> 2.25 
SizeOfCode: 0x00000200 
SizeOfInitializedData: 0x00045400 
SizeOfUninitializedData: 0x00000000 
AddressOfEntryPoint: 0x00002000 
BaseOfCode: 0x00001000 
BaseOfData: 0x00002000 
ImageBase: 0x00DE0000 <--- 非一般性基址
SectionAlignment: 0x00001000 
FileAlignment: 0x00001000 
MajorOperatingSystemVersion: 0x0001 
MinorOperatingSystemVersion: 0x0000 -> 1.00 
MajorImageVersion: 0x0000 
MinorImageVersion: 0x0000 -> 0.00 
MajorSubsystemVersion: 0x0004 
MinorSubsystemVersion: 0x0000 -> 4.00 
Win32VersionValue: 0x00000000 
SizeOfImage: 0x00049000 
SizeOfHeaders: 0x00001000 
CheckSum: 0x00000000 
Subsystem: 0x0003 (WINDOWS_CUI) 
DllCharacteristics: 0x0000 
SizeOfStackReserve: 0x00100000 
SizeOfStackCommit: 0x00002000 
SizeOfHeapReserve: 0x00100000 
SizeOfHeapCommit: 0x00001000 
LoaderFlags: 0xABDBFFDE <--- 伪值
NumberOfRvaAndSizes: 0xDFFFDDDE <--- 伪值
400000作为Win32应用程序的一般基址,常常被逆向者在分析程序时使用。当程序不能保护自身时,这种简单的修改就可以对逆向工程造成影响,因为程序并不使用这样的内存地址。

"Anti" OllyDbg: 
LoaderFlags and NumberOfRvaAndSizes were modified.. I have Reverse Engineered OllyDBG and Soft ICE to find a few tricks that could slow down the analysis of a binary. With those two modifications, Olly will pretend that the binary isn't a good image and will eventually run the application without breaking at its entry point. This could be a bad thing if you wanted to debug a mal ware on your computer, because you would get infected. 
反OllyDbg:
通过修改LoaderFlags和NumberOfRvaAndSizes实现。逆向OllyDbg和SoftICE后,我们会发现这种方法可以阻碍二进制代码的分析,因为程序会认为此二进制不是正确的基址,从而不在入口点处执行。导致直接的后果是:如果你调试一个病毒程序,你的计算机将会被感染。

Anti Soft ICE : Blue Screen of Death and no Chocolate: 
The NumberOfRvaAndSizes field has been modified in order to reboot any computer running a recent version of Soft ICE. While Disassembling the PE Loader of Soft ICE, i found a very critical vulnerability in Soft ICE that allows one binary to crash any computer running Soft ICE without any code execution. This vulnerability (bug) has been reported to Compuware and should be fixed in the next version. Apparently it didn't happen on some of the authors of the submissions for some reasons. Oh well. 
Here is the disassembly of Soft ICE PE loader to find out why it reboots your computer: 
.text:000A79FE 
.text:000A79FE loc_A79FE: ; CODE XREF: sub_A79B9+31j 
.text:000A79FE ; sub_A79B9+3Cj 
.text:000A79FE ; DATA XREF: .text:00012F9Bo 
.text:000A79FE sti 
.text:000A79FF mov esi, ecx 
.text:000A7A01 mov ax, [esi] 
.text:000A7A04 cmp ax, 'ZM' 
.text:000A7A08 jnz not_PE_file 
.text:000A7A08 
.text:000A7A0E mov edi, [esi+_IMAGE_DOS_HEADER.e_lfanew] 
.text:000A7A11 add edi, esi 
.text:000A7A13 mov ax, [edi] 
.text:000A7A16 cmp ax, 'EP' 
.text:000A7A1A jnz not_PE_file 
.text:000A7A1A 
.text:000A7A20 movzx ecx, [edi+IMAGE_NT_HEADERS.FileHeader.NumberOfSections] 
.text:000A7A24 or ecx, ecx 
.text:000A7A26 jz not_PE_file 
.text:000A7A26 
.text:000A7A2C mov eax, [edi+IMAGE_NT_HEADERS.OptionalHeader.NumberOfRvaAndSizes] 
.text:000A7A2F lea edi, [edi+eax*8+IMAGE_NT_HEADERS.OptionalHeader.DataDirectory] 
.text:000A7A33 mov eax, ecx 
.text:000A7A35 imul eax, 28h 
.text:000A7A38 mov al, [eax+edi] ; CRITICAL BUG! One can force EAX+EDI to be equal to zero. Reading at [0] in ring 0 isn't nice eh ;-) 
.text:000A7A3B 
.text:000A7A3B loc_A7A3B: ; DATA XREF: .text:00012FA5o 
.text:000A7A3B cli 
 

  
.text:000A7A3C call sub_15C08 
.text:000A7A3C 
.text:000A7A41 mov byte_FA259, 0 
.text:000A7A48 push eax ; Save EAX 
.text:000A7A49 mov eax, dword_16B56F ; EAX is modified by a saved dword 
.text:000A7A4E mov dr7, eax ; Debug Register 7 take the value in EAX 
.text:000A7A51 pop eax ; EAX is restored 
.text:000A7A52 mov dword_FC6CC, esp 
.text:000A7A58 mov esp, offset unk_FBABC 
.text:000A7A5D and esp, 0FFFFFFFCh 
.text:000A7A60 xor al, al ; AL is zeroed? Why this mov al, [eax+edi] then ? 
.text:000A7A60 ; I don't see the point. old code? 
.text:000A7A62 call sub_4D2EB 
.text:000A7A62 
.text:000A7A67 call sub_36AC1 
.text:000A7A67 
.text:000A7A6C xor edx, edx 
.text:000A7A6E 
.text:000A7A6E loc_A7A6E: ; CODE XREF: sub_A79B9+124j 
.text:000A7A6E call sub_74916 
.text:000A7A6E 
反SoftIce:蓝屏死机花屏:
修改NumberOfRvaAndSizes会令含SoftIce的系统重起。通过反汇编SoftIce装载器,我发现:无需代码执行,一个二进位的变动就可使一台含SoftIce的电脑立刻崩溃。不过此漏洞已上报COMPUWARE并将在下一版本中改进。但是,由于某种原因,一些系统却运行正常。让我们来看一下反汇编后的SoftICE装载器里到底有什么:
.text:000A79FE 
.text:000A79FE loc_A79FE: ; CODE XREF: sub_A79B9+31j 
.text:000A79FE ; sub_A79B9+3Cj 
.text:000A79FE ; DATA XREF: .text:00012F9Bo 
.text:000A79FE sti 
.text:000A79FF mov esi, ecx 
.text:000A7A01 mov ax, [esi] 
.text:000A7A04 cmp ax, 'ZM' 
.text:000A7A08 jnz not_PE_file 
.text:000A7A08 
.text:000A7A0E mov edi, [esi+_IMAGE_DOS_HEADER.e_lfanew] 
.text:000A7A11 add edi, esi 
.text:000A7A13 mov ax, [edi] 
.text:000A7A16 cmp ax, 'EP' 
.text:000A7A1A jnz not_PE_file 
.text:000A7A1A 
.text:000A7A20 movzx ecx, [edi+IMAGE_NT_HEADERS.FileHeader.NumberOfSections] 
.text:000A7A24 or ecx, ecx 
.text:000A7A26 jz not_PE_file 
.text:000A7A26 
.text:000A7A2C mov eax, [edi+IMAGE_NT_HEADERS.OptionalHeader.NumberOfRvaAndSizes] 
.text:000A7A2F lea edi, [edi+eax*8+IMAGE_NT_HEADERS.OptionalHeader.DataDirectory] 
.text:000A7A33 mov eax, ecx 
.text:000A7A35 imul eax, 28h 
.text:000A7A38 mov al, [eax+edi] ; EAX+EDI=0则崩溃。权限0中读取[0]地址可不妙。 
.text:000A7A3B 
.text:000A7A3B loc_A7A3B: ; DATA XREF: .text:00012FA5o 
.text:000A7A3B cli 
.text:000A7A3C call sub_15C08 
.text:000A7A3C 
.text:000A7A41 mov byte_FA259, 0 
.text:000A7A48 push eax ; Save EAX 
.text:000A7A49 mov eax, dword_16B56F ; 用双字修改EAX中值 
.text:000A7A4E mov dr7, eax ; 寄存器7从EAX中取值 
.text:000A7A51 pop eax ; 恢复EAX 
.text:000A7A52 mov dword_FC6CC, esp 
.text:000A7A58 mov esp, offset unk_FBABC 
.text:000A7A5D and esp, 0FFFFFFFCh 
.text:000A7A60 xor al, al ; AL取0?为什么要mov al, [eax+edi]? 
.text:000A7A60 ; 不明白.原始代码? 
.text:000A7A62 call sub_4D2EB 
.text:000A7A62 
.text:000A7A67 call sub_36AC1 
.text:000A7A67 
.text:000A7A6C xor edx, edx 
.text:000A7A6E 
.text:000A7A6E loc_A7A6E: ; CODE XREF: sub_A79B9+124j 
.text:000A7A6E call sub_74916 
.text:000A7A6E 

As you can see from the code above, we can force Soft ICE to read at memory location [0] or something similar using a special value inside the PE header. For this binary i didn't bother calculating the exact value to read at address [0], that's may explain why it didn't crash for some people.I won't explain how to calculate this special value because it is trivial and i don't want Dark lords to use that trick without a little brainstorming. 
To fix this problems, one needs to patch the value in the PE Header. The standard value for NumberOfRvaAndSizes is 0x10.Just patch this value in the PE Header and the Soft ICE wrecking will be gone. The OllyDBG problem as well, because it is based on BOTH fields modifications. You can also nullify the other field if you want. 
如你所见,我们可以令SoftIce读取[0]地址或其它特殊值。但我并不知道这时确切的数值,这可能与系统是否崩溃有关。我不想讨论怎样计算这一特殊值,因为它与主题无关,另外也给大家留下讨论的空间。
你可以通过值的修改来修正这一问题。NumberOfRvaAndSizes的一般值为0x10。修改它。另外因为Olly和SoftIce都基于这种技术,因此你都可以通过改变NumberOfRvaAndSizes的值或归0来搞定。


• Section Modification: Or how to kill many tools. 
->Section Header Table 
1. item: 
Name: CODE 
VirtualSize: 0x00001000 
VirtualAddress: 0x00001000 
SizeOfRawData: 0x00001000 
PointerToRawData: 0x00001000 
PointerToRelocations: 0x00000000 
PointerToLinenumbers: 0x00000000 
NumberOfRelocations: 0x0000 
NumberOfLinenumbers: 0x0000 
Characteristics: 0xE0000020 
(CODE, EXECUTE, READ, WRITE) 
2. item: 
Name: DATA 
VirtualSize: 0x00045000 
VirtualAddress: 0x00002000 
SizeOfRawData: 0x00045000 
PointerToRawData: 0x00002000 
PointerToRelocations: 0x00000000 
 

  
PointerToLinenumbers: 0x00000000 
NumberOfRelocations: 0x0000 
NumberOfLinenumbers: 0x0000 
Characteristics: 0xC0000040 
(INITIALIZED_DATA, READ, WRITE) 
3. item: 
Name: NicolasB 
VirtualSize: 0x00001000 
VirtualAddress: 0x00047000 
SizeOfRawData: 0xEFEFADFF <--- BIG Size of section on the disk. 
PointerToRawData: 0x00047000 
PointerToRelocations: 0x00000000 
PointerToLinenumbers: 0x00000000 
NumberOfRelocations: 0x0000 
NumberOfLinenumbers: 0x0000 
Characteristics: 0xC0000040 
(INITIALIZED_DATA, READ, WRITE) 
4. item: 
Name: .idata 
VirtualSize: 0x00001000 
VirtualAddress: 0x00048000 
SizeOfRawData: 0x00001000 
PointerToRawData: 0x00047000 
PointerToRelocations: 0x00000000 
PointerToLinenumbers: 0x00000000 
NumberOfRelocations: 0x0000 
NumberOfLinenumbers: 0x0000 
Characteristics: 0xC0000040 
(INITIALIZED_DATA, READ, WRITE) 
节的修改:工具杀手。
->Section Header Table (节头表)
条目1:
Name: CODE 
VirtualSize: 0x00001000 
VirtualAddress: 0x00001000 
SizeOfRawData: 0x00001000 
PointerToRawData: 0x00001000 
PointerToRelocations: 0x00000000 
PointerToLinenumbers: 0x00000000 
NumberOfRelocations: 0x0000 
NumberOfLinenumbers: 0x0000 
Characteristics: 0xE0000020 
(CODE, EXECUTE, READ, WRITE) 

条目2:
Name: DATA 
VirtualSize: 0x00045000 
VirtualAddress: 0x00002000 
SizeOfRawData: 0x00045000 
PointerToRawData: 0x00002000 
PointerToRelocations: 0x00000000 
 

  
PointerToLinenumbers: 0x00000000 
NumberOfRelocations: 0x0000 
NumberOfLinenumbers: 0x0000 
Characteristics: 0xC0000040 
(INITIALIZED_DATA, READ, WRITE) 

条目3:
Name: NicolasB 
VirtualSize: 0x00001000 
VirtualAddress: 0x00047000 
SizeOfRawData: 0xEFEFADFF <--- 大容量的节
PointerToRawData: 0x00047000 
PointerToRelocations: 0x00000000 
PointerToLinenumbers: 0x00000000 
NumberOfRelocations: 0x0000 
NumberOfLinenumbers: 0x0000 
Characteristics: 0xC0000040 
(INITIALIZED_DATA, READ, WRITE) 

条目4:
Name: .idata 
VirtualSize: 0x00001000 
VirtualAddress: 0x00048000 
SizeOfRawData: 0x00001000 
PointerToRawData: 0x00047000 
PointerToRelocations: 0x00000000 
PointerToLinenumbers: 0x00000000 
NumberOfRelocations: 0x0000 
NumberOfLinenumbers: 0x0000 
Characteristics: 0xC0000040 
(INITIALIZED_DATA, READ, WRITE)

From those informations, we can conclude a few things. First, the binary doesn't seem to be compressed, because the Virtual Address and Size matche the Raw Offset and Size at one exception, the NicolasB section. This section has an extremly big size of raw data, which will crash a few tools and make a few others very very slow. 
通过以上信息,我们可以推断出以下一些结论。1。二进制代码未被压缩,因为除了NicolasB,其它节的Virtual Address= the Raw Offset,Virtual Size= Raw Size。NicolasB节包含了巨大的raw数值,这将使进行逆向的工具崩溃或受到阻碍。

IDA will try to allocate a LOT of memory because it thinks that the section is THAT big, turning your computer into a very slow turtle ;-). Eventually, it will load the file, or run out of memory, depending of the computer you are using to do the analysis. 
This modification will also create havoc with many tools such as Objdump, PE editor, some memory dumpers etc. It is very easy to fix this problem, you need to correct the Raw Size. If you look at the section following this special one, you will find that it starts at the very same Raw Offset. This means that the other section is actually null on the disk. You can therefore, safely replace the big value by zero. 
当节包含巨大数值时,IDA会分配大量的内存空间,这将使你的计算机运行缓慢。事实上,是装载文件或是内存溢出,都取决于正在用来分析的电脑性能。
另外,这种修改还会对许多工具如Objdump,Peeditor,一些内存Dumper等产生严重破坏。不过修正这个问题很简单,只要修改一下Raw Size即可。尽管你发现某个节与其它不同,但是它们仍然是具有相同的Raw Offset. 这就意味着,如果其它节是空的,处于安全考虑,你可以把特殊的那个节也取空。

Protection Weakness: 
While writing this binary, i knew people were going to patch the PE header but i didn't do any integrity checks on purpose. Originally i wanted to use the value in the PE Header as keys to decrypt a few layers of the protection, and the result would have been an unworking binary if this one had been changed.
I have also changed a few other things in the PE header, but nothing of real interest here. (who said Cosmetic?) 
保护漏洞:
当我贴出这些代码时,许多人都会去对PE头进行修正,可我不是这样做的。最初,我想利用PE Header中的数值作为打开保护层的钥匙,但是一旦改变数值,它就变成了一段无效的二进制代码。另外我还试着修改其它地方,可都一无所获。

• Junk Code 
All along the binary, i have added junk code between real instructions, in order to make the analysis a little harder. The junk code are long blocks of code that does nothing but fancy operations to disturb the analyst , especially when he choose to do a static analysis of the binary. Each block of Junk Code is different and have been generated by a personal tool. A Thrash generator which creates macros to be inserted in the code source around real instructions. 
Here is how it looks inside a disassembler: 
The junk code starts with a pushad (save all registers states onto the stack) and finish with a popad (restore register states).Here is the end of a block of junk: 
垃圾代码
在真正的代码中,我添加了一些垃圾代码,这是为了增加分析的难度。它们冗长无意义,但是可以阻止分析的进行,特别是静态分析时。而每一个又各不相同,由独立的工具产生。一个好的程序会把这种代码大量的运用于真正的代码之中。
以下是我们在反汇编器中所看到的:
垃圾代码以pushad开头(在堆栈中保存所有寄存器状态)并以popad(恢复寄存器状态)。这是它们的尾部:

Protection Weakness: 
The thrash generator isn't perfect (at least with the options i have used here ;) and it is easy to find the start and the end of a block of junk code. The junk code is bounded by pushad/popad. When i wrote this binary i was aware of this problem, but this is a perfect real life example of protection weakness. It allows Reverse Engineers to practice IDA/Ollydbg scripting. Very interesting scripts were found in the submissions. I invite you to have a look at them if you didn't know how to write one. When i wrote the binary, i already had a better version of my Thrash generator that doesn't use any pushad/popad around the blocks of useless code, but we will keep it for another challenge, if any.
保护漏洞:
这个程序不是完美的,你很容易找到垃圾代码的头和尾。即pushad/popad。当我写这段代码时想过这个问题,但这确实是最典型的保护漏洞。逆向者用它来练习IDA/Ollydbg脚本会非常有趣。如果你不会,建议你去了解一下。当我完成这个程序后,我又写了更好的非典型漏洞的版本,这是后话了。

• SEH - Structured Exception Handling 
Windows SEH were used extensively in this binary. It allows one to access the context structure of the current application, and therefore, access privileged registers such as Debug Registers. Those registers are used by Hardware Breakpoints (BPM). If you can access them, you can also erase the hardware breakpoints. 
SEH-结构异常链
SEH被广泛地运用在二进制代码中。它允许你访问当前应用程序的context structure和优先级寄存器(调试器)。这些寄存器被硬件断点(BPM)所使用。因为可存取,因此这些硬件断点也可被擦除。

• Timing Detection Through SEH 
Here is a little detection i invented to detect debuggers. If we merge SEH (And access to context structure) with the known Timing Detection Technique, we can detect a lot of Ring 3 debuggers and Tracers. The idea is to read the Time Stamp Counter using RDTSC (number of cycles executed by the CPU basically) and then generating an Exception.
 通过SEH侦测时间
这是一个侦测调试器的程序。当我在SEH(存取context structure)中加入时间侦测技术后,可以发现:许多Ring 3级别的调试器和追踪器都可以被侦测到。原理是通过RDTSC(CPU执行数字循环)读取了Time Stamp指针最终产生异常。

In the exception handler, we can access the EAX register (previously modified by RDTSC) in the Context Structure, which contains the TSC. In the Exception Handler, we use RDTSC one more time, to get the current TSC value. Now, we can compare both TSC to see whether the program has been debugged/traced or not. If such an action has occured, the difference of cycles will be huges, thus triggering the Payload. In this binary, i just modified EIP through the context structure. The application resumes at a different location skipping mandatory instructions.The application crashes eventually. It seems that on some version on Windows, it doesn't work as expected because of the utilisation of the CPUID instruction, that will modify the ECX register. 
在异常链中,我们可以访问Context Structure中的EAX寄存器(先前被RDTSC修改)。同时,我们更多的使用RDTSC得到当前TSC的数值。现在,我们比较这两个TSC,看是否程序被调试/跟踪。如果调试/跟踪,两者值会有巨大差距,导致最终被发现。虽然我修改了context structure中EIP的数值,使得程序跳过强制指令定位到新的位置。可是最终程序仍然崩溃。可以看出Windows中的一些版本,因为CPUID指令修改了ECX寄存器而并不象我们想象中的那样运行。

The detection became less stealth because of this "bug", but it would still have been a matter of time until someone discovered it anyway. Many people wondered why i used CPUID in the program before RDTSC. The reason is that on recent CPU such as P4, there is a feature called: Out of Order Execution. The CPUID is a synchronization instruction which tells to the CPU not to use Out Of Order execution, avoiding False Positives in the debugger detection. If you don't tell to the CPU not to use OOO execution, you don't know in which order the CPU is going to execute your code. It can be different from your source code. Sometimes, it will create a false positive and your program will crash for no reason. 
Here is the code of this detection: 
E0000h is the maximum cycles difference accepted by this detection. If the number is bigger, then a debugger is most likely running and debugging our application. 
尽管此侦测的原理有了一些进展,但仍然有很多需要去发觉。许多人奇怪为什么CPUID使用在RDTSC之前。因为现在的CPU(如P4)具有被称之为“Out of Order Execution”的功能。CPUID是一个同步指令,为了避免调试器侦测中的错误行为,它会禁止CPU使用Out of Order功能。即使你打开了这项功能,你也不知道在什么地方执行了你的代码,并可能与你的原代码有出入、执行错误的命令甚至使系统莫名其妙的崩溃。
下面是侦测的代码:
E0000H是侦测差距的上限。如果超过这个上限,调试器可能会正常运行。

Protection Weakness: 
I have used a fixed value for the number of cycles: E0000h. I could have (Actually i can do it with my layer generator) used a random value rather than a constant and therefore, making the scan for this constant useless. I could also have used different instructions for each SEH to make the creation of a generic pattern difficult. The biggest weakness of this detection is the constant and the usage of the same instructions for every checks. It is also possible to write a Kernel Module Driver to catch every execution of RDTSC (See Intel documentation for further informations) and return very similar values, thus bypassing the detection completely.
保护漏洞:
我使用了上限E0000H。然而我可以使用任意数值代替这个常数。另外我还可以对每个SEH使用不同的指令使其产生不同的效果。最大的漏洞就是常数的设置和每个校验使用相同的指令。因此,我们可以写出一段Kernel Module Driver,去获取RDTSC的执行过程和返回的数值,以此击败这种保护方式。

• BPX Detection: 
As we are going to use API functions, We have to protect them from beeing BPX'ed by an attacker. Rather than Using GetProcAddress to get the API address and then to check for an int 3 opcode (0xCC) in the API function code, i have used a different method. I directly access 
the Import Table , more precisely, the Import Address Table to read the API function address and then start to search for breakpoints. 
The int 3 opcode is 0xCC and is known by Reverse Engineers. In order to make a little less obvious, i have obfuscated the breakpoint check using a "SHR" (Shift Right) instruction: 0x660 shr 3 = 0xCC ;-). The program will then check four bytes at API function entry point, looking for a breakpoint. If a breakpoint is found, i have used a funny way to crash the application. Im using RDTSC to generate a pseudo random number and i put this number onto the stack. To modify EIP, i simply use the RET instruction, which will transfer us to random memory address, crashing our application. Each time a detection occurs, the address is different, thus hard to monitor. The crash occurs far from the detection code and Soft ICE's FAULT ON won't catch it either. 
BPX侦测:
当使用API函数时,我们需要防止攻击者利用BPX作恶。一般的方法是:使用GetProcAddress得到API地址并在函数代码中检测INT3操作码,而我使用了另一种不同的方法:在输入表读取API函数地址时直接访问输入表,查找设置的断点。
大家都知道,INT3的操作码是0xCC。为了使其更具迷惑性,我使用了“SHR”这种指令:0x660 shr 3=0xCC.程序因此在API函数的入口点中校验这四个字节,查找断点。一旦发现,程序崩溃。我使用RDTSC产生任意一个假的数值,并把此值放入堆栈。修改了EIP后,我使用RET语句引导我们到达任意一个内存地址中,从而使程序崩溃。而且每次侦测的地址都不同也无法监测。崩溃来自于侦测的代码,就连SoftIce的FAULT ON功能也无法实现。

Protection Weakness: 
First, the Imports aren't protected, therefore anyone can read the Imported functions from the binary. From The import table we can see that printf, GetCommandLineA and ExitProcess are used. This is a weakness. A Reverse Engineer can put breakpoints on those functions, or at least, guess they are going to be used at some point. In the case of our binary, one can guess that the application is waiting for a special command line. A solution would be to load the Import Table manually.
保护漏洞:
首先,输入表未受保护,因此任何人都可以从代码中读取输入函数。从输入表中,我们可以发现如printf,GetCommandLineA和ExitProess 语句的使用。这是一个漏洞。逆向者可以在上述地方下断点或预测将会使用的地方。以代码为例,我们可以预测到程序正在等待一项特殊的指令。而对策就是手动装载输入表。

For this we could use a home made GetProcAddress function to browse the Export Table of the dlls we want to import functions from, and then, get the address of the API function from there. A Kernel32 address is always on the stack when a binary is started, so we could have used this value to get the dll's ImageBase (Or use the PEB, SEH chaining etc..). We would have everything needed to get the address of Loadlibrary which allows us to Load ANY dll, and thus, to get the address of ANY API function. With this method, we don't need any Import Table at all.
这样说来,我们可以通过GetProcAddress浏览dll的输出表函数并得到API地址。当代码运行时,堆栈中的Kernel32地址可以帮助我们得到dll的基址(或使用PEB,SHE链等)。我们从装载dll的Loadlibrary的地址处得到所有API函数的地址,而无须借助任何输入表。

Well actually, this isn't true. There is a mandatory thing to do to keep compatibility with all versions of Windows. We have to create a very small Import Table, with at least ONE import from Kernel32, else the binary won't run on Windows 2000. The Windows 2000 PE Loader is different from the one in Windows XP. XP doesn't care whether there is any import table or not. 
事实上,这是不正确的。为了与各个版本的Windows兼容,输入表是必须的。我们需要建立一个输入表,至少要包括一个Kernel32中的输入,否则2000中将无法运行。因为2000与XP的PE装载器是不同的,后者并不在乎是否有输入表存在。

The small Import Table is just for compatibility issue, the real import table is encrypted and will be decrypted at runtime by the protection.
Then, it is just a matter of loading the Imports mimicing the Operating System. We need to put the API address in the Import Address Table (of the decrypted Import Table) manually. The Reverse Engineer has no clue about the API functions used by the binary until he gets to the part of the code that will decrypt and load the Imports.
这种输入表完全是为了兼容而服务,真正的输入表在运行时被其保护所加解密。
模拟一下系统装载输入表的过程。我们需要手动的把API地址放到IAT中。逆向者只有在得到已解密和装载的输入后才能了解真正的API函数。

The BPX protection has a few weaknesses. I only check for four bytes at API entry point, which can be easily bypassed, if the API has many instructions. One could put a breakpoint to the first instruction after the 4 bytes boundary.A Better check would use a Length Disassembler Engine (LDE) which tells us the size of the instructions. With this, we can safely scan a lot of instructions without triggering any false positive.
BPX保护有一个缺点。由于指令的单一,仅仅校验API入口点的4个字节很容易被解密,只要在这个陷阱后的第一个指令处下断点即可。更好的校验是分析指令长度的Length Disassembler Engine(LDE)技术。这项技术可以使程序正确地执行校验。

A genuine instruction can contain the byte 0xCC and yet not beeing a breakpoint. Eg: Mov eax, 0x4010CC. The detection would trigger a false positive on this instruction, because of the 0xCC inside of it. On the other hand, a LDE would tell us the size of this instruction (5 Bytes). An int 3 (breakpoint) is either one or two bytes (0xCC or 0xCD 0x03). We would therefore skip the current instruction and check the following one.
正确的指令不是断点,但仍然包含0xCC。例如:Mov eax, 0x4010CC。此处会造成错误的校验,因为含有0xCC。而LDE却不同,它会校验这些指令的长度(5个字节)。而INT3断点只有一个或2个(0xCC or 0xCD 0x03)。因此我们可以跳过这种指令而校验其它。

Also, the BPX check is only done once per API at a given location in the binary.Once we have stepped over those checks , we can put a breakpoint on any API function without triggering any error. This weakness wasn't fixed on purpose because this is a common error in Protection Systems.
There is another kind of BPX detection that will be described in the next section 
另外BPX校验只是确定地方API的一次校验。因此我们可以在校验后的区域设置断点。由于这是一个普遍现象,因此这个漏洞无法修复。
下一节将介绍另一种BPX侦测方式。

请大家多指教...

关闭本页
 
首页 | 投资与合作 | 服务条款 | 隐私政策 | 收藏本站 | 设为首页 | 新用户注册 | 免责声明 | 使用帮助
Copyright ©2005-2008 chinaitpower.com All rights reserved. www.chinaitpower.com 版权所有