今年是第二年打祥云杯,只能说逆向的题型多钟多样,上来一个内核题就给我整蒙了。
出了这两个小题目,简单记录一下。
勒索解密
吐槽:你家勒索病毒连个图形化都没有,谁知道往哪儿给你打钱啊
这个题是win32的逆向,主要的逻辑比较清晰的,重点就是加密,然后疯狂的查文档去看就行了,主要分析如下:
main函数还是比较清晰的:
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v3; // ecx
void *v4; // ecx
void *v5; // eax
_DWORD *i; // esi
unsigned int v7; // edi
int v8; // ebx
void **v9; // ebx
void **v10; // edi
size_t v11; // ecx
size_t v12; // ecx
void *v13; // ecx
void *v14; // eax
int v16; // [esp+10h] [ebp-C0h] BYREF
char v17[4]; // [esp+14h] [ebp-BCh]
void *Block[5]; // [esp+18h] [ebp-B8h] BYREF
unsigned int v19; // [esp+2Ch] [ebp-A4h]
void *v20[6]; // [esp+30h] [ebp-A0h] BYREF
HCRYPTPROV phProv[21]; // [esp+48h] [ebp-88h] BYREF
void *Src[5]; // [esp+9Ch] [ebp-34h] BYREF
unsigned int v23; // [esp+B0h] [ebp-20h]
__int64 v24; // [esp+B4h] [ebp-1Ch] BYREF
int v25; // [esp+BCh] [ebp-14h]
int v26; // [esp+CCh] [ebp-4h]
v19 = 15;
Block[4] = 0;
LOBYTE(Block[0]) = 0;
sub_11458A0(Block, ".bmp", 4u);
v26 = 0;
sub_1146D00((int)&v16, v3, Block, v17[0]);
v26 = -1;
if ( v19 >= 0x10 )
{
v4 = Block[0];
if ( v19 + 1 >= 0x1000 )
{
if ( ((int)Block[0] & 0x1F) != 0 )
LABEL_4:
_invalid_parameter_noinfo_noreturn();
v5 = (void *)*((_DWORD *)Block[0] - 1);
if ( v5 >= Block[0] )
_invalid_parameter_noinfo_noreturn();
if ( (unsigned int)(Block[0] - v5) < 4 )
_invalid_parameter_noinfo_noreturn();
if ( (unsigned int)(Block[0] - v5) > 0x23 )
_invalid_parameter_noinfo_noreturn();
v4 = (void *)*((_DWORD *)Block[0] - 1);
}
j_j___free_base(v4);
}
v24 = 0i64;
v25 = 0;
v26 = 1;
sub_1145AA0(1, (char)"C:\XX_CTF_XX\", (int)&v24);
for ( i = (_DWORD *)v24; i != (_DWORD *)HIDWORD(v24); i += 8 )// 遍历文件进行加密
{
v7 = i[6];
v8 = i[7]; // i应该是个结构体数组
// i[0] 指向文件名
// i[6]是文件长度
v23 = 15;
Src[4] = 0;
LOBYTE(Src[0]) = 0;
sub_1145580(Src, i, 0, 0xFFFFFFFF); // 读取文件或者校验文件
//
LOBYTE(v26) = 2;
if ( v8 | v7 && v8 <= 0 )
{
if ( v7 <= 0x100000 )
{
sub_1146B70(v20, (int)Src); // 创建一个新文件?
memset(phProv, 0, sizeof(phProv));
phProv[8] = 15;
phProv[7] = 0;
LOBYTE(phProv[3]) = 0;
phProv[16] = 15;
phProv[15] = 0;
LOBYTE(phProv[11]) = 0;
phProv[0] = 0;
phProv[1] = 0;
phProv[2] = 0;
phProv[10] = 0;
phProv[18] = 0;
phProv[19] = 0;
phProv[9] = 0;
phProv[17] = 0;
phProv[20] = 0;
LOBYTE(v26) = 4;
v9 = v20;
if ( v20[5] >= (void *)0x10 )
v9 = (void **)v20[0];
v10 = Src;
if ( v23 >= 0x10 )
v10 = (void **)Src[0];
if ( CryptAcquireContextA(phProv, 0, "Microsoft Enhanced RSA and AES Cryptographic Provider", 0x18u, 0xF0000000) )
{
if ( *(_BYTE *)v10 )
v11 = strlen((const char *)v10);
else
v11 = 0;
sub_11458A0(&phProv[3], v10, v11);
if ( *(_BYTE *)v9 )
v12 = strlen((const char *)v9);
else
v12 = 0;
sub_11458A0(&phProv[11], v9, v12);
sub_11418F0((int)phProv); // 这里创建了key
// 并进行了加密操作
if ( phProv[10] )
j_j_j___free_base((void *)phProv[10]);
if ( phProv[18] )
j_j_j___free_base((void *)phProv[18]);
if ( phProv[19] )
j_j_j___free_base((void *)phProv[19]);
if ( phProv[1] )
CryptDestroyKey(phProv[1]);
if ( phProv[2] )
CryptDestroyKey(phProv[2]);
if ( phProv[0] )
CryptReleaseContext(phProv[0], 0);
}
else
{
if ( phProv[10] )
j_j_j___free_base((void *)phProv[10]);
if ( phProv[18] )
j_j_j___free_base((void *)phProv[18]);
if ( phProv[19] )
j_j_j___free_base((void *)phProv[19]);
if ( phProv[1] )
CryptDestroyKey(phProv[1]);
if ( phProv[2] )
CryptDestroyKey(phProv[2]);
if ( phProv[0] )
CryptReleaseContext(phProv[0], 0);
}
sub_11459A0(&phProv[11]); // 释放一些资源
sub_11459A0(&phProv[3]);
sub_11459A0(v20);
LOBYTE(v26) = 1;
sub_11459A0(Src);
}
else
{
LOBYTE(v26) = 1;
sub_11459A0(Src);
}
}
else
{
LOBYTE(v26) = 1;
if ( v23 >= 0x10 )
{
v13 = Src[0];
if ( v23 + 1 >= 0x1000 )
{
if ( ((int)Src[0] & 0x1F) != 0 )
goto LABEL_4;
v14 = (void *)*((_DWORD *)Src[0] - 1);
if ( v14 >= Src[0] || (unsigned int)(Src[0] - v14) < 4 || (unsigned int)(Src[0] - v14) > 0x23 )
goto LABEL_4;
v13 = (void *)*((_DWORD *)Src[0] - 1);
}
j_j___free_base(v13);
}
}
}
sub_1146390(&v24);
return 0;
}
重点看创建了key和加密部分的这个函数,跟进去:
char __thiscall sub_11418F0(int this)
{
char v3; // bl
int v4; // ecx
BOOL v5; // eax
int v6; // ecx
unsigned int v7; // edi
void *v8; // eax
__int128 v9; // [esp+Ch] [ebp-84h] BYREF
HCRYPTHASH phHash; // [esp+1Ch] [ebp-74h] BYREF
int v11[23]; // [esp+20h] [ebp-70h] BYREF
__int128 pbData; // [esp+7Ch] [ebp-14h] BYREF
if ( !(unsigned __int8)sub_11410F0() )
return 0;
pbData = 0ui64;
v9 = 0ui64;
v11[0] = 0x67452301; // 这里像是key
//
v11[1] = 0xEFCDAB89;
v11[2] = 0x98BADCFE;
v11[3] = 0x10325476;
v11[4] = 0;
v11[5] = 0;
sub_1147290(0x41u);
sub_11473F0(&v9, (int)v11);
*(_QWORD *)&pbData = __PAIR64__(DWORD1(v9), HIDWORD(v9));
HIDWORD(pbData) = v9; // 这里的具体操作是:
// "0123456789abcdeffedcba9876543210" 转码:
// v9="21e7b18e4fd4544b28c8aea3b22fc60e"
DWORD2(pbData) = _time64(0);
phHash = 0;
v3 = 0;
if ( CryptCreateHash(*(_DWORD *)this, 0x800Cu, 0, 0, &phHash) )// sha256
{
if ( CryptHashData(phHash, (const BYTE *)&pbData, 16u, 0) )// 应该是把key弄过去了
// 对上面的16个字符进行sha256
{
v5 = CryptDeriveKey(*(_DWORD *)this, 0x660Eu, phHash, 0, (HCRYPTKEY *)(this + 4));// AES 660e
//
v3 = 0;
v4 = 1;
if ( v5 )
v3 = 1;
}
}
if ( phHash )
CryptDestroyHash(phHash);
if ( !v3 )
return 0;
if ( !sub_11415D0((HCRYPTPROV *)this, v4) ) // 解密了一个公钥?
// 使用base64存的
//
return 0;
if ( !sub_1141700((void *)this, &pbData, v6) )// 加密
return 0;
v7 = 16 * ((*(_DWORD *)(this + 36) + 15) / 16 + 1);
v8 = operator new[](v7, (const struct std::nothrow_t *)&unk_1175EB0);
*(_DWORD *)(this + 72) = v8;
if ( !v8 )
return 0;
memset(v8, 0, v7);
memmove_0(*(void **)(this + 72), *(const void **)(this + 40), *(_DWORD *)(this + 36));
if ( !sub_11417D0((_DWORD *)this) ) // 最后的对flag文件进行的加密
//
return 0;
sub_1141400((_DWORD *)this); // 写文件
return 1;
}
重点就是这个key是怎么来的,然后下面的aes 660e是查文档知道的。
这个key有一部分是time(0)的时间戳,动调一下和输入无关,这个地方我的第一想法是爆破,但是转念一想好像没必要,直接查了flag文件的创建时间就知道了时间戳,夹在里面就得到了key:
然后关注文件输出的部分:
重点就是这三部分的内容,我自己写flag试了一试,第一部分是文件加密的结果,第二部分是key加密的结果,key我们有了,不关心,最后还有4bytes的内容,也无所谓,最后直接对flag.bmp.ctf_encrypt解密然后去掉最后的东西就行了
脚本如下(写的比较乱,很多测试数据在里面,将就一下):
from Crypto.Cipher import AES
import base64
import hashlib
import time
# 字符类型的时间
tss1 = '2021-8-16 15:17:25'
tss1 = '2021-8-21 16:21:37'
timeArray = time.strptime(tss1, "%Y-%m-%d %H:%M:%S")
timeStamp = int(time.mktime(timeArray))
print(hex(timeStamp))
def fill(b, x):
while len(b) % 16 != 0:
b += x
return b
#0x6120b08d
#0x611a1105 0x6120b791
# data= b'flag{12345678901234567890}'
data = b"xa2Kx19.Rxac1xcd0xb1x06x86 x1bx9dxc1xc854Sx88x1ax9axd9xf0xa1Sxc12xa5.$xadHx03x1fJxb5L'bxdb'x8cO4xd4x94"
data = open('flag.bmp.ctf_crypter','rb').read()
# key = bytes.fromhex('B22FC60E4FD4544B6bAE206121E7B18E')
#key = bytes.fromhex('B22FC60E4FD4544B8db0206121E7B18E')
key = bytes.fromhex('B22FC60E4FD4544B05111a6121E7B18E') #flag
#key = bytes.fromhex('B22FC60E4FD4544B91b7206121E7B18E')
key = hashlib.new('sha256', key).digest()[:16]
print(key)
print(len(key))
aes = AES.new(key, AES.MODE_CBC, iv=b'x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x01')
_data = aes.decrypt(fill(data, b'x00'))
#print(_data)
open('flag.bmp','wb').write(_data)
最后就是简单的AES-CBC,但是我根据文件头去掉多余信息以后图片查看器还是打不开,但是ps能打开,就不管那么多了,直接拿flag:
Rev_Dizzy
简单题,就是IDA会卡住,出来5000行代码,对输入进行+-^运算,把所有+换成-,-换成+,然后异或不变反着跑一下直接就能出来了,脚本太长不贴了,结果如下:
结语
后面还有一个macos的题,一个hardware的题,一个内核APC题,没做出来,没办法,慢慢补了。
就这样吧。