funnyre-花指令与符号执行angr

从funnyre的题中学习花指令和符号执行angr

结合恶意代码实战分析书籍和funnyre题目:

0x1 花指令

用来防止反编译。

0x11 分为可执行和不可执行

可执行

能够正常运行的但又不改变原始程序逻辑性。无用指令。特点:①可以正常运行;②不改变任何寄存器的值;③反汇编器可以正确反汇编该指令。

病毒在传播时通过变形引擎随机产生一组该类别花指令并插入到病毒正常代码中,可以改变病毒的特征码,从而起到变形的作用。

不可执行

插入到原始代码中但又不改变原始程序逻辑性。无用字节。特点:①不可以正常运行;②不改变任何寄存器的值;③反汇编器可能会错误反汇编这些字节。

是某个合法指令的一部分,但是程序运行时,位于实际不可执行的代码路径。

0x12 常见花指令
1、指向同一目的地址的两个连续条件跳转指令+虚假的call指令 jz+jnz+call

jz+jnz在效果上相当于jmp,则总是会跳转

ida这类面向代码流的反汇编工具:当遇到jnz的时候,不管false分支会不会执行,都会反汇编它

​ 当出现call指令时,会先反汇编紧跟call指令之后的字节,然后再反汇编call指令的调用目标

机器执行中:

hello字符串不会被当作指令执行,call会直接去pop

image-20210126142113968

但是反汇编中:

hello这串字符串被当作指令了

image-20210126142153025

可以使用c或者d转换数据和代码

general-option-numberofbytes可以显示出字节源码

2、固定的跳转指令xor+jz

xor eax,eax会将eax寄存器置0,同时也会置位标志寄存器的zero标志,jz是如果zero标志被置位,就会跳转,那么就肯定会跳,但是反汇编器总会反汇编false分支,所以就会反汇编后续紧跟的。

如下图:E9和E8的作用一样,E9是jmp的字节码,E8是call的,可以用来欺骗反汇编器

image-20210126144811365

转换后:

image-20210126144927590

image-20210126145012481
3、无效指令patchbyte

一个字节同时被两个指令使用,反编译器就会产生困惑

可以使用idc调用patchbyte函数,用nop指令序列替换

在这个图里,先执行mov,然后xor后寄存器置0,jz必然会执行,jz跳转到mov中间,后半个字符串是jmp指令,跳转到真正的位置

image-20210126145914975

但是反汇编中就会:

image-20210126150038992

使用idc进行简化,因为实际效果就是eax置0,所以可以将这些db都置0,改为nop

image-20210126150251070

0x2 angr

angr是一个利用python开发的二进制程序分析框架,安装时会修改libz3和libVEX,可能会影响其他程序的正常使用,所以学习使用python虚拟环境。

angr模拟执行的过程就像人阅读代码,一边阅读的过程一边做预测、抽象执行结果,预估“执行结果”。在angr模拟执行前,必须先将变量(内存或者寄存器)符号化(声明哪些变量是符号),这些符号化的输入也正是程序的输入抽象。

0x21 安装虚拟环境virtualenvwrapper:

pip install之后初始化source报错

image-20210126160200586

需要在~/.bashrc文件中设置python解释器的路径

插入:

1
2
export VIRTUALENVWRAPPER_PYTHON=/usr/bin/python3
export VIRTUALENVWRAPPER_VIRTUALENV=/usr/local/bin/virtualenv

然后source ~/.bashrc生效

image-20210126160341966

0x22 安装angr:

mkvirtualenv --python=$(which python3) angr && pip install angr

image-20210126161130297

环境就装好了

0x23 angr使用:

参考:https://xz.aliyun.com/t/7117/#toc-17

先创建一个proj,载入需要模拟执行的程序

>>> proj = angr.Project('/bin/true')

factory,即 AngrObjectFactory,提供重要分析对象的接口,如 blocks / state / SimulationManager等

state,project 只是给出程序最初镜像的信息,state 可以给出模拟程序执行到某条指令时的进程的具体状态。可以设置 state 对象初始化寄存器/内存/栈帧等信息

通过solver可以访问求解引擎claripy,用于求解约束,也就可以求解出相应输出的输入

image-20210126163407337

0x3 funnyre实践

先修改花指令

40061A处的call有问题,使用c和d来转换

image-20210126144235928

用d转换成数据

image-20210126143748946

此时我们再用c将40061B转为代码,整个代码就变成了这样:

image-20210126143848888

根据上下的几个函数,应该大家格式都是一样,所以编写python脚本,使用idc的patchbyte把jz和jnz以及0e8h都给nop掉

image-20210126153307012

往下拉,发现这样的地方还有好几处,全都改掉之后,发现还是不能反编译

在main的地方感觉和平时见的不太一样,右键createp,创建一个函数,就可以反编译了:

接下来来看主函数:输入的flag长度要为38并且定义了首尾是flag{},然后对所有字符进行了一大堆异或操作

image-20210126153743674

然后在最后,和unk_4025C0数组做对比

image-20210126154110881

image-20210126154152338

这么多操作总不能重新写一个脚本跑

获取程序的开始地址:我们的程序开始是以v6=0开始,xor eax,eax就是将eax置0,64位rax是以eax作为其最低的32位

image-20210126175926148

获取存储要求解的字符串的开始地址:

在main函数里我们需要得知rbx的值,在main函数的前一个init函数中有定义

image-20210126171814154

获取这是我们最终想要的结果的地址:

image-20210126171024631

动态调试的时候在这里还看到了定义:

image-20210126182027607

使用angr,编写脚本如下:

image-20210126175145859