├── InjectorTest ├── InjectorTest.cpp ├── dllmain.cpp └── InjectorTest.vcxproj ├── test ├── main.cpp └── test.vcxproj ├── InjectorTest2 ├── dllmain.cpp ├── InjectorTest2.h ├── InjectorTest2.cpp └── InjectorTest2.vcxproj ├── README.md ├── GenericInjector ├── Misc.h ├── PEParser.h ├── ArgumentParser.h ├── PEParser.cpp ├── InjectedFunction.cpp ├── GenericInjector.vcxproj ├── GenericInjector.h ├── InjectedFunction.h ├── GenericInjector.cpp └── FunctionInfo.h ├── .gitignore ├── GenericInjector.sln └── LICENSE /InjectorTest/InjectorTest.cpp: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/main.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akemimadoka/GenericInjector/HEAD/test/main.cpp -------------------------------------------------------------------------------- /InjectorTest2/dllmain.cpp: -------------------------------------------------------------------------------- 1 | #include "InjectorTest2.h" 2 | 3 | InitInjector(InjectorTest2::GetInjector()) 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | GenericInjector 2 | ============================= 3 | GenericInjector是一个用于简化注入器编写的通用Win32程序注入器模板,作者的目标是通过使用它来快速开发注入器并减少不必要的错误。 4 | 5 | 部分源码及设计思想参考于 acaly 的工程 GSInjector ,在此向其致谢。 6 | 7 | 这个工程使用GPLv3协议开源,意在促进互相学习。工程本身还极不完善,作者不推荐将其或其一部分用于大型工程。 8 | 9 | 目前开发完成的功能有: 10 | 11 | * 分析已加载的Win32程序的输入表,并可以进行快速注入。 12 | 13 | * 分析对象的虚表并进行注入(仅在Visual Studio 2015生成的程序上验证通过,其他编译器生成的程序未进行测试)。 14 | 15 | * 提供一定程度上的类型安全的注入。 16 | 17 | 工程使用Visual Studio 2015进行编译,其他编译器未进行测试。 18 | 19 | 对于工程的疑问可以在百度贴吧上 @jxhzq1996 或者加 QQ 794424922 进行交流。 20 | -------------------------------------------------------------------------------- /InjectorTest2/InjectorTest2.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | class InjectorTest2 final 6 | : public GenericInjector 7 | { 8 | public: 9 | InjectorTest2(); 10 | ~InjectorTest2(); 11 | 12 | static InjectorTest2& GetInjector(); 13 | 14 | private: 15 | IDirect3D9* m_pDx9; 16 | IDirect3DDevice9* m_pDevice; 17 | 18 | void OnLoad() override; 19 | void OnUnload() override; 20 | 21 | void Dx9CreateHandler(UINT SDKVersion, IDirect3D9* pDx9); 22 | void Dx9CreateDeviceHandler(IDirect3D9* pDx9, UINT Adapter, D3DDEVTYPE DeviceType, HWND hFocusWindow, DWORD BehaviorFlags, D3DPRESENT_PARAMETERS* pPresentationParameters, IDirect3DDevice9** ppReturnedDeviceInterface, HRESULT RetCode); 23 | }; 24 | -------------------------------------------------------------------------------- /GenericInjector/Misc.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | template 5 | struct CaseIgnoredStringEqualTo; 6 | 7 | template <> 8 | struct CaseIgnoredStringEqualTo 9 | { 10 | bool operator()(std::basic_string const& a, std::basic_string const& b) const noexcept 11 | { 12 | return lstrcmpiW(a.c_str(), b.c_str()) == 0; 13 | } 14 | }; 15 | 16 | template <> 17 | struct CaseIgnoredStringEqualTo 18 | { 19 | bool operator()(std::basic_string const& a, std::basic_string const& b) const noexcept 20 | { 21 | return lstrcmpiA(a.c_str(), b.c_str()) == 0; 22 | } 23 | }; 24 | 25 | typedef std::basic_string tstring; 26 | 27 | template 28 | __forceinline tstring GetTString(Itea i1, Itea i2) 29 | { 30 | return tstring(i1, i2); 31 | } 32 | 33 | template 34 | __forceinline tstring GetTString(Container const& container) 35 | { 36 | return GetTString(container.begin(), container.end()); 37 | } -------------------------------------------------------------------------------- /GenericInjector/PEParser.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "InjectedFunction.h" 3 | #include "Misc.h" 4 | #include 5 | 6 | class PEPaser final 7 | { 8 | public: 9 | explicit PEPaser(const byte* pPEData); 10 | ~PEPaser() = default; 11 | 12 | bool DllImported(tstring const& DllName) const noexcept; 13 | 14 | LPDWORD GetImportFunctionAddress(tstring const& DllName, tstring const& Funcname) const noexcept; 15 | LPDWORD GetImportFunctionAddress(tstring const& DllName, DWORD Index) const noexcept; 16 | 17 | IMAGE_DOS_HEADER const& GetDosHeader() const; 18 | IMAGE_NT_HEADERS const& GetNTHeaders() const; 19 | std::vector const& GetSections() const; 20 | 21 | private: 22 | bool m_Inited; 23 | IMAGE_DOS_HEADER m_DosHeader; 24 | IMAGE_NT_HEADERS m_NTHeader; 25 | std::vector m_Sections; 26 | std::unordered_map, std::unordered_map>, std::hash, CaseIgnoredStringEqualTo> m_ImportTable; 27 | 28 | void init(const byte* pPEData); 29 | }; -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Debug/ 2 | Release/ 3 | GenericInjector/Debug/ 4 | GenericInjector/Release/ 5 | InjectorTest/Debug/ 6 | InjectorTest/Release/ 7 | test/Debug/ 8 | test/Release/ 9 | 10 | ipch/ 11 | 12 | *.sdf 13 | *.suo 14 | 15 | ############### 16 | # fixed file # 17 | ############### 18 | *.exe 19 | *.txt 20 | *.def 21 | *.dsp 22 | *.lib 23 | *.doc 24 | *.dll 25 | *.lnt 26 | *.pm 27 | *.xls 28 | *.dbg 29 | *.DBG 30 | *.o 31 | *.obj 32 | *.plg 33 | *.keep 34 | *.bin 35 | 36 | ############### 37 | # temp file # 38 | ############### 39 | ~* 40 | *.log 41 | *.cmm 42 | *.men 43 | *.ini 44 | *.fig 45 | *.cmp 46 | *.tmp 47 | 48 | ############### 49 | # script # 50 | ############### 51 | *.bat 52 | *.rar 53 | *.opendb 54 | /test/test.vcxproj.user 55 | /test/test.vcxproj.filters 56 | /InjectorTest2/InjectorTest2.vcxproj.user 57 | /InjectorTest2/InjectorTest2.vcxproj.filters 58 | /InjectorTest/InjectorTest.vcxproj.user 59 | /InjectorTest/InjectorTest.vcxproj.filters 60 | /GenericInjector.VC.db 61 | /GenericInjector/GenericInjector.vcxproj.user 62 | /GenericInjector/GenericInjector.vcxproj.filters 63 | /GenericInjector/GenericInjector.vcxproj.bak 64 | *.db 65 | -------------------------------------------------------------------------------- /GenericInjector/ArgumentParser.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "InjectedFunction.h" 3 | 4 | template 5 | class ArgumentParser final 6 | { 7 | template 8 | struct Get; 9 | 10 | template 11 | struct Get, I> 12 | { 13 | typedef TypeSequence seq; 14 | typedef GetType type; 15 | 16 | static type Impl(LPVOID pArg) 17 | { 18 | return *reinterpret_cast(pArg + SubSequence::Type::AlignedSize); 19 | } 20 | }; 21 | 22 | public: 23 | typedef typename GetFunctionAnalysis::FunctionAnalysis FuncInfo; 24 | 25 | explicit ArgumentParser(LPVOID pArgs) 26 | : ArgumentParser(pArgs, typename std::make_index_sequence::type{}) 27 | { 28 | } 29 | 30 | template 31 | decltype(auto) GetArg() 32 | { 33 | return std::get(m_Args); 34 | } 35 | 36 | template 37 | decltype(auto) GetArg() const 38 | { 39 | return std::get(m_Args); 40 | } 41 | 42 | private: 43 | typename CastToTuple::Type m_Args; 44 | 45 | template 46 | ArgumentParser(LPVOID pArgs, std::index_sequence) 47 | : m_Args{ Get::Impl(pArgs)... } 48 | { 49 | } 50 | }; 51 | -------------------------------------------------------------------------------- /InjectorTest2/InjectorTest2.cpp: -------------------------------------------------------------------------------- 1 | #include "InjectorTest2.h" 2 | #include 3 | #include 4 | #include 5 | 6 | InjectorTest2::InjectorTest2() 7 | : m_pDx9(nullptr), m_pDevice(nullptr) 8 | { 9 | } 10 | 11 | InjectorTest2::~InjectorTest2() 12 | { 13 | } 14 | 15 | InjectorTest2& InjectorTest2::GetInjector() 16 | { 17 | static InjectorTest2 Instance; 18 | 19 | return Instance; 20 | } 21 | 22 | void InjectorTest2::OnLoad() 23 | { 24 | try 25 | { 26 | auto pInjector = InjectImportTable(_T("d3d9.dll"), _T("Direct3DCreate9")); 27 | pInjector->RegisterAfter(this, &InjectorTest2::Dx9CreateHandler); 28 | } 29 | catch (std::system_error& sysex) 30 | { 31 | std::stringstream ss; 32 | ss << "what: " << sysex.what() << ", code:" << sysex.code(); 33 | 34 | MessageBoxA(NULL, ss.str().c_str(), "Unhandled exception caught", MB_OK | MB_ICONERROR); 35 | std::cerr << ss.str() << std::endl; 36 | exit(EXIT_FAILURE); 37 | } 38 | catch (std::exception& ex) 39 | { 40 | MessageBoxA(NULL, ex.what(), "Unhandled exception caught", MB_OK | MB_ICONERROR); 41 | std::cerr << ex.what() << std::endl; 42 | exit(EXIT_FAILURE); 43 | } 44 | } 45 | 46 | void InjectorTest2::OnUnload() 47 | { 48 | } 49 | 50 | void InjectorTest2::Dx9CreateHandler(UINT /*SDKVersion*/, IDirect3D9* pDx9) 51 | { 52 | if (pDx9 != nullptr) 53 | { 54 | m_pDx9 = pDx9; 55 | auto pInjector = InjectVirtualTable(m_pDx9, 16); 56 | pInjector->RegisterAfter(this, &InjectorTest2::Dx9CreateDeviceHandler); 57 | //UnhookInjector(reinterpret_cast(pInjector->GetFunctionPointer())); 58 | } 59 | } 60 | 61 | void InjectorTest2::Dx9CreateDeviceHandler(IDirect3D9* pDx9, UINT Adapter, D3DDEVTYPE DeviceType, HWND hFocusWindow, DWORD BehaviorFlags, D3DPRESENT_PARAMETERS* pPresentationParameters, IDirect3DDevice9** ppReturnedDeviceInterface, HRESULT RetCode) 62 | { 63 | if (pDx9 == m_pDx9 && RetCode == S_OK && ppReturnedDeviceInterface != nullptr && *ppReturnedDeviceInterface != nullptr) 64 | { 65 | m_pDevice = *ppReturnedDeviceInterface; 66 | std::stringstream ss; 67 | ss << m_pDx9 << "@" << m_pDevice; 68 | MessageBoxA(NULL, ss.str().c_str(), "Dx Got", MB_OK); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /InjectorTest/dllmain.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | // Inherit from GenericInjector to design your own injector 8 | class InjectorTest final 9 | : public GenericInjector 10 | { 11 | public: 12 | InjectorTest() 13 | { 14 | } 15 | 16 | ~InjectorTest() 17 | { 18 | } 19 | 20 | __declspec(noinline) 21 | void TestHook(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, double uType, int ReturnedValue) 22 | { 23 | // Output the argument of our injected function MessageBoxW passed by origin program 24 | std::wcout << hWnd << std::endl << lpText << std::endl << lpCaption << std::endl << uType << std::endl << ReturnedValue << std::endl; 25 | //GetPEPaser().GetImportFunctionAddress(_T(""), _T("")); 26 | // You can modify the returned value by simply assigning it 27 | // Turn off the optimization if you are using release configuration 28 | ReturnedValue = IDYES; 29 | 30 | // But you cannot modify arguments, lol 31 | } 32 | 33 | __declspec(noinline) 34 | static void PutC(int Code, int ReturnValue) 35 | { 36 | TCHAR tStr[2] {0}; 37 | tStr[0] = static_cast(Code); 38 | MessageBox(NULL, _T("Hooked"), tStr, MB_OK); 39 | } 40 | 41 | void OnLoad() override 42 | { 43 | try 44 | { 45 | std::wcout.imbue(std::locale("", LC_CTYPE)); 46 | auto pFunc = GetPEPaser().GetImportFunctionAddress(_T("user32.dll"), _T("MessageBoxW")); 47 | std::cout << reinterpret_cast(*pFunc) << std::endl << MessageBoxW << std::endl << GetProcAddress(GetModuleHandleA("user32.dll"), "MessageBoxW") << std::endl; 48 | std::cout << GetPEPaser().GetImportFunctionAddress(_T("ucrtbased.dll"), _T("__stdio_common_vfprintf_s")) << std::endl << std::endl; 49 | auto pInjector = InjectImportTable(_T("user32.dll"), _T("MessageBoxW")); 50 | pInjector->RegisterAfter(this, &InjectorTest::TestHook); 51 | auto pInjector2 = InjectImportTable(_T("ucrtbased.dll"), _T("putchar")); 52 | pInjector2->RegisterAfter(PutC); 53 | pInjector2->Replace(nullptr); 54 | InjectImportTable(_T("ucrtbased.dll"), _T("__stdio_common_vfprintf_s")); 55 | 56 | constexpr byte Pattern[] = { 0x8B, 0xF4, 0x68, 0x1D, 0x11, '*', '*', 0x8B, 0xFC, 0x68, 0x1D, 0x11, '*', '*', }; 57 | constexpr byte Wildcard[] = { '*', }; 58 | auto pMem = FindMemory(nullptr, nullptr, Pattern, Wildcard, 1); 59 | 60 | if (pMem) 61 | { 62 | constexpr byte Target[] = { 0x90, }; 63 | //ModifyCode(reinterpret_cast(pMem) - reinterpret_cast(GetInstance()), sizeof Pattern, Target, false); 64 | InjectCode(reinterpret_cast(pMem) - reinterpret_cast(GetInstance()), 0x47, Target); 65 | } 66 | } 67 | catch (std::system_error& sysex) 68 | { 69 | std::stringstream ss; 70 | ss << "what: " << sysex.what() << ", code:" << sysex.code(); 71 | 72 | MessageBoxA(NULL, ss.str().c_str(), "Unhandled exception caught", MB_OK | MB_ICONERROR); 73 | std::cerr << ss.str() << std::endl; 74 | exit(EXIT_FAILURE); 75 | } 76 | catch (std::exception& ex) 77 | { 78 | MessageBoxA(NULL, ex.what(), "Unhandled exception caught", MB_OK | MB_ICONERROR); 79 | std::cerr << ex.what() << std::endl; 80 | exit(EXIT_FAILURE); 81 | } 82 | } 83 | 84 | void OnUnload() override 85 | { 86 | } 87 | 88 | static InjectorTest& GetInjectorInstance() 89 | { 90 | static InjectorTest Instance; 91 | return Instance; 92 | } 93 | 94 | private: 95 | 96 | }; 97 | 98 | // Implement our injector, you can also use global variant 99 | // If you don't use GenericInjetor in a dll project, you don't need to use InitInjector but you need to implement your own init function 100 | InitInjector(InjectorTest::GetInjectorInstance()) 101 | -------------------------------------------------------------------------------- /GenericInjector.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.24720.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test", "test\test.vcxproj", "{7F823FA5-5165-4D8F-9DDD-A78BD7548B2B}" 7 | ProjectSection(ProjectDependencies) = postProject 8 | {14B6C989-6CB8-4F22-8B09-115EFCD57342} = {14B6C989-6CB8-4F22-8B09-115EFCD57342} 9 | EndProjectSection 10 | EndProject 11 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GenericInjector", "GenericInjector\GenericInjector.vcxproj", "{B760AC22-3E1A-4FEA-8F51-82A517EE6642}" 12 | EndProject 13 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "InjectorTest", "InjectorTest\InjectorTest.vcxproj", "{14B6C989-6CB8-4F22-8B09-115EFCD57342}" 14 | ProjectSection(ProjectDependencies) = postProject 15 | {B760AC22-3E1A-4FEA-8F51-82A517EE6642} = {B760AC22-3E1A-4FEA-8F51-82A517EE6642} 16 | EndProjectSection 17 | EndProject 18 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "InjectorTest2", "InjectorTest2\InjectorTest2.vcxproj", "{1F731EBD-5A0E-4626-A82A-CC33489FD722}" 19 | ProjectSection(ProjectDependencies) = postProject 20 | {B760AC22-3E1A-4FEA-8F51-82A517EE6642} = {B760AC22-3E1A-4FEA-8F51-82A517EE6642} 21 | EndProjectSection 22 | EndProject 23 | Global 24 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 25 | Debug|x64 = Debug|x64 26 | Debug|x86 = Debug|x86 27 | Release|x64 = Release|x64 28 | Release|x86 = Release|x86 29 | EndGlobalSection 30 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 31 | {7F823FA5-5165-4D8F-9DDD-A78BD7548B2B}.Debug|x64.ActiveCfg = Debug|x64 32 | {7F823FA5-5165-4D8F-9DDD-A78BD7548B2B}.Debug|x64.Build.0 = Debug|x64 33 | {7F823FA5-5165-4D8F-9DDD-A78BD7548B2B}.Debug|x86.ActiveCfg = Debug|Win32 34 | {7F823FA5-5165-4D8F-9DDD-A78BD7548B2B}.Debug|x86.Build.0 = Debug|Win32 35 | {7F823FA5-5165-4D8F-9DDD-A78BD7548B2B}.Release|x64.ActiveCfg = Release|x64 36 | {7F823FA5-5165-4D8F-9DDD-A78BD7548B2B}.Release|x64.Build.0 = Release|x64 37 | {7F823FA5-5165-4D8F-9DDD-A78BD7548B2B}.Release|x86.ActiveCfg = Release|Win32 38 | {7F823FA5-5165-4D8F-9DDD-A78BD7548B2B}.Release|x86.Build.0 = Release|Win32 39 | {B760AC22-3E1A-4FEA-8F51-82A517EE6642}.Debug|x64.ActiveCfg = Debug|x64 40 | {B760AC22-3E1A-4FEA-8F51-82A517EE6642}.Debug|x64.Build.0 = Debug|x64 41 | {B760AC22-3E1A-4FEA-8F51-82A517EE6642}.Debug|x86.ActiveCfg = Debug|Win32 42 | {B760AC22-3E1A-4FEA-8F51-82A517EE6642}.Debug|x86.Build.0 = Debug|Win32 43 | {B760AC22-3E1A-4FEA-8F51-82A517EE6642}.Release|x64.ActiveCfg = Release|x64 44 | {B760AC22-3E1A-4FEA-8F51-82A517EE6642}.Release|x64.Build.0 = Release|x64 45 | {B760AC22-3E1A-4FEA-8F51-82A517EE6642}.Release|x86.ActiveCfg = Release|Win32 46 | {B760AC22-3E1A-4FEA-8F51-82A517EE6642}.Release|x86.Build.0 = Release|Win32 47 | {14B6C989-6CB8-4F22-8B09-115EFCD57342}.Debug|x64.ActiveCfg = Debug|x64 48 | {14B6C989-6CB8-4F22-8B09-115EFCD57342}.Debug|x64.Build.0 = Debug|x64 49 | {14B6C989-6CB8-4F22-8B09-115EFCD57342}.Debug|x86.ActiveCfg = Debug|Win32 50 | {14B6C989-6CB8-4F22-8B09-115EFCD57342}.Debug|x86.Build.0 = Debug|Win32 51 | {14B6C989-6CB8-4F22-8B09-115EFCD57342}.Release|x64.ActiveCfg = Release|x64 52 | {14B6C989-6CB8-4F22-8B09-115EFCD57342}.Release|x64.Build.0 = Release|x64 53 | {14B6C989-6CB8-4F22-8B09-115EFCD57342}.Release|x86.ActiveCfg = Release|Win32 54 | {14B6C989-6CB8-4F22-8B09-115EFCD57342}.Release|x86.Build.0 = Release|Win32 55 | {1F731EBD-5A0E-4626-A82A-CC33489FD722}.Debug|x64.ActiveCfg = Debug|x64 56 | {1F731EBD-5A0E-4626-A82A-CC33489FD722}.Debug|x64.Build.0 = Debug|x64 57 | {1F731EBD-5A0E-4626-A82A-CC33489FD722}.Debug|x86.ActiveCfg = Debug|Win32 58 | {1F731EBD-5A0E-4626-A82A-CC33489FD722}.Debug|x86.Build.0 = Debug|Win32 59 | {1F731EBD-5A0E-4626-A82A-CC33489FD722}.Release|x64.ActiveCfg = Release|x64 60 | {1F731EBD-5A0E-4626-A82A-CC33489FD722}.Release|x64.Build.0 = Release|x64 61 | {1F731EBD-5A0E-4626-A82A-CC33489FD722}.Release|x86.ActiveCfg = Release|Win32 62 | {1F731EBD-5A0E-4626-A82A-CC33489FD722}.Release|x86.Build.0 = Release|Win32 63 | EndGlobalSection 64 | GlobalSection(SolutionProperties) = preSolution 65 | HideSolutionNode = FALSE 66 | EndGlobalSection 67 | EndGlobal 68 | -------------------------------------------------------------------------------- /GenericInjector/PEParser.cpp: -------------------------------------------------------------------------------- 1 | #include "PEParser.h" 2 | 3 | PEPaser::PEPaser(const byte* pPEData) 4 | : m_Inited(false) 5 | { 6 | if (pPEData == nullptr) 7 | { 8 | throw std::invalid_argument("pPEData can not be a nullptr."); 9 | } 10 | 11 | init(pPEData); 12 | } 13 | 14 | bool PEPaser::DllImported(tstring const& DllName) const noexcept 15 | { 16 | return m_Inited ? m_ImportTable.find(DllName) != m_ImportTable.end() : false; 17 | } 18 | 19 | LPDWORD PEPaser::GetImportFunctionAddress(tstring const& DllName, tstring const& Funcname) const noexcept 20 | { 21 | if (!m_Inited) 22 | { 23 | return nullptr; 24 | } 25 | 26 | auto tItea = m_ImportTable.find(DllName); 27 | if (tItea == m_ImportTable.end()) 28 | { 29 | return nullptr; 30 | } 31 | 32 | auto tFuncItea = tItea->second.first.find(Funcname); 33 | return tFuncItea == tItea->second.first.end() ? nullptr : tFuncItea->second; 34 | } 35 | 36 | LPDWORD PEPaser::GetImportFunctionAddress(tstring const& DllName, DWORD Index) const noexcept 37 | { 38 | if (!m_Inited) 39 | { 40 | return nullptr; 41 | } 42 | 43 | auto tItea = m_ImportTable.find(DllName); 44 | if (tItea == m_ImportTable.end()) 45 | { 46 | return nullptr; 47 | } 48 | 49 | auto tFuncItea = tItea->second.second.find(Index); 50 | return tFuncItea == tItea->second.second.end() ? nullptr : tFuncItea->second; 51 | } 52 | 53 | IMAGE_DOS_HEADER const& PEPaser::GetDosHeader() const 54 | { 55 | if (!m_Inited) 56 | { 57 | throw std::runtime_error("Not initialized."); 58 | } 59 | 60 | return m_DosHeader; 61 | } 62 | 63 | IMAGE_NT_HEADERS const& PEPaser::GetNTHeaders() const 64 | { 65 | if (!m_Inited) 66 | { 67 | throw std::runtime_error("Not initialized."); 68 | } 69 | 70 | return m_NTHeader; 71 | } 72 | 73 | std::vector const& PEPaser::GetSections() const 74 | { 75 | if (!m_Inited) 76 | { 77 | throw std::runtime_error("Not initialized."); 78 | } 79 | 80 | return m_Sections; 81 | } 82 | 83 | void PEPaser::init(const byte* pPEData) 84 | { 85 | m_Sections.clear(); 86 | m_ImportTable.clear(); 87 | auto pCurrentPointer = pPEData; 88 | 89 | if (IsBadReadPtr(pCurrentPointer, sizeof(IMAGE_DOS_HEADER))) 90 | { 91 | throw std::invalid_argument("Pointer not readable."); 92 | } 93 | memcpy_s(&m_DosHeader, sizeof(IMAGE_DOS_HEADER), pCurrentPointer, sizeof(IMAGE_DOS_HEADER)); 94 | if (m_DosHeader.e_magic != IMAGE_DOS_SIGNATURE) 95 | { 96 | throw std::invalid_argument("Not a valid pe header."); 97 | } 98 | 99 | pCurrentPointer = pPEData + m_DosHeader.e_lfanew; 100 | if (IsBadReadPtr(pCurrentPointer, sizeof(IMAGE_NT_HEADERS))) 101 | { 102 | throw std::invalid_argument("Pointer not readable."); 103 | } 104 | memcpy_s(&m_NTHeader, sizeof(IMAGE_NT_HEADERS), pCurrentPointer, sizeof(IMAGE_NT_HEADERS)); 105 | if (m_NTHeader.Signature != IMAGE_NT_SIGNATURE) 106 | { 107 | throw std::invalid_argument("Not a valid pe header."); 108 | } 109 | 110 | pCurrentPointer += sizeof(IMAGE_NT_HEADERS); 111 | m_Sections.resize(m_NTHeader.FileHeader.NumberOfSections); 112 | if (IsBadReadPtr(pCurrentPointer, m_Sections.size() * sizeof(IMAGE_SECTION_HEADER))) 113 | { 114 | throw std::invalid_argument("Pointer not readable."); 115 | } 116 | memcpy_s(m_Sections.data(), m_Sections.size() * sizeof(IMAGE_SECTION_HEADER), pCurrentPointer, m_Sections.size() * sizeof(IMAGE_SECTION_HEADER)); 117 | pCurrentPointer += m_Sections.size() * sizeof(IMAGE_SECTION_HEADER); 118 | std::vector ImportTable(m_NTHeader.OptionalHeader.DataDirectory[1].Size / sizeof(IMAGE_IMPORT_DESCRIPTOR)); 119 | 120 | for (auto itea = m_Sections.rbegin(); itea != m_Sections.rend(); ++itea) 121 | { 122 | if (itea->VirtualAddress <= m_NTHeader.OptionalHeader.DataDirectory[1].VirtualAddress) 123 | { 124 | if (IsBadReadPtr(m_NTHeader.OptionalHeader.DataDirectory[1].VirtualAddress + pPEData, ImportTable.size() * sizeof(IMAGE_IMPORT_DESCRIPTOR))) 125 | { 126 | throw std::invalid_argument("Pointer not readable."); 127 | } 128 | memcpy_s(ImportTable.data(), ImportTable.size() * sizeof(IMAGE_IMPORT_DESCRIPTOR), m_NTHeader.OptionalHeader.DataDirectory[1].VirtualAddress + pPEData, ImportTable.size() * sizeof(IMAGE_IMPORT_DESCRIPTOR)); 129 | for (auto const& i : ImportTable) 130 | { 131 | if (i.FirstThunk != 0ul) 132 | { 133 | auto& FunctionInfo = m_ImportTable[GetTString(std::string(reinterpret_cast(pPEData + i.Name)))]; 134 | 135 | pCurrentPointer = i.OriginalFirstThunk + pPEData; 136 | for (uint iFunc = 0u; ; ++iFunc) 137 | { 138 | IMAGE_THUNK_DATA FuncThunkData; 139 | if (IsBadReadPtr(pCurrentPointer, sizeof(IMAGE_THUNK_DATA))) 140 | { 141 | throw std::invalid_argument("Pointer not readable."); 142 | } 143 | memcpy_s(&FuncThunkData, sizeof(IMAGE_THUNK_DATA), pCurrentPointer, sizeof(IMAGE_THUNK_DATA)); 144 | pCurrentPointer += sizeof(IMAGE_THUNK_DATA); 145 | if (FuncThunkData.u1.Ordinal == 0ul) 146 | { 147 | break; 148 | } 149 | 150 | if (FuncThunkData.u1.Ordinal & IMAGE_ORDINAL_FLAG) 151 | { 152 | FunctionInfo.second[FuncThunkData.u1.Ordinal & ~IMAGE_ORDINAL_FLAG] = const_cast(reinterpret_cast(pPEData + i.FirstThunk + iFunc * sizeof(IMAGE_THUNK_DATA))); 153 | } 154 | else 155 | { 156 | // ignore hint 157 | FunctionInfo.first[GetTString(std::string(reinterpret_cast(pPEData + FuncThunkData.u1.Function + sizeof(WORD))))] = const_cast(reinterpret_cast(pPEData + i.FirstThunk + iFunc * sizeof(IMAGE_THUNK_DATA))); 158 | } 159 | } 160 | } 161 | } 162 | 163 | break; 164 | } 165 | } 166 | 167 | m_Inited = true; 168 | } 169 | -------------------------------------------------------------------------------- /GenericInjector/InjectedFunction.cpp: -------------------------------------------------------------------------------- 1 | #include "InjectedFunction.h" 2 | 3 | DWORD Functor::CallImpl(CallingConventionEnum CallingConvention, LPVOID pStackTop, LPVOID pArgs, LPVOID pOriginObject, LPDWORD lpReturnValue, DWORD ArgSize, DWORD ActualArgSize, bool ReceiveReturnedValue) const 4 | { 5 | LPVOID pmemcpy = &memcpy; 6 | LPVOID pFunc = GetFunctionPointer(); 7 | if (!pFunc) 8 | return 0ul; 9 | 10 | LPVOID pObject = GetObjectPointer(); 11 | 12 | if (!pArgs) 13 | { 14 | pArgs = pStackTop; 15 | } 16 | 17 | DWORD tReturnValue = 0ul; 18 | 19 | switch (CallingConvention) 20 | { 21 | case CallingConventionEnum::Thiscall: 22 | if (!pObject) 23 | { 24 | if (!pOriginObject) 25 | { 26 | return 0ul; 27 | } 28 | 29 | pObject = pOriginObject; 30 | pOriginObject = nullptr; 31 | } 32 | else 33 | { 34 | if (pOriginObject) 35 | { 36 | ActualArgSize -= sizeof pOriginObject; 37 | } 38 | } 39 | 40 | if (!ReceiveReturnedValue) 41 | { 42 | __asm 43 | { 44 | push ecx; 45 | push edx; 46 | 47 | sub esp, ActualArgSize; 48 | lea eax, [esp]; 49 | 50 | push ActualArgSize; 51 | push pArgs; 52 | push eax; 53 | call pmemcpy; 54 | add esp, 12; 55 | 56 | cmp pOriginObject, 0; 57 | je NoObject1; 58 | push pOriginObject; 59 | NoObject1: 60 | 61 | mov eax, pFunc; 62 | mov ecx, pObject; 63 | call eax; 64 | mov tReturnValue, eax; 65 | 66 | pop edx; 67 | pop ecx; 68 | } 69 | } 70 | else 71 | { 72 | tReturnValue = *lpReturnValue; 73 | __asm 74 | { 75 | push ecx; 76 | push edx; 77 | 78 | push tReturnValue; 79 | sub esp, ActualArgSize; 80 | lea eax, [esp]; 81 | 82 | push ActualArgSize; 83 | push pArgs; 84 | push eax; 85 | call pmemcpy; 86 | add esp, 12; 87 | 88 | cmp pOriginObject, 0; 89 | je NoObject2; 90 | push pOriginObject; 91 | NoObject2: 92 | 93 | mov eax, pFunc; 94 | mov ecx, pObject; 95 | call eax; 96 | mov eax, [esp - 4]; 97 | mov tReturnValue, eax; 98 | 99 | pop edx; 100 | pop ecx; 101 | } 102 | } 103 | 104 | return tReturnValue; 105 | case CallingConventionEnum::Cdecl: 106 | { 107 | // ReSharper disable once CppEntityNeverUsed 108 | LPDWORD tpReturn; 109 | // Only replace function can have variable argument 110 | if (HasVariableArgument()) 111 | { 112 | __asm 113 | { 114 | push ecx; 115 | push edx; 116 | 117 | sub esp, DefaultAlignBase * 2; 118 | lea ebx, [esp]; 119 | 120 | mov esp, pStackTop; 121 | 122 | cmp pOriginObject, 0; 123 | je NoObject7; 124 | mov ebx, [esp - DefaultAlignBase]; 125 | push pOriginObject; 126 | NoObject7: 127 | 128 | cmp pObject, 0; 129 | je NoThis5; 130 | lea eax, [ebx + DefaultAlignBase]; 131 | mov eax, [esp]; 132 | push pObject; 133 | NoThis5: 134 | 135 | mov eax, pFunc; 136 | call eax; 137 | 138 | mov tReturnValue, eax; 139 | 140 | cmp pOriginObject, 0; 141 | je NoObject8; 142 | lea eax, [esp - DefaultAlignBase]; 143 | mov eax, [ebx]; 144 | NoObject8: 145 | 146 | cmp pObject, 0; 147 | je NoThis6; 148 | mov esp, [ebx + DefaultAlignBase]; 149 | jmp EndOfInject; 150 | 151 | NoThis6: 152 | lea esp, [ebx + DefaultAlignBase * 2]; 153 | 154 | EndOfInject: 155 | pop ecx; 156 | pop edx; 157 | } 158 | } 159 | else 160 | { 161 | if (!ReceiveReturnedValue) 162 | { 163 | __asm 164 | { 165 | push ecx; 166 | push edx; 167 | 168 | sub esp, ActualArgSize; 169 | lea eax, [esp]; 170 | mov tpReturn, eax; 171 | 172 | push ActualArgSize; 173 | push pArgs; 174 | push eax; 175 | call pmemcpy; 176 | add esp, 12; 177 | 178 | cmp pOriginObject, 0; 179 | je NoObject3; 180 | push pOriginObject; 181 | NoObject3: 182 | 183 | cmp pObject, 0; 184 | je NoThis1; 185 | push pObject; 186 | NoThis1: 187 | 188 | mov eax, pFunc; 189 | call eax; 190 | 191 | push ArgSize; 192 | push tpReturn; 193 | push pStackTop; 194 | call pmemcpy; 195 | add esp, 12; 196 | 197 | mov tReturnValue, eax; 198 | add esp, ActualArgSize; 199 | 200 | pop edx; 201 | pop ecx; 202 | } 203 | } 204 | else 205 | { 206 | tReturnValue = *lpReturnValue; 207 | __asm 208 | { 209 | push ecx; 210 | push edx; 211 | 212 | push tReturnValue; 213 | sub esp, ActualArgSize; 214 | lea eax, [esp]; 215 | mov tpReturn, eax; 216 | 217 | push ActualArgSize; 218 | push pArgs; 219 | push eax; 220 | call pmemcpy; 221 | add esp, 12; 222 | 223 | cmp pOriginObject, 0; 224 | je NoObject4; 225 | push pOriginObject; 226 | NoObject4: 227 | 228 | cmp pObject, 0; 229 | je NoThis2; 230 | push pObject; 231 | NoThis2: 232 | 233 | mov eax, pFunc; 234 | call eax; 235 | 236 | push ArgSize; 237 | push tpReturn; 238 | push pStackTop; 239 | call pmemcpy; 240 | add esp, 12; 241 | 242 | add esp, ActualArgSize; 243 | pop tReturnValue; 244 | 245 | pop edx; 246 | pop ecx; 247 | } 248 | } 249 | } 250 | 251 | return tReturnValue; 252 | } 253 | case CallingConventionEnum::Stdcall: 254 | if (!ReceiveReturnedValue) 255 | { 256 | __asm 257 | { 258 | push ecx; 259 | push edx; 260 | 261 | sub esp, ActualArgSize; 262 | lea eax, [esp]; 263 | 264 | push ActualArgSize; 265 | push pArgs; 266 | push eax; 267 | call pmemcpy; 268 | add esp, 12; 269 | 270 | cmp pOriginObject, 0; 271 | je NoObject5; 272 | push pOriginObject; 273 | NoObject5: 274 | 275 | cmp pObject, 0; 276 | je NoThis3; 277 | push pObject; 278 | NoThis3: 279 | 280 | mov eax, pFunc; 281 | call eax; 282 | mov tReturnValue, eax; 283 | 284 | pop edx; 285 | pop ecx; 286 | } 287 | } 288 | else 289 | { 290 | tReturnValue = *lpReturnValue; 291 | __asm 292 | { 293 | push ecx; 294 | push edx; 295 | 296 | 297 | push tReturnValue; 298 | sub esp, ActualArgSize; 299 | lea eax, [esp]; 300 | 301 | push ActualArgSize; 302 | push pArgs; 303 | push eax; 304 | call pmemcpy; 305 | add esp, 12; 306 | 307 | cmp pOriginObject, 0; 308 | je NoObject6; 309 | push pOriginObject; 310 | NoObject6: 311 | 312 | cmp pObject, 0; 313 | je NoThis4; 314 | push pObject; 315 | NoThis4: 316 | 317 | mov eax, pFunc; 318 | call eax; 319 | mov eax, [esp - 4]; 320 | mov tReturnValue, eax; 321 | 322 | pop edx; 323 | pop ecx; 324 | } 325 | } 326 | 327 | return tReturnValue; 328 | default: 329 | break; 330 | } 331 | 332 | return 0ul; 333 | } 334 | -------------------------------------------------------------------------------- /test/test.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {7F823FA5-5165-4D8F-9DDD-A78BD7548B2B} 23 | Win32Proj 24 | test 25 | 8.1 26 | test 27 | 28 | 29 | 30 | Application 31 | true 32 | v141 33 | Unicode 34 | 35 | 36 | Application 37 | false 38 | v141 39 | true 40 | Unicode 41 | 42 | 43 | Application 44 | true 45 | v141 46 | Unicode 47 | 48 | 49 | Application 50 | false 51 | v141 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | true 75 | 76 | 77 | true 78 | 79 | 80 | false 81 | 82 | 83 | false 84 | 85 | 86 | 87 | 88 | 89 | Level3 90 | Disabled 91 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 92 | true 93 | MultiThreadedDebugDLL 94 | 95 | 96 | Console 97 | true 98 | 99 | 100 | 101 | 102 | 103 | 104 | Level3 105 | Disabled 106 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 107 | true 108 | MultiThreadedDebugDLL 109 | 110 | 111 | Console 112 | true 113 | 114 | 115 | 116 | 117 | Level3 118 | 119 | 120 | Full 121 | true 122 | true 123 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 124 | true 125 | MultiThreadedDLL 126 | AnySuitable 127 | 128 | 129 | Console 130 | true 131 | true 132 | true 133 | 134 | 135 | 136 | 137 | Level3 138 | 139 | 140 | MaxSpeed 141 | true 142 | true 143 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 144 | true 145 | 146 | 147 | Console 148 | true 149 | true 150 | true 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | -------------------------------------------------------------------------------- /GenericInjector/GenericInjector.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {B760AC22-3E1A-4FEA-8F51-82A517EE6642} 23 | Win32Proj 24 | GenericInjector 25 | 10.0.15063.0 26 | 27 | 28 | 29 | StaticLibrary 30 | true 31 | v141 32 | Unicode 33 | 34 | 35 | StaticLibrary 36 | false 37 | v141 38 | true 39 | Unicode 40 | 41 | 42 | StaticLibrary 43 | true 44 | v141 45 | Unicode 46 | 47 | 48 | StaticLibrary 49 | false 50 | v141 51 | true 52 | Unicode 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | true 74 | 75 | 76 | true 77 | 78 | 79 | false 80 | 81 | 82 | false 83 | 84 | 85 | 86 | 87 | 88 | Level3 89 | Disabled 90 | WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) 91 | true 92 | MultiThreadedDebugDLL 93 | 94 | 95 | Windows 96 | true 97 | 98 | 99 | 100 | 101 | 102 | 103 | Level3 104 | Disabled 105 | _DEBUG;_LIB;%(PreprocessorDefinitions) 106 | true 107 | MultiThreadedDebugDLL 108 | 109 | 110 | Windows 111 | true 112 | 113 | 114 | 115 | 116 | Level3 117 | 118 | 119 | MaxSpeed 120 | false 121 | true 122 | WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) 123 | true 124 | 125 | 126 | Windows 127 | true 128 | true 129 | true 130 | 131 | 132 | 133 | 134 | Level3 135 | 136 | 137 | MaxSpeed 138 | true 139 | true 140 | NDEBUG;_LIB;%(PreprocessorDefinitions) 141 | true 142 | 143 | 144 | Windows 145 | true 146 | true 147 | true 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | -------------------------------------------------------------------------------- /InjectorTest2/InjectorTest2.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {1F731EBD-5A0E-4626-A82A-CC33489FD722} 23 | Win32Proj 24 | InjectorTest2 25 | 8.1 26 | 27 | 28 | 29 | DynamicLibrary 30 | true 31 | v141 32 | Unicode 33 | 34 | 35 | DynamicLibrary 36 | false 37 | v141 38 | true 39 | Unicode 40 | 41 | 42 | DynamicLibrary 43 | true 44 | v141 45 | Unicode 46 | 47 | 48 | DynamicLibrary 49 | false 50 | v141 51 | true 52 | Unicode 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | true 74 | 75 | 76 | true 77 | 78 | 79 | false 80 | 81 | 82 | false 83 | 84 | 85 | 86 | 87 | 88 | Level3 89 | Disabled 90 | WIN32;_DEBUG;_WINDOWS;_USRDLL;INJECTORTEST2_EXPORTS;%(PreprocessorDefinitions) 91 | true 92 | ..\GenericInjector;%(AdditionalIncludeDirectories) 93 | MultiThreadedDebugDLL 94 | 95 | 96 | Windows 97 | true 98 | ..\$(Configuration)\;%(AdditionalLibraryDirectories) 99 | GenericInjector.lib;%(AdditionalDependencies) 100 | 101 | 102 | 103 | 104 | 105 | 106 | Level3 107 | Disabled 108 | _DEBUG;_WINDOWS;_USRDLL;INJECTORTEST2_EXPORTS;%(PreprocessorDefinitions) 109 | true 110 | MultiThreadedDebugDLL 111 | 112 | 113 | Windows 114 | true 115 | 116 | 117 | 118 | 119 | Level3 120 | 121 | 122 | MaxSpeed 123 | true 124 | true 125 | WIN32;NDEBUG;_WINDOWS;_USRDLL;INJECTORTEST2_EXPORTS;%(PreprocessorDefinitions) 126 | true 127 | ..\GenericInjector;%(AdditionalIncludeDirectories) 128 | 129 | 130 | Windows 131 | true 132 | true 133 | true 134 | ..\$(Configuration)\;%(AdditionalLibraryDirectories) 135 | GenericInjector.lib;%(AdditionalDependencies) 136 | 137 | 138 | 139 | 140 | Level3 141 | 142 | 143 | MaxSpeed 144 | true 145 | true 146 | NDEBUG;_WINDOWS;_USRDLL;INJECTORTEST2_EXPORTS;%(PreprocessorDefinitions) 147 | true 148 | 149 | 150 | Windows 151 | true 152 | true 153 | true 154 | 155 | 156 | 157 | 158 | false 159 | 160 | 161 | false 162 | 163 | 164 | false 165 | 166 | 167 | false 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | -------------------------------------------------------------------------------- /InjectorTest/InjectorTest.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {14B6C989-6CB8-4F22-8B09-115EFCD57342} 23 | Win32Proj 24 | InjectorTest 25 | 8.1 26 | 27 | 28 | 29 | DynamicLibrary 30 | true 31 | v141 32 | Unicode 33 | 34 | 35 | DynamicLibrary 36 | false 37 | v141 38 | true 39 | Unicode 40 | 41 | 42 | DynamicLibrary 43 | true 44 | v141 45 | Unicode 46 | 47 | 48 | DynamicLibrary 49 | false 50 | v141 51 | true 52 | Unicode 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | true 74 | 75 | 76 | true 77 | 78 | 79 | false 80 | 81 | 82 | false 83 | 84 | 85 | 86 | 87 | 88 | Level3 89 | Disabled 90 | WIN32;_DEBUG;_WINDOWS;_USRDLL;INJECTORTEST_EXPORTS;%(PreprocessorDefinitions) 91 | true 92 | ..\GenericInjector;%(AdditionalIncludeDirectories) 93 | MultiThreadedDebugDLL 94 | 95 | 96 | Windows 97 | true 98 | ..\$(Configuration)\;%(AdditionalLibraryDirectories) 99 | GenericInjector.lib;%(AdditionalDependencies) 100 | 101 | 102 | 103 | 104 | 105 | 106 | Level3 107 | Disabled 108 | _DEBUG;_WINDOWS;_USRDLL;INJECTORTEST_EXPORTS;%(PreprocessorDefinitions) 109 | true 110 | ..\GenericInjector;%(AdditionalIncludeDirectories) 111 | MultiThreadedDebugDLL 112 | 113 | 114 | Windows 115 | true 116 | ..\$(Configuration)\;%(AdditionalLibraryDirectories) 117 | GenericInjector.lib;%(AdditionalDependencies) 118 | 119 | 120 | 121 | 122 | Level3 123 | 124 | 125 | MaxSpeed 126 | true 127 | true 128 | WIN32;NDEBUG;_WINDOWS;_USRDLL;INJECTORTEST_EXPORTS;%(PreprocessorDefinitions) 129 | true 130 | ..\GenericInjector;%(AdditionalIncludeDirectories) 131 | 132 | 133 | Windows 134 | true 135 | true 136 | true 137 | ..\$(Configuration)\;%(AdditionalLibraryDirectories) 138 | GenericInjector.lib;%(AdditionalDependencies) 139 | 140 | 141 | 142 | 143 | Level3 144 | 145 | 146 | MaxSpeed 147 | true 148 | true 149 | NDEBUG;_WINDOWS;_USRDLL;INJECTORTEST_EXPORTS;%(PreprocessorDefinitions) 150 | true 151 | ..\GenericInjector;%(AdditionalIncludeDirectories) 152 | 153 | 154 | Windows 155 | true 156 | true 157 | true 158 | ..\$(Configuration)\;%(AdditionalLibraryDirectories) 159 | GenericInjector.lib;%(AdditionalDependencies) 160 | 161 | 162 | 163 | 164 | false 165 | 166 | 167 | false 168 | 169 | 170 | false 171 | 172 | 173 | false 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | -------------------------------------------------------------------------------- /GenericInjector/GenericInjector.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "InjectedFunction.h" 3 | #include "PEParser.h" 4 | #include 5 | #include 6 | #include 7 | 8 | class GenericInjector 9 | { 10 | public: 11 | GenericInjector() = default; 12 | virtual ~GenericInjector(); 13 | 14 | void Init(HMODULE hDll, LPCTSTR lpModuleName = nullptr); 15 | void Init(HMODULE hDll, HINSTANCE hInstance); 16 | void Uninit(); 17 | 18 | HMODULE GetInstance() const noexcept 19 | { 20 | return m_hInstance; 21 | } 22 | 23 | HMODULE GetModule() const noexcept 24 | { 25 | return m_hDll; 26 | } 27 | 28 | HANDLE GetProcess() const noexcept 29 | { 30 | return m_hProcess; 31 | } 32 | 33 | PEPaser const& GetPEPaser(); 34 | 35 | template 36 | auto InjectImportTable(tstring const& DllName, tstring const& FuncName) 37 | { 38 | auto const& tPaser = GetPEPaser(); 39 | if (!tPaser.DllImported(DllName)) 40 | { 41 | throw std::invalid_argument("Cannot locate dll."); 42 | } 43 | 44 | return InjectFunctionPointer>(tPaser.GetImportFunctionAddress(DllName, FuncName)); 45 | } 46 | 47 | template 48 | auto InjectImportTable(tstring const& DllName, DWORD Index) 49 | { 50 | auto const& tPaser = GetPEPaser(); 51 | 52 | if (!tPaser.DllImported(DllName)) 53 | { 54 | throw std::invalid_argument("Cannot locate dll."); 55 | } 56 | 57 | return InjectFunctionPointer>(tPaser.GetImportFunctionAddress(DllName, Index)); 58 | } 59 | 60 | // Only tested on Visual Studio 2015 61 | template 62 | auto InjectVirtualTable(LPVOID pObject, DWORD dwIndex) 63 | { 64 | return InjectFunctionPointer>(*static_cast(pObject) + dwIndex); 65 | } 66 | 67 | // Return address which match pattern you specified, or nullptr if not found 68 | // Search address from pAddressBase to pAddressEnd, if pAddressBase or pAddressEnd is nullptr then use default range 69 | byte* FindMemory(void* pAddressBase, void* pAddressEnd, const byte* pPattern, size_t PatternSize, const byte* pWildcard, size_t WildcardSize, size_t Alignment) noexcept; 70 | 71 | template 72 | byte* FindMemory(void* pAddressBase, void* pAddressEnd, const byte (&Pattern)[PatternSize], size_t Alignment) noexcept 73 | { 74 | return FindMemory(pAddressBase, pAddressEnd, Pattern, PatternSize, nullptr, 0, Alignment); 75 | } 76 | 77 | template 78 | byte* FindMemory(void* pAddressBase, void* pAddressEnd, const byte(&Pattern)[PatternSize], const byte(&Wildcard)[WildcardSize], size_t Alignment) noexcept 79 | { 80 | return FindMemory(pAddressBase, pAddressEnd, Pattern, PatternSize, Wildcard, WildcardSize, Alignment); 81 | } 82 | 83 | protected: 84 | virtual void OnLoad() = 0; 85 | virtual void OnUnload() = 0; 86 | 87 | protected: 88 | void InjectPointer(LPDWORD lpAddr, DWORD dwPointer) const; 89 | static void InjectPointer(HANDLE hProcess, LPDWORD lpAddr, DWORD dwPointer); 90 | 91 | // Cannot call this in hooking function because unhooking will delete the injector 92 | void UnhookInjector(DWORD FunctionAddr); 93 | 94 | template 95 | void GetCode(DWORD dwOffset, DWORD dwSize, Container& container) const 96 | { 97 | GetCode(GetProcess(), GetInstance(), dwOffset, dwSize, container); 98 | } 99 | 100 | template 101 | void GetCode(DWORD dwOffset, byte(&Buffer)[Size]) const 102 | { 103 | GetCode(GetProcess(), GetInstance(), dwOffset, Buffer); 104 | } 105 | 106 | template 107 | static void GetCode(HANDLE hProcess, HMODULE hInstance, DWORD dwOffset, DWORD dwSize, Container& container) 108 | { 109 | std::vector Buffer(dwSize); 110 | GetCode(hProcess, hInstance, dwOffset, Buffer.data(), Buffer.size()); 111 | std::copy(Buffer.begin(), Buffer.end(), std::back_inserter(container)); 112 | } 113 | 114 | template 115 | static void GetCode(HANDLE hProcess, HMODULE hInstance, DWORD dwOffset, byte (&Buffer)[Size]) 116 | { 117 | GetCode(hProcess, hInstance, dwOffset, Buffer, Size); 118 | } 119 | 120 | static void GetCode(HANDLE hProcess, HMODULE hInstance, DWORD dwOffset, byte* pBuffer, size_t BufferSize); 121 | 122 | template 123 | void InjectCode(DWORD dwDestOffset, DWORD dwDestSize, const byte(&Code)[Size]) const 124 | { 125 | InjectCode(GetProcess(), GetInstance(), dwDestOffset, dwDestSize, Code); 126 | } 127 | 128 | void InjectCode(DWORD dwDestOffset, DWORD dwDestSize, const byte* lpCode, DWORD dwCodeSize) const; 129 | 130 | template 131 | static void InjectCode(HANDLE hProcess, HMODULE hInstance, DWORD dwDestOffset, DWORD dwDestSize, const byte (&Code)[Size]) 132 | { 133 | InjectCode(hProcess, hInstance, dwDestOffset, dwDestSize, Code, Size); 134 | } 135 | static void InjectCode(HANDLE hProcess, HMODULE hInstance, DWORD dwDestOffset, DWORD dwDestSize, const byte* lpCode, DWORD dwCodeSize); 136 | 137 | template 138 | void ModifyCode(DWORD dwDestOffset, DWORD dwDestSize, const byte(&Code)[Size], bool bFillNop = true) const 139 | { 140 | ModifyCode(GetProcess(), GetInstance(), dwDestOffset, dwDestSize, Code, bFillNop); 141 | } 142 | 143 | void ModifyCode(DWORD dwDestOffset, DWORD dwDestSize, const byte* lpCode, DWORD dwCodeSize, bool bFillNop = true) const; 144 | 145 | template 146 | static void ModifyCode(HANDLE hProcess, HMODULE hInstance, DWORD dwDestOffset, DWORD dwDestSize, const byte (&Code)[Size], bool bFillNop = true) 147 | { 148 | ModifyCode(hProcess, hInstance, dwDestOffset, dwDestSize, Code, Size, bFillNop); 149 | } 150 | static void ModifyCode(HANDLE hProcess, HMODULE hInstance, DWORD dwDestOffset, DWORD dwDestSize, const byte* lpCode, DWORD dwCodeSize, bool bFillNop = true); 151 | 152 | static byte* GenerateJmpCode(HINSTANCE hInstance, DWORD TargetOffset); 153 | static byte* GenerateJmpCode(HINSTANCE hInstance, DWORD From, DWORD To); 154 | 155 | private: 156 | bool m_Inited; 157 | HMODULE m_hDll, m_hInstance; 158 | HANDLE m_hProcess; 159 | std::unique_ptr m_pPEPaser; 160 | std::unordered_map, std::unique_ptr>> m_InjectorMap; 161 | 162 | void SetInstance(LPCTSTR lpModuleName = NULL) noexcept 163 | { 164 | SetInstance(GetModuleHandle(lpModuleName)); 165 | } 166 | 167 | void SetInstance(HMODULE hModule) noexcept; 168 | void SetProcess(HANDLE hProcess = INVALID_HANDLE_VALUE) noexcept; 169 | 170 | static byte* GenerateInjectStdcallEntry(HINSTANCE hInstance, LPVOID pInjector, DWORD dwArgSize); 171 | static byte* GenerateInjectCdeclEntry(HINSTANCE hInstance, LPVOID pInjector, DWORD dwArgSize); 172 | 173 | template 174 | FunctionInjector* AllocInjector(LPDWORD FunctionAddr) 175 | { 176 | auto tItea = m_InjectorMap.find(*FunctionAddr); 177 | if (tItea == m_InjectorMap.end()) 178 | { 179 | auto tRet = new FunctionInjector; 180 | auto& InjectorPair = m_InjectorMap[*FunctionAddr]; 181 | InjectorPair.first.insert(FunctionAddr); 182 | InjectorPair.second.reset(static_cast(tRet)); 183 | 184 | return tRet; 185 | } 186 | 187 | if (!tItea->second.second) 188 | { 189 | auto tRet = new FunctionInjector; 190 | tItea->second.first.insert(FunctionAddr); 191 | tItea->second.second.reset(static_cast(tRet)); 192 | 193 | return tRet; 194 | } 195 | 196 | return dynamic_cast*>(tItea->second.second.get()); 197 | } 198 | 199 | template 200 | void InjectStdCallFunction(HINSTANCE hInstance, FunctionInjector* pInjector, LPDWORD lpAddr) 201 | { 202 | union 203 | { 204 | FunctionPrototype FunctionPointer; 205 | DWORD RawValue; 206 | } Bugfix; 207 | 208 | Bugfix.RawValue = *lpAddr; 209 | 210 | byte* pInjectStdcallEntry = GenerateInjectStdcallEntry(hInstance, pInjector, GetFunctionAnalysis::FunctionAnalysis::ArgType::Size); 211 | 212 | pInjector->m_OriginalFunction.OriginalFunctionPointer = Bugfix.FunctionPointer; 213 | pInjector->m_ReplacedFunc.first = std::move(std::make_unique>(Bugfix.FunctionPointer)); 214 | pInjector->m_ReplacedFunc.second = nullptr; 215 | InjectPointer(lpAddr, reinterpret_cast(pInjectStdcallEntry)); 216 | } 217 | 218 | template 219 | void InjectCdeclFunction(HINSTANCE hInstance, FunctionInjector* pInjector, LPDWORD lpAddr) 220 | { 221 | union 222 | { 223 | FunctionPrototype FunctionPointer; 224 | DWORD RawValue; 225 | } Bugfix; 226 | 227 | Bugfix.RawValue = *lpAddr; 228 | 229 | byte* pInjectCdeclEntry = GenerateInjectCdeclEntry(hInstance, pInjector, GetFunctionAnalysis::FunctionAnalysis::ArgType::Size); 230 | 231 | pInjector->m_OriginalFunction.OriginalFunctionPointer = Bugfix.FunctionPointer; 232 | pInjector->m_ReplacedFunc.first = std::move(std::make_unique>(Bugfix.FunctionPointer)); 233 | pInjector->m_ReplacedFunc.second = nullptr; 234 | InjectPointer(lpAddr, reinterpret_cast(pInjectCdeclEntry)); 235 | } 236 | 237 | template 238 | FunctionInjector* InjectFunctionPointer(LPDWORD lpAddr) 239 | { 240 | if (!lpAddr || !*lpAddr) 241 | { 242 | throw std::invalid_argument("lpAddr or address which lpAddr points to should not be a nullptr."); 243 | } 244 | 245 | auto pInjector = AllocInjector(lpAddr); 246 | if (!pInjector) 247 | { 248 | throw std::runtime_error("Failed to alloc injector."); 249 | } 250 | 251 | switch (GetFunctionAnalysis::FunctionAnalysis::CallingConvention) 252 | { 253 | case CallingConventionEnum::Stdcall: 254 | case CallingConventionEnum::Thiscall: 255 | case CallingConventionEnum::Fastcall: 256 | InjectStdCallFunction(GetInstance(), pInjector, lpAddr); 257 | break; 258 | case CallingConventionEnum::Cdecl: 259 | InjectCdeclFunction(GetInstance(), pInjector, lpAddr); 260 | break; 261 | default: 262 | throw std::invalid_argument("Unknown calling convention."); 263 | } 264 | 265 | return pInjector; 266 | } 267 | }; 268 | 269 | #ifndef InitInjector 270 | # define InitInjector(Injector) \ 271 | BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID)\ 272 | {\ 273 | switch (ul_reason_for_call)\ 274 | {\ 275 | case DLL_PROCESS_ATTACH:\ 276 | (Injector).Init(hModule);\ 277 | break;\ 278 | case DLL_THREAD_ATTACH:\ 279 | case DLL_THREAD_DETACH:\ 280 | break;\ 281 | case DLL_PROCESS_DETACH:\ 282 | (Injector).Uninit();\ 283 | break;\ 284 | }\ 285 | return TRUE;\ 286 | } 287 | #endif 288 | -------------------------------------------------------------------------------- /GenericInjector/InjectedFunction.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "FunctionInfo.h" 3 | #include 4 | #include 5 | 6 | #define NOMINMAX 1 7 | #include 8 | 9 | template 10 | struct CastArgs 11 | { 12 | static_assert(IsSameTemplate::value && IsSameTemplate>::value, "T1 and T2 should be TypeSequence."); 13 | static_assert(SequenceConvertible::value, "T1 and T2 should be convertible."); 14 | 15 | static void Execute(const byte* pOrigin, byte* pOut) 16 | { 17 | *reinterpret_cast(pOut) = static_cast(*reinterpret_cast(pOrigin)); 18 | CastArgs::Execute(pOrigin + calc_align(sizeof(typename T1::Type)), pOut + calc_align(sizeof(typename T2::Type))); 19 | } 20 | }; 21 | 22 | template <> 23 | struct CastArgs, TypeSequence<>> 24 | { 25 | static void Execute(const byte* /*pOrigin*/, byte* /*pOut*/) 26 | { 27 | } 28 | }; 29 | 30 | struct Functor 31 | { 32 | typedef void (*CastArgFunc)(const byte*, byte*); 33 | 34 | virtual ~Functor() = default; 35 | 36 | virtual bool HasVariableArgument() const noexcept = 0; 37 | 38 | virtual LPVOID GetFunctionPointer() const noexcept = 0; 39 | virtual LPVOID GetObjectPointer() const noexcept = 0; 40 | virtual DWORD GetArgSize() const noexcept = 0; 41 | virtual DWORD GetArgCount() const noexcept = 0; 42 | virtual DWORD Call(CastArgFunc pCastArgFunc, LPVOID pStackTop, LPVOID pObject, LPDWORD lpReturnValue, DWORD ArgSize, bool ReceiveReturnedValue) = 0; 43 | 44 | protected: 45 | DWORD CallImpl(CallingConventionEnum CallingConvention, LPVOID pStackTop, LPVOID pArgs, LPVOID pOriginObject, LPDWORD lpReturnValue, DWORD ArgSize, DWORD ActualArgSize, bool ReceiveReturnedValue) const; 46 | }; 47 | 48 | template 49 | class InjectedFunction final 50 | : public Functor 51 | { 52 | public: 53 | typedef typename GetFunctionAnalysis::FunctionAnalysis FunctionInfo; 54 | 55 | // ReSharper disable once CppNonExplicitConvertingConstructor 56 | InjectedFunction(Func pFunc) 57 | : m_pFunc(pFunc), 58 | m_pObject(nullptr) 59 | { 60 | } 61 | 62 | InjectedFunction(typename FunctionInfo::ClassType* pObject, Func pFunc) 63 | : m_pFunc(pFunc), 64 | m_pObject(pObject) 65 | { 66 | } 67 | 68 | ~InjectedFunction() = default; 69 | 70 | bool HasVariableArgument() const noexcept override 71 | { 72 | return FunctionInfo::HasVariableArgument; 73 | } 74 | 75 | LPVOID GetFunctionPointer() const noexcept override 76 | { 77 | return m_RawPointer; 78 | } 79 | 80 | LPVOID GetObjectPointer() const noexcept override 81 | { 82 | return m_pObject; 83 | } 84 | 85 | DWORD GetArgSize() const noexcept override 86 | { 87 | return FunctionInfo::ArgType::Size; 88 | } 89 | 90 | DWORD GetArgCount() const noexcept override 91 | { 92 | return FunctionInfo::ArgType::Count; 93 | } 94 | 95 | DWORD Call(CastArgFunc pCastArgFunc, LPVOID pStackTop, LPVOID pObject, LPDWORD lpReturnValue, DWORD ArgSize, bool ReceiveReturnedValue) override 96 | { 97 | constexpr uint tmpArgSize = FunctionInfo::ArgType::AlignedSize; 98 | // may include this 99 | const uint ActualArgSize = ReceiveReturnedValue ? tmpArgSize - calc_align(sizeof(typename GetType::Type)) : tmpArgSize; 100 | byte tArg[tmpArgSize]; 101 | 102 | if (pCastArgFunc) 103 | { 104 | pCastArgFunc(static_cast(pStackTop), tArg); 105 | return CallImpl(FunctionInfo::CallingConvention, pStackTop, tArg, pObject, lpReturnValue, ArgSize, ActualArgSize, ReceiveReturnedValue); 106 | } 107 | 108 | if (ReceiveReturnedValue) 109 | { 110 | // Error 111 | return 0ul; 112 | } 113 | return CallImpl(FunctionInfo::CallingConvention, pStackTop, nullptr, pObject, lpReturnValue, ArgSize, ActualArgSize, false); 114 | } 115 | 116 | Func GetOriginFunctionPointer() const noexcept 117 | { 118 | return m_pFunc; 119 | } 120 | 121 | private: 122 | union 123 | { 124 | Func m_pFunc; 125 | void* m_RawPointer; 126 | }; 127 | 128 | typename FunctionInfo::ClassType* m_pObject; 129 | }; 130 | 131 | template 132 | auto MakeInjectedFunction(Func pFunc) 133 | { 134 | return InjectedFunction(pFunc); 135 | } 136 | 137 | template 138 | struct GetCastArgsStructHelper 139 | { 140 | typedef CastArgs::Type> Type; 141 | }; 142 | 143 | template 144 | struct GetCastArgsStructHelper 145 | { 146 | typedef CastArgs::Type, T2> Type; 147 | }; 148 | 149 | template 150 | struct GetCastArgsStruct 151 | { 152 | typedef typename GetCastArgsStructHelper= T2::Count, T1, T2>::Type Type; 153 | }; 154 | 155 | template 156 | struct GetCastArgsStruct, TypeSequence> 157 | { 158 | struct Type 159 | { 160 | static constexpr Functor::CastArgFunc Execute = nullptr; 161 | }; 162 | }; 163 | 164 | template 165 | struct PopFrontIf 166 | { 167 | typedef typename GetType<1u, T>::TargetSequence Type; 168 | }; 169 | 170 | template 171 | struct PopFrontIf 172 | { 173 | typedef T Type; 174 | }; 175 | 176 | template 177 | struct PopBackIf 178 | { 179 | typedef typename SubSequence::Type Type; 180 | }; 181 | 182 | template 183 | struct PopBackIf 184 | { 185 | typedef T Type; 186 | }; 187 | 188 | struct FunctionInjectorBase 189 | { 190 | virtual ~FunctionInjectorBase() = default; 191 | 192 | virtual LPVOID GetFunctionPointer() const noexcept = 0; 193 | virtual void Execute(DWORD dwECX, LPVOID lpStackTop) = 0; 194 | }; 195 | 196 | template 197 | class FunctionInjector final 198 | : public FunctionInjectorBase 199 | { 200 | friend class GenericInjector; 201 | public: 202 | typedef typename GetFunctionAnalysis::FunctionAnalysis FunctionAnalysis; 203 | 204 | FunctionInjector() = default; 205 | 206 | template 207 | // ReSharper disable once CppNonExplicitConvertingConstructor 208 | FunctionInjector(Func pFunc) 209 | : m_ReplacedFunc(std::make_unique>(pFunc)) 210 | { 211 | m_OriginalFunction.OriginalFunctionPointer = pFunc; 212 | } 213 | 214 | ~FunctionInjector() = default; 215 | 216 | template 217 | LPVOID Replace(Func pFunc) 218 | { 219 | typedef std::conditional_t::value, typename FunctionAnalysis::ArgType, typename AppendSequence, typename FunctionAnalysis::ArgType>::Type> Func1Arg; 220 | typedef typename GetFunctionAnalysis::FunctionAnalysis::ArgType Func2Arg; 221 | typedef typename FunctionAnalysis::ArgType RealFunc1Arg; 222 | typedef typename PopFrontIf::value, Func2Arg>::Type RealFunc2Arg; 223 | static_assert(std::is_same::value || std::is_convertible, typename Func2Arg::Type>::value, "Original class type cannot be converted to target class."); 224 | static_assert(RealFunc2Arg::Count <= RealFunc1Arg::Count, "Too many arguments."); 225 | static_assert(!(FunctionAnalysis::HasVariableArgument ^ GetFunctionAnalysis::HasVariableArgument), "Replace function should have variable argument if original function has variable argument."); 226 | 227 | auto tRet = m_ReplacedFunc.first ? m_ReplacedFunc.first->GetFunctionPointer() : nullptr; 228 | m_ReplacedFunc.first = std::make_unique>(pFunc); 229 | m_ReplacedFunc.second = GetCastArgsStruct::Type::Execute; 230 | 231 | return tRet; 232 | } 233 | 234 | LPVOID Replace(nullptr_t) 235 | { 236 | auto tRet = m_ReplacedFunc.first ? m_ReplacedFunc.first->GetFunctionPointer() : nullptr; 237 | m_ReplacedFunc.first.reset(); 238 | m_ReplacedFunc.second = nullptr; 239 | return tRet; 240 | } 241 | 242 | template 243 | void RegisterBefore(Func pFunc) 244 | { 245 | RegisterBefore(nullptr, pFunc); 246 | } 247 | 248 | template 249 | void RegisterBefore(typename GetFunctionAnalysis::FunctionAnalysis::ClassType* pObject, Func pFunc) 250 | { 251 | typedef std::conditional_t::value, typename FunctionAnalysis::ArgType, typename AppendSequence, typename FunctionAnalysis::ArgType>::Type> Func1Arg; 252 | typedef typename GetFunctionAnalysis::FunctionAnalysis::ArgType Func2Arg; 253 | typedef typename FunctionAnalysis::ArgType RealFunc1Arg; 254 | typedef typename PopBackIf::Type, va_list>::value, typename PopFrontIf::value, Func2Arg>::Type>::Type RealFunc2Arg; 255 | static_assert(std::is_same::value || std::is_convertible, typename Func2Arg::Type>::value, "Original class type cannot be converted to target class."); 256 | static_assert(RealFunc2Arg::Count <= RealFunc1Arg::Count + 1u, "Too many arguments."); 257 | static_assert(!GetFunctionAnalysis::FunctionAnalysis::HasVariableArgument, "Hook function cannot have variable argument."); 258 | m_BeforeFunc.emplace_back(std::make_pair(std::make_unique>(pObject, pFunc), GetCastArgsStruct::Type::Execute)); 259 | } 260 | 261 | template 262 | void RegisterAfter(Func pFunc) 263 | { 264 | RegisterAfter(nullptr, pFunc); 265 | } 266 | 267 | template 268 | void RegisterAfter(typename GetFunctionAnalysis::FunctionAnalysis::ClassType* pObject, Func pFunc) 269 | { 270 | typedef std::conditional_t::value, typename FunctionAnalysis::ArgType, typename AppendSequence, typename FunctionAnalysis::ArgType>::Type> Func1Arg; 271 | typedef typename GetFunctionAnalysis::FunctionAnalysis::ArgType Func2Arg; 272 | typedef typename FunctionAnalysis::ArgType RealFunc1Arg; 273 | typedef typename PopBackIf::Type, va_list>::value, typename PopFrontIf::value, Func2Arg>::Type>::Type RealFunc2Arg; 274 | static_assert(std::is_same::value || std::is_convertible, typename Func2Arg::Type>::value, "Original class type cannot be converted to target class."); 275 | static_assert(RealFunc2Arg::Count <= RealFunc1Arg::Count + 1u, "Too many arguments."); 276 | static_assert(!GetFunctionAnalysis::FunctionAnalysis::HasVariableArgument, "Hook function cannot have variable argument."); 277 | m_AfterFunc.emplace_back(std::make_pair(std::make_unique>(pObject, pFunc), GetCastArgsStruct::Type::Execute)); 278 | } 279 | 280 | LPVOID GetFunctionPointer() const noexcept override 281 | { 282 | return m_OriginalFunction.RawPointer; 283 | } 284 | 285 | void Execute(DWORD dwECX, LPVOID lpStackTop) override 286 | { 287 | LPVOID pObject = nullptr; 288 | if (!std::is_same::value) 289 | { 290 | if (FunctionAnalysis::CallingConvention == CallingConventionEnum::Thiscall) 291 | { 292 | pObject = reinterpret_cast(dwECX); 293 | } 294 | else 295 | { 296 | pObject = *reinterpret_cast(lpStackTop); 297 | lpStackTop = static_cast(lpStackTop) + sizeof(LPVOID); 298 | } 299 | 300 | if (!pObject) 301 | { 302 | // Error 303 | __asm xor eax, eax; 304 | return; 305 | } 306 | } 307 | 308 | DWORD tReturnValue = 0ul; 309 | bool tReceiveReturnedValue; 310 | constexpr auto tArgSize = FunctionAnalysis::ArgType::AlignedSize; 311 | 312 | for (auto& Func : m_BeforeFunc) 313 | { 314 | Func.first->Call(Func.second, lpStackTop, pObject, &tReturnValue, tArgSize, false); 315 | } 316 | if (m_ReplacedFunc.first) 317 | { 318 | tReturnValue = m_ReplacedFunc.first->Call(m_ReplacedFunc.second, lpStackTop, pObject, &tReturnValue, tArgSize, false); 319 | } 320 | else 321 | { 322 | __asm xor eax, eax; 323 | } 324 | __asm mov tReturnValue, eax; 325 | 326 | for (auto& Func : m_AfterFunc) 327 | { 328 | tReceiveReturnedValue = Func.first->GetArgCount() == FunctionAnalysis::ArgType::Count + (std::is_same::value ? 0 : 1) + 1; 329 | tReturnValue = Func.first->Call(Func.second, lpStackTop, pObject, &tReturnValue, tArgSize, tReceiveReturnedValue); 330 | } 331 | __asm mov eax, tReturnValue; 332 | } 333 | 334 | private: 335 | union 336 | { 337 | FunctionPrototype OriginalFunctionPointer; 338 | LPVOID RawPointer; 339 | } m_OriginalFunction; 340 | 341 | std::pair, Functor::CastArgFunc> m_ReplacedFunc; 342 | std::vector, Functor::CastArgFunc>> m_BeforeFunc; 343 | std::vector, Functor::CastArgFunc>> m_AfterFunc; 344 | }; 345 | -------------------------------------------------------------------------------- /GenericInjector/GenericInjector.cpp: -------------------------------------------------------------------------------- 1 | #include "GenericInjector.h" 2 | #include 3 | 4 | namespace 5 | { 6 | // Used for injecting stdcall function which prototype is same as function InjectorHelper 7 | // Avoid using ecx and edx because they may be used in function calling 8 | const byte InjectStdcallTemplate[] 9 | { 10 | 0x53, //push ebx 11 | 0x8D, 0x5C, 0x24, 0x08, //lea ebx, [esp+8] // skip saved ebx and ret addr which is pushed by instruction call 12 | 0x56, //push esi 13 | 0xBE, 0x00, 0x00, 0x00, 0x00, //mov esi, 0x00000000(+0x7) 14 | 0xB8, 0x00, 0x00, 0x00, 0x00, //mov eax, 0x00000000(+0xC) 15 | 0x53, //push ebx 16 | 0x51, //push ecx 17 | 0x56, //push esi 18 | 0xFF, 0xD0, //call eax 19 | 0x5E, //pop esi 20 | 0x5B, //pop ebx 21 | 0xC2, 0x00, 0x00, //ret 0(+0x18) // clear the stack and return 22 | }; 23 | 24 | const byte InjectCdeclTemplate[] 25 | { 26 | 0x53, //push ebx 27 | 0x8D, 0x5C, 0x24, 0x08, //lea ebx, [esp+8] // skip saved ebx and ret addr which is pushed by instruction call 28 | 0x56, //push esi 29 | 0xBE, 0x00, 0x00, 0x00, 0x00, //mov esi, 0x00000000(+7) 30 | 0xB8, 0x00, 0x00, 0x00, 0x00, //mov eax, 0x00000000(+C) 31 | 0x53, //push ebx 32 | 0x51, //push ecx 33 | 0x56, //push esi 34 | 0xFF, 0xD0, //call eax 35 | 0x5E, //pop esi 36 | 0x5B, //pop ebx 37 | 0xC3, //retn // return 38 | }; 39 | 40 | const byte JmpTemplate[] 41 | { 42 | 0xE9, 0x00, 0x00, 0x00, 0x00, //jmp 0x00000000(+1) // offset 43 | }; 44 | 45 | __declspec(noinline) 46 | void __stdcall InjectorHelper(FunctionInjectorBase* pInjector, DWORD dwECX, LPVOID pStackTop) 47 | { 48 | pInjector->Execute(dwECX, pStackTop); 49 | } 50 | 51 | byte* AllocCode(size_t szCode, HANDLE hProcess = INVALID_HANDLE_VALUE) 52 | { 53 | auto pCode = static_cast(VirtualAllocEx(hProcess == INVALID_HANDLE_VALUE || !hProcess ? GetCurrentProcess() : hProcess, NULL, szCode, MEM_COMMIT, PAGE_EXECUTE_READWRITE)); 54 | if (!pCode) 55 | { 56 | throw std::system_error(std::error_code(GetLastError(), std::system_category()), "Cannot alloc code."); 57 | } 58 | return pCode; 59 | } 60 | 61 | void FreeCode(byte* pMem, HANDLE hProcess = INVALID_HANDLE_VALUE) 62 | { 63 | VirtualFreeEx(hProcess == INVALID_HANDLE_VALUE || !hProcess ? GetCurrentProcess() : hProcess, pMem, NULL, MEM_RELEASE); 64 | } 65 | } 66 | 67 | GenericInjector::~GenericInjector() 68 | { 69 | for (auto const& i : m_InjectorMap) 70 | { 71 | UnhookInjector(i.first); 72 | } 73 | } 74 | 75 | void GenericInjector::Init(HMODULE hDll, LPCTSTR lpModuleName) 76 | { 77 | Init(hDll, GetModuleHandle(lpModuleName)); 78 | } 79 | 80 | void GenericInjector::Init(HMODULE hDll, HINSTANCE hInstance) 81 | { 82 | if (m_Inited) 83 | { 84 | throw std::runtime_error("Injector already initialized."); 85 | } 86 | 87 | m_hDll = hDll; 88 | SetProcess(); 89 | SetInstance(hInstance); 90 | m_Inited = true; 91 | OnLoad(); 92 | } 93 | 94 | void GenericInjector::Uninit() 95 | { 96 | if (!m_Inited) 97 | { 98 | throw std::runtime_error("Injector have not initialized."); 99 | } 100 | 101 | m_Inited = false; 102 | OnUnload(); 103 | } 104 | 105 | void GenericInjector::SetInstance(HMODULE hModule) noexcept 106 | { 107 | if (m_hInstance != hModule) 108 | { 109 | m_pPEPaser.reset(); 110 | m_hInstance = hModule; 111 | } 112 | } 113 | 114 | void GenericInjector::SetProcess(HANDLE hProcess) noexcept 115 | { 116 | m_hProcess = hProcess == INVALID_HANDLE_VALUE || !hProcess ? GetCurrentProcess() : hProcess; 117 | } 118 | 119 | PEPaser const& GenericInjector::GetPEPaser() 120 | { 121 | if (!m_pPEPaser) 122 | { 123 | m_pPEPaser = std::move(std::make_unique(reinterpret_cast(GetInstance()))); 124 | } 125 | 126 | return *m_pPEPaser; 127 | } 128 | 129 | byte* GenericInjector::FindMemory(void* pAddressBase, void* pAddressEnd, const byte* pPattern, size_t PatternSize, const byte* pWildcard, size_t WildcardSize, size_t Alignment) noexcept 130 | { 131 | if (!pPattern || !PatternSize || !Alignment) 132 | { 133 | return nullptr; 134 | } 135 | 136 | const size_t FirstSectionRVA = GetPEPaser().GetSections().front().VirtualAddress; 137 | const size_t MemSize = GetPEPaser().GetNTHeaders().OptionalHeader.SizeOfImage - std::max(PatternSize, Alignment) - FirstSectionRVA; 138 | 139 | byte* pStart = reinterpret_cast(GetInstance()) + FirstSectionRVA; 140 | byte* pEndPointer = pStart + MemSize; 141 | 142 | byte* pCurrentPointer = static_cast(pAddressBase); 143 | if (pCurrentPointer < pStart) 144 | { 145 | pCurrentPointer = pStart; 146 | } 147 | else if (pCurrentPointer >= pEndPointer) 148 | { 149 | return nullptr; 150 | } 151 | 152 | if (pAddressEnd && pAddressEnd < pEndPointer) 153 | { 154 | if (pAddressEnd <= pCurrentPointer) 155 | { 156 | return nullptr; 157 | } 158 | 159 | pEndPointer = static_cast(pAddressEnd); 160 | } 161 | 162 | if (IsBadReadPtr(pCurrentPointer, MemSize)) 163 | { 164 | return nullptr; 165 | } 166 | 167 | if (!pWildcard) 168 | { 169 | WildcardSize = 0; 170 | } 171 | 172 | for (; pCurrentPointer < pEndPointer; pCurrentPointer += Alignment) 173 | { 174 | for (size_t i = 0; i < PatternSize; ++i) 175 | { 176 | if (pCurrentPointer[i] != pPattern[i]) 177 | { 178 | for (size_t j = 0; j < WildcardSize; ++j) 179 | { 180 | if (pPattern[i] == pWildcard[j]) 181 | { 182 | goto WildcardMatched; 183 | } 184 | } 185 | goto NotMatched; 186 | } 187 | WildcardMatched: 188 | continue; 189 | } 190 | return pCurrentPointer; 191 | NotMatched: 192 | continue; 193 | } 194 | 195 | return nullptr; 196 | } 197 | 198 | void GenericInjector::InjectPointer(LPDWORD lpAddr, DWORD dwPointer) const 199 | { 200 | InjectPointer(GetProcess(), lpAddr, dwPointer); 201 | } 202 | 203 | void GenericInjector::InjectPointer(HANDLE hProcess, LPDWORD lpAddr, DWORD dwPointer) 204 | { 205 | if (hProcess == INVALID_HANDLE_VALUE || !hProcess) 206 | { 207 | hProcess = GetCurrentProcess(); 208 | } 209 | 210 | DWORD tOldProtect; 211 | 212 | if (!VirtualProtectEx(hProcess, lpAddr, sizeof(LPVOID), PAGE_READWRITE, &tOldProtect)) 213 | { 214 | throw std::system_error(std::error_code(GetLastError(), std::system_category()), "Cannot modify the protect of injected function."); 215 | } 216 | *lpAddr = dwPointer; 217 | if (!VirtualProtectEx(hProcess, lpAddr, sizeof(LPVOID), tOldProtect, &tOldProtect)) 218 | { 219 | throw std::system_error(std::error_code(GetLastError(), std::system_category()), "Cannot modify the protect of injected function."); 220 | } 221 | } 222 | 223 | void GenericInjector::UnhookInjector(DWORD FunctionAddr) 224 | { 225 | auto itea = m_InjectorMap.find(FunctionAddr); 226 | if (itea != m_InjectorMap.end()) 227 | { 228 | for (auto InjectedAddr : itea->second.first) 229 | { 230 | // : Memory leaks 231 | InjectPointer(InjectedAddr, FunctionAddr); 232 | } 233 | 234 | itea->second.first.clear(); 235 | itea->second.second.reset(); 236 | } 237 | } 238 | 239 | void GenericInjector::GetCode(HANDLE hProcess, HMODULE hInstance, DWORD dwOffset, byte* pBuffer, size_t BufferSize) 240 | { 241 | if (hProcess == INVALID_HANDLE_VALUE || !hProcess) 242 | { 243 | hProcess = GetCurrentProcess(); 244 | } 245 | 246 | if (hInstance == INVALID_HANDLE_VALUE || !hInstance || !pBuffer || !BufferSize) 247 | { 248 | throw std::invalid_argument("Invalid argument."); 249 | } 250 | 251 | byte* pCode = reinterpret_cast(hInstance) + dwOffset; 252 | DWORD oldProtect; 253 | if (!VirtualProtectEx(hProcess, pCode, BufferSize, PAGE_EXECUTE_READWRITE, &oldProtect)) 254 | { 255 | throw std::system_error(std::error_code(GetLastError(), std::system_category()), "Cannot modify the protect of code."); 256 | } 257 | memcpy_s(pBuffer, BufferSize, pCode, BufferSize); 258 | if (!VirtualProtectEx(hProcess, pCode, BufferSize, oldProtect, &oldProtect)) 259 | { 260 | throw std::system_error(std::error_code(GetLastError(), std::system_category()), "Cannot modify the protect of code."); 261 | } 262 | } 263 | 264 | void GenericInjector::InjectCode(DWORD dwDestOffset, DWORD dwDestSize, const byte * lpCode, DWORD dwCodeSize) const 265 | { 266 | InjectCode(GetProcess(), GetInstance(), dwDestOffset, dwDestSize, lpCode, dwCodeSize); 267 | } 268 | 269 | void GenericInjector::InjectCode(HANDLE hProcess, HMODULE hInstance, DWORD dwDestOffset, DWORD dwDestSize, const byte* lpCode, DWORD dwCodeSize) 270 | { 271 | if (hProcess == INVALID_HANDLE_VALUE || !hProcess) 272 | { 273 | hProcess = GetCurrentProcess(); 274 | } 275 | 276 | if (hInstance == INVALID_HANDLE_VALUE || !hInstance || !lpCode) 277 | { 278 | throw std::invalid_argument("Invalid argument."); 279 | } 280 | 281 | if (dwDestSize < sizeof JmpTemplate) 282 | { 283 | throw std::invalid_argument("Size of dest code should be at least bigger than JmpTemplate."); 284 | } 285 | 286 | byte* pDest = reinterpret_cast(hInstance) + dwDestOffset; 287 | byte* pNewCode = AllocCode(dwCodeSize + sizeof JmpTemplate); 288 | memcpy_s(pNewCode, dwCodeSize + sizeof JmpTemplate, lpCode, dwCodeSize); 289 | byte pJmpCode[sizeof JmpTemplate]; 290 | memcpy_s(pJmpCode, sizeof JmpTemplate, JmpTemplate, sizeof JmpTemplate); 291 | *reinterpret_cast(pJmpCode + 1) = static_cast(pDest + dwDestSize - (pNewCode + dwCodeSize) - sizeof pJmpCode); 292 | memcpy_s(pNewCode + dwCodeSize, sizeof JmpTemplate, pJmpCode, sizeof pJmpCode); 293 | FlushInstructionCache(hInstance, pNewCode, dwCodeSize + sizeof JmpTemplate); 294 | 295 | *reinterpret_cast(pJmpCode + 1) = static_cast(pNewCode - pDest - sizeof pJmpCode); 296 | DWORD oldProtect; 297 | if (!VirtualProtectEx(hProcess, pDest, dwDestSize, PAGE_EXECUTE_READWRITE, &oldProtect)) 298 | { 299 | throw std::system_error(std::error_code(GetLastError(), std::system_category()), "Cannot modify the protect of code."); 300 | } 301 | memcpy_s(pDest, dwDestSize, pJmpCode, sizeof pJmpCode); 302 | if (!VirtualProtectEx(hProcess, pDest, dwDestSize, oldProtect, &oldProtect)) 303 | { 304 | throw std::system_error(std::error_code(GetLastError(), std::system_category()), "Cannot modify the protect of code."); 305 | } 306 | } 307 | 308 | void GenericInjector::ModifyCode(DWORD dwDestOffset, DWORD dwDestSize, const byte* lpCode, DWORD dwCodeSize, bool bFillNop) const 309 | { 310 | ModifyCode(GetProcess(), GetInstance(), dwDestOffset, dwDestSize, lpCode, dwCodeSize, bFillNop); 311 | } 312 | 313 | void GenericInjector::ModifyCode(HANDLE hProcess, HMODULE hInstance, DWORD dwDestOffset, DWORD dwDestSize, const byte* lpCode, DWORD dwCodeSize, bool bFillNop) 314 | { 315 | if (hProcess == INVALID_HANDLE_VALUE || !hProcess) 316 | { 317 | hProcess = GetCurrentProcess(); 318 | } 319 | 320 | if (hInstance == INVALID_HANDLE_VALUE || !hInstance || !lpCode) 321 | { 322 | throw std::invalid_argument("Invalid argument."); 323 | } 324 | 325 | if (dwDestSize < dwCodeSize) 326 | { 327 | throw std::invalid_argument("Size of code is too big, consider using InjectCode."); 328 | } 329 | 330 | byte* pDest = reinterpret_cast(hInstance) + dwDestOffset; 331 | DWORD oldProtect; 332 | if (!VirtualProtectEx(hProcess, pDest, dwDestSize, PAGE_EXECUTE_READWRITE, &oldProtect)) 333 | { 334 | throw std::system_error(std::error_code(GetLastError(), std::system_category()), "Cannot modify the protect of code."); 335 | } 336 | memcpy_s(pDest, dwDestSize, lpCode, dwCodeSize); 337 | if (dwDestSize > dwCodeSize && bFillNop) 338 | { 339 | // NOP: 0x90 340 | memset(pDest + dwCodeSize, 0x90, dwDestSize - dwCodeSize); 341 | } 342 | if (!VirtualProtectEx(hProcess, pDest, dwDestSize, oldProtect, &oldProtect)) 343 | { 344 | throw std::system_error(std::error_code(GetLastError(), std::system_category()), "Cannot modify the protect of code."); 345 | } 346 | } 347 | 348 | byte* GenericInjector::GenerateInjectStdcallEntry(HINSTANCE hInstance, LPVOID pInjector, DWORD dwArgSize) 349 | { 350 | byte* pInjectStdcallEntry = AllocCode(sizeof InjectStdcallTemplate); 351 | memcpy_s(pInjectStdcallEntry, sizeof InjectStdcallTemplate, InjectStdcallTemplate, sizeof InjectStdcallTemplate); 352 | *reinterpret_cast(pInjectStdcallEntry + 7) = pInjector; 353 | *reinterpret_cast(pInjectStdcallEntry + 12) = &InjectorHelper; 354 | *reinterpret_cast(pInjectStdcallEntry + 24) = static_cast(dwArgSize); 355 | 356 | FlushInstructionCache(hInstance, pInjectStdcallEntry, sizeof InjectStdcallTemplate); 357 | 358 | return pInjectStdcallEntry; 359 | } 360 | 361 | byte* GenericInjector::GenerateInjectCdeclEntry(HINSTANCE hInstance, LPVOID pInjector, DWORD dwArgSize) 362 | { 363 | byte* pInjectCdeclEntry = AllocCode(sizeof InjectCdeclTemplate); 364 | memcpy_s(pInjectCdeclEntry, sizeof InjectCdeclTemplate, InjectCdeclTemplate, sizeof InjectCdeclTemplate); 365 | *reinterpret_cast(pInjectCdeclEntry + 7) = pInjector; 366 | *reinterpret_cast(pInjectCdeclEntry + 12) = &InjectorHelper; 367 | 368 | FlushInstructionCache(hInstance, pInjectCdeclEntry, sizeof InjectCdeclTemplate); 369 | 370 | return pInjectCdeclEntry; 371 | } 372 | 373 | byte* GenericInjector::GenerateJmpCode(HINSTANCE hInstance, DWORD TargetOffset) 374 | { 375 | byte* pJmpCode = AllocCode(sizeof JmpTemplate); 376 | memcpy_s(pJmpCode, sizeof JmpTemplate, JmpTemplate, sizeof JmpTemplate); 377 | *reinterpret_cast(pJmpCode + 1) = TargetOffset; 378 | 379 | FlushInstructionCache(hInstance, pJmpCode, sizeof JmpTemplate); 380 | 381 | return pJmpCode; 382 | } 383 | 384 | byte* GenericInjector::GenerateJmpCode(HINSTANCE hInstance, DWORD From, DWORD To) 385 | { 386 | return GenerateJmpCode(hInstance, To - From); 387 | } 388 | -------------------------------------------------------------------------------- /GenericInjector/FunctionInfo.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | typedef uint32_t uint; 7 | typedef uint8_t byte; 8 | 9 | enum : uint 10 | { 11 | DefaultAlignBase = sizeof(void*), 12 | }; 13 | 14 | constexpr uint calc_align(uint n, uint align = DefaultAlignBase) 15 | { 16 | return n + align - 1 & ~(align - 1); 17 | } 18 | 19 | template 20 | struct IsSameTemplate 21 | { 22 | constexpr static bool value = std::is_same::value; 23 | }; 24 | 25 | template