很有意思的一道题

  1. 很有意思的一道题

很有意思的一道题

前言

第一次看耐下性子看官方文档写题(太菜了),记录一下。

ida打开直接分析,C++写的,一堆没见过的库,上网查了下,是wincrypt.h库函数的内容。

int sub_443A60()
{
  BYTE pbData__1[4]; // [esp+D0h] [ebp-D4h] BYREF
  char n125; // [esp+D4h] [ebp-D0h]
  char v3; // [esp+D5h] [ebp-CFh]
  char v4; // [esp+D6h] [ebp-CEh]
  char v5; // [esp+D7h] [ebp-CDh]
  char v6; // [esp+D8h] [ebp-CCh]
  char n57; // [esp+D9h] [ebp-CBh]
  char v8; // [esp+DAh] [ebp-CAh]
  char n48; // [esp+DBh] [ebp-C9h]
  char v10; // [esp+DCh] [ebp-C8h]
  char n33; // [esp+DDh] [ebp-C7h]
  char n90; // [esp+DEh] [ebp-C6h]
  char v13; // [esp+DFh] [ebp-C5h]
  char n34; // [esp+E0h] [ebp-C4h]
  char n46; // [esp+E1h] [ebp-C3h]
  char n55; // [esp+E2h] [ebp-C2h]
  char n69; // [esp+E3h] [ebp-C1h]
  char n79; // [esp+E4h] [ebp-C0h]
  char v19; // [esp+E5h] [ebp-BFh]
  char n48_1; // [esp+E6h] [ebp-BEh]
  char v21; // [esp+E7h] [ebp-BDh]
  char v22; // [esp+E8h] [ebp-BCh]
  char v23; // [esp+E9h] [ebp-BBh]
  char v24; // [esp+EAh] [ebp-BAh]
  char v25; // [esp+EBh] [ebp-B9h]
  char n4; // [esp+ECh] [ebp-B8h]
  char v27; // [esp+EDh] [ebp-B7h]
  char v28; // [esp+EEh] [ebp-B6h]
  char v29; // [esp+EFh] [ebp-B5h]
  int pdwDataLen[3]; // [esp+F8h] [ebp-ACh] BYREF
  BYTE pbData_[4]; // [esp+104h] [ebp-A0h] BYREF
  int v32[10]; // [esp+110h] [ebp-94h] BYREF
  BYTE pbData[12]; // [esp+138h] [ebp-6Ch] BYREF
  char v34[40]; // [esp+144h] [ebp-60h] BYREF
  int phKey[3]; // [esp+16Ch] [ebp-38h] BYREF
  int phProv[3]; // [esp+178h] [ebp-2Ch] BYREF
  char Src[5]; // [esp+184h] [ebp-20h] BYREF
  char f_[15]; // [esp+189h] [ebp-1Bh] BYREF
  int v39; // [esp+198h] [ebp-Ch] BYREF

  __CheckForDebuggerJustMyCode(&unk_4C70A3);
  v39 = 0;
  sub_4391EF(&unk_4B6F60, &v39);
  Src[0] = 8;
  Src[1] = 2;
  Src[2] = 0;
  Src[3] = 0;
  Src[4] = 16;
  strcpy(f_, "f");
  f_[2] = 0;
  strcpy(&f_[3], " ");
  f_[5] = 0;
  f_[6] = 0;
  phProv[0] = 0;
  phKey[0] = 0;
  CryptAcquireContextA(phProv, 0, 0, 24u, 0);   // BOOL CryptAcquireContextA(
                                                //   [out] HCRYPTPROV *phProv,
                                                //   [in]  LPCSTR     szContainer,
                                                //   [in]  LPCSTR     szProvider,
                                                //   [in]  DWORD      dwProvType,
                                                //   [in]  DWORD      dwFlags
                                                // );
  memset(v32, 0, 32);
  j__memmove(pbData, Src, 0xCu);
  sub_439F8C(v32, v39);
  j__memmove(v34, v32, 0x20u);
  CryptImportKey(phProv[0], pbData, 0x2Cu, 0, 0, phKey);// BOOL CryptImportKey(
                                                //   [in]  HCRYPTPROV hProv,
                                                //   [in]  const BYTE *pbData,
                                                //   [in]  DWORD      dwDataLen,
                                                //   [in]  HCRYPTKEY  hPubKey,
                                                //   [in]  DWORD      dwFlags,
                                                //   [out] HCRYPTKEY  *phKey
                                                // );
  *pbData_ = 2;
  CryptSetKeyParam(phKey[0], 4u, pbData_, 0);   // BOOL CryptSetKeyParam(
                                                //   [in] HCRYPTKEY  hKey,
                                                //   [in] DWORD      dwParam,
                                                //   [in] const BYTE *pbData,
                                                //   [in] DWORD      dwFlags
                                                // );
  pdwDataLen[0] = 32;
  qmemcpy(pbData__1, "l]4", 3);
  pbData__1[3] = -33;
  n125 = 125;
  v3 = -52;
  v4 = -1;
  v5 = -32;
  v6 = -21;
  n57 = 57;
  v8 = -101;
  n48 = 48;
  v10 = 0x80;
  n33 = 33;
  n90 = 90;
  v13 = -71;
  n34 = 34;
  n46 = 46;
  n55 = 55;
  n69 = 69;
  n79 = 79;
  v19 = -67;
  n48_1 = 48;
  v21 = -5;
  v22 = -37;
  v23 = -58;
  v24 = -85;
  v25 = -28;
  n4 = 4;
  v27 = -74;
  v28 = -18;
  v29 = -59;
  CryptDecrypt(phKey[0], 0, 1, 0, pbData__1, pdwDataLen);// NTSTATUS BCryptDecrypt(
                                                //   [in, out]           BCRYPT_KEY_HANDLE hKey,
                                                //   [in]                PUCHAR            pbInput,
                                                //   [in]                ULONG             cbInput,
                                                //   [in, optional]      VOID              *pPaddingInfo,
                                                //   [in, out, optional] PUCHAR            pbIV,
                                                //   [in]                ULONG             cbIV,
                                                //   [out, optional]     PUCHAR            pbOutput,
                                                //   [in]                ULONG             cbOutput,
                                                //   [out]               ULONG             *pcbResult,
                                                //   [in]                ULONG             dwFlags
                                                // );
  if ( *pbData__1 == 1734437990 )
    return sub_439C49("you got it %s\n", pbData__1);
  else
    return sub_439C49("wrong");
}

于是上网查微软的官方文档

首先看到语法,ida里的值和结构体中的值进行比对,然后重点看有关dwProvType的内容

image-20250424200204193

image-20250424200404406

双击进去查看

image-20250424200428616

可以看到有不少预定义的提供程序类型

然后我们开始去wincrypt.h里面找

image-20250424200623751

看到只有RSA_AES对应的值是24(0x18),那么我们就可以初步判断这是AES-RSA加密

然后继续看CryptImportKey
根据phData的值可得知AES的密钥长度是32(dataLen = 0x2C = 44 =32 + 12)。

然后再看CryptSetKeyParam,注意看dwParam的有关内容,然后也还是和上面的方法一样,去.h文件里搜所提及到的

image-20250424204322464

这里可以看到dwParam的值指定的是加密的模式,然后在官方文档中可以看到

image-20250424204332540

也就是我们所熟知的几种模式,继续在.h文件里查,ida中可知pbData_=2

image-20250424204624018

到此,可以确定是AES_CBC了

但是AES还分192,128和256,以下是确定办法

找到CryptImportKey函数的文档内容,看到结构体中的第二个参数

image-20250424212524839

查看PUBLICKEYSTRUCT

image-20250424213004131

看到最后一个参数

image-20250424213021152

双击查看AIG_ID

image-20250424213129572

image-20250424213214596

这样就确定了是AES256-CBC

接下来就是key了,查看sub_439F8C函数

int __cdecl sub_443780(int a1, int Src)
{
  int result; // eax
  int n15; // [esp+D0h] [ebp-8h]

  result = __CheckForDebuggerJustMyCode(&unk_4C70A3);
  for ( n15 = 15; n15 >= 0; --n15 )
  {
    j__memmove((a1 + 2 * n15), &Src, 2u);
    Src ^= (Src << 7) ^ (Src << 11) ^ (Src >> 5);
    result = n15 - 1;
  }
  return result;
}

但是真实的不是右移5位,具体怎么确认的,就是附加进程调试,然后观察运行结果,同时自己写一个一样的,以二进制的形式去比对哪一位有出入,因为是相互异或,所以好确认是哪一位出问题了,找到问题,反推出是在哪里位移的时候出问题了

最后附上解密脚本

from Crypto.Cipher import AES
import binascii

def keygen(src):
    key = bytearray(32)
    for i in range(15, -1, -1):
        key[2 * i:2 * i + 2] = (src & 0xFFFF).to_bytes(2, byteorder='little')
        src ^= ((src & 0xFFFF) << 7) ^ ((src & 0xFFFF) << 11) ^ ((src & 0xFFFF) >> 4)
    return key
ciphertext_hex = "6c5d34df7dccffe0eb399b3080215ab9222e37454fbd30fbdbc6abe404b6eec5"
ciphertext = binascii.unhexlify(ciphertext_hex)
target_prefix = b"666c6167"
for src in range(0x0, 0xFFFFFFFF + 1):
    key = keygen(src)
    cipher = AES.new(key, AES.MODE_ECB)
    try:
        decrypted = cipher.decrypt(ciphertext)
        if decrypted.hex().startswith(target_prefix.decode()):
            print(f"Found SRC: {src:#010x}")
            print(f"Decrypted plaintext: {decrypted.hex()}")
            break
    except Exception as e:
        continue

image-20250424215544201

flag{father_Debug_child_is_fUN!}


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 1621925986@qq.com

💰

×

Help us with donation