Angr实例分析——layer7_onlyone

本博客由闲散白帽子胖胖鹏鹏胖胖鹏潜力所写,仅仅作为个人技术交流分享,不得用做商业用途。转载请注明出处,未经许可禁止将本博客内所有内容转载、商用。      

      这周我们依旧继续进行Angr的实例解析,我在使用Angr的过程中,发现了需要hook函数的地方,就看了下Angr-doc中提供的几种用法。这里layer7_onlyone为例,简单介绍下使用hook的正确姿势。本文涉及的CTF题目及相关的求解代码在这

      首先是打开软件看下,要求我们输入字符串,我们随便输入一下,shell关闭,生成了一个密文文档,看来是要我们解密出来这个文件了。把exe拖到IDA里面,找到main函数。


      乏善可陈。。。。v7是我们输入的字符串,sub_4010E0是strlen,看来主要的解密就在sub_401000里面了。

import angr
import logging

def decrypt(state):
    buf = state.regs.edx # 选择第二个入参
    # 跳过了真正的解密流程,并且直接返回正确的值(太懒了吧)
    state.memory.store(buf, state.solver.BVV(int('layerseven\x00'.encode('hex'), 16), 88))

def main():
    # 加载文件
    p = angr.Project("onlyone.exe", use_sim_procedures=True)
    # hook malloc函数,这里使用标准库函数
    p.hook(0x2398, angr.SIM_PROCEDURES['libc']['malloc'])
    # hook decryptHook函数,因为我们不支持指数pow/开方sqrt/向下取证floor 
    p.hook(0x401038, decrypt, length=5)

    # 这是 'encrypted' 文件的内容
    # 我们输入的字符串加密之后应该和文件中的内容相同
    encrypted = "253e315126363a2e551c".decode('hex')

    # 因为前面的内容都没有什么意义,所以我们从目标函数开始执行
    initial_state = p.factory.blank_state(addr=0x401000)

    # 这里是我们的输入字符串的地址(这里字符串的地址我有些疑问,不知道为什么是这样,也许是随机选的)
    str_ptr = 0x800000

    # 加载输入字符串,并且确保字符串不为空
    content = initial_state.memory.load(str_ptr, len(encrypted))
    for i in xrange(0, len(content), 8):
        initial_state.add_constraints(content[i + 7 : i] != 0)

    # 确保输入字符串以\0结尾
    zero = initial_state.memory.load(str_ptr + len(encrypted), 1)
    initial_state.add_constraints(zero == 0)

    # 把字符串地址push放到栈上
    initial_state.stack_push(initial_state.solver.BVV(str_ptr, 32))
    # push返回地址
    initial_state.stack_push(initial_state.solver.BVV(0, 32))

    # 创建初始路径

    # 调用explorer执行函数
    # 注意Veritesting很重要,因为我们想要避免不需要的分支,缓解路径爆炸
    ex = angr.surveyors.Explorer(p, start=initial_state, find=(0x4010c9, ), enable_veritesting=True)
    print "Executing..."
    angr.surveyors.explorer.l.setLevel(logging.DEBUG)
    angr.surveyors.surveyor.l.setLevel(logging.DEBUG)
    r = ex.run()

    if r.found:
        final_state = r.found[0]
    else:
        final_state = r.errored[0].previous_run.initial_state

    # 读取最后加密的字符串,并且约束使得string等于加密后的字符串
    buf_ptr = final_state.memory.load(final_state.regs.ebp - 0x18, 4, endness=p.arch.memory_endness)
    for i in xrange(0, len(encrypted)):
        final_state.add_constraints(final_state.memory.load(buf_ptr + i, 1) == ord(encrypted[i]))

    # 最后求解得到我们的正确输入
    input_string = final_state.memory.load(str_ptr, 10)
    print "Solving..."
    candidates = final_state.solver.eval_upto(input_string, 2)

    assert len(candidates) == 1
    return hex(candidates[0])[2 : -1].decode('hex')

def test():
    assert main() == 'I_H4TE_X0r'

if __name__ == "__main__":
    print main()
      以上就是求解的代码,用了这么多Angr,给我的感觉是,还是离不开人工分析,包括decrypt函数都是我们需要进行主动hook的,不熟悉Angr之前觉得很好用,但是熟悉之后,觉得还是需要大量的人工分析的。

猜你喜欢

转载自blog.csdn.net/zhuzhuzhu22/article/details/80647234