angr符号执行用例解析——defcon2016quals_baby-re

用例源码以及二进制文件链接:https://github.com/angr/angr-doc/tree/master/examples/defcon2016quals_baby-re

执行二进制文件,发现需要输入13个字符,然后通过一系列计算判断输入字符是否满足约束条件。

在IDA里非常清晰可以看见输入错误的字符串及其调用地址:


所以avoid_addr=0x402941

而正确的字符串以及地址为:


可以看出,flag将作为程序输出,所以find_addr地址可以设置为0x40292c(亲测有效)。

不过这一次就又对scanf函数进行了hook,用来创建13个符号变量。

这次是一定要hook scanf函数的。因为它默认会返回一个无约束的一个符号变量(是否是同一个符号变量还不确定),在scanf函数前面存在fflush函数,它的功能是清空缓冲区,将输入写入stream。

而且hook的函数,也出现了新的知识点:

	class my_scanf(angr.SimProcedure):
		def run(self, fmt, ptr):
			if 'scanf_count' not in self.state.globals:
				self.state.globals['scanf_count'] = 0
			c = self.state.globals['scanf_count']
			self.state.mem[ptr].dword = flag_chars[c]
			self.state.globals['scanf_count'] = c + 1

在符号执行的过程中,是可以创建变量的,存储在state.globals里面。这里面就创建了一个变量scanf_count是记录scanf函数的数目,使得第i个scanf函数的state.mem赋值第i个符号变量。

用例源码:

#!/usr/bin/env python2

"""
Author: David Manouchehri <manouchehri@protonmail.com>
DEFCON CTF Qualifier 2016
Challenge: baby-re
Team: hack.carleton
Write-up: http://hack.carleton.team/2016/05/21/defcon-ctf-qualifier-2016-baby-re/
Runtime: ~8 minutes (single threaded E5-2650L v3 @ 1.80GHz on DigitalOcean)

DigitalOcean is horrible for single threaded applications, I would highly suggest using something else.
"""

import angr
import claripy

def main():
	proj = angr.Project('./baby-re',  load_options={'auto_load_libs': False})

	# let's provide the exact variables received through the scanf so we don't have to worry about parsing stdin into a bunch of ints.
	flag_chars = [claripy.BVS('flag_%d' % i, 32) for i in xrange(13)]
	class my_scanf(angr.SimProcedure):
		def run(self, fmt, ptr):
			if 'scanf_count' not in self.state.globals:
				self.state.globals['scanf_count'] = 0
			c = self.state.globals['scanf_count']
			self.state.mem[ptr].dword = flag_chars[c]
			self.state.globals['scanf_count'] = c + 1
	proj.hook_symbol('__isoc99_scanf', my_scanf(), replace=True)

	sm = proj.factory.simulation_manager()

	# search for just before the printf("%c%c...")
	# If we get to 0x402941, "Wrong" is going to be printed out, so definitely avoid that.
	sm.explore(find=0x4028E9, avoid=0x402941)

	# evaluate each of the flag chars against the constraints on the found state to construct the flag
	flag = ''.join(chr(sm.one_found.solver.eval(c)) for c in flag_chars)
	return flag

def test():
	assert main() == 'Math is hard!'

if __name__ == '__main__':
	print(repr(main()))



阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页