祥云杯2021 Windows R0題 Rev_APC

一看題目給出的sys文件,可以確定是本人最擅長的Windows內核和注入相關題目,必須要把這個驅動安排的明明白白的。
附件已上傳對應的i64文件,把驅動拖入IDA分析,發現創建了通信設備。

解密加密的dll文件數據,釋放到C:\WINDOWS\TEMP\InjectDLL.dll。

注冊了minifilter,暫時沒有發現這個minifilter有什么用,可能是配合LoadImage回調里的計算文件名用的。

還另外創建了LPC端口用來與r3通信。

創建一個SystemThread監聽LPC端口:

創建了進程通知回調和LoadImage回調:

先來看進程通知回調:

insert_contextlist這邊取了一波進程映像名字符串的hash,如果是explorer.exe,就設置context+301的flag,其實就是指定注入explorer.exe。

來看LoadImage回調。
如果當前執行該回調的進程是explorer.exe,則插入一個內核模式apc:


fntable[0x100]的地方是執行注入r3的函數,所以文件名算出來的合必須為0x100。

注入shellcode的過程是用ZwMapViewOfSection來申請R3注入代碼所需的RWX內存,然后插入用戶模式APC,異步執行R3注入代碼。


R3 shellcode:

至此,該驅動的基本功能已分析完畢。
構建調試環境條件
因為要求注入的是explorer.exe,但是注入系統的explorer.exe會導致進程崩潰,所以我自己編譯了一份與explorer.exe映像名稱相同的exe來調試InjectDLL.dll。
我這里預先計算出來了一個文件名來繞過Load。
Image回調的限制

分析InjectDLL.dll
先Load了一個不存在的dll,調用了里面的"GetContentHash"函數:

字符串"test"對應的hash為36F028580BB02CC8272A9A020F4200E346E276AE664E45EE80745574E2F5AB80,經過一番搜索后,可知這是SHA3-256算法。
在線計算得出字符串'AkiraDDL'的SHA3-256值為9d5f741799d7e62274f01963516316d2eb6888b737bab0a2b0e1774e3b7389e5。
手動編寫一個dll,導出GetContentHash函數,粗略cmp一下就行。

創建設備與R0通信,然后再使用lpc和r0通信,這里的數據都是固定的,動態調試dump下來就行。

這里的rand沒有設定隨機數種子,所以可以模擬出來每次的返回值。

case里面是與驅動通信來加密buf的,將各個加密函數求出逆運算即可解密flag。
flag : flag{Kmode_Umode_Communication!}
解密代碼:
#include
#include "windows.h"
#include "intrin.h"
void re_dec1(PCHAR b1, PCHAR b2) {
char v9;
char v8;
for (int i = 0x1F; i >= 0; i--) {
v9 = i;
v8 = b1[i];
b2[i] ^= v8;
b1[i] -= 0x10;
}
}
void re_dec2(PCHAR b1, PCHAR b2) {
char v8 = 0;
for (int i = 0x1F; i >= 0; i--) {
char b1v = b1[i];
_asm mov al, b1v;
_asm ror al, 4;
_asm mov v8, al;
b2[i] ^= v8;
}
for (int i = 0x1F; i >= 0; i--) {
b1[i] += 80;
}
}
void re_dec3(PCHAR b1, PCHAR b2) {
for (int i = 0x1F; i >= 0; i--) {
b2[i] ^= b1[i];
}
}
void re_dec4(PCHAR b1, PCHAR b2) {
/*for (int i = 0xF; i >= 0; i--) {
char v = b1[i * 2];
b2[i * 2 + 1] ^= v >> 4;
b2[i * 2] ^= v * 16;
}*/
BYTE* v8; // r8
BYTE* fu_sz; // r10
unsigned __int64 v10; // r9
unsigned __int8 v_pbuf1; // cl
v8 = (BYTE*)(b2 + 1);
fu_sz = (BYTE*)-0x20;
v10 = 0x10;// 0x10
do
{
v_pbuf1 = v8[(DWORD64)fu_sz - 1];
*v8 ^= v_pbuf1 >> 4;
v8 += 2;
*(v8 - 3) ^= 16 * v_pbuf1;
--v10;
} while (v10);
for (int i = 0x1F; i >= 0; i--) {
b1[i] += 80;
}
}
void re_dec5(PCHAR b1, PCHAR b2) {
for (int i = 0x1F; i >= 0; i--) {
b2[i] ^= b1[i];
}
char* buf1_right = &b1[0x10];
char* buf1_left = &b1[0x10 - 1];
for (int i = 0xF; i >= 0; i--) {
char temp = 0;
temp = *buf1_right;
*buf1_right = *buf1_left;
*buf1_left = temp;
buf1_left--;
buf1_right++;
}
char* buf1_start = b1;
char* buf1_end = &b1[0x20 - 1];
for (int i = 0xF; i >= 0; i--) {
char temp = 0;
temp = *buf1_start;
*buf1_start = *buf1_end;
*buf1_end = temp;
buf1_start++;
buf1_end--;
}
}
void re_dec6(PUCHAR b1, PUCHAR b2) {
unsigned char v8 = 0;
unsigned char v7 = 0;
unsigned char v6 = 0;
for (int i = 0x1F; i >= 0; i--) {
if (b1[i] == 0x50)
continue;
if (b1[i] == 0x80)
__debugbreak();
if (b1[i] > 0x50 && b1[i] <= 0xCF) {
v8 = b1[i];
b1[i] += 0x30;
b2[i] += v8;
}
else if (b1[i] > 0x20 && b1[i] <= 0x4F) {
v7 = b1[i];
b1[i] += 0x30;
b2[i] ^= v7 >> 4;
}
else if (b1[i] > 0xD0 && b1[i] <= 0xFF) {
v6 = b1[i];
b1[i] += 0x50;
b2[i] -= v6;
}
}
}
int main()
{
unsigned char pstatic[] = { 0xF5, 0x9A, 0xF7, 0xA1, 0xC4, 0xA7, 0xD6, 0x23, 0xE1, 0x28, 0xEF, 0xB8, 0xDE, 0x23, 0xE7, 0x2F };
unsigned char pebuf[] = { 0xDC, 0xA7, 0xCA, 0x92, 0xFE, 0x9D, 0xED, 0xB8, 0x70, 0x29, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5 };
unsigned char use_buf1[0x20] = { 0 };
memcpy(use_buf1, pstatic, 0x10);
memcpy(use_buf1 + 0x10, pebuf, 0x10);
unsigned char use_buf2[0x20] = { 0 };
*(ULONG64*)use_buf2 = 0x2F34A83A1B38C557;
*(ULONG64*)(use_buf2 + 0x8) = 0xEE8F2F04E4C69739;
*(ULONG*)(use_buf2 + 0x10) = 0x6780515E;
*(ULONG*)(use_buf2 + 0x14) = 0x486FC924;
*(ULONG*)(use_buf2 + 0x18) = 0xC7BD7F5B;
*(ULONG*)(use_buf2 + 0x1C) = 0xEBC2C2B0;
unsigned char all_buf[0x40] = { 0 };
memcpy(all_buf, use_buf1, 0x20);
memcpy(all_buf + 0x20, use_buf2, 0x20);
char* pbuf1 = (char*)all_buf;
char* pbuf2 = (char*)(all_buf + 0x20);
int reverseidx[32] = { 0 };
HMODULE hmod = LoadLibraryA("ucrtbase.dll");
typedef int (*fnrand)();
fnrand prand = (fnrand)GetProcAddress(hmod, "rand");
for (int i = 0; i < 32; i++) {
int v = prand() % 6;
reverseidx[i] = v;
printf("%d", v);
}
for (int j = 31; j >= 0; j--) {
int i = reverseidx[j];
switch (i)
{
case 0:
re_dec1(pbuf1, pbuf2);
break;
case 1:
re_dec2(pbuf1, pbuf2);
break;
case 2:
re_dec3(pbuf1, pbuf2);
break;
case 3:
re_dec4(pbuf1, pbuf2);
break;
case 4:
re_dec5(pbuf1, pbuf2);
break;
case 5:
re_dec6((PUCHAR)pbuf1, (PUCHAR)pbuf2);
break;
default:
break;
}
}
}