angr的深入学习
以前就会用angr 最近在看的时候 发现很多框架都利用了 angr 做了很多事情
于是根据 两个项目来深入学习一下angr
一个是 反混淆ollvm的代码
项目地址
https://github.com/pcy190/deflat
一个是 auto pwn 的框架
https://github.com/ChrisTheCoolHut/Zeratool
这两个都用到了angr 。。 所以重点学习一下
首先先说一下符号执行 其实根据这个名字 符号执行 大概都能懂是干啥的
就是根据符号 约束来求解 具体体现点就是 利用了ir
ir 是一个中间语言 如果了解过编译原理 应该能懂 ir
ir 里面有一个 ITE If-Then-Else 如果给定的IR表达式值为0,返回一个IR表达式,否则返回另一个
用伪代码可以这样理解
if(x+5=10){
then:
y=10
else:
y=20
}
就可以表示为 y=ITE(x+5==10,10,20)
其中 x 就可以约束 然后路径规划 求解,,, 详情可以看看angr源码 这几天看他们的官方文档 看的有点头疼 = = =
然后我们可以自己写代码来看 加深一下印象
这里用到的程序 是上面ollvm给的例子
# -*- coding: utf-8 -*
from angr import*
from pyvex import*
import archinfo
'''
.text:00000000004008CC B8 39 B0 59 AF mov eax, 0AF59B039h
.text:00000000004008D1 B9 FD 15 87 19 mov ecx, 198715FDh
.text:00000000004008D6 48 8B 55 F0 mov rdx, [rbp+var_10]
.text:00000000004008DA 0F BE 32 movsx esi, byte ptr [rdx]
.text:00000000004008DD 81 FE 62 00 00 00 cmp esi, 62h
.text:00000000004008E3 0F 44 C1 cmovz eax, ecx
.text:00000000004008E6 89 45 E4 mov [rbp+var_1C], eax
.text:00000000004008E9 E9 AD 00 00 00 jmp loc_40099B
; -----------------------------
[0x0F,0x84,0xB6,0x02,0x00,0x00]
'''
p=Project('check_passwd_flat')
irsb=p.factory.block(0x4008CC).vex
irsb.pp()
'''
00 | ------ IMark(0x4008cc, 5, 0) ------
01 | ------ IMark(0x4008d1, 5, 0) ------
02 | PUT(rcx) = 0x00000000198715fd
03 | PUT(rip) = 0x00000000004008d6
04 | ------ IMark(0x4008d6, 4, 0) ------
05 | t11 = GET:I64(rbp)
06 | t10 = Add64(t11,0xfffffffffffffff0)
07 | t12 = LDle:I64(t10)
08 | PUT(rdx) = t12
09 | PUT(rip) = 0x00000000004008da
10 | ------ IMark(0x4008da, 3, 0) ------
11 | t15 = LDle:I8(t12)
12 | t37 = 8Sto32(t15)
13 | t14 = t37
14 | t38 = 32Uto64(t14)
15 | t13 = t38
16 | PUT(rsi) = t13
17 | ------ IMark(0x4008dd, 6, 0) ------
18 | t39 = 64to32(t13)
19 | t16 = t39
20 | PUT(cc_op) = 0x0000000000000007
21 | t40 = 32Uto64(t16)
22 | t18 = t40
23 | PUT(cc_dep1) = t18
24 | PUT(cc_dep2) = 0x0000000000000062
25 | ------ IMark(0x4008e3, 3, 0) ------
26 | t43 = 64to32(0x0000000000000062)
27 | t44 = 64to32(t18)
28 | t42 = CmpEQ32(t44,t43)
29 | t41 = 1Uto64(t42)
30 | t31 = t41
31 | t45 = 64to1(t31)
32 | t26 = t45
33 | t46 = ITE(t26,0x198715fd,0xaf59b039)
34 | t25 = t46
35 | t47 = 32Uto64(t25)
36 | t24 = t47
37 | PUT(rax) = t24
38 | PUT(rip) = 0x00000000004008e6
39 | ------ IMark(0x4008e6, 3, 0) ------
40 | t32 = Add64(t11,0xffffffffffffffe4)
41 | t48 = 64to32(t24)
42 | t34 = t48
43 | STle(t32) = t34
44 | ------ IMark(0x4008e9, 5, 0) ------
'''
一种可以很明显看到 汇编在 ir 里面的解释 ITE 表现的情况 可以在汇编里面看的出来
伪c里面的对照
大概了解好了ite 就可以讲ollvm的项目代码了
首先 理清一下ollvm混淆 借用一下 https://security.tencent.com/index.php/blog/msg/112 上面的一个图
观察可以看得出 真实块的后继块就是 预处理器 只需要 找到预处理器 就ok
ret 块是没有后继的块 序言是没有前继
找的方法是根据 cfg图
cfg图 可以根据 边 点 的关系来判断出来预处理 和序言
先找到了没有 前继块的序言块 和没有后继块的ret 块
然后根据 主分发器的前继 来寻找 预处理器 找到预处理器之后 直接找真实块就ok
接下来就是难点了,, 怎么确认真实块的逻辑关系。。
接着看那个函数
statement 一个IR statement正在被解释执行(translate) 的时候断下来
这里就为啥我开篇说了ite 的概念 要不然 这里就理解不了 0xbird大佬在他的文章上也解释了这一做法,
修改临时变量 再执行就可以得到分支的地址 这一做法可以得到两个分支的逻辑 如果遇到call 就直接返回,
看看执行的块 是否在真实块集合里面 如果在的话 就把真实块地址返回
然后到 flow 这个列表里面 然后根据这
这样就找到了真实块之间的调用关系
最后就是根据调用块来直接patch
如果是有分支的话
针对产生分支的真实块把CMOV指令改成相应的条件跳转指令跳向符合条件的分支,例如CMOVZ 改成JZ ,再在这条之后添加JMP 指令跳向另一分支
然后这个脚本就分析完毕了
这里说一下无名侠大佬针对arm的ollvm
发现arm 这个好多工具 支持的都不是太好 angr 也是如此
所以无名侠大佬用的unicorn 模拟执行
其中 有一点就是 他们的虚假块和真实块之间的判断就只能利用特征码来判断
所以就麻烦了许多,,
zeratool
发现也是17、18年的项目。
参考链接
https://security.tencent.com/index.php/blog/msg/112
推荐阅读
-
学习Linq之前必须知道的几种语法糖
-
Oracle学习:表的创建与主键自增长
-
小弟想学习php,哪位前辈可以给小弟介绍一下学习php很好的书或者学习视频,该怎么处理
-
sql server 2005中新增加的try catch学习
-
在上海乐字节学习的第二十天(持续更新中)
-
学习python的第十七天(二分法,有名函数,匿名函数,内置函数)
-
学习python的第十八天(模块导入及使用,关键字,模块搜索路径,python文件的两种用途)
-
用Python爬取了拉勾网的招聘信息+详细教程+趣味学习+快速爬虫入门+学习交流+大神+爬虫入门
-
php学习笔记 面向对象中[接口]与[多态性]的应用
-
Android进阶学习:移动端开发重点学习的十点,不能再得过且过的写业务代码了