- 工具共分为几大部分:Bootstrap部分、路径发现部分、Rewriter部分
- Bootstrap部分:分析dispatcher所在的位置、使用的blockid变量
- 特殊问题:
- 我们需要找到每次循环会回到的地方,但是程序千变万化总是出一堆幺蛾子
- 在O0的情况下pre-dispatcher和dispatcher会分开,这导致xref最多的block是predispatcher,整个识别就会失败。在O3情况下predispatcher和dispatcher会被合并到一起,所以反而没事
- (Linux)有时候,程序会根据blockid的范围将一个dispatcher的blockid分成了两组,形成了两个分开的dispatcher
- (Windows)有时候,引用最多的block并不是dispatcher,而是到dispatcher的一个1way block,我们需要处理这种情况
- IDA microcode中的mop_S比较迷幻
- 有两种mop_S的blockid:%var_XXX 、[&(%varXXX)].4
- 在ARM32中,第一种的似乎需要使用vd2ida进行转换,否则偏移会出错,第二种则不需要
- Bootstrap按理来说应该在MMAT_GLBOPT1做,但是发现OPT2,OPT3其实也都可以,所以写个循环让他在每个mmat都试一次成功了再说
- 路径发现部分:实施Unconstrained符号执行、求解路径id和nextid
- 需要注意的点:
- 我们假定了blockid是constrained求解,而nextid是进行完全unconstrained求解的
- 各种特殊问题:
- ARM Thumb上的blockid赋值通过MOV + MOVK组合两步赋值,需要保证itstate可以在小范围内保持constraint,否则当有两个nextid存在时([0x1234abcd, 0xabcd1234])就会有这样的blockid情况:[0xabcdabcd, 0x1234, 0xabcd1234, 0x12341234]
- Thumb上面的ITT NE通过itstate实现,需要特殊处理itstate
- 通过魔改angr的ITE实现,利用claripy的named variable特性,从而保证可以共享部分符号变量
- vex通过检测block开头之前10条指令是否存在可能的IT指令来决定是否优化
- 这导致处理it state优化时过于谨慎,若block开头十分靠近page边缘(比如0x15004),优化直接不会发生;虽然这个block可能并不是从上面直接fall through过来的,但是若存在itt指令同样会禁用优化
- 一旦优化失败,block结尾会多出一个successor,vex会插入一个符号化的itstate检查,成立时会忽略这条指令蹦到下一条指令(然而实际上应该绝对会跳走)
- 通过在lift的时候在前面补上16个0,让vex误以为没有itt指令即可强行开启优化
- vex最多可以lift 99条指令,如果恰好执行到it block中间停下来了,下一个block就会被禁用优化
- 通过lift之前扫描block里面有哪些it,并限制block大小在被截断it指令之前停下来,这样it block就不会被截断了
- 即使有这么多对策,还是会有优化失败的情况(block开头十分靠近page边缘时),我们在检查exit_guard时,检查跳转条件是不是只和itstate相关,若是的话则直接concrete掉
- 因为itt block中禁止带有跳转指令,所以若跳转条件只和itstate相关,使用concrete值是等价的
- ARM32
- 有神奇的指令POPEQ:可以搞出来POPEQ {XX, PC}这样的条件返回指令
- 整数除法运算竟然直接走的是aeabi:aeabi的idivmod会在r0返回结果r1返回余数,然而angr的callless并不能知道r1被改了,然后就丢路径了
- 放弃处理这种情况,没有意义
- ARM64上有神奇的指令CCMP,在条件成立时才会进行cmp,需要angr的ccall支持multivalue(上个月刚加进去)
- 我们hook了cc_op,使用更加激进的求解方式,若求出两个值则手动构造If XXX == value1 then value1 else value2这样的表达式喂给ccall,从而解决问题
- PPC上的blockid比对方式是:if (id ^ 0xabcd0000 == 0x1234),暂时没啥影响
- 我们求解nextid的时候没有做到unconstrained
- 一部分blockid在prolog处赋值,然后在path结尾直接使用,而此时若条件里面带有内存访问的,如果在prolog已经被赋值了,那就不是unconstrained了
- 一部分寄存器会作为顶层scope的变量使用,会在多处赋值,导致不是unconstrained
- 我们unconstrained掉所有寄存器,然后恢复sp、bp、ip这三个
- 一部分样本中,nextid为动态计算,利用算式:(会对基于pattern和unicorn的方案带来极大困难)
每个平台有各种各样的问题:
此外:
( -(XXX != 0) & (blockid1 - blockid2) ) + (blockid2)
- Rewriter部分
- microcode相关
- IDA的优化器有问题,会出现50342的情况(需要设置MBA_PRCDEFS)
- IDA若在mba结尾插入block,则不会触发relocate操作,速度会加快5~10倍左右
- 即使用idapython,复制所有insn的速度也比copy_block快
- IDA的优化规则REMOVE_IF_BRANCH会导致我们的合法branch被nop掉,所以我们直接patch ida将其禁用
- 生成代码相关
- IDA对于除法pattern的优化有问题,所以recompile出来的结果会和原来结果不同
- IDA的unsigned和signed没有严格区分,导致结果会不同