[Rev] CTFShow RE2

👁 0 views
📅 2026-06-28 ⏱ 3 min 🏆 CTFShow
[Reverse] RC4文件加密逆向 | N0N-7r4mm313D Writeups

[Reverse] RC4文件加密逆向

👁 0 views
📅 2026-06-28 ⏱ 6 min 🏆 CTF Reverse

0x01 程序整体逻辑分析

拿到逆向程序,按照常规逆向思路优先定位 main 函数梳理整体业务流程:

int __cdecl main_0(int argc, const char **argv, const char **envp)
{
  char v4; // [esp+0h] [ebp-44Ch]
  char v5; // [esp+0h] [ebp-44Ch]
  char v6; // [esp+0h] [ebp-44Ch]
  char v7; // [esp+0h] [ebp-44Ch]
  char v8; // [esp+0h] [ebp-44Ch]
  char v9; // [esp+0h] [ebp-44Ch]
  char v10[4]; // [esp+194h] [ebp-2B8h] BYREF
  int v11; // [esp+1A0h] [ebp-2ACh]
  FILE *v12; // [esp+1ACh] [ebp-2A0h]
  FILE *v13; // [esp+1B8h] [ebp-294h]
  _BYTE v14[264]; // [esp+1C4h] [ebp-288h] BYREF
  _BYTE v15[264]; // [esp+2CCh] [ebp-180h] BYREF
  char Str1[60]; // [esp+3D4h] [ebp-78h] BYREF
  char Str[56]; // [esp+410h] [ebp-3Ch] BYREF

  __CheckForDebuggerJustMyCode(&unk_40B027);
  memset(Str, 0, 50);
  memset(Str1, 0, 50);
  memset(v15, 0, 0x100u);
  memset(v14, 0, 0x100u);
  v11 = 1;
  do
  {
    sub_401037(asc_406BA0, v4);
    sub_401037((char *)&byte_406BFC, v5);
    sub_401037(asc_406C48, v6);
    sub_401037((char *)&byte_406CA8, v7);
    sub_401037((char *)&byte_406D0C, v8);
    sub_401037(a1, v9);
    sub_401073("%d", (char)v10);
    if ( *(_DWORD *)v10 == 1 )
    {
      v13 = fopen("flag.txt", "r");
      if ( !v13 )
      {
        sub_401037((char *)&byte_406D48, v4);
        getchar();
        exit(0);
      }
      v12 = fopen("enflag.txt", "w");
      if ( !v12 )
      {
        sub_401037((char *)&byte_406D70, v4);
        getchar();
        exit(0);
      }
      sub_401037(asc_406D84, v4);
      sub_401073("%s", (char)Str);
      sub_401069(Str, Str1);
      sub_401028(Str, v15, v14, v13, v12);
    }
    else if ( *(_DWORD *)v10 == 2 )
    {
      v11 = 0;
    }
    else
    {
      sub_401037(asc_406D98, v4);
    }
  }
  while ( v11 );
  return 0;
}

程序执行流程:

  1. 输入 1 进入文件加密功能,输入 2 退出程序
  2. 控制台读取用户输入密钥存入 Str
  3. 调用 sub_401069 进行密钥合法性校验
  4. 密钥校验通过后读取 flag.txt,使用密钥加密,密文写入 enflag.txt
  5. 密钥错误直接输出 Error!,不会执行加密逻辑

0x02 密钥校验函数逆向

sub_401069 是跳板函数,仅跳转至 sub_401A70,直接进入真实校验函数分析:

char __cdecl sub_401A70(char *Str, char *Str1)
{
  char v3; // [esp+0h] [ebp-E4h]
  signed int i; // [esp+D0h] [ebp-14h]
  signed int v5; // [esp+DCh] [ebp-8h]

  __CheckForDebuggerJustMyCode(&unk_40B027);
  v5 = strlen(Str);
  for ( i = 0; i < v5; ++i )
    Str1[i] += Str[i] ^ 0x1F;
  if ( !strcmp(Str1, "DH~mqqvqxB^||zll@Jq~jkwpmvez{") )
    sub_401037((char *)&byte_406B80, v3);
  else
    sub_401037("Error!\n", v3);
  return *Str1;
}

关键点:main 中提前 memset(Str1,0,50) 将数组全部置零,因此运算式可简化:

Str1[i] = Str[i] ^ 0x1F
校验条件:Str1 == "DH~mqqvqxB^||zll@Jq~jkwpmvez{"
逆推公式:Str[i] = 目标字节 ^ 0x1F

编写Python脚本还原合法密钥:

target = b"DH~mqqvqxB^||zll@Jq~jkwpmvez{"
key = bytes([c ^ 0x1F for c in target])
print(key)

运行结果:[Warnning]Access_Unauthorized

0x03 加密算法识别:标准RC4

密钥校验通过后进入 sub_401028 加密函数,通过函数调用链特征判定为标准RC4:

  • 开辟两块264字节缓冲区用于存放256位S盒(栈对齐多余空间)
  • 初始化S盒 0~255顺序赋值
  • KSA密钥调度算法:使用用户密钥打乱S盒
  • PRGA伪随机密钥流生成,逐字节异或加密文件数据

RC4特性:加密与解密使用同一套算法,使用合法密钥对密文文件 enflag.txt 解密即可还原明文。

0x04 RC4解密脚本编写

def rc4(key, data):
    S = list(range(256))
    j = 0
    key_len = len(key)
    # KSA 密钥调度
    for i in range(256):
        j = (j + S[i] + key[i % key_len]) % 256
        S[i], S[j] = S[j], S[i]
    # PRGA 密钥流生成
    i = j = 0
    result = []
    for byte in data:
        i = (i + 1) % 256
        j = (j + S[i]) % 256
        S[i], S[j] = S[j], S[i]
        keystream_byte = S[(S[i] + S[j]) % 256]
        result.append(byte ^ keystream_byte)
    return bytes(result)

if __name__ == "__main__":
    key = b"[Warnning]Access_Unauthorized"
    with open("enflag.txt", "rb") as f:
        cipher_data = f.read()
    plain_data = rc4(key, cipher_data)
    print(plain_data.decode())

0x05 运行结果 & Flag

flag{RC4&->ENc0d3F1le}

0x06 逆向考点总结

  • 逆向入门规范:优先分析main函数梳理整体业务时序
  • 跳板函数识别,跳过无意义跳转直达核心算法函数
  • 栈内存初始状态分析,简化异或逆运算推导
  • 经典RC4加密特征识别(256字节S盒、KSA+PRGA两段式结构)
  • 二进制文件加解密必须以rb模式读取,防止编码破坏原始字节

© 2026 N0N-7r4mm313D CTF Team | All Rights Reserved

Made with ❤ by Untrammeled17