angr脚本——以angrctf解题记录为参考

​ angr是用于逆向工程中进行二进制分析的一个python框架

​ 符号执行 (Symbolic Execution)是一种程序分析技术。其可以通过分析程序来得到让特定代码区域执行的输入。使用符号执行分析一个程序时,该程序会使用符号值作为输入,而非一般执行程序时使用的具体值。在达到目标代码时,分析器可以得到相应的路径约束,然后通过约束求解器来得到可以触发目标代码的具体值。

​ 以下脚本均用Python3执行,在笔者Ubuntu16.04虚拟机上通过,且能够得到正确的结果

0x00.白给题,简单脚本

  1. import angr
  2. p = angr.Project("./00_angr_find")
  3. init_state = p.factory.entry_state()
  4. sm = p.factory.simulation_manager(init_state)
  5. sm.explore(find=0x08048678) # 输出GoodJob的地方
  6. found_state = sm.found[0]
  7. found_state.posix.dumps(0) # 标准输入

0x01.增加限制条件——explore函数中find和avoid的使用

  1. import angr
  2. import sys
  3. def main(argv):
  4. path_to_binary = argv[1]
  5. project = angr.Project(path_to_binary)
  6. initial_state = project.factory.entry_state()
  7. simulation = project.factory.simgr(initial_state)
  8. # Explore the binary, but this time, instead of only looking for a state that
  9. # reaches the print_good_address, also find a state that does not reach
  10. # will_not_succeed_address. The binary is pretty large, to save you some time,
  11. # everything you will need to look at is near the beginning of the address
  12. # space.
  13. # (!)
  14. print_good_address = 0x080485e5
  15. will_not_succeed_address = 0x080485a8
  16. simulation.explore(find=print_good_address, avoid=will_not_succeed_address)
  17. if simulation.found:
  18. solution_state = simulation.found[0]
  19. print (solution_state.posix.dumps(0))
  20. else:
  21. raise Exception('Could not find the solution')
  22. if __name__ == '__main__':
  23. main(sys.argv)

0x02.find和avoid的进一步使用——以输出作为限制条件

  1. project = angr.Project(path_to_binary)
  2. initial_state = project.factory.entry_state()
  3. simulation = project.factory.simgr(initial_state)
  4. # Define a function that checks if you have found the state you are looking
  5. # for.
  6. def is_successful(state):
  7. # Dump whatever has been printed out by the binary so far into a string.
  8. stdout_output = state.posix.dumps(1)
  9. # Return whether 'Good Job.' has been printed yet.
  10. # (!)
  11. return b'Good Job.' in stdout_output # :boolean
  12. # Same as above, but this time check if the state should abort. If you return
  13. # False, Angr will continue to step the state. In this specific challenge, the
  14. # only time at which you will know you should abort is when the program prints
  15. # "Try again."
  16. def should_abort(state):
  17. stdout_output = state.posix.dumps(1)
  18. return b'Try again.' in stdout_output # :boolean
  19. # Tell Angr to explore the binary and find any state that is_successful identfies
  20. # as a successful state by returning True.
  21. simulation.explore(find=is_successful, avoid=should_abort)
  22. if simulation.found:
  23. solution_state = simulation.found[0]
  24. print(solution_state.posix.dumps(0))
  25. else:
  26. raise Exception('Could not find the solution')
  27. if __name__ == '__main__':
  28. main(sys.argv)

0x03.寄存器符号化

  1. import angr
  2. import sys
  3. import claripy
  4. def main(argv):
  5. bin_path = argv[1]
  6. p = angr.Project(bin_path) # 执行前的初始化工作,例如生成中间语言等
  7. start_addr = 0x80488d1 # 指定程序入口地址
  8. init_state = p.factory.blank_state(addr=start_addr)
  9. pass1 = claripy.BVS('pass1', 32) # 生成符号向量,前者为名称,后者为32/64位
  10. pass2 = claripy.BVS('pass2', 32)
  11. pass3 = claripy.BVS('pass3', 32)
  12. init_state.regs.eax = pass1 # 设置初始状态时各寄存器的状态
  13. init_state.regs.ebx = pass2
  14. init_state.regs.edx = pass3
  15. sm = p.factory.simulation_manager(init_state) # 开始模拟执行
  16. def is_good(state):
  17. return b'Good Job' in state.posix.dumps(1)
  18. def is_bad(state):
  19. return b'Try again' in state.posix.dumps(1)
  20. sm.explore(find=is_good, avoid=is_bad) # 寻找结果
  21. if sm.found:
  22. found_state = sm.found[0]
  23. password1 = found_state.solver.eval(pass1) # 求出结果
  24. password2 = found_state.solver.eval(pass2)
  25. password3 = found_state.solver.eval(pass3)
  26. print("Solution: {:x} {:x} {:x}".format(password1, password2, password3))
  27. else:
  28. raise Exception("No solution found")
  29. if __name__ == '__main__':
  30. main(sys.argv)

0x04.栈符号化

  1. import angr
  2. import sys
  3. import claripy
  4. def main(argv):
  5. bin_path = argv[1]
  6. p = angr.Project(bin_path) # 执行前的初始化工作,例如生成中间语言等
  7. start_addr = 0x8048697 # 指定程序入口地址
  8. init_state = p.factory.blank_state(addr=start_addr) # 初始化状态
  9. pass1 = claripy.BVS('pass1', 32) # 生成符号向量,前者为名称,后者为32/64位
  10. pass2 = claripy.BVS('pass2', 32)
  11. # 对栈的模拟
  12. # /-------- The stack --------\
  13. # ebp -> | padding |
  14. # |---------------------------|
  15. # ebp - 0x01 | more padding |
  16. # |---------------------------|
  17. # ebp - 0x02 | even more padding |
  18. # |---------------------------|
  19. # . . . <- How much padding? Hint: how
  20. # |---------------------------| many bytes is password0?
  21. # ebp - 0x0b | password0, second byte |
  22. # |---------------------------|
  23. # ebp - 0x0c | password0, first byte |
  24. # |---------------------------|
  25. # ebp - 0x0d | password1, last byte |
  26. # |---------------------------|
  27. # . . .
  28. # |---------------------------|
  29. # ebp - 0x10 | password1, first byte |
  30. # |---------------------------|
  31. # . . .
  32. # |---------------------------|
  33. # esp -> | |
  34. # \---------------------------/
  35. #
  36. padding_size = 8 # 栈中填充的长度,即输入的内容入栈时esp=ebp-0x08
  37. # 对栈的情况进行模拟
  38. # ebp是父ebp,保存完父函数ebp才开辟本函数栈空间,当函数执行完以后会有一个pop ebp恢复父函数ebp
  39. # 但是因为我们要执行的代码与父函数无关,只用执行到find的地方就可以了,不用返回父函数接着执行,所以保存不保存父函数ebp都无所谓
  40. # 即:ebp是上一个栈桢的栈基,在这个函数里,这个ebp的值是未知的,在这个angr程序里不会执行到在函数最后几条指令的pop ebp,自然也就不需要再push ebp
  41. init_state.regs.ebp = init_state.regs.esp
  42. init_state.regs.esp -= padding_size
  43. # 模拟scanf的入栈过程
  44. init_state.stack_push(pass1)
  45. init_state.stack_push(pass2)
  46. sm = p.factory.simulation_manager(init_state) # 开始模拟执行
  47. def is_good(state):
  48. return b'Good Job' in state.posix.dumps(1)
  49. def is_bad(state):
  50. return b'Try again' in state.posix.dumps(1)
  51. sm.explore(find=is_good, avoid=is_bad) # 寻找结果
  52. if sm.found:
  53. found_state = sm.found[0]
  54. password1 = found_state.solver.eval(pass1) # 求出结果
  55. password2 = found_state.solver.eval(pass2)
  56. print("Solution: {} {}".format(password1, password2))
  57. else:
  58. raise Exception("No solution found")
  59. if __name__ == '__main__':
  60. main(sys.argv)

0x05.静态内存符号化

  1. import angr
  2. import claripy
  3. import sys
  4. def main(argv):
  5. path_to_binary = argv[1]
  6. project = angr.Project(path_to_binary)
  7. start_address = 0x8048606
  8. initial_state = project.factory.blank_state(addr=start_address)
  9. # The binary is calling scanf("%8s %8s %8s %8s").
  10. # (!)
  11. password0 = claripy.BVS('password0', 8*8)
  12. password1 = claripy.BVS('password1', 8*8)
  13. password2 = claripy.BVS('password2', 8*8)
  14. password3 = claripy.BVS('password3', 8*8)
  15. # Determine the address of the global variable to which scanf writes the user
  16. # input. The function 'initial_state.memory.store(address, value)' will write
  17. # 'value' (a bitvector) to 'address' (a memory location, as an integer.) The
  18. # 'address' parameter can also be a bitvector (and can be symbolic!).
  19. # (!)
  20. password0_address = 0xa29faa0
  21. initial_state.memory.store(password0_address, password0)
  22. password1_address = 0xa29faa8
  23. initial_state.memory.store(password1_address, password1)
  24. password2_address = 0xa29fab0
  25. initial_state.memory.store(password2_address, password2)
  26. password3_address = 0xa29fab8
  27. initial_state.memory.store(password3_address, password3)
  28. simulation = project.factory.simgr(initial_state)
  29. def is_successful(state):
  30. stdout_output = state.posix.dumps(sys.stdout.fileno())
  31. return b'Good Job.' in stdout_output
  32. def should_abort(state):
  33. stdout_output = state.posix.dumps(sys.stdout.fileno())
  34. return b'Try again.' in stdout_output
  35. simulation.explore(find=is_successful, avoid=should_abort)
  36. if simulation.found:
  37. solution_state = simulation.found[0]
  38. # Solve for the symbolic values. We are trying to solve for a string.
  39. # Therefore, we will use eval, with named parameter cast_to=str
  40. # which returns a string instead of an integer.
  41. # (!)
  42. solution0 = solution_state.se.eval(password0,cast_to=bytes).decode("utf-8")
  43. solution1 = solution_state.se.eval(password1,cast_to=bytes).decode("utf-8")
  44. solution2 = solution_state.se.eval(password2,cast_to=bytes).decode("utf-8")
  45. solution3 = solution_state.se.eval(password3,cast_to=bytes).decode("utf-8")
  46. solution = ' '.join([ solution0, solution1, solution2, solution3 ])
  47. print (solution)
  48. else:
  49. raise Exception('Could not find the solution')
  50. if __name__ == '__main__':
  51. main(sys.argv)

0x06.动态内存符号化

  1. # malloc出来的内存地址是不确定的,但是,我们可以跳过malloc和scanf,给指针变量buffer一个指定的内存地址
  2. import angr
  3. import sys
  4. import claripy
  5. def main(argv):
  6. bin_path = argv[1]
  7. p = angr.Project(bin_path)
  8. start_address = 0x0804869E # 跳过malloc和scanf
  9. init_state = p.factory.blank_state(addr=start_address)
  10. buffer0 = 0x44444444 # 随便指定两块内存地址,存放符号化向量
  11. buffer1 = 0x44444544
  12. buffer0_addr = 0xa79a118 # 指向这两块内存地址的指针,存放他们的地址
  13. buffer1_addr = 0xa79a120
  14. # Note: by default, Angr stores integers in memory with big-endianness. To
  15. # specify to use the endianness of your architecture, use the parameter
  16. # endness=project.arch.memory_endness. On x86, this is little-endian.
  17. # (!)
  18. # 内存中的内容是小端序的,故要加上参数endness = p.arch.memory_endness,否则写入的地址是大端序的
  19. init_state.memory.store(buffer0_addr, buffer0, endness=p.arch.memory_endness)
  20. init_state.memory.store(buffer1_addr, buffer1, endness=p.arch.memory_endness)
  21. # 存入符号向量
  22. p0 = claripy.BVS('p0', 64)
  23. p1 = claripy.BVS('p1', 64)
  24. init_state.memory.store(buffer0, p0)
  25. init_state.memory.store(buffer1, p1)
  26. sm = p.factory.simulation_manager(init_state)
  27. def is_successful(state):
  28. return b'Good Job.' in state.posix.dumps(1)
  29. def should_abort(state):
  30. return b'Try again.' in state.posix.dumps(1)
  31. sm.explore(find=is_successful, avoid=should_abort)
  32. if sm.found:
  33. solution = sm.found[0]
  34. pass0 = solution.se.eval(p0, cast_to=bytes).decode("utf-8")
  35. pass1 = solution.se.eval(p1, cast_to=bytes).decode("utf-8")
  36. print("Solution: {} {}".format(pass0, pass1))
  37. else:
  38. raise Exception('Could not find the solution')
  39. if __name__ == '__main__':
  40. main(sys.argv)

0x07.文件符号化

  1. import angr
  2. import sys
  3. import claripy
  4. def main(argv):
  5. bin_path = argv[1]
  6. p = angr.Project(bin_path)
  7. # 从scanf及ignore_me后,memset前开始执行
  8. start_addr = 0x80488de
  9. init_state = p.factory.blank_state(addr=start_addr)
  10. filename = "WCEXPXBW.txt"
  11. filesize = 0x40
  12. # 构造符号向量
  13. password = init_state.solver.BVS("password", filesize*8)
  14. # 构造符号化文件,SimFile函数用于构造文件信息,包括文件名,文件内容和文件大小
  15. sim_file = angr.storage.SimFile(filename, content=password, size=filesize)
  16. # 将符号化文件插入到初始状态中,angr.fs.insert是将文件插入到文件系统中,需要文件名与符号化的文件
  17. init_state.fs.insert(filename, sim_file)
  18. sm = p.factory.simgr(init_state)
  19. def is_successful(state):
  20. return b'Good Job.' in state.posix.dumps(1)
  21. def should_abort(state):
  22. return b'Try again.' in state.posix.dumps(1)
  23. sm.explore(find=is_successful, avoid=should_abort)
  24. if sm.found:
  25. solution = sm.found[0]
  26. password_str = solution.solver.eval(password, cast_to=bytes).decode("utf-8")
  27. print("Solution: {} ".format(password_str))
  28. else:
  29. raise Exception('Could not find the solution')
  30. if __name__ == '__main__':
  31. main(sys.argv)

0x08.增加约束条件解决路径爆炸问题

  1. import angr
  2. import sys
  3. def main(argv):
  4. bin_path = argv[1]
  5. p = angr.Project(bin_path)
  6. start_addr = 0x0804862A
  7. init_state = p.factory.blank_state(addr=start_addr)
  8. buffer_addr = 0x0804A050
  9. password = init_state.solver.BVS("password", 16*8)
  10. init_state.memory.store(buffer_addr, password)
  11. sm = p.factory.simgr(init_state)
  12. check_addr = 0x08048565#此地址并非调用call check的地址,而是点进去看到的函数的代码段的起始地址
  13. sm.explore(find=check_addr)#寻找各种到此函数的路径
  14. if sm.found:
  15. check_state = sm.found[0]
  16. desired_string = "BWYRUBQCMVSBRGFU"
  17. check_param1 = buffer_addr
  18. check_param2 = 0x10
  19. #从内存中把经过变化的buffer再取出来,进行后一步比较
  20. check_bvs = check_state.memory.load(check_param1, check_param2)
  21. check_constraint = desired_string == check_bvs
  22. check_state.add_constraints(check_constraint)
  23. password1 = check_state.solver.eval(password, cast_to=bytes).decode("utf-8")
  24. print("Solution: {}".format(password1))
  25. if __name__ == '__main__':
  26. main(sys.argv)

0x09.设置hook函数解决路径爆炸问题

  1. import angr
  2. import sys
  3. import claripy
  4. def main(argv):
  5. bin_path = argv[1]
  6. p = angr.Project(bin_path)
  7. init_state = p.factory.entry_state()#从main函数开始,angr自动帮你处理输入
  8. # Hook the address of where check_equals_ is called.
  9. # (!)
  10. check_addr = 0x080486B8#call check_equals这条指令的位置
  11. check_skip_size = 5#
  12. #自定义hook函数
  13. @p.hook(check_addr, length = check_skip_size)#先指定call hook函数的位置,再指定call hook函数这条指令的大小
  14. def check_hook(state):
  15. user_input_addr = 0x0804A054
  16. user_input_length = 16
  17. user_input_bvs = state.memory.load(user_input_addr, user_input_length)#从指定的位置取出输入
  18. desired_string = "XKSPZSJKJYQCQXZV"#我们想要的字符串
  19. #hook函数的返回,返回值给到eax
  20. state.regs.eax = claripy.If(desired_string == user_input_bvs, claripy.BVV(1, 32), claripy.BVV(0, 32))
  21. def is_good(state):
  22. return b'Good Job.' in state.posix.dumps(1)
  23. def is_bad(state):
  24. return b'Try again.' in state.posix.dumps(1)
  25. sm = p.factory.simgr(init_state)
  26. sm.explore(find=is_good, avoid=is_bad)
  27. if sm.found:
  28. found_state = sm.found[0]
  29. print("Solution: {}".format(found_state.posix.dumps(0)))
  30. else:
  31. raise Exception("Solution Not found")
  32. if __name__ == '__main__':
  33. main(sys.argv)

0x0A.hook所有同名函数

  1. import angr
  2. import sys
  3. import claripy
  4. def main(argv):
  5. bin_path = argv[1]
  6. p = angr.Project(bin_path)
  7. init_state = p.factory.entry_state()
  8. #将hook函数设置成一个类
  9. class mySimPro(angr.SimProcedure):
  10. def run(self, to_check, length):#传入用户输入的地址和输入长度
  11. user_input_buffer_address = to_check
  12. user_input_buffer_length = length
  13. angr_bvs = self.state.memory.load(user_input_buffer_address, user_input_buffer_length)#让angr从内存中把输入的东西提取出来
  14. desired = 'WQNDNKKWAWOLXBAC'
  15. return claripy.If(desired == angr_bvs, claripy.BVV(1, 32), claripy.BVV(0, 32))
  16. check_symbol = "check_equals_WQNDNKKWAWOLXBAC"#函数名称
  17. p.hook_symbol(check_symbol, mySimPro())
  18. sm = p.factory.simgr(init_state)
  19. def is_good(state):
  20. return b"Good Job" in state.posix.dumps(1)
  21. def is_bad(state):
  22. return b"Try again" in state.posix.dumps(1)
  23. sm.explore(find=is_good, avoid=is_bad)
  24. if sm.found:
  25. found_state = sm.found[0]
  26. password = found_state.posix.dumps(0)
  27. print("Solution: {}".format(password.decode("utf-8")))
  28. else:
  29. raise Exception("Solution not found")
  30. if __name__ == '__main__':
  31. main(sys.argv)

未完待续

angr脚本——以angrctf解题记录为参考的更多相关文章

  1. pwnable.kr input解题记录

    pwnable input解题记录 给了源码如下: #include "stdio.h" #include "unistd.h" #include " ...

  2. ssrf解题记录

    ssrf解题记录 最近工作需要做一些Web的代码审计,而我Web方面还比较薄弱,决定通过一些ctf的题目打打审计基础,练练思维,在博客上准备开几个专题专门记录刷题的过程. pwn题最近做的也很少,也要 ...

  3. PADS Logic 脚本的 Fields 一个对象记录

    PADS Logic 脚本的 Fields 一个对象记录 PADS Laogic 有一个非常棒的脚本功能,可以导出所以元件. 我目前是把脚本绑定到 Ctrl+S 上,在保存时自动导出 txt 文件,方 ...

  4. LeetCode解题记录(贪心算法)(二)

    1. 前言 由于后面还有很多题型要写,贪心算法目前可能就到此为止了,上一篇博客的地址为 LeetCode解题记录(贪心算法)(一) 下面正式开始我们的刷题之旅 2. 贪心 763. 划分字母区间(中等 ...

  5. ;~ 并发运行的AutoHotkey脚本真机实际测试模板参考20191010.ahk

    ;~ 并发运行的AutoHotkey脚本真机实际测试模板参考20191010.ahk;~ 2019年10月10日;~ 徐晓亮(aahk6188);~ 操作系统测试环境: Windows 7 专业版 3 ...

  6. 实验吧web解题记录

    自以为sql注入掌握的还是比较系统的,然而,做了这些题之后才发现,大千世界无奇不有,真是各种猥琐的思路...还是要多学习学习姿势跟上节奏 登录一下好吗?? http://ctf5.shiyanbar. ...

  7. LeetCode解题记录(贪心算法)(一)

    1. 前言 目前得到一本不错的算法书籍,页数不多,挺符合我的需要,于是正好借这个机会来好好的系统的刷一下算法题,一来呢,是可以给部分同学提供解题思路,和一些自己的思考,二来呢,我也可以在需要复习的时候 ...

  8. 自动交互脚本之expect使用记录

    之前一直没怎么用这个命令,意外用了一下,还不错,那这个是干嘛的呢 我们或多或少会远程登录其他服务器,需要执行某项任务,通常需要手动接入,输入密码啊,等等 那我们如何有效的自动执行呢,expect可以解 ...

  9. 针对Linux上Java程式运行脚本的Log信息记录操作人员记录以及成功运行判断

    简介与优点 使用该教程,能直观地看到java启动脚本是否启动/关闭成功 能让自己的启动时间日期都记录在Log中 能记录有哪些人登陆了该服务器操作了启动关闭脚本(记录IP地址) 使用说明 在原有的启动和 ...

随机推荐

  1. 顶级开源项目 Sentry 20.x JS-SDK 设计艺术(概述篇)

    SDK 开发 顶级开源项目 Sentry 20.x JS-SDK 设计艺术(理念与设计原则篇) 顶级开源项目 Sentry 20.x JS-SDK 设计艺术(开发基础篇) 系列 Snuba:Sentr ...

  2. CSS中的块级元素,行内元素,行内块元素

    博客转载于:https://blog.csdn.net/swebin/article/details/90405950 块级元素 block 块级元素,该元素呈现块状,所以他有自己的宽度和高度,也就是 ...

  3. c语言跨文件调用函数中声明的变量

    转载:weixin_33885253 变量的作用域 变量根据其作用域有全局变量和局部变量之分.全局变量作用域是整个文件,并且可以使用关键字extern达到跨文件调用的目的.但是局部变量值作用于它当前所 ...

  4. 基础篇:JAVA引用类型和ThreadLocal

    前言 平时并发编程,除了维护修改共享变量的场景,有时我们也需要为每一个线程设置一个私有的变量,进行线程隔离,java提供的ThreadLocal可以帮助我们实现,而讲到ThreadLocal则不得不讲 ...

  5. CentOS离线安装Nginx

    在医院搭建项目环境时,因为医院通常都是内网的,访问不了外网,所以很多服务都得通过离线的方式安装,下面讲讲CentOs系统中如何离线安装Nginx. 安装准备 Nginx离线安装依赖gcc.g++环境, ...

  6. Go语言GC实现原理及源码分析

    转载请声明出处哦~,本篇文章发布于luozhiyun的博客:https://www.luozhiyun.com/archives/475 本文使用的 Go 的源码1.15.7 介绍 三色标记法 三色标 ...

  7. NameError: name 'foo' is not defined Python常见错误

    1.变量或者函数名拼写错误 2.在一个定义新变量中使用增值操作符 没有定义的变量被引用时候会出现此错误

  8. 第23 章 : Kubernetes API 编程范式

    Kubernetes API 编程范式 需求来源 首先我们先来看一下 API 编程范式的需求来源. 在 Kubernetes 里面, API 编程范式也就是 Custom Resources Defi ...

  9. 锋利的NodeJS之NodeJS多线程

    最近刚好有朋友在问Node.js多线程的问题,我总结了一下,可以考虑使用源码包里面的worker_threads或者第三方的模块来实现. 首先明确一下多线程在Node.js中的概念,然后在聊聊work ...

  10. MyBatis笔记(六)

    1. 动态SQL 1.1 介绍 概念:**动态SQL指的是根据不同的查询条件 , 生成不同的Sql语句.* 官网描述: MyBatis 的强大特性之一便是它的动态 SQL.如果你有使用 JDBC 或其 ...