760 字
4 分钟
POFP逆向题
题目分析
先用exeinfo看一眼信息,发现没有壳直接拖进ida。
打开看一眼有什么内容,搜索flag字段成功找到主逻辑sub_4155F0,对其进行反编译:
__int64 sub_4155F0(){ FILE *v0; // eax __int64 v1; // rax __int64 v3; // [esp-8h] [ebp-1B8h] char v4; // [esp+0h] [ebp-1B0h] char v5; // [esp+0h] [ebp-1B0h] int m; // [esp+D0h] [ebp-E0h] int k; // [esp+DCh] [ebp-D4h] int j; // [esp+E8h] [ebp-C8h] int i; // [esp+F4h] [ebp-BCh] char Buffer[72]; // [esp+100h] [ebp-B0h] BYREF char Buf1[40]; // [esp+148h] [ebp-68h] BYREF int v12[6]; // [esp+170h] [ebp-40h] BYREF int v13[9]; // [esp+188h] [ebp-28h] BYREF int savedregs; // [esp+1B0h] [ebp+0h] BYREF
sub_41132F(&unk_41C00F); v12[0] = 69; v12[1] = 86; v12[2] = 327; v12[3] = 69; sub_4110D7("welcome to POFP CTF\n", v4); sub_4110D7("input your flag:", v5); _acrt_iob_func(0); v0 = (FILE *)sub_411253(); fgets(Buffer, 64, v0); if ( sub_411253() ) { if ( j_strlen(Buffer) == 32 ) { j_memset(v13, 0, 0x20u); for ( i = 0; i <= 7; ++i ) v13[i] = Buffer[4 * i] | (Buffer[4 * i + 1] << 8) | (Buffer[4 * i + 2] << 16) | (Buffer[4 * i + 3] << 24); for ( j = 0; j <= 7; j += 2 ) sub_4113CA(&v13[j], v12); for ( k = 0; k <= 7; ++k ) { for ( m = 0; m <= 3; ++m ) Buf1[4 * k + m] = (unsigned int)v13[k] >> (8 * m); } if ( !j_memcmp(Buf1, &byte_41A0EC, 0x20u) ) puts("success"); else puts("fake"); sub_411253(); LODWORD(v1) = 0; } else { puts("fake"); sub_411253(); LODWORD(v1) = 0; } } else { LODWORD(v1) = 1; } v3 = v1; sub_4111EF(&savedregs, &dword_415880); return v3;}逻辑梳理
拿 Buf1 里的 32 个字节和全局的 byte_41A0EC 做比较。
byte_41A0EC运行时被 sub_411790 XOR 0x32,所以我们需要还原其真实密文:
39 c1 97 e8 dc a1 ec c6 6f 2e 29 d8 26 76 ab 9f9e 6d 79 20 12 db 0c 93 46 34 8f 34 ab 29 df e8之后看函数吧,发现sub_4113CA → sub_414D30 —— TEA变种加密:
int __cdecl sub_414D30(unsigned int *a1, int a2){ int result; // eax unsigned int i; // [esp+D0h] [ebp-2Ch] unsigned int v4; // [esp+DCh] [ebp-20h] unsigned int v5; // [esp+E8h] [ebp-14h] unsigned int v6; // [esp+F4h] [ebp-8h]
sub_41132F(&unk_41C00F); v4 = *a1; v5 = a1[1]; v6 = 0; for ( i = 0; i <= 0x23; ++i ) { v4 += (v6 + *(_DWORD *)(a2 + 4 * (v6 & 3))) ^ (v5 + ((16 * v5) ^ (v5 >> 5))); v6 += 1640531783; v5 += (v6 + *(_DWORD *)(a2 + 4 * ((v6 >> 11) & 3))) ^ (v4 + ((16 * v4) ^ (v4 >> 5))); } *a1 = v4; result = 4; a1[1] = v5; return result;}加密信息分析
经过分析之后得到下面关于加密的信息:
- 轮数:36(0x23)
- delta:0x61C88747(1640531783)
- sum 初始为 0,每轮累加 delta
- key:[0x45, 0x56, 0x147, 0x45]
解密流程
- 取出byte_41A0EC
- 先逐字节异或 0x32 得到真实密文
- 每 4 字节按小端打包成 8 个 uint32
- 按 2 个 uint32 一组,用 TEA 变种(36 轮,delta=0x61C88747,key=[0x45,0x56,0x147,0x45])解密
- 把 8 个解密后的 uint32 按小端还原回 32 字节,即明文/flag
Exploit
import struct
ENC_RAW = bytes([ 0x0b,0xf3,0xa5,0xda,0xec,0x93,0xde,0xf4, 0x5d,0x1c,0x1b,0xea,0x14,0x44,0x99,0xad, 0xac,0x5f,0x4b,0x12,0x20,0xe9,0x3e,0xa1, 0x74,0x06,0xbd,0x06,0x99,0x1b,0xed,0xda])
KEY = [0x45, 0x56, 0x147, 0x45]DELTA = 0x61C88747ROUNDS = 36 # i <= 0x23
def tea_decrypt_block(v0: int, v1: int): s = (DELTA * ROUNDS) & 0xFFFFFFFF for _ in range(ROUNDS): v1 = (v1 - (((v0 << 4 ^ v0 >> 5) + v0) ^ (s + KEY[(s >> 11) & 3]))) & 0xFFFFFFFF s = (s - DELTA) & 0xFFFFFFFF v0 = (v0 - (((v1 << 4 ^ v1 >> 5) + v1) ^ (s + KEY[s & 3]))) & 0xFFFFFFFF return v0, v1
def solve(): data = bytes([b ^ 0x32 for b in ENC_RAW]) ints = [struct.unpack("<I", data[i:i+4])[0] for i in range(0, len(data), 4)] out = bytearray() for i in range(0, len(ints), 2): v0, v1 = tea_decrypt_block(ints[i], ints[i+1]) out.extend(struct.pack("<I", v0)) out.extend(struct.pack("<I", v1)) print("raw bytes:", out) print("flag:", out.decode().strip())
if __name__ == "__main__": solve()最终Flag
POFP{yAstZwntQ5ufUWOjYabaQaBI6} 分享
如果这篇文章对你有帮助,欢迎分享给更多人!
部分信息可能已经过时









