├── LICENSE ├── README.md ├── bin └── hello_world_dll │ ├── x64.dll │ └── x86.dll ├── example └── sample.cpp └── src ├── handle_helper.hpp ├── injector.cpp ├── injector.hpp └── injector_exception.hpp /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2014 kaimi.io 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 | # C++ Code Injector Class 2 | [![CPP](https://img.shields.io/badge/cpp-green.svg)](https://en.wikipedia.org/wiki/C%2B%2B) [![License](https://img.shields.io/badge/license-MIT-red.svg)](https://raw.githubusercontent.com/kaimi-io/cpp-injector-class/master/LICENSE) 3 | 4 | [![Telegram](https://img.shields.io/badge/Telegram--lightgrey?logo=telegram&style=social)](https://t.me/kaimi_io) 5 | [![Twitter](https://img.shields.io/twitter/follow/kaimi_io?style=social)](https://twitter.com/kaimi_io) 6 | ## Description 7 | ### Available methods 8 | ```cpp 9 | // Inject a DLL with the specified name in the process with the specified name 10 | void inject(const std::wstring& proc_name, const std::wstring& dll_name); 11 | 12 | // Inject a raw assembly code in the process with the specified name 13 | void inject(const std::wstring& proc_name, const BYTE * code, unsigned long int code_size); 14 | 15 | // Inject a DLL with the specified name in the process with the specified PID (Process Idenitifer) 16 | void inject(unsigned int pid, const std::wstring& dll_name); 17 | 18 | // Inject a raw assembly code in the process with the specified PID (Process Idenitifer) 19 | void inject(unsigned int pid, const BYTE * code, unsigned long int code_size); 20 | 21 | // Enable/Disable blocking mode (should we call WaitForSingleObject and VirtualFreeEx functions after creating a remote thread) 22 | void set_blocking(bool active); 23 | ``` 24 | ### Usage 25 | ```cpp 26 | #include "injector.hpp" 27 | 28 | // Inject x64.dll to the "CFF Explorer.exe" process in non-blocking mode 29 | int main() 30 | { 31 | injector a; 32 | a.set_blocking(false); 33 | 34 | try 35 | { 36 | a.inject(L"CFF Explorer.exe", L"x64.dll"); 37 | } 38 | catch(const injector_exception& e) 39 | { 40 | e.show_error(); 41 | } 42 | 43 | return 0; 44 | } 45 | ``` 46 | ## License 47 | C++ Code Injector Class Copyright © 2011-2020 by Kaimi (Sergey Belov) - https://kaimi.io. 48 | 49 | C++ Code Injector Class is free software: you can redistribute it and/or modify it under the terms of the Massachusetts Institute of Technology (MIT) License. 50 | 51 | You should have received a copy of the MIT License along with C++ Code Injector Class. If not, see [MIT License](LICENSE). 52 | -------------------------------------------------------------------------------- /bin/hello_world_dll/x64.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaimi-io/cpp-injector-class/ab3b84f5798db27fe61324cc2cb2b5a336fc302a/bin/hello_world_dll/x64.dll -------------------------------------------------------------------------------- /bin/hello_world_dll/x86.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaimi-io/cpp-injector-class/ab3b84f5798db27fe61324cc2cb2b5a336fc302a/bin/hello_world_dll/x86.dll -------------------------------------------------------------------------------- /example/sample.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "injector.hpp" 4 | 5 | int main() 6 | { 7 | injector a; 8 | a.set_blocking(false); 9 | 10 | try 11 | { 12 | a.inject(L"putty.exe", L"x86.dll"); 13 | } 14 | catch(const injector_exception &e) 15 | { 16 | std::wcout << e.get_error() << std::endl; 17 | } 18 | 19 | return 0; 20 | } -------------------------------------------------------------------------------- /src/handle_helper.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | class handle_helper 4 | { 5 | public: 6 | handle_helper(HANDLE h) 7 | :h_(h) 8 | {} 9 | 10 | handle_helper(handle_helper& other) 11 | :h_(other.h_) 12 | { 13 | other.h_ = INVALID_HANDLE_VALUE; 14 | } 15 | 16 | handle_helper() 17 | :h_(INVALID_HANDLE_VALUE) 18 | {} 19 | 20 | handle_helper& operator=(handle_helper& other) 21 | { 22 | close(); 23 | h_ = other.h_; 24 | other.h_ = INVALID_HANDLE_VALUE; 25 | return *this; 26 | } 27 | 28 | handle_helper& operator=(HANDLE h) 29 | { 30 | close(); 31 | h_ = h; 32 | return *this; 33 | } 34 | 35 | HANDLE& get() { return h_; } 36 | 37 | ~handle_helper() 38 | { 39 | close(); 40 | } 41 | 42 | void close() 43 | { 44 | if(h_ != INVALID_HANDLE_VALUE) 45 | { 46 | CloseHandle(h_); 47 | h_ = INVALID_HANDLE_VALUE; 48 | } 49 | } 50 | 51 | void reset() { h_ = INVALID_HANDLE_VALUE; } 52 | 53 | private: 54 | HANDLE h_; 55 | }; -------------------------------------------------------------------------------- /src/injector.cpp: -------------------------------------------------------------------------------- 1 | #include "injector.hpp" 2 | 3 | 4 | unsigned int injector::inst_cnt_ = 0; 5 | TOKEN_PRIVILEGES injector::old_tp_; 6 | 7 | injector::injector() 8 | :is_blocking_(true) 9 | { 10 | if(!inst_cnt_) 11 | adjust_privileges(true); 12 | 13 | inst_cnt_++; 14 | } 15 | 16 | injector::injector(const injector &inj) 17 | :is_blocking_(inj.is_blocking_) 18 | { 19 | inst_cnt_++; 20 | } 21 | 22 | injector::~injector() 23 | { 24 | inst_cnt_--; 25 | 26 | if(!inst_cnt_) 27 | adjust_privileges(false); 28 | } 29 | 30 | injector& injector::operator=(const injector& inj) 31 | { 32 | if(&inj != this) 33 | is_blocking_ = inj.is_blocking_; 34 | return *this; 35 | } 36 | 37 | void injector::file_exists(const std::wstring& file_name) 38 | { 39 | std::ifstream file; 40 | file.open(file_name, std::ios::in); 41 | 42 | if(file.is_open()) 43 | return; 44 | else 45 | throw injector_exception(L"Can't open DLL", __LINE__); 46 | } 47 | 48 | void injector::adjust_privileges(bool debug_rights) 49 | { 50 | TOKEN_PRIVILEGES tp; 51 | handle_helper token; 52 | DWORD tpSize = sizeof(TOKEN_PRIVILEGES); 53 | LUID luid; 54 | 55 | if(!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token.get())) 56 | if(GetLastError() != ERROR_CALL_NOT_IMPLEMENTED) 57 | throw injector_exception(L"OpenProcessToken", __LINE__); 58 | 59 | if(debug_rights) 60 | { 61 | if(!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid)) 62 | throw injector_exception(L"LookupPrivilegeValue", __LINE__); 63 | 64 | ZeroMemory(&tp, sizeof(tp)); 65 | tp.PrivilegeCount = 1; 66 | tp.Privileges[0].Luid = luid; 67 | tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; 68 | } 69 | else 70 | { 71 | ZeroMemory(&tp, sizeof(tp)); 72 | tp = old_tp_; 73 | } 74 | 75 | if(!AdjustTokenPrivileges(token.get(), FALSE, &tp, sizeof(TOKEN_PRIVILEGES), &old_tp_, &tpSize)) 76 | throw injector_exception(L"AdjustTokenPrivileges", __LINE__); 77 | } 78 | 79 | unsigned int injector::find_process_by_name(const std::wstring& proc_name) 80 | { 81 | PROCESSENTRY32 pe32; 82 | unsigned int pid = 0; 83 | handle_helper ss; 84 | 85 | if( (ss = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS | TH32CS_SNAPMODULE32, 0)).get() == INVALID_HANDLE_VALUE ) 86 | throw injector_exception(L"CreateToolhelp32Snapshot", __LINE__); 87 | 88 | pe32.dwSize = sizeof(PROCESSENTRY32); 89 | 90 | if(!Process32First(ss.get(), &pe32)) 91 | throw injector_exception(L"Process32First", __LINE__); 92 | 93 | do 94 | { 95 | if(!proc_name.compare(pe32.szExeFile)) 96 | { 97 | pid = pe32.th32ProcessID; 98 | break; 99 | } 100 | } while(Process32Next(ss.get(), &pe32)); 101 | 102 | return pid; 103 | } 104 | 105 | void injector::init_injector_struct(injectorcode& cmds, const std::wstring& dll_name) 106 | { 107 | if(dll_name.length() > bufsize) 108 | throw injector_exception(L"DLL name exceeds buffer size", __LINE__); 109 | 110 | wchar_t full_path[bufsize]; 111 | if(!GetFullPathName(dll_name.c_str(), bufsize, full_path, NULL)) 112 | throw injector_exception(L"GetFullPathName", __LINE__); 113 | else 114 | wcscpy_s(cmds.libraryname, bufsize, full_path); 115 | 116 | #ifdef _WIN64 117 | //sub rsp, 8 118 | cmds.stack_init = 0x08EC8348; 119 | 120 | //sub rsp, 20 121 | cmds.loadlibrary_init_stack = 0x20EC8348; 122 | //lea rcx, eip+.. 123 | cmds.instr_mov_loadlibrary.a = 0x48; 124 | cmds.instr_mov_loadlibrary.b = 0x8D; 125 | cmds.instr_mov_loadlibrary.c = 0x0D; 126 | 127 | cmds.loadlibrary_arg = offsetof(injectorcode, libraryname) - offsetof(injectorcode, loadlibrary_arg); 128 | //call qword ptr... 129 | cmds.instr_call_loadlibrary = 0x15ff; 130 | cmds.adr_from_call_loadlibrary = offsetof(injectorcode, addr_loadlibrary) - offsetof(injectorcode, loadlibrary_clear_stack); 131 | //add rsp, 20 132 | cmds.loadlibrary_clear_stack = 0x20C48348; 133 | 134 | //sub rsp, 20 135 | cmds.exitthread_init_stack = 0x20EC8348; 136 | //mov rcx, 0 137 | cmds.instr_mov_exitthread.a = 0x48; 138 | cmds.instr_mov_exitthread.b = 0xC7; 139 | cmds.instr_mov_exitthread.c = 0xC1; 140 | cmds.exitthread_arg = 0; 141 | //call qword ptr... 142 | cmds.instr_call_exitthread = 0x15ff; 143 | cmds.adr_from_call_exitthread = offsetof(injectorcode, addr_exitthread) - offsetof(injectorcode, addr_loadlibrary); 144 | 145 | cmds.addr_loadlibrary = reinterpret_cast(GetProcAddress(GetModuleHandle(L"kernel32.dll"), "LoadLibraryW")); 146 | cmds.addr_exitthread = reinterpret_cast(GetProcAddress(GetModuleHandle(L"kernel32.dll"), "ExitThread")); 147 | #else 148 | cmds.ebx_to_eax = 0xC38B; 149 | cmds.mov_eax_offset = 0xC083; 150 | cmds.loadlibrary_arg = offsetof(injectorcode, libraryname); 151 | 152 | cmds.instr_push_loadlibrary_arg = 0x50; 153 | 154 | cmds.instr_call_loadlibrary = 0x53ff; 155 | cmds.adr_from_call_loadlibrary = offsetof(injectorcode, addr_loadlibrary); 156 | 157 | 158 | cmds.instr_push_exitthread_arg = 0x68; 159 | cmds.exitthread_arg = 0; 160 | 161 | cmds.instr_call_exitthread = 0x53ff; 162 | cmds.adr_from_call_exitthread = offsetof(injectorcode, addr_exitthread); 163 | 164 | cmds.addr_loadlibrary = reinterpret_cast(GetProcAddress(GetModuleHandle(L"kernel32.dll"), "LoadLibraryW")); 165 | cmds.addr_exitthread = reinterpret_cast(GetProcAddress(GetModuleHandle(L"kernel32.dll"), "ExitThread")); 166 | #endif 167 | } 168 | 169 | void * injector::open_and_alloc(handle_helper& process, unsigned int pid, unsigned long int size) 170 | { 171 | void * base_addr = NULL; 172 | 173 | if( !(process = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ, FALSE, pid)).get() ) 174 | { 175 | process.reset(); 176 | throw injector_exception(L"OpenProcess", __LINE__); 177 | } 178 | 179 | if( !(base_addr = reinterpret_cast(VirtualAllocEx(process.get(), NULL, size, MEM_COMMIT, PAGE_READWRITE))) ) 180 | throw injector_exception(L"VirtualAllocEx", __LINE__); 181 | 182 | return base_addr; 183 | } 184 | 185 | void injector::write_to_memory(handle_helper& process, void * base_addr, const BYTE * code, unsigned long int code_size) 186 | { 187 | DWORD junk; 188 | 189 | if(!WriteProcessMemory(process.get(), base_addr, code, code_size, NULL)) 190 | throw injector_exception(L"WriteProcessMemory", __LINE__); 191 | 192 | if(!VirtualProtectEx(process.get(), base_addr, code_size, PAGE_EXECUTE_READ, &junk)) 193 | throw injector_exception(L"VirtualProtectEx", __LINE__); 194 | } 195 | 196 | void injector::run_remote_code(handle_helper& process, handle_helper& remote_thread, void * base_addr) 197 | { 198 | if( !(remote_thread = CreateRemoteThread(process.get(), NULL, 0, reinterpret_cast(base_addr), base_addr, 0, NULL)).get() ) 199 | throw injector_exception(L"CreateRemoteThread", __LINE__); 200 | } 201 | 202 | void injector::wait_and_free(handle_helper& process, handle_helper& remote_thread, void * base_addr) 203 | { 204 | if(is_blocking_) 205 | { 206 | WaitForSingleObject(remote_thread.get(), INFINITE); 207 | 208 | if(!VirtualFreeEx(process.get(), base_addr, 0, MEM_RELEASE)) 209 | throw injector_exception(L"VirtualFreeEx", __LINE__); 210 | } 211 | } 212 | 213 | void injector::inject(const std::wstring& proc_name, const BYTE * code, unsigned long int code_size) 214 | { 215 | inject(find_process_by_name(proc_name), code, code_size); 216 | } 217 | 218 | void injector::inject(const std::wstring& proc_name, const std::wstring& dll_name) 219 | { 220 | inject(find_process_by_name(proc_name), dll_name); 221 | } 222 | 223 | void injector::inject(unsigned int pid, const std::wstring& dll_name) 224 | { 225 | file_exists(dll_name); 226 | 227 | injectorcode cmds; 228 | init_injector_struct(cmds, dll_name); 229 | inject(pid, reinterpret_cast(&cmds), sizeof(injectorcode)); 230 | } 231 | 232 | void injector::inject(unsigned int pid, const BYTE * code, unsigned long int code_size) 233 | { 234 | if(!pid) 235 | throw injector_exception(L"Zero PID specified", __LINE__); 236 | 237 | handle_helper process, remote_thread; 238 | void * base_addr = NULL; 239 | 240 | try 241 | { 242 | base_addr = open_and_alloc(process, pid, code_size); 243 | write_to_memory(process, base_addr, code, code_size); 244 | 245 | run_remote_code(process, remote_thread, base_addr); 246 | wait_and_free(process, remote_thread, base_addr); 247 | } 248 | catch(const injector_exception&) 249 | { 250 | if(base_addr) 251 | VirtualFreeEx(process.get(), base_addr, 0, MEM_RELEASE); 252 | 253 | throw; 254 | } 255 | } 256 | -------------------------------------------------------------------------------- /src/injector.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "handle_helper.hpp" 9 | #include "injector_exception.hpp" 10 | 11 | class injector 12 | { 13 | typedef unsigned long (__stdcall * thr_rtn)(void *); 14 | typedef ULONGLONG QWORD; 15 | 16 | public: 17 | injector(); 18 | injector(const injector &inj); 19 | ~injector(); 20 | injector& operator=(const injector& inj); 21 | 22 | void inject(const std::wstring& proc_name, const std::wstring& dll_name); 23 | void inject(const std::wstring& proc_name, const BYTE * code, unsigned long int code_size); 24 | void inject(unsigned int pid, const std::wstring& dll_name); 25 | void inject(unsigned int pid, const BYTE * code, unsigned long int code_size); 26 | 27 | void set_blocking(bool active) { is_blocking_ = active; } 28 | void show() { std::cout< 2 | 3 | class injector_exception : public std::runtime_error 4 | { 5 | public: 6 | injector_exception(const std::wstring& err, long line) 7 | :std::runtime_error(""), 8 | error_text_(err), error_line_(line) 9 | {} 10 | 11 | const std::wstring& get_error() const 12 | { 13 | return error_text_; 14 | } 15 | 16 | private: 17 | std::wstring error_text_; 18 | long error_line_; 19 | }; 20 | --------------------------------------------------------------------------------