Loading... # 驱动逆向2 ## ByteCTF2019-DancingKeys 非常简单,静态分析就能出。类似Shadow对键盘输入做了个监听 驱动主函数如下 ~~~c++ __int64 __fastcall sub_140002C90(struct _DRIVER_OBJECT *a1) { unsigned int i; // [rsp+20h] [rbp-18h] version_check(); cpu_check(); debug_check(); sub_1400033E0(a1); sub_1400028A0(a1); for ( i = 0; i < 0x1B; ++i ) a1->MajorFunction[i] = sub_1400029D0; a1->MajorFunction[3] = keyboard_listen; a1->MajorFunction[27] = remove_keyboard_listen; a1->MajorFunction[22] = sub_140002BF0; a1->MajorFunction[14] = dec_flag; a1->DriverUnload = sub_140002B50; return 0LL; } ~~~ 其中前三个函数校验了windows版本、CPU型号以及调试检测 ~~~c++ __int64 version_check() { ULONG MajorVersion; // [rsp+40h] [rbp-18h] BYREF ULONG BuildNumber; // [rsp+44h] [rbp-14h] BYREF ULONG MinorVersion[4]; // [rsp+48h] [rbp-10h] BYREF MajorVersion = 0; BuildNumber = 0; MinorVersion[0] = 0; PsGetVersion(&MajorVersion, MinorVersion, &BuildNumber, 0LL); dword_140006068 = BuildNumber * (MinorVersion[0] + MajorVersion); DbgPrintEx( 0x4Du, 0x233u, "Windows Version: %u %u %u, Version Magic: %x\n", MajorVersion, MinorVersion[0], BuildNumber, BuildNumber * (MinorVersion[0] + MajorVersion)); if ( dword_140006068 == 0xDEADBEEF && MajorVersion == 0xE9 ) { DbgPrintEx(0x4Du, 0x233u, "Okay, you are running this driver with the correct os version\n"); return 0LL; } else { DbgPrintEx(0x4Du, 0x233u, "Oh no, you should run this driver in Windows 233 instead of Windows %u\n", MajorVersion); return 3221225473LL; } } __int64 cpu_check() { unsigned __int8 *v5; // rax signed __int64 v6; // rcx unsigned __int8 v7; // dl int v8; // eax _RAX = 0LL; __asm { cpuid } unk_140006070 = _RBX; *(&unk_140006070 + 1) = _RDX; *(&unk_140006070 + 1) = _RCX; DbgPrintEx(0x4Du, 0x233u, "CPU Vendor: %s\n", &unk_140006070); v5 = &unk_140006070; v6 = "FakeIntel" - &unk_140006070; while ( 1 ) { v7 = *v5; if ( *v5 != v5[v6] ) break; ++v5; if ( !v7 ) { v8 = 0; goto LABEL_6; } } v8 = v7 < v5[v6] ? -1 : 1; LABEL_6: if ( v8 ) { DbgPrintEx( 0x4Du, 0x233u, "This Driver is incompatible with your CPU vendor \"%s\", you should use FAKE intel processor lol", &unk_140006070); return 0xC0000001LL; } else { DbgPrintEx(0x4Du, 0x233u, "Okay, you are now using fake intel CPU\n"); return 0LL; } } NTSTATUS debug_check() { return PsCreateSystemThread(&ThreadHandle, 0, 0LL, 0xFFFFFFFFFFFFFFFFLL, 0LL, StartRoutine, 0LL); } void __fastcall __noreturn StartRoutine(PVOID StartContext) { while ( 1 ) { KdDisableDebugger(); if ( dword_140006040 ) { sub_140002880(&dword_140006044, 1u); PsTerminateSystemThread(0); } sub_1400033A0(500); } } ~~~ 可知version_check要求dword_140006068值为0xDEADBEEF,cpu_check要求cpu值(unk_140006070)为`FakeIntel` 中间的基本就是在做键盘过滤程序安装、监听,其中keyboard_listen读取了16字节输入存入byte_140006890 ~~~c++ __int64 __fastcall keyboard_listen(struct _DEVICE_OBJECT *a1, IRP *a2) { if ( a1 == DeviceObject ) return sub_1400029D0(a1, a2); else return sub_140003920(a1, a2); } NTSTATUS __fastcall sub_140003920(__int64 a1, IRP *a2) { int v2; // r9d __int64 v4; // [rsp+30h] [rbp-18h] v4 = *(a1 + 64); sub_140003990(a2); LOBYTE(v2) = 1; sub_140003A60(a2, sub_1400037A0, 0, v2, 0, 0); ++dword_140006080; return IofCallDriver(*(v4 + 8), a2); } __int64 __fastcall sub_1400037A0(__int64 a1, __int64 a2) { unsigned int i; // [rsp+30h] [rbp-28h] __int64 v4; // [rsp+38h] [rbp-20h] unsigned __int64 v5; // [rsp+40h] [rbp-18h] if ( *(a2 + 48) >= 0 ) { v4 = *(a2 + 24); v5 = *(a2 + 56) / 0xCuLL; for ( i = 0; i < v5; ++i ) { if ( !*(v4 + 12LL * i + 4) ) { byte_140006890[dword_140006088] = (byte_14000608C + 42) ^ *(v4 + 12LL * i + 2); byte_14000608C = byte_140006890[dword_140006088]; DbgPrintEx(0x4Du, 0x233u, "Magic code %d: %02x\n", dword_140006088, byte_140006890[dword_140006088]); if ( ++dword_140006088 == 16 ) { DbgPrintEx(0x4Du, 0x233u, "Magic code buffer is now full\n"); dword_140006088 = 0; byte_14000608C = 0; } } } } --dword_140006080; if ( *(a2 + 65) ) sub_140003A20(a2); return *(a2 + 48); } ~~~ 最后是dec_flag ~~~c++ __int64 __fastcall dec_flag(PDEVICE_OBJECT a1, IRP *a2) { unsigned int v3; // [rsp+20h] [rbp-28h] __int64 v4; // [rsp+28h] [rbp-20h] struct _IRP *MasterIrp; // [rsp+30h] [rbp-18h] v4 = sub_140002D80(a2); if ( *(v4 + 24) == 0x222404 ) { if ( a1 == DeviceObject ) { MasterIrp = a2->AssociatedIrp.MasterIrp; v3 = *(v4 + 8); if ( MasterIrp && v3 >= 0x64 ) { sub_1400030B0(); sub_140002DD0(MasterIrp); a2->IoStatus.Information = v3; a2->IoStatus.Status = 0; IofCompleteRequest(a2, 0); return 0LL; } DbgPrintEx(0x4Du, 0x233u, "Invalid Output Buffer\n"); } else { DbgPrintEx(0x4Du, 0x233u, "Wrong device!\n"); } } else { DbgPrintEx(0x4Du, 0x233u, "Wrong device control code!\n"); } a2->IoStatus.Information = 0LL; a2->IoStatus.Status = 0; IofCompleteRequest(a2, 0); return 0LL; } ~~~ 其中sub_1400030B0对前面unk_140006070(12字节,多的补0)+dword_140006068总共16字节,每4字节做一次md5,然后保存前8字节 ~~~c++ __int64 sub_1400030B0() { __int64 result; // rax unsigned int i; // [rsp+20h] [rbp-38h] _DWORD v2[4]; // [rsp+28h] [rbp-30h] BYREF qmemcpy(v2, &unk_140006070, 0xCuLL); result = 12LL; v2[3] = dword_140006068; for ( i = 0; i < 4; ++i ) { md5(&v2[i], 4LL, &unk_140006048 + 8 * i); result = i + 1; } return result; } ~~~ sub_140002DD0很明显是在做AES-CBC解密,其中key来自unk_140006048(16字节),iv来自byte_140006890(16字节,给的word文件里),密文v13 ~~~c++ _BYTE *__fastcall sub_140002DD0(void *a1) { _BYTE *result; // rax _BYTE v2[192]; // [rsp+20h] [rbp-158h] BYREF _BYTE v3[64]; // [rsp+E0h] [rbp-98h] BYREF _BYTE v4[16]; // [rsp+120h] [rbp-58h] BYREF _BYTE v5[32]; // [rsp+130h] [rbp-48h] BYREF memset(v2, 0, sizeof(v2)); v3[0] = -119; v3[1] = -73; v3[2] = -18; v3[3] = -43; v3[4] = 126; v3[5] = -33; v3[6] = -66; v3[7] = 103; v3[8] = -69; v3[9] = -19; v3[10] = -61; v3[11] = 109; v3[12] = 81; v3[13] = 116; v3[14] = -82; v3[15] = 83; v3[16] = 3; v3[17] = -92; v3[18] = 96; v3[19] = -45; v3[20] = -25; v3[21] = -11; v3[22] = -95; v3[23] = -34; v3[24] = 7; v3[25] = 107; v3[26] = 37; v3[27] = -41; v3[28] = 87; v3[29] = -50; v3[30] = -62; v3[31] = 111; v3[32] = -45; v3[33] = -91; v3[34] = -1; v3[35] = -41; v3[36] = -116; v3[37] = 84; v3[38] = 22; v3[39] = -97; v3[40] = -29; v3[41] = -65; v3[42] = 2; v3[43] = 20; v3[44] = -98; v3[45] = 7; v3[46] = -66; v3[47] = -78; v3[48] = 109; v3[49] = 40; v3[50] = 101; v3[51] = -108; v3[52] = 25; v3[53] = -31; v3[54] = 81; v3[55] = -33; v3[56] = 126; v3[57] = 118; v3[58] = -93; v3[59] = -61; v3[60] = -78; v3[61] = 59; v3[62] = -90; v3[63] = 105; qmemcpy(v5, &unk_140006048, sizeof(v5)); qmemcpy(v4, byte_140006890, sizeof(v4)); sub_1400010D0(v2, v5, v4); sub_140001000(v2, v3, 64LL); result = v3; qmemcpy(a1, v3, 0x40uLL); return result; } __int64 __fastcall sub_1400010D0(__int64 a1, __int64 a2, const void *a3) { __int64 result; // rax sub_140002350(a1, a2); result = a1; qmemcpy((a1 + 176), a3, 0x10uLL); return result; } __int64 __fastcall sub_140002350(__int64 a1, __int64 a2) { __int64 result; // rax char v3; // [rsp+4h] [rbp-24h] char v4; // [rsp+4h] [rbp-24h] unsigned __int8 v5; // [rsp+5h] [rbp-23h] unsigned __int8 v6; // [rsp+6h] [rbp-22h] unsigned __int8 v7; // [rsp+7h] [rbp-21h] unsigned int i; // [rsp+8h] [rbp-20h] unsigned int j; // [rsp+8h] [rbp-20h] unsigned int v10; // [rsp+Ch] [rbp-1Ch] int v11; // [rsp+10h] [rbp-18h] for ( i = 0; i < 4; ++i ) { *(a1 + 4 * i) = *(a2 + 4 * i); *(a1 + 4 * i + 1) = *(a2 + 4 * i + 1); *(a1 + 4 * i + 2) = *(a2 + 4 * i + 2); *(a1 + 4 * i + 3) = *(a2 + 4 * i + 3); result = i + 1; } for ( j = 4; j < 0x2C; ++j ) { v3 = *(a1 + 4 * j - 4); v5 = *(a1 + 4 * j - 4 + 1); v6 = *(a1 + 4 * j - 4 + 2); v7 = *(a1 + 4 * j - 4 + 3); if ( !(j % 4) ) { v4 = RijnDael_AES_LONG_140005100[v5]; v5 = RijnDael_AES_LONG_140005100[v6]; v6 = RijnDael_AES_LONG_140005100[v7]; v7 = RijnDael_AES_LONG_140005100[*(a1 + 4 * j - 4)]; v3 = byte_140005300[j / 4] ^ v4; } v11 = 4 * j; v10 = 4 * j - 16; *(a1 + 4 * j) = v3 ^ *(a1 + v10); *(a1 + (v11 + 1)) = v5 ^ *(a1 + v10 + 1); *(a1 + (v11 + 2)) = v6 ^ *(a1 + v10 + 2); *(a1 + 4 * j + 3) = v7 ^ *(a1 + v10 + 3); result = j + 1; } return result; } __int64 __fastcall sub_140001000(__int64 a1, char *a2, unsigned int a3) { __int64 result; // rax unsigned __int64 i; // [rsp+20h] [rbp-38h] _BYTE v5[16]; // [rsp+28h] [rbp-30h] BYREF for ( i = 0LL; ; i += 16LL ) { result = a3; if ( i >= a3 ) break; qmemcpy(v5, a2, sizeof(v5)); aes_dec(a2, a1); sub_140002720(a2, a1 + 176); qmemcpy((a1 + 176), v5, 0x10uLL); a2 += 16; } return result; } __int64 __fastcall sub_140002720(__int64 a1, __int64 a2) { __int64 result; // rax unsigned __int8 i; // [rsp+0h] [rbp-18h] for ( i = 0; ; ++i ) { result = i; if ( i >= 0x10u ) break; *(a1 + i) ^= *(a2 + i); } return result; } ~~~ 所以写脚本解密即可 ~~~python from Crypto.Cipher import AES from hashlib import md5 s = b"FakeIntel\x00\x00\x00"+int.to_bytes(0xDEADBEEF, length=4, byteorder="little") result = b"" for i in range(4): result += md5(s[i*4:i*4+4]).digest()[:8] key = result[:16] iv = bytes.fromhex("25 40 5a 86 b5 f1 3e 58 80 9b db 0b 30 49 66 8c".replace(" ", "")) cipher = [-119, -73, -18, -43, 126, -33, -66, 103, -69, -19, -61, 109, 81, 116, -82, 83, 3, -92, 96, -45, -25, -11, -95, -34, 7, 107, 37, -41, 87, -50, -62, 111, -45, -91, -1, -41, -116, 84, 22, -97, -29, -65, 2, 20, -98, 7, -66, -78, 109, 40, 101, -108, 25, -31, 81, -33, 126, 118, -93, -61, -78, 59, -90, 105] cipher = [i&0xff for i in cipher] print(AES.new(key, AES.MODE_CBC, iv).decrypt(bytes(cipher))) ~~~ 得到`bytectf{d0f20eff6fc4f6060776c8ca63319a1e}` 接下来学习如何patch使得驱动自己吐出flag 首先要patch的点肯定是前三个函数,可以考虑直接nop掉,直接手动把dword_140006068和unk_140006070填好  然后下断点在解密flag完成后。首先先还原iv输入键值 ~~~python s = [int(i,16) for i in "25 40 5a 86 b5 f1 3e 58 80 9b db 0b 30 49 66 8c".split(" ")] b = 0 for i in range(len(s)-1, -1, -1): if i != 0: s[i] ^= (s[i-1] + 42) else: s[i] ^= 42 print(s) # [15, 15, 48, 2, 5, 46, 293, 48, 2, 49, 30, 270, 5, 19, 21, 28] ~~~ 分析可知对应按键为`Tab, Tab, B, 1, 4, C, K, B, 1, N, A, Backspace, 4, R, Y, Enter` 此外还需要写代码通过DeviceIoControl使用控制码0x222404与驱动通讯获取flag 直接采用了https://xz.aliyun.com/news/5944的代码 ~~~c #include <windows.h> #include <stdio.h> void getflag() { DWORD z = 0; char buffer[0x64] = {0}; HANDLE LINK; //“打开”驱动的符号链接 LINK = CreateFileW(L"\\\\.\\DancingKeys",0,FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_EXISTING,0,NULL); DeviceIoControl(LINK, 0x222404,buffer,0x64,buffer,0x64,&z,(LPOVERLAPPED)NULL); printf("%s\n", buffer); //关闭符号链接句柄 CloseHandle(LINK); } int main(int argc, char *argv[]) { getflag(); Sleep(100000); return 0; } ~~~ 打开编译后的程序,然后回到桌面按下刚才的键值,此时可以直接拿到flag了  ## RCTF2017-MyDriver2-397 非常早的题了,难度非常低,会调试就可以了 中间被rva坑了,以后先使用`!dh`获取入口点(address of entrypoint),然后下断点到入口点再去找具体逻辑,这个题直接断在flag解密的位置即可  下面分析下代码如何通讯获取flag,首先入口函数 ~~~c++ NTSTATUS __stdcall DriverEntry(_DRIVER_OBJECT *DriverObject, PUNICODE_STRING RegistryPath) { NTSTATUS result; // eax BOOLEAN IsWdmVersionAvailable; // al const WCHAR *v5; // rdx int v6; // ebx PVOID SystemRoutineAddress; // rax struct _UNICODE_STRING DestinationString; // [rsp+40h] [rbp-38h] BYREF struct _UNICODE_STRING SymbolicLinkName; // [rsp+50h] [rbp-28h] BYREF struct _UNICODE_STRING SystemRoutineName; // [rsp+60h] [rbp-18h] BYREF PDEVICE_OBJECT DeviceObject; // [rsp+80h] [rbp+8h] BYREF DriverObject->MajorFunction[0] = (PDRIVER_DISPATCH)&sub_116A4; DriverObject->MajorFunction[2] = (PDRIVER_DISPATCH)&sub_116A4; DriverObject->MajorFunction[14] = (PDRIVER_DISPATCH)&sub_116CC; DriverObject->DriverUnload = (PDRIVER_UNLOAD)sub_11660; RtlInitUnicodeString(&DestinationString, L"\\Device\\Myhook"); result = IoCreateDevice(DriverObject, 0, &DestinationString, 0x22u, 0, 0, &DeviceObject); if ( result >= 0 ) { IsWdmVersionAvailable = IoIsWdmVersionAvailable(1u, 0x10u); v5 = L"\\DosDevices\\Global\\Myhook"; if ( !IsWdmVersionAvailable ) v5 = L"\\DosDevices\\Myhook"; RtlInitUnicodeString(&SymbolicLinkName, v5); v6 = IoCreateSymbolicLink(&SymbolicLinkName, &DestinationString); if ( v6 >= 0 ) { sub_11008(); qword_16448 = (__int64)ExAllocatePool(NonPagedPool, 0x3200uLL); memmove((void *)qword_16448, &loc_13110, 0x3200uLL); sub_113C8(); RtlInitUnicodeString(&SystemRoutineName, L"NtCreateFile"); SystemRoutineAddress = MmGetSystemRoutineAddress(&SystemRoutineName); Src = (void *)sub_110B8(SystemRoutineAddress); return 0; } else { IoDeleteDevice(DeviceObject); return v6; } } return result; } ~~~ ### MajorFunction DriverObject->MajorFunction通过下标值派发函数,值在Wdm.h中进行了定义 ~~~c #define IRP_MJ_CREATE 0x00 #define IRP_MJ_CREATE_NAMED_PIPE 0x01 #define IRP_MJ_CLOSE 0x02 #define IRP_MJ_READ 0x03 #define IRP_MJ_WRITE 0x04 #define IRP_MJ_QUERY_INFORMATION 0x05 #define IRP_MJ_SET_INFORMATION 0x06 #define IRP_MJ_QUERY_EA 0x07 #define IRP_MJ_SET_EA 0x08 #define IRP_MJ_FLUSH_BUFFERS 0x09 #define IRP_MJ_QUERY_VOLUME_INFORMATION 0x0a #define IRP_MJ_SET_VOLUME_INFORMATION 0x0b #define IRP_MJ_DIRECTORY_CONTROL 0x0c #define IRP_MJ_FILE_SYSTEM_CONTROL 0x0d #define IRP_MJ_DEVICE_CONTROL 0x0e #define IRP_MJ_INTERNAL_DEVICE_CONTROL 0x0f #define IRP_MJ_SHUTDOWN 0x10 #define IRP_MJ_LOCK_CONTROL 0x11 #define IRP_MJ_CLEANUP 0x12 #define IRP_MJ_CREATE_MAILSLOT 0x13 #define IRP_MJ_QUERY_SECURITY 0x14 #define IRP_MJ_SET_SECURITY 0x15 #define IRP_MJ_POWER 0x16 #define IRP_MJ_SYSTEM_CONTROL 0x17 #define IRP_MJ_DEVICE_CHANGE 0x18 #define IRP_MJ_QUERY_QUOTA 0x19 #define IRP_MJ_SET_QUOTA 0x1a #define IRP_MJ_PNP 0x1b #define IRP_MJ_PNP_POWER IRP_MJ_PNP // Obsolete.... #define IRP_MJ_MAXIMUM_FUNCTION 0x1b ~~~ * IRP_MJ_CREATE:用户态CreateFile会走sub_116A4 * IRP_MJ_CLOSE:CloseHandle会走sub_116A4 * IRP_MJ_DEVICE_CONTROL:DeviceIoControl会走sub_116CC * DriverUnload卸载执行sub_11660 ### 创建内核设备对象 使用IoCreateDevice创建了内核设备对象`\Device\Myhook`,0x22表示FILE_DEVICE_UNKNOWN(自定义控制设备) ### 创建用户态符号链接 使用IoCreateSymbolicLink创建用户态符号链接`\\.\Myhook` ### 创建用户态符号链接成功会进行初始化 * sub_11008:打开一个固定文件,应该是用来判断ZwCreateFile是否可用 ~~~c++ NTSTATUS sub_11008() { _UNICODE_STRING DestinationString; // [rsp+60h] [rbp-58h] BYREF _IO_STATUS_BLOCK IoStatusBlock; // [rsp+70h] [rbp-48h] BYREF _OBJECT_ATTRIBUTES ObjectAttributes; // [rsp+80h] [rbp-38h] BYREF RtlInitUnicodeString(&DestinationString, L"\\??\\C:\\WINDOWS\\system32\\taskhost.exe"); ObjectAttributes.RootDirectory = 0LL; ObjectAttributes.SecurityDescriptor = 0LL; ObjectAttributes.SecurityQualityOfService = 0LL; ObjectAttributes.Length = 48; ObjectAttributes.Attributes = 576; ObjectAttributes.ObjectName = &DestinationString; return ZwCreateFile(&FileHandle, 0x80000000, &ObjectAttributes, &IoStatusBlock, 0LL, 0x80u, 0, 3u, 0x40u, 0LL, 0); } ~~~ * memmove拷贝了一段代码到NonPagedPool * sub_113C8:解密字符串,算法非常简单,就是异或 ~~~python s = [0x70, 0x74, 0x37, 0x65, 0x47, 0x66, 0x05, 0x61, 0x11, 0x20, 0x0C, 0x73, 0x6D, 0x41, 0x3A, 0x73, 0x36, 0x6D, 0x16, 0x6C, 0x09, 0x5F, 0x28, 0x6E, 0x0B, 0x69, 0x31, 0x65, 0x6D, 0x68, 0x5C, 0x6F, 0x58, 0x5F, 0x6A, 0x72, 0x02, 0x00, 0x78, 0x00, 0x74, 0x00, 0x50, 0x00, 0x5F, 0x00, 0x67, 0x00, 0x69, 0x00, 0x76, 0x00, 0x65, 0x00, 0x4D, 0x00, 0x65, 0x00, 0x5F, 0x00, 0x66, 0x00, 0x6C, 0x00, 0x61, 0x00, 0x67, 0x00, 0x5F, 0x00, 0x32, 0x00, 0x33, 0x00, 0x33, 0x00, 0x2E, 0x00, 0x74, 0x00, 0x78, 0x00, 0x74, 0x00, 0x50, 0x00, 0x5F, 0x00, 0x67, 0x00, 0x69, 0x00, 0x76, 0x00, 0x65, 0x00, 0x4D, 0x00, 0x65, 0x00, 0x5F, 0x00, 0x66, 0x00, 0x6C, 0x00, 0x61, 0x00, 0x67, 0x00, 0x5F, 0x00, 0x32, 0x00, 0x33, 0x00, 0x33, 0x00, 0x2E, 0x00, 0x74, 0x00, 0x78, 0x00, 0x74, 0x00, 0x50, 0x00] xor = 0x5c3113c5 s1 = [0x95, 0x13, 0x6E, 0x5C, 0xA2, 0x13, 0x58, 0x5C, 0xB3, 0x13, 0x54, 0x5C, 0x88, 0x13, 0x54, 0x5C, 0x9A, 0x13, 0x57, 0x5C, 0xA9, 0x13, 0x50, 0x5C, 0xA2, 0x13, 0x6E, 0x5C, 0xF7, 0x13, 0x02, 0x5C, 0xF6, 0x13, 0x1F, 0x5C, 0xB1, 0x13, 0x49, 0x5C, 0xB1, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] s1 = [int.from_bytes(s1[i:i+4], byteorder='little')^xor for i in range(0, len(s1), 4)] s1 = b"".join([i.to_bytes(4, byteorder='little') for i in s1]) for i in range(128): s[i] ^= s1[i%0x2a] print(bytes(s)) ~~~ ### 实现inline hook NtCreateFile * 使用MmGetSystemRoutineAddress获取了NtCreateFile地址并传入sub_110B8 * sub_110B8: ~~~c++ PVOID __fastcall sub_110B8(char *Dst) { char *v2; // rdi unsigned int v3; // ebx unsigned int v4; // eax PVOID PoolWithTag; // rdi unsigned __int8 CurrentIrql; // bl unsigned __int64 v7; // rcx unsigned __int64 v8; // r11 char *v9; // rbx __int64 v10; // r11 unsigned __int64 v11; // rax unsigned __int64 v12; // rax _BYTE v14[14]; // [rsp+20h] [rbp-38h] _BYTE v15[14]; // [rsp+30h] [rbp-28h] *(_QWORD *)v15 = 0xFFFF0000000025FFuLL; v2 = Dst; v3 = 0; *(_QWORD *)v14 = 0xFFFF0000000025FFuLL; do { v4 = qword_16448(v2, 64LL); v3 += v4; v2 += v4; } while ( v3 <= 0xE ); LODWORD(Size) = v3; PoolWithTag = ExAllocatePoolWithTag(NonPagedPool, v3, 'SYSQ'); CurrentIrql = KeGetCurrentIrql(); __writecr8(2uLL); v7 = __readcr0(); __writecr0(v7 & 0xFFFFFFFFFFFEFFFFuLL); _disable(); memmove(PoolWithTag, Dst, (unsigned int)Size); v8 = __readcr0(); _enable(); __writecr0(v8 | 0x10000); __writecr8(CurrentIrql); v9 = (char *)ExAllocatePoolWithTag(NonPagedPool, (unsigned int)(Size + 14), 'SYSQ'); memset(v9, 144, (unsigned int)(Size + 14)); *(_QWORD *)&v14[6] = &Dst[(unsigned int)Size]; memmove(v9, PoolWithTag, (unsigned int)Size); v10 = (unsigned int)Size; *(_QWORD *)&v9[(unsigned int)Size] = *(_QWORD *)v14; *(_DWORD *)&v9[v10 + 8] = *(_DWORD *)&v14[8]; *(_WORD *)&v9[v10 + 12] = *(_WORD *)&v14[12]; qword_16420 = (__int64 (__fastcall *)(_QWORD, _QWORD, _QWORD, _QWORD, _QWORD, _DWORD, _DWORD))v9; *(_QWORD *)&v15[6] = sub_114D0; LOBYTE(v9) = KeGetCurrentIrql(); __writecr8(2uLL); v11 = __readcr0(); __writecr0(v11 & 0xFFFFFFFFFFFEFFFFuLL); _disable(); memset(Dst, 144, (unsigned int)Size); *(_QWORD *)Dst = *(_QWORD *)v15; *((_DWORD *)Dst + 2) = *(_DWORD *)&v15[8]; *((_WORD *)Dst + 6) = *(_WORD *)&v15[12]; v12 = __readcr0(); _enable(); __writecr0(v12 | 0x10000); __writecr8((unsigned __int8)v9); return PoolWithTag; } ~~~ 1. 利用qword_16448函数计算汇编指令字节,具体的我们可以不去管他的实现,最终目标是要在NtCreateFile开头找到某个汇编指令地址前字节不小于14(值为v3/Size)。**为什么不小于14呢?答:要确保能够装得下3、4步骤构造的jmp指令** 2. 接着ExAllocatePoolWithTag分配Size大小空间给PoolWithTag,并拷贝NtCreateFile函数开头v3字节到PoolWithTag 3. 再次ExAllocatePoolWithTag分配Size+14大小空间给v9,同时初始化&v14[6]开始8字节为NtCreateFile Size位置的地址(也就是inline hook的返回地址) **填充过程**:首先全部填充nop,然后将上一步的PoolWithTag拷贝进去,然后紧跟着填充了14字节v14,此时v14内容是`FF 25 00 00 00 00 + 返回NtCreateFile的地址8字节`,相当于实现了`jmp addr` 最终v9赋值给了qword_16420 4. 同第三步构造了v15内容是`FF 25 00 00 00 00 + sub_114D0的地址8字节`,把v15填充回了Dst,也就是NtCreateFile的开头,所以执行NtCreateFile会先跳转sub_114D0 到这里inline hook基本分析完成 ### hook函数分析 sub_114D0如下,可以看到开头call调用了qword_16420,所以相当于调用了NtCreateFile并获取返回结果 ~~~c++ __int64 __fastcall sub_114D0(__int64 a1, __int64 a2, POBJECT_ATTRIBUTES a3, __int64 a4, __int64 a5, int a6, int a7) { int v8; // esi int v9; // eax struct _UNICODE_STRING *ObjectName; // rdi __int64 v11; // rax unsigned int i; // ebx v8 = qword_16420(a1, a2, a3, a4, a5, a6, a7); if ( v8 >= 0 ) { if ( wcsstr(a3->ObjectName->Buffer, SubStr) ) { v9 = dword_16428; ObjectName = a3->ObjectName; if ( dword_16428 != -1 ) { ++dword_16428; if ( (unsigned int)(v9 + 1) >= 8 ) { v11 = 0LL; for ( i = 0; byte_16390[v11]; ++i ) { if ( v11 >= 128 ) break; ++v11; } sub_115DC(); sub_112B4(ObjectName, (__int64)byte_16390, i); dword_16428 = -1; } } } } return (unsigned int)v8; } ~~~ NtCreateFile成功会使用wcsstr去匹配检查创建文件的路径里是否包含SubStr,根据计算可知值为`P_giveMe_flag_233.txt` 接着利用dword_16428作为计数器,当比较满足8次时才会触发if里的内容 if里首先统计了解密完的byte_16390字符串长度,接着sub_115DC,很明显是在卸载hook ~~~c++ __int64 sub_115DC() { PVOID SystemRoutineAddress; // rax size_t v1; // r8 const void *v2; // rdx unsigned __int8 CurrentIrql; // bl unsigned __int64 v4; // rcx unsigned __int64 v5; // r11 __int64 result; // rax struct _UNICODE_STRING DestinationString; // [rsp+20h] [rbp-18h] BYREF RtlInitUnicodeString(&DestinationString, L"NtCreateFile"); SystemRoutineAddress = MmGetSystemRoutineAddress(&DestinationString); v1 = (unsigned int)Size; v2 = Src; CurrentIrql = KeGetCurrentIrql(); __writecr8(2uLL); v4 = __readcr0(); __writecr0(v4 & 0xFFFFFFFFFFFEFFFFuLL); _disable(); memmove(SystemRoutineAddress, v2, v1); v5 = __readcr0(); _enable(); __writecr0(v5 | 0x10000); result = CurrentIrql; __writecr8(CurrentIrql); Src = 0LL; LODWORD(Size) = 0; return result; } ~~~ 接着把解密后的flag写入文件 ~~~c++ char __fastcall sub_112B4(struct _UNICODE_STRING *a1, __int64 a2, unsigned int a3) { char v3; // bl __int64 v4; // rdi struct _IO_STATUS_BLOCK IoStatusBlock; // [rsp+60h] [rbp-48h] BYREF struct _OBJECT_ATTRIBUTES v7; // [rsp+70h] [rbp-38h] BYREF union _LARGE_INTEGER ByteOffset; // [rsp+B8h] [rbp+10h] BYREF HANDLE FileHandle; // [rsp+C8h] [rbp+20h] BYREF ByteOffset.QuadPart = 0LL; v7.Length = 48; IoStatusBlock.Pointer = 0LL; v7.RootDirectory = 0LL; v7.SecurityDescriptor = 0LL; v7.SecurityQualityOfService = 0LL; v3 = 1; v4 = a3; v7.ObjectName = a1; IoStatusBlock.Information = 0LL; v7.Attributes = 576; if ( ZwCreateFile(&FileHandle, 0x40000000u, &v7, &IoStatusBlock, 0LL, 0x80u, 1u, 3u, 0x60u, 0LL, 0) < 0 || ZwWriteFile(FileHandle, 0LL, 0LL, 0LL, &IoStatusBlock, byte_16390, v4, &ByteOffset, 0LL) < 0 ) { v3 = 0; } else { ByteOffset.QuadPart += v4; } if ( FileHandle ) ZwClose(FileHandle); return v3; } ~~~ ### 获取flag 从前面分析可知我们需要做的就是跑着驱动,然后执行8次CreateFile,每次目录里都需要包括`P_giveMe_flag_233.txt`,注意8次CreateFileW一定不要包含GENERIC_WRITE,否则就会报错蓝屏(ZwCreateFile失败导致FileHandle不对)  ~~~c // trigger_mydriver2.c #define _CRT_SECURE_NO_WARNINGS #include <windows.h> #include <stdio.h> #include <wchar.h> int wmain(int argc, wchar_t **argv) { const wchar_t *needle = L"P_giveMe_flag_233.txt"; const wchar_t *path = (argc > 1) ? argv[1] : L"C:\\Users\\Administrator\\Desktop\\re_sys\\RCTF2017-MyDriver2-397\\P_giveMe_flag_233.txt"; if (wcsstr(path, needle) == NULL) { wprintf(L"[!] Path must contain: %ls\n", needle); return 1; } wprintf(L"[*] Target path: %ls\n", path); wprintf(L"[*] Triggering 8 successful CreateFileW calls...\n"); for (int i = 1; i <= 8; i++) { HANDLE h = CreateFileW( path, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ); if (h == INVALID_HANDLE_VALUE) { DWORD e = GetLastError(); wprintf(L"[!] CreateFileW #%d failed, err=%lu\n", i, e); return 2; } CloseHandle(h); wprintf(L"[+] CreateFileW #%d ok\n", i); } Sleep(300); // give hook write a moment HANDLE rf = CreateFileW( path, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); if (rf == INVALID_HANDLE_VALUE) { wprintf(L"[!] Open for read failed, err=%lu\n", GetLastError()); return 3; } char buf[512] = {0}; DWORD got = 0; if (!ReadFile(rf, buf, sizeof(buf) - 1, &got, NULL)) { wprintf(L"[!] ReadFile failed, err=%lu\n", GetLastError()); CloseHandle(rf); return 4; } CloseHandle(rf); printf("[*] File content (%lu bytes): %s\n", got, buf); return 0; } ~~~ 最后修改:2026 年 02 月 12 日 © 允许规范转载 打赏 赞赏作者 支付宝微信 赞 如果觉得我的文章对你有用,请随意赞赏