- 简介
- 一、基础知识篇
- 二、工具篇
- 三、分类专题篇
- 四、技巧篇
- 五、高级篇
- 六、题解篇
- 6.1 Pwn
- 6.1.1 pwn HCTF2016 brop
- 6.1.2 pwn NJCTF2017 pingme
- 6.1.3 pwn XDCTF2015 pwn200
- 6.1.4 pwn BackdoorCTF2017 Fun-Signals
- 6.1.5 pwn GreHackCTF2017 beerfighter
- 6.1.6 pwn DefconCTF2015 fuckup
- 6.1.7 pwn 0CTF2015 freenote
- 6.1.8 pwn DCTF2017 Flex
- 6.1.9 pwn RHme3 Exploitation
- 6.1.10 pwn 0CTF2017 BabyHeap2017
- 6.1.11 pwn 9447CTF2015 Search-Engine
- 6.1.12 pwn N1CTF2018 vote
- 6.1.13 pwn 34C3CTF2017 readme_revenge
- 6.1.14 pwn 32C3CTF2015 readme
- 6.1.15 pwn 34C3CTF2017 SimpleGC
- 6.1.16 pwn HITBCTF2017 1000levels
- 6.1.17 pwn SECCONCTF2016 jmper
- 6.1.18 pwn HITBCTF2017 Sentosa
- 6.1.19 pwn HITBCTF2018 gundam
- 6.1.20 pwn 33C3CTF2016 babyfengshui
- 6.1.21 pwn HITCONCTF2016 Secret_Holder
- 6.1.22 pwn HITCONCTF2016 Sleepy_Holder
- 6.1.23 pwn BCTF2016 bcloud
- 6.1.24 pwn HITCONCTF2016 HouseofOrange
- 6.1.25 pwn HCTF2017 babyprintf
- 6.1.26 pwn 34C3CTF2017 300
- 6.1.27 pwn SECCONCTF2016 tinypad
- 6.1.28 pwn ASISCTF2016 b00ks
- 6.1.29 pwn Insomni'hackteaserCTF2017 TheGreatEscapepart-3
- 6.1.30 pwn HITCONCTF2017 Ghostinthe_heap
- 6.1.31 pwn HITBCTF2018 mutepig
- 6.1.32 pwn SECCONCTF2017 vmnofun
- 6.1.33 pwn 34C3CTF2017 LFA
- 6.1.34 pwn N1CTF2018 memsafety
- 6.1.35 pwn 0CTF2018 heapstorm2
- 6.1.36 pwn NJCTF2017 messager
- 6.1.37 pwn sixstarctf2018 babystack
- 6.1.38 pwn HITCONCMT2017 pwn200
- 6.1.39 pwn BCTF2018 houseofAtum
- 6.1.40 pwn LCTF2016 pwn200
- 6.1.41 pwn PlaidCTF2015 PlaidDB
- 6.1.42 pwn hacklu2015 bookstore
- 6.1.43 pwn 0CTF2018 babyheap
- 6.1.44 pwn ASIS2017 start_hard
- 6.1.45 pwn LCTF2016 pwn100
- 6.2 Reverse
- 6.3 Web
- 6.1 Pwn
- 七、实战篇
- 7.1 CVE
- 7.1.1 CVE-2017-11543 tcpdump sliplink_print 栈溢出漏洞
- 7.1.2 CVE-2015-0235 glibc _nsshostnamedigitsdots 堆溢出漏洞
- 7.1.3 CVE-2016-4971 wget 任意文件上传漏洞
- 7.1.4 CVE-2017-13089 wget skipshortbody 栈溢出漏洞
- 7.1.5 CVE–2018-1000001 glibc realpath 缓冲区下溢漏洞
- 7.1.6 CVE-2017-9430 DNSTracer 栈溢出漏洞
- 7.1.7 CVE-2018-6323 GNU binutils elfobjectp 整型溢出漏洞
- 7.1.8 CVE-2010-2883 Adobe CoolType SING 表栈溢出漏洞
- 7.1.9 CVE-2010-3333 Microsoft Word RTF pFragments 栈溢出漏洞
- 7.1 CVE
- 八、学术篇
- 8.1 The Geometry of Innocent Flesh on the Bone: Return-into-libc without Function Calls (on the x86)
- 8.2 Return-Oriented Programming without Returns
- 8.3 Return-Oriented Rootkits: Bypassing Kernel Code Integrity Protection Mechanisms
- 8.4 ROPdefender: A Detection Tool to Defend Against Return-Oriented Programming Attacks
- 8.5 Data-Oriented Programming: On the Expressiveness of Non-Control Data Attacks
- 8.7 What Cannot Be Read, Cannot Be Leveraged? Revisiting Assumptions of JIT-ROP Defenses
- 8.9 Symbolic Execution for Software Testing: Three Decades Later
- 8.10 AEG: Automatic Exploit Generation
- 8.11 Address Space Layout Permutation (ASLP): Towards Fine-Grained Randomization of Commodity Software
- 8.13 New Frontiers of Reverse Engineering
- 8.14 Who Allocated My Memory? Detecting Custom Memory Allocators in C Binaries
- 8.21 Micro-Virtualization Memory Tracing to Detect and Prevent Spraying Attacks
- 8.22 Practical Memory Checking With Dr. Memory
- 8.23 Evaluating the Effectiveness of Current Anti-ROP Defenses
- 8.24 How to Make ASLR Win the Clone Wars: Runtime Re-Randomization
- 8.25 (State of) The Art of War: Offensive Techniques in Binary Analysis
- 8.26 Driller: Augmenting Fuzzing Through Selective Symbolic Execution
- 8.27 Firmalice - Automatic Detection of Authentication Bypass Vulnerabilities in Binary Firmware
- 8.28 Cross-Architecture Bug Search in Binary Executables
- 8.29 Dynamic Hooks: Hiding Control Flow Changes within Non-Control Data
- 8.30 Preventing brute force attacks against stack canary protection on networking servers
- 8.33 Under-Constrained Symbolic Execution: Correctness Checking for Real Code
- 8.34 Enhancing Symbolic Execution with Veritesting
- 8.38 TaintEraser: Protecting Sensitive Data Leaks Using Application-Level Taint Tracking
- 8.39 DART: Directed Automated Random Testing
- 8.40 EXE: Automatically Generating Inputs of Death
- 8.41 IntPatch: Automatically Fix Integer-Overflow-to-Buffer-Overflow Vulnerability at Compile-Time
- 8.42 Dynamic Taint Analysis for Automatic Detection, Analysis, and Signature Generation of Exploits on Commodity Software
- 8.43 DTA++: Dynamic Taint Analysis with Targeted Control-Flow Propagation
- 8.44 Superset Disassembly: Statically Rewriting x86 Binaries Without Heuristics
- 8.45 Ramblr: Making Reassembly Great Again
- 8.46 FreeGuard: A Faster Secure Heap Allocator
- 8.48 Reassembleable Disassembling
- 九、附录
3.2.6 指令混淆
为什么需要指令混淆
软件的安全性严重依赖于代码复杂化后被分析者理解的难度,通过指令混淆,可以将原始的代码指令转换为等价但极其复杂的指令,从而尽可能地提高分析和破解的成本。
常见的混淆方法
代码变形
代码变形是指将单条或多条指令转变为等价的单条或多条其他指令。其中对单条指令的变形叫做局部变形,对多条指令结合起来考虑的变成叫做全局变形。
例如下面这样的一条赋值指令:
mov eax, 12345678h
可以使用下面的组合指令来替代:
push 12345678h
pop eax
更进一步:
pushfd
mov eax, 1234
shl eax, 10
mov ax, 5678
popfd
pushfd
和 popfd
是为了保护 EFLAGS 寄存器不受变形后指令的影响。
继续替换:
pushfd
push 1234
pop eax
shl eax, 10
mov ax 5678
这样的结果就是简单的指令也可能会变成上百上千条指令,大大提高了理解的难度。
再看下面的例子:
jmp {label}
可以变成:
push {label}
ret
而且 IDA 不能识别出这种 label 标签的调用结构。
指令:
call {label}
可以替换成:
push {call指令后面的那个label}
push {label}
ret
指令:
push {op}
可以替换成:
sub esp, 4
mov [esp], {op}
下面我们来看看全局变形。对于下面的代码:
mov eax, ebx
mov ecx, eax
因为两条代码具有关联性,在变形时需要综合考虑,例如下面这样:
mov cx, bx
mov ax, cx
mov ch, bh
mov ah, bh
这种具有关联性的特定使得通过变形后的代码推导变形前的代码更加困难。
花指令
花指令就是在原始指令中插入一些虽然可以被执行但是没有任何作用的指令,它的出现只是为了扰乱分析,不仅是对分析者来说,还是对反汇编器、调试器来说。
来看个例子,原始指令如下:
add eax, ebx
mul ecx
加入花指令之后:
xor esi, 011223344h
add esi, eax
add eax, ebx
mov edx, eax
shl edx, 4
mul ecx
xor esi, ecx
其中使用了源程序不会使用到的 esi 和 edx 寄存器。这就是一种纯粹的垃圾指令。
有的花指令用于干扰反汇编器,例如下面这样:
01003689 50 push eax
0100368A 53 push ebx
加入花指令后:
01003689 50 push eax
0100368A EB 01 jmp short 0100368D
0100368C FF53 6A call dword ptr [ebx+6A]
乍一看似乎很奇怪,其实是加入因为加入了机器码 EB 01 FF
,使得线性分析的反汇编器产生了误判。而在执行时,第二条指令会跳转到正确的位置,流程如下:
01003689 50 push eax
0100368A EB 01 jmp short 0100368D
0100368C 90 nop
0100368D 53 push ebx
扰乱指令序列
指令一般都是按照一定序列执行的,例如下面这样:
01003689 push eax
0100368A push ebx
0100368B xor eax, eax
0100368D cmp eax, 0
01003690 jne short 01003695
01003692 inc eax
01003693 jmp short 0100368D
01003695 pop ebx
01003696 pop eax
指令序列看起来很清晰,所以扰乱指令序列就是要打乱这种指令的排列方式,以干扰分析者:
01003689 push eax
0100368A jmp short 01003694
0100368C xor eax, eax
0100368E jmp short 01003697
01003690 jne short 0100369F
01003692 jmp short 0100369C
01003694 push ebx
01003695 jmp short 0100368C
01003697 cmp eax, 0
0100369A jmp short 01003690
0100369C inc eax
0100369D jmp short 01003697
0100369F pop ebx
010036A0 pop eax
虽然看起来很乱,但真实的执行顺序没有改变。
多分支
多分支是指利用不同的条件跳转指令将程序的执行流程复杂化。与扰乱指令序列不同的时,多分支改变了程序的执行流。举个例子:
01003689 push eax
0100368A push ebx
0100368B push ecx
0100368C push edx
变形如下:
01003689 push eax
0100368A je short 0100368F
0100368C push ebx
0100368D jmp short 01003690
0100368F push ebx
01003690 push ecx
01003691 push edx
代码里加入了一个条件分支,但它究竟会不会触发我们并不关心。于是程序具有了不确定性,需要在执行时才能确定。但可以肯定的时,这段代码的执行结果和原代码相同。
再改进一下,用不同的代码替换分支处的代码:
01003689 push eax
0100368A je short 0100368F
0100368C push ebx
0100368D jmp short 01003693
0100368F push eax
01003690 mov dword ptr [esp], ebx
01003693 push ecx
01003694 push edx
不透明谓词
不透明谓词是指一个表达式的值在执行到某处时,对程序员而言是已知的,但编译器或静态分析器无法推断出这个值,只能在运行时确定。上面的多分支其实也是利用了不透明谓词。
下面的代码中:
mov esi, 1
... ; some code not touching esi
dec esi
...
cmp esi, 0
jz real_code
; fake luggage
real_code:
假设我们知道这里 esi 的值肯定是 0,那么就可以在 fake luggage 处插入任意长度和复杂度的指令,以达到混淆的目的。
其它的例子还有(同样假设esi为0):
add eax, ebx
mul ecx
add eax, esi
间接指针
dummy_data1 db 100h dup (0)
message1 db 'hello world', 0
dummy_data2 db 200h dup (0)
message2 db 'another message', 0
func proc
...
mov eax, offset dummy_data1
add eax, 100h
push eax
call dump_string
...
mov eax, offset dummy_data2
add eax, 200h
push eax
call dump_string
...
func endp
这里通过 dummy_data 来间接地引用 message,但 IDA 就不能正确地分析到对 message 的引用。
代码虚拟化
基于虚拟机的代码保护也可以算是代码混淆技术的一种,是目前各种混淆中保护效果最好的。简单地说,该技术就是通过许多模拟代码来模拟被保护的代码的执行,然后计算出与被保护代码执行时相同的结果。
+------------+
| 头部指令序列 | -------> | 代码虚拟机入口 |
|------------| |
| | | 保存代码现场 |
| | |
| 中间指令序列 | | 模拟执行中间指令序列 |
| | |
| | | 设置新的代码现场 |
|------------| |
| 尾部指令序列 | <------- | 代码虚拟机出口 |
+------------+
当原始指令执行到指令序列的开始处,就转入代码虚拟机的入口。此时需要保存当前线程的上下文信息,然后进入模拟执行阶段,该阶段是代码虚拟机的核心。有两种方案来保证虚拟机代码与原始代码的栈空间使用互不冲突,一种是在堆上开辟开辟新的空间,另一种是继续使用原始代码所使用的栈空间,这两种方案互有优劣,在实际中第二种使用较多。
对于怎样模拟原始代码,同样有两种方案。一种是将原本的指令序列转变为一种具有直接或者间接对应关系的,只有虚拟机才能理解的代码数据。例如用 0
来表示 push
, 1 表示 mov
等。这种直接或间接等价的数据称为 opcode。另一种方案是将原始代码的意义直接转换成新的代码,类似于代码变形,这种方案基于指令语义,所以设计难度非常大。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论