gdb, gdb! 还记得大二在B站看jyy的操作系统课的时候,他用gdb浇灭了我的黑客梦。
Debugging Refresher level1 直接run一下,再continue一下就好了,gdb的简单使用
level2 1 2 3 4 5 6 7 8 9 (gdb) p/x $r12 $2 = 0 x18215a7f83c98b8e (gdb) c Continuing. Random value: 0 x18215a7f83c98b8e You input: 18215 a7f83c98b8e The correct answer is: 18215 a7f83c98b8e You win! Here is your flag: pwn.college{ETe5riLiwE1X6xz1M0yCPK4-4 WH.0 VN0IDLxYTN1YzW}
level3 通过在read处打断点,然后执行到read函数时,查看寄存器信息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 (gdb) info registers rax 0x7fff17b9e998 140733591447960 rbx 0x5f31bbc71d10 104667208424720 rcx 0x3 3 rdx 0x8 8 rsi 0x7fff17b9e998 140733591447960 rdi 0x3 3 rbp 0x7fff17b9e9b0 0x7fff17b9e9b0 rsp 0x7fff17b9e968 0x7fff17b9e968 r8 0x3d 61 r9 0x2c 44 r10 0x0 0 r11 0x246 582 r12 0x5f31bbc712a0 104667208422048 r13 0x7fff17b9eaa0 140733591448224 r14 0x0 0 r15 0x0 0 rip 0x5f31bbc71210 0x5f31bbc71210 <read@plt> eflags 0x202 [ IF ]
rsi
作为read系统调用的buf参数,所以在随机数被set后,读取这个地址的值即可。(我发现不能断点给到read函数,因为从main中执行到read的时候,有其他函数会调用read,然后就会导致它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 (gdb) c Continuing. The random value has been set ! Program /breakpoint trap.0x00005f31bbc71c64 in main () (gdb) x / 0x7fff17b9e998: 0xd81c082e 0x7fff17b9e9a8: 0x34e94700 (gdb) c Continuing. Random You The You Breakpoint (gdb) c Continuing. pwn.college{s102MI2GzaSB-30x2SjuMIlWzDy.0lN0IDLxYTN1YzW} [Inferior
没毛病!至于为什么这个随机值是反着的,存储的是小端序,手动改成大端序才行。而且输出是以4字节为单位的,所以只需要将八字节反向一下即可。类似地,以一个字节为单位输出,也可以手动转换
1 2 3 (gdb) x /8x 0x7ffccd2aa938 0x7ffccd2aa938 : 0x30 0xe5 0x4d 0x5b 0xce 0xe0 0x9a 0x51 (gdb) 519ae0ce5b4de530
**PS:**在后续的学习中,发现可以使用x/gx 以8字节为单位输出
level4 我也不知道是不是我第三关的方法用错了,第四关就是使用第三关的方法,一直重复,一直读取read系统调用时rsi
寄存器的那个地址,并且在scanf
系统调用前打个断点,这样方便我们读取这个随机值。重复5次还是几次就能拿到flag了。
level5 使用gdb脚本
1 2 3 4 5 6 7 8 9 10 11 start break *main+704 commands set $rsi_buf = $rsi printf "buf_addr value: %llx\n" , $rsi_buf end break *main+752 commands set $local_variable = *(unsigned long long*)($rsi_buf ) printf "Random value: %llx\n" , $local_variable end
呃,这个脚本不是完全的自动化,但是手动复制输入给程序,重复几次后就能获得到flag了。但是实际上应该是可以做到自动化输入的,或许可以借助python脚本来写,或者直接改内存。
level6 改寄存器的值做不到哇,scanf
函数是通过调用多次read
系统调用,每次都只写入1个字节
我麻了,尝试那么多次。实际上应该跳过scanf,而不是重定向标准输入。只需要获取scanf所读取的内存地址,在这个地址将那个随机值放进去,然后控制rip
寄存器,跳过scanf就好了。用一下exp会出现一个问题:
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 runbreak *main+572 commands silent set $rsi_buf = $rsi printf "buf_addr value: %llx\n" , $rsi_buf continue end break *main+606 commands silent set $local_variable = *(uint64_t*)($rsi_buf ) printf "Random value: %llx\n" , $local_variable set $rbp set $rip =$rip + 0 x18 continue end break *main+637 commands ptype $rsi ptype $local_variable set $rsi =$local_variable info registers continue end
我仅仅修改了printf的内存,导致它输出的内容是一致的,但是后续的cmp指令所比较的,还是取的rbp-0x18
地址的内容,这个内容我并没有修改,所以认定为不匹配。我直接修改rbp-0x18
地址的内容为这个随机值即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 runbreak *main+572 commands silent set $rsi_buf = $rsi continue end break *main+606 commands silent set $local_variable = *(uint64_t*)($rsi_buf ) set $rbp set $rip =$rip + 0 x18 continue end break *main+630 commands silent set *(uint64_t*)($rbp -0x10 ) = $local_variable continue end
OK,自动化成功!
level7 1 2 3 (gdb) call (void)win () You win! Here is your flag: pwn.college{kS6uX4HwFALgoHPYfILQokDmQwX.0FM1IDLxYTN1YzW }
gdb直接可以调用函数,其实在前面的关卡,我看到有个win函数,就修改过rip寄存器跳到win函数,直接能够获取flag。不过还是一步步做收获大些。毕竟这flag啥也不是
level8 因为程序是被损坏了,但是我认为open
,read
等系统调用应该都挺重要的,所以直接jump过去执行,单步执行来到write
后就拿到flag了。
入口是从win+47
位置开始的。