今年是第二年打祥雲杯,只能說逆向的題型多鍾多樣,上來一個內核題就給我整蒙了。
出了這兩個小題目,簡單記錄一下。
勒索解密
吐槽:你家勒索病毒連個圖形化都沒有,誰知道往哪兒給你打錢啊
這個題是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"\xa2K\x19.R\xac1\xcd0\xb1\x06\x86\t\x1b\x9d\xc1\xc854S\x88\x1a\x9a\xd9\xf0\xa1S\xc12\xa5.$\xadH\x03\x1fJ\xb5L'b\xdb'\x8cO4\xd4\x94"
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'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01')
_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題,沒做出來,沒辦法,慢慢補了。
就這樣吧。