程序分析

ida载入有反调试

导入表搜索发现IsDebugPresent,跟踪到sub_140001800函数

bool sub_140001800()
{
  HANDLE CurrentProcess; // rax
  BOOL pbDebuggerPresent; // [rsp+20h] [rbp-18h] BYREF
 
  if ( IsDebuggerPresent() )
    return 1;
  pbDebuggerPresent = 0;
  CurrentProcess = GetCurrentProcess();
  CheckRemoteDebuggerPresent(CurrentProcess, &pbDebuggerPresent);
  return pbDebuggerPresent;
}

回追进汇编nop掉if判断逻辑后可以正常调试

__int64 __fastcall sub_140001860(CDialog *a1)
{
  CDialog::OnInitDialog(a1);
  sub_140002E30(a1, *((_QWORD *)a1 + 46), 1LL);
  sub_140002E30(a1, *((_QWORD *)a1 + 46), 0LL);
  if ( !sub_140001800() )
    return 1LL;
  CDialog::EndDialog(a1, 2);
  return 0LL;
}

运行发现输入错误的flag会在编辑框中显示key错误

CWnd::SetWindowTextW处下断点,跟踪到主要逻辑

// Hidden C++ exception states: #wind=3
__int64 __fastcall sub_140001A60(__int64 a1)
{
  __int64 v1; // rax
  char *ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_; // rax
  const wchar_t *v3; // rax
  char v5; // [rsp+20h] [rbp-58h]
  __int64 (__fastcall *v6)(); // [rsp+28h] [rbp-50h]
  __int64 s[4]; // [rsp+30h] [rbp-48h] BYREF
  _BYTE v8[8]; // [rsp+50h] [rbp-28h] BYREF
  _BYTE v9[8]; // [rsp+58h] [rbp-20h] BYREF
 
  CWnd::UpdateData(a1, 1);
  v6 = off_1400100B8[2];
  v1 = ATL::CSimpleStringT<wchar_t,1>::operator wchar_t const *(a1 + 376);
  ATL::CStringT<char,StrTraitMFC_DLL<char,ATL::ChTraitsCRT<char>>>::CStringT<char,StrTraitMFC_DLL<char,ATL::ChTraitsCRT<char>>>(
    v9,
    v1);
  ATL::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>(v8);
  ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_ = ATL::CSimpleStringT<char,1>::GetString(v9);
  sub_140001D20(s, ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_);
  v5 = (v6)(s);
  sub_140002C70(s);
  if ( v5 )
    ATL::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>::LoadStringW(v8, 101LL);
  else
    ATL::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>::LoadStringW(v8, 102LL);
  v3 = ATL::CSimpleStringT<wchar_t,1>::operator wchar_t const *(v8);
  CWnd::SetWindowTextW((a1 + 384), v3);
  ATL::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>::~CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>(v8);
  return ATL::CStringT<char,StrTraitMFC_DLL<char,ATL::ChTraitsCRT<char>>>::~CStringT<char,StrTraitMFC_DLL<char,ATL::ChTraitsCRT<char>>>(v9);
}

动调发现v6是一个指向主要验证逻辑的函数指针,跟踪查看逻辑

// Hidden C++ exception states: #wind=3
__int64 __fastcall sub_1400040A0(__int64 input)
{
  _QWORD *s0_head; // rax
  _QWORD *head; // rax
  _QWORD *v3; // rax
  char v5; // [rsp+20h] [rbp-158h] BYREF
  unsigned __int8 v6; // [rsp+21h] [rbp-157h]
  _BYTE v7[6]; // [rsp+22h] [rbp-156h] BYREF
  __int64 v8; // [rsp+28h] [rbp-150h]
  _QWORD *s0_end; // [rsp+30h] [rbp-148h]
  __int64 v10; // [rsp+38h] [rbp-140h]
  _QWORD *end; // [rsp+40h] [rbp-138h]
  __int64 v12; // [rsp+48h] [rbp-130h]
  _QWORD *v13; // [rsp+50h] [rbp-128h]
  __int64 v14; // [rsp+58h] [rbp-120h] BYREF
  __int64 v15; // [rsp+60h] [rbp-118h] BYREF
  __int64 v16; // [rsp+68h] [rbp-110h] BYREF
  __int64 v17; // [rsp+70h] [rbp-108h] BYREF
  __int64 v18; // [rsp+78h] [rbp-100h] BYREF
  __int64 v19; // [rsp+80h] [rbp-F8h] BYREF
  __int64 buf_1[3]; // [rsp+88h] [rbp-F0h] BYREF
  _BYTE buf[24]; // [rsp+A0h] [rbp-D8h] BYREF
  __int64 buf_2_input[3]; // [rsp+B8h] [rbp-C0h] BYREF
  _BYTE buf_0[24]; // [rsp+D0h] [rbp-A8h] BYREF
  __int64 s_1[4]; // [rsp+E8h] [rbp-90h] BYREF
  __int64 s_0[4]; // [rsp+108h] [rbp-70h] BYREF
  char out[32]; // [rsp+128h] [rbp-50h] BYREF
  char s_2[32]; // [rsp+148h] [rbp-30h] BYREF
 
  sub_140003FB0(s_0, 201LL);
  sub_140003FB0(s_1, 202LL);
  sub_140003FB0(s_2, 203LL);
  MIFClmnDbl::__autoclassinit2_unsigned___int64_(buf_0, 0x18uLL);// 初始化缓冲区
  v8 = unknown_libname_154(v7);
  s0_end = get_end(s_0, &v14);
  s0_head = get_head(s_0, &v15);
  sub_140004750(buf_0, *s0_head, *s0_end, v8);
  MIFClmnDbl::__autoclassinit2_unsigned___int64_(buf_1, 0x18uLL);
  v10 = unknown_libname_154(&v7[1]);
  end = get_end(s_1, &v16);
  head = get_head(s_1, &v17);
  sub_140004750(buf_1, *head, *end, v10);
  if ( len(buf_1) != 16 )
  {
    v5 = 0;
    sub_140004BD0(buf_1, 16LL, &v5);
  }
  MIFClmnDbl::__autoclassinit2_unsigned___int64_(buf_2_input, 0x18uLL);
  v12 = unknown_libname_154(&v7[2]);
  v13 = sub_1400053C0(input, &v18);
  v3 = sub_140005480(input, &v19);
  sub_140004860(buf_2_input, *v3, *v13, v12);
  MIFClmnDbl::__autoclassinit2_unsigned___int64_(buf, 0x18uLL);
  sub_140005200(buf);
  RCB_CBC(buf_0, buf_1, buf_2_input, buf);
  base64(out, buf);
  v6 = strcmp(out, s_2);
  sub_140002C70(out);
  sub_140005150(buf);
  sub_140005150(buf_2_input);
  sub_140005150(buf_1);
  sub_140005150(buf_0);
  sub_140002C70(s_2);
  sub_140002C70(s_1);
  sub_140002C70(s_0);
  return v6;
}

主要验证逻辑

RCB_CBC(buf_0, buf_1, buf_2_input, buf);
base64(out, buf);
v6 = strcmp(out, s_2);

动调发现buf_0, buf_1, buf_2_input 分别是key, iv, 和明文
在RCB_CBC中进行了加密后再base64编码后和s_2比较

解密代码

把s_2解除base64编码后解密即可

让ai跑一份rc6_cbc的解密函数

/* rc6_cbc_decrypt.c : 纯 C 实现 RC6-CBC 解密
 * 编译:  gcc rc6_cbc_decrypt.c -o rc6_cbc_decrypt
 */
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
 
/* ————— RC6 常量 ————— */
#define W   32                  /* 字长 */
#define R   20                  /* 轮数 */
#define P32 0xB7E15163u         /* e */
#define Q32 0x9E3779B9u         /* φ */
 
static inline uint32_t ROTL32(uint32_t x, uint32_t n)
{ return (x << (n & 31)) | (x >> (32 - (n & 31))); }
 
static inline uint32_t ROTR32(uint32_t x, uint32_t n)
{ return (x >> (n & 31)) | (x << (32 - (n & 31))); }
 
/* ——— 1. RC6 密钥扩展 (与加密相同) ——— */
static void rc6_key_schedule(const uint8_t *key, size_t keylen, uint32_t S[2 * (R + 2)])
{
    /* 1-1  PKCS#7 补齐到 16 / 24 / 32 字节 */
    uint8_t real[32] = {0};
    size_t legal = keylen <= 16 ? 16 : (keylen <= 24 ? 24 : 32);
    memcpy(real, key, keylen);
    uint8_t pad = (uint8_t)(legal - keylen);
    memset(real + keylen, pad, pad);
 
    /* 1-2  字节 → 小端 32-bit L[] */
    uint32_t L[8] = {0};
    size_t c = legal / 4;
    for (size_t i = 0; i < legal; ++i)
        L[i / 4] |= (uint32_t)real[i] << (8 * (i & 3));
 
    /* 1-3  初始化 S[] */
    size_t t = 2 * (R + 2);     /* 44 */
    S[0] = P32;
    for (size_t i = 1; i < t; ++i) S[i] = S[i - 1] + Q32;
 
    /* 1-4  三倍混合 */
    uint32_t A = 0, B = 0;
    size_t i = 0, j = 0, v = 3 * ((c > t) ? c : t);
    for (size_t k = 0; k < v; ++k) {
        A = S[i] = ROTL32(S[i] + A + B, 3);
        B = L[j] = ROTL32(L[j] + A + B, A + B);
        i = (i + 1) % t;
        j = (j + 1) % c;
    }
}
 
/* ——— 2. RC6 解密单块 (ECB) ——— */
static void rc6_decrypt_block(const uint8_t in[16], uint8_t out[16], const uint32_t S[44])
{
    uint32_t A = ((uint32_t*)in)[0];
    uint32_t B = ((uint32_t*)in)[1];
    uint32_t C = ((uint32_t*)in)[2];
    uint32_t D = ((uint32_t*)in)[3];
 
    /* 逆向最后的操作 */
    C -= S[43];
    A -= S[42];
 
    /* 逆向主轮循环 */
    for (uint32_t i = R; i >= 1; --i) {
        /* 逆向轮转寄存器 */
        uint32_t tmp = D;  D = C;  C = B;  B = A;  A = tmp;
        
        uint32_t u = ROTL32(D * (2 * D + 1), 5);
        uint32_t t = ROTL32(B * (2 * B + 1), 5);
        C = ROTR32(C - S[2 * i + 1], t) ^ u;
        A = ROTR32(A - S[2 * i], u) ^ t;
    }
 
    /* 逆向初始操作 */
    D -= S[1];
    B -= S[0];
 
    ((uint32_t*)out)[0] = A;
    ((uint32_t*)out)[1] = B;
    ((uint32_t*)out)[2] = C;
    ((uint32_t*)out)[3] = D;
}
 
/* ——— 3. 移除 PKCS#7 填充 ——— */
static size_t remove_pkcs7_padding(uint8_t *data, size_t len)
{
    if (len == 0) return 0;
    
    uint8_t pad = data[len - 1];
    if (pad == 0 || pad > 16) return len;  /* 无效填充 */
    
    /* 检验填充是否正确 */
    for (size_t i = len - pad; i < len; ++i) {
        if (data[i] != pad) return len;  /* 填充错误 */
    }
    
    return len - pad;
}
 
/* ——— 4. RC6-CBC 解密接口 ——— */
size_t rc6_cbc_decrypt(const uint8_t *key, size_t keylen,
                       const uint8_t iv[16],
                       const uint8_t *ciphertext, size_t ct_len,
                       uint8_t **plaintext)
{
    if (ct_len % 16 != 0) return 0;  /* 密文长度必须是16的倍数 */
    
    /* 4-1  分配解密缓冲区 */
    *plaintext = malloc(ct_len);
    if (*plaintext == NULL) return 0;
 
    /* 4-2  生成轮密钥 */
    uint32_t S[44];
    rc6_key_schedule(key, keylen, S);
 
    /* 4-3  CBC 解密迭代 */
    uint8_t prev[16];
    memcpy(prev, iv, 16);
 
    for (size_t off = 0; off < ct_len; off += 16) {
        uint8_t block[16];
        
        /* 解密当前块 */
        rc6_decrypt_block(ciphertext + off, block, S);
        
        /* 与前一个密文块异或 (CBC) */
        for (int i = 0; i < 16; ++i) {
            (*plaintext)[off + i] = block[i] ^ prev[i];
        }
        
        /* 更新prev为当前密文块 */
        memcpy(prev, ciphertext + off, 16);
    }
 
    /* 4-4  移除PKCS#7填充 */
    size_t real_len = remove_pkcs7_padding(*plaintext, ct_len);
    
    return real_len;
}
 
/* ——— 5. 十六进制字符串转字节数组 ——— */
static size_t hex_to_bytes(const char *hex_str, uint8_t **bytes)
{
    size_t len = strlen(hex_str);
    if (len % 2 != 0) return 0;  /* 长度必须为偶数 */
    
    size_t byte_len = len / 2;
    *bytes = malloc(byte_len);
    if (*bytes == NULL) return 0;
    
    for (size_t i = 0; i < byte_len; ++i) {
        int high = (hex_str[2*i] >= '0' && hex_str[2*i] <= '9') ? hex_str[2*i] - '0' : 
                   (hex_str[2*i] >= 'A' && hex_str[2*i] <= 'F') ? hex_str[2*i] - 'A' + 10 :
                   (hex_str[2*i] >= 'a' && hex_str[2*i] <= 'f') ? hex_str[2*i] - 'a' + 10 : -1;
        int low = (hex_str[2*i+1] >= '0' && hex_str[2*i+1] <= '9') ? hex_str[2*i+1] - '0' : 
                  (hex_str[2*i+1] >= 'A' && hex_str[2*i+1] <= 'F') ? hex_str[2*i+1] - 'A' + 10 :
                  (hex_str[2*i+1] >= 'a' && hex_str[2*i+1] <= 'f') ? hex_str[2*i+1] - 'a' + 10 : -1;
        
        if (high == -1 || low == -1) {
            free(*bytes);
            return 0;
        }
        
        (*bytes)[i] = (uint8_t)((high << 4) | low);
    }
    
    return byte_len;
}
 
/* ——— 6. 示例 main ——— */
int main(void)
{
    const uint8_t key[] = "FSZ36f3vU8s5";          /* 12 字节原始密钥 */
    const uint8_t iv[16] = "WcE4Bbm4kHYQsAcX";     /* 16 字节初始向量 */
    
    /* 你提供的密文十六进制字符串 */
    const char *cipher_hex = "44A0936B3F9FB72D49DAAB33E0323AB7D6E63222C1C6A16BA48EF47D4E0831E99CCC894CFB3D48A154286C8B7531B5C5";
    
    /* 将十六进制字符串转换为字节数组 */
    uint8_t *ciphertext = NULL;
    size_t ct_len = hex_to_bytes(cipher_hex, &ciphertext);
    
    if (ct_len == 0) {
        printf("错误: 无法解析十六进制密文\n");
        return 1;
    }
    
    printf("密文长度: %zu 字节\n", ct_len);
    
    /* 解密 */
    uint8_t *plaintext = NULL;
    size_t pt_len = rc6_cbc_decrypt(key, sizeof(key) - 1, iv, 
                                    ciphertext, ct_len, &plaintext);
    
    if (pt_len == 0) {
        printf("解密失败\n");
        free(ciphertext);
        return 1;
    }
    
    printf("解密结果: ");
    for (size_t i = 0; i < pt_len; ++i) {
        if (plaintext[i] >= 32 && plaintext[i] <= 126) {
            putchar(plaintext[i]);  /* 可打印字符 */
        } else {
            printf("\\x%02X", plaintext[i]);  /* 不可打印字符用十六进制显示 */
        }
    }
    printf("\n");
    
    /* 清理内存 */
    free(ciphertext);
    free(plaintext);
    return 0;
}

flag{68f25cc8-1a9f-40e8-ac3b-a85982a52f8f}