POFP逆向题

chaoji_xinren 发布于 7 天前 12 次阅读


ccc.exe

先用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_4113CAsub_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。

Exp

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}

此作者没有提供个人介绍。
最后更新于 2025-12-11