gdb常用命令:
- Run :跑一遍程序
- Start :通过 gdb 内部识别程序的入口点(没有反调试程序下),并下断点,给出反编译指令
- i:查看内部(insight) I r 就是查看寄存器 ,I b 就是查看断点
- Disassemble 某个函数名字(地址):反编译
Disassemble $rip :寄存器内容看作地址,反编译
Set disassembly-flavor intel :设置x86下的汇编,再反编译即可 - B 设置断点 ,使用方式: b *地址
- Ni 下一条指令 (单步执行)
- D (number of interrupt) 删除某断点
- Disable b (number of interrupt)让某断点失效
- Finish (步出),执行完某个函数
- Print $rip 打印某个寄存器的值
- X :
/20gx :16进制(x),g是8个字节为一组
/20i $rip : 从 $rip 开始,反编译 20 行
/20b $rip:从某个地址,一个一个字节去看内存的值,20个字节
注:x命令,组间地址是从左到右递增的,但是组内地址是从右到左由低变高。什么意思?x/20gx :8字节为一组,从左到右每一组地址递增,但是一组间(8字节)地址却从右到左递增 -
如果是x/20wx:则是4个字节为一组,以此类推
-
- P :显示内容,即print
- Set 赋值指令:set * 地址=值 ; set $rip=值
-
寄存器:
Eflags : 为了记住,直接贴图
-
-
Eflags 下的跳转指令:
-
-
-
关于 64位 对 32 位的兼容:
老配方,rax 前32位就是 eax,以此类推
-
-
编译器视角下的寄存器:一段程序,无论定义多少变量。在使用这些变量时,编译出来的机器码(汇编),多数情况会用寄存器来操作这些变量,很少会放进进程的虚拟内存中,因为慢(涉及到分页过程,从cpu到内存,再从内存到寄存器)。
汇编指令:
- Lea : 以前是用来载入偏移地址到某个寄存器中,现在的编译器(升级)把它当成一条计算指令,即 lea rax,[rbp-0x18] 等于 把rbp减去0x18 再赋值给 rax。这些变化,习惯就好。
- JNE
-
其中,cmp al,0x61 让al 与 0x61相减,结果保留在 ZF 位(是否相等)、CF位(看谁大谁小);jne 0x4007bc (如果ZF位不等于0,则跳转到该地址)
其中,源码为 if (b[0]='a'){ fun (sh);} else { return 0;}
Cmp 与 sub 的区别:前者不会赋值只作比较,后者会将结果赋给寄存器
-
-
- Movzx eax,byte ptr [rbp-0x10]
-
BYTE WORD DWORD QWORD
8 位 16 位 32 位 64 位
ptr : 指针 ,意味着要去 [rbp-0x10]这个地址取内容,放入eax中 - Test 与 and 区别:前者不会修改内容,只作计算,后者会
-
练习:
-
-
练习二:不可见字符
-
不可见字符:shell 中输入的字符都是可见字符,但是程序中的(cmp al,0x10 ) :0x10 是不可见字符,怎么办?
- 直接修改内存,将$rbp-0x10 处地址的值修改为0x10
- 修改rax寄存器
- 在shell终端输入不可见字符是不行的,需要利用py脚本
-
练习三:指针跳转
代码逻辑:
if([rdp-0x10]==0){
Goto <main+92>;
Else{
Rdx = [rdp-0x10];
Goto Rdx;
}
按照逻辑,查找getshell的函数地址,赋值给rdx
Set $rdx = func -
并没有跳转到func,发现是源码有问题:
把 system(cmd)修改为 system(sh),编译后再试一下。
同样的操作,让程序断在 call edx 时,直接修改rdx