实验开始前还得先把文件编译一下
在相应目录下,使用如下命令,如
1 python generate.py 1234 00_angr_find
贴一个angr中文文档angr (非官方)
angr-官方 ,英文的。angr-api ,英语苦手泪目。
(本题中,有些导入的库不一定那个用得到,但是题目数量多,懒得增删改,就一次性导入了…)
00_angr_find 先上伪代码
当然,其实这整个实验,汇编语言是很重要的,伪代码是为了帮助理清程序逻辑。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 .text:0804864E push offset s2 ; "FPQPMQXT" .text:08048653 lea eax, [ebp+s1] .text:08048656 push eax ; s1 .text:08048657 call _strcmp .text:0804865C add esp, 10h .text:0804865F test eax, eax .text:08048661 jz short loc_8048675 .text:08048663 sub esp, 0Ch .text:08048666 push offset s ; "Try again." .text:0804866B call _puts .text:08048670 add esp, 10h .text:08048673 jmp short loc_8048685 .text:08048675 ; --------------------------------------------------------------------------- .text:08048675 .text:08048675 loc_8048675: ; CODE XREF: main+9A↑j .text:08048675 sub esp, 0Ch .text:08048678 push offset aGoodJob ; "Good Job." .text:0804867D call _puts .text:08048682 add esp, 10h .text:08048685 .text:08048685 loc_8048685:
(这题可以直接逆来着)
发现程序中正确结果的走向,有good job语句,利用angr的explore,让angr找到相对应路径,执行即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import angrimport sysimport claripyfrom Crypto.Util.number import long_to_bytesdef main (): path_to_binary = "E:\\LAB\\angr\\angr\program\\00_angr_find" project = angr.Project(path_to_binary, auto_load_libs=False ) initial_state = project.factory.entry_state() simulation = project.factory.simgr(initial_state) print_good_address = 0x8048678 simulation.explore(find=print_good_address) if simulation.found: solution_state = simulation.found[0 ] solution = solution_state.posix.dumps(sys.stdin.fileno()) print ("THE Answer is: {}" .format (solution)) else : raise Exception('Could not find the solution' ) if __name__ == "__main__" : main()
01_angr_avoid 这题因为太大反汇编不了,所以还是得看汇编。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 .text:0804890F jz short loc_804892E .text:08048911 call avoid_me .text:08048916 sub esp, 8 .text:08048919 lea eax, [ebp+var_20] .text:0804891C push eax .text:0804891D lea eax, [ebp+var_34] .text:08048920 push eax .text:08048921 call maybe_good .text:08048926 add esp, 10h .text:08048929 jmp loc_80D456F .text:0804892E ; --------------------------------------------------------------------------- .text:0804892E .text:0804892E loc_804892E: ; CODE XREF: main+30D↑j .text:0804892E sub esp, 8 .text:08048931 lea eax, [ebp+var_20] .text:08048934 push eax .text:08048935 lea eax, [ebp+var_34] .text:08048938 push eax .text:08048939 call maybe_good .text:0804893E add esp, 10h .text:08048941 jmp loc_80D456F .text:08048946 ; --------------------------------------------------------------------------- .text:08048946 .text:08048946 loc_8048946: ; CODE XREF: main+2E5↑j .text:08048946 call avoid_me .....
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 text:080485B5 public maybe_good .text:080485B5 maybe_good proc near ; CODE XREF: main+31F↓p .text:080485B5 ; main+337↓p ... .text:080485B5 .text:080485B5 arg_0 = dword ptr 8 .text:080485B5 arg_4 = dword ptr 0Ch .text:080485B5 .text:080485B5 ; __unwind { .text:080485B5 push ebp .text:080485B6 mov ebp, esp .text:080485B8 sub esp, 8 .text:080485BB movzx eax, should_succeed .text:080485C2 test al, al .text:080485C4 jz short loc_80485EF .text:080485C6 sub esp, 4 .text:080485C9 push 8 .text:080485CB push [ebp+arg_4] .text:080485CE push [ebp+arg_0] .text:080485D1 call _strncmp .text:080485D6 add esp, 10h .text:080485D9 test eax, eax .text:080485DB jnz short loc_80485EF .text:080485DD sub esp, 0Ch .text:080485E0 push offset aGoodJob ; "Good Job." .text:080485E5 call _puts .text:080485EA add esp, 10h .text:080485ED jmp short loc_80485FF .text:080485EF ; --------------------------------------------------------------------------- .text:080485EF .text:080485EF loc_80485EF: ; CODE XREF: maybe_good+F↑j .text:080485EF ; maybe_good+26↑j .text:080485EF sub esp, 0Ch .text:080485F2 push offset aTryAgain ; "Try again." .text:080485F7 call _puts .text:080485FC add esp, 10h .text:080485FF .text:080485FF loc_80485FF: ; CODE XREF: maybe_good+38↑j .text:080485FF nop .text:08048600 leave .text:08048601 retn .text:08048601 ; } // starts at 80485B5 .text:08048601 maybe_good endp .text:08048601
有俩个函数,avoid me和maybegood。字面意思,正确寻址应该在后者里。
查找到goodjob地址为0x080485E0
要避开的tryagain在0x080485F2
(其实这题就把上一题的脚本拿来改个地址也可以得出答案,不过会慢很多,因为不会避开avoidme函数)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 import angrimport sysimport claripyfrom Crypto.Util.number import long_to_bytesdef main (): path_to_binary = "E:\\LAB\\angr\\angr\program\\01_angr_avoid" project = angr.Project(path_to_binary, auto_load_libs=False ) initial_state = project.factory.entry_state() simulation = project.factory.simgr(initial_state) avoid_me_address = 0x080485A8 maybe_good_address = 0x080485E0 simulation.explore(find=maybe_good_address, avoid=avoid_me_address) if simulation.found: solution_state = simulation.found[0 ] solution = solution_state.posix.dumps(sys.stdin.fileno()) print ("ANSWER is: {}" .format (solution)) else : raise Exception('Could not find the solution' ) if __name__ == "__main__" : main()
这个脚本相比于上一道题,在于explore中多了一个avoid=要排除执行的路径。
02_angr_find_condition
首先乍一看,多个goodjob,这题肯定就不能直接寻址goodjob了,不唯一。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 .text:0804876B loc_804876B: ; CODE XREF: main+112↑j .text:0804876B cmp [ebp+var_38], 0DEADBEEFh .text:08048772 jz short loc_80487B5 .text:08048774 sub esp, 8 .text:08048777 lea eax, [ebp+s2] .text:0804877A push eax ; s2 .text:0804877B lea eax, [ebp+s1] .text:0804877E push eax ; s1 .text:0804877F call _strcmp .text:08048784 add esp, 10h .text:08048787 test eax, eax .text:08048789 jz short loc_80487A0 .text:0804878B sub esp, 0Ch .text:0804878E push offset s ; "Try again." .text:08048793 call _puts .text:08048798 add esp, 10h .text:0804879B jmp loc_804D267 .text:080487A0 ; --------------------------------------------------------------------------- .text:080487A0 .text:080487A0 loc_80487A0: ; CODE XREF: main+1C1↑j .text:080487A0 sub esp, 0Ch .text:080487A3 push offset aGoodJob ; "Good Job." .text:080487A8 call _puts .text:080487AD add esp, 10h .text:080487B0 jmp loc_804D267 .text:080487B5 ; --------------------------------------------------------------------------- .text:080487B5 .text:080487B5 loc_80487B5: ; CODE XREF: main+1AA↑j .text:080487B5 sub esp, 8 .text:080487B8 lea eax, [ebp+s2] .text:080487BB push eax ; s2 .text:080487BC lea eax, [ebp+s1] .text:080487BF push eax ; s1 .text:080487C0 call _strcmp .text:080487C5 add esp, 10h .text:080487C8 test eax, eax .text:080487CA jz short loc_80487E1 .text:080487CC sub esp, 0Ch .text:080487CF push offset s ; "Try again." .text:080487D4 call _puts .text:080487D9 add esp, 10h .text:080487DC jmp loc_804D267 .text:080487E1 ; --------------------------------------------------------------------------- .text:080487E1 .text:080487E1 loc_80487E1: ; CODE XREF: main+202↑j .text:080487E1 sub esp, 0Ch .text:080487E4 push offset aGoodJob ; "Good Job." .text:080487E9 call _puts
当然,这题想要直接逆也是可以的,不过我们还是用angr
首先是思路:够狠上面一样题的是,还是应该回避tryagain一类的失败路径,然后再设置两个回调函数,当返回tryagain时,回避路径,反之则不回避。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 import angrimport sysimport claripyfrom Crypto.Util.number import long_to_bytesdef main (): path_to_binary = "E:\\LAB\\angr\\angr\program\\02_angr_find_condition" project = angr.Project(path_to_binary, auto_load_libs=False ) initial_state = project.factory.entry_state() simulation = project.factory.simgr(initial_state) def is_successful (state ): stdout_output = state.posix.dumps(sys.stdout.fileno()) if b'Good Job.' in stdout_output: return True else : return False def should_abort (state ): stdout_output = state.posix.dumps(sys.stdout.fileno()) if b'Try again.' in stdout_output: return True else : return False simulation.explore(find=is_successful, avoid=should_abort) if simulation.found: solution_state = simulation.found[0 ] solution = solution_state.posix.dumps(sys.stdin.fileno()) print ("The ANSWER is: {}" .format (solution)) else : raise Exception('Could not find the solution' ) if __name__ == "__main__" : main()
03_angr_symbolic_registers
上面是伪代码。
主函数汇编
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 .text:080488E8 main proc near ; DATA XREF: _start+17↑o .text:080488E8 .text:080488E8 var_14 = dword ptr -14h .text:080488E8 var_10 = dword ptr -10h .text:080488E8 var_C = dword ptr -0Ch .text:080488E8 var_4 = dword ptr -4 .text:080488E8 argc = dword ptr 8 .text:080488E8 argv = dword ptr 0Ch .text:080488E8 envp = dword ptr 10h .text:080488E8 .text:080488E8 ; __unwind { .text:080488E8 lea ecx, [esp+4] .text:080488EC and esp, 0FFFFFFF0h .text:080488EF push dword ptr [ecx-4] .text:080488F2 push ebp .text:080488F3 mov ebp, esp .text:080488F5 push ecx .text:080488F6 sub esp, 14h .text:080488F9 sub esp, 0Ch .text:080488FC push offset aEnterThePasswo ; "Enter the password: " .text:08048901 call _printf .text:08048906 add esp, 10h .text:08048909 call get_user_input .text:0804890E mov [ebp+var_14], eax .text:08048911 mov [ebp+var_10], ebx .text:08048914 mov [ebp+var_C], edx .text:08048917 sub esp, 0Ch .text:0804891A push [ebp+var_14] .text:0804891D call complex_function_1 .text:08048922 add esp, 10h .text:08048925 mov ecx, eax .text:08048927 mov [ebp+var_14], ecx .text:0804892A sub esp, 0Ch .text:0804892D push [ebp+var_10] .text:08048930 call complex_function_2 .text:08048935 add esp, 10h .text:08048938 mov ecx, eax .text:0804893A mov [ebp+var_10], ecx .text:0804893D sub esp, 0Ch .text:08048940 push [ebp+var_C] .text:08048943 call complex_function_3 .text:08048948 add esp, 10h .text:0804894B mov ecx, eax .text:0804894D mov [ebp+var_C], ecx .text:08048950 cmp [ebp+var_14], 0 .text:08048954 jnz short loc_8048962 .text:08048956 cmp [ebp+var_10], 0 .text:0804895A jnz short loc_8048962 .text:0804895C cmp [ebp+var_C], 0 .text:08048960 jz short loc_8048974
可以看到,四个函数调用。第一个就是让我们输入三个数;eax,ebx,edx分别存放输入数据,也是接下来三个函数的参数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 .text:0804889A get_user_input proc near ; CODE XREF: main+21↓p .text:0804889A .text:0804889A var_18 = dword ptr -18h .text:0804889A var_14 = dword ptr -14h .text:0804889A var_10 = dword ptr -10h .text:0804889A var_C = dword ptr -0Ch .text:0804889A .text:0804889A ; __unwind { .text:0804889A push ebp .text:0804889B mov ebp, esp .text:0804889D sub esp, 18h .text:080488A0 mov ecx, large gs:14h .text:080488A7 mov [ebp+var_C], ecx .text:080488AA xor ecx, ecx .text:080488AC lea ecx, [ebp+var_10] .text:080488AF push ecx .text:080488B0 lea ecx, [ebp+var_14] .text:080488B3 push ecx .text:080488B4 lea ecx, [ebp+var_18] .text:080488B7 push ecx .text:080488B8 push offset aXXX ; "%x %x %x" .text:080488BD call ___isoc99_scanf .text:080488C2 add esp, 10h .text:080488C5 mov ecx, [ebp+var_18] .text:080488C8 mov eax, ecx .text:080488CA mov ecx, [ebp+var_14] .text:080488CD mov ebx, ecx .text:080488CF mov ecx, [ebp+var_10] .text:080488D2 mov edx, ecx .text:080488D4 nop .text:080488D5 mov ecx, [ebp+var_C] .text:080488D8 xor ecx, large gs:14h .text:080488DF jz short locret_80488E6 .text:080488E1 call ___stack_chk_fail .text:080488E6 ; --------------------------------------------------------------------------- .text:080488E6 .text:080488E6 locret_80488E6: ; CODE XREF: get_user_input+45↑j .text:080488E6 leave .text:080488E7 retn .text:080488E7 ; } // starts at 804889A .text:080488E7 get_user_input endp
我们看接下来三个函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 .text:08048509 complex_function_1 proc near ; CODE XREF: main+35↓p .text:08048509 .text:08048509 arg_0 = dword ptr 8 .text:08048509 .text:08048509 ; __unwind { .text:08048509 push ebp .text:0804850A mov ebp, esp .text:0804850C xor [ebp+arg_0], 0A78D4A5Fh .text:08048513 add [ebp+arg_0], 3EC98793h .text:0804851A xor [ebp+arg_0], 51FCDF96h .text:08048521 add [ebp+arg_0], 546F1B35h .text:08048528 add [ebp+arg_0], 7CD06332h .text:0804852F add [ebp+arg_0], 3BEEDF0Bh .text:08048536 add [ebp+arg_0], 5824146Ch .text:0804853D add [ebp+arg_0], 15CBC4FFh .text:08048544 mov ecx, [ebp+arg_0] .text:08048547 sub ecx, 46C32D9h .text:0804854D mov [ebp+arg_0], ecx .text:08048550 xor [ebp+arg_0], 9B99C626h .text:08048557 add [ebp+arg_0], 6590A23Dh .text:0804855E xor [ebp+arg_0], 0D16C9C3Ch .text:08048565 xor [ebp+arg_0], 0D73D3031h .text:0804856C xor [ebp+arg_0], 37EB59D3h .text:08048573 mov ecx, [ebp+arg_0] .text:08048576 sub ecx, 5DDF68E6h .text:0804857C mov [ebp+arg_0], ecx .text:0804857F mov ecx, [ebp+arg_0] .text:08048582 sub ecx, 64F2CB17h .text:08048588 mov [ebp+arg_0], ecx .text:0804858B xor [ebp+arg_0], 0F1C347B6h .text:08048592 xor [ebp+arg_0], 0BB664966h .text:08048599 xor [ebp+arg_0], 0DCD816D2h .text:080485A0 xor [ebp+arg_0], 0A523DD67h .text:080485A7 add [ebp+arg_0], 2C64C6B3h .text:080485AE mov ecx, [ebp+arg_0] .text:080485B1 sub ecx, 632C11C3h .text:080485B7 mov [ebp+arg_0], ecx .text:080485BA add [ebp+arg_0], 220BCB4Ch .text:080485C1 xor [ebp+arg_0], 0C13B368Ah .text:080485C8 xor [ebp+arg_0], 7323522h .text:080485CF mov ecx, [ebp+arg_0] .text:080485D2 sub ecx, 7FA5CE75h .text:080485D8 mov [ebp+arg_0], ecx .text:080485DB add [ebp+arg_0], 2BA5F91Ah .text:080485E2 xor [ebp+arg_0], 0CEF3925Eh .text:080485E9 xor [ebp+arg_0], 879B8252h .text:080485F0 add [ebp+arg_0], 6D4F923Bh .text:080485F7 mov ecx, [ebp+arg_0] .text:080485FA sub ecx, 491AF770h .text:08048600 mov [ebp+arg_0], ecx .text:08048603 xor [ebp+arg_0], 96C75B6h .text:0804860A xor [ebp+arg_0], 51EC989Bh .text:08048611 xor [ebp+arg_0], 0DE67C3E3h .text:08048618 add [ebp+arg_0], 2C688544h .text:0804861F xor [ebp+arg_0], 6A479F09h .text:08048626 xor [ebp+arg_0], 0D8B0FF0Dh .text:0804862D xor [ebp+arg_0], 37172519h .text:08048634 mov ecx, [ebp+arg_0] .text:08048637 mov eax, ecx .text:08048639 pop ebp .text:0804863A retn .text:0804863A ; } // starts at 8048509 .text:0804863A complex_function_1 endp
逻辑简单,就是异或。
来说整体思路:
首先是三个参数,要异或过后符合要求,输出goodjob。由于这题没有指出最后要比较的三个数的具体的值,所以我们不能套用前面那的方法。此处应该控制输入的三个值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 import angrimport sysimport claripyfrom Crypto.Util.number import long_to_bytesdef main (): path_to_binary = "E:\\LAB\\angr\\angr\program\\03_angr_symbolic_registers" project = angr.Project(path_to_binary) start_addr = 0x0804890E initial_state = project.factory.blank_state(addr=start_addr) passwd_size_in_bits = 32 passwd0 = claripy.BVS('passwd0' , passwd_size_in_bits) passwd1 = claripy.BVS('passwd1' , passwd_size_in_bits) passwd2 = claripy.BVS('passwd2' , passwd_size_in_bits) initial_state.regs.eax = passwd0 initial_state.regs.ebx = passwd1 initial_state.regs.edx = passwd2 simulation = project.factory.simgr(initial_state) def is_successful (state ): stdout_output = state.posix.dumps(sys.stdout.fileno()) if b'Good Job.' in stdout_output: return True else : return False def should_abort (state ): stdout_output = state.posix.dumps(sys.stdout.fileno()) if b'Try again.' in stdout_output: return True else : return False simulation.explore(find=is_successful, avoid=should_abort) if simulation.found: for i in simulation.found: solution_state = i solution0 = format (solution_state.solver.eval (passwd0), 'x' ) solution1 = format (solution_state.solver.eval (passwd1), 'x' ) solution2 = format (solution_state.solver.eval (passwd2), 'x' ) solution = solution0 + " " + solution1 + " " + solution2 print ("The ANSWER is: {}" .format (solution)) else : raise Exception('Could not find the solution' ) if __name__ == "__main__" : main()
04_angr_symbolic_stack
可以明确,关键在handle_user函数里面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 handle_user proc near ; CODE XREF: main+21↓p .text:08048679 .text:08048679 var_10 = dword ptr -10h .text:08048679 var_C = dword ptr -0Ch .text:08048679 .text:08048679 ; __unwind { .text:08048679 push ebp .text:0804867A mov ebp, esp .text:0804867C sub esp, 18h .text:0804867F sub esp, 4 .text:08048682 lea eax, [ebp+var_10] .text:08048685 push eax .text:08048686 lea eax, [ebp+var_C] .text:08048689 push eax .text:0804868A push offset aUU ; "%u %u" .text:0804868F call ___isoc99_scanf .text:08048694 add esp, 10h .text:08048697 mov eax, [ebp+var_C] .text:0804869A sub esp, 0Ch .text:0804869D push eax .text:0804869E call complex_function0 .text:080486A3 add esp, 10h .text:080486A6 mov [ebp+var_C], eax .text:080486A9 mov eax, [ebp+var_10] .text:080486AC sub esp, 0Ch .text:080486AF push eax .text:080486B0 call complex_function1 .text:080486B5 add esp, 10h .text:080486B8 mov [ebp+var_10], eax .text:080486BB mov eax, [ebp+var_C] .text:080486BE cmp eax, 0D3062A4Ch .text:080486C3 jnz short loc_80486CF .text:080486C5 mov eax, [ebp+var_10] .text:080486C8 cmp eax, 694E5BA0h .text:080486CD jz short loc_80486E1 .text:080486CF .text:080486CF loc_80486CF: ; CODE XREF: handle_user+4A↑j .text:080486CF sub esp, 0Ch .text:080486D2 push offset s ; "Try again." .text:080486D7 call _puts .text:080486DC add esp, 10h .text:080486DF jmp short loc_80486F1 .text:080486E1 ; --------------------------------------------------------------------------- .text:080486E1 .text:080486E1 loc_80486E1: ; CODE XREF: handle_user+54↑j .text:080486E1 sub esp, 0Ch .text:080486E4 push offset aGoodJob ; "Good Job." .text:080486E9 call _puts .text:080486EE add esp, 10h .text:080486F1 .text:080486F1 loc_80486F1: ; CODE XREF: handle_user+66↑j .text:080486F1 nop .text:080486F2 leave .text:080486F3 retn .text:080486F3 ; } // starts at 8048679 .text:080486F3 handle_user endp
我们可以看一下complex_function0的函数汇编代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 .text:080484A9 complex_function0 proc near ; CODE XREF: handle_user+25↓p .text:080484A9 .text:080484A9 arg_0 = dword ptr 8 .text:080484A9 .text:080484A9 ; __unwind { .text:080484A9 push ebp .text:080484AA mov ebp, esp .text:080484AC xor [ebp+arg_0], 0D53642BEh .text:080484B3 xor [ebp+arg_0], 58FC2926h .text:080484BA xor [ebp+arg_0], 25596A36h .text:080484C1 xor [ebp+arg_0], 0A7AFAA43h .text:080484C8 xor [ebp+arg_0], 1559CAFEh .text:080484CF xor [ebp+arg_0], 0D8D89C66h .text:080484D6 xor [ebp+arg_0], 6B8B30B6h .text:080484DD xor [ebp+arg_0], 0B5E7C180h .text:080484E4 xor [ebp+arg_0], 1FA429F6h .text:080484EB xor [ebp+arg_0], 21C70AF4h .text:080484F2 xor [ebp+arg_0], 0B7261E1Dh .text:080484F9 xor [ebp+arg_0], 0ADD88AD8h .text:08048500 xor [ebp+arg_0], 3E16A0F2h .text:08048507 xor [ebp+arg_0], 0DF2308FBh .text:0804850E xor [ebp+arg_0], 2273AAFh .text:08048515 xor [ebp+arg_0], 8E69AC70h .text:0804851C xor [ebp+arg_0], 0AC8924h .text:08048523 xor [ebp+arg_0], 561B782h .text:0804852A xor [ebp+arg_0], 5A64A924h .text:08048531 xor [ebp+arg_0], 0B118005Bh .text:08048538 xor [ebp+arg_0], 61461EA2h .text:0804853F xor [ebp+arg_0], 0E0E04E79h .text:08048546 xor [ebp+arg_0], 0A8DDACAAh .text:0804854D xor [ebp+arg_0], 82AF667Dh .text:08048554 xor [ebp+arg_0], 0B3CB4464h .text:0804855B xor [ebp+arg_0], 43B7BB1Ah .text:08048562 xor [ebp+arg_0], 0DF30F25Bh .text:08048569 xor [ebp+arg_0], 4C0F3376h .text:08048570 xor [ebp+arg_0], 0B2E462E5h .text:08048577 xor [ebp+arg_0], 7BF4CFC3h .text:0804857E xor [ebp+arg_0], 0C2960388h .text:08048585 xor [ebp+arg_0], 27071524h .text:0804858C mov eax, [ebp+arg_0] .text:0804858F pop ebp .text:08048590 retn .text:08048590 ; } // starts at 80484A9 .text:08048590 complex_function0 endp
其实可以看出,跟上一题的思路其实相差不大。所以本题,我们也需要控制两个参数。
故,思路如下:
开始地址设置在输入函数之后,传参数给complex之前。通过complex函数内部可以知道,esp寄存器的值给了ebp(两个complex函数都是这样)。然后我们设置两个参数并将其压入栈。之后的操作和上一题大同小异
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 import angrimport claripyfrom Crypto.Util.number import long_to_bytesimport syspro =angr.Project("E:\\LAB\\angr\\angr\program\\04_angr_symbolic_stack" ) start_addr = 0x08048697 init_state = pro.factory.blank_state(addr=start_addr) init_state.regs.ebp = init_state.regs.esp passwd0 = claripy.BVS("passwd0" , 32 ) passwd1 = claripy.BVS("passwd1" , 32 ) init_state.regs.esp -= 8 init_state.stack_push(passwd0) init_state.stack_push(passwd1) simulation = pro.factory.simgr(init_state) def success (state ): output = state.posix.dumps(1 ) return b"Good Job." in output def abort (state ): output = state.posix.dumps(1 ) return b"Try again." in output simulation.explore(find=success, avoid=abort) if simulation.found: res = simulation.found[0 ] solution0 = res.solver.eval (passwd0) solution1 = res.solver.eval (passwd1) solution = " " .join(map (str , [solution0, solution1])) print ("The Answer is :{}" .format (solution)) else : print ("No Answer!" )
05_angr_symbolic_memory
还是先贴上伪代码
接着看汇编
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 main proc near ; DATA XREF: _start+17↑o .text:080485A8 .text:080485A8 var_C = dword ptr -0Ch .text:080485A8 var_4 = dword ptr -4 .text:080485A8 argc = dword ptr 8 .text:080485A8 argv = dword ptr 0Ch .text:080485A8 envp = dword ptr 10h .text:080485A8 .text:080485A8 ; __unwind { .text:080485A8 lea ecx, [esp+4] .text:080485AC and esp, 0FFFFFFF0h .text:080485AF push dword ptr [ecx-4] .text:080485B2 push ebp .text:080485B3 mov ebp, esp .text:080485B5 push ecx .text:080485B6 sub esp, 14h .text:080485B9 sub esp, 4 .text:080485BC push 21h ; '!' ; n .text:080485BE push 0 ; c .text:080485C0 push offset user_input ; s .text:080485C5 call _memset .text:080485CA add esp, 10h .text:080485CD sub esp, 0Ch .text:080485D0 push offset aEnterThePasswo ; "Enter the password: " .text:080485D5 call _printf .text:080485DA add esp, 10h .text:080485DD sub esp, 0Ch .text:080485E0 push offset unk_9FD92B8 .text:080485E5 push offset unk_9FD92B0 .text:080485EA push offset unk_9FD92A8 .text:080485EF push offset user_input .text:080485F4 push offset a8s8s8s8s ; "%8s %8s %8s %8s" .text:080485F9 call ___isoc99_scanf .text:080485FE add esp, 20h .text:08048601 mov [ebp+var_C], 0 .text:08048608 jmp short loc_8048637 .text:0804860A ; --------------------------------------------------------------------------- .text:0804860A .text:0804860A loc_804860A: ; CODE XREF: main+93↓j .text:0804860A mov eax, [ebp+var_C] .text:0804860D add eax, 9FD92A0h .text:08048612 movzx eax, byte ptr [eax] .text:08048615 movsx eax, al .text:08048618 sub esp, 8 .text:0804861B push [ebp+var_C] .text:0804861E push eax .text:0804861F call complex_function .text:08048624 add esp, 10h .text:08048627 mov edx, eax .text:08048629 mov eax, [ebp+var_C] .text:0804862C add eax, 9FD92A0h .text:08048631 mov [eax], dl .text:08048633 add [ebp+var_C], 1 .text:08048637 .text:08048637 loc_8048637: ; CODE XREF: main+60↑j .text:08048637 cmp [ebp+var_C], 1Fh .text:0804863B jle short loc_804860A .text:0804863D sub esp, 4 .text:08048640 push 20h ; ' ' ; n .text:08048642 push offset s2 ; "THNJXTHBJUCDIMEEMLZNGMHISXAIXDQG" .text:08048647 push offset user_input ; s1 .text:0804864C call _strncmp .text:08048651 add esp, 10h .text:08048654 test eax, eax .text:08048656 jz short loc_804866A .text:08048658 sub esp, 0Ch .text:0804865B push offset s ; "Try again." .text:08048660 call _puts .text:08048665 add esp, 10h .text:08048668 jmp short loc_804867A .text:0804866A ; --------------------------------------------------------------------------- .text:0804866A .text:0804866A loc_804866A: ; CODE XREF: main+AE↑j .text:0804866A sub esp, 0Ch .text:0804866D push offset aGoodJob ; "Good Job." .text:08048672 call _puts .text:08048677 add esp, 10h .text:0804867A .text:0804867A loc_804867A: ; CODE XREF: main+C0↑j .text:0804867A mov eax, 0 .text:0804867F mov ecx, [ebp+var_4] .text:08048682 leave .text:08048683 lea esp, [ecx-4] .text:08048686 retn .text:08048686 ; } // starts at 80485A8 .text:08048686 main endp
思路跟上一题差不多,函数开始地址还是设在相应的位置0x08048601
然后是初始化符号位向量,且注意四个数据的地址,然后是一如既往的设置成功的路径和避免的路径。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 import angrimport claripyfrom Crypto.Util.number import long_to_bytesimport sysproject =angr.Project("E:\\LAB\\angr\\angr\program\\05_angr_symbolic_memory" ) start_address = 0x8048601 initial_state = project.factory.blank_state(addr=start_address) password0 = claripy.BVS('password0' , 8 * 8 ) password1 = claripy.BVS('password1' , 8 * 8 ) password2 = claripy.BVS('password2' , 8 * 8 ) password3 = claripy.BVS('password3' , 8 * 8 ) password0_address = 0x9FD92A0 initial_state.memory.store(password0_address, password0) password1_address = 0x9FD92A8 initial_state.memory.store(password1_address, password1) password2_address = 0x9FD92B0 initial_state.memory.store(password2_address, password2) password3_address = 0x9FD92B8 initial_state.memory.store(password3_address, password3) simulation = project.factory.simgr(initial_state) def is_successful (state ): stdout_output = state.posix.dumps(sys.stdout.fileno()) return 'Good Job' in str (stdout_output) def should_abort (state ): stdout_output = state.posix.dumps(sys.stdout.fileno()) return 'Try again' in str (stdout_output) simulation.explore(find=is_successful, avoid=should_abort) if simulation.found: solution_state = simulation.found[0 ] solution0 = solution_state.se.eval (password0) solution1 = solution_state.se.eval (password1) solution2 = solution_state.se.eval (password2) solution3 = solution_state.se.eval (password3) solution = ' ' .join(map ('{:x}' .format , [ solution0, solution1,solution2,solution3 ])) solution = " " .join(map (str , [solution0, solution1])) print ("The Answer is :{}" .format (solution)) else : print ("No Answer!" )
06_angr_symbolic_dynamic_memory
伪代码如上。
看一下程序逻辑,输入两个数据,经过处理,比较,相等则通过。要注意buffer两个数据是存放在堆区的。、
所以在
main函数部分汇编代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 .text:08048691 call ___isoc99_scanf .text:08048696 add esp, 10h .text:08048699 mov [ebp+var_C], 0 .text:080486A0 jmp short loc_8048706 .text:080486A2 ; --------------------------------------------------------------------------- .text:080486A2 .text:080486A2 loc_80486A2: ; CODE XREF: main+FE↓j .text:080486A2 mov edx, ds:buffer0 .text:080486A8 mov eax, [ebp+var_C] .text:080486AB lea ebx, [edx+eax] .text:080486AE mov edx, ds:buffer0 .text:080486B4 mov eax, [ebp+var_C] .text:080486B7 add eax, edx .text:080486B9 movzx eax, byte ptr [eax] .text:080486BC movsx eax, al .text:080486BF sub esp, 8 .text:080486C2 push [ebp+var_C] .text:080486C5 push eax .text:080486C6 call complex_function .text:080486CB add esp, 10h .text:080486CE mov [ebx], al .text:080486D0 mov edx, ds:buffer1 .text:080486D6 mov eax, [ebp+var_C] .text:080486D9 lea ebx, [edx+eax] .text:080486DC mov eax, [ebp+var_C] .text:080486DF lea edx, [eax+20h] .text:080486E2 mov ecx, ds:buffer1 .text:080486E8 mov eax, [ebp+var_C] .text:080486EB add eax, ecx .text:080486ED movzx eax, byte ptr [eax] .text:080486F0 movsx eax, al .text:080486F3 sub esp, 8 .text:080486F6 push edx .text:080486F7 push eax .text:080486F8 call complex_function .text:080486FD add esp, 10h .text:08048700 mov [ebx], al .text:08048702 add [ebp+var_C], 1 .text:08048706
再来看看complex的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 .text:080485A9 complex_function proc near ; CODE XREF: main+BA↓p .text:080485A9 ; main+EC↓p .text:080485A9 .text:080485A9 arg_0 = dword ptr 8 .text:080485A9 arg_4 = dword ptr 0Ch .text:080485A9 .text:080485A9 ; __unwind { .text:080485A9 push ebp .text:080485AA mov ebp, esp .text:080485AC sub esp, 8 .text:080485AF cmp [ebp+arg_0], 40h ; '@' .text:080485B3 jle short loc_80485BB .text:080485B5 cmp [ebp+arg_0], 5Ah ; 'Z' .text:080485B9 jle short loc_80485D5 .text:080485BB .text:080485BB loc_80485BB: ; CODE XREF: complex_function+A↑j .text:080485BB sub esp, 0Ch .text:080485BE push offset s ; "Try again." .text:080485C3 call _puts .text:080485C8 add esp, 10h .text:080485CB sub esp, 0Ch .text:080485CE push 1 ; status .text:080485D0 call _exit .text:080485D5 ; --------------------------------------------------------------------------- .text:080485D5 .text:080485D5 loc_80485D5: ; CODE XREF: complex_function+10↑j .text:080485D5 mov eax, [ebp+arg_0] .text:080485D8 lea ecx, [eax-41h] .text:080485DB mov edx, [ebp+arg_4] .text:080485DE mov eax, edx .text:080485E0 add eax, eax .text:080485E2 add eax, edx .text:080485E4 shl eax, 2 .text:080485E7 add eax, edx .text:080485E9 add ecx, eax .text:080485EB mov edx, 4EC4EC4Fh .text:080485F0 mov eax, ecx .text:080485F2 imul edx .text:080485F4 sar edx, 3 .text:080485F7 mov eax, ecx .text:080485F9 sar eax, 1Fh .text:080485FC sub edx, eax .text:080485FE mov eax, edx .text:08048600 imul eax, 1Ah .text:08048603 sub ecx, eax .text:08048605 mov eax, ecx .text:08048607 add eax, 41h ; 'A' .text:0804860A leave .text:0804860B retn .text:0804860B ; } // starts at 80485A9 .text:0804860B complex_function endp
本题跟上面一题脚本的主要区别在于,要设置数据保存的地址。而malloc是在堆区中分配的,而没有明确地址。故脚本中我们要指出地址来存放数据(随意指定,只要不在代码区等影响程序运行的地方就行)。以及需要指定数据在内存中是以小端序存储的(angr默认是大端序)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 import angrimport claripyfrom Crypto.Util.number import long_to_bytesimport sysproject =angr.Project("E:\\LAB\\angr\\angr\program\\06_angr_symbolic_dynamic_memory" ) start_addr = 0x08048699 initial_state = project.factory.blank_state(addr=start_addr) password0 = claripy.BVS('password0' , 64 ) password1 = claripy.BVS('password1' , 64 ) fake0_addr = 0x4444440 fake1_addr = 0x4444450 buffer0_addr = 0x09FD92AC buffer1_addr = 0x09FD92B4 initial_state.memory.store(buffer0_addr,fake0_addr,endness=project.arch.memory_endness) initial_state.memory.store(buffer1_addr,fake1_addr,endness=project.arch.memory_endness) initial_state.memory.store(fake0_addr, password0) initial_state.memory.store(fake1_addr, password1) simulation = project.factory.simgr(initial_state) def is_successful (state ): stdout_output = state.posix.dumps(sys.stdout.fileno()) return b'Good Job.' in stdout_output def should_abort (state ): stdout_output = state.posix.dumps(sys.stdout.fileno()) return b'Try again.' in stdout_output simulation.explore(find=is_successful, avoid=should_abort) if simulation.found: solution_state = simulation.found[0 ] solution0 = solution_state.se.eval (password0) solution1 = solution_state.se.eval (password1) solution = ' ' .join(map ('{:x}' .format , [ solution0, solution1 ])) print ("The Answer is :{}" .format (solution)) else : print ("No Answer!" )
07_angr_symbolic_file
伪代码整体逻辑为输入数据后,经过ignoerme处理,并保存在文件里MRXJ…txt,然后读取文件的数据,经过计算然后比较。
再看看ignoreme的伪代码
main函数部分汇编代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 .text:080488B2 push offset buffer .text:080488B7 push offset a64s ; "%64s" .text:080488BC call ___isoc99_scanf .text:080488C1 add esp, 10h .text:080488C4 sub esp, 8 .text:080488C7 push 40h ; '@' ; n .text:080488C9 push offset buffer ; int .text:080488CE call ignore_me .text:080488D3 add esp, 10h .text:080488D6 sub esp, 4 .text:080488D9 push 40h ; '@' ; n .text:080488DB push 0 ; c .text:080488DD push offset buffer ; s .text:080488E2 call _memset .text:080488E7 add esp, 10h .text:080488EA sub esp, 8 .text:080488ED push offset aRb ; "rb" .text:080488F2 push offset name ; "MRXJKZYR.txt" .text:080488F7 call _fopen .text:080488FC add esp, 10h .text:080488FF mov ds:fp, eax .text:08048904 mov eax, ds:fp .text:08048909 push eax ; stream .text:0804890A push 40h ; '@' ; n .text:0804890C push 1 ; size .text:0804890E push offset buffer ; ptr .text:08048913 call _fread .text:08048918 add esp, 10h .text:0804891B mov eax, ds:fp .text:08048920 sub esp, 0Ch .text:08048923 push eax ; stream .text:08048924 call _fclose .text:08048929 add esp, 10h .text:0804892C sub esp, 0Ch .text:0804892F push offset name ; "MRXJKZYR.txt" .text:08048934 call _unlink .text:08048939 add esp, 10h .text:0804893C mov [ebp+var_C], 0 .text:08048943 jmp short loc_8048972 .text:08048945 ; ----------------------------------------------------------------------
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 import angrimport claripyfrom Crypto.Util.number import long_to_bytesimport sysproject =angr.Project("E:\\LAB\\angr\\angr\program\\07_angr_symbolic_file" ) start_addr = 0x080488EA filename = 'MRXJKZYR.txt' symbolic_file_size_bytes = 64 password = claripy.BVS('password' , symbolic_file_size_bytes * 8 ) password_file = angr.SimFile(filename, content=password, size=symbolic_file_size_bytes) initial_state = project.factory.blank_state(addr=start_addr,fs{filename:password_file}) simulation = project.factory.simgr(initial_state) def is_successful (state ): stdout_output = state.posix.dumps(sys.stdout.fileno()) return b'Good Job.' in stdout_output def should_abort (state ): stdout_output = state.posix.dumps(sys.stdout.fileno()) return b'Try again.' in stdout_output simulation.explore(find=is_successful, avoid=should_abort) if simulation.found: solution_state = simulation.found[0 ] solution = long_to_bytes(solution_state.solver.eval (password)) print ("The Answer is :{}" .format (solution)) else : print ("No Answer!" )
08_angr_constraints
惯例伪代码
逻辑很简单输入数据经过变化后与passwd比较,不过是在函数中比较。
main部分汇编
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 .text:08048603 push offset aEnterThePasswo ; "Enter the password: " .text:08048608 call _printf .text:0804860D add esp, 10h .text:08048610 sub esp, 8 .text:08048613 push offset buffer .text:08048618 push offset a16s ; "%16s" .text:0804861D call ___isoc99_scanf .text:08048622 add esp, 10h .text:08048625 mov [ebp+var_C], 0 .text:0804862C jmp short loc_8048663 .text:0804862E ; --------------------------------------------------------------------------- .text:0804862E .text:0804862E loc_804862E: ; CODE XREF: main+B4↓j .text:0804862E mov eax, 0Fh .text:08048633 sub eax, [ebp+var_C] .text:08048636 mov edx, eax .text:08048638 mov eax, [ebp+var_C] .text:0804863B add eax, 804A050h .text:08048640 movzx eax, byte ptr [eax] .text:08048643 movsx eax, al .text:08048646 sub esp, 8 .text:08048649 push edx .text:0804864A push eax .text:0804864B call complex_function .text:08048650 add esp, 10h .text:08048653 mov edx, eax .text:08048655 mov eax, [ebp+var_C] .text:08048658 add eax, 804A050h .text:0804865D mov [eax], dl .text:0804865F add [ebp+var_C], 1 .text:08048663 .text:08048663 loc_8048663: ; CODE XREF: main+79↑j .text:08048663 cmp [ebp+var_C], 0Fh .text:08048667 jle short loc_804862E .text:08048669 sub esp, 8 .text:0804866C push 10h .text:0804866E push offset buffer .text:08048673 call check_equals_MRXJKZYRKMKENFZB .text:08048678 add esp, 10h .text:0804867B test eax, eax .text:0804867D jnz short loc_8048691 .text:0804867F sub esp, 0Ch .text:08048682 push offset s ; "Try again." .text:08048687 call _puts .text:0804868C add esp, 10h .text:0804868F jmp short loc_80486A1 .text:08048691 ; --------------------------------------------------------------------------- .text:08048691 .text:08048691 loc_8048691: ; CODE XREF: main+CA↑j .text:08048691 sub esp, 0Ch .text:08048694 push offset aGoodJob ; "Good Job." .text:08048699 call _puts .text:0804869E add esp, 10h
complex的代码跟刚开始两道差不多,就不放了
思路:
开始地址还是设在输入后调用complex前,0x8048625
然后构造输入,从后面的比较函数的参数来看,buffer是16(0x10)个字节,80比特大小。再者就是buffer的地址
然后是循环的计算,在8048669位置结束,从下一行开始,就是往检查函数传参了。
需要注意的是,在检查函数里,是一个一个字符进行比较,这样就会让程序每次执行if语句16次,然后计算该走的分支,如此一算就是2^16,这就会产生路径爆炸问题。故,通过添加约束条件来避免这个问题。
列出脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 import angrimport claripyfrom Crypto.Util.number import long_to_bytesimport sysproject =angr.Project("E:\\LAB\\angr\\angr\program\\08_angr_constraints" ) start_addr = 0x08048625 initial_state = project.factory.blank_state(addr=start_addr) buffer = claripy.BVS('buffer' , 16 *8 ) buffer_addr = 0x0804A050 initial_state.memory.store(buffer_addr, buffer) simulation = project.factory.simgr(initial_state) addr_to_check_constraint = 0x08048669 simulation.explore(find=addr_to_check_constraint) if simulation.found: solution_state = simulation.found[0 ] constrained_parameter_addr = 0x0804A050 constrained_parameter_size_bytes = 16 constrained_parameter_bitvector = solution_state.memory.load(constrained_parameter_addr, constrained_parameter_size_bytes) constrained_parameter_desired_value = 'MRXJKZYRKMKENFZB' constrained_expression = constrained_parameter_bitvector == constrained_parameter_desired_value solution_state.add_constraints(constrained_expression) solution = long_to_bytes(solution_state.se.eval (buffer)) print ("The ANSWER is :{}" .format (solution.decode("utf-8" ))) else : print ("No Answer!" )
09_angr_hooks
程序逻辑大致为先后输入两个数据,各自经过一系列变化,然后比较。
可以看到本题依旧有同样的检查函数,那么同上题一样,相同原理也会产生路径爆炸问题,本题如其名,我们通过angr的hook来避免路径爆炸,所以,我们钩取检查函数,定义一个自己的检查函数来作为替换。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 import angrimport claripyfrom Crypto.Util.number import long_to_bytesimport sysproject =angr.Project("E:\\LAB\\angr\\angr\program\\09_angr_hooks" ) initial_state = project.factory.entry_state() check_equals_called_address = 0x80486B3 instruction_to_skip_length = 0x5 @project.hook(check_equals_called_address, length=instruction_to_skip_length ) def skip_check_equals_ (state ): user_input_buffer_address = 0x804A054 user_input_buffer_length = 0x10 user_input_string = state.memory.load( user_input_buffer_address, user_input_buffer_length ) check_against_string = 'MRXJKZYRKMKENFZB' state.regs.eax = claripy.If( user_input_string == check_against_string, claripy.BVV(1 , 32 ), claripy.BVV(0 , 32 ) ) simulation = project.factory.simgr(initial_state) def is_successful (state ): stdout_output = state.posix.dumps(sys.stdout.fileno()) return 'Good Job' in str (stdout_output) def should_abort (state ): stdout_output = state.posix.dumps(sys.stdout.fileno()) return 'Try again' in str (stdout_output) simulation.explore(find=is_successful, avoid=should_abort) if simulation.found: solution_state = simulation.found[0 ] solution = solution_state.posix.dumps(sys.stdin.fileno()) print ("The ANSWER is :{}" .format (solution.decode("utf-8" ))) else : print ("No Answer!" )
10_angr_simprocedures
程序逻辑大同小异,不说了。
虽然说上一道和这一道没放汇编代码,但还是要看的,比如这道
可以看到检查函数被调用了多次,那么我们无法通过准确的地址来进行钩取,故,我们需要通过函数名来钩取。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 import angrimport claripyfrom Crypto.Util.number import long_to_bytesimport sysproject =angr.Project("E:\\LAB\\angr\\angr\program\\10_angr_simprocedures" ) initial_state = project.factory.entry_state() class ReplacementCheckEquals (angr.SimProcedure): def run (self, check_data_address, check_data_length ): check_input_string = self.state.memory.load( check_data_address, check_data_length ) check_against_string = 'MRXJKZYRKMKENFZB' return claripy.If(check_input_string == check_against_string, claripy.BVV(1 , 32 ), claripy.BVV(0 , 32 )) check_equals_symbol = 'check_equals_MRXJKZYRKMKENFZB' project.hook_symbol(check_equals_symbol, ReplacementCheckEquals()) simulation = project.factory.simgr(initial_state) def is_successful (state ): stdout_output = state.posix.dumps(sys.stdout.fileno()) return 'Good Job' in str (stdout_output) def should_abort (state ): stdout_output = state.posix.dumps(sys.stdout.fileno()) return 'Try again' in str (stdout_output) simulation.explore(find=is_successful, avoid=should_abort) if simulation.found: solution_state = simulation.found[0 ] solution = solution_state.posix.dumps(sys.stdin.fileno()) print ("The ANSWER is :{}" .format (solution.decode("utf-8" ))) else : print ("No Answer!" )
本题脚本与上一题整体差别不大。主要在函数钩取方式上。
11_angr_sim_scanf
程序逻辑很明了,将原有数据变化,然后输入两个数据(每个四字节)与变化的数据作比较。
查看汇编代码发现,有多次调用输入函数,并比较的代码。这就是本题的关键点。
其实跟上一题差不多,这次很多调用的是输入,不是检查,相同的方法,钩取输入函数。不过参数设置上需要注意,数据大小,以及将数据以小端形式保存到地址中。并且需要将两个输入数据设置为全局变量。(需要重新设置两个变量来存储,因为我们在函数中设置的只是自定义的输入函数的局部变量,要想传到main中,得设置全局变量)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 import angrimport claripyfrom Crypto.Util.number import long_to_bytesimport sysproject =angr.Project("E:\\LAB\\angr\\angr\program\\11_angr_sim_scanf" ) initial_state = project.factory.entry_state() class ReplacementScanf (angr.SimProcedure): def run (self, format_string, scanf0_address, scanf1_address ): scanf0 = claripy.BVS('scanf0' , 4 * 8 ) scanf1 = claripy.BVS('scanf1' , 4 * 8 ) self.state.memory.store(scanf0_address, scanf0, endness=project.arch.memory_endness) self.state.memory.store(scanf1_address, scanf1, endness=project.arch.memory_endness) self.state.globals ['solution0' ] = scanf0 self.state.globals ['solution1' ] = scanf1 scanf_symbol = '__isoc99_scanf' project.hook_symbol(scanf_symbol, ReplacementScanf()) simulation = project.factory.simgr(initial_state) def is_successful (state ): stdout_output = state.posix.dumps(sys.stdout.fileno()) return 'Good Job' in str (stdout_output) def should_abort (state ): stdout_output = state.posix.dumps(sys.stdout.fileno()) return 'Try again' in str (stdout_output) simulation.explore(find=is_successful, avoid=should_abort) if simulation.found: solution_state = simulation.found[0 ] stored_solutions0 = solution_state.globals ['solution0' ] stored_solutions1 = solution_state.globals ['solution1' ] solution0 = solution_state.se.eval (stored_solutions0) solution1 = solution_state.se.eval (stored_solutions1) print ("The ANSWER is :{}" .format (solution0)) print ("The ANSWER is :{}" .format (solution1)) else : print ("No Answer!" )
12_angr_veritesting
输入数据,经过祖传complex函数变化,然后作比较。
其实这个for循环和if语句加起来相当于一个检查函数,而且会执行2^32次,路径爆炸是必须的。
这题除了避免路径爆炸外,其余的跟刚开始几道题差不多。
放脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 import angrimport claripyfrom Crypto.Util.number import long_to_bytesimport sysproject =angr.Project("E:\\LAB\\angr\\angr\program\\12_angr_veritesting" ) initial_state = project.factory.entry_state() simulation = project.factory.simgr(initial_state,veritesting = True ) def is_successful (state ): stdout_output = state.posix.dumps(sys.stdout.fileno()) return 'Good Job.' in str (stdout_output) def should_abort (state ): stdout_output = state.posix.dumps(sys.stdout.fileno()) return 'Try again.' in str (stdout_output) simulation.explore(find = is_successful,avoid = should_abort) if simulation.found : solution_state = simulation.found[0 ] print (solution_state.posix.dumps(sys.stdin.fileno())) else : print ("No Answer!" )
本题主要是利用了project.factory.simgr() 函数提供的veritesting 参数来指定是否自动合并路径。
贴上大佬的简单原理解释
13_angr_static_binary
伪代码如上。也是祖传complex函数变化。
乍一看,跟第一题还很像。但是用第一题的脚本跑半天出不来。而且还报错。
点进printf,scanf这类函数可以发现,呈现的是函数的源代码,不再是之前的跳转命令。
这个程序是静态链接编译的,故才会包含printf,scanf这类libc函数的实现。
因此我们需要hook掉这些实现的libc函数,避免angr在这些libc函数里东跑西跑迷失。
本题的libc函数有printf,scanf,puts,还有一个glibc函数__libc_start_main
hook掉之后,脚本就跟第一题的一样了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 import angrimport claripyfrom Crypto.Util.number import long_to_bytesimport sysproject =angr.Project("E:\\LAB\\angr\\angr\program\\13_angr_static_binary" ) initial_state = project.factory.entry_state() simulation = project.factory.simgr(initial_state,veritesting = True ) project.hook(0x804ed40 , angr.SIM_PROCEDURES['libc' ]['printf' ]()) project.hook(0x804ed80 , angr.SIM_PROCEDURES['libc' ]['scanf' ]()) project.hook(0x804f350 , angr.SIM_PROCEDURES['libc' ]['puts' ]()) project.hook(0x8048d10 , angr.SIM_PROCEDURES['glibc' ]['__libc_start_main' ]()) def is_successful (state ): stdout_output = state.posix.dumps(sys.stdout.fileno()) return 'Good Job.' in str (stdout_output) def should_abort (state ): stdout_output = state.posix.dumps(sys.stdout.fileno()) return 'Try again.' in str (stdout_output) simulation.explore(find = is_successful,avoid = should_abort) if simulation.found : solution_state = simulation.found[0 ] print (solution_state.posix.dumps(sys.stdin.fileno())) else : print ("No Answer!" )
14_angr_shared_library
…….
这题还有一个对应的.so文件,这就需要用angr来动态链接了。
反编译的时候会报错,寄。
原因摘自其他大神博客
这是因为generate.py 里面有一个Bug ,在最后的一个gcc 编译命令因为-L 参数缺少了指定当前目录,导致在寻找lib14_angr_shared_library.so 的时候找到了系统库目录,所以gcc 抛出了这个找不到14_angr_shared_library: No such file or directory
的问题,代码修改如下:
1 2 3 4 5 with tempfile.NamedTemporaryFile(delete=False , suffix='.c' ) as temp: temp.write(c_code) temp.seek(0 ) - os.system('gcc -m32 -I . -L ' + '/' .join(output_file.split('/' )[0 :-1 ]) + ' -o ' + output_file + ' ' + temp.name + ' -l' + output_file.split('/' )[-1 ]) + os.system('gcc -m32 -I . -L . ' + '/' .join(output_file.split('/' )[0 :-1 ]) + ' -o ' + output_file + ' ' + temp.name + ' -l' + output_file.split('/' )[-1 ])
回到函数本身,这个validate是验证函数哈,但是不在v这个程序里,在.so文件动态链接着
所以我们用IDA打开.so文件,查看validate代码。
…祖传complex,没什么好说的。
这题考点在于这个动态链接。我们对这个.so文件进行符号执行。(加载文件时加载.so文件)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 from msilib.schema import Binaryimport angrimport claripyfrom Crypto.Util.number import long_to_bytesimport sysbase = 0x400000 project = angr.Project("E:\\LAB\\angr\\angr\program\\lib14_angr_shared_library.so" , load_options={ 'main_opts' : { 'custom_base_addr' : base } }) buf_pointer = claripy.BVV(0x03000000 , 32 ) validate_addr = base+0x6d7 init_state = project.factory.call_state(validate_addr, buf_pointer, claripy.BVV(8 , 32 )) flag = claripy.BVS("flag" , 8 *8 ) init_state.memory.store(buf_pointer, flag) good = base+0x783 simu = project.factory.simgr(init_state) simu.explore(find=good) if simu.found: solu_state = simu.found[0 ] solu_state.add_constraints(solu_state.regs.eax != 0 ) flag = solu_state.solver.eval (flag, cast_to=bytes ) print (flag) else : print ("No result!" )
15_angr_arbitrary_read
接下来重点看汇编代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 .text:080484C9 main proc near ; DATA XREF: _start+17↑o .text:080484C9 .text:080484C9 var_1C = byte ptr -1Ch .text:080484C9 s = dword ptr -0Ch .text:080484C9 var_4 = dword ptr -4 .text:080484C9 argc = dword ptr 8 .text:080484C9 argv = dword ptr 0Ch .text:080484C9 envp = dword ptr 10h .text:080484C9 .text:080484C9 ; __unwind { .text:080484C9 lea ecx, [esp+4] .text:080484CD and esp, 0FFFFFFF0h .text:080484D0 push dword ptr [ecx-4] .text:080484D3 push ebp .text:080484D4 mov ebp, esp .text:080484D6 push ecx .text:080484D7 sub esp, 24h .text:080484DA mov eax, try_again .text:080484DF mov [ebp+s], eax .text:080484E2 sub esp, 0Ch .text:080484E5 push offset aEnterThePasswo ; "Enter the password: " .text:080484EA call _printf .text:080484EF add esp, 10h .text:080484F2 sub esp, 4 .text:080484F5 lea eax, [ebp+var_1C] .text:080484F8 push eax .text:080484F9 push offset key .text:080484FE push offset aU20s ; "%u %20s" .text:08048503 call ___isoc99_scanf .text:08048508 add esp, 10h .text:0804850B mov eax, ds:key .text:08048510 cmp eax, 228BF7Eh .text:08048515 jz short loc_8048531 .text:08048517 cmp eax, 3AD516Ah .text:0804851C jnz short loc_8048542 .text:0804851E mov eax, try_again .text:08048523 sub esp, 0Ch .text:08048526 push eax ; s .text:08048527 call _puts .text:0804852C add esp, 10h .text:0804852F jmp short loc_8048553 .text:08048531 ; --------------------------------------------------------------------------- .text:08048531 .text:08048531 loc_8048531: ; CODE XREF: main+4C↑j .text:08048531 mov eax, [ebp+s] .text:08048534 sub esp, 0Ch .text:08048537 push eax ; s .text:08048538 call _puts .text:0804853D add esp, 10h .text:08048540 jmp short loc_8048553 .text:08048542 ; --------------------------------------------------------------------------- .text:08048542 .text:08048542 loc_8048542: ; CODE XREF: main+53↑j .text:08048542 mov eax, try_again .text:08048547 sub esp, 0Ch .text:0804854A push eax ; s .text:0804854B call _puts .text:08048550 add esp, 10h .text:08048553 .text:08048553 loc_8048553: ; CODE XREF: main+66↑j .text:08048553 ; main+77↑j .text:08048553 nop .text:08048554 mov eax, 0 .text:08048559 mov ecx, [ebp+var_4] .text:0804855C leave .text:0804855D lea esp, [ecx-4] .text:08048560 retn .text:08048560 ; } // starts at 80484C9 .text:08048560 main endp
输入的两个参数,key,v4,分别对应汇编代码中的s(var_c),var_1c,由此可见,两个参数相距0x10(16个字节大小,其实也就是说,var_1c(v4)是16个字节大小。)而输入会输入20个字节。必定会将key变量覆盖部分甚至全部。(其实%u就是占四个字节,这里key就刚好全部被覆盖了)
再会看程序逻辑,将key与0x228BF7E比较,相等则不输出tryagain,至此我们找到了输出s的方法
我们使用hook,钩取scanf
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 from msilib.schema import Binaryimport angrimport claripyfrom Crypto.Util.number import long_to_bytesimport sysbinary_path = "E:\\LAB\\angr\\angr\program\\15_angr_arbitrary_read" pro = angr.Project(binary_path) init_state = pro.factory.entry_state() class ReplaceScanf (angr.SimProcedure): def run (self, format_str, param0, param1 ): scanf0 = claripy.BVS("0" , 32 ) scanf1 = claripy.BVS("1" , 20 *8 ) for ch in scanf1.chop(bits=8 ): self.state.add_constraints(ch >= "A" , ch <= "Z" ) self.state.memory.store(param0, scanf0, endness=pro.arch.memory_endness) self.state.memory.store(param1, scanf1) self.state.globals ["solutions" ] = (scanf0, scanf1) scanf_sym = "__isoc99_scanf" pro.hook_symbol(scanf_sym, ReplaceScanf()) def check_puts (state ): puts_para = state.memory.load(state.regs.esp+4 , 4 , endness=pro.arch.memory_endness) if state.solver.symbolic(puts_para): good_str = 0x4D52584B copy_state = state.copy() copy_state.add_constraints(puts_para == good_str) if copy_state.satisfiable(): state.add_constraints(puts_para == good_str) return True else : return False else : return False def success (state ): puts_plt = 0x08048370 if state.addr == puts_plt: return check_puts(state) else : return False simu = pro.factory.simgr(init_state) simu.explore(find=success) if simu.found: solu_state = simu.found[0 ] (scanf0, scanf1) = solu_state.globals ["solutions" ] flag = str (solu_state.solver.eval (scanf0)).encode("utf-8" ) flag += b" " + solu_state.solver.eval (scanf1, cast_to=bytes ) print (flag) else : print ("No result!" )
16_angr_arbitrary_write
汇编
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 .text:08048569 ; int __cdecl main(int argc, const char **argv, const char **envp) .text:08048569 public main .text:08048569 main proc near ; DATA XREF: _start+17↑o .text:08048569 .text:08048569 input_buffer(s) = byte ptr -1Ch .text:08048569 target_buffer(dest) = dword ptr -0Ch .text:08048569 var_4 = dword ptr -4 .text:08048569 argc = dword ptr 8 .text:08048569 argv = dword ptr 0Ch .text:08048569 envp = dword ptr 10h .text:08048569 .text:08048569 ; __unwind { .text:08048569 lea ecx, [esp+4] .text:0804856D and esp, 0FFFFFFF0h .text:08048570 push dword ptr [ecx-4] .text:08048573 push ebp .text:08048574 mov ebp, esp .text:08048576 push ecx .text:08048577 sub esp, 24h .text:0804857A mov [ebp+target_buffer], offset unimportant_buffer .text:08048581 sub esp, 4 .text:08048584 push 10h ; n .text:08048586 push 0 ; c .text:08048588 lea eax, [ebp+input_buffer] .text:0804858B push eax ; s .text:0804858C call _memset ; 清空input_buffer 的内容 .text:08048591 add esp, 10h .text:08048594 sub esp, 4 .text:08048597 push 0Ch ; n .text:08048599 push offset src ; "PASSWORD" .text:0804859E push offset password_buffer ; dest .text:080485A3 call _strncpy ; 复制PASSWORD 到全局内存password_buffer .text:080485A8 add esp, 10h .text:080485AB sub esp, 0Ch .text:080485AE push offset aEnterThePasswo ; "Enter the password: " .text:080485B3 call _printf .text:080485B8 add esp, 10h .text:080485BB sub esp, 4 .text:080485BE lea eax, [ebp+input_buffer] .text:080485C1 push eax .text:080485C2 push offset check_key .text:080485C7 push offset aU20s ; "%u %20s" .text:080485CC call ___isoc99_scanf ; scanf("%u %20s",check_key,input_buffer) .注意input_buffer 的大小是20 字节,栈上的input_buffer 默认的大小是16 字节,最后4 字节可以覆盖target_buffer . .text:080485D1 add esp, 10h .text:080485D4 mov eax, ds:check_key .text:080485D9 cmp eax, 1A25D71h .text:080485DE jz short loc_80485E9 .text:080485E0 cmp eax, 1CB7D43h .text:080485E5 jz short loc_8048601 ; 根据check_key 的输入来跳转到不同的_strncpy .text:080485E7 jmp short loc_8048618 .text:080485E9 ; --------------------------------------------------------------------------- .text:080485E9 .text:080485E9 loc_80485E9: ; CODE XREF: main+75↑j .text:080485E9 sub esp, 4 .text:080485EC push 10h ; n .text:080485EE lea eax, [ebp+input_buffer] .text:080485F1 push eax ; src .text:080485F2 push offset unimportant_buffer ; dest .text:080485F7 call _strncpy .text:080485FC add esp, 10h .text:080485FF jmp short loc_804862E .text:08048601 ; --------------------------------------------------------------------------- .text:08048601 .text:08048601 loc_8048601: ; CODE XREF: main+7C↑j .text:08048601 mov eax, [ebp+target_buffer] ; 注意这个是MOV 指令,意思是获取EBP + target_buffer 这个地址的内容保存到EAX 中 .text:08048604 sub esp, 4 .text:08048607 push 10h ; n .text:08048609 lea edx, [ebp+input_buffer] ; 注意这个是LEA 指令,意思是计算出EBP + input_buffer 的地址保存到EBX 中 .text:0804860C push edx ; src .text:0804860D push eax ; dest .text:0804860E call _strncpy ; 漏洞点在这里,strncpy(*target_buffer,input_buffer) ,也就是说input_buffer 最后四字节可以控制对任意地址的_strncpy() .总结起来就是strncpy(input_buffer[ -4 : ],input_buffer,0x10) . .text:08048613 add esp, 10h .text:08048616 jmp short loc_804862E .text:08048618 ; --------------------------------------------------------------------------- .text:08048618 .text:08048618 loc_8048618: ; CODE XREF: main+7E↑j .text:08048618 sub esp, 4 .text:0804861B push 10h ; n .text:0804861D lea eax, [ebp+input_buffer] .text:08048620 push eax ; src .text:08048621 push offset unimportant_buffer ; dest .text:08048626 call _strncpy .text:0804862B add esp, 10h .text:0804862E .text:0804862E loc_804862E: ; CODE XREF: main+96↑j .text:0804862E ; main+AD↑j .text:0804862E nop .text:0804862F sub esp, 4 .text:08048632 push 8 ; n .text:08048634 push offset key_string ; "KZYRKMKE" .text:08048639 push offset password_buffer ; s1 .text:0804863E call _strncmp ; 我们知道了上面有一个任意地址写之后,我们就需要改写key_string 或者password_buffer 一致,让_strncmp() 返回0 ,跳转到puts("Good Job") .text:08048643 add esp, 10h .text:08048646 test eax, eax .text:08048648 jz short loc_804865C .text:0804864A sub esp, 0Ch .text:0804864D push offset s ; "Try again." .text:08048652 call _puts .text:08048657 add esp, 10h .text:0804865A jmp short loc_804866C .text:0804865C ; --------------------------------------------------------------------------- .text:0804865C .text:0804865C loc_804865C: ; CODE XREF: main+DF↑j .text:0804865C sub esp, 0Ch .text:0804865F push offset aGoodJob ; "Good Job." .text:08048664 call _puts .text:08048669 add esp, 10h
这题,s和key的关系跟上题v4和key的关系一样。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 from msilib.schema import Binaryimport angrimport claripyfrom Crypto.Util.number import long_to_bytesimport sysbinary_path = "E:\\LAB\\angr\\angr\program\\16_angr_arbitrary_write" project= angr.Project(binary_path) initial_state = project.factory.entry_state() class ReplacementScanf (angr.SimProcedure): def run (self, format_string, check_key ,input_buffer ): scanf0 = claripy.BVS('scanf0' , 4 * 8 ) scanf1 = claripy.BVS('scanf1' , 20 * 8 ) for char in scanf1.chop(bits=8 ): self.state.add_constraints(char >= '0' , char <= 'z' ) self.state.memory.store(check_key, scanf0, endness=project.arch.memory_endness) self.state.memory.store(input_buffer, scanf1, endness=project.arch.memory_endness) self.state.globals ['solution0' ] = scanf0 self.state.globals ['solution1' ] = scanf1 scanf_symbol = '__isoc99_scanf' project.hook_symbol(scanf_symbol, ReplacementScanf()) def check_strncpy (state ): strncpy_dest = state.memory.load(state.regs.esp + 4 , 4 , endness=project.arch.memory_endness) strncpy_src = state.memory.load(state.regs.esp + 8 , 4 , endness=project.arch.memory_endness) strncpy_len = state.memory.load(state.regs.esp + 12 , 4 , endness=project.arch.memory_endness) src_contents = state.memory.load(strncpy_src, strncpy_len) if state.se.symbolic(strncpy_dest) and state.se.symbolic(src_contents) : if state.satisfiable(extra_constraints=(src_contents[ -1 : -64 ] == 'KZYRKMKE' ,strncpy_dest == 0x4D52584C )): state.add_constraints(src_contents[ -1 : -64 ] == 'KZYRKMKE' ,strncpy_dest == 0x4D52584C ) return True else : return False else : return False simulation = project.factory.simgr(initial_state) def is_successful (state ): strncpy_address = 0x8048410 if state.addr == strncpy_address: return check_strncpy(state) else : return False simulation.explore(find=is_successful) if simulation.found: solution_state = simulation.found[0 ] solution0 = solution_state.se.eval (solution_state.globals ['solution0' ]) solution1 = solution_state.se.eval (solution_state.globals ['solution1' ],cast_to=bytes ) print (solution0,solution1) else : print ("No result!" )
17_angr_arbitrary_jump
这么简单肯定出事儿,没有Goodjob。
汇编
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 .text:4D525886 ; int __cdecl main(int argc, const char **argv, const char **envp) .text:4D525886 public main .text:4D525886 main proc near ; DATA XREF: _start+17↑o .text:4D525886 .text:4D525886 var_C = dword ptr -0Ch .text:4D525886 var_4 = dword ptr -4 .text:4D525886 argc = dword ptr 8 .text:4D525886 argv = dword ptr 0Ch .text:4D525886 envp = dword ptr 10h .text:4D525886 .text:4D525886 ; __unwind { .text:4D525886 lea ecx, [esp+4] .text:4D52588A and esp, 0FFFFFFF0h .text:4D52588D push dword ptr [ecx-4] .text:4D525890 push ebp .text:4D525891 mov ebp, esp .text:4D525893 push ecx .text:4D525894 sub esp, 14h .text:4D525897 mov [ebp+var_C], 0 .text:4D52589E sub esp, 0Ch .text:4D5258A1 push offset aEnterThePasswo ; "Enter the password: " .text:4D5258A6 call _printf .text:4D5258AB add esp, 10h .text:4D5258AE call read_input ; .text:4D5258B3 sub esp, 0Ch .text:4D5258B6 push offset aTryAgain ; "Try again." .text:4D5258BB call _puts .text:4D5258C0 add esp, 10h .text:4D5258C3 mov eax, 0 .text:4D5258C8 mov ecx, [ebp+var_4] .text:4D5258CB leave .text:4D5258CC lea esp, [ecx-4] .text:4D5258CF retn
good job
1 2 3 4 5 6 7 8 9 10 11 12 13 14 .text:4D525849 print_good proc near .text:4D525849 ; __unwind { .text:4D525849 push ebp .text:4D52584A mov ebp, esp .text:4D52584C sub esp, 8 .text:4D52584F sub esp, 0Ch .text:4D525852 push offset s ; "Good Job." .text:4D525857 call _puts .text:4D52585C add esp, 10h .text:4D52585F sub esp, 0Ch .text:4D525862 push 0 ; status .text:4D525864 call _exit .text:4D525864 ; } // starts at 4D525849 .text:4D525864 print_good endp
reaad_input
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 read_input proc near ; CODE XREF: main+28↓p .text:4D525869 .text:4D525869 var_2B = byte ptr -2Bh .text:4D525869 .text:4D525869 ; __unwind { .text:4D525869 push ebp .text:4D52586A mov ebp, esp .text:4D52586C sub esp, 38h .text:4D52586F sub esp, 8 .text:4D525872 lea eax, [ebp+var_2B] .text:4D525875 push eax .text:4D525876 push offset format ; "%s" .text:4D52587B call ___isoc99_scanf .text:4D525880 add esp, 10h .text:4D525883 nop .text:4D525884 leave .text:4D525885 retn .text:4D525885 ; } // starts at 4D525869 .text:4D525885 read_input endp
这道题和上面两道题其实是有相似之处的,输入的长度过长会造成溢出,会覆盖某些东西。
这个函数是当输入足够长时造成的栈溢出会覆盖retn的命令。
其实思路也已经出来了
我们hook输入函数,将输入的参数字节扩大,让它不覆盖retn
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 from msilib.schema import Binaryimport angrimport claripyfrom Crypto.Util.number import long_to_bytesimport sysbinary_path = "E:\\LAB\\angr\\angr\program\\17_angr_arbitrary_jump" project= angr.Project(binary_path) initial_state = project.factory.entry_state() simulation = project.factory.simgr( initial_state, save_unconstrained=True , stashes={ 'active' : [initial_state], 'unconstrained' : [], 'found' : [], 'not_needed' : [] } ) class ReplacementScanf (angr.SimProcedure): def run (self, format_string, input_buffer_address ): input_buffer = claripy.BVS('input_buffer' , 64 * 8 ) for char in input_buffer.chop(bits=8 ): self.state.add_constraints(char >= '0' , char <= 'z' ) self.state.memory.store(input_buffer_address, input_buffer, endness=project.arch.memory_endness) self.state.globals ['solution' ] = input_buffer scanf_symbol = '__isoc99_scanf' project.hook_symbol(scanf_symbol, ReplacementScanf()) while (simulation.active or simulation.unconstrained) and (not simulation.found): for unconstrained_state in simulation.unconstrained: def should_move (s ): return s is unconstrained_state simulation.move('unconstrained' , 'found' , filter_func=should_move) simulation.step() if simulation.found: solution_state = simulation.found[0 ] solution_state.add_constraints(solution_state.regs.eip == 0x4D525849 ) solution = solution_state.se.eval (solution_state.globals ['solution' ],cast_to = bytes ) print (solution)
总结 丫的,终于写完了
感觉这个实验写完,能入个门,更多的还是得多多琢磨官方文档。