-
-
[原创]2019KCTF总决赛 第四题:西部乐园 WP
-
发表于: 2019-12-12 16:52 3854
-
一道驱动题,先把主程序拉到IDA里面看看。
int __cdecl main(int argc, const char **argv, const char **envp) { LPSTR v3; // rax DWORD v4; // eax DWORD v5; // eax LPSTR v7; // rax __int64 (*v8)(); // [rsp+40h] [rbp-878h] __int128 password; // [rsp+48h] [rbp-870h] DWORD dwIoControlCode; // [rsp+58h] [rbp-860h] BOOL v11; // [rsp+5Ch] [rbp-85Ch] HANDLE hDevice; // [rsp+60h] [rbp-858h] __int64 dwSize; // [rsp+68h] [rbp-850h] DWORD flOldProtect; // [rsp+70h] [rbp-848h] DWORD BytesReturned; // [rsp+74h] [rbp-844h] __int64 v16; // [rsp+78h] [rbp-840h] __int64 v17; // [rsp+80h] [rbp-838h] LPCWSTR lpFileName; // [rsp+88h] [rbp-830h] char *Address; // [rsp+90h] [rbp-828h] __int64 (*v20)(); // [rsp+98h] [rbp-820h] __int64 v21; // [rsp+A0h] [rbp-818h] __int64 v22; // [rsp+A8h] [rbp-810h] char v23; // [rsp+B0h] [rbp-808h] char OutBuffer; // [rsp+490h] [rbp-428h] v8 = 0i64; v17 = 0i64; v16 = 0i64; password = 0ui64; dwSize = 0i64; v3 = sub_1400014D0(aKerneloadriven_0); LoadDriver((__int64)v3); sub_140002030(); print(asc_1400062F8); scanf(Format, &password); if ( (signed __int64)password <= 96000 && (signed __int64)password >= 90000 )// 判断输入的密码的范围 { v16 = 0x42i64; v17 = 0xA9i64; dwSize = 0xE7i64; *((_QWORD *)&password + 1) = &unk_1400060B0; VirtualProtect_0(&unk_1400060B0, 0xE7); VirtualProtect_0(&OutBuffer, 1024); sub_14000256E(&v8); v8 = (__int64 (*)())((char *)v8 + 2); sub_140001760(*((__int64 *)&password + 1), 0i64, (__int64)&unk_140006B60); dwIoControlCode = 0x222041; VirtualProtect(&Address, 0xAui64, 0x40u, &flOldProtect); Address = &OutBuffer; v8 = Callback_Wrong; // 密码错误时的回调函数 v20 = Callback_Wrong; v4 = GetCurrentProcessId(); v8 = (__int64 (*)())v4; v21 = v4; v22 = password; qmemcpy(&v23, *((const void **)&password + 1), 0xE7ui64); lpFileName = aKerneloadrivev; hDevice = CreateFileW(aKerneloadrivev, 0xC0000000, 0, 0i64, 3u, 0x80u, 0i64); if ( hDevice == (HANDLE)-1i64 ) { v5 = GetLastError(); print(aFailedToObtain, aMywdmdevice, v5); return 1; } // 跟驱动通信,Address存放了密码,错误回调函数等等 v11 = DeviceIoControl(hDevice, dwIoControlCode, &Address, 0x107u, &OutBuffer, 0x400u, &BytesReturned, 0i64); if ( !v11 ) { MessageBoxW(0i64, L"wrong!", L"err", 0); return 0; } CloseHandle(hDevice); } else { MessageBoxW(0i64, L"wrong!", L"err", 0); } SleepEx(0x3E8u, 1); v7 = sub_1400014D0(aKerneloadriven_1); UnloadDriver((__int64)v7); system(pause); return 0; }
从main函数可以看出来password的范围为[90000,96000],接下来看看驱动。
int __cdecl main(int argc, const char **argv, const char **envp) { LPSTR v3; // rax DWORD v4; // eax DWORD v5; // eax LPSTR v7; // rax __int64 (*v8)(); // [rsp+40h] [rbp-878h] __int128 password; // [rsp+48h] [rbp-870h] DWORD dwIoControlCode; // [rsp+58h] [rbp-860h] BOOL v11; // [rsp+5Ch] [rbp-85Ch] HANDLE hDevice; // [rsp+60h] [rbp-858h] __int64 dwSize; // [rsp+68h] [rbp-850h] DWORD flOldProtect; // [rsp+70h] [rbp-848h] DWORD BytesReturned; // [rsp+74h] [rbp-844h] __int64 v16; // [rsp+78h] [rbp-840h] __int64 v17; // [rsp+80h] [rbp-838h] LPCWSTR lpFileName; // [rsp+88h] [rbp-830h] char *Address; // [rsp+90h] [rbp-828h] __int64 (*v20)(); // [rsp+98h] [rbp-820h] __int64 v21; // [rsp+A0h] [rbp-818h] __int64 v22; // [rsp+A8h] [rbp-810h] char v23; // [rsp+B0h] [rbp-808h] char OutBuffer; // [rsp+490h] [rbp-428h] v8 = 0i64; v17 = 0i64; v16 = 0i64; password = 0ui64; dwSize = 0i64; v3 = sub_1400014D0(aKerneloadriven_0); LoadDriver((__int64)v3); sub_140002030(); print(asc_1400062F8); scanf(Format, &password); if ( (signed __int64)password <= 96000 && (signed __int64)password >= 90000 )// 判断输入的密码的范围 { v16 = 0x42i64; v17 = 0xA9i64; dwSize = 0xE7i64; *((_QWORD *)&password + 1) = &unk_1400060B0; VirtualProtect_0(&unk_1400060B0, 0xE7); VirtualProtect_0(&OutBuffer, 1024); sub_14000256E(&v8); v8 = (__int64 (*)())((char *)v8 + 2); sub_140001760(*((__int64 *)&password + 1), 0i64, (__int64)&unk_140006B60); dwIoControlCode = 0x222041; VirtualProtect(&Address, 0xAui64, 0x40u, &flOldProtect); Address = &OutBuffer; v8 = Callback_Wrong; // 密码错误时的回调函数 v20 = Callback_Wrong; v4 = GetCurrentProcessId(); v8 = (__int64 (*)())v4; v21 = v4; v22 = password; qmemcpy(&v23, *((const void **)&password + 1), 0xE7ui64); lpFileName = aKerneloadrivev; hDevice = CreateFileW(aKerneloadrivev, 0xC0000000, 0, 0i64, 3u, 0x80u, 0i64); if ( hDevice == (HANDLE)-1i64 ) { v5 = GetLastError(); print(aFailedToObtain, aMywdmdevice, v5); return 1; } // 跟驱动通信,Address存放了密码,错误回调函数等等 v11 = DeviceIoControl(hDevice, dwIoControlCode, &Address, 0x107u, &OutBuffer, 0x400u, &BytesReturned, 0i64); if ( !v11 ) { MessageBoxW(0i64, L"wrong!", L"err", 0); return 0; } CloseHandle(hDevice); } else { MessageBoxW(0i64, L"wrong!", L"err", 0); } SleepEx(0x3E8u, 1); v7 = sub_1400014D0(aKerneloadriven_1); UnloadDriver((__int64)v7); system(pause); return 0; }
从main函数可以看出来password的范围为[90000,96000],接下来看看驱动。
在DriverEntry顺藤摸瓜,找到sub_140005080,伪代码如下。
__int64 __fastcall sub_140005080(__int64 a1, _IRP *a2) { _IO_STACK_LOCATION *v2; // rax _IRP *v3; // rbp __int64 v4; // r13 PMDL v5; // rcx _IRP *v6; // r15 PVOID v7; // r12 PMDL v8; // r14 _IRP *v9; // rdi int v10; // eax __int64 v11; // rbx int v12; // esi __int64 v13; // rcx __int64 v15; // [rsp+68h] [rbp+10h] v2 = a2->Tail.Overlay.CurrentStackLocation; v3 = a2; v4 = v2->Parameters.Create.Options; if ( v2->Parameters.Read.ByteOffset.LowPart == 0x222041 ) { v5 = a2->MdlAddress; v6 = a2->AssociatedIrp.MasterIrp; if ( v5->MdlFlags & 5 ) v7 = v5->MappedSystemVa; else v7 = MmMapLockedPagesSpecifyCache(v5, 0, MmCached, 0i64, 0, NormalPagePriority); v8 = v6->MdlAddress; v9 = v6->AssociatedIrp.MasterIrp; v15 = *(_QWORD *)&v6->Type; v10 = sub_14000110D(); v11 = v10; v12 = v4 - (v10 + 36); DecryptCode((_BYTE *)&v6->ThreadListEntry.Flink + v10 + 4, v4 - (v10 + 36), (int)v9);// 以password为key解密shellcode if ( *(_DWORD *)((char *)&v6->ThreadListEntry.Flink + v11 + 4) == v12 )// 若解密成功,则将sub_140001131处的代码以及shellcode复制到R3的内存中 { memcpy(v7, &v6->ThreadListEntry, v4 - 32); v13 = v15; LABEL_9: InsertApc(v13, 0i64, 0i64, 0i64); // 将内存地址插入到线程APC,等待执行,如果解密失败,则将错误回调函数插入到APC goto LABEL_10; } if ( v8 ) { v13 = (__int64)v8; goto LABEL_9; } } LABEL_10: v3->IoStatus.Status = 0; v3->IoStatus.Information = 0i64; IofCompleteRequest(v3, 0); return 0i64; }
__int64 __fastcall sub_140005080(__int64 a1, _IRP *a2) { _IO_STACK_LOCATION *v2; // rax _IRP *v3; // rbp __int64 v4; // r13 PMDL v5; // rcx _IRP *v6; // r15 PVOID v7; // r12 PMDL v8; // r14 _IRP *v9; // rdi int v10; // eax __int64 v11; // rbx int v12; // esi __int64 v13; // rcx __int64 v15; // [rsp+68h] [rbp+10h] v2 = a2->Tail.Overlay.CurrentStackLocation; v3 = a2; v4 = v2->Parameters.Create.Options; if ( v2->Parameters.Read.ByteOffset.LowPart == 0x222041 ) { v5 = a2->MdlAddress; v6 = a2->AssociatedIrp.MasterIrp; if ( v5->MdlFlags & 5 ) v7 = v5->MappedSystemVa; else v7 = MmMapLockedPagesSpecifyCache(v5, 0, MmCached, 0i64, 0, NormalPagePriority); v8 = v6->MdlAddress; v9 = v6->AssociatedIrp.MasterIrp; v15 = *(_QWORD *)&v6->Type; v10 = sub_14000110D(); v11 = v10; v12 = v4 - (v10 + 36); DecryptCode((_BYTE *)&v6->ThreadListEntry.Flink + v10 + 4, v4 - (v10 + 36), (int)v9);// 以password为key解密shellcode if ( *(_DWORD *)((char *)&v6->ThreadListEntry.Flink + v11 + 4) == v12 )// 若解密成功,则将sub_140001131处的代码以及shellcode复制到R3的内存中 { memcpy(v7, &v6->ThreadListEntry, v4 - 32); v13 = v15; LABEL_9: InsertApc(v13, 0i64, 0i64, 0i64); // 将内存地址插入到线程APC,等待执行,如果解密失败,则将错误回调函数插入到APC goto LABEL_10; } if ( v8 ) { v13 = (__int64)v8; goto LABEL_9; } } LABEL_10: v3->IoStatus.Status = 0; v3->IoStatus.Information = 0i64; IofCompleteRequest(v3, 0); return 0i64; }
驱动部分的主要流程:
以输入的password解密一段代码,解密成功则将代码写到程序里,并将地址插入到APC。
在程序走到"SleepEx(0x3E8u, 1);"的时候,插入到APC的地址就会被调用。
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
最后于 2019-12-14 21:31
被梦游枪手编辑
,原因: 补图
赞赏
他的文章
看原图
赞赏
雪币:
留言: