├── .gitattributes ├── .gitignore ├── README.md └── src └── SymProcSleuth.c /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Object files 5 | *.o 6 | *.ko 7 | *.obj 8 | *.elf 9 | 10 | # Linker output 11 | *.ilk 12 | *.map 13 | *.exp 14 | 15 | # Precompiled Headers 16 | *.gch 17 | *.pch 18 | 19 | # Libraries 20 | *.lib 21 | *.a 22 | *.la 23 | *.lo 24 | 25 | # Shared objects (inc. Windows DLLs) 26 | *.dll 27 | *.so 28 | *.so.* 29 | *.dylib 30 | 31 | # Executables 32 | *.exe 33 | *.out 34 | *.app 35 | *.i*86 36 | *.x86_64 37 | *.hex 38 | 39 | # Debug files 40 | *.dSYM/ 41 | *.su 42 | *.idb 43 | *.pdb 44 | 45 | # Kernel Module Compile Results 46 | *.mod* 47 | *.cmd 48 | .tmp_versions/ 49 | modules.order 50 | Module.symvers 51 | Mkfile.old 52 | dkms.conf -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SymProcSleuth 2 | 3 | SymProcSleuth is a C implementation of the `SymProcAddress` function, which is an alternative to the `GetProcAddress` function for retrieving the address of a function within a module. This implementation is based on the original C++ code from the [SymProcAddress repository](https://github.com/MzHmO/SymProcAddress/blob/main/SymProcAddress/main.cpp) by MzHmO. Special thanks to MzHmO for the original C++ implementation of `SymProcAddress`, which served as the basis for SymProcSleuth. 4 | 5 | ## Differences and Enhancements 6 | 7 | The C version of the code, SymProcSleuth, introduces several differences and enhancements compared to the original C++ implementation: 8 | 9 | 1. **Language**: The code has been translated from C++ to pure C, making it more portable and compatible with a wider range of systems and compilers. 10 | 2. **Struct-based Module Information**: Instead of using C++-specific features like `std::map`, the C version utilizes a custom `ModuleInfo` struct to store information about each processed module, including the module handle and a dynamically allocated array of function entries. 11 | 3. **Dynamic Memory Allocation**: The C version employs dynamic memory allocation using `malloc`, `realloc`, and `free` to manage the storage of module information and function entries. This allows for flexibility in handling multiple modules and functions. 12 | 4. **Callback Function**: The `EnumSymbolsCallback` function is implemented as a regular C function instead of a lambda function. It is used as a callback for the `SymEnumSymbols` function to process the enumerated symbols. 13 | 5. **Multiple Module Enumeration**: SymProcSleuth has been enhanced to handle multiple enumerations of the same module efficiently. It maintains a list of processed modules and checks if a module has already been processed before enumerating its symbols, avoiding duplicate entries. 14 | 6. **Error Handling**: The C version includes proper error handling, setting the last error to `ERROR_PROC_NOT_FOUND` when a function is not found within a module. 15 | 7. **Memory Management**: SymProcSleuth provides a `FreeSymbols` function to free the dynamically allocated memory used for storing module information and function entries. This ensures proper cleanup and prevents memory leaks. 16 | 17 | ## Compilation Instructions 18 | 19 | To compile the SymProcSleuth code, follow these steps: 20 | 21 | ### Windows (using Visual Studio) 22 | 23 | 1. Open a Visual Studio Developer Command Prompt. 24 | 2. Navigate to the directory containing the `SymProcSleuth.c` file. 25 | 3. Run the following command to compile the code: 26 | 27 | ``` 28 | cl /EHsc SymProcSleuth.c /link dbghelp.lib 29 | ``` 30 | 31 | This command compiles the `SymProcSleuth.c` file with exception handling enabled (`/EHsc`) and links against the `dbghelp.lib` library. 32 | 33 | ### Windows (using MinGW) 34 | 35 | 1. Open a command prompt. 36 | 2. Navigate to the directory containing the `SymProcSleuth.c` file. 37 | 3. Run the following command to compile the code: 38 | 39 | ``` 40 | gcc -o SymProcSleuth.exe SymProcSleuth.c -ldbghelp 41 | ``` 42 | 43 | This command compiles the `SymProcSleuth.c` file and links against the `dbghelp` library, generating an executable named `SymProcSleuth.exe`. 44 | 45 | Make sure you have the necessary compiler (Visual Studio or MinGW) installed and properly configured on your system. Additionally, ensure that the `dbghelp.lib` library is available in your system's library search path. 46 | 47 | After successful compilation, you can run the resulting executable (`SymProcSleuth.exe`) to test the functionality of the `SymProcAddress` function. 48 | 49 | ## License 50 | 51 | This code is released under the [MIT License](LICENSE). 52 | 53 | References: 54 | 55 | https://github.com/MzHmO/SymProcAddress/blob/main/SymProcAddress/main.cpp 56 | -------------------------------------------------------------------------------- /src/SymProcSleuth.c: -------------------------------------------------------------------------------- 1 | /** 2 | * SymProcSleuth is an implementation of GetProcAddress that uses the 3 | * SymFromAddr and SymEnumSymbols Windows API functions to look up exported 4 | * function addresses by name from a module handle. It caches the results 5 | * to avoid repeated symbol lookups. FreeSymbols releases the cache. 6 | */ 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #pragma comment(lib, "Dbghelp.lib") 13 | 14 | // Define a function pointer type for MessageBoxW for the example usage below 15 | typedef int(WINAPI *MessageBoxWFunc)(HWND hWnd, LPCWSTR lpText, LPCWSTR lpCaption, UINT uType); 16 | 17 | // Define a structure to hold function entry details 18 | typedef struct { 19 | char* name; // Function name 20 | FARPROC address; // Function address 21 | } FuncEntry; 22 | 23 | // Define a structure to hold module entry details 24 | typedef struct { 25 | HMODULE module; // Handle to the module 26 | FuncEntry* funcs; // Pointer to the functions within the module 27 | size_t numFuncs; // Number of functions 28 | } ModuleEntry; 29 | 30 | static ModuleEntry* modules = NULL; // Dynamically allocated array of module entries 31 | static size_t numModules = 0; // Number of module entries 32 | 33 | // Callback function for enumerating symbols. Adds function entries to the module entry. 34 | BOOL CALLBACK EnumSymbolsCallback(PSYMBOL_INFO pSymInfo, ULONG SymbolSize, PVOID UserContext) { 35 | ModuleEntry* moduleEntry = (ModuleEntry*)UserContext; 36 | // Check if symbol is a function 37 | if (pSymInfo->Flags & 0x00000200) { 38 | // Reallocate funcs array to accommodate the new function 39 | moduleEntry->funcs = realloc(moduleEntry->funcs, (moduleEntry->numFuncs + 1) * sizeof(FuncEntry)); 40 | // Duplicate the symbol name and save it along with its address 41 | moduleEntry->funcs[moduleEntry->numFuncs].name = _strdup(pSymInfo->Name); 42 | moduleEntry->funcs[moduleEntry->numFuncs].address = (FARPROC)pSymInfo->Address; 43 | // Increment the number of functions 44 | moduleEntry->numFuncs++; 45 | } 46 | return TRUE; 47 | } 48 | 49 | // Finds the address of a symbol (function) within a specified module. 50 | FARPROC SymProcAddress(HMODULE hModule, LPCSTR lpProcName) { 51 | for (size_t i = 0; i < numModules; i++) { 52 | // Check if the module matches the one we're looking for 53 | if (modules[i].module == hModule) { 54 | // Search for the function by name within the module 55 | for (size_t j = 0; j < modules[i].numFuncs; j++) { 56 | if (_stricmp(modules[i].funcs[j].name, lpProcName) == 0) { 57 | // If found, return its address 58 | return modules[i].funcs[j].address; 59 | } 60 | } 61 | // If the function was not found, set error and return NULL 62 | SetLastError(127); // ERROR_PROC_NOT_FOUND 63 | return NULL; 64 | } 65 | } 66 | 67 | // If module is not already in the list, add it and enumerate its symbols 68 | ModuleEntry newModule; 69 | newModule.module = hModule; 70 | newModule.funcs = NULL; 71 | newModule.numFuncs = 0; 72 | 73 | // Initialize symbols enumeration for the process and specified module 74 | HANDLE hProc = GetCurrentProcess(); 75 | SymInitialize(hProc, NULL, TRUE); 76 | #ifdef _WIN64 77 | // Enumerate symbols for 64-bit module 78 | if (!SymEnumSymbols(hProc, (DWORD64)hModule, NULL, EnumSymbolsCallback, &newModule)) { 79 | SymCleanup(hProc); 80 | return NULL; 81 | } 82 | #else 83 | // Enumerate symbols for 32-bit module 84 | if (!SymEnumSymbols(hProc, (DWORD)hModule, NULL, EnumSymbolsCallback, &newModule)) { 85 | SymCleanup(hProc); 86 | return NULL; 87 | } 88 | #endif 89 | 90 | // Add the new module to the modules array 91 | modules = realloc(modules, (numModules + 1) * sizeof(ModuleEntry)); 92 | modules[numModules] = newModule; 93 | numModules++; 94 | 95 | // Try to find the function again in the newly added module 96 | for (size_t i = 0; i < newModule.numFuncs; i++) { 97 | if (_stricmp(newModule.funcs[i].name, lpProcName) == 0) { 98 | return newModule.funcs[i].address; 99 | } 100 | } 101 | 102 | // If the function is still not found, set error and return NULL 103 | SetLastError(127); // ERROR_PROC_NOT_FOUND 104 | return NULL; 105 | } 106 | 107 | // Frees allocated memory for symbols and resets module information 108 | void FreeSymbols() { 109 | for (size_t i = 0; i < numModules; i++) { 110 | // Free memory allocated for function names 111 | for (size_t j = 0; j < modules[i].numFuncs; j++) { 112 | free(modules[i].funcs[j].name); 113 | } 114 | // Free memory allocated for function entries 115 | free(modules[i].funcs); 116 | } 117 | // Free memory allocated for module entries 118 | free(modules); 119 | modules = NULL; 120 | numModules = 0; 121 | } 122 | 123 | // Example usage demonstrating how to use the implemented functions 124 | 125 | int main() 126 | { 127 | HMODULE hModule = NULL; 128 | 129 | // Load "user32.dll" module 130 | hModule = LoadLibraryA("user32.dll"); 131 | 132 | // Find the address of "MessageBoxW" function within the loaded module 133 | MessageBoxWFunc MessageBoxWPtr = (MessageBoxWFunc)(SymProcAddress(hModule, "MessageBoxW")); 134 | 135 | // Call the found function to display a message box 136 | MessageBoxWPtr(NULL, L"Lol who said we needed GetProcAddress() ?? xD", L"Hi from a7t0fwa7", MB_OK); 137 | 138 | // Free allocated symbols information 139 | FreeSymbols(); 140 | 141 | return 0; 142 | } 143 | --------------------------------------------------------------------------------