基本的 `angr` 框架问题 - 我的求解器有问题吗?

逆向工程 快手 愤怒
2021-07-03 03:11:07

我是逆向工程的新手,我最近刚刚了解了angr一个框架,它使用符号执行来获取给定输出的输入。我试图完成的crackme使用angr到目前为止我的代码是:

import angr
import claripy

strlen = 10
base_addr = 0x00000000

flag_chars = [claripy.BVS('flag_%d' % i, 8) for i in range(strlen)]
flag = claripy.Concat(*flag_chars)

proj = angr.Project("./d4rkfl0w-Crackme-4", main_opts={'base_addr': base_addr})
state = proj.factory.full_init_state(args=["./d4rkfl0w-Crackme-4"], add_options=angr.options.unicorn, stdin=flag)
state.memory.store(state.regs.rbp-0x8, flag)

for k in flag_chars:
    state.solver.add(k > 0x20)
    state.solver.add(k < 0x7e)

sm = proj.factory.simulation_manager(state)
sm.run()

for de in sm.deadended:
    print(de.posix.dumps(0), de.posix.dumps(1))

输出是:

> python3 test.py 
WARNING | 2020-08-24 18:55:24,821 | angr.state_plugins.symbolic_memory | The program is accessing memory or registers with an unspecified value. This could indicate unwanted behavior.
WARNING | 2020-08-24 18:55:24,822 | angr.state_plugins.symbolic_memory | angr will cope with this by generating an unconstrained symbolic variable and continuing. You can resolve this by:
WARNING | 2020-08-24 18:55:24,822 | angr.state_plugins.symbolic_memory | 1) setting a value to the initial state
WARNING | 2020-08-24 18:55:24,823 | angr.state_plugins.symbolic_memory | 2) adding the state option ZERO_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to make unknown regions hold null
WARNING | 2020-08-24 18:55:24,823 | angr.state_plugins.symbolic_memory | 3) adding the state option SYMBOL_FILL_UNCONSTRAINED_{MEMORY_REGISTERS}, to suppress these messages.
WARNING | 2020-08-24 18:55:24,823 | angr.state_plugins.symbolic_memory | Filling memory at 0x7fffffffffefff8 with 64 unconstrained bytes referenced from 0x38e0d0 (strlen+0x0 in libc.so.6 (0x8e0d0))
b'((0$0(0(0(' b'\nPlease enter the password: \nSorry that is incorrect.\n'
b'0(0$0(:(((' b'\nPlease enter the password: \nSorry that is incorrect.\n'
b'U($>"(:($(' b'\nPlease enter the password: \nSorry that is incorrect.\n'
b'U(6>$(:(0(' b'\nPlease enter the password: \nSorry that is incorrect.\n'
b'U(6>-(:($(' b'\nPlease enter the password: \nSorry that is incorrect.\n'
b'U(6$-(:(Y(' b'\nPlease enter the password: \nSorry that is incorrect.\n'

这应该是一个相对简单的crackme(我已经用静态分析解决了它,我想以不同的方式获得标志)。我在代码中做错了什么吗?

实际的标志应该是:

U6-:YL."+

1个回答

看看你的解决方案,以及你对预期标志的剧透,看起来你已经完成了一半(字面意思)。

angr 正在解决所有其他字符并匹配标志的一半,就好像标志被视为宽字符串/utf16。我稍微修改了您的脚本,使其具有 20 个字符的字符串,每隔一个字节为空,以换行符结尾,并且 angr 成功找到了该标志。

使用 Angr 有很多其他方法可以做到这一点,但这似乎与您最初使用它的方式很接近。

修改后的 Angr 脚本:

import angr
import claripy

strlen = 20
base_addr = 0x00000000

flag_chars = [claripy.BVS('flag_%d' % i, 8) for i in range(strlen)]
flag = claripy.Concat(*flag_chars)

proj = angr.Project("./d4rkfl0w-Crackme-4", main_opts={'base_addr': base_addr})
state = proj.factory.full_init_state(args=["./d4rkfl0w-Crackme-4"], add_options=angr.options.unicorn, stdin=flag)
# The script functions the same with or without this
# Presumably symbolic stdin handles storing the flag 
# in memory for us
# state.memory.store(state.regs.rbp-0x8, flag)

# It seems like the program is reading in widechars/utf16
# this code will constrain every other byte to be printable, 
# with zero bytes in between
zero_char = False
for k in flag_chars[:-2]:
    if not zero_char:
        state.solver.add(k > 0x20)
        state.solver.add(k < 0x7e)
    else:
        state.solver.add(k == 0x0)
    zero_char = not zero_char

# Flag ends in widechar newline (not strictly needed)
state.solver.add(flag_chars[-2] == 0xa)
state.solver.add(flag_chars[-1] == 0x00)

sm = proj.factory.simulation_manager(state)
sm.run()

for de in sm.deadended:
    print(de.posix.dumps(0), de.posix.dumps(1))
    print("UTF16 flag: %s" % str(de.posix.dumps(0), encoding='utf=16'))
~                                                                        

截断的输出:

b'U\x006\x00-\x00:\x00Y\x00L\x00.\x00!\x00+\x00\n\x00' b'\n请输入密码:\n对不起,不正确。\n' UTF16标志: U6-:YL.!+
b'U\x006\x00-\x00:\x00Y\x00L\x00.\x00"\x00+\x00\n\x00' b"\n请输入密码:\n正确,好吧完成。\n" UTF16 标志:U6-:YL。"+