├── secret.rar ├── vuln-drvs.rar ├── special-package.zip ├── EACReversing-master.zip ├── README.md ├── scanner.cpp └── kmac.cpp /secret.rar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mathisvickie/KMAC/HEAD/secret.rar -------------------------------------------------------------------------------- /vuln-drvs.rar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mathisvickie/KMAC/HEAD/vuln-drvs.rar -------------------------------------------------------------------------------- /special-package.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mathisvickie/KMAC/HEAD/special-package.zip -------------------------------------------------------------------------------- /EACReversing-master.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mathisvickie/KMAC/HEAD/EACReversing-master.zip -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # UPDATE 2 | Okay guys, so I have pushed here some more vulnerable drivers in special-package.zip which some has even ioctl codes in their file name so you know where to look for treats. Anyway you will probably not need them lol because of secret.rar which is password protected but anyone with three digit iq will figure out the password (name of archive will help you) and what the content of that archive is used for. Then look into that batch script located inside. You will need to change your local time in order to use it because it already expired long time ago but window$ will load anyway expired drivers so no worries. Just hope (better backup) they will not take down my whole repo/account for sharing this. You can thank me later <3 and remember to also seed, not just feed. 3 | 4 | # KMAC 5 | KernelMode AntiCheat 6 | 7 | Just to see of what you should be aware when dealing with KMAC on Windows. 8 | 9 | Many people are fighting with KMAC in ring0 and its cool to have some vulnerable signed driver that opens for you a door to windows kernel. You may either try to exploit some CVE: https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=driver or find yourself a 0-day which is not that easy but it's less detectable by anticheats. Here is a nice tool which enumerates x64 driver imports: https://gist.github.com/adrianyy/9c481c9b3b115a910985ce310d948534 10 | I will add archive with some publicly well known drivers (signed & vulnerable), password for extraction is just simply 'password'. Nice talk about vulnerable drivers: https://eclypsium.com/2019/08/10/screwed-drivers-signed-sealed-delivered/ 11 | 12 | Looks like in ci.dll are some undocumented structures that leave some traces about (un)loaded drivers, see this: 13 | https://github.com/TheCruZ/kdmapper/blob/master/kdmapper/intel_driver.cpp#L626 https://key08.com/index.php/2021/02/06/902.html 14 | 15 | about BE see: https://secret.club/ 16 | 17 | Recently I dived into EAC (working on game Enlisted because its cool & free2play) and it looks like game calls eac.dll!CreateGameClient to get [this interface](https://github.com/mathisvickie/EAC-Emulator/blob/main/dllmain.cpp#L4). To note: real interface is larger but Enlisted does not use those functions after offset 0x50 (also doesnt use 0x30, 0x38, 0x40 and 0x48 vfuncs) 18 | 19 | ## Blacklisted 20 | Most likely all vulnerable drivers in 'git' directory inside that huge zip are blacklisted by popular anticheats (avoid using all versions of them). 21 | 22 | 100% blacklist by f4ceit and v4nguard: 23 | - winring 24 | - msio64 25 | - ene 26 | - inpoutx64 27 | - glckio2 28 | - ntiolib_x64 29 | - asio 30 | 31 | V4nguard is scanning for this devices (i bet 4 bl4cklist -> b4n): 32 | - \Device\ATSZIO 33 | - \Device\genericdrv 34 | - \DosDevices\AIDA64Driver 35 | - \DosDevices\ALSysIO 36 | - \DosDevices\AsUpdateio 37 | - \DosDevices\Asusgio 38 | - \DosDevices\BS_Def 39 | - \DosDevices\CITMDRV 40 | - \DosDevices\EneTechIo 41 | - \DosDevices\GLCKIo2 42 | - \DosDevices\Global\CPUZ 43 | - \DosDevices\HOSTNT 44 | - \DosDevices\NTIOLib 45 | - \DosDevices\NVFLASH 46 | - \DosDevices\RTCore 47 | - \DosDevices\SE64 48 | - \DosDevices\WinIoB 49 | - \DosDevices\WinRing0 50 | - \DosDevices\ZemanaAntiMalware 51 | - \DosDevices\driveragent%d 52 | - \DosDevices\inpout 53 | 54 | # CVEs 55 | As you may see many known bad drivers are blacklisted by BE/EAC and others because they were already used in public game cheats (and released on uc). Here are listed most recent interesting CVEs: (i will try to keep this list updated) 56 | 57 | ## CVE-2021-36276 (dbutil version2) 58 | Dell DBUtilDrv2.sys driver (versions 2.5 and 2.6) contains an insufficient access control vulnerability which may lead to escalation of privileges, denial of service, or information disclosure. Local authenticated user access is required. 59 | Note: it is WDF driver so I won't make any PoC - feel free to analyze it, search for imports MmMapIoSpace and MmUnmapIoSpace. 60 | 61 | ## CVE-2021-31728 & CVE-2021-31727 (zemana again) 62 | Not recommended to use because zemana was already detected in past even if these are new CVEs. https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-31728 & https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-31727 POC: https://github.com/irql0/CVE-2021-31728 63 | 64 | ## CVE-2021-28685 (AsIO2_64.sys + AsIO2_32.sys) 65 | Looks like this is second version of infamous AsIO and they just can't learn from own mistakes. https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-28685 66 | //todo: more info 67 | 68 | ## CVE-2021-27965 (MsIo64.sys) 69 | For PoC see: https://github.com/mathisvickie/CVE-2021-27965 70 | 71 | ## CVE-2021-21551 (dbutil_2_3.sys) 72 | Local Privilege Escalation to nt authority/system PoC: https://github.com/mathisvickie/CVE-2021-21551 73 | 74 | ## CVE-2020-0796 aka SMBGhost 75 | RCE in microsoft SMB v3 protocol (when using compression) which can be used on localhost or remotely on LAN (arbitrary kernel memory read/write). Exploiting requires Windows10 1903 or 1909. Advantage is that nothing suspicious is running on target system because attack vector is network and bug happens in srv2.sys - microsoft windows file. See POC: https://github.com/ZecOps/CVE-2020-0796-LPE-POC (write_what_where - write arbitrary kernel memory over local network) 76 | -------------------------------------------------------------------------------- /scanner.cpp: -------------------------------------------------------------------------------- 1 | //---------------------------------------------------------------------------------- 2 | //not my work, see https://gist.github.com/adrianyy/9c481c9b3b115a910985ce310d948534 3 | //usage: scanner 4 | //---------------------------------------------------------------------------------- 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | 15 | static_assert( sizeof( void* ) == 8 ); 16 | 17 | constexpr auto kernel_image_name = "ntoskrnl.exe"; 18 | const auto searched_imports = { 19 | "MmMapIoSpace", 20 | "MmMapIoSpaceEx", 21 | "MmMapLockedPages", 22 | "MmMapLockedPagesSpecifyCache", 23 | "MmMapLockedPagesWithReservedMapping" 24 | }; 25 | 26 | namespace pe 27 | { 28 | inline PIMAGE_NT_HEADERS get_nt_headers_unsafe( uint8_t* base ) 29 | { 30 | const auto dos_header = PIMAGE_DOS_HEADER( base ); 31 | const auto nt_headers = PIMAGE_NT_HEADERS( base + dos_header->e_lfanew ); 32 | 33 | return nt_headers; 34 | } 35 | 36 | PIMAGE_NT_HEADERS get_nt_headers( uint8_t* base ) 37 | { 38 | const auto dos_header = PIMAGE_DOS_HEADER( base ); 39 | if( dos_header->e_magic != IMAGE_DOS_SIGNATURE ) 40 | return nullptr; 41 | 42 | const auto nt_headers = PIMAGE_NT_HEADERS( base + dos_header->e_lfanew ); 43 | if( nt_headers->Signature != IMAGE_NT_SIGNATURE || nt_headers->FileHeader.Machine != IMAGE_FILE_MACHINE_AMD64 ) 44 | return nullptr; 45 | 46 | return nt_headers; 47 | } 48 | 49 | template 50 | T* file_rva_to_va( uint8_t* base, const uint32_t rva ) 51 | { 52 | const auto nt_headers = get_nt_headers_unsafe( base ); 53 | const auto sections = IMAGE_FIRST_SECTION( nt_headers ); 54 | 55 | for( size_t i = 0; i < nt_headers->FileHeader.NumberOfSections; ++i ) 56 | { 57 | const auto& section = sections[ i ]; 58 | if( rva >= section.VirtualAddress && rva < section.VirtualAddress + section.SizeOfRawData ) 59 | return ( T* )( base + ( rva - section.VirtualAddress + section.PointerToRawData ) ); 60 | } 61 | 62 | return ( T* )( base + rva ); 63 | } 64 | } 65 | 66 | std::vector read_file( const std::string& file_path ) 67 | { 68 | std::ifstream stream( file_path, std::ios::in | std::ios::ate | std::ios::binary ); 69 | if( !stream ) 70 | return {}; 71 | 72 | const auto size = stream.tellg( ); 73 | stream.seekg( 0, std::ios::beg ); 74 | 75 | std::vector buffer( size ); 76 | stream.read( ( char* )buffer.data( ), size ); 77 | 78 | return buffer; 79 | } 80 | 81 | void check_file( const std::string& display_path, const std::string& file_path ) 82 | { 83 | auto buffer = read_file( file_path ); 84 | if( buffer.empty( ) ) 85 | { 86 | std::printf( "Failed to read file: %s.\n", display_path.c_str( ) ); 87 | return; 88 | } 89 | 90 | const auto base = buffer.data( ); 91 | const auto nt_headers = pe::get_nt_headers( base ); 92 | if( !nt_headers || nt_headers->OptionalHeader.Subsystem != IMAGE_SUBSYSTEM_NATIVE ) 93 | return; 94 | 95 | const auto import_dir = nt_headers->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_IMPORT ]; 96 | if( !import_dir.Size || !import_dir.VirtualAddress ) 97 | return; 98 | 99 | bool printed_name = false; 100 | auto import_descriptor = pe::file_rva_to_va( base, import_dir.VirtualAddress ); 101 | for( ; import_descriptor->Name; ++import_descriptor ) 102 | { 103 | const auto module_name = pe::file_rva_to_va( base, import_descriptor->Name ); 104 | if( strcmp( module_name, kernel_image_name ) != 0 ) 105 | continue; 106 | 107 | auto imported_func = pe::file_rva_to_va( base, 108 | import_descriptor->OriginalFirstThunk ? import_descriptor->OriginalFirstThunk : import_descriptor->FirstThunk ); 109 | 110 | for( ; imported_func->u1.AddressOfData; ++imported_func ) 111 | { 112 | if( imported_func->u1.Ordinal & IMAGE_ORDINAL_FLAG ) 113 | continue; 114 | 115 | const auto import_name = pe::file_rva_to_va( base, 116 | uint32_t( imported_func->u1.AddressOfData ) )->Name; 117 | 118 | if( std::find( std::begin( searched_imports ), std::end( searched_imports ), std::string( import_name ) ) 119 | != searched_imports.end( ) ) 120 | { 121 | if( !printed_name ) 122 | { 123 | std::printf( "\n%s:\n", display_path.c_str( ) ); 124 | printed_name = true; 125 | } 126 | 127 | std::printf( " => %s\n", import_name ); 128 | } 129 | } 130 | } 131 | } 132 | 133 | void check_files_in_directory( const std::string& directory_path ) 134 | { 135 | namespace fs = std::filesystem; 136 | 137 | const auto normalized_path = fs::path( directory_path ).string( ) + "\\"; 138 | for( const auto& file : fs::recursive_directory_iterator( directory_path ) ) 139 | { 140 | if( fs::is_regular_file( file ) && file.path( ).extension( ) == ".sys" ) 141 | { 142 | const auto file_path = file.path( ).string( ); 143 | const auto has_substr = file_path.find_first_of( normalized_path ) != std::string::npos; 144 | const auto display_path = has_substr ? ( ".\\" + file_path.substr( normalized_path.size( ) ) ) : file_path; 145 | 146 | check_file( display_path, file_path ); 147 | } 148 | } 149 | } 150 | 151 | int main( int argc, char* argv[ ] ) 152 | { 153 | if( argc != 2 ) 154 | { 155 | std::printf( "Invalid arguments.\n" ); 156 | } 157 | else 158 | { 159 | std::printf( "Searching in %s.\n", argv[ 1 ] ); 160 | check_files_in_directory( argv[ 1 ] ); 161 | std::printf( "\nDone.\n" ); 162 | } 163 | 164 | std::cin.get( ); 165 | } 166 | -------------------------------------------------------------------------------- /kmac.cpp: -------------------------------------------------------------------------------- 1 | //-------------------------------------------------------------------------------------------- 2 | //not my work, see https://github.com/ApexLegendsUC/anti-cheat-emulator/blob/master/Source.cpp 3 | //-------------------------------------------------------------------------------------------- 4 | 5 | #include 6 | #include 7 | extern "C" { 8 | #include "NativeStructs.h" 9 | } 10 | #include "defs.h" 11 | #include "hyperdetect.h" 12 | 13 | #define xorstr_(x) x 14 | 15 | #define POOL_TAG 'enoN' 16 | 17 | void sleep(LONG milliseconds) 18 | { 19 | LARGE_INTEGER interval; 20 | interval.QuadPart = -(10000ll * milliseconds); 21 | 22 | KeDelayExecutionThread(KernelMode, FALSE, &interval); 23 | } 24 | 25 | 26 | NTSTATUS BBSearchPattern(IN PCUCHAR pattern, IN UCHAR wildcard, IN ULONG_PTR len, IN const VOID* base, IN ULONG_PTR size, OUT PVOID* ppFound, int index = 0) 27 | { 28 | ASSERT(ppFound != NULL && pattern != NULL && base != NULL); 29 | if (ppFound == NULL || pattern == NULL || base == NULL) 30 | return STATUS_ACCESS_DENIED; //STATUS_INVALID_PARAMETER; 31 | int cIndex = 0; 32 | for (ULONG_PTR i = 0; i < size - len; i++) 33 | { 34 | BOOLEAN found = TRUE; 35 | for (ULONG_PTR j = 0; j < len; j++) 36 | { 37 | if (pattern[j] != wildcard && pattern[j] != ((PCUCHAR)base)[i + j]) 38 | { 39 | found = FALSE; 40 | break; 41 | } 42 | } 43 | 44 | if (found != FALSE && cIndex++ == index) 45 | { 46 | *ppFound = (PUCHAR)base + i; 47 | return STATUS_SUCCESS; 48 | } 49 | } 50 | 51 | return STATUS_NOT_FOUND; 52 | } 53 | 54 | NTSTATUS BBScanSection(IN PCCHAR section, IN PCUCHAR pattern, IN UCHAR wildcard, IN ULONG_PTR len, OUT PVOID* ppFound, PVOID base = nullptr) 55 | { 56 | //ASSERT(ppFound != NULL); 57 | if (ppFound == NULL) 58 | return STATUS_ACCESS_DENIED; //STATUS_INVALID_PARAMETER 59 | 60 | if (nullptr == base) 61 | base = GetKernelBase(); 62 | if (base == nullptr) 63 | return STATUS_ACCESS_DENIED; //STATUS_NOT_FOUND; 64 | 65 | PIMAGE_NT_HEADERS64 pHdr = RtlImageNtHeader(base); 66 | if (!pHdr) 67 | return STATUS_ACCESS_DENIED; // STATUS_INVALID_IMAGE_FORMAT; 68 | 69 | //PIMAGE_SECTION_HEADER pFirstSection = (PIMAGE_SECTION_HEADER)(pHdr + 1); 70 | PIMAGE_SECTION_HEADER pFirstSection = (PIMAGE_SECTION_HEADER)((uintptr_t)&pHdr->FileHeader + pHdr->FileHeader.SizeOfOptionalHeader + sizeof(IMAGE_FILE_HEADER)); 71 | 72 | for (PIMAGE_SECTION_HEADER pSection = pFirstSection; pSection < pFirstSection + pHdr->FileHeader.NumberOfSections; pSection++) 73 | { 74 | //DbgPrint("section: %s\r\n", pSection->Name); 75 | ANSI_STRING s1, s2; 76 | RtlInitAnsiString(&s1, section); 77 | RtlInitAnsiString(&s2, (PCCHAR)pSection->Name); 78 | if (RtlCompareString(&s1, &s2, TRUE) == 0) 79 | { 80 | PVOID ptr = NULL; 81 | NTSTATUS status = BBSearchPattern(pattern, wildcard, len, (PUCHAR)base + pSection->VirtualAddress, pSection->Misc.VirtualSize, &ptr); 82 | if (NT_SUCCESS(status)) { 83 | *(PULONG64)ppFound = (ULONG_PTR)(ptr); //- (PUCHAR)base 84 | //DbgPrint("found\r\n"); 85 | return status; 86 | } 87 | //we continue scanning because there can be multiple sections with the same name. 88 | } 89 | } 90 | 91 | return STATUS_ACCESS_DENIED; //STATUS_NOT_FOUND; 92 | } 93 | 94 | PSYSTEM_MODULE_INFORMATION GetKernelModuleList() 95 | { 96 | NTSTATUS status = STATUS_SUCCESS; 97 | ULONG neededSize = 0; 98 | 99 | ZwQuerySystemInformation( 100 | SystemModuleInformation, 101 | &neededSize, 102 | 0, 103 | &neededSize 104 | ); 105 | 106 | PSYSTEM_MODULE_INFORMATION pModuleList; 107 | 108 | pModuleList = (PSYSTEM_MODULE_INFORMATION)ExAllocatePoolWithTag(NonPagedPool, neededSize, POOL_TAG); 109 | if (pModuleList == NULL) 110 | { 111 | return FALSE; 112 | } 113 | 114 | status = ZwQuerySystemInformation(SystemModuleInformation, 115 | pModuleList, 116 | neededSize, 117 | 0 118 | ); 119 | 120 | /*for (ULONG i = 0; i < pModuleList->ulModuleCount; i++) 121 | { 122 | auto name_offset = pModuleList->Modules[i].ModuleNameOffset; 123 | if (name_offset > 256) 124 | break; 125 | if (_stricmp(&pModuleList->Modules[i].ImageName[name_offset], name) == NULL) { 126 | auto address = (UINT64)pModuleList->Modules[i].Base; 127 | ExFreePoolWithTag(pModuleList, POOL_TAG); 128 | return address; 129 | } 130 | } 131 | */ 132 | 133 | //ExFreePoolWithTag(pModuleList, POOL_TAG); 134 | 135 | return pModuleList; 136 | } 137 | 138 | 139 | 140 | void get_thread_start_address(PETHREAD ThreadObj, uintptr_t* pStartAddr) 141 | { 142 | *pStartAddr = NULL; 143 | HANDLE hThread; 144 | if (!NT_SUCCESS(ObOpenObjectByPointer(ThreadObj, OBJ_KERNEL_HANDLE, nullptr, GENERIC_READ, *PsThreadType, KernelMode, &hThread))) { 145 | DbgPrint("ObOpenObjectByPointer failed.\n"); 146 | return; 147 | } 148 | 149 | uintptr_t start_addr; 150 | ULONG returned_bytes; 151 | 152 | if (!NT_SUCCESS(NtQueryInformationThread(hThread, ThreadQuerySetWin32StartAddress, &start_addr, sizeof(start_addr), &returned_bytes))) { 153 | DbgPrint("NtQueryInformationThread failed.\n"); 154 | NtClose(hThread); 155 | return; 156 | } 157 | 158 | if (MmIsAddressValid((void*)start_addr)) { 159 | *pStartAddr = start_addr; 160 | } 161 | else 162 | DbgPrint("(not a detection) Invalid start addr %p.\r\n", start_addr); 163 | 164 | NtClose(hThread); 165 | } 166 | 167 | bool is_address_outside_of_module_list(PSYSTEM_MODULE_INFORMATION pModuleList, uintptr_t addr) 168 | { 169 | if (addr == NULL) 170 | return false; 171 | __try { 172 | for (ULONG i = 0; i < pModuleList->ulModuleCount; i++) 173 | { 174 | if (addr >= reinterpret_cast(pModuleList->Modules[i].Base) && 175 | addr < reinterpret_cast(pModuleList->Modules[i].Base) + pModuleList->Modules[i].Size) { 176 | return false; 177 | } 178 | } 179 | } 180 | __except (EXCEPTION_EXECUTE_HANDLER) { 181 | DbgPrint("Access Violation was raised (is_address_outside_of_module_list).\n"); 182 | } 183 | return true; 184 | } 185 | 186 | void print_module_name(PSYSTEM_MODULE_INFORMATION pModuleList, uintptr_t addr) 187 | { 188 | if (addr == NULL) 189 | return; 190 | __try { 191 | for (ULONG i = 0; i < pModuleList->ulModuleCount; i++) 192 | { 193 | if (addr >= reinterpret_cast(pModuleList->Modules[i].Base) && addr < reinterpret_cast(pModuleList->Modules[i].Base) + pModuleList->Modules[i].Size) { 194 | auto name_offset = pModuleList->Modules[i].ModuleNameOffset; 195 | if (name_offset > 256) 196 | continue; 197 | DbgPrint("module: %s\n", &pModuleList->Modules[i].ImageName[name_offset]); 198 | return; 199 | } 200 | } 201 | } 202 | __except (EXCEPTION_EXECUTE_HANDLER) { 203 | DbgPrint("Access Violation was raised (is_address_outside_of_module_list).\n"); 204 | } 205 | DbgPrint("Module: \n"); 206 | } 207 | 208 | //PsGetCurrentThreadStackBase 209 | ULONG GetThreadStackBaseOffset() 210 | { 211 | UNICODE_STRING s = RTL_CONSTANT_STRING(L"PsGetCurrentThreadStackBase"); 212 | 213 | auto CurrentThreadStackBase = (uintptr_t(NTAPI*)())MmGetSystemRoutineAddress(&s); 214 | auto CurrentThread = (uintptr_t)PsGetCurrentThread(); 215 | auto current_stack = CurrentThreadStackBase(); 216 | 217 | ULONG Offset = NULL; 218 | while (*PULONGLONG(CurrentThread + Offset) != current_stack) 219 | Offset += 8; 220 | 221 | return Offset; 222 | } 223 | 224 | ULONG GetThreadStackLimitOffset() 225 | { 226 | UNICODE_STRING s = RTL_CONSTANT_STRING(L"PsGetCurrentThreadStackLimit"); 227 | 228 | auto CurrentThreadStackLimit = (uintptr_t(NTAPI*)())MmGetSystemRoutineAddress(&s); 229 | auto CurrentThread = (uintptr_t)PsGetCurrentThread(); 230 | auto current_stack = CurrentThreadStackLimit(); 231 | 232 | ULONG Offset = NULL; 233 | while (*PULONGLONG(CurrentThread + Offset) != current_stack) 234 | Offset += 8; 235 | 236 | return Offset; 237 | } 238 | 239 | ULONG GetInitialThreadStackOffset() 240 | { 241 | UNICODE_STRING s = RTL_CONSTANT_STRING(L"IoGetInitialStack"); 242 | 243 | auto CurrentThreadStack = (uintptr_t(NTAPI*)())MmGetSystemRoutineAddress(&s); 244 | auto CurrentThread = (uintptr_t)PsGetCurrentThread(); 245 | auto current_stack = CurrentThreadStack(); 246 | 247 | ULONG Offset = NULL; 248 | while (*PULONGLONG(CurrentThread + Offset) != current_stack) 249 | Offset += 8; 250 | 251 | return Offset; 252 | } 253 | 254 | ULONG GetThreadCurrentStackLocationOffset(PETHREAD ThreadObj, ULONG stack_base_offset, ULONG stack_limit_offset, ULONG initial_stack_offset) 255 | { 256 | auto thread_stack_base = stack_base_offset ? *(uintptr_t*)((uintptr_t)ThreadObj + stack_base_offset) : 0; 257 | auto thread_stack_limit = stack_limit_offset ? *(uintptr_t*)((uintptr_t)ThreadObj + stack_limit_offset) : 0; 258 | 259 | if (!thread_stack_base || !thread_stack_limit || !initial_stack_offset) 260 | return NULL; 261 | 262 | auto thread_obj = (uintptr_t)ThreadObj; 263 | 264 | ULONG Offset = NULL; 265 | while (Offset < 0x2F8) { 266 | if (Offset != initial_stack_offset && *PULONGLONG(thread_obj + Offset) < thread_stack_base && *PULONGLONG(thread_obj + Offset) > thread_stack_limit) 267 | return Offset; 268 | Offset += 8; 269 | } 270 | 271 | return NULL; 272 | } 273 | 274 | #define KELOCKTHREAD 0x12EE7C 275 | #define KERESUMETHREAD 0x30E90 276 | 277 | typedef uintptr_t(NTAPI*tThreadLock)(void* thread_obj); 278 | 279 | 280 | bool printed_stack_offsets = false; 281 | ULONG CopyStack(PETHREAD ThreadObj, void* copied_stack, ULONG copied_stack_buffer_len) 282 | { 283 | RtlZeroMemory(copied_stack, copied_stack_buffer_len); 284 | auto stack_base_offset = GetThreadStackBaseOffset(); 285 | auto stack_limit_offset = GetThreadStackLimitOffset(); 286 | auto initial_stack_offset = GetInitialThreadStackOffset(); 287 | if (!printed_stack_offsets) { 288 | DbgPrint("stack_base_offset = 0x%x, stack_limit_offset = 0x%x, initial_stack_offset = 0x%x\n", stack_base_offset, stack_limit_offset, initial_stack_offset); 289 | printed_stack_offsets = true; 290 | } 291 | 292 | auto stack_base = stack_base_offset ? *(uintptr_t*)((uintptr_t)ThreadObj + stack_base_offset) : 0; 293 | auto stack_limit = stack_limit_offset ? *(uintptr_t*)((uintptr_t)ThreadObj + stack_limit_offset) : 0; 294 | auto initial_stack = initial_stack_offset ? *(uintptr_t*)((uintptr_t)ThreadObj + initial_stack_offset) : 0; 295 | auto current_stack_location_offset = GetThreadCurrentStackLocationOffset(ThreadObj, stack_base_offset, stack_limit_offset, initial_stack_offset); 296 | auto pcurrent_stack_location = current_stack_location_offset ? (uintptr_t*)((uintptr_t)ThreadObj + current_stack_location_offset) : nullptr; 297 | if (ThreadObj == KeGetCurrentThread() || !stack_base || !stack_limit || !initial_stack || !current_stack_location_offset || PsIsThreadTerminating(ThreadObj) || pcurrent_stack_location == nullptr) 298 | return NULL; 299 | 300 | tThreadLock lock_thread = (tThreadLock)((uintptr_t)GetKernelBase() + KELOCKTHREAD); 301 | tThreadLock resume_thread = (tThreadLock)((uintptr_t)GetKernelBase() + KERESUMETHREAD); 302 | 303 | 304 | //to-do: lock thread to make it more stable 305 | 306 | #define KTHREAD_SPINLOCK_OFFSET 0x2E0 //taken from KeSuspendThread 307 | 308 | //KeAcquireSpinLockAtDpcLevel((PKSPIN_LOCK)((uintptr_t)ThreadObj + KTHREAD_SPINLOCK_OFFSET)); //no idea what i'm doing 309 | lock_thread(ThreadObj); 310 | 311 | auto current_stack_location = *pcurrent_stack_location; 312 | auto current_stack_size = stack_base - current_stack_location; 313 | if (current_stack_location > stack_limit && current_stack_location < stack_base && MmGetPhysicalAddress((PVOID)current_stack_location).QuadPart) { 314 | if (current_stack_size > copied_stack_buffer_len) 315 | current_stack_size = copied_stack_buffer_len; 316 | if (!MmIsAddressValid((PVOID)current_stack_location)) { 317 | //KeReleaseSpinLockFromDpcLevel((PKSPIN_LOCK)((uintptr_t)ThreadObj + KTHREAD_SPINLOCK_OFFSET)); 318 | resume_thread(ThreadObj); 319 | return NULL; 320 | } 321 | memmove(copied_stack, (PVOID)current_stack_location, current_stack_size); 322 | //KeReleaseSpinLockFromDpcLevel((PKSPIN_LOCK)((uintptr_t)ThreadObj + KTHREAD_SPINLOCK_OFFSET)); 323 | } 324 | else 325 | current_stack_size = NULL; 326 | resume_thread(ThreadObj); 327 | 328 | return current_stack_size; 329 | } 330 | 331 | struct sStackWalkList { 332 | uintptr_t Rsp, Rip; 333 | }; 334 | 335 | #include 336 | 337 | typedef LOGICAL(NTAPI*tMmCanThreadFault)(VOID); 338 | 339 | int c = 0; 340 | void walk_stack_thread(PETHREAD ThreadObj, sStackWalkList* results) 341 | { 342 | /*tMmCanThreadFault MmCanThreadFault = (tMmCanThreadFault)((uintptr_t)GetKernelBase() + MM_CAN_THREAD_FAULT_OFFSET); 343 | if (MmCanThreadFault()) { 344 | DbgPrint("Thread can fault\r\n"); 345 | return; 346 | }*/ 347 | 348 | UCHAR copied_stack[0x1000]; 349 | if (auto stack_len = CopyStack(ThreadObj, copied_stack, sizeof(copied_stack))) { 350 | //if (stack_len != NULL) 351 | //DbgPrint("copied stack, size = %d\n", stack_len); 352 | 353 | if (stack_len >= 0x48 && stack_len != 0x1000 && (c == 0)) { 354 | int FuncEnumCnt = 0; 355 | 356 | CONTEXT ctx; 357 | RtlZeroMemory(&ctx, sizeof(ctx)); 358 | ctx.Rip = *PULONGLONG(&copied_stack[0] + 0x38); 359 | ctx.Rsp = reinterpret_cast(&copied_stack[0] + 0x40); 360 | 361 | //KNONVOLATILE_CONTEXT_POINTERS NvContext; 362 | 363 | uintptr_t ntoskrnl_text_section_start_addr; 364 | ULONG len; 365 | get_ntoskrnl_text_section(&ntoskrnl_text_section_start_addr, &len); 366 | if (!ntoskrnl_text_section_start_addr || !len) 367 | return; 368 | if (ctx.Rip >= ntoskrnl_text_section_start_addr && ctx.Rip < ntoskrnl_text_section_start_addr + len) { 369 | __try { 370 | do { 371 | if (ctx.Rip < reinterpret_cast(MmSystemRangeStart) || ctx.Rsp < reinterpret_cast(MmSystemRangeStart)) 372 | break; 373 | 374 | if (!MmIsAddressValid((PVOID)ctx.Rip) || !MmIsAddressValid((PVOID)ctx.Rsp)) { 375 | break; 376 | } 377 | 378 | results[FuncEnumCnt].Rip = ctx.Rip; 379 | results[FuncEnumCnt].Rsp = ctx.Rsp; 380 | 381 | DWORD64 ImageBase = NULL; 382 | auto old_irql = KeRaiseIrqlToDpcLevel(); 383 | auto f = RtlLookupFunctionEntry(ctx.Rip, &ImageBase, NULL); 384 | KeLowerIrql(old_irql); 385 | if (!f) 386 | break; 387 | 388 | PVOID HandlerData = NULL; 389 | DWORD64 EstablisherFrame = NULL; 390 | #define UNW_FLAG_NHANDLER NULL 391 | RtlVirtualUnwind(UNW_FLAG_NHANDLER, ImageBase, ctx.Rip, f, &ctx, &HandlerData, &EstablisherFrame, nullptr); 392 | ++FuncEnumCnt; 393 | if (!ctx.Rip) 394 | break; 395 | } while (FuncEnumCnt < 0x20); 396 | //DbgPrint("enumerated %d functions.\n", FuncEnumCnt); 397 | } 398 | __except (EXCEPTION_EXECUTE_HANDLER) { 399 | DbgPrint("Access Violation was raised (1).\n"); 400 | } 401 | 402 | } 403 | 404 | } 405 | } 406 | } 407 | 408 | 409 | //from http://www.geoffchappell.com/studies/windows/km/ntoskrnl/api/ex/sysinfo/bigpool_entry.htm 410 | typedef struct _SYSTEM_BIGPOOL_ENTRY 411 | { 412 | union { 413 | uintptr_t VirtualAddress; 414 | ULONG_PTR NonPaged : 1; 415 | }; 416 | ULONG_PTR SizeInBytes; 417 | union { 418 | UCHAR Tag[4]; 419 | ULONG TagUlong; 420 | }; 421 | } SYSTEM_BIGPOOL_ENTRY, *PSYSTEM_BIGPOOL_ENTRY; 422 | 423 | //from http://www.geoffchappell.com/studies/windows/km/ntoskrnl/api/ex/sysinfo/bigpool.htm 424 | typedef struct _SYSTEM_BIGPOOL_INFORMATION { 425 | ULONG Count; 426 | SYSTEM_BIGPOOL_ENTRY AllocatedInfo[ANYSIZE_ARRAY]; 427 | } SYSTEM_BIGPOOL_INFORMATION, *PSYSTEM_BIGPOOL_INFORMATION; 428 | 429 | void scan_bigpool_check_1(uintptr_t addr) 430 | { 431 | ULONG len = 4 * 1024 * 1024; 432 | auto mem = ExAllocatePoolWithTag(POOL_TYPE::NonPagedPool, len, POOL_TAG); 433 | 434 | if (NT_SUCCESS(ZwQuerySystemInformation(SystemBigPoolInformation, mem, len, &len))) { 435 | auto pBuf = reinterpret_cast(mem); 436 | for (ULONG i = 0; i < pBuf->Count; i++) { 437 | if (pBuf->AllocatedInfo[i].TagUlong == 'enoN' && addr >= pBuf->AllocatedInfo[i].VirtualAddress && addr < pBuf->AllocatedInfo[i].VirtualAddress + pBuf->AllocatedInfo[i].SizeInBytes) { 438 | if (pBuf->AllocatedInfo[i].SizeInBytes > 0x1000) { 439 | __try { 440 | UCHAR zeroedoutpehdr[0x1000]{}; 441 | if (auto pe_hdr = MmMapIoSpace(MmGetPhysicalAddress((void*)pBuf->AllocatedInfo[i].VirtualAddress), PAGE_SIZE, MEMORY_CACHING_TYPE::MmNonCached)) { 442 | if (memcmp(pe_hdr, zeroedoutpehdr, 0x1000)) 443 | DbgPrint("[DETECTION] running kdmapper/drvmap manual mapped driver detected (99%% confidence).\n"); 444 | 445 | MmUnmapIoSpace(pe_hdr, PAGE_SIZE); 446 | } 447 | else 448 | DbgPrint("[DETECTION] Unable to map physical memory to dump/verify but manual map driver detected anyways with 95%% confidence.\n"); 449 | } 450 | __except (EXCEPTION_EXECUTE_HANDLER) { 451 | DbgPrint("Access Violation was raised.\n"); 452 | } 453 | } 454 | } 455 | } 456 | } 457 | else 458 | DbgPrint("Failed to get bigpool.\n"); 459 | 460 | ExFreePoolWithTag(mem, POOL_TAG); 461 | } 462 | 463 | 464 | void scan_bigpool_check_2() 465 | { 466 | ULONG len = 4 * 1024 * 1024; 467 | auto mem = ExAllocatePoolWithTag(POOL_TYPE::NonPagedPool, len, POOL_TAG); 468 | 469 | if (NT_SUCCESS(ZwQuerySystemInformation(SystemBigPoolInformation, mem, len, &len))) { 470 | auto pBuf = reinterpret_cast(mem); 471 | for (ULONG i = 0; i < pBuf->Count; i++) { 472 | __try { 473 | /*if (auto page = MmMapIoSpaceEx(MmGetPhysicalAddress((void*)pBuf->AllocatedInfo[i].VirtualAddress), PAGE_SIZE, PAGE_READWRITE)) { 474 | 475 | MmUnmapIoSpace(page, PAGE_SIZE); 476 | } 477 | */ 478 | 479 | //https://www.unknowncheats.me/forum/2427433-post12.html 480 | if (pBuf->AllocatedInfo[i].TagUlong == 'SldT') { 481 | DbgPrint("[FLAG] TdlS pooltag detected\n"); 482 | if (auto page = MmMapIoSpaceEx(MmGetPhysicalAddress((void*)pBuf->AllocatedInfo[i].VirtualAddress), PAGE_SIZE, PAGE_READWRITE)) { 483 | if (*(PULONG)((uintptr_t)page + 0x184) == 0x0B024BC8B48) 484 | DbgPrint("[DETECTION] 0x0B024BC8B48 found at pool + 0x184\n"); 485 | //to-do: also hash the memory(with custom crc32 table) and check for 0C8931AEBh 486 | 487 | MmUnmapIoSpace(page, PAGE_SIZE); 488 | } 489 | } 490 | 491 | /* 492 | pooltags checked: 493 | if ( *(v3 - 1) != 'rcIC' || *v3 <= v3[1] ) 494 | { 495 | if ( *(v3 - 1) == 'csIC' && *v3 > v3[1] ) 496 | if these pooltags do not exist, code integrity is disabled(maybe this occurs due to UPGDSED, which disables the initialization of CI?) 497 | */ 498 | 499 | } 500 | __except (EXCEPTION_EXECUTE_HANDLER) { 501 | DbgPrint("Access Violation was raised (scan_bigpool_check_2).\n"); 502 | } 503 | } 504 | } 505 | else 506 | DbgPrint("Failed to get bigpool.\n"); 507 | 508 | ExFreePoolWithTag(mem, POOL_TAG); 509 | } 510 | 511 | void scan_system_threads(PSYSTEM_MODULE_INFORMATION pModuleList) 512 | { 513 | sleep(1000); 514 | for (ULONG thrd_id = 4; thrd_id < 0x30000; thrd_id += 4) { 515 | PETHREAD ThreadObj; 516 | if (!NT_SUCCESS(PsLookupThreadByThreadId(reinterpret_cast(thrd_id), &ThreadObj))) 517 | continue; 518 | if (!PsIsSystemThread(ThreadObj) || ThreadObj == KeGetCurrentThread()) { 519 | //DbgPrint("Ignoring non-system thread.\n"); 520 | continue; 521 | } 522 | uintptr_t start_addr; 523 | get_thread_start_address(ThreadObj, &start_addr); 524 | //DbgPrint("thread start addr = %p\n", start_addr); 525 | if (is_address_outside_of_module_list(pModuleList, start_addr)) 526 | DbgPrint("[DETECTION] Thread Start Address outside of module list. thread id = %d\n", thrd_id); 527 | 528 | if (start_addr && (memcmp((void*)start_addr, "\xFF\xE1", 2) == 0)) { 529 | DbgPrint("[DETECTION] jmp rcx.\n"); 530 | } 531 | 532 | sStackWalkList results[0x20]; 533 | RtlZeroMemory(&results[0], sizeof(results)); 534 | walk_stack_thread(ThreadObj, &results[0]); 535 | for (int i = 0; i < 0x20; i++) { 536 | if (results[i].Rsp == 0) 537 | break; 538 | if (is_address_outside_of_module_list(pModuleList, results[i].Rip)) { 539 | DbgPrint("[FLAG/DETECTION] Stack Scan found Address outside of module list.\n"); 540 | scan_bigpool_check_1(results[i].Rip); 541 | } 542 | //print_module_name(pModuleList, results[i].Rip); 543 | //DbgPrint("stack walk found %p\n---------------\n", results[i].Rip); 544 | } 545 | 546 | } 547 | 548 | } 549 | 550 | typedef struct _UNLOADED_DRIVERS { 551 | UNICODE_STRING Name; 552 | PVOID StartAddress; 553 | PVOID EndAddress; 554 | LARGE_INTEGER CurrentTime; 555 | } UNLOADED_DRIVERS, *PUNLOADED_DRIVERS; 556 | 557 | extern "C" PUNLOADED_DRIVERS MmUnloadedDrivers; 558 | 559 | void check_unloaded() 560 | { 561 | //to-do 562 | } 563 | 564 | struct PiDDBCacheEntry 565 | { 566 | LIST_ENTRY List; 567 | UNICODE_STRING DriverName; 568 | ULONG TimeDateStamp; 569 | NTSTATUS LoadStatus; 570 | char _0x0028[16]; // data from the shim engine, or uninitialized memory for custom drivers 571 | }; 572 | 573 | EXTERN_C PVOID ResolveRelativeAddress( 574 | _In_ PVOID Instruction, 575 | _In_ ULONG OffsetOffset, 576 | _In_ ULONG InstructionSize 577 | ) 578 | { 579 | ULONG_PTR Instr = (ULONG_PTR)Instruction; 580 | LONG RipOffset = *(PLONG)(Instr + OffsetOffset); 581 | PVOID ResolvedAddr = (PVOID)(Instr + InstructionSize + RipOffset); 582 | 583 | return ResolvedAddr; 584 | } 585 | 586 | UCHAR PiDDBLockPtr_sig[] = "\x48\x8D\x0D\x00\x00\x00\x00\xE8\x00\x00\x00\x00\x4C\x8B\x8C"; 587 | UCHAR PiDDBCacheTablePtr_sig[] = "\x66\x03\xD2\x48\x8D\x0D"; 588 | //you can also put the sig within the function, but some of the sig ends up on the stack and in the .text section, and causes issues when zeroing the sig memory. 589 | 590 | extern "C" bool LocatePiDDB(PERESOURCE* lock, PRTL_AVL_TABLE* table) 591 | { 592 | PVOID PiDDBLockPtr = nullptr, PiDDBCacheTablePtr = nullptr; 593 | if (!NT_SUCCESS(BBScanSection("PAGE", PiDDBLockPtr_sig, 0, sizeof(PiDDBLockPtr_sig) - 1, reinterpret_cast(&PiDDBLockPtr)))) { 594 | DbgPrintEx(0, 0, xorstr_("Unable to find PiDDBLockPtr sig.\n")); 595 | return false; 596 | } 597 | 598 | if (!NT_SUCCESS(BBScanSection("PAGE", PiDDBCacheTablePtr_sig, 0, sizeof(PiDDBCacheTablePtr_sig) - 1, reinterpret_cast(&PiDDBCacheTablePtr)))) { 599 | DbgPrintEx(0, 0, xorstr_("Unable to find PiDDBCacheTablePtr sig.\n")); 600 | return false; 601 | } 602 | 603 | PiDDBCacheTablePtr = PVOID((uintptr_t)PiDDBCacheTablePtr + 3); 604 | 605 | *lock = (PERESOURCE)(ResolveRelativeAddress(PiDDBLockPtr, 3, 7)); 606 | *table = (PRTL_AVL_TABLE)(ResolveRelativeAddress(PiDDBCacheTablePtr, 3, 7)); 607 | 608 | return true; 609 | } 610 | 611 | void check_piddb() 612 | { 613 | PERESOURCE PiDDBLock; PRTL_AVL_TABLE table; 614 | if (!LocatePiDDB(&PiDDBLock, &table)) { 615 | DbgPrint("sig scanning failed.\n"); 616 | return; 617 | } 618 | ExAcquireResourceExclusiveLite(PiDDBLock, TRUE); 619 | 620 | for (PiDDBCacheEntry* p = (PiDDBCacheEntry*)RtlEnumerateGenericTableAvl(table, TRUE); 621 | p != NULL; 622 | p = (PiDDBCacheEntry*)RtlEnumerateGenericTableAvl(table, FALSE)) { 623 | if (p->TimeDateStamp == 0x5284eac3) 624 | DbgPrint("[DETECTION] kdmapper detected, driver: %wZ\n", p->DriverName); 625 | if (p->TimeDateStamp == 0x57CD1415) 626 | DbgPrint("[DETECTION] drvmap detected, driver: %wZ\n", p->DriverName); 627 | } 628 | 629 | ExReleaseResourceLite(PiDDBLock); 630 | } 631 | 632 | static inline unsigned long long rdtsc_diff_vmexit() { 633 | auto t1 = __rdtsc(); 634 | int r[4]; 635 | __cpuid(r, 1); 636 | return __rdtsc() - t1; 637 | } 638 | 639 | int cpu_rdtsc_force_vmexit() { 640 | int i; 641 | unsigned long long avg = 0; 642 | for (i = 0; i < 10; i++) { 643 | avg = avg + rdtsc_diff_vmexit(); 644 | sleep(500); 645 | } 646 | avg = avg / 10; 647 | return (avg < 1000 && avg > 0) ? FALSE : TRUE; 648 | } 649 | 650 | bool detect_hypervisor() 651 | { 652 | if (cpu_rdtsc_force_vmexit()) { 653 | DbgPrint("Detected hypervisor through a timing attack\n"); 654 | return true; 655 | } 656 | 657 | __try { 658 | __vmx_vmread(NULL, nullptr); 659 | DbgPrint("Detected hypervisor through vmread\n"); 660 | } 661 | __except (EXCEPTION_EXECUTE_HANDLER) { 662 | 663 | } 664 | return false; 665 | } 666 | 667 | #pragma pack(push, 4) 668 | #define POBJECT PVOID 669 | 670 | typedef struct _SYSTEM_HANDLE 671 | { 672 | ULONG uIdProcess; 673 | UCHAR ObjectType; 674 | UCHAR Flags; 675 | USHORT Handle; 676 | POBJECT pObject; 677 | ACCESS_MASK GrantedAccess; 678 | }SYSTEM_HANDLE; 679 | 680 | typedef struct _SYSTEM_HANDLE_INFORMATION { 681 | ULONG uCount; 682 | SYSTEM_HANDLE Handles[ANYSIZE_ARRAY]; 683 | } SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION; 684 | 685 | #pragma pack(pop) 686 | 687 | PSYSTEM_HANDLE_INFORMATION GetHandleList() 688 | { 689 | NTSTATUS status = STATUS_SUCCESS; 690 | ULONG neededSize = 0; 691 | 692 | //ZwQuerySystemInformation(SystemHandleInformation, &neededSize, 0, &neededSize); //returns incorrect size for whatever reason -- don't use. 693 | neededSize = 8 * 1024 * 1024; 694 | 695 | PSYSTEM_HANDLE_INFORMATION pHandleList; 696 | 697 | if (pHandleList = (PSYSTEM_HANDLE_INFORMATION)ExAllocatePoolWithTag(NonPagedPool, neededSize, POOL_TAG)) { 698 | 699 | NTSTATUS r; 700 | if (NT_SUCCESS(r = ZwQuerySystemInformation(SystemHandleInformation, pHandleList, neededSize, 0))) 701 | return pHandleList; 702 | else 703 | DbgPrint("r = %x\n", r); 704 | } 705 | return nullptr; 706 | } 707 | 708 | 709 | void check_physical_memory_handles() 710 | { 711 | auto handles = GetHandleList(); 712 | if (!handles) { 713 | DbgPrint("Unable to obtain handle list\n"); 714 | return; 715 | } 716 | UNICODE_STRING phys_mem_str; 717 | OBJECT_ATTRIBUTES oaAttributes; 718 | RtlInitUnicodeString(&phys_mem_str, xorstr_(L"\\Device\\PhysicalMemory")); 719 | InitializeObjectAttributes(&oaAttributes, &phys_mem_str, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, (HANDLE)NULL, (PSECURITY_DESCRIPTOR)NULL); 720 | HANDLE hPhysMem; 721 | auto ntStatus = ZwOpenSection(&hPhysMem, SECTION_ALL_ACCESS, &oaAttributes); 722 | 723 | PVOID Object; 724 | if (!NT_SUCCESS(ObReferenceObjectByHandle(hPhysMem, 1, nullptr, KernelMode, &Object, nullptr))) { 725 | DbgPrint("Unablle to get PhyiscalMemory object.\n"); 726 | ExFreePoolWithTag(handles, POOL_TAG); 727 | ZwClose(hPhysMem); 728 | return; 729 | } 730 | 731 | ZwClose(hPhysMem); 732 | 733 | __try { 734 | for (ULONG i = 0; i < handles->uCount; i++) { 735 | if (handles->Handles[i].uIdProcess == 4) 736 | continue; //ignore system process for detection. 737 | if (handles->Handles[i].pObject == Object) { //is PhysicalMemory object? 738 | //DbgPrint("found physmem handle\n"); 739 | if (!ObIsKernelHandle((HANDLE)handles->Handles[i].Handle)) 740 | DbgPrint("[DETECTION] Usermode PhysicalMemory handle detected, pid = %d, access = 0x%x.\n", handles->Handles[i].uIdProcess, handles->Handles[i].GrantedAccess); 741 | } 742 | } 743 | } __except (EXCEPTION_EXECUTE_HANDLER) { 744 | DbgPrint("Unexpected AV in check_physical_memory_handles\n"); 745 | } 746 | 747 | ObDereferenceObject(Object); 748 | 749 | ExFreePoolWithTag(handles, POOL_TAG); 750 | } 751 | 752 | PSYSTEM_PROCESS_INFO get_process_list() 753 | { 754 | NTSTATUS status = STATUS_SUCCESS; 755 | ULONG neededSize = 0; 756 | 757 | neededSize = 8 * 1024 * 1024; 758 | 759 | PSYSTEM_PROCESS_INFO pProcessList; 760 | 761 | if (pProcessList = (decltype(pProcessList))ExAllocatePoolWithTag(NonPagedPool, neededSize, POOL_TAG)) { 762 | 763 | NTSTATUS r; 764 | if (NT_SUCCESS(r = ZwQuerySystemInformation(SystemProcessInformation, pProcessList, neededSize, 0))) 765 | return pProcessList; 766 | else 767 | DbgPrint("r = %x\n", r); 768 | } 769 | return nullptr; 770 | 771 | } 772 | 773 | #include "page_table_defs.h" 774 | 775 | void detect_perfect_injector() 776 | { 777 | UNICODE_STRING phys_mem_str; 778 | OBJECT_ATTRIBUTES oaAttributes; 779 | RtlInitUnicodeString(&phys_mem_str, xorstr_(L"\\Device\\PhysicalMemory")); 780 | InitializeObjectAttributes(&oaAttributes, &phys_mem_str, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, (HANDLE)NULL, (PSECURITY_DESCRIPTOR)NULL); 781 | HANDLE hPhysMem; 782 | if (!NT_SUCCESS(ZwOpenSection(&hPhysMem, SECTION_ALL_ACCESS, &oaAttributes))) { 783 | DbgPrint("Failed to open phys mem section\n"); 784 | return; 785 | } 786 | 787 | PVOID PhysicalMemoryBegin = NULL; 788 | 789 | auto Range = MmGetPhysicalMemoryRanges(); 790 | DWORD64 PhysicalMemorySize = 0; 791 | 792 | while (Range->NumberOfBytes.QuadPart) 793 | { 794 | PhysicalMemorySize = max(PhysicalMemorySize, Range->BaseAddress.QuadPart + Range->NumberOfBytes.QuadPart); 795 | Range++; 796 | } 797 | 798 | if (!NT_SUCCESS(ZwMapViewOfSection( 799 | hPhysMem, 800 | ZwCurrentProcess(), 801 | &PhysicalMemoryBegin, 802 | NULL, 803 | NULL, 804 | nullptr, 805 | &PhysicalMemorySize, 806 | ViewUnmap, 807 | NULL, 808 | PAGE_READWRITE))) { 809 | DbgPrintEx(0, 0, "ZwMapViewOfSection failed.\n"); 810 | ZwClose(hPhysMem); 811 | return; 812 | } 813 | 814 | auto processes = get_process_list(); 815 | if (!processes) { 816 | DbgPrint("Unable to get process list.\n"); 817 | ZwUnmapViewOfSection(ZwCurrentProcess(), PhysicalMemoryBegin); 818 | ZwClose(hPhysMem); 819 | return; 820 | } 821 | 822 | auto walk = processes; 823 | while (walk->NextEntryOffset) 824 | { 825 | /*if ((ULONG)walk->UniqueProcessId != 12132) { 826 | walk = (PSYSTEM_PROCESS_INFO)((uintptr_t)walk + walk->NextEntryOffset); 827 | continue; 828 | }*/ 829 | 830 | //instead of reading cr3 from _EPROCESS::DirectoryBase you can obtain it by attaching to the process & __readcr3(). 831 | KAPC_STATE apcState; 832 | 833 | PEPROCESS process = NULL; 834 | if (walk->UniqueProcessId != NULL) 835 | if (NT_SUCCESS(PsLookupProcessByProcessId(walk->UniqueProcessId, &process))) { 836 | 837 | __try { 838 | KeStackAttachProcess(process, &apcState); 839 | auto cr3 = __readcr3(); 840 | KeUnstackDetachProcess(&apcState); 841 | 842 | PTE_CR3 Cr3 = { cr3 }; 843 | 844 | auto system_range_start = VIRT_ADDR{ (uintptr_t)MmSystemRangeStart }; 845 | 846 | //max value of 9 bits = 512 847 | for (int pml4_index = system_range_start.pml4_index; pml4_index < 512; pml4_index++) 848 | { 849 | uint64_t pml4_addr = PFN_TO_PAGE(Cr3.pml4_p) + sizeof(PML4E) * pml4_index; 850 | if (pml4_addr > PhysicalMemorySize) 851 | continue; 852 | auto pml4 = (PML4E*)((uintptr_t)PhysicalMemoryBegin + pml4_addr); 853 | if (pml4->present && pml4->user) { 854 | for (int pdpt_index = system_range_start.pdpt_index; pdpt_index < 512; pdpt_index++) { 855 | 856 | auto pdpte_addr = PFN_TO_PAGE(pml4->pdpt_p) + sizeof(PDPTE) * pdpt_index; 857 | if (pdpte_addr > PhysicalMemorySize) 858 | continue; 859 | 860 | auto pdpte = (PDPTE*)((uintptr_t)PhysicalMemoryBegin + pdpte_addr); 861 | if (!pdpte->present || !pdpte->user) 862 | continue; 863 | 864 | DbgPrint("[DETECTION] kernelmode memory mapped to usermode: %wZ\n", walk->ImageName); 865 | } 866 | 867 | } 868 | } 869 | } 870 | __except (EXCEPTION_EXECUTE_HANDLER) { 871 | DbgPrint("unexpected AV in detect PI\n"); 872 | } 873 | ObDereferenceObject(process); 874 | } 875 | else 876 | DbgPrint("Unable to lookup _EPROCESS from PID %d\n", (ULONG)walk->UniqueProcessId); 877 | //DbgPrint("process: %ws checked\n", walk->ImageName.Buffer); 878 | 879 | walk = (PSYSTEM_PROCESS_INFO)((uintptr_t)walk + walk->NextEntryOffset); // Calculate the address of the next entry. 880 | } 881 | 882 | ExFreePoolWithTag(processes, POOL_TAG); 883 | ZwUnmapViewOfSection(ZwCurrentProcess(), PhysicalMemoryBegin); 884 | ZwClose(hPhysMem); 885 | } 886 | 887 | typedef struct _SYSTEM_BOOT_ENVIRONMENT_INFORMATION_V1 { 888 | struct _GUID BootIdentifier; 889 | enum _FIRMWARE_TYPE FirmwareType; 890 | } SYSTEM_BOOT_ENVIRONMENT_INFORMATION_V1, *PSYSTEM_BOOT_ENVIRONMENT_INFORMATION_V1; 891 | 892 | // Size=32 893 | typedef struct _SYSTEM_BOOT_ENVIRONMENT_INFORMATION { 894 | struct _GUID BootIdentifier; 895 | enum _FIRMWARE_TYPE FirmwareType; 896 | unsigned __int64 BootFlags; 897 | } SYSTEM_BOOT_ENVIRONMENT_INFORMATION, *PSYSTEM_BOOT_ENVIRONMENT_INFORMATION; 898 | 899 | void get_boot_uuid() 900 | { 901 | //SystemBootEnvironmentInformation(0x5a) 902 | NTSTATUS status = STATUS_SUCCESS; 903 | ULONG neededSize = 0; 904 | 905 | neededSize = PAGE_SIZE; 906 | 907 | PSYSTEM_BOOT_ENVIRONMENT_INFORMATION pBootInfo; 908 | 909 | if (pBootInfo = (decltype(pBootInfo))ExAllocatePoolWithTag(NonPagedPool, neededSize, POOL_TAG)) { 910 | 911 | NTSTATUS r; 912 | if (NT_SUCCESS(r = ZwQuerySystemInformation(SystemBootEnvironmentInformation, pBootInfo, neededSize, 0))) { 913 | DbgPrint("boot GUID: %08X-%04X-%04X-%02X%02X%02X%02X%02X%02X%02X%02X\n", pBootInfo->BootIdentifier.Data1, pBootInfo->BootIdentifier.Data2, pBootInfo->BootIdentifier.Data3, pBootInfo->BootIdentifier.Data4[0], pBootInfo->BootIdentifier.Data4[1], pBootInfo->BootIdentifier.Data4[2], pBootInfo->BootIdentifier.Data4[3], pBootInfo->BootIdentifier.Data4[4], pBootInfo->BootIdentifier.Data4[5], pBootInfo->BootIdentifier.Data4[6], pBootInfo->BootIdentifier.Data4[7]); 914 | ExFreePoolWithTag(pBootInfo, POOL_TAG); 915 | } 916 | else 917 | DbgPrint("r = %x\n", r); 918 | } 919 | } 920 | 921 | typedef struct _DIRECTORY_BASIC_INFORMATION { 922 | UNICODE_STRING ObjectName; 923 | UNICODE_STRING ObjectTypeName; 924 | } DIRECTORY_BASIC_INFORMATION, *PDIRECTORY_BASIC_INFORMATION; 925 | 926 | void check_driver_dispatch(PSYSTEM_MODULE_INFORMATION pModuleList) 927 | { 928 | HANDLE hDir; 929 | UNICODE_STRING str; 930 | OBJECT_ATTRIBUTES oa; 931 | RtlInitUnicodeString(&str, xorstr_(L"\\Driver")); 932 | InitializeObjectAttributes(&oa, &str, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, (HANDLE)NULL, (PSECURITY_DESCRIPTOR)NULL); 933 | if (!NT_SUCCESS(ZwOpenDirectoryObject(&hDir, DIRECTORY_QUERY, &oa))) { 934 | DbgPrint("Failed to open \\Driver directory object.\n"); 935 | return; 936 | } 937 | 938 | PVOID Obj; 939 | if (!NT_SUCCESS(ObReferenceObjectByHandle(hDir, DIRECTORY_QUERY, nullptr, KernelMode, &Obj, nullptr))) { 940 | DbgPrint("ObReferenceObjectByHandle failed.\n"); 941 | return; 942 | } 943 | NtClose(hDir); 944 | 945 | auto obj_type = ObGetObjectType(Obj); 946 | ObDereferenceObject(Obj); 947 | 948 | HANDLE h; 949 | if (!NT_SUCCESS(ObOpenObjectByName(&oa, obj_type, KernelMode, NULL, DIRECTORY_QUERY, nullptr, &h))) { 950 | DbgPrint("ObOpenObjectByName failed.\n"); 951 | return; 952 | } 953 | 954 | auto dir_info = (PDIRECTORY_BASIC_INFORMATION)ExAllocatePoolWithTag(POOL_TYPE::NonPagedPool, PAGE_SIZE, POOL_TAG); 955 | ULONG ulContext = 0; 956 | 957 | ULONG returned_bytes; 958 | 959 | while (NT_SUCCESS(ZwQueryDirectoryObject(h, dir_info, PAGE_SIZE, TRUE, FALSE, &ulContext, &returned_bytes))) { 960 | PDRIVER_OBJECT pObj; 961 | wchar_t wsDriverName[100] = L"\\Driver\\"; 962 | wcscat(wsDriverName, dir_info->ObjectName.Buffer); 963 | UNICODE_STRING ObjName; 964 | ObjName.Length = ObjName.MaximumLength = wcslen(wsDriverName) * 2; 965 | ObjName.Buffer = wsDriverName; 966 | if (NT_SUCCESS(ObReferenceObjectByName(&ObjName, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, NULL, *IoDriverObjectType, KernelMode, nullptr, (PVOID*)&pObj))) { 967 | //DbgPrint("%wZ\n", pObj->DriverName); 968 | 969 | if (is_address_outside_of_module_list(pModuleList, reinterpret_cast(pObj->MajorFunction[IRP_MJ_DEVICE_CONTROL]))) { 970 | DbgPrint("[DETECTION] %wZ driver has spoofed driver dispatch\n", pObj->DriverName); 971 | } 972 | 973 | if (is_address_outside_of_module_list(pModuleList, (uintptr_t)pObj->DriverStart)) { 974 | DbgPrint("[DETECTION] %wZ driver has spoofed DriverStart\n", pObj->DriverName); 975 | } 976 | 977 | auto dd = reinterpret_cast(pObj->MajorFunction[IRP_MJ_DEVICE_CONTROL]); 978 | if (dd < (uintptr_t)pObj->DriverStart || dd >(uintptr_t)pObj->DriverStart + pObj->DriverSize) { 979 | DbgPrint("[DETECTION] %wZ driver has spoofed driver dispatch (2)\n", pObj->DriverName); 980 | } 981 | 982 | if (is_address_outside_of_module_list(pModuleList, reinterpret_cast(pObj->FastIoDispatch))) { 983 | DbgPrint("[DETECTION] %wZ driver has spoofed FastIoDispatch\n", pObj->DriverName); 984 | } 985 | 986 | ObDereferenceObject(pObj); 987 | } 988 | 989 | } 990 | 991 | 992 | ZwClose(h); 993 | } 994 | 995 | void check() 996 | { 997 | get_boot_uuid(); //checking if i've spoofed it properly. 998 | 999 | auto pModuleList = GetKernelModuleList(); 1000 | 1001 | DbgPrint("Scanning system threads\n"); 1002 | scan_system_threads(pModuleList); 1003 | DbgPrint("Finished scanning system threads\n"); 1004 | 1005 | DbgPrint("Scanning bigpool\n"); 1006 | scan_bigpool_check_2(); 1007 | DbgPrint("Finished scanning bigpool\n"); 1008 | 1009 | DbgPrint("Scanning PiDDBCacheTable\n"); 1010 | check_piddb(); 1011 | DbgPrint("scanned PiDDBCacheTable\n"); 1012 | 1013 | if (is_lazy_hypervisor_running()) //https://gist.github.com/drew0709/d31840bebbbb1ff1d112a6f46e162c05 1014 | DbgPrint("lazy hypervisor detected\n"); 1015 | if (!detect_hypervisor()) 1016 | DbgPrint("HV not detected\n"); 1017 | 1018 | check_physical_memory_handles(); 1019 | 1020 | detect_perfect_injector(); 1021 | 1022 | check_driver_dispatch(pModuleList); 1023 | DbgPrint("scanned driver dispatch\n"); 1024 | 1025 | ExFreePoolWithTag(pModuleList, POOL_TAG); 1026 | 1027 | DbgPrint("Scan routine finished!\n"); 1028 | } 1029 | 1030 | DRIVER_UNLOAD MyUnload; 1031 | 1032 | _Use_decl_annotations_ 1033 | VOID 1034 | MyUnload( 1035 | struct _DRIVER_OBJECT *DriverObject 1036 | ) 1037 | { 1038 | // Function body 1039 | } 1040 | 1041 | EXTERN_C 1042 | NTSTATUS 1043 | NTAPI 1044 | DriverEntry( 1045 | _In_ PDRIVER_OBJECT DriverObject, 1046 | _In_ PUNICODE_STRING RegistryPath 1047 | ) 1048 | { 1049 | //UNREFERENCED_PARAMETER(DriverObject); 1050 | UNREFERENCED_PARAMETER(RegistryPath); 1051 | 1052 | DriverObject->DriverUnload = MyUnload; 1053 | 1054 | DbgPrint("(KERNEL) anti-cheat simulator v2\n"); 1055 | 1056 | HANDLE hThread; 1057 | if (NT_SUCCESS(PsCreateSystemThread(&hThread, STANDARD_RIGHTS_ALL, NULL, NULL, NULL, (PKSTART_ROUTINE)&check, NULL))) 1058 | ZwClose(hThread); 1059 | else 1060 | DbgPrint("Failed to create check thread.\n"); 1061 | 1062 | return true; 1063 | } 1064 | --------------------------------------------------------------------------------