mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4mobile wallpaper 5mobile wallpaper 6
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 9f
9e 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]

解密流程#

  1. 取出byte_41A0EC
  2. 先逐字节异或 0x32 得到真实密文
  3. 每 4 字节按小端打包成 8 个 uint32
  4. 按 2 个 uint32 一组,用 TEA 变种(36 轮,delta=0x61C88747,key=[0x45,0x56,0x147,0x45])解密
  5. 把 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 = 0x61C88747
ROUNDS = 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}
分享

如果这篇文章对你有帮助,欢迎分享给更多人!

POFP逆向题
https://chaojixin.ren/posts/pofp逆向题/
作者
超級の新人
发布于
2025-12-11
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时

封面
Sample Song
Sample Artist
封面
Sample Song
Sample Artist
0:00 / 0:00