很有意思的一道题
前言:
第一次看耐下性子看官方文档写题(太菜了),记录一下。
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的内容
双击进去查看
可以看到有不少预定义的提供程序类型
然后我们开始去wincrypt.h里面找
看到只有RSA_AES对应的值是24(0x18),那么我们就可以初步判断这是AES-RSA加密
然后继续看CryptImportKey
根据phData的值可得知AES的密钥长度是32(dataLen = 0x2C = 44 =32 + 12)。
然后再看CryptSetKeyParam,注意看dwParam的有关内容,然后也还是和上面的方法一样,去.h文件里搜所提及到的
这里可以看到dwParam的值指定的是加密的模式,然后在官方文档中可以看到
也就是我们所熟知的几种模式,继续在.h文件里查,ida中可知pbData_=2
到此,可以确定是AES_CBC了
但是AES还分192,128和256,以下是确定办法
找到CryptImportKey函数的文档内容,看到结构体中的第二个参数
查看PUBLICKEYSTRUCT
看到最后一个参数
双击查看AIG_ID
这样就确定了是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
flag{father_Debug_child_is_fUN!}
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 1621925986@qq.com