├── LICENSE ├── README.md ├── chdr.cpp └── chdr.h /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 ch4ncellor 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | chdr (Cheddar 🧀) 3 |

4 | 5 | --- 6 | 7 |

8 | Cheddar 🧀 is a lightweight library geared towards windows process hacking/manipulation, but with much more use case. 9 |

10 | 11 | --- 12 | 13 |

14 | This is currently an early WIP, contributions&issues are welcomed. 15 |

16 | 17 | -------------------------------------------------------------------------------- /chdr.cpp: -------------------------------------------------------------------------------- 1 | #include "chdr.h" 2 | 3 | // Process_t definitions and functions. 4 | namespace chdr 5 | { 6 | // Get target proces by name. 7 | Process_t::Process_t(const wchar_t* m_wszProcessName, std::int32_t m_ParseType, DWORD m_dDesiredAccess) 8 | { 9 | HANDLE m_hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL); 10 | 11 | PROCESSENTRY32 entry = { 0 }; 12 | entry.dwSize = sizeof(entry); 13 | 14 | while (Process32Next(m_hSnapShot, &entry)) 15 | { 16 | if (std::wcscmp(entry.szExeFile, m_wszProcessName) != 0) 17 | continue; 18 | 19 | this->m_nTargetProcessID = entry.th32ProcessID; 20 | this->m_hTargetProcessHandle = OpenProcess(m_dDesiredAccess, false, this->m_nTargetProcessID); 21 | break; 22 | } 23 | 24 | CloseHandle(m_hSnapShot); 25 | 26 | CH_ASSERT(true, this->m_hTargetProcessHandle && this->m_hTargetProcessHandle != INVALID_HANDLE_VALUE, 27 | "Couldn't obtain valid HANDLE for process %ws", m_wszProcessName); 28 | 29 | this->m_bShouldFreeHandleAtDestructor = this->m_hTargetProcessHandle && this->m_hTargetProcessHandle != INVALID_HANDLE_VALUE; 30 | this->m_eProcessArchitecture = this->GetProcessArchitecture_Internal(); 31 | 32 | this->m_szProcessPath = this->GetProcessPath_Internal(); 33 | this->m_szProcessName = this->GetProcessName_Internal(); 34 | 35 | this->m_PEHeaderData = PEHeaderData_t(*this, m_ParseType); 36 | } 37 | 38 | // Get target proces by PID. 39 | Process_t::Process_t(std::uint32_t m_nProcessID, std::int32_t m_ParseType, DWORD m_dDesiredAccess) 40 | { 41 | this->m_nTargetProcessID = m_nProcessID; 42 | this->m_hTargetProcessHandle = OpenProcess(m_dDesiredAccess, false, this->m_nTargetProcessID); 43 | 44 | CH_ASSERT(true, this->m_hTargetProcessHandle && this->m_hTargetProcessHandle != INVALID_HANDLE_VALUE, 45 | "Couldn't obtain valid HANDLE for PID %i", m_nProcessID); 46 | 47 | this->m_bShouldFreeHandleAtDestructor = this->m_hTargetProcessHandle && this->m_hTargetProcessHandle != INVALID_HANDLE_VALUE; 48 | this->m_eProcessArchitecture = this->GetProcessArchitecture_Internal(); 49 | 50 | this->m_szProcessPath = this->GetProcessPath_Internal(); 51 | this->m_szProcessName = this->GetProcessName_Internal(); 52 | 53 | this->m_PEHeaderData = PEHeaderData_t(*this, m_ParseType); 54 | } 55 | 56 | // Get target proces by HANDLE. 57 | Process_t::Process_t(HANDLE m_hProcessHandle, std::int32_t m_ParseType) 58 | { 59 | this->m_hTargetProcessHandle = m_hProcessHandle; 60 | this->m_nTargetProcessID = GetProcessId(this->m_hTargetProcessHandle); 61 | 62 | this->m_bShouldFreeHandleAtDestructor = this->m_hTargetProcessHandle && this->m_hTargetProcessHandle != INVALID_HANDLE_VALUE; 63 | this->m_eProcessArchitecture = this->GetProcessArchitecture_Internal(); 64 | 65 | this->m_szProcessPath = this->GetProcessPath_Internal(); 66 | this->m_szProcessName = this->GetProcessName_Internal(); 67 | 68 | this->m_PEHeaderData = PEHeaderData_t(*this, m_ParseType); 69 | } 70 | 71 | // Default dtor 72 | Process_t::~Process_t() 73 | { 74 | // Just to be safe, free all of our allocated memory from this process. 75 | if (!this->m_AllocatedMemoryTracker.empty()) 76 | { 77 | for (const auto& ForgottenMemory : this->m_AllocatedMemoryTracker) 78 | if (this->Free(ForgottenMemory.first)) // Has to be this ugly since I've included string enc in the log macros... :( 79 | CH_LOG("Successful free of memory at 0x%X with size 0x%X", ForgottenMemory.first, ForgottenMemory.second) 80 | else 81 | CH_LOG("Couldn't free memory at 0x%X with size 0x%X", ForgottenMemory.first, ForgottenMemory.second); 82 | } 83 | 84 | // Not allowed to release this HANDLE, or was already released. 85 | CH_ASSERT(true, 86 | this->m_bShouldFreeHandleAtDestructor && 87 | this->m_hTargetProcessHandle && 88 | this->m_hTargetProcessHandle != INVALID_HANDLE_VALUE, 89 | "adawdasd"); 90 | 91 | CloseHandle(m_hTargetProcessHandle); 92 | } 93 | 94 | // The process ID of the target process. (lol) 95 | std::uint32_t Process_t::GetProcessID() 96 | { 97 | return this->m_nTargetProcessID; 98 | } 99 | 100 | // Ensure we found a HANDLE to the target process. 101 | bool Process_t::IsValid() 102 | { 103 | return this->m_hTargetProcessHandle && this->m_hTargetProcessHandle != INVALID_HANDLE_VALUE; 104 | } 105 | 106 | // Is this process 32-bit running on 64-bit OS? 107 | bool Process_t::IsWow64() 108 | { 109 | BOOL m_bIsWow64 = FALSE; 110 | IsWow64Process(this->m_hTargetProcessHandle, &m_bIsWow64); 111 | 112 | return m_bIsWow64; 113 | } 114 | 115 | // Get name of target process. 116 | std::string Process_t::GetProcessName_Internal() 117 | { 118 | TCHAR m_szProcessNameBuffer[MAX_PATH]; 119 | if (!GetModuleBaseName(this->m_hTargetProcessHandle, NULL, m_szProcessNameBuffer, MAX_PATH)) 120 | return ""; 121 | 122 | m_szProcessNameBuffer[MAX_PATH - 1] = '\0'; 123 | 124 | // TCHAR->string 125 | _bstr_t m_szPreProcessName(m_szProcessNameBuffer); 126 | return std::string(m_szPreProcessName); 127 | } 128 | 129 | // The base address of the target process. 130 | std::uintptr_t Process_t::GetBaseAddress() 131 | { 132 | for (auto& CurrentModule : this->EnumerateModules(true)) 133 | { 134 | if (std::strcmp(CurrentModule.m_szName.c_str(), this->m_szProcessName.c_str()) != 0) 135 | continue; 136 | 137 | return CurrentModule.m_BaseAddress; 138 | } 139 | return NULL; 140 | } 141 | 142 | // Helper function to get name of target process. 143 | std::string Process_t::GetName() 144 | { 145 | return this->m_szProcessName; 146 | } 147 | 148 | // Get filesystem path of target process. 149 | std::string Process_t::GetProcessPath_Internal() 150 | { 151 | TCHAR m_szProcessPathBuffer[MAX_PATH]; 152 | if (!GetModuleFileNameEx(this->m_hTargetProcessHandle, NULL, m_szProcessPathBuffer, MAX_PATH)) 153 | return ""; 154 | 155 | m_szProcessPathBuffer[MAX_PATH - 1] = '\0'; 156 | 157 | // TCHAR->string 158 | _bstr_t m_szPreProcessPath(m_szProcessPathBuffer); 159 | return std::string(m_szPreProcessPath); 160 | } 161 | 162 | // Helper function to get filesystem path of target process. 163 | std::string Process_t::GetPath() 164 | { 165 | return this->m_szProcessPath; 166 | } 167 | 168 | // Get architecture of target process. 169 | Process_t::eProcessArchitecture Process_t::GetProcessArchitecture_Internal() 170 | { 171 | SYSTEM_INFO m_SystemInformation = { 0 }; 172 | GetNativeSystemInfo(&m_SystemInformation); 173 | 174 | // Native x86, or WOW64 process. 175 | if (m_SystemInformation.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL || this->IsWow64()) 176 | return eProcessArchitecture::ARCHITECTURE_x86; 177 | 178 | // Everything else should be native x64. 179 | return eProcessArchitecture::ARCHITECTURE_x64; 180 | } 181 | 182 | // Helper function to get architecture of target process. 183 | Process_t::eProcessArchitecture Process_t::GetProcessArchitecture() 184 | { 185 | return this->m_eProcessArchitecture; 186 | } 187 | 188 | // Helper function to get PE header data of target process. 189 | PEHeaderData_t Process_t::GetPEHeaderData() 190 | { 191 | return this->m_PEHeaderData; 192 | } 193 | 194 | // Did we suspend the target process ourselves? 195 | bool Process_t::IsManuallySuspended() 196 | { 197 | return this->m_bIsProcessManuallySuspended; 198 | } 199 | 200 | // Is the target process suspended? 201 | bool Process_t::IsSuspended() 202 | { 203 | // Traverse all threads, and ensure each is in a suspended state. 204 | for (const auto& CurrentThread : this->EnumerateThreads()) 205 | { 206 | if (CurrentThread.m_bIsThreadSuspended) 207 | continue; 208 | 209 | return false; 210 | } 211 | return true; 212 | } 213 | 214 | // Is the target process running under a debugger? 215 | bool Process_t::IsBeingDebugged() 216 | { 217 | BOOL m_bHasRemoteDebugger = FALSE; 218 | CheckRemoteDebuggerPresent(this->m_hTargetProcessHandle, &m_bHasRemoteDebugger); 219 | return m_bHasRemoteDebugger; 220 | } 221 | 222 | // The PEB of the target process. 223 | PEB Process_t::GetPEB() 224 | { 225 | NtQueryInformationProcess_fn NtQueryInformationProcess = 226 | CH_R_CAST(GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtQueryInformationProcess")); 227 | 228 | PROCESS_BASIC_INFORMATION m_ProcessBasicInformation; 229 | 230 | // Get address where PEB resides in this target process. 231 | if (NtQueryInformationProcess(this->m_hTargetProcessHandle, PROCESSINFOCLASS::ProcessBasicInformation, 232 | &m_ProcessBasicInformation, sizeof(PROCESS_BASIC_INFORMATION), nullptr) != 0x00000000/*STATUS_SUCCESS*/) 233 | { 234 | CH_LOG("NtQueryInformationProcess failure!"); 235 | return {}; 236 | } 237 | 238 | // Read PEB from found base address. 239 | const PEB m_PEB = this->Read(CH_R_CAST(m_ProcessBasicInformation.PebBaseAddress)); 240 | return m_PEB; 241 | } 242 | 243 | // Custom struct to pass through as lpReserved to communicate extra data to module. 244 | struct TransmittedData_t { 245 | char szKey[256]; 246 | }; 247 | 248 | // Data to pass through our shellcode. 249 | struct LoaderData_t { 250 | std::uintptr_t m_ModuleBase = 0u, m_ImageBase = 0u, m_EntryPoint = 0u, m_LoadLibrary = 0u, m_GetProcAddress = 0u, m_Memset = 0u; 251 | std::uint32_t m_RelocDirVA = 0u, m_RelocDirSize = 0u, m_ImportDirVA = 0u, m_PEHeaderSize = 0u, m_eInjectionFlags = 0u, m_Reason = 0u; 252 | TransmittedData_t m_CustomTransmitted = {}; 253 | } LoaderData; 254 | 255 | // Code to fix up needed data, then execute DllMain in target process. 256 | void __stdcall Shellcode(LoaderData_t* m_LoaderData) 257 | { 258 | const std::uintptr_t m_TargetBase = m_LoaderData->m_ModuleBase; 259 | const std::uintptr_t m_ImageBase = m_LoaderData->m_ImageBase; 260 | 261 | // Calculate delta to relocate. 262 | const std::uintptr_t m_Delta = m_TargetBase - m_ImageBase; 263 | 264 | if (m_Delta && m_LoaderData->m_RelocDirVA) 265 | // Relocate image. 266 | { 267 | PIMAGE_BASE_RELOCATION m_pRelocation = CH_R_CAST(m_TargetBase + m_LoaderData->m_RelocDirVA); 268 | PIMAGE_BASE_RELOCATION m_pRelocationEnd = CH_R_CAST(CH_R_CAST(m_pRelocation) + m_LoaderData->m_RelocDirSize - sizeof(IMAGE_BASE_RELOCATION)/*?*/); 269 | 270 | for (; m_pRelocation < m_pRelocationEnd; 271 | m_pRelocation = CH_R_CAST(CH_R_CAST(m_pRelocation) + m_pRelocation->SizeOfBlock)) 272 | { 273 | std::uint16_t* m_pRelocationType = CH_R_CAST(m_pRelocation + 0x1); 274 | 275 | const std::size_t m_nRelocationAmount = CH_S_CAST((m_pRelocation->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(std::uint16_t)); 276 | for (std::size_t i = 0u; i < m_nRelocationAmount; ++i, ++m_pRelocationType) 277 | { 278 | switch (*m_pRelocationType >> 0xC) 279 | { 280 | #if defined (_WIN64) 281 | case IMAGE_REL_BASED_DIR64: 282 | #else 283 | case IMAGE_REL_BASED_HIGHLOW: 284 | #endif 285 | *CH_R_CAST(m_TargetBase + m_pRelocation->VirtualAddress + ((*m_pRelocationType) & 0xFFF)) += m_Delta; 286 | break; 287 | } 288 | } 289 | } 290 | } 291 | 292 | typedef int(__stdcall* DllMain_fn)(std::uintptr_t, std::uint32_t, void*); 293 | DllMain_fn DllMain = CH_R_CAST(m_TargetBase + m_LoaderData->m_EntryPoint); 294 | 295 | if (!m_LoaderData->m_ImportDirVA) 296 | { 297 | // Call EP of our module. 298 | DllMain(m_TargetBase, m_LoaderData->m_Reason, CH_R_CAST(&m_LoaderData->m_CustomTransmitted)); 299 | return; 300 | } 301 | 302 | typedef HMODULE(__stdcall* LoadLibraryA_fn)(LPCSTR); 303 | LoadLibraryA_fn _LoadLibraryA = CH_R_CAST(m_LoaderData->m_LoadLibrary); 304 | 305 | typedef FARPROC(__stdcall* GetProcAddress_fn)(HMODULE, LPCSTR); 306 | GetProcAddress_fn _GetProcAddress = CH_R_CAST(m_LoaderData->m_GetProcAddress); 307 | 308 | // Fix up imports. 309 | PIMAGE_IMPORT_DESCRIPTOR m_pImports = CH_R_CAST(m_TargetBase + m_LoaderData->m_ImportDirVA); 310 | for (; m_pImports->Name; ++m_pImports) 311 | { 312 | const HMODULE m_hImportModule = _LoadLibraryA(CH_R_CAST(m_TargetBase + m_pImports->Name)); 313 | 314 | ULONG_PTR* m_pNameReference = CH_R_CAST(m_TargetBase + m_pImports->OriginalFirstThunk); 315 | ULONG_PTR* m_pThunk = CH_R_CAST(m_TargetBase + m_pImports->FirstThunk); 316 | 317 | for (; *m_pNameReference; ++m_pNameReference, ++m_pThunk) 318 | { 319 | if (IMAGE_SNAP_BY_ORDINAL(*m_pNameReference)) 320 | *CH_R_CAST(m_pThunk) = _GetProcAddress(m_hImportModule, CH_R_CAST(*m_pNameReference & 0xFFFF)); 321 | else 322 | { 323 | PIMAGE_IMPORT_BY_NAME m_pThunkData = CH_R_CAST(m_TargetBase + *m_pNameReference); 324 | *CH_R_CAST(m_pThunk) = _GetProcAddress(m_hImportModule, m_pThunkData->Name); 325 | } 326 | } 327 | } 328 | 329 | // Call EP of our module. 330 | DllMain(m_TargetBase, m_LoaderData->m_Reason, CH_R_CAST(&m_LoaderData->m_CustomTransmitted)); 331 | 332 | typedef void*(__stdcall* memset_fn)(std::uintptr_t, std::int32_t, std::size_t); 333 | memset_fn _memset = CH_R_CAST(m_LoaderData->m_Memset); 334 | 335 | if (m_LoaderData->m_eInjectionFlags & Process_t::eManualMapInjectionFlags::INJECTION_EXTRA_WIPEPEHEADERS) 336 | _memset(m_TargetBase, NULL, m_LoaderData->m_PEHeaderSize); 337 | 338 | if (m_LoaderData->m_eInjectionFlags & Process_t::eManualMapInjectionFlags::INJECTION_EXTRA_WIPEENTRYPOINT) 339 | _memset(m_TargetBase + m_LoaderData->m_EntryPoint, NULL, 0x20u); 340 | }; 341 | 342 | // Internal manual map function. 343 | bool Process_t::ManualMapInject_Internal(std::uint8_t* m_ImageBuffer, std::int32_t m_eInjectionFlags) 344 | { 345 | // Grab DOS header. 346 | const PIMAGE_DOS_HEADER m_pDosHeaders = CH_R_CAST(m_ImageBuffer); 347 | if (m_pDosHeaders->e_magic != IMAGE_DOS_SIGNATURE) 348 | { 349 | CH_LOG("Couldn't find IMAGE_DOS_SIGNATURE for m_ImageBuffer"); 350 | return false; 351 | } 352 | 353 | // Grab NT header. 354 | const PIMAGE_NT_HEADERS m_pNTHeaders = CH_R_CAST(m_ImageBuffer + m_pDosHeaders->e_lfanew); 355 | if (m_pNTHeaders->Signature != IMAGE_NT_SIGNATURE) 356 | { 357 | CH_LOG("Couldn't find IMAGE_NT_SIGNATURE for m_ImageBuffer"); 358 | return false; 359 | } 360 | 361 | // Address our module will be in context of target process. 362 | const std::uintptr_t m_TargetBaseAddress = this->Allocate(m_pNTHeaders->OptionalHeader.SizeOfImage, PAGE_EXECUTE_READWRITE, false); 363 | 364 | // Copy over PE Header to target process. 365 | if (!this->Write(m_TargetBaseAddress, m_ImageBuffer, m_pNTHeaders->OptionalHeader.SizeOfImage)) 366 | { 367 | CH_LOG("Couldn't copy over PE header data to target process."); 368 | return false; 369 | } 370 | 371 | // Copy over needed sections to target process. 372 | PIMAGE_SECTION_HEADER m_pSectionHeaders = IMAGE_FIRST_SECTION(m_pNTHeaders); 373 | for (std::size_t i = 0u; i < m_pNTHeaders->FileHeader.NumberOfSections; ++i, ++m_pSectionHeaders) 374 | { 375 | const std::uintptr_t m_Address = m_TargetBaseAddress + m_pSectionHeaders->VirtualAddress; 376 | if (!this->Write(m_Address, &m_ImageBuffer[m_pSectionHeaders->PointerToRawData], m_pSectionHeaders->SizeOfRawData)) 377 | { 378 | CH_LOG("Couldn't copy over %s section's data to target process.", CH_R_CAST(m_pSectionHeaders->Name)); 379 | return false; 380 | } 381 | } 382 | 383 | // Can use this to communicate some data to the target process. 384 | // lpReserved parameter in DllMain is unused, so popping our custom struct here is all good. 385 | TransmittedData_t m_CustomTransmittedData = {}; 386 | strcpy_s<256>(m_CustomTransmittedData.szKey, "Hello from the other side!"); 387 | 388 | // Populate loader data structure. 389 | LoaderData = { 390 | m_TargetBaseAddress, 391 | CH_S_CAST(m_pNTHeaders->OptionalHeader.ImageBase), 392 | CH_S_CAST(m_pNTHeaders->OptionalHeader.AddressOfEntryPoint), 393 | CH_R_CAST(LoadLibraryA), 394 | CH_R_CAST(GetProcAddress), 395 | CH_R_CAST(std::memset), 396 | CH_S_CAST(m_pNTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress), 397 | CH_S_CAST(m_pNTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size), 398 | CH_S_CAST(m_pNTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress), 399 | CH_S_CAST(m_pNTHeaders->OptionalHeader.SizeOfHeaders), 400 | CH_S_CAST(m_eInjectionFlags), 401 | DLL_PROCESS_ATTACH, 402 | NULL 403 | }; 404 | 405 | if (m_eInjectionFlags & eManualMapInjectionFlags::INJECTION_EXTRA_CUSTOMARGUMENTS) 406 | LoaderData.m_CustomTransmitted = m_CustomTransmittedData; 407 | 408 | // Fix up data if we've built with mismatched architecture. 409 | if (m_pNTHeaders->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) 410 | { 411 | #if defined (_WIN64) // Didn't already have NT_HEADERS32 data. 412 | const PIMAGE_NT_HEADERS32 m_NT32Temporary = CH_R_CAST(m_pNTHeaders); 413 | LoaderData.m_EntryPoint = CH_S_CAST(m_NT32Temporary->OptionalHeader.AddressOfEntryPoint); 414 | LoaderData.m_RelocDirVA = CH_S_CAST(m_NT32Temporary->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress); 415 | LoaderData.m_RelocDirSize = CH_S_CAST(m_NT32Temporary->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size); 416 | LoaderData.m_ImportDirVA = CH_S_CAST(m_NT32Temporary->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress); 417 | #endif 418 | } 419 | else if (m_pNTHeaders->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) 420 | { 421 | #if defined (_WIN32) // Didn't already have NT_HEADERS64 data. 422 | const PIMAGE_NT_HEADERS64 m_NT64Temporary = CH_R_CAST(m_pNTHeaders); 423 | LoaderData.m_EntryPoint = CH_S_CAST(m_NT64Temporary->OptionalHeader.AddressOfEntryPoint); 424 | LoaderData.m_RelocDirVA = CH_S_CAST(m_NT64Temporary->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress); 425 | LoaderData.m_RelocDirSize = CH_S_CAST(m_NT64Temporary->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size); 426 | LoaderData.m_ImportDirVA = CH_S_CAST(m_NT64Temporary->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress); 427 | #endif 428 | } 429 | 430 | // Address our loader data will be in context of target process. 431 | const std::uintptr_t m_LoaderDataAddress = this->Allocate(sizeof(LoaderData_t), PAGE_EXECUTE_READWRITE, false); 432 | 433 | // Copy over loader data to target process. 434 | if (!this->Write(m_LoaderDataAddress, &LoaderData, sizeof(LoaderData_t))) 435 | { 436 | CH_LOG("Couldn't copy over loader data to target process."); 437 | return false; 438 | } 439 | 440 | if (m_eInjectionFlags & eManualMapInjectionFlags::INJECTION_MODE_THREADHIJACK) 441 | // Hijack an existing thread in target process to execute our shellcode. 442 | { 443 | } 444 | else 445 | // Simply CreateRemoteThread in target process to execute our shellcode. 446 | { 447 | // Address our shellcode will be in context of target process. 448 | const std::uintptr_t m_ShellcodeAddress = this->Allocate(4096, PAGE_EXECUTE_READWRITE, false); 449 | 450 | // Copy over shellcode to target process. 451 | if (!this->Write(m_ShellcodeAddress, Shellcode, 4096)) 452 | { 453 | CH_LOG("Couldn't copy over loader shellcode to target process."); 454 | return false; 455 | } 456 | 457 | const std::int32_t m_nThreadResult = this->_CreateRemoteThread(CH_R_CAST(m_ShellcodeAddress), CH_R_CAST(m_LoaderDataAddress)); 458 | if (m_nThreadResult <= 0) 459 | { 460 | CH_LOG("Failed to create remote thread in target process with code %i.", m_nThreadResult); 461 | return false; 462 | } 463 | } 464 | 465 | return true; 466 | } 467 | 468 | // Manual map injection from module on disk. 469 | bool Process_t::ManualMapInject(const char* m_szDLLPath, std::int32_t m_eInjectionFlags) 470 | { 471 | ByteArray_t m_FileImageBuffer = { 0 }; 472 | 473 | // Fill local image buffer from file on disk. 474 | std::ifstream m_fFile(m_szDLLPath, std::ios::binary); 475 | (&m_FileImageBuffer)->assign((std::istreambuf_iterator(m_fFile)), std::istreambuf_iterator()); 476 | m_fFile.close(); 477 | 478 | if (!m_FileImageBuffer.size()) 479 | { 480 | CH_LOG("Couldn't parse desired m_ImageBuffer to manual map."); 481 | return false; 482 | } 483 | 484 | return ManualMapInject_Internal(m_FileImageBuffer.data(), m_eInjectionFlags); 485 | } 486 | 487 | // Manual map injection from module in memory. 488 | bool Process_t::ManualMapInject(std::uint8_t* m_ImageBuffer, std::int32_t m_eInjectionFlags) 489 | { 490 | return ManualMapInject_Internal(m_ImageBuffer, m_eInjectionFlags); 491 | } 492 | 493 | // Manual map injection from ImageFile_t. 494 | bool Process_t::ManualMapInject(ImageFile_t& m_ImageFile, std::int32_t m_eInjectionFlags) 495 | { 496 | if (!m_ImageFile.m_ImageBuffer.size()) 497 | { 498 | CH_LOG("Couldn't parse desired ImageFile_t to manual map."); 499 | return false; 500 | } 501 | 502 | return ManualMapInject_Internal(m_ImageFile.m_ImageBuffer.data(), m_eInjectionFlags); 503 | } 504 | 505 | // LoadLibrary injection from module on disk. 506 | bool Process_t::LoadLibraryInject(const char* m_szDLLPath) 507 | { 508 | // Allocate memory in target process. 509 | const std::uintptr_t m_AllocatedMemory = this->Allocate(strlen(m_szDLLPath), PAGE_READWRITE); 510 | if (!m_AllocatedMemory) 511 | { 512 | CH_LOG("Couldn't allocate memory to target process. Error code was #%i", GetLastError()); 513 | return false; 514 | } 515 | 516 | // Write string name for our module in previously allocated space. 517 | const std::size_t m_nWrittenBytes = this->Write(m_AllocatedMemory, CH_C_CAST(m_szDLLPath)); 518 | if (!m_nWrittenBytes) 519 | { 520 | CH_LOG("Couldn't write module name to target process. Error code was #%i", GetLastError()); 521 | return false; 522 | } 523 | 524 | // Load DLL by invoking LoadLibrary(m_szDLLPath) in a target process 525 | const std::int32_t m_nThreadResult = this->_CreateRemoteThread(CH_R_CAST(LoadLibraryA), CH_R_CAST(m_AllocatedMemory)); 526 | if (m_nThreadResult <= 0) 527 | { 528 | CH_LOG("Failed to create remote thread in target process with code %i.", m_nThreadResult); 529 | return false; 530 | } 531 | 532 | return true; 533 | } 534 | 535 | // Traverse and cache data about all threads in a target process. 536 | std::vector Process_t::EnumerateThreads() 537 | { 538 | std::vector m_EnumeratedThreads = {}; 539 | 540 | HANDLE m_hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, this->m_nTargetProcessID); 541 | Thread_t::NtQueryInformationThread_fn NtQueryInformationThread = 542 | CH_R_CAST(GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtQueryInformationThread")); 543 | 544 | THREADENTRY32 mEntry = { 0 }; 545 | mEntry.dwSize = sizeof(mEntry); 546 | 547 | if (!Thread32First(m_hSnapShot, &mEntry)) 548 | return {}; 549 | 550 | while (Thread32Next(m_hSnapShot, &mEntry)) 551 | { 552 | // Ensure our target process owns this thread. 553 | if (mEntry.th32OwnerProcessID != this->m_nTargetProcessID) 554 | continue; 555 | 556 | // Open handle to this specific thread. 557 | HANDLE m_hThreadHandle = OpenThread(THREAD_ALL_ACCESS, FALSE, mEntry.th32ThreadID); 558 | if (!m_hThreadHandle || m_hThreadHandle == INVALID_HANDLE_VALUE) 559 | continue; 560 | 561 | ADD_SCOPE_HANDLER(CloseHandle, m_hThreadHandle); 562 | 563 | DWORD m_dThreadStartAddress = 0; 564 | if (NtQueryInformationThread(m_hThreadHandle, 565 | Thread_t::THREADINFOCLASS::ThreadQuerySetWin32StartAddress, 566 | &m_dThreadStartAddress, 567 | this->m_eProcessArchitecture == eProcessArchitecture::ARCHITECTURE_x64 ? sizeof(DWORD) * 2 : sizeof(DWORD), 568 | nullptr) != 0x00000000/*STATUS_SUCCESS*/) 569 | continue; 570 | 571 | const bool m_bIsThreadSuspended = WaitForSingleObject(m_hThreadHandle, 0) == WAIT_ABANDONED; 572 | 573 | m_EnumeratedThreads.push_back( 574 | { mEntry.th32ThreadID, 575 | m_dThreadStartAddress, 576 | m_bIsThreadSuspended } 577 | ); 578 | } 579 | 580 | CloseHandle(m_hSnapShot); 581 | 582 | return m_EnumeratedThreads; 583 | } 584 | 585 | // Traverse and cache data about all loaded modules in a target process. 586 | std::vector Process_t::EnumerateModules(bool m_bUseCachedData) 587 | { 588 | // To take up less processing power, or if you know nothing will be loaded into the target process unexpectedly. 589 | if (m_bUseCachedData && this->m_bHasCachedProcessesModules) 590 | { 591 | if (!this->m_EnumeratedModulesCached.empty()) 592 | return this->m_EnumeratedModulesCached; 593 | } 594 | 595 | if (!this->m_EnumeratedModulesCached.empty()) 596 | // Wipe any previously cached data. 597 | this->m_EnumeratedModulesCached.clear(); 598 | 599 | HANDLE m_hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, this->m_nTargetProcessID); 600 | 601 | MODULEENTRY32 mEntry = { 0 }; 602 | mEntry.dwSize = sizeof(mEntry); 603 | 604 | while (Module32Next(m_hSnapShot, &mEntry)) 605 | { 606 | wchar_t m_wszModPath[MAX_PATH]; 607 | if (!GetModuleFileNameEx(this->m_hTargetProcessHandle, mEntry.hModule, m_wszModPath, sizeof(m_wszModPath) / sizeof(wchar_t))) 608 | continue; 609 | 610 | m_wszModPath[MAX_PATH - 1] = '\0'; 611 | 612 | // Convert wstring->string. 613 | _bstr_t m_bszPreModulePath(m_wszModPath); 614 | _bstr_t m_bszPreModuleName(mEntry.szModule); 615 | 616 | std::string m_szModulePath(m_bszPreModulePath); 617 | std::string m_szModuleName(m_bszPreModuleName); 618 | 619 | this->m_EnumeratedModulesCached.push_back({ 620 | m_szModuleName, m_szModulePath, 621 | CH_S_CAST(mEntry.modBaseSize), 622 | CH_R_CAST(mEntry.modBaseAddr) } 623 | ); 624 | } 625 | 626 | CloseHandle(m_hSnapShot); 627 | 628 | return this->m_EnumeratedModulesCached; 629 | } 630 | 631 | // Sets debug privileges of a target process. 632 | bool Process_t::SetDebugPrivilege(bool m_bShouldEnable) 633 | { 634 | HANDLE m_hToken; 635 | if (!OpenProcessToken(this->m_hTargetProcessHandle, TOKEN_ALL_ACCESS, &m_hToken)) 636 | return false; 637 | 638 | ADD_SCOPE_HANDLER(CloseHandle, m_hToken); 639 | 640 | LUID m_LUID; 641 | if (!LookupPrivilegeValue(NULL, L"SeDebugPrivilege", &m_LUID)) 642 | return false; 643 | 644 | TOKEN_PRIVILEGES m_TokenPrivileges; 645 | m_TokenPrivileges.PrivilegeCount = 1; 646 | m_TokenPrivileges.Privileges[0].Luid = m_LUID; 647 | m_TokenPrivileges.Privileges[0].Attributes = m_bShouldEnable ? SE_PRIVILEGE_ENABLED : 0; 648 | 649 | const bool result = 650 | AdjustTokenPrivileges(m_hToken, false, &m_TokenPrivileges, sizeof(m_TokenPrivileges), NULL, NULL) 651 | && GetLastError() != ERROR_NOT_ALL_ASSIGNED; 652 | 653 | return result; 654 | } 655 | 656 | // Suspend every thread in a target process. 657 | void Process_t::Suspend() 658 | { 659 | NtSuspendProcess_fn NtSuspendProcess = CH_R_CAST(GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtSuspendProcess")); 660 | this->m_bIsProcessManuallySuspended = NtSuspendProcess(this->m_hTargetProcessHandle) == 0x00000000/*STATUS_SUCCESS*/; 661 | 662 | CH_ASSERT(false, this->m_bIsProcessManuallySuspended, "Failed to suspend process!"); 663 | } 664 | 665 | // Resume every previously suspended thread in a target process. 666 | void Process_t::Resume() 667 | { 668 | // TODO: Is there any use case of resuming a suspended process (that WE didn't suspend??). 669 | CH_ASSERT(true, this->m_bIsProcessManuallySuspended, "Attempted to resume process that was never suspended!"); 670 | 671 | NtResumeProcess_fn NtResumeProcess = CH_R_CAST(GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtResumeProcess")); 672 | 673 | this->m_bIsProcessManuallySuspended = NtResumeProcess(this->m_hTargetProcessHandle) != 0x00000000/*STATUS_SUCCESS*/; 674 | CH_ASSERT(false, this->m_bIsProcessManuallySuspended == false, "Failed to resume suspended process!"); 675 | } 676 | 677 | // Get desired export address by name. 678 | std::uintptr_t Process_t::GetRemoteProcAddress(const char* m_szModuleName, const char* m_szExportName) 679 | { 680 | chdr::Module_t m_Module(*this, 681 | m_szModuleName, 682 | PEHeaderData_t::PEHEADER_PARSING_TYPE::TYPE_EXPORT_DIRECTORY // Only parse exports. 683 | ); 684 | 685 | if (!m_Module.IsValid() || !m_Module.GetPEHeaderData().IsValid()) 686 | return 0u; 687 | 688 | auto ExportData = m_Module.GetPEHeaderData().GetExportData(); 689 | if (ExportData.empty() || ExportData.find(m_szExportName) == ExportData.end()) 690 | // Ensure we even have this export in our map. 691 | return 0u; 692 | 693 | return ExportData[m_szExportName].m_nAddress; 694 | } 695 | 696 | // VirtualAllocEx implementation. 697 | std::uintptr_t Process_t::Allocate(std::size_t m_AllocationSize, DWORD m_dProtectionType, bool m_bShouldTrack) 698 | { 699 | const std::uintptr_t m_AllocatedMemory = CH_R_CAST(VirtualAllocEx(this->m_hTargetProcessHandle, nullptr, m_AllocationSize, MEM_COMMIT | MEM_RESERVE, m_dProtectionType)); 700 | if (m_AllocatedMemory && m_bShouldTrack) 701 | this->m_AllocatedMemoryTracker.insert({ m_AllocatedMemory, m_AllocationSize }); 702 | 703 | return m_AllocatedMemory; 704 | } 705 | 706 | // VirtualFreeEx implementation. 707 | bool Process_t::Free(std::uintptr_t m_FreeAddress) 708 | { 709 | const bool m_bDidFree = VirtualFreeEx(this->m_hTargetProcessHandle, (LPVOID)m_FreeAddress, NULL, MEM_RELEASE); 710 | if (m_bDidFree && this->m_AllocatedMemoryTracker.find(m_FreeAddress) != this->m_AllocatedMemoryTracker.end()) 711 | this->m_AllocatedMemoryTracker.erase(this->m_AllocatedMemoryTracker.find(m_FreeAddress)); 712 | 713 | return m_bDidFree; 714 | } 715 | 716 | // VirtualQueryEx implementation. 717 | std::size_t Process_t::Query(LPCVOID m_QueryAddress, MEMORY_BASIC_INFORMATION* m_MemoryInformation) 718 | { 719 | return VirtualQueryEx(this->m_hTargetProcessHandle, m_QueryAddress, m_MemoryInformation, sizeof(MEMORY_BASIC_INFORMATION)); 720 | } 721 | 722 | // CreateRemoteThread implementation. 723 | std::int32_t Process_t::_CreateRemoteThread(LPVOID m_lpStartAddress, LPVOID m_lpParameter) 724 | { 725 | HANDLE m_hRemoteThread = CreateRemoteThread(this->m_hTargetProcessHandle, 726 | NULL, 727 | NULL, 728 | CH_R_CAST(m_lpStartAddress), 729 | m_lpParameter, 730 | NULL, 731 | NULL 732 | ); 733 | 734 | if (!m_hRemoteThread || m_hRemoteThread == INVALID_HANDLE_VALUE) 735 | return 0; 736 | 737 | ADD_SCOPE_HANDLER(CloseHandle, m_hRemoteThread); 738 | 739 | if (WaitForSingleObject(m_hRemoteThread, INFINITE) == WAIT_FAILED) 740 | return -1; 741 | 742 | return 1; 743 | } 744 | 745 | // GetModule implementation. 746 | Module_t& Process_t::GetModule(const char* m_szModuleName, std::int32_t m_ParseType) 747 | { 748 | if (!IsValid()) 749 | CH_LOG("Invalid process called GetModule : %s", m_szProcessName); 750 | 751 | if (m_AllocatedModules.count(m_szModuleName) > 0u) 752 | return m_AllocatedModules[m_szModuleName]; 753 | 754 | Module_t m_Module(*this, m_szModuleName, m_ParseType); 755 | m_AllocatedModules[m_szModuleName] = m_Module; 756 | 757 | return m_AllocatedModules[m_szModuleName]; 758 | } 759 | } 760 | 761 | // PEHeaderData_t definitions and functions. 762 | namespace chdr 763 | { 764 | // Parsing data out of this image's buffer. 765 | PEHeaderData_t::PEHeaderData_t(std::uint8_t* m_ImageBuffer, std::size_t m_ImageSize, std::int32_t m_ParseType) 766 | { 767 | if (m_ParseType & PEHEADER_PARSING_TYPE::TYPE_NONE) 768 | return; 769 | 770 | CH_ASSERT(true, m_ImageBuffer && m_ImageSize, "Failed to read PE image."); 771 | 772 | this->m_pDOSHeaders = CH_R_CAST(m_ImageBuffer); 773 | this->m_pNTHeaders = CH_R_CAST(m_ImageBuffer + this->m_pDOSHeaders->e_lfanew); 774 | 775 | this->m_bIsValidInternal = 776 | this->m_pDOSHeaders->e_magic == IMAGE_DOS_SIGNATURE && 777 | this->m_pNTHeaders->Signature == IMAGE_NT_SIGNATURE; 778 | 779 | // Ensure image PE headers was valid. 780 | CH_ASSERT(true, this->IsValid(), "Couldn't find MZ&NT header."); 781 | 782 | PIMAGE_SECTION_HEADER m_pSectionHeaders = IMAGE_FIRST_SECTION(this->m_pNTHeaders); 783 | for (std::size_t i = 0u; i < m_pNTHeaders->FileHeader.NumberOfSections; ++i, ++m_pSectionHeaders) 784 | { 785 | this->m_SectionData.push_back( 786 | { CH_R_CAST(m_pSectionHeaders->Name), 787 | m_pSectionHeaders->VirtualAddress, 788 | m_pSectionHeaders->Misc.VirtualSize, 789 | m_pSectionHeaders->Characteristics, 790 | m_pSectionHeaders->PointerToRawData, 791 | m_pSectionHeaders->PointerToRawData } 792 | ); 793 | } 794 | 795 | for (std::size_t i = 0u; i < IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR/*Maximum*/; ++i) 796 | this->m_DirectoryData.push_back(this->m_pNTHeaders->OptionalHeader.DataDirectory[i]); 797 | 798 | // Gather all needed directories, then ensure we're using the correct type for the image. 799 | IMAGE_DATA_DIRECTORY m_ExportDataDirectory = this->GetDataDirectory(IMAGE_DIRECTORY_ENTRY_EXPORT); 800 | IMAGE_DATA_DIRECTORY m_ImportDataDirectory = this->GetDataDirectory(IMAGE_DIRECTORY_ENTRY_IMPORT); 801 | IMAGE_DATA_DIRECTORY m_DebugDataDirectory = this->GetDataDirectory(IMAGE_DIRECTORY_ENTRY_DEBUG); 802 | 803 | if (this->m_pNTHeaders->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) 804 | { 805 | #if defined (_WIN64) // Didn't already have NT_HEADERS32 data. 806 | const PIMAGE_NT_HEADERS32 m_NT32Temporary = CH_R_CAST(this->m_pNTHeaders); 807 | m_ExportDataDirectory = m_NT32Temporary->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]; 808 | m_ImportDataDirectory = m_NT32Temporary->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]; 809 | m_DebugDataDirectory = m_NT32Temporary->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG]; 810 | 811 | if (!this->m_DirectoryData.empty()) 812 | // Reset directory data to be filled with one of correct architecture. 813 | this->m_DirectoryData.clear(); 814 | 815 | for (std::size_t i = 0u; i < IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR/*Maximum*/; ++i) 816 | this->m_DirectoryData.push_back(m_NT32Temporary->OptionalHeader.DataDirectory[i]); 817 | #endif 818 | } 819 | else if (this->m_pNTHeaders->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) 820 | { 821 | #if defined (_WIN32) // Didn't already have NT_HEADERS64 data. 822 | const PIMAGE_NT_HEADERS64 m_NT64Temporary = CH_R_CAST(this->m_pNTHeaders); 823 | m_ExportDataDirectory = m_NT64Temporary->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]; 824 | m_ImportDataDirectory = m_NT64Temporary->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]; 825 | m_DebugDataDirectory = m_NT64Temporary->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG]; 826 | 827 | if (!this->m_DirectoryData.empty()) 828 | // Reset directory data to be filled with one of correct architecture. 829 | this->m_DirectoryData.clear(); 830 | 831 | for (std::size_t i = 0u; i < IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR/*Maximum*/; ++i) 832 | this->m_DirectoryData.push_back(m_NT64Temporary->OptionalHeader.DataDirectory[i]); 833 | #endif 834 | } 835 | 836 | const bool bDebugEnabled = m_ParseType & PEHEADER_PARSING_TYPE::TYPE_DEBUG_DIRECTORY || m_ParseType & PEHEADER_PARSING_TYPE::TYPE_ALL; 837 | if (!bDebugEnabled || 838 | !m_DebugDataDirectory.VirtualAddress || 839 | !m_DebugDataDirectory.Size) 840 | { 841 | if (bDebugEnabled) 842 | CH_LOG("Debug table didn't exist for current region. 0x%x | 0X%x", m_DebugDataDirectory.VirtualAddress, m_DebugDataDirectory.Size); 843 | } 844 | else // Debug table parsing. 845 | { 846 | const PIMAGE_DEBUG_DIRECTORY m_DebugDirectoryData = CH_R_CAST(m_ImageBuffer + this->RvaToOffset(m_DebugDataDirectory.VirtualAddress)); 847 | if (m_DebugDirectoryData->Type == IMAGE_DEBUG_TYPE_CODEVIEW) 848 | { 849 | const CV_INFO_PDB70* m_PDBInfo = CH_R_CAST(m_ImageBuffer + this->RvaToOffset(m_DebugDirectoryData->AddressOfRawData)); 850 | 851 | wchar_t m_wszGUIDStr[MAX_PATH]; 852 | if (!StringFromGUID2(m_PDBInfo->Signature, m_wszGUIDStr, sizeof(m_wszGUIDStr))) 853 | // Couldn't read GUID, whatever lol. 854 | this->m_DebugData = { (char*)m_PDBInfo->PdbFileName, "", m_PDBInfo->Age, m_PDBInfo->CvSignature }; 855 | else 856 | { 857 | m_wszGUIDStr[MAX_PATH - 1] = '\0'; 858 | 859 | // wchar_t->string 860 | _bstr_t m_szPreGUIDStr(m_wszGUIDStr); 861 | 862 | this->m_DebugData = { 863 | (char*)m_PDBInfo->PdbFileName, std::string(m_szPreGUIDStr), 864 | m_PDBInfo->Age, m_PDBInfo->CvSignature 865 | }; 866 | } 867 | } 868 | } 869 | 870 | const bool bExportsEnabled = m_ParseType & PEHEADER_PARSING_TYPE::TYPE_EXPORT_DIRECTORY || m_ParseType & PEHEADER_PARSING_TYPE::TYPE_ALL; 871 | if (!bExportsEnabled || 872 | !m_ExportDataDirectory.VirtualAddress || 873 | !m_ExportDataDirectory.Size) 874 | { 875 | if (bExportsEnabled) 876 | CH_LOG("Export table didn't exist for current region. 0x%x | 0X%x", m_ExportDataDirectory.VirtualAddress, m_ExportDataDirectory.Size); 877 | } 878 | else // Export table parsing. 879 | { 880 | const PIMAGE_EXPORT_DIRECTORY m_pExportDirectory = CH_R_CAST(m_ImageBuffer + this->RvaToOffset(m_ExportDataDirectory.VirtualAddress)); 881 | 882 | const std::uint16_t* m_pOrdinalAddress = CH_R_CAST(m_ImageBuffer + this->RvaToOffset(m_pExportDirectory->AddressOfNameOrdinals)); 883 | const std::uint32_t* m_pNamesAddress = CH_R_CAST(m_ImageBuffer + this->RvaToOffset(m_pExportDirectory->AddressOfNames)); 884 | const std::uint32_t* m_pFunctionAddress = CH_R_CAST(m_ImageBuffer + this->RvaToOffset(m_pExportDirectory->AddressOfFunctions)); 885 | 886 | // Traverse export table and cache desired data. 887 | for (std::size_t i = 0u; i < m_pExportDirectory->NumberOfNames; ++i) 888 | { 889 | const std::uint16_t m_CurrentOrdinal = m_pOrdinalAddress[i]; 890 | const std::uint32_t m_CurrentName = m_pNamesAddress[i]; 891 | 892 | if (m_CurrentName == 0u || m_CurrentOrdinal > m_pExportDirectory->NumberOfNames) 893 | // Happend a few times, dunno. 894 | continue; 895 | 896 | char* m_szExportName = CH_R_CAST(m_ImageBuffer + this->RvaToOffset(m_CurrentName)); 897 | this->m_ExportData[m_szExportName] = { m_pFunctionAddress[m_CurrentOrdinal], m_CurrentOrdinal }; 898 | } 899 | } 900 | 901 | const bool bImportsEnabled = m_ParseType & PEHEADER_PARSING_TYPE::TYPE_IMPORT_DIRECTORY || m_ParseType & PEHEADER_PARSING_TYPE::TYPE_ALL; 902 | if (!bImportsEnabled || 903 | !m_ImportDataDirectory.VirtualAddress || 904 | !m_ImportDataDirectory.Size) 905 | { 906 | if (bImportsEnabled) 907 | CH_LOG("Import table didn't exist for current region. 0x%X | 0x%X", 908 | m_ImportDataDirectory.VirtualAddress, m_ImportDataDirectory.Size); 909 | } 910 | else // Import table parsing. 911 | { 912 | PIMAGE_IMPORT_DESCRIPTOR m_pImportDescriptor = CH_R_CAST(m_ImageBuffer + this->RvaToOffset(m_ImportDataDirectory.VirtualAddress)); 913 | 914 | for (; m_pImportDescriptor->Name; ++m_pImportDescriptor) 915 | { 916 | // Read module name. 917 | char* m_szModuleName = CH_R_CAST(m_ImageBuffer + this->RvaToOffset(m_pImportDescriptor->Name)); 918 | 919 | PIMAGE_THUNK_DATA m_pThunkData = CH_R_CAST(m_ImageBuffer + this->RvaToOffset(m_pImportDescriptor->OriginalFirstThunk)); 920 | 921 | for (; m_pThunkData->u1.AddressOfData; ++m_pThunkData) 922 | { 923 | if (m_pThunkData->u1.Ordinal & IMAGE_ORDINAL_FLAG32) 924 | // TODO: Imports by ordinal, dunno how I will make this nice. 925 | continue; 926 | 927 | // Read function name. 928 | char* m_szFunctionName = CH_R_CAST(m_ImageBuffer + this->RvaToOffset(std::uint32_t(m_pThunkData->u1.AddressOfData + 2))); 929 | 930 | // Cache desired data. 931 | this->m_ImportData[m_szFunctionName] = { m_szModuleName }; 932 | } 933 | } 934 | } 935 | } 936 | 937 | // Parsing data out of this image's process. 938 | PEHeaderData_t::PEHeaderData_t(Process_t& m_Process, std::int32_t m_ParseType, std::uintptr_t m_CustomBaseAddress) 939 | { 940 | if (m_ParseType & PEHEADER_PARSING_TYPE::TYPE_NONE) 941 | return; 942 | 943 | const std::uintptr_t m_BaseAddress = m_CustomBaseAddress != 0u ? m_CustomBaseAddress : m_Process.GetBaseAddress(); 944 | CH_ASSERT(true, m_BaseAddress, "Couldn't find base address of target process."); 945 | 946 | IMAGE_DOS_HEADER m_pDOSHeadersTemporary = m_Process.Read(m_BaseAddress); 947 | this->m_pDOSHeaders = &m_pDOSHeadersTemporary; 948 | 949 | IMAGE_NT_HEADERS m_pNTHeadersTemporary = m_Process.Read(m_BaseAddress + this->m_pDOSHeaders->e_lfanew); 950 | this->m_pNTHeaders = &m_pNTHeadersTemporary; 951 | 952 | this->m_bIsValidInternal = 953 | this->m_pDOSHeaders->e_magic == IMAGE_DOS_SIGNATURE && 954 | this->m_pNTHeaders->Signature == IMAGE_NT_SIGNATURE; 955 | 956 | // Ensure image PE headers was valid. 957 | CH_ASSERT(true, this->IsValid(), "Couldn't find MZ&NT header."); 958 | 959 | for (std::size_t i = 0u; i < m_pNTHeaders->FileHeader.NumberOfSections; ++i) 960 | { 961 | std::size_t m_nSectionOffset = sizeof(IMAGE_NT_HEADERS) + (i * sizeof(IMAGE_SECTION_HEADER)); 962 | IMAGE_SECTION_HEADER m_pSectionHeaders = m_Process.Read(m_BaseAddress + this->m_pDOSHeaders->e_lfanew + m_nSectionOffset); 963 | 964 | this->m_SectionData.push_back( 965 | { CH_R_CAST(m_pSectionHeaders.Name), 966 | m_pSectionHeaders.VirtualAddress, 967 | m_pSectionHeaders.Misc.VirtualSize, 968 | m_pSectionHeaders.Characteristics, 969 | m_pSectionHeaders.PointerToRawData, 970 | m_pSectionHeaders.SizeOfRawData } 971 | ); 972 | } 973 | 974 | for (std::size_t i = 0u; i < IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR/*Maximum*/; ++i) 975 | this->m_DirectoryData.push_back(this->m_pNTHeaders->OptionalHeader.DataDirectory[i]); 976 | 977 | // Gather all needed directories, then ensure we're using the correct type for the image. 978 | IMAGE_DATA_DIRECTORY m_ExportDataDirectory = this->GetDataDirectory(IMAGE_DIRECTORY_ENTRY_EXPORT); 979 | IMAGE_DATA_DIRECTORY m_ImportDataDirectory = this->GetDataDirectory(IMAGE_DIRECTORY_ENTRY_IMPORT); 980 | IMAGE_DATA_DIRECTORY m_DebugDataDirectory = this->GetDataDirectory(IMAGE_DIRECTORY_ENTRY_DEBUG); 981 | 982 | if (this->m_pNTHeaders->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) 983 | { 984 | #if defined (_WIN64) // Didn't already have NT_HEADERS32 data. 985 | const PIMAGE_NT_HEADERS32 m_NT32Temporary = CH_R_CAST(this->m_pNTHeaders); 986 | m_ExportDataDirectory = m_NT32Temporary->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]; 987 | m_ImportDataDirectory = m_NT32Temporary->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]; 988 | m_DebugDataDirectory = m_NT32Temporary->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG]; 989 | this->m_nMismatchedArchitecture = CH_S_CAST(Process_t::eProcessArchitecture::ARCHITECTURE_x64); // For future notifications of dangerous actions. 990 | #endif 991 | } 992 | else if (this->m_pNTHeaders->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) 993 | { 994 | #if defined (_WIN32) // Didn't already have NT_HEADERS64 data. 995 | const PIMAGE_NT_HEADERS64 m_NT64Temporary = CH_R_CAST(this->m_pNTHeaders); 996 | m_ExportDataDirectory = m_NT64Temporary->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]; 997 | m_ImportDataDirectory = m_NT64Temporary->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]; 998 | m_DebugDataDirectory = m_NT64Temporary->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG]; 999 | this->m_nMismatchedArchitecture = CH_S_CAST(Process_t::eProcessArchitecture::ARCHITECTURE_x64); // For future notifications of dangerous actions. 1000 | #endif 1001 | } 1002 | 1003 | const bool bDebugEnabled = m_ParseType & PEHEADER_PARSING_TYPE::TYPE_DEBUG_DIRECTORY || m_ParseType & PEHEADER_PARSING_TYPE::TYPE_ALL; 1004 | if (!bDebugEnabled || 1005 | !m_DebugDataDirectory.VirtualAddress || 1006 | !m_DebugDataDirectory.Size) 1007 | { 1008 | if (bDebugEnabled) 1009 | CH_LOG("Debug table didn't exist for current region. 0x%x | 0X%x", m_DebugDataDirectory.VirtualAddress, m_DebugDataDirectory.Size); 1010 | } 1011 | else // Debug table parsing. 1012 | { 1013 | const IMAGE_DEBUG_DIRECTORY m_DebugDirectoryData = m_Process.Read(m_BaseAddress + m_DebugDataDirectory.VirtualAddress); 1014 | if (m_DebugDirectoryData.Type == IMAGE_DEBUG_TYPE_CODEVIEW) 1015 | { 1016 | const CV_INFO_PDB70 m_PDBInfo = m_Process.Read(m_BaseAddress + m_DebugDirectoryData.AddressOfRawData); 1017 | 1018 | wchar_t m_wszGUIDStr[MAX_PATH]; 1019 | if (!StringFromGUID2(m_PDBInfo.Signature, m_wszGUIDStr, sizeof(m_wszGUIDStr))) 1020 | // Couldn't read GUID, whatever lol. 1021 | this->m_DebugData = { (char*)m_PDBInfo.PdbFileName, "", m_PDBInfo.Age, m_PDBInfo.CvSignature }; 1022 | else 1023 | { 1024 | m_wszGUIDStr[MAX_PATH - 1] = '\0'; 1025 | 1026 | // wchar_t->string 1027 | _bstr_t m_szPreGUIDStr(m_wszGUIDStr); 1028 | 1029 | this->m_DebugData = { 1030 | (char*)m_PDBInfo.PdbFileName, std::string(m_szPreGUIDStr), 1031 | m_PDBInfo.Age, m_PDBInfo.CvSignature 1032 | }; 1033 | } 1034 | } 1035 | } 1036 | 1037 | const bool bExportsEnabled = m_ParseType & PEHEADER_PARSING_TYPE::TYPE_EXPORT_DIRECTORY || m_ParseType & PEHEADER_PARSING_TYPE::TYPE_ALL; 1038 | if (!bExportsEnabled || 1039 | !m_ExportDataDirectory.VirtualAddress || 1040 | !m_ExportDataDirectory.Size) 1041 | { 1042 | if (bExportsEnabled) 1043 | CH_LOG("Export table didn't exist for current region. 0x%x | 0X%x", 1044 | m_ExportDataDirectory.VirtualAddress, m_ExportDataDirectory.Size); 1045 | } 1046 | else // Export table parsing. 1047 | { 1048 | IMAGE_EXPORT_DIRECTORY m_pExportDirectory = m_Process.Read(m_BaseAddress + (uintptr_t)m_ExportDataDirectory.VirtualAddress); 1049 | 1050 | // Read whole RVA block. 1051 | const auto m_FuncRVABlock = std::make_unique(m_pExportDirectory.NumberOfFunctions * sizeof(std::uint32_t)); 1052 | const std::size_t m_nReadRVA = m_Process.Read(m_BaseAddress + m_pExportDirectory.AddressOfFunctions, m_FuncRVABlock.get(), m_pExportDirectory.NumberOfFunctions * sizeof(std::uint32_t)); 1053 | 1054 | // Read whole name block. 1055 | const auto m_NameRVABlock = std::make_unique(m_pExportDirectory.NumberOfNames * sizeof(std::uint32_t)); 1056 | const std::size_t m_nReadName = m_Process.Read(m_BaseAddress + m_pExportDirectory.AddressOfNames, m_NameRVABlock.get(), m_pExportDirectory.NumberOfNames * sizeof(std::uint32_t)); 1057 | 1058 | // Read whole ordinal block. 1059 | const auto m_OrdinalBlock = std::make_unique(m_pExportDirectory.NumberOfNames * sizeof(std::uint16_t)); 1060 | const std::size_t m_nReadOrdinal = m_Process.Read(m_BaseAddress + m_pExportDirectory.AddressOfNameOrdinals, m_OrdinalBlock.get(), m_pExportDirectory.NumberOfNames * sizeof(std::uint16_t)); 1061 | 1062 | // Ye. 1063 | const std::uint32_t* m_pFuncBlock = m_FuncRVABlock.get(); 1064 | const std::uint32_t* m_pNameBlock = m_NameRVABlock.get(); 1065 | const std::uint16_t* m_pOrdinalBlock = m_OrdinalBlock.get(); 1066 | 1067 | // Traverse export table and cache desired data. 1068 | for (std::size_t i = 0u; i < m_pExportDirectory.NumberOfNames; ++i) 1069 | { 1070 | if (m_nReadRVA == 0u || m_nReadName == 0u || m_nReadOrdinal == 0u) 1071 | // One or more read failed, no point to waste time here. 1072 | break; 1073 | 1074 | const std::uint16_t m_CurrentOrdinal = m_pOrdinalBlock[i]; 1075 | const std::uint32_t m_CurrentName = m_pNameBlock[i]; 1076 | 1077 | if (m_CurrentName == 0u || m_CurrentOrdinal > m_pExportDirectory.NumberOfNames) 1078 | // Happend a few times, dunno. 1079 | continue; 1080 | 1081 | // Read export name. 1082 | char m_szExportName[MAX_PATH]; 1083 | const std::size_t m_ReadNameBytes = m_Process.Read(m_BaseAddress + m_CurrentName, m_szExportName, sizeof(m_szExportName)); 1084 | 1085 | if (m_ReadNameBytes == 0u) 1086 | // Something went wrong while reading exp name, don't cache this. 1087 | continue; 1088 | 1089 | m_szExportName[MAX_PATH - 1] = '\0'; 1090 | 1091 | // Cache desired data. 1092 | this->m_ExportData[m_szExportName] = { m_pFuncBlock[m_CurrentOrdinal], m_CurrentOrdinal }; 1093 | } 1094 | } 1095 | 1096 | const bool bImportsEnabled = m_ParseType & PEHEADER_PARSING_TYPE::TYPE_IMPORT_DIRECTORY || m_ParseType & PEHEADER_PARSING_TYPE::TYPE_ALL; 1097 | if (!bImportsEnabled || 1098 | !m_ImportDataDirectory.VirtualAddress || 1099 | !m_ImportDataDirectory.Size) 1100 | { 1101 | if (bImportsEnabled) 1102 | CH_LOG("Import table didn't exist for current region. 0x%X | 0x%X", 1103 | m_ImportDataDirectory.VirtualAddress, m_ImportDataDirectory.Size); 1104 | } 1105 | else // Import table parsing. 1106 | { 1107 | // Read whole descriptor block. 1108 | const auto m_ImpDescriptorBlock = std::make_unique(m_ImportDataDirectory.Size); 1109 | m_Process.Read(m_BaseAddress + m_ImportDataDirectory.VirtualAddress, m_ImpDescriptorBlock.get(), m_ImportDataDirectory.Size); 1110 | 1111 | for (std::size_t i = 0u; ; ++i) 1112 | { 1113 | const IMAGE_IMPORT_DESCRIPTOR m_pImportDescriptor = CH_R_CAST(m_ImpDescriptorBlock.get())[i]; 1114 | if (!m_pImportDescriptor.Name) 1115 | break; 1116 | 1117 | // Read module name. 1118 | char m_szModuleName[MAX_PATH]; 1119 | const std::size_t m_nReadModuleName = m_Process.Read(m_BaseAddress + m_pImportDescriptor.Name, m_szModuleName, sizeof(m_szModuleName)); 1120 | 1121 | if (m_nReadModuleName == 0u) 1122 | // Something went wrong while reading imp module, don't cache this. 1123 | continue; 1124 | 1125 | m_szModuleName[MAX_PATH - 1] = '\0'; 1126 | 1127 | for (std::size_t n = m_pImportDescriptor.OriginalFirstThunk; ; n += sizeof(IMAGE_THUNK_DATA32)) 1128 | { 1129 | IMAGE_THUNK_DATA m_pThunkData = m_Process.Read(m_BaseAddress + n); 1130 | if (!m_pThunkData.u1.AddressOfData) 1131 | break; 1132 | 1133 | if (m_pThunkData.u1.Ordinal & IMAGE_ORDINAL_FLAG32) 1134 | // TODO: Imports by ordinal, dunno how I will make this nice. 1135 | continue; 1136 | 1137 | // Read function name. 1138 | char m_szFunctionName[MAX_PATH]; 1139 | const std::size_t m_ReadNameBytes = m_Process.Read(m_BaseAddress + std::uintptr_t(m_pThunkData.u1.AddressOfData) + 2, m_szFunctionName, sizeof(m_szFunctionName)); 1140 | 1141 | if (m_ReadNameBytes == 0u) 1142 | // Something went wrong while reading imp name, don't cache this. 1143 | continue; 1144 | 1145 | m_szFunctionName[MAX_PATH - 1] = '\0'; 1146 | 1147 | // Cache desired data. 1148 | this->m_ImportData[m_szFunctionName] = { m_szModuleName }; 1149 | } 1150 | } 1151 | } 1152 | } 1153 | 1154 | // Ensure we found the target PE header. 1155 | bool PEHeaderData_t::IsValid() 1156 | { 1157 | return this->m_bIsValidInternal; 1158 | } 1159 | 1160 | // Helper function to get DOS header of PE image. 1161 | PIMAGE_DOS_HEADER PEHeaderData_t::GetDOSHeader() 1162 | { 1163 | return this->m_pDOSHeaders; 1164 | } 1165 | 1166 | // Helper function to get NT headers of PE image. 1167 | PIMAGE_NT_HEADERS PEHeaderData_t::GetNTHeader() 1168 | { 1169 | if (this->m_nMismatchedArchitecture != 0u) 1170 | CH_LOG("Warning: Architecture of PE image was not expected by build type. Actual was 0x%X", 1171 | this->m_nMismatchedArchitecture); 1172 | 1173 | return this->m_pNTHeaders; 1174 | } 1175 | 1176 | // Helper function to get specific data directory of PE image. 1177 | IMAGE_DATA_DIRECTORY PEHeaderData_t::GetDataDirectory(std::size_t m_nDirIndex) 1178 | { 1179 | if (m_nDirIndex > IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR) 1180 | // Prevent user from doing an oopsie OOB read. 1181 | return {}; 1182 | 1183 | return this->m_DirectoryData[m_nDirIndex]; 1184 | } 1185 | 1186 | // Helper function to get section data of PE image. 1187 | std::vector PEHeaderData_t::GetSectionData() 1188 | { 1189 | return this->m_SectionData; 1190 | } 1191 | 1192 | // Helper function to get exported functions' data of PE image. 1193 | std::map PEHeaderData_t::GetExportData() 1194 | { 1195 | return this->m_ExportData; 1196 | } 1197 | 1198 | // Helper function to get imported functions' data of PE image. 1199 | std::map PEHeaderData_t::GetImportData() 1200 | { 1201 | return this->m_ImportData; 1202 | } 1203 | 1204 | // Helper function to get debug directories data of PE image. 1205 | PEHeaderData_t::DebugData_t PEHeaderData_t::GetDebugData() 1206 | { 1207 | return this->m_DebugData; 1208 | } 1209 | // Convert relative virtual address to file offset. 1210 | std::uint32_t PEHeaderData_t::RvaToOffset(std::uint32_t m_nRva) 1211 | { 1212 | for (const auto& SectionData : this->GetSectionData()) 1213 | { 1214 | if (m_nRva < SectionData.m_Address || 1215 | m_nRva > SectionData.m_Address + SectionData.m_Size) 1216 | continue; 1217 | 1218 | return SectionData.m_PointerToRawData + m_nRva - SectionData.m_Address; 1219 | } 1220 | return NULL; 1221 | } 1222 | 1223 | std::uint32_t PEHeaderData_t::OffsetToRva(std::uint32_t m_nOffset) 1224 | { 1225 | for (const auto& SectionData : this->GetSectionData()) 1226 | { 1227 | if (m_nOffset < SectionData.m_PointerToRawData || 1228 | m_nOffset > SectionData.m_Address + SectionData.m_Size) 1229 | continue; 1230 | 1231 | return SectionData.m_Address + m_nOffset - SectionData.m_PointerToRawData; 1232 | } 1233 | return NULL; 1234 | } 1235 | 1236 | // Get certain section by address in memory. 1237 | PEHeaderData_t::SectionData_t PEHeaderData_t::GetSectionByAddress(std::uint32_t m_nAddress) 1238 | { 1239 | // Traverse all sections to find which one our address resides in. 1240 | for (const auto& SectionData : this->GetSectionData()) 1241 | { 1242 | if (m_nAddress < SectionData.m_Address || 1243 | m_nAddress > SectionData.m_Address + SectionData.m_Size) 1244 | continue; 1245 | 1246 | return SectionData; 1247 | } 1248 | return {}; // Wtf. Couldn't find. 1249 | } 1250 | } 1251 | 1252 | // ImageFile_t definitions and functions. 1253 | namespace chdr 1254 | { 1255 | // Used for parsing PE's from file. 1256 | ImageFile_t::ImageFile_t(const char* m_szImagePath, std::int32_t m_ParseType) 1257 | { 1258 | CH_ASSERT(true, std::filesystem::exists(m_szImagePath), "File at %s doesn't exist, or wasn't accessible.", m_szImagePath); 1259 | 1260 | // Fill image buffer. 1261 | std::ifstream m_fFile(m_szImagePath, std::ios::binary); 1262 | (&m_ImageBuffer)->assign((std::istreambuf_iterator(m_fFile)), std::istreambuf_iterator()); 1263 | m_fFile.close(); 1264 | 1265 | // Parse PE header information. 1266 | m_PEHeaderData = PEHeaderData_t((&m_ImageBuffer)->data(), (&m_ImageBuffer)->size(), m_ParseType); 1267 | } 1268 | 1269 | // Used for parsing PE's from memory. 1270 | ImageFile_t::ImageFile_t(std::uint8_t* m_ImageBuffer, std::size_t m_nImageSize, std::int32_t m_ParseType) 1271 | { 1272 | // Copy over to object-specific variable to possibly use later. 1273 | this->m_ImageBuffer.resize(m_nImageSize); 1274 | std::memcpy(&this->m_ImageBuffer[0], m_ImageBuffer, m_nImageSize); 1275 | 1276 | // Parse PE header information. 1277 | m_PEHeaderData = PEHeaderData_t(this->m_ImageBuffer.data(), this->m_ImageBuffer.size(), m_ParseType); 1278 | } 1279 | 1280 | // Ensure we found the target PE image. 1281 | bool ImageFile_t::IsValid() 1282 | { 1283 | return this->m_ImageBuffer.size() != 0; 1284 | } 1285 | 1286 | // Helper function to get PE header data of PE image. 1287 | PEHeaderData_t ImageFile_t::GetPEHeaderData() 1288 | { 1289 | return this->m_PEHeaderData; 1290 | } 1291 | 1292 | void ImageFile_t::WriteToFile(const char* m_szFilePath) 1293 | { 1294 | std::ofstream file(m_szFilePath, std::ios_base::out | std::ios_base::binary); 1295 | if (!file.is_open()) 1296 | // Unable to setup desired file. 1297 | return; 1298 | 1299 | file.write(CH_R_CAST(this->m_ImageBuffer.data()), this->m_ImageBuffer.size()); 1300 | file.close(); 1301 | } 1302 | } 1303 | 1304 | // Driver_t definitions and functions. 1305 | namespace chdr 1306 | { 1307 | Driver_t::Driver_t(const char* m_szDriverPath) 1308 | { 1309 | // Object-specific driver path. 1310 | this->m_szDriverPath = m_szDriverPath; 1311 | } 1312 | 1313 | // Initialize by driver information. 1314 | Driver_t::Driver_t(const char* m_szDriverName, DWORD m_dDesiredAccess, DWORD m_dSharedMode, DWORD m_dCreationDisposition, DWORD m_dFlagsAndAttributes) 1315 | { 1316 | this->m_hTargetDriverHandle = CreateFileA(m_szDriverName, 1317 | m_dDesiredAccess, 1318 | m_dSharedMode, 1319 | nullptr, 1320 | m_dCreationDisposition, 1321 | m_dFlagsAndAttributes, 1322 | nullptr); 1323 | 1324 | CH_ASSERT(false, this->m_hTargetDriverHandle && this->m_hTargetDriverHandle != INVALID_HANDLE_VALUE, "Failed to get HANDLE to desired service!"); 1325 | } 1326 | 1327 | // For opening an HANDLE to a currently loaded driver. 1328 | bool Driver_t::SetupHandle(const char* m_szDriverName, DWORD m_dDesiredAccess, DWORD m_dSharedMode, DWORD m_dCreationDisposition, DWORD m_dFlagsAndAttributes) 1329 | { 1330 | this->m_hTargetDriverHandle = CreateFileA(m_szDriverName, 1331 | m_dDesiredAccess, 1332 | m_dSharedMode, 1333 | nullptr, 1334 | m_dCreationDisposition, 1335 | m_dFlagsAndAttributes, 1336 | nullptr); 1337 | 1338 | return this->m_hTargetDriverHandle && this->m_hTargetDriverHandle != INVALID_HANDLE_VALUE; 1339 | } 1340 | 1341 | // For destroying a HANDLE to a currently loaded driver. 1342 | bool Driver_t::DestroyHandle() 1343 | { 1344 | if (!this->m_hTargetDriverHandle || this->m_hTargetDriverHandle == INVALID_HANDLE_VALUE) 1345 | { 1346 | // Sir, why have you tried to release this handle without ensuring it was setup correctly?! 1347 | CH_LOG("Tried to release an invalid HANDLE!"); 1348 | return NULL; 1349 | } 1350 | 1351 | const bool m_bDidSucceed = CloseHandle(this->m_hTargetDriverHandle); 1352 | return m_bDidSucceed; 1353 | } 1354 | 1355 | // Send IOCTL request to the target driver, returning the response. 1356 | DWORD Driver_t::SendIOCTL(DWORD m_dControlCode, LPVOID m_pInBuffer, DWORD m_dBufferSize, LPVOID m_pOutBuffer, DWORD m_dOutBufferSize) 1357 | { 1358 | DWORD m_dBytesReturned = { 0 }; 1359 | const BOOL m_bDidSucceed = DeviceIoControl(this->m_hTargetDriverHandle, 1360 | m_dControlCode, m_pInBuffer, 1361 | m_dBufferSize, m_pOutBuffer, 1362 | m_dOutBufferSize, &m_dBytesReturned, 1363 | nullptr); 1364 | 1365 | if (!m_bDidSucceed) 1366 | { 1367 | CH_LOG("DeviceIoControl failed with error code #%i!", GetLastError()); 1368 | return NULL; 1369 | } 1370 | 1371 | return m_dBytesReturned; 1372 | } 1373 | 1374 | // Loads a target driver through the service manager. Obviously, these drivers must be SIGNED. 1375 | SC_HANDLE Driver_t::LoadDriver(const char* m_szDriverPaths, const char* m_szDriverName) 1376 | { 1377 | // Create HANDLE to the service manager. 1378 | const SC_HANDLE m_hServiceManager = OpenSCManagerA(nullptr, nullptr, SC_MANAGER_CREATE_SERVICE); 1379 | if (!m_hServiceManager || m_hServiceManager == INVALID_HANDLE_VALUE) 1380 | { 1381 | CH_LOG("Couldn't obtain valid HANDLE to service manager!"); 1382 | return CH_R_CAST(INVALID_HANDLE_VALUE); 1383 | } 1384 | // Tell service manager to create&intialize our service. 1385 | const SC_HANDLE m_hCreatedService = CreateServiceA(m_hServiceManager, 1386 | m_szDriverName, m_szDriverName, 1387 | SERVICE_START | SERVICE_STOP | DELETE, 1388 | SERVICE_KERNEL_DRIVER, SERVICE_DEMAND_START, SERVICE_ERROR_IGNORE, 1389 | m_szDriverPaths, nullptr, nullptr, nullptr, nullptr, nullptr); 1390 | 1391 | if (!m_hCreatedService || m_hCreatedService == INVALID_HANDLE_VALUE) 1392 | { 1393 | CH_LOG("Failed to create desired service!"); 1394 | return CH_R_CAST(INVALID_HANDLE_VALUE); 1395 | } 1396 | 1397 | ADD_SCOPE_HANDLER(CloseServiceHandle, m_hServiceManager); 1398 | 1399 | // Finally, start the service. 1400 | if (!StartServiceA(m_hCreatedService, NULL, nullptr)) 1401 | { 1402 | CH_LOG("Failed to start desired service!"); 1403 | return CH_R_CAST(INVALID_HANDLE_VALUE); 1404 | } 1405 | 1406 | // Release unneeded handle. 1407 | return m_hCreatedService; 1408 | } 1409 | 1410 | // Unloads a target driver that was previously loaded through the service manager. 1411 | void Driver_t::UnloadDriver(const SC_HANDLE m_hLoadedStartedService) 1412 | { 1413 | // Send STOP signal to desired service. 1414 | SERVICE_STATUS m_ServiceStatus{}; 1415 | ControlService(m_hLoadedStartedService, SERVICE_CONTROL_STOP, &m_ServiceStatus); 1416 | 1417 | // Finally, delete the service. 1418 | DeleteService(m_hLoadedStartedService); 1419 | } 1420 | } 1421 | 1422 | // Thread_t definitions and functions. 1423 | namespace chdr 1424 | { 1425 | // Initialize with TID. 1426 | Thread_t::Thread_t(std::uint32_t m_dThreadID) 1427 | { 1428 | this->m_dThreadID = m_dThreadID; 1429 | this->m_hThreadHandle = OpenThread(THREAD_ALL_ACCESS, FALSE, this->m_dThreadID); 1430 | 1431 | // Only release this HANDLE if we've actually got access to it. 1432 | this->m_bShouldFreeHandleAtDestructor = this->m_hThreadHandle && this->m_hThreadHandle != INVALID_HANDLE_VALUE; 1433 | } 1434 | 1435 | // Initialize with TID&HANDLE. 1436 | Thread_t::Thread_t(HANDLE m_hThreadHandle) 1437 | { 1438 | this->m_hThreadHandle = m_hThreadHandle; 1439 | this->m_dThreadID = GetThreadId(this->m_hThreadHandle); 1440 | 1441 | // We should NEVER try to release this HANDLE, as it's a copy of another. 1442 | this->m_bShouldFreeHandleAtDestructor = false; 1443 | } 1444 | 1445 | // Default dtor 1446 | Thread_t::~Thread_t() 1447 | { 1448 | // Not allowed to release this HANDLE, or was already released. 1449 | CH_ASSERT(true, 1450 | this->m_bShouldFreeHandleAtDestructor && 1451 | this->m_hThreadHandle && 1452 | this->m_hThreadHandle != INVALID_HANDLE_VALUE, 1453 | ""); 1454 | 1455 | // FIXME: 1456 | CloseHandle(this->m_hThreadHandle); 1457 | } 1458 | 1459 | #pragma warning( push ) 1460 | #pragma warning( disable : 6258 ) // Using TerminateThread does not allow proper thread clean. 1461 | // Terminating a target thread. 1462 | void Thread_t::Terminate() 1463 | { 1464 | CH_ASSERT(true, this->m_hThreadHandle && this->m_hThreadHandle != INVALID_HANDLE_VALUE, "Tried to terminate a target thread with an empty HANDLE!"); 1465 | 1466 | TerminateThread(this->m_hThreadHandle, EXIT_FAILURE/*To give some sort of graceful termination.*/); 1467 | CloseHandle(this->m_hThreadHandle); // Can we even do this? Common sense dictates no. 1468 | } 1469 | #pragma warning( pop ) 1470 | 1471 | // Suspending a target thread. 1472 | void Thread_t::Suspend() 1473 | { 1474 | CH_ASSERT(true, this->m_hThreadHandle && this->m_hThreadHandle != INVALID_HANDLE_VALUE, "Tried to suspend a target thread with an empty HANDLE!"); 1475 | 1476 | this->m_bIsThreadManuallySuspended = SuspendThread(this->m_hThreadHandle) != 0; 1477 | } 1478 | 1479 | // Resuming a target thread. 1480 | void Thread_t::Resume() 1481 | { 1482 | CH_ASSERT(true, this->m_hThreadHandle && this->m_hThreadHandle != INVALID_HANDLE_VALUE, "Tried to resume a target thread with an empty HANDLE!"); 1483 | 1484 | this->m_bIsThreadManuallySuspended = ResumeThread(this->m_hThreadHandle) == 0; 1485 | CH_ASSERT(true, !this->m_bIsThreadManuallySuspended, "Failed to resume thread with TID %i!", this->m_dThreadID); 1486 | } 1487 | 1488 | // Get context of this thread. 1489 | CONTEXT Thread_t::GetThreadCTX() 1490 | { 1491 | CONTEXT result; { result.ContextFlags = CONTEXT_FULL; } 1492 | GetThreadContext(this->m_hThreadHandle, &result); 1493 | return result; 1494 | } 1495 | 1496 | // Set context of this thread. 1497 | void Thread_t::SetThreadCTX(CONTEXT m_Context) 1498 | { 1499 | SetThreadContext(this->m_hThreadHandle, &m_Context); 1500 | } 1501 | 1502 | // Check which module this thread is associated with. 1503 | std::string Thread_t::GetOwningModule(chdr::Process_t& m_Process, bool m_bUseCachedData) 1504 | { 1505 | const std::uintptr_t m_StartAddress = this->GetStartAddress(); 1506 | if (m_StartAddress == 0u) 1507 | // Weird, shouldn't happen unless we have no valid HANDLE. 1508 | return "N/A (Couldn't find thread start address.)"; 1509 | 1510 | // Traverse through all loaded modules, and determine where our thread lies in. 1511 | for (auto& CurrentModule : m_Process.EnumerateModules(m_bUseCachedData)) 1512 | { 1513 | if (m_StartAddress < CurrentModule.m_BaseAddress || 1514 | m_StartAddress > CurrentModule.m_BaseAddress + CurrentModule.m_nSize) 1515 | continue; 1516 | 1517 | return CurrentModule.m_szName; 1518 | } 1519 | return "N/A (Passing through false as default parameter should be your solution.)"; 1520 | } 1521 | 1522 | // Ensure we found a HANDLE to the target thread. 1523 | bool Thread_t::IsValid() 1524 | { 1525 | return this->m_hThreadHandle && this->m_hThreadHandle != INVALID_HANDLE_VALUE; 1526 | } 1527 | 1528 | // Is the target thread suspended? 1529 | bool Thread_t::IsSuspended() 1530 | { 1531 | bool m_bIsThreadSuspended = WaitForSingleObject(this->m_hThreadHandle, 0) == WAIT_ABANDONED; 1532 | return m_bIsThreadSuspended; 1533 | } 1534 | 1535 | // Did we suspend the target thread ourselves? 1536 | bool Thread_t::IsManuallySuspended() 1537 | { 1538 | return this->m_bIsThreadManuallySuspended; 1539 | } 1540 | 1541 | // Get's the start address of a target thread. 1542 | std::uint32_t Thread_t::GetStartAddress() 1543 | { 1544 | NtQueryInformationThread_fn NtQueryInformationThread = 1545 | CH_R_CAST(GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtQueryInformationThread")); 1546 | 1547 | std::uint32_t m_dThreadStartAddress = NULL; 1548 | NtQueryInformationThread(this->m_hThreadHandle, THREADINFOCLASS::ThreadQuerySetWin32StartAddress, &m_dThreadStartAddress, sizeof(std::uint32_t), nullptr); 1549 | return m_dThreadStartAddress; 1550 | } 1551 | } 1552 | 1553 | // Module_t definitions and functions. 1554 | namespace chdr 1555 | { 1556 | // Setup module in process by (non-case sensitive) name. 1557 | Module_t::Module_t(chdr::Process_t& m_Process, const char* m_szModuleName, std::int32_t m_ParseType) 1558 | { 1559 | // Walk all loaded modules until we land on the wish module. 1560 | for (const auto& CurrentModule : m_Process.EnumerateModules()) 1561 | { 1562 | if (std::strcmp(CurrentModule.m_szName.c_str(), m_szModuleName) != 0) 1563 | continue; 1564 | 1565 | this->m_dModuleBaseAddress = CurrentModule.m_BaseAddress; 1566 | this->m_dModuleSize = CurrentModule.m_nSize; 1567 | break; // Found what we needed, exit loop. 1568 | } 1569 | 1570 | CH_ASSERT(true, this->m_dModuleBaseAddress && this->m_dModuleSize, "Couldn't find desired module %s", m_szModuleName); 1571 | 1572 | this->SetupModule_Internal(m_Process, m_ParseType); 1573 | } 1574 | 1575 | // Setup module in process by address in processes' memory space. 1576 | Module_t::Module_t(chdr::Process_t& m_Process, std::uintptr_t m_dModuleBaseAddress, std::uint32_t m_dModuleSize, std::int32_t m_ParseType) 1577 | { 1578 | this->m_dModuleBaseAddress = m_dModuleBaseAddress; 1579 | this->m_dModuleSize = m_dModuleSize; 1580 | 1581 | this->SetupModule_Internal(m_Process, m_ParseType); 1582 | } 1583 | 1584 | void Module_t::SetupModule_Internal(chdr::Process_t& m_Process, std::int32_t m_ParseType) 1585 | { 1586 | // Read whole module to our buffer in heap. 1587 | const auto m_ModuleDataTemp = std::make_unique(this->m_dModuleSize); 1588 | const std::size_t m_nReadBytes = m_Process.Read(this->m_dModuleBaseAddress, m_ModuleDataTemp.get(), this->m_dModuleSize); 1589 | 1590 | CH_ASSERT(true, m_nReadBytes != 0u, "Failed to read image of desired module."); 1591 | 1592 | // Ensure vector holding image buffer has sufficient size. 1593 | this->m_ModuleData.resize(m_nReadBytes); 1594 | 1595 | // Copy over read data from this module. 1596 | std::memcpy(&this->m_ModuleData[0], m_ModuleDataTemp.get(), m_nReadBytes); 1597 | 1598 | this->m_PEHeaderData = PEHeaderData_t(m_Process, m_ParseType, this->m_dModuleBaseAddress); 1599 | } 1600 | 1601 | // Helper function to get PE header data of target process. 1602 | PEHeaderData_t Module_t::GetPEHeaderData() 1603 | { 1604 | return this->m_PEHeaderData; 1605 | } 1606 | 1607 | // Helper function to get module data of target process. 1608 | ByteArray_t Module_t::GetModuleData() 1609 | { 1610 | return this->m_ModuleData; 1611 | } 1612 | 1613 | // Ensure we found the target module in memory. 1614 | bool Module_t::IsValid() 1615 | { 1616 | return this->m_ModuleData.size() != 0; 1617 | } 1618 | 1619 | Address_t Module_t::FindIDASignature(std::string_view m_szSignature) 1620 | { 1621 | static auto ToBytes = [](std::string_view m_szSignature) { 1622 | std::vector byteArray = {}; 1623 | const auto m_pStart = CH_C_CAST(m_szSignature.data()); 1624 | const auto m_pEnd = m_pStart + m_szSignature.length(); 1625 | 1626 | for (char* m_pCurrent = m_pStart; m_pCurrent < m_pEnd; ++m_pCurrent) { 1627 | if (*m_pCurrent == '?') { 1628 | ++m_pCurrent; 1629 | 1630 | if (*m_pCurrent == '?') 1631 | ++m_pCurrent; 1632 | 1633 | byteArray.push_back(-1); 1634 | } 1635 | else 1636 | byteArray.push_back(strtoul(m_pCurrent, &m_pCurrent, 16)); 1637 | } 1638 | return byteArray; 1639 | }; 1640 | 1641 | if (!m_dModuleSize || !IsValid()) { 1642 | CH_LOG("Invalid module provided."); 1643 | return Address_t(); 1644 | } 1645 | 1646 | // can't be ByteArray_t type because of wildcards. 1647 | const auto m_byteArray = ToBytes(m_szSignature); 1648 | const std::size_t nSize = m_byteArray.size(); 1649 | const int* pBytes = m_byteArray.data(); 1650 | 1651 | // Find matching sequences. 1652 | for (std::size_t i = 0u; i < m_dModuleSize - nSize; ++i) { 1653 | bool bFound = true; 1654 | 1655 | for (std::size_t j = 0u; j < nSize; ++j) { 1656 | if (pBytes[j] != -1 && m_ModuleData[i + j] != pBytes[j]) { 1657 | bFound = false; 1658 | break; 1659 | } 1660 | } 1661 | 1662 | if (bFound) 1663 | return Address_t(&m_ModuleData[i]); 1664 | } 1665 | 1666 | CH_LOG("Couldn't find IDA signature %s.", m_szSignature.data()); 1667 | return Address_t(); 1668 | } 1669 | } 1670 | 1671 | // Miscelleanous functions. 1672 | namespace chdr 1673 | { 1674 | namespace misc 1675 | { 1676 | 1677 | } 1678 | } -------------------------------------------------------------------------------- /chdr.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | namespace chdr 16 | { 17 | 18 | #if 1 19 | #define SHOULD_PRINT_DEBUG_LOGS // Log errors/verbose information. 20 | #endif 21 | 22 | #ifdef SHOULD_PRINT_DEBUG_LOGS 23 | // Custom debug assert/log. 24 | #define CH_LOG(s, ...) { std::printf("[!] "); std::printf(XOR(s), __VA_ARGS__); std::printf("\n"); } 25 | #define CH_ASSERT(x, b, s, ...) if (!(b)) { CH_LOG(s, __VA_ARGS__) if constexpr (x) return; } 26 | #else 27 | // Custom debug assert/log. 28 | #define CH_LOG(s, ...) (void)0 29 | #define CH_ASSERT(x, b, s, ...) if (!(b)) { if (x) return; } 30 | #endif 31 | 32 | // Custom casting macros, because fucking C++ style casts are just TOO long. 33 | #define CH_R_CAST reinterpret_cast 34 | #define CH_S_CAST static_cast 35 | #define CH_D_CAST dynamic_cast 36 | #define CH_C_CAST const_cast 37 | 38 | // For ease of use. 39 | using ByteArray_t = std::vector; 40 | 41 | // To be used internally by PEHeaderData_t. 42 | class Process_t; 43 | class Module_t; 44 | class Driver_t; 45 | 46 | class Module_t; 47 | class Address_t; 48 | 49 | // For easy and organized PE header parsing. 50 | class PEHeaderData_t 51 | { 52 | struct SectionData_t 53 | { 54 | std::string m_szName = ""; 55 | std::uint32_t m_Address = 0u; 56 | std::uint32_t m_Size = 0u; 57 | std::uint32_t m_Characteristics = 0u; 58 | std::uint32_t m_PointerToRawData = 0u; 59 | std::uint32_t m_SizeOfRawData = 0u; 60 | }; 61 | 62 | struct ExportData_t 63 | { 64 | std::uint32_t m_nAddress = 0u; 65 | std::uint16_t m_nOrdinal = 0u; 66 | }; 67 | 68 | struct ImportData_t 69 | { 70 | std::string m_szModuleName = ""; 71 | }; 72 | 73 | struct DebugData_t 74 | { 75 | std::string m_szPDBPath = ""; 76 | std::string m_szGUIDSignature = ""; 77 | std::uint32_t m_Age = 0u; 78 | std::uint32_t m_CVSignature = 0u; 79 | }; 80 | 81 | // Only for PDB7.0 format! 82 | struct CV_INFO_PDB70 83 | { 84 | DWORD CvSignature; 85 | GUID Signature; 86 | DWORD Age; 87 | BYTE PdbFileName[MAX_PATH]; 88 | }; 89 | 90 | // For caching desired data. 91 | std::vector m_SectionData = { }; 92 | std::map m_ExportData = { }; 93 | std::map m_ImportData = { }; 94 | std::vector m_DirectoryData = { }; 95 | DebugData_t m_DebugData = { }; 96 | 97 | PIMAGE_DOS_HEADER m_pDOSHeaders = { 0 }; 98 | PIMAGE_NT_HEADERS m_pNTHeaders = { 0 }; 99 | 100 | bool m_bIsValidInternal = false; 101 | std::uint32_t m_nMismatchedArchitecture = false; 102 | public: 103 | enum PEHEADER_PARSING_TYPE : std::int32_t 104 | { 105 | TYPE_NONE = (0 << 0), 106 | TYPE_ALL = (1 << 1), 107 | TYPE_EXPORT_DIRECTORY = (1 << 2), 108 | TYPE_IMPORT_DIRECTORY = (1 << 3), 109 | TYPE_DEBUG_DIRECTORY = (1 << 4), 110 | TYPE_SECTIONS = (1 << 5) 111 | }; 112 | public: 113 | // Default ctor 114 | PEHeaderData_t() { } 115 | 116 | // Parsing data out of this image's buffer. 117 | PEHeaderData_t(std::uint8_t* m_ImageBuffer, std::size_t m_ImageSize, std::int32_t m_ParseType = PEHEADER_PARSING_TYPE::TYPE_NONE); 118 | 119 | // Parsing data out of this image's process. 120 | PEHeaderData_t(Process_t& m_Process, std::int32_t m_ParseType = PEHEADER_PARSING_TYPE::TYPE_ALL, std::uintptr_t m_CustomBaseAddress = NULL); 121 | 122 | public: 123 | // Ensure we found the target PE header. 124 | bool IsValid(); 125 | 126 | // Helper function to get DOS header of PE image. 127 | PIMAGE_DOS_HEADER GetDOSHeader(); 128 | 129 | // Helper function to get NT headers of PE image. 130 | PIMAGE_NT_HEADERS GetNTHeader(); 131 | 132 | // Helper function to get specific data directory of PE image. 133 | IMAGE_DATA_DIRECTORY GetDataDirectory(std::size_t m_nDirIndex); 134 | 135 | // Helper function to get section data of PE image. 136 | std::vector GetSectionData(); 137 | 138 | // Helper function to get exported functions' data of PE image. 139 | std::map GetExportData(); 140 | 141 | // Helper function to get imported functions' data of PE image. 142 | std::map GetImportData(); 143 | 144 | // Helper function to get debug directories' data of PE image. 145 | DebugData_t GetDebugData(); 146 | 147 | // Convert relative virtual address to file offset. 148 | std::uint32_t RvaToOffset(std::uint32_t m_nRva); 149 | 150 | // Convert file offset to relative virtual address. 151 | std::uint32_t OffsetToRva(std::uint32_t m_nOffset); 152 | 153 | // Get certain section by address in memory. 154 | SectionData_t GetSectionByAddress(std::uint32_t m_nAddress); 155 | }; 156 | 157 | // PE Image utility helpers 158 | class ImageFile_t 159 | { 160 | PEHeaderData_t m_PEHeaderData = { }; 161 | public: 162 | 163 | // Used for parsing PE's from file. 164 | ImageFile_t(const char* m_szImagePath, std::int32_t m_ParseType = PEHeaderData_t::PEHEADER_PARSING_TYPE::TYPE_ALL); 165 | 166 | // Used for parsing PE's from memory. 167 | ImageFile_t(std::uint8_t* m_ImageBuffer, std::size_t m_nImageSize, std::int32_t m_ParseType = PEHeaderData_t::PEHEADER_PARSING_TYPE::TYPE_ALL); 168 | 169 | public: 170 | ByteArray_t m_ImageBuffer; 171 | 172 | public: 173 | 174 | // Ensure we found the target PE image. 175 | bool IsValid(); 176 | 177 | // Helper function to get PE header data of PE image. 178 | PEHeaderData_t GetPEHeaderData(); 179 | 180 | // Writes data in local buffer to file. 181 | void WriteToFile(const char* m_szFilePath); 182 | }; 183 | 184 | // Driver utility helpers 185 | class Driver_t 186 | { 187 | std::string m_szDriverPath = ""; 188 | HANDLE m_hTargetDriverHandle = { 0 }; 189 | 190 | public: 191 | // Default ctor. 192 | Driver_t() { } 193 | 194 | // Initialize by driver path. 195 | Driver_t(const char* m_szDriverPath); 196 | 197 | // Initialize by driver information. 198 | Driver_t(const char* m_szDriverName, DWORD m_dDesiredAccess, DWORD m_dSharedMode, DWORD m_dCreationDisposition, DWORD m_dFlagsAndAttributes); 199 | 200 | public: 201 | 202 | // For opening an HANDLE to a currently loaded driver. 203 | bool SetupHandle(const char* m_szDriverName, DWORD m_dDesiredAccess, DWORD m_dSharedMode, DWORD m_dCreationDisposition, DWORD m_dFlagsAndAttributes); 204 | 205 | // For destroying a HANDLE to a currently loaded driver. 206 | bool DestroyHandle(); 207 | 208 | // Send IOCTL request to the target driver, returning the response. 209 | DWORD SendIOCTL(DWORD m_dControlCode, LPVOID m_pInBuffer, DWORD m_dBufferSize, LPVOID m_pOutBuffer, DWORD m_dOutBufferSize); 210 | 211 | // Loads a target driver through the service manager. Obviously, these drivers must be SIGNED. 212 | SC_HANDLE LoadDriver(const char* m_szDriverPaths, const char* m_szDriverName); 213 | 214 | // Unloads a target driver that was previously loaded through the service manager. 215 | void UnloadDriver(const SC_HANDLE m_hLoadedStartedService); 216 | }; 217 | 218 | class Module_t 219 | { 220 | std::uintptr_t m_dModuleBaseAddress = NULL; 221 | std::uint32_t m_dModuleSize = NULL; 222 | 223 | PEHeaderData_t m_PEHeaderData = { }; 224 | 225 | public: 226 | ByteArray_t m_ModuleData = { }; 227 | 228 | // Default ctor 229 | Module_t() { } 230 | 231 | // Setup module in process by (non-case sensitive) name. 232 | Module_t(chdr::Process_t& m_Process, const char* m_szModuleName, std::int32_t m_ParseType = PEHeaderData_t::PEHEADER_PARSING_TYPE::TYPE_ALL); 233 | 234 | // Setup module in process by address in process. (plz pass correct data here :D) 235 | Module_t(chdr::Process_t& m_Process, std::uintptr_t m_dModuleBaseAddress, std::uint32_t m_dModuleSize, std::int32_t m_ParseType = PEHeaderData_t::PEHEADER_PARSING_TYPE::TYPE_ALL); 236 | 237 | // Ease of use for building constructors. 238 | void SetupModule_Internal(chdr::Process_t& m_Process, std::int32_t m_ParseType = PEHeaderData_t::PEHEADER_PARSING_TYPE::TYPE_ALL); 239 | public: 240 | 241 | // Helper function to get PE header data of target process. 242 | PEHeaderData_t GetPEHeaderData(); 243 | 244 | // Helper function to get module data of target process. 245 | ByteArray_t GetModuleData(); 246 | 247 | // Ensure we found the target module in memory. 248 | bool IsValid(); 249 | 250 | // Helper function to find some bytes in the module data. 251 | Address_t FindIDASignature(std::string_view m_szSignature); 252 | }; 253 | 254 | class Thread_t 255 | { 256 | // Basic thread information. 257 | std::uint32_t m_dThreadID = 0; 258 | HANDLE m_hThreadHandle = { }; 259 | 260 | // Acts as a lock, to only resume threads previously suspended. 261 | bool m_bIsThreadManuallySuspended = false; 262 | 263 | // Acts as a lock, to only free HANDLE's that we've internally obtained. 264 | bool m_bShouldFreeHandleAtDestructor = false; 265 | 266 | public: 267 | enum THREADINFOCLASS { 268 | ThreadBasicInformation, 269 | ThreadTimes, 270 | ThreadPriority, 271 | ThreadBasePriority, 272 | ThreadAffinityMask, 273 | ThreadImpersonationToken, 274 | ThreadDescriptorTableEntry, 275 | ThreadEnableAlignmentFaultFixup, 276 | ThreadEventPair, 277 | ThreadQuerySetWin32StartAddress, 278 | ThreadZeroTlsCell, 279 | ThreadPerformanceCount, 280 | ThreadAmILastThread, 281 | ThreadIdealProcessor, 282 | ThreadPriorityBoost, 283 | ThreadSetTlsArrayAddress, 284 | ThreadIsIoPending, 285 | ThreadHideFromDebugger 286 | }; 287 | 288 | typedef NTSTATUS(__stdcall* NtQueryInformationThread_fn)(HANDLE, THREADINFOCLASS, PVOID, ULONG, PULONG); 289 | public: 290 | // Default ctor 291 | Thread_t() { } 292 | 293 | // Initialize with TID. 294 | Thread_t(std::uint32_t m_dThreadID); 295 | 296 | // Initialize with TID&HANDLE. 297 | Thread_t(HANDLE m_hThreadHandle); 298 | 299 | // Default dtor 300 | ~Thread_t(); 301 | 302 | // Terminating a target thread. 303 | void Terminate(); 304 | 305 | // Suspending a target thread. 306 | void Suspend(); 307 | 308 | // Resuming a target thread. 309 | void Resume(); 310 | 311 | // Get context of this thread. 312 | CONTEXT GetThreadCTX(); 313 | 314 | // Set context of this thread. 315 | void SetThreadCTX(CONTEXT m_Context); 316 | 317 | // Check which module this thread is associated with. 318 | std::string GetOwningModule(chdr::Process_t& m_Process, bool m_bUseCachedData = true); 319 | 320 | // Ensure we found a HANDLE to the target thread. 321 | bool IsValid(); 322 | 323 | // Is the target thread suspended? 324 | bool IsSuspended(); 325 | 326 | // Did we suspend the target thread ourselves? 327 | bool IsManuallySuspended(); 328 | 329 | // Get's the start address of a target thread. 330 | std::uint32_t GetStartAddress(); 331 | }; 332 | 333 | // Process utility helpers 334 | class Process_t 335 | { 336 | public: 337 | // Default ctor 338 | Process_t() { } 339 | 340 | // Get target proces by name. 341 | Process_t(const wchar_t* m_wszProcessName, std::int32_t m_ParseType = PEHeaderData_t::PEHEADER_PARSING_TYPE::TYPE_ALL, DWORD m_dDesiredAccess = PROCESS_ALL_ACCESS); 342 | 343 | // Get target proces by PID. 344 | Process_t(std::uint32_t m_nProcessID, std::int32_t m_ParseType = PEHeaderData_t::PEHEADER_PARSING_TYPE::TYPE_ALL, DWORD m_dDesiredAccess = PROCESS_ALL_ACCESS); 345 | 346 | // Get target proces by HANDLE. 347 | Process_t(HANDLE m_hProcessHandle, std::int32_t m_ParseType = PEHeaderData_t::PEHEADER_PARSING_TYPE::TYPE_ALL); 348 | 349 | // Default dtor 350 | ~Process_t(); 351 | 352 | enum eManualMapInjectionFlags : std::int32_t 353 | { 354 | INJECTION_NONE = (0 << 0), 355 | INJECTION_MODE_THREADHIJACK = (1 << 1), 356 | INJECTION_MODE_CREATEREMOTETHREAD = (1 << 2), 357 | INJECTION_EXTRA_CUSTOMARGUMENTS = (1 << 3), 358 | INJECTION_EXTRA_WIPEPEHEADERS = (1 << 4), 359 | INJECTION_EXTRA_WIPEENTRYPOINT = (1 << 5), 360 | INJECTION_EXTRA_WIPEGARBAGESECTIONS = (1 << 6), 361 | INJECTION_MAXIMUM = INJECTION_EXTRA_WIPEGARBAGESECTIONS + 1 362 | }; 363 | 364 | enum class eProcessArchitecture : std::int32_t 365 | { 366 | ARCHITECTURE_UNKNOWN = (0 << 0), 367 | ARCHITECTURE_x64 = (1 << 6), 368 | ARCHITECTURE_x86 = (1 << 5), 369 | ARCHITECTURE_MAXIMUM = ARCHITECTURE_x86 + 1 370 | }; 371 | 372 | private: 373 | PEHeaderData_t m_PEHeaderData = { }; 374 | 375 | // Basic process information. 376 | HANDLE m_hTargetProcessHandle = { 0 }; 377 | std::uint32_t m_nTargetProcessID = 0; 378 | 379 | // For saving off this processes' architecture type. 380 | eProcessArchitecture m_eProcessArchitecture = eProcessArchitecture::ARCHITECTURE_UNKNOWN; 381 | 382 | // For saving off this processes' name. 383 | std::string m_szProcessName = ""; 384 | 385 | // For saving off this processes' filesystem path. 386 | std::string m_szProcessPath = ""; 387 | 388 | // Acts as a lock, to only resume threads previously suspended. 389 | bool m_bIsProcessManuallySuspended = false; 390 | 391 | // Acts as a lock, to only free HANDLE's that we've internally obtained. 392 | bool m_bShouldFreeHandleAtDestructor = false; 393 | 394 | enum PROCESSINFOCLASS 395 | { 396 | ProcessBasicInformation = 0x00, 397 | ProcessQuotaLimits = 0x01, 398 | ProcessIoCounters = 0x02, 399 | ProcessVmCounters = 0x03, 400 | ProcessTimes = 0x04, 401 | ProcessBasePriority = 0x05, 402 | ProcessRaisePriority = 0x06, 403 | ProcessDebugPort = 0x07, 404 | ProcessExceptionPort = 0x08, 405 | ProcessAccessToken = 0x09, 406 | ProcessLdtInformation = 0x0A, 407 | ProcessLdtSize = 0x0B, 408 | ProcessDefaultHardErrorMode = 0x0C, 409 | ProcessIoPortHandlers = 0x0D, 410 | ProcessPooledUsageAndLimits = 0x0E, 411 | ProcessWorkingSetWatch = 0x0F, 412 | ProcessUserModeIOPL = 0x10, 413 | ProcessEnableAlignmentFaultFixup = 0x11, 414 | ProcessPriorityClass = 0x12, 415 | ProcessWx86Information = 0x13, 416 | ProcessHandleCount = 0x14, 417 | ProcessAffinityMask = 0x15, 418 | ProcessPriorityBoost = 0x16, 419 | ProcessDeviceMap = 0x17, 420 | ProcessSessionInformation = 0x18, 421 | ProcessForegroundInformation = 0x19, 422 | ProcessWow64Information = 0x1A, 423 | ProcessImageFileName = 0x1B, 424 | ProcessLUIDDeviceMapsEnabled = 0x1C, 425 | ProcessBreakOnTermination = 0x1D, 426 | ProcessDebugObjectHandle = 0x1E, 427 | ProcessDebugFlags = 0x1F, 428 | ProcessHandleTracing = 0x20, 429 | ProcessIoPriority = 0x21, 430 | ProcessExecuteFlags = 0x22, 431 | ProcessResourceManagement = 0x23, 432 | ProcessCookie = 0x24, 433 | ProcessImageInformation = 0x25, 434 | ProcessCycleTime = 0x26, 435 | ProcessPagePriority = 0x27, 436 | ProcessInstrumentationCallback = 0x28, 437 | ProcessThreadStackAllocation = 0x29, 438 | ProcessWorkingSetWatchEx = 0x2A, 439 | ProcessImageFileNameWin32 = 0x2B, 440 | ProcessImageFileMapping = 0x2C, 441 | ProcessAffinityUpdateMode = 0x2D, 442 | ProcessMemoryAllocationMode = 0x2E, 443 | ProcessGroupInformation = 0x2F, 444 | ProcessTokenVirtualizationEnabled = 0x30, 445 | ProcessConsoleHostProcess = 0x31, 446 | ProcessWindowInformation = 0x32, 447 | ProcessHandleInformation = 0x33, 448 | ProcessMitigationPolicy = 0x34, 449 | ProcessDynamicFunctionTableInformation = 0x35, 450 | ProcessHandleCheckingMode = 0x36, 451 | ProcessKeepAliveCount = 0x37, 452 | ProcessRevokeFileHandles = 0x38, 453 | ProcessWorkingSetControl = 0x39, 454 | ProcessHandleTable = 0x3A, 455 | ProcessCheckStackExtentsMode = 0x3B, 456 | ProcessCommandLineInformation = 0x3C, 457 | ProcessProtectionInformation = 0x3D, 458 | ProcessMemoryExhaustion = 0x3E, 459 | ProcessFaultInformation = 0x3F, 460 | ProcessTelemetryIdInformation = 0x40, 461 | ProcessCommitReleaseInformation = 0x41, 462 | ProcessDefaultCpuSetsInformation = 0x42, 463 | ProcessAllowedCpuSetsInformation = 0x43, 464 | ProcessSubsystemProcess = 0x44, 465 | ProcessJobMemoryInformation = 0x45, 466 | ProcessInPrivate = 0x46, 467 | ProcessRaiseUMExceptionOnInvalidHandleClose = 0x47, 468 | ProcessIumChallengeResponse = 0x48, 469 | ProcessChildProcessInformation = 0x49, 470 | ProcessHighGraphicsPriorityInformation = 0x4A, 471 | ProcessSubsystemInformation = 0x4B, 472 | ProcessEnergyValues = 0x4C, 473 | ProcessActivityThrottleState = 0x4D, 474 | ProcessActivityThrottlePolicy = 0x4E, 475 | ProcessWin32kSyscallFilterInformation = 0x4F, 476 | ProcessDisableSystemAllowedCpuSets = 0x50, 477 | ProcessWakeInformation = 0x51, 478 | ProcessEnergyTrackingState = 0x52, 479 | ProcessManageWritesToExecutableMemory = 0x53, 480 | ProcessCaptureTrustletLiveDump = 0x54, 481 | ProcessTelemetryCoverage = 0x55, 482 | ProcessEnclaveInformation = 0x56, 483 | ProcessEnableReadWriteVmLogging = 0x57, 484 | ProcessUptimeInformation = 0x58, 485 | ProcessImageSection = 0x59, 486 | ProcessDebugAuthInformation = 0x5A, 487 | ProcessSystemResourceManagement = 0x5B, 488 | ProcessSequenceNumber = 0x5C, 489 | ProcessLoaderDetour = 0x5D, 490 | ProcessSecurityDomainInformation = 0x5E, 491 | ProcessCombineSecurityDomainsInformation = 0x5F, 492 | ProcessEnableLogging = 0x60, 493 | ProcessLeapSecondInformation = 0x61, 494 | ProcessFiberShadowStackAllocation = 0x62, 495 | ProcessFreeFiberShadowStackAllocation = 0x63, 496 | MaxProcessInfoClass = 0x64 497 | }; 498 | 499 | typedef NTSTATUS(__stdcall* NtSuspendProcess_fn)(HANDLE); 500 | typedef NTSTATUS(__stdcall* NtResumeProcess_fn)(HANDLE); 501 | typedef NTSTATUS(__stdcall* NtQueryInformationProcess_fn)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG); 502 | 503 | // Relevant information pertaining a target thread. 504 | struct ThreadInformation_t 505 | { 506 | std::uint32_t m_nThreadID = 0; 507 | std::uint32_t m_nThreadStartAddress = 0; 508 | bool m_bIsThreadSuspended = false; 509 | }; 510 | 511 | // Relevant information pertaining a target module. 512 | struct ModuleInformation_t 513 | { 514 | std::string m_szName = ""; 515 | std::string m_szPath = ""; 516 | std::uint32_t m_nSize = 0u; 517 | std::uintptr_t m_BaseAddress = 0u; 518 | }; 519 | 520 | struct AllocatedMemoryData_t 521 | { 522 | std::uintptr_t m_AllocatedAddress = 0u; 523 | std::size_t m_nAllocatedSize = 0u; 524 | }; 525 | 526 | // Caching all loaded modules in target process. 527 | std::vector m_EnumeratedModulesCached = {}; 528 | 529 | // Track allocated memory (removed on ::VirtualFreeEx calls). 530 | std::map m_AllocatedMemoryTracker; 531 | 532 | // Keep track of modules already initialized. 533 | std::unordered_map m_AllocatedModules; 534 | 535 | bool m_bHasCachedProcessesModules = false; 536 | 537 | // Get architecture of target process. 538 | eProcessArchitecture GetProcessArchitecture_Internal(); 539 | 540 | // Get name of target process. 541 | std::string GetProcessName_Internal(); 542 | 543 | // Get filesystem path of target process. 544 | std::string GetProcessPath_Internal(); 545 | 546 | // Internal manual map function. 547 | bool ManualMapInject_Internal(std::uint8_t* m_ImageBuffer, std::int32_t m_eInjectionFlags = eManualMapInjectionFlags::INJECTION_NONE); 548 | public: 549 | 550 | // Helper function to get architecture of target process. 551 | eProcessArchitecture GetProcessArchitecture(); 552 | 553 | // Helper function to get PE header data of target process. 554 | PEHeaderData_t GetPEHeaderData(); 555 | 556 | // Helper function to get filesystem path of target process. 557 | std::string GetPath(); 558 | 559 | // Helper function to get name of target process. 560 | std::string GetName(); 561 | 562 | // The base address of the target process. 563 | std::uintptr_t GetBaseAddress(); 564 | 565 | // The process ID of the target process. (lol) 566 | std::uint32_t GetProcessID(); 567 | 568 | // The PEB of the target process. 569 | PEB GetPEB(); 570 | 571 | // Ensure we found a HANDLE to the target process. 572 | bool IsValid(); 573 | 574 | // Is target process 32-bit running on 64-bit OS? 575 | bool IsWow64(); 576 | 577 | // Did we suspend the target process ourselves? 578 | bool IsManuallySuspended(); 579 | 580 | // Is the target process suspended? 581 | bool IsSuspended(); 582 | 583 | // Is the target process running under a debugger? 584 | bool IsBeingDebugged(); 585 | 586 | // Manual map injection from module on disk. 587 | bool ManualMapInject(const char* m_szDLLPath, std::int32_t m_eInjectionFlags = eManualMapInjectionFlags::INJECTION_NONE); 588 | 589 | // Manual map injection from module in memory. 590 | bool ManualMapInject(std::uint8_t* m_ImageBuffer, std::int32_t m_eInjectionFlags = eManualMapInjectionFlags::INJECTION_NONE); 591 | 592 | // Manual map injection from ImageFile_t. 593 | bool ManualMapInject(ImageFile_t& m_ImageFile, std::int32_t m_eInjectionFlags = eManualMapInjectionFlags::INJECTION_NONE); 594 | 595 | // LoadLibrary injection from module on disk. 596 | bool LoadLibraryInject(const char* m_szDLLPath); 597 | 598 | // Traverse and cache data about all threads in a target process. 599 | std::vector EnumerateThreads(); 600 | 601 | // Traverse and cache data about all loaded modules in a target process. 602 | std::vector EnumerateModules(bool m_bUseCachedData = false); 603 | 604 | // Sets debug privileges of a target process. 605 | bool SetDebugPrivilege(bool m_bShouldEnable); 606 | 607 | // Suspend every thread in a target process. 608 | void Suspend(); 609 | 610 | // Resume every previously suspended thread in a target process. 611 | void Resume(); 612 | 613 | // Get desired export address by name. 614 | std::uintptr_t GetRemoteProcAddress(const char* m_szModuleName, const char* m_szExportName); 615 | 616 | // ReadProcessMemory implementation. 617 | template T Read(std::uintptr_t m_ReadAddress) 618 | { 619 | T m_pOutputRead; 620 | ReadProcessMemory(this->m_hTargetProcessHandle, (LPCVOID)m_ReadAddress, &m_pOutputRead, sizeof(T), nullptr); 621 | return m_pOutputRead; 622 | } 623 | 624 | // ReadProcessMemory implementation - allows byte arrays. 625 | template std::size_t Read(std::uintptr_t m_ReadAddress, S m_pBuffer, std::size_t m_nBufferSize) 626 | { 627 | SIZE_T m_nBytesRead = 0u; 628 | ReadProcessMemory(this->m_hTargetProcessHandle, (LPCVOID)m_ReadAddress, m_pBuffer, m_nBufferSize, &m_nBytesRead); 629 | return m_nBytesRead; 630 | } 631 | 632 | // WriteProcessMemory implementation. 633 | template std::size_t Write(std::uintptr_t m_WriteAddress, S m_WriteValue) 634 | { 635 | SIZE_T lpNumberOfBytesWritten = NULL; // Fuck you MSVC. 636 | WriteProcessMemory(this->m_hTargetProcessHandle, (LPVOID)m_WriteAddress, (LPCVOID)m_WriteValue, sizeof(S), &lpNumberOfBytesWritten); 637 | return lpNumberOfBytesWritten; 638 | } 639 | 640 | // WriteProcessMemory implementation. 641 | template std::size_t Write(std::uintptr_t m_WriteAddress, S m_WriteValue, std::size_t m_WriteSize) 642 | { 643 | SIZE_T lpNumberOfBytesWritten = NULL; // Fuck you MSVC. 644 | WriteProcessMemory(this->m_hTargetProcessHandle, (LPVOID)m_WriteAddress, (LPCVOID)m_WriteValue, m_WriteSize, &lpNumberOfBytesWritten); 645 | return lpNumberOfBytesWritten; 646 | } 647 | 648 | // VirtualAllocEx implementation. 649 | std::uintptr_t Allocate(std::size_t m_AllocationSize, DWORD m_dProtectionType, bool m_bShouldTrack = true); 650 | 651 | // VirtualFreeEx implementation. 652 | bool Free(std::uintptr_t m_FreeAddress); 653 | 654 | // VirtualQueryEx implementation. 655 | std::size_t Query(LPCVOID m_QueryAddress, MEMORY_BASIC_INFORMATION* m_MemoryInformation); 656 | 657 | // _CreateRemoteThread implementation. 658 | std::int32_t _CreateRemoteThread(LPVOID m_lpStartAddress, LPVOID m_lpParameter); 659 | 660 | // GetModule implementation. 661 | Module_t& GetModule(const char* m_szModuleName, std::int32_t m_ParseType = PEHeaderData_t::PEHEADER_PARSING_TYPE::TYPE_ALL); 662 | }; 663 | 664 | // Address helper class 665 | class Address_t 666 | { 667 | private: 668 | uintptr_t m_dAddress = NULL; 669 | public: 670 | Address_t() { }; 671 | 672 | // Templated ctor. 673 | template Address_t(const T m_Address) 674 | { 675 | m_dAddress = CH_R_CAST(m_Address); 676 | } 677 | 678 | // Operator. 679 | bool operator==(const Address_t& m_Address) const 680 | { 681 | return m_dAddress == m_Address.Get(); 682 | } 683 | 684 | // Operator. 685 | bool operator!=(const Address_t& m_Address) const 686 | { 687 | return m_dAddress != m_Address.Get(); 688 | } 689 | 690 | // Getter. 691 | template T Get() const 692 | { 693 | return m_dAddress ? T(m_dAddress) : T(); 694 | } 695 | 696 | // Dereferences one time and casts. 697 | template T& To() 698 | { 699 | return *CH_R_CAST(m_dAddress); 700 | } 701 | 702 | // Offset current address. 703 | template T Offset(std::ptrdiff_t m_Offset) 704 | { 705 | return m_dAddress ? CH_R_CAST(m_dAddress + m_Offset) : T(); 706 | } 707 | 708 | // Dereferences address X times. 709 | template T Deref(std::size_t m_nCount) 710 | { 711 | if (!m_dAddress) { 712 | CH_LOG("Invalid address called @Address_t::Deref."); 713 | return T(); 714 | } 715 | 716 | std::uintptr_t m_Address = m_dAddress; 717 | while (m_nCount--) 718 | if (m_Address) 719 | m_Address = *CH_R_CAST(m_Address); 720 | 721 | return CH_R_CAST(m_Address); 722 | } 723 | 724 | // Follows relative jmp. - E8 725 | template T Relative(std::ptrdiff_t m_Offset) 726 | { 727 | if (!m_dAddress) { 728 | CH_LOG("Invalid address called @Address_t::Relative."); 729 | return T(); 730 | } 731 | 732 | const std::uintptr_t m_Address = m_dAddress + m_Offset; 733 | const std::ptrdiff_t m_RelativeOffset = *CH_R_CAST(m_Address); 734 | if (!m_RelativeOffset) { 735 | CH_LOG("Invalid relative offset @Address_t::Relative."); 736 | return T(); 737 | } 738 | 739 | return CH_R_CAST(m_Address + m_RelativeOffset + sizeof(uint32_t)); 740 | } 741 | }; 742 | 743 | namespace math 744 | { 745 | class Color 746 | { 747 | public: 748 | float r = 0.0f; 749 | float g = 0.0f; 750 | float b = 0.0f; 751 | float a = 255.0f; 752 | public: 753 | Color() : r{}, g{}, b{}, a{} { } 754 | Color(float _r, float _g, float _b, float _a) : r{ _r }, g{ _g }, b{ _b }, a{ _a } { } 755 | 756 | bool operator==(const Color& v) const { return v.r == this->r && v.g == this->g && v.b == this->b && v.a == this->a; } 757 | bool operator!=(const Color& v) const { return v.r != this->r || v.g != this->g || v.b != this->b || v.a != this->a; } 758 | 759 | void Reset(float _r = 0.0f, float _g = 0.0f, float _b = 0.0f, float _a = 0.0f) { this->r = _r; this->g = _g; this->b = _b; this->a = _a; } 760 | }; 761 | 762 | class Vector2D 763 | { 764 | public: 765 | int x = 0; 766 | int y = 0; 767 | public: 768 | Vector2D() : x{}, y{} { } 769 | Vector2D(int _x, int _y) : x{ _x }, y{ _y } { } 770 | 771 | bool operator==(const Vector2D& v) const { return v.x == this->x && v.y == this->y; } 772 | bool operator!=(const Vector2D& v) const { return v.x != this->x || v.y != this->y; } 773 | 774 | // TODO: more arithmetic operators, too lazy to type it all out rn. 775 | Vector2D operator-(const Vector2D& v) const { return { this->x - v.x, this->y - v.y}; } 776 | Vector2D operator+(const Vector2D& v) const { return { this->x + v.x, this->y + v.y }; } 777 | Vector2D operator-=(const Vector2D& v) { return { this->x -= v.x, this->y -= v.y }; } 778 | Vector2D operator+=(const Vector2D& v) { return { this->x += v.x, this->y += v.y }; } 779 | Vector2D operator/=(const Vector2D& v) { return { this->x /= v.x, this->y /= v.y }; } 780 | Vector2D operator*=(const Vector2D& v) { return { this->x *= v.x, this->y *= v.y }; } 781 | 782 | void Reset(int _x = 0, int _y = 0) { this->x = _x; this->y = _y; } 783 | }; 784 | 785 | class Vector3D 786 | { 787 | public: 788 | float x = 0.0f; 789 | float y = 0.0f; 790 | float z = 0.0f; 791 | public: 792 | Vector3D() : x{}, y{}, z{} { } 793 | Vector3D(float _x, float _y, float _z) : x{ _x }, y{ _y }, z{ _z } { } 794 | 795 | bool operator==(const Vector3D& v) const { return v.x == this->x && v.y == this->y && v.z == this->z; } 796 | bool operator!=(const Vector3D& v) const { return v.x != this->x || v.y != this->y || v.z != this->z; } 797 | 798 | // TODO: more arithmetic operators, too lazy to type it all out rn. 799 | Vector3D operator-(const Vector3D& v) const { return { this->x - v.x, this->y - v.y, this->z - v.z }; } 800 | Vector3D operator+(const Vector3D& v) const { return { this->x + v.x, this->y + v.y, this->z + v.z }; } 801 | Vector3D operator-=(const Vector3D& v) { return { this->x -= v.x, this->y -= v.y, this->z -= v.z }; } 802 | Vector3D operator+=(const Vector3D& v) { return { this->x += v.x, this->y += v.y, this->z += v.z }; } 803 | Vector3D operator/=(const Vector3D& v) { return { this->x /= v.x, this->y /= v.y, this->z /= v.z }; } 804 | Vector3D operator*=(const Vector3D& v) { return { this->x *= v.x, this->y *= v.y, this->z *= v.z }; } 805 | 806 | bool IsValid() const { return this->x != 0.0f && this->y != 0.0f && this->z != 0.0f; } 807 | void Reset(float _x = 0.0f, float _y = 0.0f, float _z = 0.0f) { this->x = _x; this->y = _y; this->z = _z; } 808 | 809 | float Length() const { return std::sqrtf(this->LengthSqr()); } 810 | float LengthSqr() const { return (this->x * this->x) + (this->y * this->y) + (this->z * this->z); } 811 | float Length2DSqr() const { return (this->x * this->x) + (this->y * this->y); } 812 | float Length2D() const { return std::sqrtf(this->x * this->x + this->y * this->y); } 813 | float Distance(const Vector3D& v) const { Vector3D m_vecDelta = { this->x - v.x, this->y - v.y, this->z - v.z }; return m_vecDelta.Length2D(); } 814 | }; 815 | } 816 | 817 | namespace misc 818 | { 819 | #define XOR(str) []() { constexpr auto s = chdr::misc::StringEncryption(str); return s.Decrypted(); }() 820 | constexpr std::size_t COMPILETIME_SEED = (__TIME__[3] - '0') * 10 + (__TIME__[4] - '0'); // Temp, make this more unique lmfao. 821 | 822 | template 823 | class StringEncryption 824 | { 825 | std::int8_t m_Xored[nStringSize][2] = { 0 }; 826 | public: 827 | constexpr StringEncryption(const char* m_szToEncrypt) { 828 | for (std::size_t i = 0u; i < nStringSize; i++) { 829 | this->m_Xored[i][0] = CH_S_CAST(COMPILETIME_SEED * i); 830 | this->m_Xored[i][1] = m_szToEncrypt[i] ^ this->m_Xored[i][0]; 831 | } 832 | } 833 | 834 | const char* Decrypted() const { 835 | static char m_DecryptedData[nStringSize]; 836 | m_DecryptedData[0] = this->m_Xored[0][1]; 837 | 838 | for (std::size_t i = 1u; m_DecryptedData[i - 1u]; ++i) 839 | m_DecryptedData[i] = this->m_Xored[i][1] ^ this->m_Xored[i][0]; 840 | 841 | return m_DecryptedData; 842 | } 843 | }; 844 | 845 | #define CREATE_XORED_POINTER(type, ptr) chdr::misc::PointerEncryption(ptr); 846 | 847 | #pragma optimize( "", off ) 848 | template 849 | class PointerEncryption 850 | { 851 | std::uintptr_t m_Xored[2] = { 0 }; 852 | public: 853 | PointerEncryption(T* m_pToEncrypt) { 854 | this->m_Xored[0] = CH_R_CAST(m_pToEncrypt) ^ CH_R_CAST(m_pToEncrypt); 855 | this->m_Xored[1] = this->m_Xored[0] ^ (CH_R_CAST(m_pToEncrypt) + (COMPILETIME_SEED * 0xB00B1E)); 856 | } 857 | 858 | __forceinline T* operator->() { return this->Decrypted(); } 859 | __forceinline bool IsValid() const { return CH_R_CAST(this->Decrypted()) != NULL; } 860 | __forceinline T* Decrypted() const { return CH_R_CAST((this->m_Xored[0] ^ this->m_Xored[1]) - (COMPILETIME_SEED * 0xB00B1E)); } 861 | }; 862 | #pragma optimize( "", on ) 863 | 864 | #define ADD_SCOPE_HANDLER(a, b) chdr::misc::QueuedScopeHandler ScopeHandler(a, b); 865 | #define PUSH_SCOPE_HANDLER(a, b) ScopeHandler.AddToTail(a, b); 866 | 867 | // This is fine for now, but because the template is class-specific, you can't currently queue more than one type. 868 | template 869 | class QueuedScopeHandler 870 | { 871 | std::vector> m_QueuedCalls; 872 | public: 873 | QueuedScopeHandler(Callback call, Parameters ...param) { this->AddToTail( call, param...); } 874 | ~QueuedScopeHandler() { for (const auto& QueuedCalls : this->m_QueuedCalls) std::invoke(QueuedCalls.first, QueuedCalls.second); } 875 | 876 | void AddToTail(Callback call, Parameters ...param) { this->m_QueuedCalls.push_back({ call, param... }); } 877 | }; 878 | } 879 | } --------------------------------------------------------------------------------