1.SU_BBRE
这道题加了一点pwn的知识,二进制不分家了属于是
附件打开有一个很明显的rc4,注意小端序,解出来第一段flag是We1com3ToReWorld
def KSA(key):
S = list(range(256))
j = 0
for i in range(256):
j = (j + S[i] + key[i % len(key)]) % 256
S[i], S[j] = S[j], S[i]
return S
def PRGA(S):
i, j = 0, 0
while True:
i = (i + 1) % 256
j = (j + S[i]) % 256
S[i], S[j] = S[j], S[i]
K = S[(S[i] + S[j]) % 256]
yield K
def RC4Decrypt(key, text):
S = KSA(key)
keystream = PRGA(S)
res = []
for char in text:
res.append(char ^ next(keystream))
return bytes(res)
#示例
key = b'suctf'
plaintext = [0x2f, 0x5a, 0x57, 0x65, 0x14, 0x8f, 0x69, 0xcd, 0x93, 0x29, 0x1a, 0x55, 0x18, 0x40, 0xe4, 0x5e]
Rc4encrypt = RC4Decrypt(key, plaintext)
print(Rc4encrypt)
.text:0040223D ; =============== S U B R O U T I N E =======================================
.text:0040223D
.text:0040223D ; Attributes: noreturn bp-based frame
.text:0040223D
.text:0040223D public function1
.text:0040223D function1 proc near
.text:0040223D
.text:0040223D var_1F = dword ptr -1Fh
.text:0040223D var_1B = dword ptr -1Bh
.text:0040223D var_17 = byte ptr -17h
.text:0040223D var_16 = byte ptr -16h
.text:0040223D var_C = dword ptr -0Ch
.text:0040223D
.text:0040223D ; __unwind {
.text:0040223D push ebp
.text:0040223E mov ebp, esp
.text:00402240 sub esp, 28h
.text:00402243 mov [ebp+var_1F], 4D626D41h
.text:0040224A mov [ebp+var_1B], 294E4953h
.text:00402251 mov [ebp+var_17], 28h ; '('
.text:00402255 sub esp, 0Ch
.text:00402258 push offset s ; "hhh,you find me:"
.text:0040225D call puts
.text:00402262 add esp, 10h
.text:00402265 sub esp, 8
.text:00402268 lea eax, [ebp+var_16]
.text:0040226B push eax
.text:0040226C push offset aS ; "%s"
.text:00402271 call __isoc99_scanf
.text:00402276 add esp, 10h
.text:00402279 mov [ebp+var_C], 0
.text:00402280 jmp short loc_4022B5
.text:00402282 ; ---------------------------------------------------------------------------
.text:00402282
.text:00402282 loc_402282: ; CODE XREF: function1+7C↓j
.text:00402282 lea edx, [ebp+var_16]
.text:00402285 mov eax, [ebp+var_C]
.text:00402288 add eax, edx
.text:0040228A movzx eax, byte ptr [eax]
.text:0040228D movsx eax, al
.text:00402290 sub eax, [ebp+var_C]
.text:00402293 mov edx, eax
.text:00402295 lea ecx, [ebp+var_1F]
.text:00402298 mov eax, [ebp+var_C]
.text:0040229B add eax, ecx
.text:0040229D movzx eax, byte ptr [eax]
.text:004022A0 movsx eax, al
.text:004022A3 cmp edx, eax
.text:004022A5 jz short loc_4022B1
.text:004022A7 sub esp, 0Ch
.text:004022AA push 0 ; status
.text:004022AC call exit
.text:004022B1 ; ---------------------------------------------------------------------------
.text:004022B1
.text:004022B1 loc_4022B1: ; CODE XREF: function1+68↑j
.text:004022B1 add [ebp+var_C], 1
.text:004022B5
.text:004022B5 loc_4022B5: ; CODE XREF: function1+43↑j
.text:004022B5 cmp [ebp+var_C], 8
.text:004022B9 jle short loc_402282
.text:004022BB sub esp, 0Ch
.text:004022BE push offset aCongratulate ; "congratulate!!!"
.text:004022C3 call puts
.text:004022C8 add esp, 10h
.text:004022CB sub esp, 0Ch
.text:004022CE push 0 ; status
.text:004022D0 call exit
.text:004022D0 ; } // starts at 40223D
.text:004022D0 function1 endp
但是后面function1还有一段加密,其实就是字符减去下标,逆向用输入加下标就行
flag = ''
enc = [0x41, 0x6D, 0x62, 0x4D, 0x53, 0x49, 0x4e, 0x29, 0x28]
for i, j in enumerate(enc):
flag+=chr(i+j)
print(flag)
解出来是AndPWNT00
这是一个栈溢出漏洞,RC4只加密了16个字符,但是主函数中输入 19 个字符,所有末尾 3个用来栈溢出,ret 到 function1
function1 的地址是 0x0040223D,转换成字符串就是 =”@
所有flag就是SUCTF{We1com3ToReWorld=”@AndPWNT00}
2.SU_minesweeper
有点像一个扫雷游戏
这里是一个16进制的转换,但是这个字符对应集和标准hex是不一样的,是 abcdef0123456789
byte_4020 是一个 20×20 的矩阵(共400个字节),有些为 -1,其他为0~8的数字
这里是判断规则
sub_13C9计算 (i, j) 及其九宫格内的 bit 为 1 的个数(即9个格子的1的数量)
sub_1352 返回 a1(50字节=400bit)里第 20*n19+n19_1 位的 bit
这道题的逻辑就是把输入经过处理(每两个字符拼成一个字节)后,然后变成了50字节,也就是400个bit,这里就形成了一个20×20的扫雷地图,其中1的位置就是地雷,byte_4020对应的就是这个地图里面的数字即附近地雷的数量
z3 找出满足条件的一组解,然后按以上规则转换为输入
from z3 import *
# Step 1: 把你的 byte_4020 贴成 20x20 列表
byte_4020_raw = [
0x3,0x4,0xFF,0xFF,0xFF,0x5,0xFF,0xFF,0xFF,0xFF,0xFF,0x4,0x4,0xFF,0xFF,0xFF,0xFF,0x2,0xFF,0xFF,
0x4,0xFF,0x7,0xFF,0xFF,0xFF,0x4,0x6,0x6,0xFF,0xFF,0xFF,0xFF,0x6,0x5,0x6,0x4,0xFF,0x5,0xFF,
0x4,0x7,0xFF,0x8,0xFF,0x6,0xFF,0xFF,0x6,0x6,0x5,0xFF,0xFF,0xFF,0xFF,0xFF,0x3,0x3,0xFF,0x3,
0xFF,0x5,0x6,0x6,0xFF,0xFF,0xFF,0xFF,0x4,0x5,0x4,0x5,0x7,0x6,0xFF,0xFF,0x4,0xFF,0x2,0x1,
0xFF,0xFF,0xFF,0x3,0x4,0xFF,0xFF,0x5,0x4,0x3,0xFF,0xFF,0x7,0x4,0x3,0xFF,0xFF,0x1,0x1,0xFF,
0xFF,0x4,0x3,0xFF,0x2,0xFF,0x4,0x3,0xFF,0xFF,0x2,0xFF,0x5,0x4,0xFF,0xFF,0x2,0x2,0xFF,0xFF,
0x4,0xFF,0x4,0xFF,0x3,0x5,0x6,0xFF,0xFF,0x0,0xFF,0xFF,0xFF,0x2,0xFF,0xFF,0xFF,0x1,0x4,0xFF,
0xFF,0x7,0x5,0xFF,0xFF,0x3,0x3,0x2,0xFF,0xFF,0x4,0xFF,0xFF,0x5,0x7,0xFF,0x3,0x2,0x4,0x4,
0xFF,0x7,0x5,0x4,0x3,0xFF,0xFF,0x4,0xFF,0x2,0x4,0x5,0xFF,0xFF,0x6,0x5,0x4,0xFF,0x2,0xFF,
0xFF,0x7,0x4,0xFF,0xFF,0x3,0xFF,0x4,0x4,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x4,0x3,0x2,0x2,
0xFF,0xFF,0x2,0x4,0x3,0x5,0xFF,0xFF,0x5,0xFF,0x4,0xFF,0x6,0xFF,0xFF,0x6,0xFF,0xFF,0xFF,0xFF,
0x3,0x3,0xFF,0x4,0xFF,0xFF,0xFF,0xFF,0xFF,0x6,0xFF,0x6,0x6,0xFF,0x7,0x6,0x4,0xFF,0x4,0x3,
0xFF,0x4,0x3,0x5,0x4,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x4,0x6,0x7,0xFF,0xFF,0x4,0xFF,0xFF,
0xFF,0x7,0xFF,0x5,0xFF,0x5,0xFF,0xFF,0x6,0x7,0x7,0xFF,0x5,0x6,0x6,0xFF,0xFF,0x2,0x4,0x4,
0xFF,0xFF,0xFF,0xFF,0xFF,0x6,0xFF,0xFF,0x7,0x7,0x6,0xFF,0x6,0xFF,0xFF,0xFF,0xFF,0x3,0xFF,0x3,
0x5,0xFF,0x7,0xFF,0x5,0xFF,0x6,0xFF,0x5,0xFF,0xFF,0x7,0x8,0xFF,0xFF,0x3,0xFF,0x3,0xFF,0xFF,
0xFF,0xFF,0xFF,0x3,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x6,0x5,0x3,0xFF,0x4,0x5,0x5,0x3,
0xFF,0xFF,0x6,0x5,0x5,0x6,0xFF,0x6,0x5,0x2,0x4,0x3,0x4,0xFF,0xFF,0x3,0x4,0x4,0x6,0x5,
0xFF,0x3,0xFF,0x5,0x5,0x5,0xFF,0xFF,0x5,0xFF,0xFF,0x4,0xFF,0xFF,0x4,0xFF,0x7,0x7,0x8,0x6,
0xFF,0xFF,0xFF,0xFF,0x5,0xFF,0xFF,0xFF,0x4,0xFF,0x3,0xFF,0x3,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x5,0x3
]
# reshape to 20x20
byte_4020 = [byte_4020_raw[i*20:(i+1)*20] for i in range(20)]
# Step 2: 定义 20x20 的二值变量
B = [[Bool(f'b_{i}_{j}') for j in range(20)] for i in range(20)]
s = Solver()
# Step 3: 对每个 (i,j),byte_4020 不为 0xFF 的格子,约束其九宫格内 1 的数量
for i in range(20):
for j in range(20):
val = byte_4020[i][j]
if val != 0xFF:
cells = []
for di in [-1,0,1]:
for dj in [-1,0,1]:
ni, nj = i+di, j+dj
if 0 <= ni < 20 and 0 <= nj < 20:
cells.append(B[ni][nj])
s.add(Sum([If(x, 1, 0) for x in cells]) == val)
# Step 4: 求解
if s.check() != sat:
print("No solution!")
exit(1)
m = s.model()
# Step 5: 提取 bit 并打包成 50 字节
bits = []
for i in range(20):
for j in range(20):
bits.append(1 if m[B[i][j]] else 0)
bytes_ = []
for k in range(0, 400, 8):
b = 0
for p in range(8):
if k+p < len(bits):
b |= bits[k+p] << p
bytes_.append(b)
# Step 6: 按题目自定义编码输出100位字符串
def inv_sub_122B(x):
if 0 <= x <= 5:
return chr(ord('a')+x)
elif 6 <= x <= 15:
return chr(ord('0')+x-6)
else:
raise ValueError("invalid nibble")
result = ''
for b in bytes_:
hi = (b >> 4) & 0xF
lo = b & 0xF
result += inv_sub_122B(hi)
result += inv_sub_122B(lo)
print(result)
f57503596fb80f955fa5cad3cb282aa18ac62922a1981ea7b53b07a30709b508f3176601154250d509b7bee0f2170b898617
之后转换成md5加上SUCTF{}就是flag了
SUCTF{d661b98e4241de7423ef2d953098329d}
3.SU_Harmony
ArkTS层只有调so层,验证逻辑还是要去so找
全局搜索check找到加密函数
加了混淆,代码很丑
在贴出的循环部分,sub_57B0 是唯一被反复对输入片段直接调用的子函数,也就是验证的核心代码
所有的函数都加了大量的混淆,只能一个一个点开慢慢看
最终分析一下前面那一坨函数并整理一下可以得到
sub_6D20:字符串 * 字符串
sub_8270:字符串 * 数字
sub_9890:字符串 + 字符串
sub_A8F0:字符串 – 字符串
sub_C160:字符串的值整除2
也就是一个二元一次方程
最后写脚本解密一下
import math
from Crypto.Util.number import long_to_bytes
dt = [
999272289930604998,
1332475531266467542,
1074388003071116830,
1419324015697459326,
978270870200633520,
369789474534896558,
344214162681978048,
2213954953857181622
]
for x in dt:
c = (2 * x) + 3
delta = 4 + 4 * c
delta = math.sqrt(delta)
sol = (-2 + delta) / 2
sol = int(sol)
print(long_to_bytes(sol)[::-1].decode(), end="")
SUCTF{Ma7h_WorldIs_S0_B3aut1ful}