PWN-学习技巧积累

解题流程

checksecfile 命令查看架构. checksec 可以查看保护.

nc 命令

如:

1
nc 43.156.14.141 1141

43.156.14.141 是服务器 IP, 1141 是端口.

python pwn 库

p32p64 中的 ppack 的含义, 用于将整数数值转换为对应长度的二进制数据.

注意 p32p64 都是小端序, 如:

1
2
3
4
5
6
from pwn import *

address = 0x12345678
address_bytes = p64(address)

print(address_bytes)

输出结果为:

1
\x78\x56\x34\x12\x00\x00\x00\x00

常用的方法有:

  • remote
  • process
  • sendline
  • sendlineafter
  • interactive
  • recvuntil
  • recv

获取 main 函数地址的作用

可以用于确定二进制文件的基址(Base Address):main函数通常是程序的入口点,获取其地址可以帮助确定二进制文件在内存中的基址。通过计算main地址与二进制文件在内存中的偏移量,可以推断出其他函数和全局变量在内存中的地址.

其可用于应对开启了 RELRO 保护的二进制文件, 此时所有函数的地址都只有偏移, 因此知道了 main 函数的地址也就得知了二进制文件的基址, 也就能得到其他函数的地址.

1
文件基址 = main函数的地址 - main函数的偏移

Shellcode 的示例和作用

如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/* execve(path='/bin///sh', argv=['sh'], envp=0) */
/* push b'/bin///sh\x00' */
push 0x68
push 0x732f2f2f
push 0x6e69622f
mov ebx, esp
/* push argument array ['sh\x00'] */
/* push 'sh\x00\x00' */
push 0x1010101
xor dword ptr [esp], 0x1016972
xor ecx, ecx
push ecx /* null terminate */
push 4
pop ecx
add ecx, esp
push ecx /* 'sh\x00' */
mov ecx, esp
xor edx, edx
/* call execve() */
push SYS_execve /* 0xb */
pop eax
int 0x80

但实际发送的是二进制形式的 shellcode:

1
shellcode = asm(shellcraft.sh())

asm 将汇编代码进行汇编, 返回一个包含对应二进制指令的字节序列.

得到的为:

1
b'jhh///sh/bin\x89\xe3h\x01\x01\x01\x01\x814$ri\x01\x011\xc9Qj\x04Y\x01\xe1Q\x89\xe11\xd2j\x0bX\xcd\x80'

注意一段内存空间的地址, 将 shellcode 存入一段内存空间后, 利用 return 来执行存储在这段空间中的程序.

ssize_t 类型

其为有符号整数类型. 定义为:

1
typedef long int ssize_t

(定义于 <sys/types.h> 头文件中)

大小为:

  • 32 位平台, 4 字节, 同 int
  • 64 位平台, 8 字节, 同 long int

变量在栈中的位置不是连续的

如:

1
2
3
4
5
char buf[55]; // [rsp+0h] [rbp-50h] BYREF
char v5; // [rsp+37h] [rbp-19h]
ssize_t v6; // [rsp+38h] [rbp-18h]
unsigned int seed[2]; // [rsp+40h] [rbp-10h]
unsigned int v8; // [rsp+4Ch] [rbp-4h]

在函数的局部栈帧中,变量的分配位置是由编译器和特定的编译器优化策略决定的。这些优化策略旨在提高代码的性能和效率,并在满足语言规范的前提下进行变量的布局。

在给出的代码片段中,变量的偏移位置并不连续,而是分散的。这是因为编译器可能对变量进行了优化和调整,以减少内存对齐的浪费和结构填充(padding)。编译器可能会对变量进行重新排列,以便更有效地利用内存,减少内存访问的成本。

一些常见的优化策略和原因包括:

  • 内存对齐:编译器通常会对变量进行内存对齐,以提高内存访问的效率。对齐要求可能会导致变量之间存在一些填充字节,使得变量的偏移位置不连续。

  • 寄存器使用:编译器可能会优化寄存器的使用,将一些变量存储在寄存器中而不是内存中。这可能导致寄存器中的变量没有在栈帧中分配空间。

  • 优化策略:编译器可能会使用各种优化策略来提高代码的性能和效率。这些策略可能会导致变量的重新排列和位置调整,以便更好地利用寄存器和内存访问。

python 的 ctypes 库

其用于创建和模拟 C 数据结构.

加载一个 C 库, 如:

1
2
from ctypes import *
libc = cdll.LoadLibrary("libc.so.6")

这里的 cdll (C Declared DLL) 是 ctypes 库中的一个类.

然后可以用这个返回的对象来调用 C 函数:

1
libc.srand(0x61616161)

函数指针数组

1
2
3
4
5
6
7
8
9
10
v3[0] = sub_8048604;
v3[1] = sub_8048618;
v3[2] = sub_804862C;
v3[3] = sub_8048640;
v3[4] = sub_8048654;
v3[5] = sub_8048668;
v3[6] = sub_804867C;
v3[7] = sub_8048690;
v3[8] = sub_80486A4;
v3[9] = sub_80486B8;

这里的 v3 就是函数指针数组.

关于有 canary 的题

checksec 发现有 canary 之后, 就需要考虑绕过.

关于没有对数组边界进行审查

如:

1
v13[v5] = v7

这里没有限制 v5


PWN-学习技巧积累
http://example.com/2023/08/03/PWN-学习技巧积累/
作者
Jie
发布于
2023年8月3日
许可协议