黑客大神告诉你:Angr_CTF从精通到弃坑( 四 )

int __cdecl complex_function(signed int a1, int a2){if ( a1 <= 64 || a1 > 90 ){puts("Try again.");exit(1);}return (a1 - 65 + 29 * a2) % 26 + 65;}_BOOL4 __cdecl check_equals_ORSDDWXHZURJRBDH(int a1, unsigned int a2){int v3; // [esp+8h] [ebp-8h]unsigned int i; // [esp+Ch] [ebp-4h]v3 = 0;for ( i = 0; i < a2; ++i ){if ( *(_BYTE *)(i + a1) == *(_BYTE *)(i + 0x804C048) )++v3;}return v3 == a2;}这一题与上一题相似 ,我们必须替换check_equals函数。 但是 , 我们可以发现check_equals被调用了很多次 , 以致于无法通过地址Hook每个调用位置 。这时我们必须使用SimProcedure编写我们自己的check_equals实现 , 然后通过函数名Hook所有对check_equals的调用
黑客大神告诉你:Angr_CTF从精通到弃坑文章插图
Hooking Symbols每一个程序都有一个符号表 , angr可以确保从每个导入符号都可以解析出地址 , 可以使用angr提供的Project.hook_symbolAPI来通过符号名来Hook函数所有的调用地址 , 这意味着可以用自己的代码替换函数 , 一个简单的例子:
>>> class NotVeryRand(SimProcedure):...def run(self, return_values=None):...rand_idx = self.state.globals.get('rand_idx', 0) % len(return_values)...out = return_values[rand_idx]...self.state.globals['rand_idx'] = rand_idx + 1...return out>>> project.hook_symbol('rand', NotVeryRand(return_values=[413, 612, 1025, 1111]))老样子别说话 , 上EXP:
import angrimport claripyimport sysdef Go():path_to_binary = "./10_angr_simprocedures"project = angr.Project(path_to_binary, auto_load_libs=False)initial_state = project.factory.entry_state()class ReplacementCheckEquals(angr.SimProcedure):def run(self, to_check, length):user_input_buffer_address = to_checkuser_input_buffer_length = lengthuser_input_string = self.state.memory.load(user_input_buffer_address,user_input_buffer_length)check_against_string = 'ORSDDWXHZURJRBDH'return claripy.If(user_input_string == check_against_string,claripy.BVV(1, 32),claripy.BVV(0, 32))check_equals_symbol = 'check_equals_ORSDDWXHZURJRBDH'project.hook_symbol(check_equals_symbol, ReplacementCheckEquals())simulation = project.factory.simgr(initial_state)def is_successful(state):stdout_output = state.posix.dumps(1)if b'Good Job.\n' in stdout_output:return Trueelse:return Falsedef should_abort(state):stdout_output = state.posix.dumps(1)if b'Try again.\n' instdout_output:return Trueelse:return Falsesimulation.explore(find=is_successful, avoid=should_abort)if simulation.found:for i in simulation.found:solution_state = isolution = solution_state.posix.dumps(0)print("[+] Success! Solution is: {0}".format(solution.decode('utf-8')))#print(solution0)else:raise Exception('Could not find the solution')if __name__ == "__main__":Go()运行一下查看结果
黑客大神告诉你:Angr_CTF从精通到弃坑文章插图
这里前面的部分都可以直接照抄上面一题的代码 , 关键是定义一个继承angr.SimProcedure的类 , 以利用Angr的SimProcedures 。
class ReplacementCheckEquals(angr.SimProcedure):SimProcedure用Python编写的我们自己的函数代替了原来函数 。除了用Python编写之外 , 该函数的行为与用C编写的任何函数基本相同 。 self之后的任何参数都将被视为要替换的函数的参数 ,参数将是符号位向量 。另外 , Python可以以常用的Python方式返回 , Angr将以与原来函数相同的方式对待它
我们先来看一下函数原型:
_BOOL4 __cdecl check_equals_ORSDDWXHZURJRBDH(char *to_check, unsigned int length){int v3; // [esp+8h] [ebp-8h]unsigned int i; // [esp+Ch] [ebp-4h]v3 = 0;for ( i = 0; i < length; ++i ){if ( to_check[i] == *(_BYTE *)(i + 0x804C048) )++v3;}return v3 == length;}不难发现函数的第一个参数是待检测字符串首地址指针 , 然后就是字符串的长度 , 接下来我们就可以开始书写我们的模拟函数
def run(self, to_check, length):#即第一个参数user_input_buffer_address = to_check#即第二个参数user_input_buffer_length = length#使用self.state在SimProcedure中查找系统状态 , 从该状态的内存中提取出数据user_input_string = self.state.memory.load(user_input_buffer_address,user_input_buffer_length)check_against_string = 'ORSDDWXHZURJRBDH'#如果符合条件则返回输入的符号位向量return claripy.If(user_input_string == check_against_string,claripy.BVV(1, 32),claripy.BVV(0, 32))Hook上check_equals函数 ,angr会自动查找与该函数符号关联的地址
check_equals_symbol = 'check_equals_WQNDNKKWAWOLXBAC' project.hook_symbol(check_equals_symbol, ReplacementCheckEquals())之后的操作与其他题目类似 , 不再赘述
11_angr_sim_scanf如题 , 这题主要是学习如何hookscanf函数 , 步骤其实与上一题是几乎一致的 , 得先找到需要hook的函数符号 , 然后编写一个继承angr.SimProcedure的类 , 然后利用hook_symbol对函数进行hook