From 19e12cf63ad53418333bdd28fe25a0bf957ca3b9 Mon Sep 17 00:00:00 2001 From: TheCruZ Date: Sun, 9 Feb 2025 03:44:30 +0100 Subject: [PATCH] Symbols refactor... --- .gitignore | 3 +- {Tools/SymChk => DbgHelp}/dbghelp.dll | Bin {Tools/SymChk => DbgHelp}/symsrv.dll | Bin README.MD | 8 +- SymbolsFromPDB/SimplestSymbolHandler.cpp | 113 +++++ SymbolsFromPDB/SimplestSymbolHandler.h | 25 + SymbolsFromPDB/Src/DataStructs.h | 31 -- SymbolsFromPDB/Src/Defines.h | 10 - SymbolsFromPDB/Src/SymbolsHandler.c | 459 ------------------ SymbolsFromPDB/Src/SymbolsHandler.h | 66 --- SymbolsFromPDB/Src/main.c | 20 - SymbolsFromPDB/SymbolsFromPDB.vcxproj | 8 +- SymbolsFromPDB/SymbolsFromPDB.vcxproj.filters | 12 +- SymbolsFromPDB/main.cpp | 91 ++++ Tools/SymCheck.bat | 104 ---- Tools/SymChk/SymbolCheck.dll | Bin 58936 -> 0 bytes Tools/SymChk/symchk.exe | Bin 99896 -> 0 bytes kdmapper/KDSymbolsHandler.cpp | 82 ++++ kdmapper/SymbolsHandler.cpp | 197 -------- kdmapper/include/DataStructs.hpp | 25 - kdmapper/include/KDSymbolsHandler.h | 35 ++ kdmapper/include/SymbolsHandler.hpp | 111 ----- kdmapper/include/utils.hpp | 5 +- kdmapper/intel_driver.cpp | 22 +- kdmapper/kdmapper.vcxproj | 9 +- kdmapper/kdmapper.vcxproj.filters | 7 +- kdmapper/main.cpp | 52 +- kdmapper/utils.cpp | 7 + 28 files changed, 418 insertions(+), 1084 deletions(-) rename {Tools/SymChk => DbgHelp}/dbghelp.dll (100%) rename {Tools/SymChk => DbgHelp}/symsrv.dll (100%) create mode 100644 SymbolsFromPDB/SimplestSymbolHandler.cpp create mode 100644 SymbolsFromPDB/SimplestSymbolHandler.h delete mode 100644 SymbolsFromPDB/Src/DataStructs.h delete mode 100644 SymbolsFromPDB/Src/Defines.h delete mode 100644 SymbolsFromPDB/Src/SymbolsHandler.c delete mode 100644 SymbolsFromPDB/Src/SymbolsHandler.h delete mode 100644 SymbolsFromPDB/Src/main.c create mode 100644 SymbolsFromPDB/main.cpp delete mode 100644 Tools/SymCheck.bat delete mode 100644 Tools/SymChk/SymbolCheck.dll delete mode 100644 Tools/SymChk/symchk.exe create mode 100644 kdmapper/KDSymbolsHandler.cpp delete mode 100644 kdmapper/SymbolsHandler.cpp delete mode 100644 kdmapper/include/DataStructs.hpp create mode 100644 kdmapper/include/KDSymbolsHandler.h delete mode 100644 kdmapper/include/SymbolsHandler.hpp diff --git a/.gitignore b/.gitignore index a9e6a59..6476787 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ kdmapper/x64 kdmapper/kdmapper.vcxproj.user x64 HelloWorld/.vs -Intermediate \ No newline at end of file +Intermediate +SymbolsFromPDB/offsets.ini \ No newline at end of file diff --git a/Tools/SymChk/dbghelp.dll b/DbgHelp/dbghelp.dll similarity index 100% rename from Tools/SymChk/dbghelp.dll rename to DbgHelp/dbghelp.dll diff --git a/Tools/SymChk/symsrv.dll b/DbgHelp/symsrv.dll similarity index 100% rename from Tools/SymChk/symsrv.dll rename to DbgHelp/symsrv.dll diff --git a/README.MD b/README.MD index 22f0a11..c5cebee 100644 --- a/README.MD +++ b/README.MD @@ -4,7 +4,7 @@ Original creator https://github.com/z175 Updated and improved by https://github.com/TheCruZ -PDB offsets parser written by https://github.com/Mohi-eddine +Initial PDB offsets parser written by https://github.com/Mohi-eddine Independent Pages allocation written by https://github.com/Herooyyy/ @@ -34,9 +34,9 @@ KDMapper is a simple tool that exploits iqvw64e.sys Intel driver to manually map Added param --PassAllocationPtr to pass allocation ptr as first param Added the possibility to modify params before call driver entry Now you can pass bytes directly to mapdriver function - Added PDB_OFFSETS macro that will allow the use of Offset PDB features (choose the target build or define in SymbolsHandler.hpp) - Added param --OffsetsPath "FilePath" to include your own offsets file path (if FilePath contains spaces, it must be enclosed in quotation marks) - Added param --DontUpdateOffset to execute without updating the offsets file (warning: you have to be sure that the offsets are not outdated to your current windows build, or you risk a potential BSOD) + Added PDB_OFFSETS macro that will allow the use of Offset PDB features (choose the target build) + Added param --offsetsPath "FilePath" to include your own offsets file path (if FilePath contains spaces, it must be enclosed in quotation marks) + Added param --dontUpdateOffsets to execute without updating the offsets file (warning: you have to be sure that the offsets are not outdated to your current windows build, or you risk a potential BSOD) Introduced new project "SymbolsFromPDB" that will help KDMapper to adapt quickly to any windows updates by parsing the target .PDB files (PDB_OFFSETS macro must be defined to use this feature) ## Requirements: diff --git a/SymbolsFromPDB/SimplestSymbolHandler.cpp b/SymbolsFromPDB/SimplestSymbolHandler.cpp new file mode 100644 index 0000000..0b24a25 --- /dev/null +++ b/SymbolsFromPDB/SimplestSymbolHandler.cpp @@ -0,0 +1,113 @@ +#include "SimplestSymbolHandler.h" + +#include +#include +#include +#include +#include +#include +#pragma comment( lib, "dbghelp.lib" ) + + +SimplestSymbolHandler::SimplestSymbolHandler(std::wstring cachePath) { + process = (unsigned long long)GetCurrentProcess(); + SymInitializeW((HANDLE)process, (L"cache*" + cachePath + L";SRV*http://msdl.microsoft.com/download/symbols").c_str(), FALSE); + SymSetOptions(SYMOPT_EXACT_SYMBOLS | SYMOPT_DEBUG); +} + +SimplestSymbolHandler::~SimplestSymbolHandler() { + SymCleanup((HANDLE)process); +} + +std::wstring SimplestSymbolHandler::GetPDB(const std::wstring binaryPath) { + + SYMSRV_INDEX_INFOW info{}; + info.sizeofstruct = sizeof(SYMSRV_INDEX_INFOW); + bool result = SymSrvGetFileIndexInfoW(binaryPath.c_str(), &info, 0); + if (!result) + { + std::wcout << "[-] Failed to find binary info. Error: " << GetLastError() << std::endl; + return {}; + } + + void* id; + DWORD idType; + if (info.guid == GUID{}) { // GUID is zeroed + id = &info.sig; + idType = SSRVOPT_DWORDPTR; + } + else { + id = &info.guid; + idType = SSRVOPT_GUIDPTR; + } + + auto pdbPath = std::make_unique(4096); + bool found = SymFindFileInPathW( + (HANDLE)process, + NULL, + info.pdbfile, + id, + info.age, + 0, + idType, + pdbPath.get(), + NULL, + NULL + ); + + return std::wstring(pdbPath.get()); +} + +std::vector SimplestSymbolHandler::GetOffset(std::wstring pdbPath, std::vector symbolName) { + + DWORD64 BaseAddr = 0x40000; + DWORD FileSize = (DWORD)std::filesystem::file_size(pdbPath); + + // Load symbols for the module + std::wcout << "[+] Loading Symbols From " << pdbPath << std::endl; + DWORD64 ModBase = SymLoadModuleExW( + (HANDLE)process, // Process handle of the current process + NULL, // Handle to the module's image file (not needed) + pdbPath.c_str(), // Path/name of the file + NULL, // User-defined short name of the module (it can be NULL) + BaseAddr, // Base address of the module (cannot be NULL if .PDB file is used, otherwise it can be NULL) + FileSize, // Size of the file (cannot be NULL if .PDB file is used, otherwise it can be NULL) + NULL, + NULL + ); + + if (ModBase == 0) + { + std::wcout << "[-] Error: SymLoadModule64() failed. Error code: " << GetLastError() << std::endl; + return {}; + } + + + SYMBOL_INFO_PACKAGEW SymInfoPackage{}; + SymInfoPackage.si.SizeOfStruct = sizeof(SYMBOL_INFOW); + SymInfoPackage.si.MaxNameLen = sizeof(SymInfoPackage.name);//MAX_SYM_NAME + 1; + + std::vector offsets{}; + + for (const auto& sym : symbolName) { + BOOL bRet = SymFromNameW( + (HANDLE)process, + sym.c_str(), + &SymInfoPackage.si + ); + if (!bRet || !SymInfoPackage.si.Address) + { + std::wcout << "[-] Error: SymFromName() failed. Sym: " << sym << " || Error code: " << GetLastError() << std::endl; + SymUnloadModule64(GetCurrentProcess(), ModBase); + return {}; + } + + // Display information about the symbol + std::wcout << "[+] Symbol " << sym << " Offset: " << (DWORD)(SymInfoPackage.si.Address - ModBase) << std::endl; + offsets.push_back((DWORD)(SymInfoPackage.si.Address - ModBase)); + } + + SymUnloadModule64(GetCurrentProcess(), ModBase); + + return offsets; +} \ No newline at end of file diff --git a/SymbolsFromPDB/SimplestSymbolHandler.h b/SymbolsFromPDB/SimplestSymbolHandler.h new file mode 100644 index 0000000..38d5d6c --- /dev/null +++ b/SymbolsFromPDB/SimplestSymbolHandler.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#include + +struct SymNeeded { + std::wstring binaryName; + std::wstring symbolName; +}; + +//Require dbghelp.dll and symsrv.dll +class SimplestSymbolHandler { +public: + + SimplestSymbolHandler(std::wstring cachePath); + + ~SimplestSymbolHandler(); + + std::wstring GetPDB(const std::wstring binaryPath); + + std::vector GetOffset(std::wstring pdbPath, std::vector symbolName); + +private: + unsigned long long process; //HANDLE but we don't want to share the windows.h include +}; \ No newline at end of file diff --git a/SymbolsFromPDB/Src/DataStructs.h b/SymbolsFromPDB/Src/DataStructs.h deleted file mode 100644 index 6fc91a5..0000000 --- a/SymbolsFromPDB/Src/DataStructs.h +++ /dev/null @@ -1,31 +0,0 @@ -/* -##################### -# MoHieDDiNNe Codes # -##################### -*/ - -#pragma once - -#include "Defines.h" - -#define Elements_Count(Array,Type) (sizeof(Array) / sizeof(Type)) - -typedef struct _SYM_INFO -{ - PVOID SymbolAddress; - PCSTR SymbolName; - DWORD NameLen; - DWORD SymbolOffset; -}SYM_INFO, * PSYM_INFO; - -typedef struct _SYM_INFO_ARRAY -{ - PSYM_INFO SymbolsArray; - SIZE_T ElementsCount; -}SYM_INFO_ARRAY, * PSYM_INFO_ARRAY; - -typedef struct _SYMBOLS_DATA -{ - PCSTR PDBFileName; - SYM_INFO_ARRAY SymbolsInfoArray; -}SYMBOLS_DATA, *PSYMBOLS_DATA; diff --git a/SymbolsFromPDB/Src/Defines.h b/SymbolsFromPDB/Src/Defines.h deleted file mode 100644 index 92d9cf4..0000000 --- a/SymbolsFromPDB/Src/Defines.h +++ /dev/null @@ -1,10 +0,0 @@ -/* -##################### -# MoHieDDiNNe Codes # -##################### -*/ - -#pragma once -#include -#include - diff --git a/SymbolsFromPDB/Src/SymbolsHandler.c b/SymbolsFromPDB/Src/SymbolsHandler.c deleted file mode 100644 index 58edcce..0000000 --- a/SymbolsFromPDB/Src/SymbolsHandler.c +++ /dev/null @@ -1,459 +0,0 @@ -#include "SymbolsHandler.h" - -BOOLEAN GenerateOffsetFile() -{ - SYM_INFO NtOsKernelFunctionsInfo[] = { - {NULL,"MmAllocateIndependentPagesEx",0,0 }, - {NULL,"MmFreeIndependentPages",0,0 }, - {NULL,"PiDDBLock",0,0 }, - {NULL,"PiDDBCacheTable",0,0 }, - {NULL,"MmSetPageProtection",0,0 }, - //Keep Adding Here - }; - - SYM_INFO WdFilterFunctionsInfo[] = { - {NULL,"MpBmDocOpenRules",0,0 }, - {NULL,"MpFreeDriverInfoEx",0,0 }, - //Keep Adding Here - }; - - SYM_INFO CIFunctionsInfo[] = { - {NULL,"g_KernelHashBucketList",0,0 }, - {NULL,"g_HashCacheLock",0,0 }, - //Keep Adding Here - }; - - SYMBOLS_DATA SymsData[] = { - {NTOSKRNL_PATH ,NtOsKernelFunctionsInfo ,Elements_Count(NtOsKernelFunctionsInfo,SYM_INFO) }, - {WDFILTER_PATH ,WdFilterFunctionsInfo ,Elements_Count(WdFilterFunctionsInfo,SYM_INFO) }, - {CIDLL_PATH ,CIFunctionsInfo ,Elements_Count(CIFunctionsInfo,SYM_INFO) }, - }; - - for(int FunctionsInfoIdx = 0 ; FunctionsInfoIdx < Elements_Count(SymsData, SYMBOLS_DATA) ;++FunctionsInfoIdx) - { - printf("\n"); - if (!InitKernelSymbolsList(SymsData[FunctionsInfoIdx].PDBFileName, &SymsData[FunctionsInfoIdx].SymbolsInfoArray)) - { - printf("Error: Failed To Get One Or More Function Offset.\n"); - return FALSE; - } - } - - HANDLE FileHandle = CreateFile(SYM_OFFSETS_PATH, GENERIC_WRITE, FILE_SHARE_READ, - NULL, CREATE_ALWAYS, 0, NULL); - - if (!FileHandle || FileHandle == INVALID_HANDLE_VALUE) - { - printf("Error: Failed To Create Symbols Offset FileHandle.\n"); - return FALSE; - } - - PBYTE Buffer = NULL; - SIZE_T OffsetSize = 2 + sizeof(SymsData->SymbolsInfoArray.SymbolsArray->SymbolOffset) * 2; - - SIZE_T BufferSize = 0; - DWORD BufferOffset = 0; - - for (int FunctionsInfoIdx = 0; FunctionsInfoIdx < Elements_Count(SymsData, SYMBOLS_DATA); ++FunctionsInfoIdx) - { - //counting number of bytes - SIZE_T BytesCount = 0; - for (int FunctionIdx = 0; FunctionIdx < SymsData[FunctionsInfoIdx].SymbolsInfoArray.ElementsCount; ++FunctionIdx) - { - BytesCount += SymsData[FunctionsInfoIdx].SymbolsInfoArray.SymbolsArray[FunctionIdx].NameLen; - BytesCount += OffsetSize; - BytesCount += 2;//for '\n' and ',' - } - BufferSize += BytesCount; - } - - if (!BufferSize) - { - printf("Error: Failed To Get FileHandle Buffer Size.\n"); - goto FailExit; - } - - Buffer = malloc(BufferSize); - if (!Buffer) - { - printf("Error: Failed To Allocate Memory For FileHandle Buffer.\n"); - goto FailExit; - } - - for (int FunctionsInfoIdx = 0; FunctionsInfoIdx < Elements_Count(SymsData, SYMBOLS_DATA); ++FunctionsInfoIdx) - { - //writing data - for (int FunctionIdx = 0; FunctionIdx < SymsData[FunctionsInfoIdx].SymbolsInfoArray.ElementsCount; ++FunctionIdx) - { - memcpy(Buffer + BufferOffset, SymsData[FunctionsInfoIdx].SymbolsInfoArray.SymbolsArray[FunctionIdx].SymbolName, SymsData[FunctionsInfoIdx].SymbolsInfoArray.SymbolsArray[FunctionIdx].NameLen); - BufferOffset += SymsData[FunctionsInfoIdx].SymbolsInfoArray.SymbolsArray[FunctionIdx].NameLen; - - *(Buffer + BufferOffset) = ','; - ++BufferOffset; - - snprintf((Buffer + BufferOffset), (size_t)(1 + OffsetSize), "0x%08X", SymsData[FunctionsInfoIdx].SymbolsInfoArray.SymbolsArray[FunctionIdx].SymbolOffset);//+1 for NULL terminator - BufferOffset += OffsetSize; - - *(Buffer + BufferOffset) = '\n'; - ++BufferOffset; - } - } - - BOOL IsWritten = WriteFile( - FileHandle, - Buffer, - BufferSize, - NULL, - NULL); - - CloseHandle(FileHandle); - free(Buffer); - - if (!IsWritten) - { - printf("Error: Failed To Write To Symbols Offset FileHandle.\n"); - return FALSE; - } - return TRUE; - -FailExit: - CloseHandle(FileHandle); - return FALSE; -} - -BOOLEAN InitKernelSymbolsList( - IN PATH_TYPE FilePath, - IN OUT PSYM_INFO_ARRAY pSymbolsArray) -{ - if (!FilePath || !pSymbolsArray) - return FALSE; - - BOOL bRet = FALSE; - BOOL Result = TRUE; - - // Set options - DWORD Options = SymGetOptions(); - - // SYMOPT_DEBUG option asks DbgHelp to print additional troubleshooting - // messages to debug output - use the debugger's Debug Output window - // to view the messages - - Options |= SYMOPT_DEBUG; - SymSetOptions(Options); - - // Initialize DbgHelp and load symbols for all modules of the current process - - bRet = SymInitialize( - GetCurrentProcess(), // Process handle of the current process - NULL, // No user-defined search path -> use default - FALSE // Do not load symbols for modules in the current process - ); - - if (!bRet) - { - printf("Error: SymInitialize() failed. Error code: %u \n", GetLastError()); - return 0; - } - - do - { - // Determine the base address and the file size - PATH_TYPE pFileName = FilePath; - - DWORD64 BaseAddr = 0; - DWORD FileSize = 0; - - if (!GetFileParams(pFileName, &BaseAddr, &FileSize)) - { - printf(("Error: Cannot obtain file parameters (internal error).\n")); - Result = FALSE; - break; - } - - // Load symbols for the module -#ifdef UNICODE - printf("-> Loading Symbols From %ls ... \n", pFileName); - DWORD64 ModBase = SymLoadModuleExW( - GetCurrentProcess(), // Process handle of the current process - NULL, // Handle to the module's image file (not needed) - pFileName, // Path/name of the file - NULL, // User-defined short name of the module (it can be NULL) - BaseAddr, // Base address of the module (cannot be NULL if .PDB file is used, otherwise it can be NULL) - FileSize, // Size of the file (cannot be NULL if .PDB file is used, otherwise it can be NULL) - NULL, - NULL - ); -#else - printf("-> Loading Symbols From %s ... \n",pFileName); - DWORD64 ModBase = SymLoadModule64( - GetCurrentProcess(), // Process handle of the current process - NULL, // Handle to the module's image file (not needed) - pFileName, // Path/name of the file - NULL, // User-defined short name of the module (it can be NULL) - BaseAddr, // Base address of the module (cannot be NULL if .PDB file is used, otherwise it can be NULL) - FileSize // Size of the file (cannot be NULL if .PDB file is used, otherwise it can be NULL) - ); -#endif - if (ModBase == 0) - { - printf("Error: SymLoadModule64() failed. Error code: %u \n", GetLastError()); - Result = FALSE; - break; - } - -#ifndef NDEBUG - printf("Load address: %I64x \n", ModBase); -#endif - // Obtain and display information about loaded symbols -#ifndef NDEBUG - ShowSymbolInfo(ModBase); -#endif - - SYMBOL_INFO_PACKAGE SymInfoPackage = { NULL }; - SymInfoPackage.si.SizeOfStruct = sizeof(SYMBOL_INFO); - SymInfoPackage.si.MaxNameLen = sizeof(SymInfoPackage.name);//MAX_SYM_NAME + 1; - // space for the name of the symbol - for (int i = 0; i < pSymbolsArray->ElementsCount; ++i) - { - PCSTR SymName = pSymbolsArray->SymbolsArray[i].SymbolName; - bRet = SymFromName( - GetCurrentProcess(), // Process handle of the current process - SymName, // Symbol name - &SymInfoPackage.si // Address of the SYMBOL_INFO structure (inside "sip" object) - ); - - if (!bRet || !SymInfoPackage.si.Address) - { - printf("Error: SymFromName() failed. Sym: %s || Error code: %u \n", pSymbolsArray->SymbolsArray[i].SymbolName, GetLastError()); - Result = FALSE; - } - else - { - // Display information about the symbol - pSymbolsArray->SymbolsArray[i].SymbolOffset = (DWORD)(SymInfoPackage.si.Address - ModBase); - pSymbolsArray->SymbolsArray[i].NameLen = SymInfoPackage.si.NameLen; - printf("Symbol %s Offset: %X\n", SymName, pSymbolsArray->SymbolsArray[i].SymbolOffset); - } - } - // Unload symbols for the module - bRet = SymUnloadModule64(GetCurrentProcess(), ModBase); - if (!bRet) - { - printf("Error: Unload Symbols failed. Error code: %u \n", GetLastError()); - } - } while (0); - - // Deinitialize DbgHelp - bRet = SymCleanup(GetCurrentProcess()); - if (!bRet) - { - printf("Error: Sym Cleanup failed. Error code: %u \n", GetLastError()); - return 0; - } - // Complete - return Result; -} - - -/////////////////////////////////////////////////////////////////////////////// -// Functions -// - -BOOLEAN GetFileParams(IN - PATH_TYPE pFileName, - OUT uintptr_t* BaseAddr, - OUT DWORD* FileSize) -{ - // Check parameters - - if (!pFileName || !BaseAddr || !FileSize) - { - return FALSE; - } - - // Determine the extension of the file - PATH_TYPE szFileExt[_MAX_EXT] = { 0 }; - -#ifdef UNICODE - _wsplitpath_s(pFileName, NULL, 0, NULL, 0, NULL, 0, szFileExt, _MAX_EXT); -#else - _splitpath_s(pFileName, NULL, 0, NULL, 0, NULL, 0, szFileExt, _MAX_EXT); -#endif - - // Is it .PDB file ? -#ifdef UNICODE - if (_wcsicmp(szFileExt, L".PDB") == 0) -#else - if (_stricmp(szFileExt, ".PDB") == 0) -#endif - { - // Yes, it is a .PDB file - // Determine its size, and use a dummy base address - - *BaseAddr = 0x10000000; // it can be any non-zero value, but if we load symbols - // from more than one file, memory regions specified - // for different files should not overlap - // (region is "base address + file size") - - if (!_GetFileSize(pFileName, FileSize)) - { - return FALSE; - } - } - else - { - // It is not a .PDB file - // Base address and file size can be 0 - *BaseAddr = 0; - *FileSize = 0; - return FALSE; - } - // Complete - return TRUE; -} - -BOOLEAN _GetFileSize( - IN PATH_TYPE pFileName, - OUT DWORD* FileSize) -{ - // Check parameters - if (!pFileName || !FileSize) - { - return FALSE; - } - - // Open the file - HANDLE hFile = CreateFile(pFileName, GENERIC_READ, FILE_SHARE_READ, - NULL, OPEN_EXISTING, 0, NULL); - - if (hFile == INVALID_HANDLE_VALUE) - { - printf("CreateFile() failed. Error: %u \n", GetLastError()); - return FALSE; - } - - // Obtain the size of the file - *FileSize = GetFileSize(hFile, NULL); - if (*FileSize == INVALID_FILE_SIZE) - { - printf("GetFileSize() failed. Error: %u \n", GetLastError()); - // and continue ... - } - - // Close the file - if (!CloseHandle(hFile)) - { - printf("CloseHandle() failed. Error: %u \n", GetLastError()); - // and continue ... - } - // Complete - return (*FileSize != INVALID_FILE_SIZE); -} - -#ifndef NDEBUG -void ShowSymbolInfo( - IN uintptr_t ModBase) -{ - // Get module information - IMAGEHLP_MODULE ModuleInfo; - - memset(&ModuleInfo, 0, sizeof(ModuleInfo)); - - ModuleInfo.SizeOfStruct = sizeof(ModuleInfo); - - BOOL bRet = SymGetModuleInfo64(GetCurrentProcess(), ModBase, &ModuleInfo); - if (!bRet) - { - printf("Error: SymGetModuleInfo64() failed. Error code: %u \n", GetLastError()); - return; - } - - // Display information about symbols - // Kind of symbols - switch (ModuleInfo.SymType) - { - case SymNone: - printf("No symbols available for the module.\n"); - break; - - case SymExport: - printf("Loaded symbols: Exports\n"); - break; - - case SymCoff: - printf("Loaded symbols: COFF\n"); - break; - - case SymCv: - printf("Loaded symbols: CodeView\n"); - break; - - case SymSym: - printf("Loaded symbols: SYM\n"); - break; - - case SymVirtual: - printf("Loaded symbols: Virtual\n"); - break; - - case SymPdb: - printf("Loaded symbols: PDB\n"); - break; - - case SymDia: - printf("Loaded symbols: DIA\n"); - break; - - case SymDeferred: - printf("Loaded symbols : Deferred\n"); // not actually loaded - break; - - default: - printf("Loaded symbols: Unknown format.\n"); - break; - } - - // Image name - if (strlen(ModuleInfo.ImageName) > 0) - { - printf("Image name: %s \n", ModuleInfo.ImageName); - } - - // Loaded image name - if (strlen(ModuleInfo.LoadedImageName) > 0) - { - printf("Loaded image name: %s \n", ModuleInfo.LoadedImageName); - } - - // Loaded PDB name - if (strlen(ModuleInfo.LoadedPdbName) > 0) - { - printf("PDB file name: %s \n", ModuleInfo.LoadedPdbName); - } - - // Is debug information unmatched ? - // (It can only happen if the debug information is contained - // in a separate file (.DBG or .PDB) - if (ModuleInfo.PdbUnmatched || ModuleInfo.DbgUnmatched) - { - printf("Warning: Unmatched symbols. \n"); - } - - // Contents - - // Line numbers available ? - printf("Line numbers: %s \n", ModuleInfo.LineNumbers ? "Available" : "Not available"); - - // Global symbols available ? - printf("Global symbols: %s \n", ModuleInfo.GlobalSymbols ? "Available" : "Not available"); - - // Type information available ? - printf("Type information: %s \n", ModuleInfo.TypeInfo ? "Available" : "Not available"); - - // Source indexing available ? - printf("Source indexing: %s \n", ModuleInfo.SourceIndexed ? "Yes" : "No"); - - // Public symbols available ? - printf("Public symbols: %s \n", ModuleInfo.Publics ? "Available" : "Not available"); -} -#endif \ No newline at end of file diff --git a/SymbolsFromPDB/Src/SymbolsHandler.h b/SymbolsFromPDB/Src/SymbolsHandler.h deleted file mode 100644 index 06423b3..0000000 --- a/SymbolsFromPDB/Src/SymbolsHandler.h +++ /dev/null @@ -1,66 +0,0 @@ -/* -##################### -# MoHieDDiNNe Codes # -##################### -*/ - -#pragma once -/////////////////////////////////////////////////////////////////////////////// -// -// some of the following functions are imported made by: -// -// Author: Oleg Starodumov -// https://www.debuginfo.com/examples/dbghelpexamples.html -// - -#include "DataStructs.h" -#include -#pragma comment( lib, "dbghelp.lib" ) - - -#ifdef UNICODE -#define NTOSKRNL_PATH L".\\Tools\\Symbols\\ntkrnlmp.pdb" -#define WDFILTER_PATH L".\\Tools\\Symbols\\WdFilter.pdb" -#define CIDLL_PATH L".\\Tools\\Symbols\\ci.pdb" - -#define SYM_OFFSETS_PATH L"SymbolsOffset.txt" - -#define PATH_TYPE PCWSTR -#else -#define NTOSKRNL_PATH ".\\Tools\\Symbols\\ntkrnlmp.pdb" -#define WDFILTER_PATH ".\\Tools\\Symbols\\WdFilter.pdb" -#define CIDLL_PATH ".\\Tools\\Symbols\\ci.pdb" - -#define FUNCOFFSET_PATH "SymbolsOffset.txt" - -#define PATH_TYPE PCSTR -#endif - - - - -BOOLEAN GenerateOffsetFile( - void -); - -BOOLEAN InitKernelSymbolsList( - IN PATH_TYPE FilePath, - IN OUT PSYM_INFO_ARRAY pSymbolsArray -); - -BOOLEAN GetFileParams(IN - PATH_TYPE pFileName, - OUT uintptr_t* BaseAddr, - OUT DWORD* FileSize -); - -BOOLEAN _GetFileSize( - IN PATH_TYPE pFileName, - OUT DWORD* FileSize -); - -#ifndef NDEBUG -void ShowSymbolInfo( - IN uintptr_t ModBase -); -#endif \ No newline at end of file diff --git a/SymbolsFromPDB/Src/main.c b/SymbolsFromPDB/Src/main.c deleted file mode 100644 index 8e05662..0000000 --- a/SymbolsFromPDB/Src/main.c +++ /dev/null @@ -1,20 +0,0 @@ - -#include "SymbolsHandler.h" - -int main() -{ - if (system("\"\"%cd%\"\\Tools\\SymCheck.bat\"") != 0) - { - printf("\nSymCheck.bat Hash Failed.\n\n"); - system("pause"); - return -1; - } - - if(!GenerateOffsetFile()) - { - system("pause"); - return -1; - } - printf("\n>> Successfully Generated Offsets File. <<\n\n"); - return 0; -} \ No newline at end of file diff --git a/SymbolsFromPDB/SymbolsFromPDB.vcxproj b/SymbolsFromPDB/SymbolsFromPDB.vcxproj index 2691207..cbcd4c6 100644 --- a/SymbolsFromPDB/SymbolsFromPDB.vcxproj +++ b/SymbolsFromPDB/SymbolsFromPDB.vcxproj @@ -92,13 +92,11 @@ - - + + - - - + diff --git a/SymbolsFromPDB/SymbolsFromPDB.vcxproj.filters b/SymbolsFromPDB/SymbolsFromPDB.vcxproj.filters index fcccb4f..11e0eee 100644 --- a/SymbolsFromPDB/SymbolsFromPDB.vcxproj.filters +++ b/SymbolsFromPDB/SymbolsFromPDB.vcxproj.filters @@ -15,21 +15,15 @@ - + Source Files - + Source Files - - Header Files - - - Header Files - - + Header Files diff --git a/SymbolsFromPDB/main.cpp b/SymbolsFromPDB/main.cpp new file mode 100644 index 0000000..a7a3c96 --- /dev/null +++ b/SymbolsFromPDB/main.cpp @@ -0,0 +1,91 @@ +//TheCruZ Remake + +#include + +#include +#include +#include +#include +#include + +#include "SimplestSymbolHandler.h" + + +std::wstring GetCurrentAppFolder() { + wchar_t buffer[1024]; + GetModuleFileNameW(NULL, buffer, 1024); + std::wstring::size_type pos = std::wstring(buffer).find_last_of(L"\\/"); + return std::wstring(buffer).substr(0, pos); +} + +int main() +{ + std::vector targetBinaries = { + L"C:\\Windows\\System32\\ntoskrnl.exe", + L"C:\\Windows\\System32\\ci.dll" + }; + + if (std::filesystem::exists(L"C:\\Windows\\System32\\drivers\\WdFilter.sys")) { + targetBinaries.push_back(L"C:\\Windows\\System32\\drivers\\WdFilter.sys"); + } + else if (std::filesystem::exists(L"C:\\Windows\\System32\\drivers\\wd\\WdFilter.sys")) { + targetBinaries.push_back(L"C:\\Windows\\System32\\drivers\\wd\\WdFilter.sys"); + } + else { + std::cout << "[-] Error: WdFilter.sys not found." << std::endl; + return -1; + } + + std::vector symbolsToRetrieve{ + { L"ntoskrnl.exe", L"MmAllocateIndependentPagesEx" }, + { L"ntoskrnl.exe", L"MmFreeIndependentPages" }, + { L"ntoskrnl.exe", L"PiDDBLock" }, + { L"ntoskrnl.exe", L"PiDDBCacheTable" }, + { L"ntoskrnl.exe", L"MmSetPageProtection" }, + { L"WdFilter.sys", L"MpBmDocOpenRules" }, + { L"WdFilter.sys", L"MpFreeDriverInfoEx" }, + { L"ci.dll", L"g_KernelHashBucketList" }, + { L"ci.dll", L"g_HashCacheLock" }, + }; + + SimplestSymbolHandler handler(GetCurrentAppFolder() + L"\\Symbols"); + + for (const auto& binPath : targetBinaries) { + auto pdbPath = handler.GetPDB(binPath); + if (pdbPath.empty()) { + std::wcout << L"[-] Failed to get symbol for " << binPath << std::endl; + return -1; + } + + std::vector symbolsForThisFile{}; + for (const auto& sym : symbolsToRetrieve) { + if (binPath.find(sym.binaryName) == std::wstring::npos) { + continue; + } + symbolsForThisFile.push_back(sym.symbolName); + } + + auto offsets = handler.GetOffset(pdbPath, symbolsForThisFile); + if (offsets.size() != symbolsForThisFile.size()) { + std::wcout << L"[-] Failed to get offsets for " << binPath << std::endl; + return -1; + } + + + // Save as init format in offsets.ini + std::wofstream ofs(GetCurrentAppFolder() + L"\\offsets.ini", std::ofstream::out | std::ofstream::app); + if (!ofs.is_open()) { + std::wcout << L"[-] Failed to open offsets.ini" << std::endl; + return -1; + } + + auto filename = std::filesystem::path(binPath).filename().wstring(); + + ofs << L"[" << filename << L"]" << std::endl; + for (size_t i = 0; i < symbolsForThisFile.size(); i++) { + ofs << symbolsForThisFile[i] << L"=" << std::dec << offsets[i] << std::endl; + } + ofs << std::endl; + } + return 0; +} \ No newline at end of file diff --git a/Tools/SymCheck.bat b/Tools/SymCheck.bat deleted file mode 100644 index 79f3e56..0000000 --- a/Tools/SymCheck.bat +++ /dev/null @@ -1,104 +0,0 @@ -@ECHO OFF - -SETLOCAL - -set SymCheck="%cd%"\Tools\SymChk\symchk.exe -set SymbolsDir="%cd%"\Tools\Symbols\ -set MS_Files=%SymbolsDir%MS_Files\ - -set "PDB_Files[0]=ntkrnlmp.pdb" -set "PDB_Files[1]=WdFilter.pdb" -set "PDB_Files[2]=ci.pdb" - -set "FilesPath[0]=%systemroot%\System32\ntoskrnl.exe" -set "FilesPath[1]=%systemroot%\System32\drivers\wd\WdFilter.sys" -if not exist %FilesPath[1]% ( - set "FilesPath[1]=%systemroot%\System32\drivers\WdFilter.sys" -) -set "FilesPath[2]=%systemroot%\System32\ci.dll" - -set /a "Index=0" -:for_loop - call set Path=%%FilesPath[%Index%]%% - echo ################################# - echo Getting Symbols For %Path%... - %SymCheck% /r %Path% /s SRV*%MS_Files%*http://msdl.microsoft.com/download/symbols - echo ################################# - if %ERRORLEVEL% NEQ 0 ( - exit %ERRORLEVEL% - ) - call set Target=%%PDB_Files[%Index%]%% - dir /b /s %MS_Files%%Target%\ > fileslist.txt - if %ERRORLEVEL% NEQ 0 ( - exit %ERRORLEVEL% - ) - - setlocal EnableDelayedExpansion - set /a "Line=0" - set "Paths[0]=crap" - - for /f "tokens=* delims=" %%i in (fileslist.txt) do ( - if !Line! GTR 1 ( - echo ################################# - echo Symbols Conflict Deleting %MS_Files%%Target% And Retrying - echo ################################# - - rmdir /s /q %MS_Files%%Target% - if %ERRORLEVEL% NEQ 0 ( - pause - exit %ERRORLEVEL% - ) - if exist %SymbolsDir%%Target% ( - del %SymbolsDir%%Target% - if %ERRORLEVEL% NEQ 0 ( - pause - exit %ERRORLEVEL% - ) - ) - goto for_loop - ) - - set "Paths[!Line!]="%%i"" - set /a "Line+=1" - ) - - del fileslist.txt - if %ERRORLEVEL% NEQ 0 ( - exit %ERRORLEVEL% - ) - set SymPath=%Paths[1]% - if not exist %SymPath% ( - echo ################################# - echo - Symbols File Does Not Exist: %SymPath% - echo - Warning: If You Force The Software To Run Your PC Will Crash - echo ################################# - if exist %SymbolsDir%%Target% (del "%SymbolsDir%%Target%") - pause - exit -1 - ) - - if not exist %SymbolsDir%%Target% ( - echo copying %SymPath% to "%SymbolsDir% - %systemroot%\System32\xcopy /q/y %SymPath% %SymbolsDir% - if not exist %SymbolsDir%%Target% ( - echo ################################# - echo - Failed To Copy PDB File: %Target% - echo - Warning: If You Force The Software To Run Your PC Will Crash - echo ################################# - pause - exit -1 - ) - ) - - set /a "Index+=1" - if not defined PDB_Files[!Index!] goto exit_for_loop -goto for_loop -:exit_for_loop - -echo ################################# -echo Batch Script Done! -echo ################################# - -ENDLOCAL -::pause -exit %ERRORLEVEL% \ No newline at end of file diff --git a/Tools/SymChk/SymbolCheck.dll b/Tools/SymChk/SymbolCheck.dll deleted file mode 100644 index 20b617a0364c2345c8b1a216846398773968c9b6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 58936 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 diff --git a/Tools/SymChk/symchk.exe b/Tools/SymChk/symchk.exe deleted file mode 100644 index 2d04ace1f7eb6ce4f72ec0c2a0c757a5bdc16464..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 99896 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

Ny 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 +#include +#include +#include +#include + +#include "utils.hpp" + +bool KDSymbolsHandler::ReloadFile(std::wstring path, std::wstring updater) { + symbols.clear(); + + if (updater.empty() && !std::filesystem::exists(path) || + !updater.empty() && !std::filesystem::exists(updater)) { + std::wcout << L"[-] Offsets file (" << path << L") or updater (" << updater << L") not found" << std::endl; + return false; + } + + if (!updater.empty()) { + path = utils::GetCurrentAppFolder() + L"\\offsets.ini"; //default name if update is requested + + //delete old file + std::filesystem::remove(path); + + std::string cmd = "\"" + std::string(updater.begin(), updater.end()) + "\""; + auto exitCode = system(cmd.c_str()); + if (exitCode != 0) { + std::wcout << L"[-] Failed to update offsets" << std::endl; + return false; + } + } + + //load file with ini format + std::wifstream file(path); + if (!file.is_open()) { + std::wcout << L"[-] Failed to open offsets file" << std::endl; + return false; + } + + std::wstring line; + std::wstring currentSection; + + while (std::getline(file, line)) { + if (line.empty() || line[0] == L';' || line[0] == L'#') { + continue; + } + if (line[0] == L'[') { + currentSection = line.substr(1, line.size() - 2); + continue; + } + auto pos = line.find(L'='); + if (pos == std::wstring::npos) { + continue; + } + auto name = line.substr(0, pos); + auto offset = line.substr(pos + 1); + symOffset sym{}; + sym.name = name; + sym.offset = std::stoull(offset, nullptr, 10); + std::wcout << L"[+] Loaded " << currentSection << L" - " << sym.name << L" - 0x" << std::hex << sym.offset << std::endl; + symbols.push_back(sym); + } + + std::wcout << L"[+] " << symbols.size() << L" Symbols Loaded" << std::endl; + return true; +} + +unsigned long long KDSymbolsHandler::GetOffset(std::wstring name) { + for (auto sym : symbols) { + if (sym.name == name) { + return sym.offset; + } + } + return 0; +} + +#endif \ No newline at end of file diff --git a/kdmapper/SymbolsHandler.cpp b/kdmapper/SymbolsHandler.cpp deleted file mode 100644 index a021eb9..0000000 --- a/kdmapper/SymbolsHandler.cpp +++ /dev/null @@ -1,197 +0,0 @@ -#include "SymbolsHandler.hpp" - -//for use in KDMapper - -#ifdef PDB_OFFSETS - -std::vector SymbolsInfoArray{}; -PTCHAR SymbolsOffsetFilePath = NULL; - -inline DWORD Crc32Str(IN PCSTR Str) -{ - if (!Str) - return 0; - - DWORD Crc32 = 0; - size_t Index = 0; - while (*(Str + Index)) - { - Crc32 = _mm_crc32_u8(Crc32, *(Str + Index)); - ++Index; - } - - return Crc32; -} - -BOOL GetSymbolsInfoFromFile(OUT std::vector *pSymInfoArray, IN OPTIONAL bool UpdateOffsetsFile) -{ - if (!pSymInfoArray) - { - printf("[-] Error: SymInfoArray Ptr is NULL.\n"); - return FALSE; - } - pSymInfoArray->clear(); - - HANDLE hFile; - bool Failed = false; - do { - if (SymbolsOffsetFilePath) - { - hFile = CreateFile(SymbolsOffsetFilePath, GENERIC_READ, FILE_SHARE_READ, - NULL, OPEN_EXISTING, 0, NULL); - break; - } - - RetryGeneratingFile: - if (Failed || UpdateOffsetsFile) - { - printf("[+] Generating Default Offsets File\n"); - if (system(SYM_FROM_PDB_EXE) != 0) - { - printf("[-] Failed To Generate Symbols Offset File.\n"); - return FALSE; - } - } - TryDefault: - hFile = CreateFile(SYM_OFFSETS_PATH, GENERIC_READ, FILE_SHARE_READ, - NULL, OPEN_EXISTING, 0, NULL); - } while (0); - - if (!hFile || hFile == INVALID_HANDLE_VALUE) - { - if (!Failed) - { - if (SymbolsOffsetFilePath) - { - printf("[>] Warning: The Supplied File Path Is Invalid --> Switching To Default Offsets File.\n"); - SymbolsOffsetFilePath = NULL;//Reset The Global Path Variable - goto TryDefault; - } - - printf("[>] Warning: Failed To Access Default Offsets File --> Default File Is Not Valid.\n"); - Failed = true; - goto RetryGeneratingFile; - } -#ifdef UNICODE - printf(("[-] Failed To Open Offsets File: %ls.\n-> Error: %u \n"), SYM_OFFSETS_PATH, GetLastError()); -#else - printf(("[-] Failed To Open Offsets File: %s.\n-> Error: %u \n"), FUNCOFFSET_PATH, GetLastError()); -#endif - return FALSE; - } - - DWORD FileSize = GetFileSize(hFile, NULL); - if (!FileSize) - { - printf("[-] Error: Offset File Is Empty.\n"); - CloseHandle(hFile); - return FALSE; - } - - std::unique_ptr BufferLocalPtr(new char[FileSize]); - PCHAR Buffer = BufferLocalPtr.get(); - if (!Buffer) - { - printf("[-] Error: Failed To Allocate Memory For Offsets File Buffer.\n"); - CloseHandle(hFile); - return FALSE; - } - - BOOL IsRead = ReadFile(hFile, Buffer, FileSize, NULL, NULL); - CloseHandle(hFile); - if (!IsRead) - { - printf("[-] Error: Failed To Read Data From Offsets File.\n"); - return FALSE; - } - - SIZE_T Index = 0; - SIZE_T LinesCount = 0; - do { - if (Buffer[Index] == '\n') - { - ++LinesCount; - } - ++Index; - } while (Index < FileSize); - - if (!LinesCount) - { - printf("[-] Error: Failed To Get Lines Count In Offsets File.\n"); - return FALSE; - } - - pSymInfoArray->reserve(LinesCount); - Index = 0; - - SIZE_T Count = 0; - SIZE_T Line = 0; - SIZE_T CommaCount = 0; - SYM_INFO SymInfo; - do { - if (Buffer[Index] == ',') - { - std::string SymName(&Buffer[Index - Count], &Buffer[Index]); - SymInfo.Crc32Hash = Crc32Str(SymName.c_str()); - Count = 0; - ++CommaCount; - } - else if (Buffer[Index] == '\n') - { - Buffer[Index] = '\0'; - DWORD Offset = NULL; - sscanf_s((PCSTR)(Buffer + Index - Count), "0x%X", &Offset); - SymInfo.SymbolOffset = Offset; - pSymInfoArray->emplace_back(SymInfo); - - SymInfo.Crc32Hash = 0; - SymInfo.Crc32Hash = 0; - ++Line; - Count = 0; - } - else - { - if (Count > MAX_SYM_NAME_LENGTH) - { - printf("Error: Max Symbol Length Exceeded.\n"); - return FALSE; - } - ++Count; - } - - ++Index; - } while (Index < FileSize); - - if (!Line || (Line != CommaCount) || (Index != FileSize)) - { - return FALSE; - } - return TRUE; -} - -DWORD GetSymbolOffsetByHash(IN const std::vector& SymInfoArray, IN DWORD SymHash) -{ - if (SymInfoArray.empty() || !SymHash) - { - printf("[-] Error: Invalid Parameter.\n"); - return 0; - } - - for (SYM_INFO SymInfo:SymInfoArray) - { - if (SymInfo.Crc32Hash == SymHash) - { - return SymInfo.SymbolOffset; - } - } - - return 0; -} - -DWORD GetSymbolOffsetByName(IN const std::vector& SymInfoArray, IN const std::string& SymName) -{ - DWORD Hash = Crc32Str(SymName.c_str()); - return GetSymbolOffsetByHash(SymInfoArray, Hash); -} - -#endif \ No newline at end of file diff --git a/kdmapper/include/DataStructs.hpp b/kdmapper/include/DataStructs.hpp deleted file mode 100644 index 299804c..0000000 --- a/kdmapper/include/DataStructs.hpp +++ /dev/null @@ -1,25 +0,0 @@ -/* -##################### -# MoHieDDiNNe Codes # -##################### -*/ - -//for use in KDMapper - -#pragma once - -#define Elements_Count(Array,Type) (sizeof(Array) / sizeof(Type)) - - -typedef struct _SYM_INFO -{ - //PCSTR SymbolName; - DWORD SymbolOffset; - DWORD Crc32Hash; -}SYM_INFO, * PSYM_INFO; - -//typedef struct SYM_INFO_ARRAY -//{ -// PSYM_INFO SymbolsArray; -// SIZE_T ElementsCount; -//}SYM_INFO_ARRAY, * PSYM_INFO_ARRAY; diff --git a/kdmapper/include/KDSymbolsHandler.h b/kdmapper/include/KDSymbolsHandler.h new file mode 100644 index 0000000..a13cd14 --- /dev/null +++ b/kdmapper/include/KDSymbolsHandler.h @@ -0,0 +1,35 @@ +//TheCruZ Remake + +#ifdef PDB_OFFSETS + +#include +#include + +#define SYM_FROM_PDB_EXE L"SymbolsFromPDB.exe" + +struct symOffset { + unsigned long long offset{}; + std::wstring name{}; +}; + +class KDSymbolsHandler +{ + +public: + KDSymbolsHandler() { + }; + + static KDSymbolsHandler* GetInstance() + { + static KDSymbolsHandler instance{}; + return &instance; + } + + bool ReloadFile(std::wstring path, std::wstring updater); + unsigned long long GetOffset(std::wstring name); + +private: + std::vector symbols{}; +}; + +#endif \ No newline at end of file diff --git a/kdmapper/include/SymbolsHandler.hpp b/kdmapper/include/SymbolsHandler.hpp deleted file mode 100644 index 773e890..0000000 --- a/kdmapper/include/SymbolsHandler.hpp +++ /dev/null @@ -1,111 +0,0 @@ -/* -##################### -# MoHieDDiNNe Codes # -##################### -*/ - -//for use in KDMapper - -#pragma once -#include -#include -#include -#include -#include //probably already included somewhere - - -#include "DataStructs.hpp" - -/////////////////////////////////// -//This macro is used to enable the use of the SymbolsHandler functions,if for any reasons you don't want to use PDB Offsets, you can still use the original method of sig scanning the kernel to get the target addresses -///*#####################*/ - -//#define PDB_OFFSETS - -///*#####################*/ -/////////////////////////////////// - - -#ifdef PDB_OFFSETS - -/////////////////////// -//macro defines -#define MAX_SYM_NAME_LENGTH (DWORD)50 //could be increased if some symbols name are over 50 characters - -#ifdef DEBUG -#ifdef UNICODE -#define SYM_OFFSETS_PATH (PCWSTR)L"..\\Bin\\SymbolsOffset.txt" -#else -#define SYM_OFFSETS_PATH (PCSTR)"..\\Bin\\SymbolsOffset.txt" -#endif -#else -#ifdef UNICODE -#define SYM_OFFSETS_PATH (PCWSTR)L".\\SymbolsOffset.txt" -#else -#define SYM_OFFSETS_PATH (PCSTR)".\\SymbolsOffset.txt" -#endif -#endif - -#ifdef DEBUG -#define SYM_FROM_PDB_EXE (PCSTR)"cd ..\\Bin\\ && SymbolsFromPDB_Debug.exe" -#else -#define SYM_FROM_PDB_EXE (PCSTR)"SymbolsFromPDB.exe" -#endif -/////////////////////// - -extern std::vector SymbolsInfoArray; -extern PTCHAR SymbolsOffsetFilePath; - -inline DWORD Crc32Str( - IN PCSTR Str -); - -BOOL GetSymbolsInfoFromFile( - OUT std::vector* pSymInfoArray, - IN OPTIONAL bool UpdateOffsetsFile = true -); - -DWORD GetSymbolOffsetByHash( - IN const std::vector& pSymInfoArray, - IN DWORD SymHash -); - -DWORD GetSymbolOffsetByName( - IN const std::vector& pSymInfoArray, - IN const std::string& SymName -); - -//just need this class to init the Symbols Info Array and clear it after main is done (usage of RAII). -class CSymInfo -{ - std::vector* m_pSymInfoArray{}; -public: - bool m_IsValid = false; - -public: - CSymInfo(std::vector *pSymInfoArray) - { - m_IsValid = GetSymbolsInfoFromFile(pSymInfoArray); - if(m_IsValid) - { - m_pSymInfoArray = pSymInfoArray; - } - } - - CSymInfo(std::vector* pSymInfoArray ,const bool UpdateOffsetsFile) - { - m_IsValid = GetSymbolsInfoFromFile(pSymInfoArray, UpdateOffsetsFile); - if (m_IsValid) - { - m_pSymInfoArray = pSymInfoArray; - } - } - - ~CSymInfo() - { - if (m_pSymInfoArray) - m_pSymInfoArray->clear(); - } -}; - -#endif \ No newline at end of file diff --git a/kdmapper/include/utils.hpp b/kdmapper/include/utils.hpp index e6a404f..e611cd4 100644 --- a/kdmapper/include/utils.hpp +++ b/kdmapper/include/utils.hpp @@ -16,9 +16,7 @@ #include #include "nt.hpp" -#ifdef PDB_OFFSETS -#include "SymbolsHandler.hpp" -#endif + namespace utils { @@ -29,4 +27,5 @@ namespace utils BOOLEAN bDataCompare(const BYTE* pData, const BYTE* bMask, const char* szMask); uintptr_t FindPattern(uintptr_t dwAddress, uintptr_t dwLen, BYTE* bMask, const char* szMask); PVOID FindSection(const char* sectionName, uintptr_t modulePtr, PULONG size); + std::wstring GetCurrentAppFolder(); } \ No newline at end of file diff --git a/kdmapper/intel_driver.cpp b/kdmapper/intel_driver.cpp index 83f3a99..6e76565 100644 --- a/kdmapper/intel_driver.cpp +++ b/kdmapper/intel_driver.cpp @@ -1,5 +1,9 @@ #include "intel_driver.hpp" +#ifdef PDB_OFFSETS +#include "KDSymbolsHandler.h" +#endif + ULONG64 intel_driver::ntoskrnlAddr = 0; char intel_driver::driver_name[100] = {}; @@ -153,7 +157,7 @@ bool intel_driver::ClearWdFilterDriverList(HANDLE device_handle) { } #ifdef PDB_OFFSETS - uintptr_t MpBmDocOpenRules = GetSymbolOffsetByName(SymbolsInfoArray, "MpBmDocOpenRules"); + uintptr_t MpBmDocOpenRules = KDSymbolsHandler::GetInstance()->GetOffset(L"MpBmDocOpenRules"); if (!MpBmDocOpenRules) { Log("[-] Failed To Get MpBmDocOpenRules." << std::endl); @@ -166,7 +170,7 @@ bool intel_driver::ClearWdFilterDriverList(HANDLE device_handle) { uintptr_t RuntimeDriversArray = MpBmDocOpenRules + 0x68; ReadMemory(device_handle, RuntimeDriversArray, &RuntimeDriversArray, sizeof(uintptr_t)); - uintptr_t MpFreeDriverInfoEx = GetSymbolOffsetByName(SymbolsInfoArray,"MpFreeDriverInfoEx"); + uintptr_t MpFreeDriverInfoEx = KDSymbolsHandler::GetInstance()->GetOffset(L"MpFreeDriverInfoEx"); if (!MpFreeDriverInfoEx) { Log("[-] Failed To Get MpFreeDriverInfoEx." << std::endl); @@ -457,7 +461,7 @@ uint64_t intel_driver::MmAllocateIndependentPagesEx(HANDLE device_handle, uint32 #ifdef PDB_OFFSETS if (!kernel_MmAllocateIndependentPagesEx) { - kernel_MmAllocateIndependentPagesEx = GetSymbolOffsetByName(SymbolsInfoArray,"MmAllocateIndependentPagesEx"); + kernel_MmAllocateIndependentPagesEx = KDSymbolsHandler::GetInstance()->GetOffset(L"MmAllocateIndependentPagesEx"); if (!kernel_MmAllocateIndependentPagesEx) { Log(L"[!] Failed to find MmAllocateIndependentPagesEx" << std::endl); return 0; @@ -500,7 +504,7 @@ bool intel_driver::MmFreeIndependentPages(HANDLE device_handle, uint64_t address if (!kernel_MmFreeIndependentPages) { #ifdef PDB_OFFSETS - kernel_MmFreeIndependentPages = GetSymbolOffsetByName(SymbolsInfoArray, "MmFreeIndependentPages"); + kernel_MmFreeIndependentPages = KDSymbolsHandler::GetInstance()->GetOffset(L"MmFreeIndependentPages"); #else kernel_MmFreeIndependentPages = intel_driver::FindPatternInSectionAtKernel(device_handle, "PAGE", intel_driver::ntoskrnlAddr, (BYTE*)"\xBA\x00\x60\x00\x00\x48\x8B\xCB\xE8\x00\x00\x00\x00\x48\x8D\x8B\x00\xF0\xFF\xFF", @@ -538,7 +542,7 @@ BOOLEAN intel_driver::MmSetPageProtection(HANDLE device_handle, uint64_t address if (!kernel_MmSetPageProtection) { #ifdef PDB_OFFSETS - kernel_MmSetPageProtection = GetSymbolOffsetByName(SymbolsInfoArray, "MmSetPageProtection"); + kernel_MmSetPageProtection = KDSymbolsHandler::GetInstance()->GetOffset(L"MmSetPageProtection"); if (!kernel_MmSetPageProtection) { Log(L"[!] Failed to find MmSetPageProtection" << std::endl); return FALSE; @@ -850,14 +854,14 @@ intel_driver::PiDDBCacheEntry* intel_driver::LookupEntry(HANDLE device_handle, P bool intel_driver::ClearPiDDBCacheTable(HANDLE device_handle) { //PiDDBCacheTable added on LoadDriver #ifdef PDB_OFFSETS - DWORD PiDDBLockOffset = GetSymbolOffsetByName(SymbolsInfoArray, "PiDDBLock"); + DWORD PiDDBLockOffset = KDSymbolsHandler::GetInstance()->GetOffset(L"PiDDBLock"); if (!PiDDBLockOffset) { Log(L"[-] Warning PiDDBLock not found" << std::endl); return false; } - DWORD PiDDBCacheTableOffset = GetSymbolOffsetByName(SymbolsInfoArray, "PiDDBCacheTable"); + DWORD PiDDBCacheTableOffset = KDSymbolsHandler::GetInstance()->GetOffset(L"PiDDBCacheTable"); if (!PiDDBLockOffset) { Log(L"[-] Warning PiDDBCacheTable not found" << std::endl); @@ -1038,14 +1042,14 @@ bool intel_driver::ClearKernelHashBucketList(HANDLE device_handle) { //Thanks @KDIo3 and @Swiftik from UnknownCheats #ifdef PDB_OFFSETS - DWORD g_KernelHashBucketListOffset = GetSymbolOffsetByName(SymbolsInfoArray, "g_KernelHashBucketList"); + DWORD g_KernelHashBucketListOffset = KDSymbolsHandler::GetInstance()->GetOffset(L"g_KernelHashBucketList"); if (!g_KernelHashBucketListOffset) { Log(L"[-] Can't Find g_KernelHashBucketList Offset" << std::endl); return false; } - DWORD g_HashCacheLockOffset = GetSymbolOffsetByName(SymbolsInfoArray, "g_HashCacheLock"); + DWORD g_HashCacheLockOffset = KDSymbolsHandler::GetInstance()->GetOffset(L"g_HashCacheLock"); if (!g_KernelHashBucketListOffset) { Log(L"[-] Can't Find g_HashCacheLock Offset" << std::endl); diff --git a/kdmapper/kdmapper.vcxproj b/kdmapper/kdmapper.vcxproj index e362121..fe7600b 100644 --- a/kdmapper/kdmapper.vcxproj +++ b/kdmapper/kdmapper.vcxproj @@ -204,7 +204,7 @@ RequireAdministrator - xcopy "$(SolutionDir)Tools" "$(SolutionDir)$(Platform)\$(Configuration)\Tools" /E /H /C /Y /I + xcopy "$(SolutionDir)DbgHelp\*" "$(SolutionDir)$(Platform)\$(Configuration)" /H /C /Y @@ -248,7 +248,7 @@ RequireAdministrator - xcopy "$(SolutionDir)Tools" "$(SolutionDir)$(Platform)\lib\Tools" /E /H /C /Y /I + xcopy "$(SolutionDir)DbgHelp\*" "$(SolutionDir)$(Platform)\lib" /H /C /Y @@ -257,18 +257,17 @@ - + - - + diff --git a/kdmapper/kdmapper.vcxproj.filters b/kdmapper/kdmapper.vcxproj.filters index fc7191a..b5cb701 100644 --- a/kdmapper/kdmapper.vcxproj.filters +++ b/kdmapper/kdmapper.vcxproj.filters @@ -7,7 +7,7 @@ - + @@ -31,8 +31,9 @@ include - - + + include + diff --git a/kdmapper/main.cpp b/kdmapper/main.cpp index 3725a15..d747541 100644 --- a/kdmapper/main.cpp +++ b/kdmapper/main.cpp @@ -8,6 +8,10 @@ #include "kdmapper.hpp" +#ifdef PDB_OFFSETS +#include "KDSymbolsHandler.h" +#endif + HANDLE iqvw64e_device_handle; @@ -96,7 +100,7 @@ void PauseIfParentIsExplorer() { void help() { Log(L"\r\n\r\n[!] Incorrect Usage!" << std::endl); #ifdef PDB_OFFSETS - Log(L"[+] Usage: kdmapper.exe [--DontUpdateOffset | --OffsetsPath \"FilePath\"][--free | --indPages][--PassAllocationPtr] driver" << std::endl); + Log(L"[+] Usage: kdmapper.exe [--dontUpdateOffsets [--offsetsPath \"FilePath\"]][--free | --indPages][--PassAllocationPtr] driver" << std::endl); #else Log(L"[+] Usage: kdmapper.exe [--free | --indPages][--PassAllocationPtr] driver" << std::endl); #endif @@ -106,27 +110,6 @@ void help() { int wmain(const int argc, wchar_t** argv) { SetUnhandledExceptionFilter(SimplestCrashHandler); -#ifdef PDB_OFFSETS - bool UpdateOffset = !(paramExists(argc, argv, L"DontUpdateOffset") > 0); - int FilePathParamIdx = paramExists(argc, argv, L"OffsetsPath"); - if (FilePathParamIdx > 0) - { - SymbolsOffsetFilePath = argv[FilePathParamIdx + 1]; -#ifdef UNICODE - printf("[+] Setting Offsets File Path To: %ls\n", SymbolsOffsetFilePath); -#else - printf("[+] Setting Offsets File Path To: %s\n", SymbolsOffsetFilePath); -#endif - } - - CSymInfo SymInfo(&SymbolsInfoArray, UpdateOffset); - if (!SymInfo.m_IsValid) { - Log(L"[-] Error: Failed To Get Symbols Info.\n"); - PauseIfParentIsExplorer(); - return -1; - } -#endif - bool free = paramExists(argc, argv, L"free") > 0; bool indPagesMode = paramExists(argc, argv, L"indPages") > 0; bool passAllocationPtr = paramExists(argc, argv, L"PassAllocationPtr") > 0; @@ -149,6 +132,23 @@ int wmain(const int argc, wchar_t** argv) { Log(L"[+] Pass Allocation Ptr as first param enabled" << std::endl); } +#ifdef PDB_OFFSETS + bool UpdateOffset = !(paramExists(argc, argv, L"dontUpdateOffsets") > 0); + int FilePathParamIdx = paramExists(argc, argv, L"offsetsPath"); + std::wstring offsetFilePath = utils::GetCurrentAppFolder() + L"\\offsets.ini"; + + if (UpdateOffset && FilePathParamIdx > 0) { + Log("[-] Can't set --offsetsPath without set --dontUpdateOffsets" << std::endl); + help(); + return -1; + } + + if (FilePathParamIdx > 0) { + offsetFilePath = argv[FilePathParamIdx + 1]; + Log("[+] Setting Offsets File Path To: " << offsetFilePath << std::endl); + } +#endif + int drvIndex = -1; for (int i = 1; i < argc; i++) { if (std::filesystem::path(argv[i]).extension().string().compare(".sys") == 0) { @@ -170,6 +170,14 @@ int wmain(const int argc, wchar_t** argv) { return -1; } +#ifdef PDB_OFFSETS + if (!KDSymbolsHandler::GetInstance()->ReloadFile(offsetFilePath, UpdateOffset ? utils::GetCurrentAppFolder() + L"\\" + SYM_FROM_PDB_EXE : L"")) { + Log(L"[-] Error: Failed To Get Symbols Info." << std::endl); + PauseIfParentIsExplorer(); + return -1; + } +#endif + iqvw64e_device_handle = intel_driver::Load(); if (iqvw64e_device_handle == INVALID_HANDLE_VALUE) { diff --git a/kdmapper/utils.cpp b/kdmapper/utils.cpp index 0a6c16f..394523e 100644 --- a/kdmapper/utils.cpp +++ b/kdmapper/utils.cpp @@ -110,4 +110,11 @@ PVOID utils::FindSection(const char* sectionName, uintptr_t modulePtr, PULONG si } } return 0; +} + +std::wstring utils::GetCurrentAppFolder() { + wchar_t buffer[1024]; + GetModuleFileNameW(NULL, buffer, 1024); + std::wstring::size_type pos = std::wstring(buffer).find_last_of(L"\\/"); + return std::wstring(buffer).substr(0, pos); } \ No newline at end of file