[原创]利用Triton 污点分析识别垃圾指令
2023-6-19 23:46:0 Author: bbs.pediy.com(查看原文) 阅读量:18 收藏

[原创]利用Triton 污点分析识别垃圾指令

2023-6-19 23:46 4386

此样本主要使用间接跳转
图片描述
可以利用unicorn模拟执行来确定x13的值,然后修改成直接跳转或者条件跳转,
本文的重点不在这里,不在多叙。本文的重点是如何识别这些垃圾指令,
因为光恢复它的跳转,ida还是不能反编译,它指令膨胀的非常严重,将max_func_size设置成4096也不行,所以对于BR x13这种指令,需要识别x13是由哪些指令生成的,将这些指令全部nop掉,才能减少方法的size。

如上图,代码中的所有跳转全部为BR指令,包括条件跳转和直接跳转,其中还包括不透明谓词

一种是自下而上进行活跃变量分析,将BR x8 patch成b 0x1000后,将x8认为是不活跃变量,然后就可以进行活跃变量分析,然后根据ud链,找到定义x8的地方,将这个定义也删除掉。这个定义语句也会使用到变量,此时它使用到的变量也属于不活跃变量,所以可以继续向上分析,这种方式只是我理论想出来的,并没有实践

第二种是我用到的方法,污点分析,将mov x8,0x234234,这种指令的x8定义为污染源,然后向下分析,将污染过得指令全部nop掉。我觉得这种方式处理起来比较简单,对于br,bl这种跳转语句不执行,还有return块也不执行,这样就可以把整个方法当做一个巨大的代码块,由上而下的执行,将特征指令会赋值的寄存器设置成污染源,这样也可以处理这种特殊情况,在方法序言处将一个常数写入栈,在后面的代码块中从栈里面读取这个值,再使用计算,这个可以避免自己再做上下文的分析,污点分析能直接定位到。

github地址https://github.com/JonathanSalwan/Triton.git
这个工具很强大,有污点分析和符号执行两个功能,我这次只使用到了它的污点分析功能
安装 pip install tritondse

使用到的主要api

1

2

3

4

5

6

7

8

9

10

Triton.setArchitecture(ARCH.AARCH64)设置架构

Triton.setConcreteMemoryAreaValue(0, bin1) 写入具体内存

Triton.taintRegister()  设置寄存器为污染源

Triton.taintMemory()设置内存地址为污染源

Triton.getConcreteRegisterValue()获取寄存器的值

Triton.untaintRegister()对寄存器进行去污染处理

Triton.untaintMemory()对内存进行去污染处理

Instruction.setOpcode()设置字节码

Instruction.setAddress()设置指令地址

Triton.processing() 执行一条语句

还是利用上面的那段代码,执行完1E7D00后,将w8寄存器设置为污染源

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

from triton import *

import idc

import ida_bytes

from capstone import *

from capstone.arm64 import *

cs = Cs(CS_ARCH_ARM64, CS_MODE_LITTLE_ENDIAN)

cs.detail = True

def get_insn2(opcode0, addr):

    insns = cs.disasm(opcode0, addr)

    for i in insns:

        return i

def taint_analysis2(start, end):

    Triton = TritonContext()

    with open('C:\\Users\\lj\\Desktop\\junks\\test1\\CoreBook2', 'rb') as f:

        bin1 = f.read()

    Triton.setArchitecture(ARCH.AARCH64)

    Triton.setConcreteMemoryAreaValue(0, bin1)

    sp = 0x100000000

    Triton.setConcreteRegisterValue(Triton.registers.x29, sp)

    Triton.setConcreteRegisterValue(Triton.registers.sp, sp)

    pc = start

    nop_addrs = []

    while pc:

        inst = Instruction()

        opcode0 = ida_bytes.get_bytes(pc, 4)

        cs_insn: CsInsn = get_insn2(opcode0, pc)

        inst.setOpcode(opcode0)

        inst.setAddress(pc)

        Triton.processing(inst)

        print(str(inst))

        if pc == 0x1E7D00:

            Triton.taintRegister(Triton.registers.w8)

        if inst.isTainted():

            idc.set_color(pc, idc.CIC_ITEM, 0xffe699)

            nop_addrs.append(pc)

        if pc >= end:

            break

        pc = pc + 4

if __name__ == '__main__':

    taint_analysis2(0x1E7D00, 0x1E7D98)

其中的大部分代码都是模板代码,主要有用的是这两句代码,将w8设置为污染源

1

2

if pc == 0x1E7D00:

    Triton.taintRegister(Triton.registers.w8)

看下效果
图片描述
在1e7d68处使用到了w8,并且将cpsr污染了,所以下一句cset指令也是污染的,
继续向下分析,可以看到间接引用w8寄存器的指令都识别出来了。
注意1e7d94处,虽然x13寄存器是被污染的,但是这条指令并没有被污染。
Triton将这种问题留给我们自己来处理,就是如果寄存器是被污染的,那么需要我们根据实际情况,来决定是否将寄存器指向的内存污染掉,上代码

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

from triton import *

import idc

import ida_bytes

from capstone import *

from capstone.arm64 import *

cs = Cs(CS_ARCH_ARM64, CS_MODE_LITTLE_ENDIAN)

cs.detail = True

def get_insn2(opcode0, addr):

    insns = cs.disasm(opcode0, addr)

    for i in insns:

        return i

def taint_analysis2(start, end):

    Triton = TritonContext()

    with open('C:\\Users\\lj\\Desktop\\junks\\test1\\CoreBook2', 'rb') as f:

        bin1 = f.read()

    Triton.setArchitecture(ARCH.AARCH64)

    Triton.setConcreteMemoryAreaValue(0, bin1)

    sp = 0x100000000

    Triton.setConcreteRegisterValue(Triton.registers.x29, sp)

    Triton.setConcreteRegisterValue(Triton.registers.sp, sp)

    pc = start

    nop_addrs = []

    while pc:

        inst = Instruction()

        opcode0 = ida_bytes.get_bytes(pc, 4)

        inst.setOpcode(opcode0)

        inst.setAddress(pc)

        Triton.processing(inst)

        print(str(inst))

        if pc == 0x1E7D00:

            Triton.taintRegister(Triton.registers.w8)

        if pc == 0x1e7d90:

            Triton.taintMemory(Triton.getConcreteRegisterValue(Triton.registers.x13))

        if inst.isTainted():

            idc.set_color(pc, idc.CIC_ITEM, 0xffe699)

            nop_addrs.append(pc)

        if pc >= end:

            break

        pc = pc + 4

if __name__ == '__main__':

    taint_analysis2(0x1E7D00, 0x1E7D9c)

这个脚本多了这几句代码

1

2

if pc == 0x1e7d90:

    Triton.taintMemory(Triton.getConcreteRegisterValue(Triton.registers.x13))

作用也很简单,执行完这句代码后,将x13指向的内存也污染掉
看下效果
图片描述
可以看到又多识别出来一些垃圾指令
那我们继续分别将代码块开始的几个寄存器,w8,w9,w10,w11,w12都给污染掉,上代码

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

from triton import *

import idc

import ida_bytes

from capstone import *

from capstone.arm64 import *

cs = Cs(CS_ARCH_ARM64, CS_MODE_LITTLE_ENDIAN)

cs.detail = True

def get_insn2(opcode0, addr):

    insns = cs.disasm(opcode0, addr)

    for i in insns:

        return i

def taint_analysis2(start, end):

    Triton = TritonContext()

    with open('C:\\Users\\lj\\Desktop\\junks\\test1\\CoreBook2', 'rb') as f:

        bin1 = f.read()

    Triton.setArchitecture(ARCH.AARCH64)

    Triton.setConcreteMemoryAreaValue(0, bin1)

    sp = 0x100000000

    Triton.setConcreteRegisterValue(Triton.registers.x29, sp)

    Triton.setConcreteRegisterValue(Triton.registers.sp, sp)

    pc = start

    nop_addrs = []

    while pc:

        inst = Instruction()

        opcode0 = ida_bytes.get_bytes(pc, 4)

        cs_insn: CsInsn = get_insn2(opcode0, pc)

        inst.setOpcode(opcode0)

        inst.setAddress(pc)

        Triton.processing(inst)

        print(str(inst))

        if pc == 0x1E7D00:

            Triton.taintRegister(Triton.registers.w8)

        if pc == 0x1E7D08:

            Triton.taintRegister(Triton.registers.w9)

        if pc == 0x1E7D10:

            Triton.taintRegister(Triton.registers.w10)

        if pc == 0x1E7D18:

            Triton.taintRegister(Triton.registers.w11)

        if pc == 0x1E7D20:

            Triton.taintRegister(Triton.registers.w12)

        if pc == 0x1e7d90:

            Triton.taintMemory(Triton.getConcreteRegisterValue(Triton.registers.x13))

        if inst.isTainted():

            idc.set_color(pc, idc.CIC_ITEM, 0xffe699)

            nop_addrs.append(pc)

        if pc >= end:

            break

        pc = pc + 4

if __name__ == '__main__':

    taint_analysis2(0x1E7D00, 0x1E7D98)

看下效果
图片描述
可以看到又多识别了一些垃圾指令。
还有一种特殊的栈变量,比如1E7D34处的指令,这种如何识别呢,其实在最开始已经谈到过这种情况的处理方式,这种栈变量的赋值方式一般是在方法序言处
图片描述
在这里给x4赋值
图片描述
在这里写到栈上
所以我们可以在0x1E4DB8处,将x4寄存器也给污染掉,上代码

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

from triton import *

import idc

import ida_bytes

from capstone import *

from capstone.arm64 import *

cs = Cs(CS_ARCH_ARM64, CS_MODE_LITTLE_ENDIAN)

cs.detail = True

def get_insn2(opcode0, addr):

    insns = cs.disasm(opcode0, addr)

    for i in insns:

        return i

def taint_analysis2(start, end):

    Triton = TritonContext()

    with open('C:\\Users\\lj\\Desktop\\junks\\test1\\CoreBook2', 'rb') as f:

        bin1 = f.read()

    Triton.setArchitecture(ARCH.AARCH64)

    Triton.setConcreteMemoryAreaValue(0, bin1)

    sp = 0x100000000

    Triton.setConcreteRegisterValue(Triton.registers.x29, sp)

    Triton.setConcreteRegisterValue(Triton.registers.sp, sp)

    pc = start

    nop_addrs = []

    while pc:

        inst = Instruction()

        opcode0 = ida_bytes.get_bytes(pc, 4)

        cs_insn: CsInsn = get_insn2(opcode0, pc)

        if cs_insn is None or cs_insn.mnemonic in ['br', 'bl', 'b']:

            pc = pc + 4

            continue

        inst.setOpcode(opcode0)

        inst.setAddress(pc)

        Triton.processing(inst)

        print(str(inst))

        if pc == 0x1E7D00:

            Triton.taintRegister(Triton.registers.w8)

        if pc == 0x1E7D08:

            Triton.taintRegister(Triton.registers.w9)

        if pc == 0x1E7D10:

            Triton.taintRegister(Triton.registers.w10)

        if pc == 0x1E7D18:

            Triton.taintRegister(Triton.registers.w11)

        if pc == 0x1E7D20:

            Triton.taintRegister(Triton.registers.w12)

        if pc == 0x1E4DB8:

            Triton.taintRegister(Triton.registers.x4)

            Triton.taintMemory(Triton.getConcreteRegisterValue(Triton.registers.x4))

        if pc == 0x1e7d90:

            Triton.taintMemory(Triton.getConcreteRegisterValue(Triton.registers.x8))

        if inst.isTainted():

            idc.set_color(pc, idc.CIC_ITEM, 0xffe699)

            nop_addrs.append(pc)

        if pc >= end:

            break

        pc = pc + 4

if __name__ == '__main__':

    taint_analysis2(0x1E4D28, 0x1E7D98)

这个脚本相比之前多了这几句代码,目的是跳过所有的跳转指令,将整个方法当成一个方法块来处理,这样会简单很多,此处我没有处理ret代码块,实际使用中,还需要跳过ret代码块,不然运行过程中pc会跑飞

1

if cs_insn is None or cs_insn.mnemonic in ['br', 'bl', 'b']:

在这里将x4寄存器和它指向的内存污染掉

1

2

3

if pc == 0x1E4DB8:

    Triton.taintRegister(Triton.registers.x4)

    Triton.taintMemory(Triton.getConcreteRegisterValue(Triton.registers.x4))

看下效果
图片描述
可以看到将对栈变量的处理也识别到了。
其实讲到这里已经差不多了,剩下的就是全局扫描识别mov adrp 这种指令,当然需要过滤掉其中的正常指令,然后适配上面的代码,就可以将大部分的垃圾指令识别到,然后nop掉。对于强迫症患者来说,也是一个福音。
代码就不上传了,文章中的代码运行应该都没问题

冰蝎,蚁剑Java内存马查杀防御技术

最后于 2023-6-20 22:43 被普通人张三编辑 ,原因:

返回


文章来源: https://bbs.pediy.com/thread-277694.htm
如有侵权请联系:admin#unsafe.sh