exeinfo 可以看到壳信息
upx -d upx脱壳
编译x86程序依赖
sudo apt-get install -y build-essential module-assistant gcc-multilib g++-multilib
搜索shellcode
gdb-peda$ shellcode search sh
Connecting to shell-storm.org...
Found 289 shellcodes
ScId Title
[132] Aix - execve /bin/sh - 88 bytes
[134] Alpha - /bin/sh - 80 bytes
[107] BSD/ppc - execve(/bin/sh) - 128 bytes
[814] BSD/x86 - setreuid(geteuid(), geteuid()) and execve(/bin/sh, /bin/sh, 0)
[92] BSD/x86 - execve(/bin/sh) & setuid(0) - 29 bytes
[601] BSD/x86 - bindshell on port 2525 - 167 bytes
[362] BSD/x86 - execve /bin/sh Crypt /bin/sh - 49 bytes
[93] BSD/x86 - execve(/bin/sh) - 27 bytes
[144] Cisco IOS - Connectback shellcode v1.0
[142] Cisco IOS - Tiny shellcode v1.0
[143] Cisco IOS - Bind shellcode v1.0
[131] Sco/x86 - execve(/bin/sh, ..., NULL) - 43 bytes
[106] FreeBSD/x86-64 - exec(/bin/sh) Shellcode - 31 bytes
查看 shellcode
gdb-peda$ shellcode display 98
Connecting to shell-storm.org...
; sm4x - 2008
; reverse portbind /bin/sh
; NULL free if address is.
; setuid(0); socket(); connect(); exit();
; 89 bytes
; FreeBSD 7.0-RELEASE
global _start
_start:
xor eax, eax
; --- setuid(0)
push eax
push eax
mov al, 0x17
push eax
int 0x80
socat tcp-l:4000,fork exec:./pwn
nc 127.0.0.1 4000
libc查询 http://libcdb.com/
查找到gadget
objdump -d babypwn4 | egrep "ret|pop"
linux下shellcode的编写
windows shellcode
# include <stdlib.h>
# include <stdio.h>
# include <string.h>
# include <windows.h>
int main(void) {
char *shellcode =
"\x33\xc9\x64\x8b\x49\x30\x8b\x49\x0c\x8b"
"\x49\x1c\x8b\x59\x08\x8b\x41\x20\x8b\x09"
"\x80\x78\x0c\x33\x75\xf2\x8b\xeb\x03\x6d"
"\x3c\x8b\x6d\x78\x03\xeb\x8b\x45\x20\x03"
"\xc3\x33\xd2\x8b\x34\x90\x03\xf3\x42\x81"
"\x3e\x47\x65\x74\x50\x75\xf2\x81\x7e\x04"
"\x72\x6f\x63\x41\x75\xe9\x8b\x75\x24\x03"
"\xf3\x66\x8b\x14\x56\x8b\x75\x1c\x03\xf3"
"\x8b\x74\x96\xfc\x03\xf3\x33\xff\x57\x68"
"\x61\x72\x79\x41\x68\x4c\x69\x62\x72\x68"
"\x4c\x6f\x61\x64\x54\x53\xff\xd6\x33\xc9"
"\x57\x66\xb9\x33\x32\x51\x68\x75\x73\x65"
"\x72\x54\xff\xd0\x57\x68\x6f\x78\x41\x01"
"\xfe\x4c\x24\x03\x68\x61\x67\x65\x42\x68"
"\x4d\x65\x73\x73\x54\x50\xff\xd6\x57\x68"
"\x72\x6c\x64\x21\x68\x6f\x20\x57\x6f\x68"
"\x48\x65\x6c\x6c\x8b\xcc\x57\x57\x51\x57"
"\xff\xd0\x57\x68\x65\x73\x73\x01\xfe\x4c"
"\x24\x03\x68\x50\x72\x6f\x63\x68\x45\x78"
"\x69\x74\x54\x53\xff\xd6\x57\xff\xd0";
DWORD why_must_this_variable;
BOOL ret = VirtualProtect(
shellcode,
strlen(shellcode),
PAGE_EXECUTE_READWRITE,
&why_must_this_variable
);
if (!ret) {
printf("VirtualProtect\n");
return 0;
}
//printf("strlen(shellcode)=%d\n", strlen(shellcode));
((void (*)(void)) shellcode)();
return 0;
}
pwn全景图
求余数运算反汇编,两者等价。
v5 = 3 * v0 - 0X30 * ((unsigned __int64)(0xAAAAAAAAAAAAAAABLL * (unsigned __int128)(3 * v0) >> 64) >> 5);
v5 = 3 * v0 % 0x30;
TITLE Program Template ; 标题可有可无
include irvine32.inc ; 包含头文件
.data
; 数据区,声明全局变量
val1 dword 10000h
val2 dword 40000h
val3 dword 20000h
finalVal dword ?
.code
; 代码区
main PROC
mov eax, val1 ; start with 10000h
add eax, val2 ; add 40000h
sub eax, val3 ; subtract 20000h
mov finalVal, eax ; store the result (30000h)
call DumpRegs ; display the registers
exit
main ENDP
funcName PROC
ret
funcName ENDP
END main
; 16 bits 8 bits 8 bits
eax ax ah al ; 累加器,是很多加、乘法指令的缺省寄存器
ebx bx bh bl ; 基地址寄存器,在内存寻址时存放基地址
ecx cx ch cl ; 计数器,是 rep 和 loop 的内定计数器
edx dx ; 总是用来放整数除法产生的余数
esp sp ; 栈顶指针
ebp bp ; 基址指针
edi di ; 目的变址
esi si ; 源变址
eip ip ; 指令指针,指向下一条指令的内存地址
eflags flags ; 标志
var db ? ; 声明一个字节,未初始化
var db 64 ; 声明一个字节,初始值为 64
db 10 ; 声明一个没有 label 的字节,值为 10
var dw ? ; 2 字节
var dd 40 ; 4 字节
arr dd 1, 2, 3 ; 数组,初始值为 1, 2, 3
arr db 10 dup(?) ; 10个元素的数组,未初始化
arr dd 100 dup(0) ; 100 个元素,初始化为 0
str db 'hello',0 ; 字符串,注意 0 结尾
mov eax, [ebx] ; 将 ebx 值指示的内存地址中的 4 个字节传送到 eax 中
mov [var], ebx ; 将 ebx 的内容传送到 var 值指示的内存地址中
movsx dest, src ; 8 / 16 bits => 16 / 32 bits,扩展传送
mov
mov <reg>, <reg>
mov <reg>, <mem>
mov <mem>, <reg>
mov <reg>, <const>
mov <mem>, <const>
mov byte ptr [var], 5
push
push <reg32>
pop
lea
Load Effective Address
lea <reg32>, <mem>
lea eax, [var] ;将地址 var 放入寄存器 eax 中
lea edi, [ebx+4*esi] ;edi = ebx+4*esi
; 某些编译器会使用 lea 指令来进行算数运算
leave
在32位汇编下相当于:
mov esp,ebp;//将ebp指向(ebp内部应当保存一个地址,所谓指向即这个地址对应的空间)的值赋给esp
pop ebp
leave指令将EBP寄存器的内容复制到ESP寄存器中,以释放分配给该过程的所有堆栈空间。
然后,它从堆栈恢复EBP寄存器的旧值。
ret
push src ; 将 src 的数据存入栈中,不允许使用立即数寻址方式。
pushad ;通用寄存器全入栈
pop dst ; 用 dst 接收出栈数据,不能使用 cs 段寄存器
popad ; 通用寄存器依次出栈
dumpregs ; 显示所有寄存器信息
dumpmem ; 显示所有内存信息
inc reg/mem ; ++
dec reg/mem ; --
xlat ; 换码指令,默认 al 寄存器?
xchg reg, mem/reg ; 交换两操作数内容
; 两操作数中必须有一个在寄存器中
; 操作数不能为段寄存器和立即数
; 源和目的操作数类型要一致
shl opr, cnt ; 逻辑左移 cnt 位
shr opr, cnt
sal opr, cnt ; 算术左移,同逻辑左移
sar opr, cnt
rol opr, cnt ; 循环左移
ror opr, cnt
call writestring ; 显示 edx 中的值
readstring buffer ; 输入字符串存到 buffer?
; 输入输入函数
readint
; 返回值:cf = 0 => 输入存放在 eax 中,cf = 1 => 输入无效
writeint
; 输出 eax 中的整数
; 类型 ptr 内存操作数或标号
cmp dword ptr [edx + 4*esi], 8 ; 强转后直接能比较
rep stosed ; 重复?
; 函数写完了记得 ret
sub esp, 8
add esp 12 ; 注意回收栈中分配空间
adc ; 进位加法,从进位标志寄存器中读值
jmp ;无条件
j[condition] ;条件跳转
cmp
call
ret
JE ;等于则跳转
JNE ;不等于则跳转
JZ ;为 0 则跳转
JNZ ;不为 0 则跳转
JS ;为负则跳转
JNS ;不为负则跳转
JC ;进位则跳转
JNC ;不进位则跳转
JO ;溢出则跳转
JNO ;不溢出则跳转
JA ;无符号大于则跳转
JNA ;无符号不大于则跳转
JAE ;无符号大于等于则跳转
JNAE ;无符号不大于等于则跳转
JG ;有符号大于则跳转
JNG ;有符号不大于则跳转
JGE ;有符号大于等于则跳转
JNGE ;有符号不大于等于则跳转
JB ;无符号小于则跳转
JNB ;无符号不小于则跳转
JBE ;无符号小于等于则跳转
JNBE ;无符号不小于等于则跳转
JL ;有符号小于则跳转
JNL ;有符号不小于则跳转
JLE ;有符号小于等于则跳转
JNLE ;有符号不小于等于则跳转
JP ;奇偶位置位则跳转
JNP ;奇偶位清除则跳转
JPE ;奇偶位相等则跳转
JPO ;奇偶位不等则跳转
C 语言的奇技淫巧 https://jin-yang.github.io/post/program-c-tips.html
常用函数
sprintf();
静态全局变量:自动初始化为 0,只在声明它的整个文件中可见。
静态函数:只能在声明它的文件当中可见,不能被其他文件使用,降低命名冲突。
C++逆向学习三步走 https://bbs.pediy.com/thread-113689.htm
strings/file/binwalk/IDA
等静态分析工具收集信息,并根据这些静态信息进行google/github
搜索动态分析的目的在于定位关键代码后,在程序运行的过程中,借由输出信息(寄存器,内存变化,程序输出)等来验证自己的推断或是理解程序功能
主要方法有:调试,符号执行,污点分析
借用系统调用的跟踪工具看一下宏观动作?
如 Tea / XTea / XXTea / IDEA / RC4 / RC5 / RC6 / AES / DES / IDEA / MD5 / SHA256 / SHA1 等加密算法,大数加减乘除、最短路等传统算法
如图、树、哈希表等高级数据结构在汇编代码中的识别。
比如使用OLLVM
,movfuscator
,花指令
,虚拟化
及SMC
等工具技术对代码进行混淆,使得程序分析十分困难。
那么对应的也有反混淆技术,最主要的目的就是复原控制流。比如模拟执行
和符号执行
保护壳类型有许多,简单的压缩壳可以归类为如下几种
对于脱壳也有相关的方法,比如单步调试法
,ESP定律
等等
反调试意在通过检测调试器等方法避免程序被调试分析。比如使用一些 API 函数如IsDebuggerPresent
检测调试器,使用SEH异常处理
,时间差检测等方法。也可以通过覆写调试端口、自调试等方法进行保护。
非常规逆向题设计的题目范围非常之广,可以是任意架构的任意格式文件。
但是逆向工程的方法学里不惧怕这些未知的平台格式,遇到这样的非常规题,我们也有一些基本的流程可以通用
主要找文件解析工具
、反汇编器
、调试器
和反编译器
。其中反汇编器
是必需的,调试器
也包含有相应的反汇编功能,而对于反编译器
则要自求多福了,得之我幸失之我命。
找工具总结起来就是:Google 大法好。合理利用 Google 搜索语法,进行关键字搜索可以帮助你更快更好地找到合适工具。
字符串搜索法
栈回溯法
逐步分析法
学习环境 https://exploit.education/phoenix/stack-five
视频教程 https://www.youtube.com/watch?v=HSlhY4Uy8SA
手把手教你栈溢出从入门到放弃
https://zhuanlan.zhihu.com/p/25816426
https://zhuanlan.zhihu.com/p/25892385
ctf-wiki https://ctf-wiki.github.io/ctf-wiki/pwn/linux/stackoverflow/basic-rop-zh/#ret2text
Pwn入坑指南 https://www.cnblogs.com/wintrysec/p/10616856.html
熊师傅推荐的题库 https://github.com/scwuaptx/HITCON-Training
视频教程 入门学习路线 https://www.bilibili.com/video/av13427867
发生栈溢出的基本前提:
ebp(rbp) 用途:
常见 x86 调用约定:
需要注意的是,32 位和 64 位程序有以下简单的区别:
int callee(int a, int b, int c) {
return a + b + c;
}
int caller(void) {
int ret;
ret = callee(1, 2, 3);
ret += 4;
return ret;
}
pwndbg> disass callee
Dump of assembler code for function callee:
0x000004ed <+0>: push ebp
0x000004ee <+1>: mov ebp,esp
0x000004f0 <+3>: mov edx,DWORD PTR [ebp+0x8]
0x000004f3 <+6>: mov eax,DWORD PTR [ebp+0xc]
0x000004f6 <+9>: add edx,eax
0x000004f8 <+11>: mov eax,DWORD PTR [ebp+0x10]
0x000004fb <+14>: add eax,edx
0x000004fd <+16>: pop ebp
0x000004fe <+17>: ret
End of assembler dump.
pwndbg> disass caller
Dump of assembler code for function caller:
0x000004ff <+0>: push ebp
0x00000500 <+1>: mov ebp,esp
0x00000502 <+3>: sub esp,0x10
0x00000505 <+6>: push 0x3
0x00000507 <+8>: push 0x2
0x00000509 <+10>: push 0x1
0x0000050b <+12>: call 0x4ed <callee>
0x00000510 <+17>: add esp,0xc
0x00000513 <+20>: mov DWORD PTR [ebp-0x4],eax
0x00000516 <+23>: add DWORD PTR [ebp-0x4],0x4
0x0000051a <+27>: mov eax,DWORD PTR [ebp-0x4]
0x0000051d <+30>: leave
0x0000051e <+31>: ret
End of assembler dump.
修改返回地址,让其指向溢出数据中的一段指令(shellcode)
修改返回地址,让其指向内存中已有的某个函数(return2libc)
修改返回地址,让其指向内存中已有的一段指令(ROP)
修改某个被调用函数的地址,让其指向另一个函数(hijack GOT)
函数 | 基本介绍 |
---|---|
printf | 输出到 stdout |
fprintf | 输出到指定 FILE 流 |
vprintf | 根据参数列表格式化输出到 stdout |
vfprintf | 根据参数列表格式化输出到指定 FILE 流 |
sprintf | 输出到字符串 |
snprintf | 输出指定字节数到字符串 |
vsprintf | 根据参数列表格式化输出到字符串 |
vsnprintf | 根据参数列表格式化输出指定字节到字符串 |
setproctitle | 设置 argv |
syslog | 输出日志 |
err, verr, warn, vwarn 等 | …… |
Return Oriented Programming
$checksec fog
[*] '/mnt/hgfs/shared/other/pwn/fog/fog'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
NX enabled如果这个保护开启就是意味着栈中数据没有执行权限,以前的经常用的call esp或者jmp esp的方法就不能使用,但是可以利用rop这种方法绕过。
关闭:-z execstack
开启:-z noexecstack
开启了的话堆栈会变的可执行,可执行的话可能意味着可以执行shellcode
RELRO 会有 Partial RELRO 和F ULL RELRO,若开启 FULL RELRO,意味着无法修改got表,否则能劫持程序流程。
关闭:-z norelro
开启(部分):-z lazy
开启(完全):-z now
如果栈中开启 Canary found,那么就不能用直接用溢出的方法覆盖栈中返回地址,而是要通过改写指针与局部变量、leak canary、overwrite canary 的方法来绕过。
关闭:-fno-stack-protector
启用(只为局部变量中含有 char 数组的函数插入保护代码):-fstack-protector
启用(为所有函数插入保护代码):-fstack-protector-all
没开启的话,栈溢出会变的更加容易
Position Independent Executable 地址随机化。 没开启的话,程序的基地址就是已知的了(0x400000)
关闭: -no-pie
开启: -pie -fPIC
Linux 平台下还有地址空间分布随机化(ASLR)的机制,即使可执行文件开启了 PIE 保护,还需要系统开启 ASLR 才会真正打乱基址,否则程序运行时依旧会在加载一个固定的基址上(不过和 No PIE 时基址不同)。
我们可以通过修改 /proc/sys/kernel/randomize_va_space
来控制 ASLR 启动与否,具体的选项有:
sudo bash -c 'echo 0 > /proc/sys/kernel/randomize_va_space'
FORTIFY_SOURCE 机制对格式化字符串有两个限制
关闭:-D_FORTIFY_SOURCE=0
开启(只会在编译的时候检查):-D_FORTIFY_SOURCE=1 -O1
开启(强检查):-D_FORTIFY_SOURCE=2 -O2
开启的话就是不能用 %n,%hn,%hhn 这种的,%4$p 也不行了。
程序没有开启地址随机化:
def debug(addr):
raw_input('debug:')
gdb.attach(r, "b *" + addr)
在程序运行时调用这个函数就可以调试了
程序开启地址随机化:
wordSz = 4
hwordSz = 2
bits = 32
PIE = 0
mypid=0
def leak(address, size):
with open('/proc/%s/mem' % mypid) as mem:
mem.seek(address)
return mem.read(size)
def findModuleBase(pid, mem):
name = os.readlink('/proc/%s/exe' % pid)
with open('/proc/%s/maps' % pid) as maps:
for line in maps:
if name in line:
addr = int(line.split('-')[0], 16)
mem.seek(addr)
if mem.read(4) == "\x7fELF":
bitFormat = u8(leak(addr + 4, 1))
if bitFormat == 2:
global wordSz
global hwordSz
global bits
wordSz = 8
hwordSz = 4
bits = 64
return addr
log.failure("Module's base address not found.")
sys.exit(1)
def debug(addr = 0):
global mypid
mypid = proc.pidof(r)[0]
raw_input('debug:')
with open('/proc/%s/mem' % mypid) as mem:
moduleBase = findModuleBase(mypid, mem)
gdb.attach(r, "set follow-fork-mode parent\nb *" + hex(moduleBase+addr))
由于开启地址随机化之后ida pro打开程序后,显示的是程序的偏移地址,而不是实际的地址,当程序加载后程序的程序的实际地址是:基地址+偏移地址,调用debug函数的时候只要把偏移地址传递进去就好
【1】利用格式化字符串漏洞泄露栈中的数据,从而找到libc的某个函数地址,再利用libc-database来判断远程libc的版本,之后再计算出libc的基址,一般做题我喜欢找__libc_start_main
的地址
【2】利用write这个函数,pwntools有个很好用的函数DynELF去利用这个函数计算出程序的各种地址,包括函数的基地址,libc的基地址,libc中system的地址
【3】利用printf函数,printf函数输出的时候遇到0x00时候会停止输出,如果输入的时候没有在最后的字节处填充0x00,那么输出的时候就可能泄露栈中的重要数据,比如libc的某个函数地址
程序没有开启任何保护:
方法一:传统的教材思路是把shellcode写入栈中,然后查找程序中或者libc中有没有call esp或者jmp esp,比如这个题目: http://blog.csdn.net/niexinming/article/details/76893510
方法二:但是现代操作系统中libc中会开启地址随机化,所以先寻找程序中system的函数,再布局栈空间,调用gets(.bss),最后调用system(‘/bin/sh’) 比如这个题目:http://blog.csdn.net/niexinming/article/details/78796408
方法三:覆盖虚表方式利用栈溢出漏洞,这个方法是m4x师傅教我的方法,我觉得很巧妙,比如这个题目:http://blog.csdn.net/niexinming/article/details/78144301
开启nx之后栈和bss段就只有读写权限,没有执行权限了,所以就要用到rop这种方法拿到系统权限,如果程序很复杂,或者程序用的是静态编译的话,那么就可以使用ROPgadget这个工具很方便的直接生成rop利用链。有时候好多程序不能直接用ROPgadget这个工具直接找到利用链,所以就要手动分析程序来getshell了,比如这两个题目: http://blog.csdn.net/niexinming/article/details/78259866
开启canary后就不能直接使用普通的溢出方法来覆盖栈中的函数返回地址了,要用一些巧妙的方法来绕过或者利canary本身的弱点来攻击
【1】利用canary泄露flag,这个方法很巧妙的运用了canary本身的弱点,当__stack_check_fail
时,会打印出正在运行中程序的名称,所以,我们只要将__libc_argv[0]
覆盖为flag的地址就能将flag打印出来,比如这个题目: http://blog.csdn.net/niexinming/article/details/78522682
【2】利用printf函数泄露一个子进程的Canary,再在另一个子进程栈中伪造Canary就可以绕过Canary的保护了,比如这个题目:http://blog.csdn.net/niexinming/article/details/78681846
【1】利用printf函数尽量多打印一些栈中的数据,根据泄露的地址来计算程序基地址,libc基地址,system地址,比如这篇文章中echo2的wp: http://blog.csdn.net/niexinming/article/details/78512274
【2】利用write泄露程序的关键信息,这样的话可以很方便的用DynELF这个函数了,比如这个文章中的rsbo2的题解:http://blog.csdn.net/niexinming/article/details/78620566
如果程序的栈可以被完全控制,那么程序的保护全打开也会被攻破,比如这个题目:http://blog.csdn.net/niexinming/article/details/78666941
格式化漏洞现在很难在成熟的软件中遇到,但是这个漏洞却很有趣
【1】pwntools有很不错的函数FmtStr和fmtstr_payload来自动计算格式化漏洞的利用点,并且自动生成payload,比如这个题目:http://blog.csdn.net/niexinming/article/details/78699413 和 http://blog.csdn.net/niexinming/article/details/78512274 中echo的题解
【2】格式化漏洞也是信息泄露的好伴侣,比如这个题目中制造格式化字符串漏洞泄露各种数据 http://blog.csdn.net/niexinming/article/details/78768850
如果把堆释放之后,没有把指针指针清0,还让指针保存下来,那么就会引发很多问题,比如这个题目 http://blog.csdn.net/niexinming/article/details/78598635
如果程序可以在内存中的任意位置写的话,那么威力绝对很大
【1】虽然只能写一个字节,但是依然可以控制程序的并getshell,比如这个题目 http://blog.csdn.net/niexinming/article/details/78542089
【2】修改got表是个控制程序流程的好办法,很多ctf题目只要能通过各种方法控制got的写入,就可以最终得到胜利,比如这个题目: http://blog.csdn.net/niexinming/article/details/78542089
【3】如果能计算出libc的基地址的话,控制top_chunk指针也是解题的好方法,比如这个题目: http://blog.csdn.net/niexinming/article/details/78759363
病毒通常会遵循如下相同步骤:
DOS 内存布局
ELF (Executable and Linkable Format)文件,也就是在 Linux 中的目标文件,主要有以下三种类型
.o
。.so
结尾。一般情况下,它有以下两种使用情景:关于 Link eDitor 的命名,https://en.wikipedia.org/wiki/GNU_linker
目标文件由汇编器和链接器创建,是文本程序的二进制形式,可以直接在处理器上运行。那些需要虚拟机才能够执行的程序 (Java) 不属于这一范围。
Pwntools is a python ctf library designed for rapid exploit development. It essentially help us write exploits quickly, and has a lot of useful functionality behind it.
Also one thing to note, pwntools has Python2 and Python3 versions. Atm this course uses the Python2, but I have plans to switch it all over to Python3. Just keep in mind that some things change between Python2 to the Python3 versions, however the changes are relatively small.
It’s fairly simple process. The installation process is pretty much just using pip:
$ sudo pip install pwn
If you have any problems, google will help a lot.
So this is going to be an explanation on how you do various things with pwntools. It will only cover a small bit of functionality.
If we want to import it into python:
from pwn import *
Now one thing that pwntools does for us, is it has some nice piping functionality which helps with IO. If we want to connect to the server at github.com
(if you have an IP address, just swap out the dns name with the IP address) on port 9000
via tcp:
target = remote("github.com", 9000)
If you want to run a target binary:
target = process("./challenge")
If you want to attach the gdb
debugger to a process:
gdb.attach(target)
If we want to attach the gdb
debugger to a process, and also immediately pass a command to gdb
to set a breakpoint at main:
gdb.attach(target, gdbscript='b *main')
Now for actual I/O. If we want to send the variable x
to the target
(target can be something like a process, or remote connection established by pwntools):
target.send(x)
If we wanted to send the variable x
followed by a newline character appended to the end:
target.sendline(x)
If we wanted to print a single line of text from target
:
print target.recvline()
If we wanted to print all text from target
up to the string out
:
print target.recvuntil("out")
Now one more thing, ELFs store data via least endian, meaning that data is stored with the least significant byte first. In a few situations where we are scanning in an integer, we will need to take this into account. Luckily pwntools will take care of this for us.
To pack the integer y
as a least endian QWORD (commonly used for x64
):
p64(x)
To pack the integer y
as a least endian DWORD (commonly used for x86
):
p32(x)
It can also unpack values we get. Let’s say we wanted to unpack a least endian QWORD and get it’s integer value:
u64(x)
To unpack a DWORD:
u32(x)
Lastly if just wanted to interact directly with target
:
target.interactive()
This is only a small bit of the functionality pwntools has. You will see a lot more of the functionality later. If you want to see more of pwntools, it has some great docs: http://docs.pwntools.com/en/stable/
send(payload)
发送payloadsendline(payload)
发送payload,并进行换行(末尾\n)sendafter(some_string, payload)
接收到 some_string 后, 发送你的 payloadrecvn(N)
接受 N(数字) 字符recvline()
接收一行输出recvlines(N)
接收 N(数字) 行输出recvuntil(some_string)
接收到 some_string 为止https://xz.aliyun.com/t/4205 IDA Pro7.0使用技巧总结 其他的慢慢看 IDA 权威指南吧
IDA Pro 逆向速参 https://juejin.im/post/6844903550460362766
a:将数据转换为字符串
f5:一键反汇编
esc:回退键,能够倒回上一部操作的视图(只有在反汇编窗口才是这个作用,如果是在其他窗口按下esc,会关闭该窗口)
shift+f12:可以打开string窗口,一键找出所有的字符串,右击setup,还能对窗口的属性进行设置
ctrl+w:保存ida数据库
ctrl+s:选择某个数据段,直接进行跳转
ctrl+鼠标滚轮:能够调节流程视图的大小
x:对着某个函数、变量按该快捷键,可以查看它的交叉引用
g:直接跳转到某个地址
n:更改变量的名称
y:更改变量的类型
/ :在反编译后伪代码的界面中写下注释
\:在反编译后伪代码的界面中隐藏/显示变量和函数的类型描述,有时候变量特别多的时候隐藏掉类型描述看起来会轻松很多
;:在反汇编后的界面中写下注释
ctrl+shift+w:拍摄IDA快照
u:undefine,取消定义函数、代码、数据的定义
OPCODE_BYTES = 6 // 机器码字节数,默认0
INDENTION = 0 // 指令缩进,默认16,设为0代码会整洁一些
COMMENTS_INDENTION = 30 // 注释缩进,默认40
MAX_TAIL = 16 // 交叉参考的深度,默认16
MAX_XREF_LENGTH = 80 // Maximal length of line with cross-references
MAX_DATALINE_LENGTH = 100 // Data directives (db,dw, etc):
// max length of argument string
SHOW_AUTOCOMMENTS = NO // Display comments for every instruction
// Advanced users will turn this off
// Please note that there is another definition
// for IBM PC below
SHOW_BASIC_BLOCKS = NO // Generate an empty line at the end of a basic block
SHOW_BORDERS = YES // Borders between data/code
SHOW_EMPTYLINES = YES // Generate empty line to make disassembly more readable
SHOW_LINEPREFIXES = YES // Show line prefixes (like 1000:0000)
SHOW_XREFS = 15 // 显示大量的交叉参考信息,默认2
SHOW_ORIGINS = NO // Generate 'org' directives
Options => ASCII String styles
IDA 从 7.0 版本开始正式支持中文字符串的显示,但仍需要配置 ida.cfg。
在 IDA \ CFG 目录下新建一个文件 Chinese.clt,内容如下。(好像默认就有了)
u2000..u206F,
u2F00..u2FDF,
u3000..u303F,
u31C0..u31EF,
u3400..u4DBF,
u4E00..u9FFF,
uF900..uFAFF,
uFE30..uFE4F,
u20000..u2A6DF,
u2A700..u2BA7F,
u2B740..u2B81F,
u2F800..u2FA1F;
gdb 还有其他一些小技巧,可以参考awesome-cheatsheets/tools/gdb.txt中的列表。该列表最初由韦神创建,我时不时也会添加一些上去。当然为了方便大家的查阅,这里直接给出汇总表格附录:
命令 | 含义 | 备注 |
---|---|---|
gdb object | 正常启动,加载可执行 | |
gdb object core | 对可执行 + core 文件进行调试 | |
gdb object pid | 对正在执行的进程进行调试 | |
gdb | 正常启动,启动后需要 file 命令手动加载 | |
gdb -tui | 启用 gdb 的文本界面(或 ctrl-x ctrl-a 更换 CLI/TUI) |
命令 | 含义 | 备注 |
---|---|---|
help | 列出命令分类 | |
help running | 查看某个类别的帮助信息 | |
help run | 查看命令 run 的帮助 | |
help info | 列出查看程序运行状态相关的命令 | |
help info line | 列出具体的一个运行状态命令的帮助 | |
help show | 列出 GDB 状态相关的命令 | |
help show commands | 列出 show 命令的帮助 |
命令 | 含义 | 备注 |
---|---|---|
break main | 对函数 main 设置一个断点,可简写为 b main | |
break 101 | 对源代码的行号设置断点,可简写为 b 101 | |
break basic.c:101 | 对源代码和行号设置断点 | |
break basic.c:foo | 对源代码和函数名设置断点 | |
break *0x00400448 | 对内存地址 0x00400448 设置断点 | |
info breakpoints | 列出当前的所有断点信息,可简写为 info break | |
delete 1 | 按编号删除一个断点 | |
delete | 删除所有断点 | |
clear | 删除在当前行的断点 | |
clear function | 删除函数断点 | |
clear line | 删除行号断点 | |
clear basic.c:101 | 删除文件名和行号的断点 | |
clear basic.c:main | 删除文件名和函数名的断点 | |
clear *0x00400448 | 删除内存地址的断点 | |
disable 2 | 禁用某断点,但是部删除 | |
enable 2 | 允许某个之前被禁用的断点,让它生效 | |
rbreak {regexpr} | 匹配正则的函数前断点,如 ex_* 将断点 ex_ 开头的函数 | |
tbreak function/line | 临时断点 | |
hbreak function/line | 硬件断点 | |
ignore {id} {count} | 忽略某断点 N-1 次 | |
condition {id} {expr} | 条件断点,只有在条件生效时才发生 | |
condition 2 i == 20 | 2号断点只有在 i == 20 条件为真时才生效 | |
watch {expr} | 对变量设置监视点 | |
info watchpoints | 显示所有观察点 | |
catch exec | 断点在exec事件,即子进程的入口地址 |
命令 | 含义 | 备注 |
---|---|---|
run | 运行程序 | |
run {args} | 以某参数运行程序 | |
run < file | 以某文件为标准输入运行程序 | |
run < <(cmd) | 以某命令的输出作为标准输入运行程序 | |
run <<< $(cmd) | 以某命令的输出作为标准输入运行程序 | Here-String |
set args {args} ... | 设置运行的参数 | |
show args | 显示当前的运行参数 | |
cont | 继续运行,可简写为 c | |
step | 单步进入(si 执行一行汇编),碰到函数会进去。 | |
step {count} | 单步多少次 | |
next | 单步跳过(ni 执行一行汇编),碰到函数不会进入。 | |
next {count} | 单步多少次 | |
CTRL+C | 发送 SIGINT 信号,中止当前运行的程序 | |
attach {process-id} | 链接上当前正在运行的进程,开始调试 | |
detach | 断开进程链接 | |
finish | 结束当前函数的运行 | |
until | 持续执行直到代码行号大于当前行号(跳出循环) | |
until {line} | 持续执行直到执行到某行 | |
kill | 杀死当前运行的函数 |
命令 | 含义 | 备注 |
---|---|---|
bt | 打印 backtrace | |
frame | 显示当前运行的栈帧 | |
up | 向上移动栈帧(向着 main 函数) | |
down | 向下移动栈帧(远离 main 函数) | |
info locals | 打印帧内的相关变量 | |
info args | 打印函数的参数 |
命令 | 含义 | 备注 |
---|---|---|
list 101 | 显示第 101 行周围 10行代码 | |
list 1,10 | 显示 1 到 10 行代码 | |
list main | 显示函数周围代码 | |
list basic.c:main | 显示另外一个源代码文件的函数周围代码 | |
list - | 重复之前 10 行代码 | |
list *0x22e4 | 显示特定地址的代码 | |
cd dir | 切换当前目录 | |
pwd | 显示当前目录 | |
search {regexpr} | 向前进行正则搜索 | |
reverse-search {regexp} | 向后进行正则搜索 | |
dir {dirname} | 增加源代码搜索路径 | |
dir | 复位源代码搜索路径(清空) | |
show directories | 显示源代码路径 |
命令 | 含义 | 备注 |
---|---|---|
print {expression} | 打印表达式,并且增加到打印历史 | |
print /x {expression} | 十六进制输出,print 可以简写为 p | |
print array[i]@count | 打印数组范围 | |
print $ | 打印之前的变量 | |
print *$->next | 打印 list | |
print $1 | 输出打印历史里第一条 | |
print ::gx | 将变量可视范围(scope)设置为全局 | |
print 'basic.c'::gx | 打印某源代码里的全局变量,(gdb 4.6) | |
print /x &main | 打印函数地址 | |
x *0x11223344 | 显示给定地址的内存数据 | |
x /nfu {address} | 打印内存数据,n是多少个,f是格式,u是单位大小 | |
x /10xb *0x11223344 | 按十六进制打印内存地址 0x11223344 处的十个字节 | |
x/x &gx | 按十六进制打印变量 gx,x和斜杆后参数可以连写 | |
x/4wx &main | 按十六进制打印位于 main 函数开头的四个 long | |
x/gf &gd1 | 打印 double 类型 | |
help x | 查看关于 x 命令的帮助 | |
info locals | 打印本地局部变量 | |
info functions {regexp} | 打印函数名称 | |
info variables {regexp} | 打印全局变量名称 | |
ptype name | 查看类型定义,比如 ptype FILE,查看 FILE 结构体定义 | |
whatis {expression} | 查看表达式的类型 | |
set var = {expression} | 变量赋值 | |
set *address = value | 可将 * 换成 char / short / long 分别设定 1/2/8 byte | |
set {int}0x600000 = 1337 | ||
display {expression} | 在单步指令后查看某表达式的值 | |
undisplay | 删除单步后对某些值的监控 | |
info display | 显示监视的表达式 | |
show values | 查看记录到打印历史中的变量的值 (gdb 4.0) | |
info history | 查看打印历史的帮助 (gdb 3.5) |
命令 | 含义 | 备注 |
---|---|---|
file {object} | 加载新的可执行文件供调试 | |
file | 放弃可执行和符号表信息 | |
symbol-file {object} | 仅加载符号表 | |
exec-file {object} | 指定用于调试的可执行文件(非符号表) | |
core-file {core} | 加载 core 用于分析 |
命令 | 含义 | 备注 |
---|---|---|
info signals | 打印信号设置 | |
handle {signo} {actions} | 设置信号的调试行为 | |
handle INT print | 信号发生时打印信息 | |
handle INT noprint | 信号发生时不打印信息 | |
handle INT stop | 信号发生时中止被调试程序 | |
handle INT nostop | 信号发生时不中止被调试程序 | |
handle INT pass | 调试器接获信号,不让程序知道 | |
handle INT nopass | 调试起不接获信号 | |
signal signo | 继续并将信号转移给程序 | |
signal 0 | 继续但不把信号给程序 |
命令 | 含义 | 备注 |
---|---|---|
info threads | 查看当前线程和 id | |
thread {id} | 切换当前调试线程为指定 id 的线程 | |
break {line} thread all | 所有线程在指定行号处设置断点 | |
thread apply {id..} cmd | 指定多个线程共同执行 gdb 命令 | |
thread apply all cmd | 所有线程共同执行 gdb 命令 | |
set schedule-locking ? | 调试一个线程时,其他线程是否执行 | |
set non-stop on/off | 调试一个线程时,其他线程是否运行 | |
set pagination on/off | 调试一个线程时,分页是否停止 | |
set target-async on/off | 同步或者异步调试,是否等待线程中止的信息 |
命令 | 含义 | 备注 |
---|---|---|
info inferiors | 查看当前进程和 id | |
inferior {id} | 切换某个进程 | |
kill inferior {id...} | 杀死某个进程 | |
set detach-on-fork on/off | 设置当进程调用fork时gdb是否同时调试父子进程 | |
set follow-fork-mode parent/child | 设置当进程调用fork时是否进入子进程 |
命令 | 含义 | 备注 |
---|---|---|
info registers | 打印普通寄存器 | |
info all-registers | 打印所有寄存器 | |
print/x $pc | 打印单个寄存器 | |
stepi | 指令级别单步进入 | si |
nexti | 指令级别单步跳过 | ni |
display/i $pc | 监控寄存器(每条单步完以后会自动打印值) | |
x/x &gx | 十六进制打印变量 | |
info line 22 | 打印行号为 22 的内存地址信息 | |
info line *0x2c4e | 打印给定内存地址对应的源代码和行号信息 | |
disassemble {addr} | 对地址进行反汇编,比如 disassemble 0x2c4e |
命令 | 含义 | 备注 |
---|---|---|
show commands | 显示历史命令 (gdb 4.0) | |
info editing | 显示历史命令 (gdb 3.5) | |
ESC-CTRL-J | 切换到 Vi 命令行编辑模式 | |
set history expansion on | 允许类 c-shell 的历史 | |
break class::member | 在类成员处设置断点 | |
list class:member | 显示类成员代码 | |
ptype class | 查看类包含的成员 | /o可以看成员偏移,类似pahole |
print *this | 查看 this 指针 | |
define command ... end | 定义用户命令 | |
<return> | 直接按回车执行上一条指令 | |
shell {command} [args] | 执行 shell 命令 | |
source {file} | 从文件加载 gdb 命令 | |
quit | 退出 gdb |
用法特性:https://github.com/pwndbg/pwndbg/blob/dev/FEATURES.md
分屏
git clone https://github.com/longld/peda.git ~/peda
echo "source ~/peda/peda.py" >> ~/.gdbinit
https://guyinatuxedo.github.io/
https://adworld.xctf.org.cn/task REVERSE或者PWN或者MOBILE三个方向合计完成任意9题
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有