diff --git a/.gitignore b/.gitignore index 52ea8bc..bbe83e5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,7 @@ build_debug/ build_release/ +.vs/* +out/* +CMakeSettings.json +build/* +bin/* \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index f17ffaf..0d4ec66 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,31 +1,44 @@ -cmake_minimum_required(VERSION 3.0) +cmake_minimum_required(VERSION 3.0...3.15) -project(dll-injection-skeleton) +project(GurkaInjector) -include(GenerateExportHeader) +find_package(CBuildKit REQUIRED) -add_definitions(-D_WIN32_WINNT=0x0502) +option(USE_STATIC_LINKING "Use static linking for the runtime" ON) -# Common compiler flags -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Werror -std=c++11") -set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0") +if (USE_STATIC_LINKING) + if (MSVC) + # MSVC specific static linking flag + add_compile_options(/MT$<$:d>) + else() + # GCC/Clang specific static linking flag + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static-libgcc -static-libstdc++") + endif() +endif() -# dll-injector.exe -set(dll-injector_src - "src/dll-injector/dll_injector.cc" -) +set(CMAKE_CXX_STANDARD 17) +set(LOCAL_PROJECT OFF) -set(dll-injector_lib - "psapi" -) +if(${CMAKE_CURRENT_SOURCE_DIR} STREQUAL ${CMAKE_SOURCE_DIR}) + set(LOCAL_PROJECT ON) +endif() -add_executable(dll-injector ${dll-injector_src}) -target_link_libraries(dll-injector ${dll-injector_lib}) +option(DIS_BUILD_EXAMPLES "Build examples for Dll Injection Skeleton" ${LOCAL_PROJECT}) +option(DIS_ENABLE_TESTING "Build examples for Dll Injection Skeleton" ${LOCAL_PROJECT}) -# injected.dll -set(injected_src - "src/injected/injected.cc" - "src/injected/console.cc" -) +add_subdirectory(src) -add_library(injected MODULE ${injected_src}) +if(DIS_BUILD_EXAMPLES) + add_subdirectory(samples) +endif() + +if(DIS_ENABLE_TESTING) + add_subdirectory(tests) +endif() + +install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/ + DESTINATION include) + +install(FILES + ${CMAKE_CURRENT_SOURCE_DIR}/cmake/GurkaInjectorConfig.cmake + DESTINATION lib/cmake/gurka) diff --git a/README.md b/README.md index 42fd46d..8bb6986 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,137 @@ -# DllInjectionSkeleton -Just a very simple dll injection skeleton, including an injector and a dll to inject - - -# Building -This assumes that you have TDM-GCC (GCC 5.1.0), CMake (>= 3.0) and a healthy Bash environment (TDM-GCC and CMake in $PATH): - - $ cd DllInjectorSkeleton - $ ./cmake.sh all - - ### To build debug - $ cd build_debug - $ mingw32-make.exe - - ### To build release - $ cd build_release - $ mingw32-make.exe +# Dll-Injection-Skeleton + +The DLL Injector library provides a simple and efficient way to inject and eject DLLs into and from running processes on Windows. With support for both command-line interface and direct integration through CMake, this tool is ideal for developers needing to modify or debug application behavior dynamically. The library leverages Windows API functions to perform DLL injection and ejection, making it a versatile addition to any developer's toolkit. The CMake integration ensures seamless inclusion in larger projects, allowing immediate access to its functionality through easy linking. + +## Build + +### Prerequisites + +Before building the project, ensure you have the following installed on your system: + +- **CMake**: Version 3.0 or higher. +- **Visual Studio**: Recommended version for Windows development. + +### Steps to Build + +#### 1. Clone the Repository + +Clone the "Dll-Injection-Skeleton" repository from GitHub: +```bash +cd path/to/dll-injection-skeleton +``` + +#### 2. Configure the Build with CMake + +Run CMake to configure the build. Specify the generator appropriate for your installed Visual Studio version (e.g., "Visual Studio 2019"): + +```bash +mkdir build +cd build +cmake -G "Visual Studio 16 2019" .. # Replace with your Visual Studio version +``` + +#### 3. Build the Project + +Open the generated solution file (`dll-injection-skeleton.sln`) in Visual Studio and build the project: + +- Select `Build` > `Build Solution` from the Visual Studio menu. +- Alternatively, build from the command line: +```bash +cmake --build . --config Release +``` +Replace `Release` with `Debug` if you need a debug build. + +#### 4. Running Examples (Optional) + +If you enabled building examples (`DIS_BUILD_EXAMPLES` option), you can run them after building: + +- Navigate to the `build` directory (if not already there). +- Run the built examples by injecting them into any process of your choice using **CLI** (See CLI). + + +## Integration + +To integrate the **Dll-Injection-Skeleton** library into your project, follow these steps: + +### Step 1: Clone the Repository + +Clone the **Dll-Injection-Skeleton** repository from GitHub into your project's directory: +```bash +git clone https://github.com/gurka/DllInjectionSkeleton.git +``` +### Step 2: Include as Subdirectory + +Integrate the library into your CMake project by adding it as a subdirectory in your `CMakeLists.txt` file. Assuming your project's root directory is where you want to include **Dll-Injection-Skeleton**: +```cmake +add_subdirectory(path/to/DllInjectionSkeleton) +``` + +Replace `path/to/DllInjectionSkeleton` with the actual path relative to your project. + +### Step 3: Link to the Library + +Link your project to the **Dll-Injection-Skeleton** library target. For example, if your project is named `MyProject` and you want to link against `dll-injector` target from **Dll-Injection-Skeleton**, modify your `CMakeLists.txt`: +```cmake +target_link_libraries(MyProject PRIVATE dll-injector) +``` + +Make sure to replace `MyProject` with the actual name of your project. + +### Step 4: Build Your Project + +Build your project using CMake and your preferred build tool (e.g., Visual Studio, Ninja, etc.). Ensure that CMake correctly identifies and integrates **Dll-Injection-Skeleton** into your build system. + +### Example CMakeLists.txt + +Here is a simplified example of how your `CMakeLists.txt` might look after integrating **Dll-Injection-Skeleton**: +```cmake +cmake_minimum_required(VERSION 3.0) +project(MyProject) + +# Include Dll-Injection-Skeleton as a subdirectory +add_subdirectory(path/to/DllInjectionSkeleton) + +# Define your project's executable or library +add_executable(MyProject main.cpp) + +# Link your project to Dll-Injection-Skeleton +target_link_libraries(MyProject PRIVATE dll-injector) +``` + +Adjust paths and configurations according to your project's structure and requirements. + + +## Usage & CLI + +### Installation: +Follow instructions in the Build section to build using CMake or download the tool. + +### CLI Usage + +The `dll-injector-cli` tool supports two commands: + +- **Inject Command**: Injects a DLL into a specified process. +```bash +dll-injector-cli inject [process name.exe] [dll path] +``` +- **Eject Command**: Ejects a DLL from a specified process. +```bash +dll-injector-cli eject [process name.exe] [dll name] +``` +### Example Usage + +Examples of using `dll-injector-cli`: +```bash +# Inject DLL into a process +dll-injector-cli inject notepad.exe path/to/mydll.dll + +# Eject DLL from a process +dll-injector-cli eject notepad.exe mydll.dll +``` +Ensure the process is running and accessible to the user. Successful DLL operations depend on correct process and DLL path/name specifications. + +### Notes + +- Ensure appropriate permissions to perform DLL injection and ejection. +- Integrate the `dll-injector` target into your CMake projects for programmatic access to DLL injection capabilities, for more information check **Integration** Section. + diff --git a/cmake.sh b/cmake.sh deleted file mode 100644 index 439022f..0000000 --- a/cmake.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash -case $1 in - 'all') - mkdir -p build_release && cd build_release && cmake -G "MinGW Makefiles" .. -DCMAKE_BUILD_TYPE=release -DCMAKE_MAKE_PROGRAM=mingw32-make.exe - cd .. - mkdir -p build_debug && cd build_debug && cmake -G "MinGW Makefiles" .. -DCMAKE_BUILD_TYPE=debug -DCMAKE_MAKE_PROGRAM=mingw32-make.exe - ;; - - 'release') - mkdir -p build_release && cd build_release && cmake -G "MinGW Makefiles" .. -DCMAKE_BUILD_TYPE=release -DCMAKE_MAKE_PROGRAM=mingw32-make.exe - ;; - - 'debug') - mkdir -p build_debug && cd build_debug && cmake -G "MinGW Makefiles" .. -DCMAKE_BUILD_TYPE=debug -DCMAKE_MAKE_PROGRAM=mingw32-make.exe - ;; - - *) - echo "Usage: $0 [ all | release | debug ]" - ;; -esac diff --git a/cmake/GurkaInjectorConfig.cmake b/cmake/GurkaInjectorConfig.cmake new file mode 100644 index 0000000..0d8c761 --- /dev/null +++ b/cmake/GurkaInjectorConfig.cmake @@ -0,0 +1 @@ +include(${CMAKE_CURRENT_LIST_DIR}/gurka-injector-targets.cmake) \ No newline at end of file diff --git a/include/gurka/dll_injector.h b/include/gurka/dll_injector.h new file mode 100644 index 0000000..b569c6d --- /dev/null +++ b/include/gurka/dll_injector.h @@ -0,0 +1,15 @@ +#include +#include + +namespace gurka { + uint64_t getModuleBaseAddress(DWORD processID, const char* dllName); + size_t findPids(const char* programName, size_t resPidsSz, DWORD* outPids); + bool injectDLL(HANDLE hProc, const char* dllFullPath); + bool injectDLL(DWORD procId, const char* dllFullPath); + bool unloadDLL(HANDLE hProc, const char* dllName); + bool unloadDLL(HANDLE hProc, const char* dllName); + bool loadDLL(const char* procName, const char* dllPath); + bool unloadDLL(DWORD procId, const char* dllName); + bool unloadDLL(const char* procName, const char* dllPath); + extern bool EnableDllLog; +} \ No newline at end of file diff --git a/include/gurka/dll_injector_cli.h b/include/gurka/dll_injector_cli.h new file mode 100644 index 0000000..0a914ec --- /dev/null +++ b/include/gurka/dll_injector_cli.h @@ -0,0 +1,5 @@ +#pragma once + +namespace gurka { + int dll_injector_main(int argc, const char** argv); +} \ No newline at end of file diff --git a/samples/CMakeLists.txt b/samples/CMakeLists.txt new file mode 100644 index 0000000..942ec34 --- /dev/null +++ b/samples/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(injected) \ No newline at end of file diff --git a/samples/injected/CMakeLists.txt b/samples/injected/CMakeLists.txt new file mode 100644 index 0000000..6dabc59 --- /dev/null +++ b/samples/injected/CMakeLists.txt @@ -0,0 +1,5 @@ +add_library(injected SHARED injected.cc console.cc) +target_include_directories(injected PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../../include) + +add_library(injected_macro_path INTERFACE) +target_compile_definitions(injected_macro_path INTERFACE INJECTED_PATH="${CMAKE_CURRENT_BINARY_DIR}") diff --git a/src/injected/console.cc b/samples/injected/console.cc similarity index 92% rename from src/injected/console.cc rename to samples/injected/console.cc index 2f75654..1364ad7 100644 --- a/src/injected/console.cc +++ b/samples/injected/console.cc @@ -50,6 +50,6 @@ void Console::write(const char* format, ...) vsnprintf(message, sizeof(message), format, args); va_end(args); - WriteConsoleA(consoleHandle, message, strlen(message), nullptr, nullptr); + WriteConsoleA(consoleHandle, message, (DWORD)strlen(message), nullptr, nullptr); } } diff --git a/src/injected/console.h b/samples/injected/console.h similarity index 100% rename from src/injected/console.h rename to samples/injected/console.h diff --git a/src/injected/injected.cc b/samples/injected/injected.cc similarity index 70% rename from src/injected/injected.cc rename to samples/injected/injected.cc index 77b07d3..32caf23 100644 --- a/src/injected/injected.cc +++ b/samples/injected/injected.cc @@ -3,17 +3,23 @@ #include "console.h" -static Console console; +HINSTANCE ghInstance; void injectedMain() { + static Console console; + console.init("Injected DLL"); - console.write("hey its me ur brother"); + + MessageBoxA(NULL, "Hi", NULL, NULL); } -extern "C" __declspec(dllexport) BOOL APIENTRY DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) +BOOL APIENTRY DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { + ghInstance = hinstDLL; + (void)lpvReserved; + static std::thread injectedMainThread; switch (fdwReason) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..aea8606 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(dll-injector) +add_subdirectory(cli) diff --git a/src/cli/CMakeLists.txt b/src/cli/CMakeLists.txt new file mode 100644 index 0000000..5074fab --- /dev/null +++ b/src/cli/CMakeLists.txt @@ -0,0 +1,11 @@ +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../../bin) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../../bin) + +add_executable(dll-injector-cli dll_injector_cli.cpp cli.cpp) +target_link_libraries(dll-injector-cli gurka::injector) + +if(CMAKE_SIZEOF_VOID_P EQUAL 8) + set_target_properties(dll-injector-cli PROPERTIES OUTPUT_NAME dll-injector-cli-64) +else() + set_target_properties(dll-injector-cli PROPERTIES OUTPUT_NAME dll-injector-cli) +endif() \ No newline at end of file diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp new file mode 100644 index 0000000..f5042de --- /dev/null +++ b/src/cli/cli.cpp @@ -0,0 +1,8 @@ +#include + +using namespace gurka; + +int main(int argc, const char* argv[]) +{ + return dll_injector_main(argc, argv); +} \ No newline at end of file diff --git a/src/cli/dll_injector_cli.cpp b/src/cli/dll_injector_cli.cpp new file mode 100644 index 0000000..5e6c586 --- /dev/null +++ b/src/cli/dll_injector_cli.cpp @@ -0,0 +1,85 @@ +#include +#include +#include +#include +#include + +using namespace gurka; + +int cli_dll_load(const char* procName, const char* dllPath, bool bRawPath = false) +{ + return loadDLL(procName, bRawPath ? dllPath : std::filesystem::absolute(dllPath).string().c_str()) ? 0 : 1; +} + +int cli_dll_unload(const char* procName, const char* dllName) +{ + return unloadDLL(procName, dllName) ? 0 : 1; +} + +int cli_dll_reload(const char* procName, const char* dllName) +{ + return unloadDLL(procName, dllName), cli_dll_load(procName, dllName); +} + +void print_usage() +{ + printf( + "usage: dll-injector-cli inject [process name.exe] [optional raw] [dll path]\n" + "usage: dll-injector-cli eject [process name.exe] [dll name]\n" + "usage: dll-injector-cli reload|reinject [process name.exe] [dll name]\n"); +} + +int gurka::dll_injector_main(int argc, const char** argv) +{ + try { + if (argc < 2) + { + print_usage(); + return 0; + } + + const char* option = argv[1]; + + if (!strcmp(option, "inject")) + { + if (argc < 4) + { + print_usage(); + return 0; + } + + return cli_dll_load(argv[2], std::string(argv[3]) == "raw" ? argv[4] : argv[3], std::string(argv[3]) == "raw"); + } + + if (!strcmp(option, "eject")) + { + if (argc < 4) + { + print_usage(); + return 0; + } + + return cli_dll_unload(argv[2], argv[3]); + } + + if (!strcmp(option, "reload") || !strcmp(option, "reinject")) + { + if (argc < 4) + { + print_usage(); + return 0; + } + + return cli_dll_reload(argv[2], argv[3]); + } + + + print_usage(); + return 0; + } + catch (const std::exception& e) + { + std::cerr << e.what() << std::endl; + return -1; + } +} \ No newline at end of file diff --git a/src/dll-injector/CMakeLists.txt b/src/dll-injector/CMakeLists.txt new file mode 100644 index 0000000..fda0f02 --- /dev/null +++ b/src/dll-injector/CMakeLists.txt @@ -0,0 +1,4 @@ +add_library_ns(gurka injector STATIC dll_injector.cc) +target_include_dir_iface(gurka-injector PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../../include include) +target_link_libraries(gurka-injector psapi) +install_target_and_headers (gurka injector) \ No newline at end of file diff --git a/src/dll-injector/dll_injector.cc b/src/dll-injector/dll_injector.cc index 50aac57..8f82089 100644 --- a/src/dll-injector/dll_injector.cc +++ b/src/dll-injector/dll_injector.cc @@ -2,33 +2,79 @@ #include #include #include +#include +#include +#include +#include +#include +#include -// Name of the program to inject the dll in -static const std::string programName = "gvim.exe"; +using namespace gurka; -// Name of the dll to inject -static const std::string dllName = "libinjected.dll"; +bool gurka::EnableDllLog = true; -DWORD findPid(const std::string& programName) +#define LOG(...) if(EnableDllLog) printf(__VA_ARGS__) + +// Function to get a pointer to the filename from a given path +const char* GetFilename(const char* path) { + // Pointers to the last occurrences of '/' and '\' + const char* unix_sep = strrchr(path, '/'); + const char* win_sep = strrchr(path, '\\'); + + // Determine which is the last separator + const char* last_sep = unix_sep > win_sep ? unix_sep : win_sep; + + // If there's no separator, the path itself is the filename + return last_sep ? last_sep + 1 : path; +} + +HMODULE GetMHandle(DWORD processID, const char* dllName) { + HMODULE hMod = 0; + HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, processID); + + if (hSnapshot != INVALID_HANDLE_VALUE) { + MODULEENTRY32 moduleEntry; + moduleEntry.dwSize = sizeof(moduleEntry); + + if (Module32First(hSnapshot, &moduleEntry)) { + do { + if (strstr(moduleEntry.szModule, dllName)) { + hMod = moduleEntry.hModule; + break; + } + } while (Module32Next(hSnapshot, &moduleEntry)); + } + CloseHandle(hSnapshot); + } + return hMod; +} + +bool IsModuleLoaded(HANDLE hProc, const char* moduleName) +{ + return GetMHandle(GetProcessId(hProc), moduleName) != 0; +} + +size_t gurka::findPids(const char* programName, size_t resPidsSz, DWORD* outPids) { // Enumerate all processes - DWORD pids[1024]; + size_t resIndex = 0; + DWORD pidsArr[1024]; DWORD temp; - if (!EnumProcesses(pids, sizeof(pids), &temp)) + if (!EnumProcesses(pidsArr, sizeof(pidsArr), &temp)) { - return 1; + return {}; } // Find the first process with the given program name auto noPids = temp / sizeof(DWORD); for (auto i = 0u; i < noPids; i++) { - if (pids[i] == 0) + if (pidsArr[i] == 0) { continue; } - auto tempHandle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pids[i]); + auto tempHandle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pidsArr[i]); if (tempHandle == NULL) { continue; @@ -39,89 +85,193 @@ DWORD findPid(const std::string& programName) { TCHAR szProcessName[MAX_PATH]; GetModuleBaseName(tempHandle, tempModule, szProcessName, sizeof(szProcessName) / sizeof(TCHAR)); - if (strcmp(programName.c_str(), szProcessName) == 0) + if (strcmp(programName, szProcessName) == 0) { - return pids[i]; + outPids[resIndex++] = pidsArr[i]; + if ((resIndex < resPidsSz) == false) + break; } } } - return 0; + return resIndex; +} + +std::optional FindInPath(const std::string& filename) { + const char* path_env = std::getenv("PATH"); + if (!path_env) return std::nullopt; + + std::istringstream path_stream(path_env); + std::string dir; + while (std::getline(path_stream, dir, ';')) { + std::filesystem::path potential_path = std::filesystem::path(dir) / filename; + if (std::filesystem::exists(potential_path)) { + return potential_path; + } + } + return std::nullopt; } -bool injectDLL(DWORD pid) +bool gurka::injectDLL(HANDLE hProc, const char* _dllFullPath) { - // Get full path of our dll - char fullDllName[1024]; - if (GetFullPathName(dllName.c_str(), sizeof(fullDllName), fullDllName, nullptr) == 0) - { - return false; - } + std::filesystem::path path = _dllFullPath; + if (!std::filesystem::exists(path)) + { + if (auto newPath = FindInPath(_dllFullPath)) path = *newPath; + else path = std::filesystem::absolute(_dllFullPath); + if (!std::filesystem::exists(path)) return false; + } + std::string dllFullPath = path.string(); + const char* dllName = GetFilename(dllFullPath.c_str()); + DWORD pid = GetProcessId(hProc); - // Open process using pid - auto handle = OpenProcess(PROCESS_ALL_ACCESS, false, pid); - if (handle == NULL) - { - return false; - } + if (IsModuleLoaded(hProc, dllName)) + return true; - // Get the address to the function LoadLibraryA in kernel32.dll - auto LoadLibAddr = (LPVOID)GetProcAddress(GetModuleHandleA("kernel32.dll"), "LoadLibraryA"); - if (LoadLibAddr == NULL) - { - return false; - } + // Get the address to the function LoadLibraryA in kernel32.dll + auto LoadLibAddr = (LPVOID)GetProcAddress(GetModuleHandleA("kernel32.dll"), "LoadLibraryA"); + if (LoadLibAddr == NULL) + { + return false; + } - // Allocate memory inside the opened process - auto dereercomp = VirtualAllocEx(handle, NULL, strlen(fullDllName), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); - if (dereercomp == NULL) - { - return false; - } + // Allocate memory inside the opened process + auto dereercomp = VirtualAllocEx(hProc, NULL, strlen(dllFullPath.c_str()), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); + if (dereercomp == NULL) + { + return false; + } - // Write the DLL name to the allocated memory - if (!WriteProcessMemory(handle, dereercomp, fullDllName, strlen(fullDllName), NULL)) - { - return false; - } + // Write the DLL name to the allocated memory + if (!WriteProcessMemory(hProc, dereercomp, dllFullPath.c_str(), strlen(dllFullPath.c_str()), NULL)) + { + return false; + } - // Create a thread in the opened process - auto remoteThread = CreateRemoteThread(handle, NULL, 0, (LPTHREAD_START_ROUTINE)LoadLibAddr, dereercomp, 0, NULL); - if (remoteThread == NULL) - { - return false; - } + // Create a thread in the opened process + auto remoteThread = CreateRemoteThread(hProc, NULL, 0, (LPTHREAD_START_ROUTINE)LoadLibAddr, dereercomp, 0, NULL); + if (remoteThread == NULL) + { + return false; + } - // Wait until thread have started (or stopped?) - WaitForSingleObject(remoteThread, INFINITE); + // Wait until thread have started (or stopped?) + WaitForSingleObject(remoteThread, INFINITE); - // Free the allocated memory - VirtualFreeEx(handle, dereercomp, strlen(fullDllName), MEM_RELEASE); + if (IsModuleLoaded(hProc, dllName)) + LOG("PID-%d: Loaded %s\n", pid, dllName); - // Close the handles - CloseHandle(remoteThread); - CloseHandle(handle); + // Free the allocated memory + VirtualFreeEx(hProc, dereercomp, strlen(dllFullPath.c_str()), MEM_RELEASE); - return true; + // Close the handles + CloseHandle(remoteThread); + + return true; } -int main(int argc, char* argv[]) +bool gurka::injectDLL(DWORD procId, const char* dllFullPath) { - printf("Finding pid for: %s\n", programName.c_str()); - auto pid = findPid(programName); - if (pid == 0) - { - fprintf(stderr, "Could not find process: %s\n", programName.c_str()); - return 1; - } + HANDLE hProcess = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ, FALSE, procId); - printf("Injecting DLL: %s\n", dllName.c_str()); - if (!injectDLL(pid)) - { - fprintf(stderr, "Could not inject DLL: %s\n", dllName.c_str()); - return 1; - } + if (hProcess == INVALID_HANDLE_VALUE) + return false; - printf("Done!\n"); - return 0; + bool bInjected = injectDLL(hProcess, dllFullPath); + + CloseHandle(hProcess); + + return bInjected; +} + +bool gurka::unloadDLL(HANDLE hProc, const char* dllName) +{ + DWORD procId = GetProcessId(hProc); + + if (!IsModuleLoaded(hProc, dllName)) + return true; + + if (procId == 0) + return false; + + HMODULE hMod = GetMHandle(procId, dllName); + + // Get the address to the function FreeLibrary in kernel32.dll + auto FreeLibAddr = (LPVOID)GetProcAddress(GetModuleHandleA("kernel32.dll"), "FreeLibrary"); + if (FreeLibAddr == NULL) + { + return false; + } + + // Create a thread in the opened process + auto remoteThread = CreateRemoteThread(hProc, NULL, 0, (LPTHREAD_START_ROUTINE)FreeLibAddr, (LPVOID)hMod, 0, NULL); + if (remoteThread == NULL) + { + return false; + } + + // Wait until thread have started (or stopped?) + WaitForSingleObject(remoteThread, INFINITE); + + if (!IsModuleLoaded(hProc, dllName)) + LOG("PID-%d: Unloaded %s\n", procId, dllName); + + // Close the handles + CloseHandle(remoteThread); + + return true; +} + +bool gurka::unloadDLL(DWORD procId, const char* dllName) +{ + HANDLE hProcess = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ, FALSE, procId); + + if (hProcess == INVALID_HANDLE_VALUE) + return false; + + bool bUnloaded = unloadDLL(hProcess, dllName); + + CloseHandle(hProcess); + + return bUnloaded; +} + +bool gurka::loadDLL(const char* procName, const char* dllPath) +{ + DWORD pids[1024]; + size_t nPids = findPids(procName, sizeof(pids), pids); + + if (nPids == 0) + { + LOG("Could not find process: %s\n", procName); + return false; + } + + for (size_t i = 0; i < nPids; i++) + { + if (!injectDLL(pids[i], dllPath)) + LOG("%s:%d Could not inject DLL: %s\n", procName, pids[i], dllPath); + } + + return true; } + +bool gurka::unloadDLL(const char* procName, const char* dllName) +{ + DWORD pids[1024]; + size_t nPids = findPids(procName, sizeof(pids), pids); + + if (nPids == 0) + { + fprintf(stderr, "Could not find process: %s\n", procName); + return false; + } + + for (size_t i = 0; i < nPids; i++) + { + if (!unloadDLL(pids[i], dllName)) + fprintf(stderr, "%s:%d Could not unload DLL: %s\n", procName, pids[i], dllName); + } + + return true; +} \ No newline at end of file diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..c61295c --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,2 @@ +add_executable(cli_test cli_test.cpp ../src/cli/dll_injector_cli.cpp) +target_link_libraries(cli_test gurka::injector injected_macro_path) \ No newline at end of file diff --git a/tests/cli_test.cpp b/tests/cli_test.cpp new file mode 100644 index 0000000..4cf02ee --- /dev/null +++ b/tests/cli_test.cpp @@ -0,0 +1,15 @@ +#include +#include + +using namespace gurka; + +int main(int argc, const char* realArgv[]) +{ + std::filesystem::current_path(INJECTED_PATH); + + const char* argv[]{ + realArgv[0], "eject", "App.exe", "TestDll.dll" + }; + + dll_injector_main(sizeof(argv) / sizeof(argv[0]), (const char**)argv); +} \ No newline at end of file