First push

This commit is contained in:
Arash
2022-01-20 10:00:57 -05:00
parent ba4d9cc68e
commit 9f901167b0
19 changed files with 1932 additions and 0 deletions
Binary file not shown.
Binary file not shown.
+31
View File
@@ -0,0 +1,31 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30413.136
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MalMemDetect", "MalMemDetect\MalMemDetect.vcxproj", "{A35A8CAB-9746-49F8-AD67-AF2F6667F30A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
ReleaseDLL|x64 = ReleaseDLL|x64
ReleaseDLL|x86 = ReleaseDLL|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{A35A8CAB-9746-49F8-AD67-AF2F6667F30A}.Debug|x64.ActiveCfg = Debug|x64
{A35A8CAB-9746-49F8-AD67-AF2F6667F30A}.Debug|x64.Build.0 = Debug|x64
{A35A8CAB-9746-49F8-AD67-AF2F6667F30A}.Debug|x86.ActiveCfg = Debug|Win32
{A35A8CAB-9746-49F8-AD67-AF2F6667F30A}.Debug|x86.Build.0 = Debug|Win32
{A35A8CAB-9746-49F8-AD67-AF2F6667F30A}.ReleaseDLL|x64.ActiveCfg = ReleaseDLL|x64
{A35A8CAB-9746-49F8-AD67-AF2F6667F30A}.ReleaseDLL|x64.Build.0 = ReleaseDLL|x64
{A35A8CAB-9746-49F8-AD67-AF2F6667F30A}.ReleaseDLL|x86.ActiveCfg = ReleaseDLL|Win32
{A35A8CAB-9746-49F8-AD67-AF2F6667F30A}.ReleaseDLL|x86.Build.0 = ReleaseDLL|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {04227E59-E80A-4656-94B4-A3892BFB1016}
EndGlobalSection
EndGlobal
+108
View File
@@ -0,0 +1,108 @@
#pragma once
typedef LONG NTSTATUS;
typedef DWORD KPRIORITY;
typedef WORD UWORD;
typedef enum _MEMORY_INFORMATION_CLASS {
MemoryBasicInformation, // MEMORY_BASIC_INFORMATION
MemoryWorkingSetInformation, // MEMORY_WORKING_SET_INFORMATION
MemoryMappedFilenameInformation, // UNICODE_STRING
MemoryRegionInformation, // MEMORY_REGION_INFORMATION
MemoryWorkingSetExInformation, // MEMORY_WORKING_SET_EX_INFORMATION
MemorySharedCommitInformation, // MEMORY_SHARED_COMMIT_INFORMATION
MemoryImageInformation, // MEMORY_IMAGE_INFORMATION
MemoryRegionInformationEx,
MemoryPrivilegedBasicInformation,
MemoryEnclaveImageInformation, // MEMORY_ENCLAVE_IMAGE_INFORMATION // since REDSTONE3
MemoryBasicInformationCapped
} MEMORY_INFORMATION_CLASS;
typedef struct _MEMORY_IMAGE_INFORMATION {
PVOID ImageBase;
SIZE_T SizeOfImage;
union {
ULONG ImageFlags;
struct {
ULONG ImagePartialMap : 1;
ULONG ImageNotExecutable : 1;
ULONG ImageSigningLevel : 4; // REDSTONE3
ULONG Reserved : 26;
};
};
} MEMORY_IMAGE_INFORMATION, * PMEMORY_IMAGE_INFORMATION;
typedef struct _CLIENT_ID
{
PVOID UniqueProcess;
PVOID UniqueThread;
} CLIENT_ID, * PCLIENT_ID;
typedef struct _THREAD_BASIC_INFORMATION
{
NTSTATUS ExitStatus;
PVOID TebBaseAddress;
CLIENT_ID ClientId;
KAFFINITY AffinityMask;
KPRIORITY Priority;
KPRIORITY BasePriority;
} THREAD_BASIC_INFORMATION, * PTHREAD_BASIC_INFORMATION;
enum THREADINFOCLASS
{
ThreadBasicInformation,
};
// These logging funcs were borrowed from Mr. Un1k0d3r and his wonderful class. https://github.com/Mr-Un1k0d3r
void Log(CHAR** data) {
CHAR path[256];
DWORD dwBytes;
snprintf(path, 255, "C:\\%d.log", GetCurrentProcessId());
HANDLE hFile = CreateFile(path, FILE_APPEND_DATA, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
WriteFile(hFile, *data, strlen(*data), &dwBytes, NULL);
memset(*data, 0x00, 256);
CloseHandle(hFile);
}
void LogDetected(CHAR** data) {
CHAR path[256];
DWORD dwBytes;
snprintf(path, 255, "C:\\Detected_%d.log", GetCurrentProcessId());
HANDLE hFile = CreateFile(path, FILE_APPEND_DATA, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
WriteFile(hFile, *data, strlen(*data), &dwBytes, NULL);
memset(*data, 0x00, 256);
CloseHandle(hFile);
}
template <typename T>
inline MH_STATUS MH_CreateHookEx(LPVOID pTarget, LPVOID pDetour, T** ppOriginal)
{
return MH_CreateHook(pTarget, pDetour, reinterpret_cast<LPVOID*>(ppOriginal));
}
template <typename T>
inline MH_STATUS MH_CreateHookApiEx(
LPCWSTR pszModule, LPCSTR pszProcName, LPVOID pDetour, T** ppOriginal)
{
return MH_CreateHookApi(
pszModule, pszProcName, pDetour, reinterpret_cast<LPVOID*>(ppOriginal));
}
NTSTATUS _ReadProcessMemory(HANDLE h_process, PVOID mem_addr, PVOID buf, SIZE_T mem_size, PSIZE_T mem_read) {
return syscall.CallSyscall("NtReadVirtualMemory", h_process, mem_addr, buf, mem_size, mem_read).result;
//return;
}
void _WriteProcessMemory(HANDLE h_process, PVOID mem_addr, PVOID buf, SIZE_T mem_size, PSIZE_T mem_read) {
syscall.CallSyscall("NtWriteVirtualMemory", h_process, mem_addr, buf, mem_size, mem_read);
return;
}
void _VirtualProtect(HANDLE h_process, PVOID* mem_addr, PSIZE_T NumberOfBytesToProtect, ULONG NewAccessProtection, PULONG OldAccessProtection) {
syscall.CallSyscall("NtProtectVirtualMemory", h_process, mem_addr, &NumberOfBytesToProtect, NewAccessProtection, OldAccessProtection);
return;
}
NTSTATUS _VirtualQuery(HANDLE h_process, PVOID mem_addr, MEMORY_INFORMATION_CLASS MemoryInformationClass, PVOID MemoryInformation, SIZE_T MemoryInformationLength, PSIZE_T ReturnLength) {
return syscall.CallSyscall("NtQueryVirtualMemory", h_process, mem_addr, MemoryInformationClass, MemoryInformation, MemoryInformationLength, ReturnLength).result;
//return;
}
+130
View File
@@ -0,0 +1,130 @@
#pragma once
std::string DetectHollowingAndHooks(DWORD processID)
{
HMODULE hMods[1024];
HANDLE hProcess;
DWORD cbNeeded;
unsigned int i;
MODULEINFO lpmodinfo;
DWORD cb = 1024;
std::string dllMonitor = "NULL";
// Get a handle to the process.
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION |
PROCESS_VM_READ,
FALSE, processID);
// Get a list of all the modules in this process.
if (EnumProcessModules(hProcess, hMods, sizeof(hMods), &cbNeeded))
{
for (i = 0; i < (cbNeeded / sizeof(HMODULE)); i++)
{
char szModName[MAX_PATH];
// Get the full path to the module's file.
if (K32GetModuleFileNameExA(hProcess, hMods[i], szModName,
sizeof(szModName) / sizeof(char)))
{
// Print the module name and handle value.
printf(TEXT("%s (%p)\n"), szModName, hMods[i]);
CHAR* log = (CHAR*)malloc(256);
snprintf(log, 255, TEXT("%s (%p)\n"), szModName, hMods[i]);
Log(&log);
free(log);
// Get file Bytes
FILE* pFile;
long lSize;
//SIZE_T lSize;
BYTE* buffer;
size_t result;
pFile = fopen(szModName, "rb");
// obtain file size:
fseek(pFile, 0, SEEK_END);
lSize = ftell(pFile);
rewind(pFile);
// allocate memory to contain the whole file:
buffer = (BYTE*)malloc(sizeof(BYTE) * lSize);
// copy the file into the buffer:
result = fread(buffer, 1, lSize, pFile);
fclose(pFile);
// Get memory Bytes
//GetModuleInformation(hProcess, hMods[i], &lpmodinfo, cb);
BYTE* buff;
buff = (BYTE*)malloc(sizeof(BYTE) * lSize);
_ReadProcessMemory(hProcess, hMods[i], buff, lSize, NULL);
PIMAGE_NT_HEADERS64 NtHeader = ImageNtHeader(buff);
PIMAGE_SECTION_HEADER Section = IMAGE_FIRST_SECTION(NtHeader);
WORD NumSections = NtHeader->FileHeader.NumberOfSections;
for (WORD i = 0; i < NumSections; i++)
{
std::string secName(reinterpret_cast<char const*>(Section->Name), 5);
// Check if the memory is executable
if (secName.find(".text") != std::string::npos) {
break;
}
Section++; // If not executable check next section
}
float inconsistencies = 0;
int encodedOpcode = 0;
for (int i = 0; i < Section->PointerToRawData; i++) {
/* the whole file is now loaded in the memory buffer. */
// terminate
if (buff[i] != buffer[i]) {
if (encodedOpcode < 4) {
encodedOpcode++;
}
else {
inconsistencies++;
}
}
}
// The first is the offset from the file on disk
// The second is the offset to .txt from memory
LPBYTE txtSectionFile = buffer + Section->PointerToRawData;
LPBYTE txtSectionMem = buff + Section->VirtualAddress;
for (int i = 0; i < Section->SizeOfRawData; i++) {
/* the whole file is now loaded in the memory buffer. */
// terminate
//printf("%x\n\n", buff[i]);
if ((char*)txtSectionFile[i] != (char*)txtSectionMem[i]) {
inconsistencies++;
}
}
float icPercent = (inconsistencies / (Section->SizeOfRawData + Section->PointerToRawData)) * 100;
if (inconsistencies > 5) {
printf("Found more than 5 bytes altered, there's potentially hooks here: %s Bytes Altered: %f\n", szModName, inconsistencies);
CHAR* log = (CHAR*)malloc(256);
snprintf(log, 255, "Found more than 5 bytes altered, there's potentially hooks here: %s Bytes Altered: %f\n", szModName, inconsistencies);
LogDetected(&log);
free(log);
}
if (inconsistencies > 10000) {
printf("FOUND DLL HOLLOW.\nNOW MONITORING: %s with %f changes found. %f%% Overall\n\n", szModName, inconsistencies, icPercent);
CHAR* log = (CHAR*)malloc(256);
snprintf(log, 255, "FOUND DLL HOLLOW.\nNOW MONITORING: %s with %f changes found. %f%% Overall\n\n", szModName, inconsistencies, icPercent);
LogDetected(&log);
free(log);
std::string moduleName(szModName, sizeof(szModName) / sizeof(char));
std::transform(moduleName.begin(), moduleName.end(), moduleName.begin(),
[](unsigned char c) { return tolower(c); });
dllMonitor = moduleName;
break;
}
free(buffer);
}
}
}
// Release the handle to the process.
CloseHandle(hProcess);
return dllMonitor;
}
+45
View File
@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="Source.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\include\syscall.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\include\utils.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Refresh.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Hollow.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="BaseDefs.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="StackWalk.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Payload.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>
+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
</PropertyGroup>
</Project>
+245
View File
@@ -0,0 +1,245 @@
<?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="ReleaseDLL|Win32">
<Configuration>ReleaseDLL</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="ReleaseDLL|x64">
<Configuration>ReleaseDLL</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{a35a8cab-9746-49f8-ad67-af2f6667f30a}</ProjectGuid>
<RootNamespace>LockdExe</RootNamespace>
<WindowsTargetPlatformVersion>10.0.18362.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseDLL|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseDLL|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
<Import Project="$(VCTargetsPath)\BuildCustomizations\masm.props" />
</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)'=='ReleaseDLL|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)'=='ReleaseDLL|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)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseDLL|Win32'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseDLL|x64'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>../;../include;</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalLibraryDirectories>../Libs;</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseDLL|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;RELEASE_DLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>../;../include;</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalLibraryDirectories>../Libs;</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<ExceptionHandling>Sync</ExceptionHandling>
<AdditionalIncludeDirectories>../include;..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>Crypt32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>../libs;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalOptions>/W4 %(AdditionalOptions)</AdditionalOptions>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>../;../include;</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalLibraryDirectories>../Libs;</AdditionalLibraryDirectories>
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseDLL|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;RELEASE_DLL64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>../include;..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<LanguageStandard>stdcpp17</LanguageStandard>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<BasicRuntimeChecks>Default</BasicRuntimeChecks>
<DebugInformationFormat>None</DebugInformationFormat>
<Optimization>MaxSpeed</Optimization>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>false</GenerateDebugInformation>
<AdditionalLibraryDirectories>../libs;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\include\syscall.cpp" />
<ClCompile Include="..\include\utils.cpp" />
<ClCompile Include="Source.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="BaseDefs.h" />
<ClInclude Include="Hollow.h" />
<ClInclude Include="Refresh.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
<Import Project="$(VCTargetsPath)\BuildCustomizations\masm.targets" />
</ImportGroup>
</Project>
+4
View File
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup />
</Project>
+98
View File
@@ -0,0 +1,98 @@
#pragma once
// This will refresh the .text section of a requested DLL
// This will also detect if a DLL has page guard on it (VEH HOOK) and act accordingly as well
int universalRefresher(const char* szModuleName) {
HANDLE hProcess;
DWORD processID = GetCurrentProcessId();
// Get a handle to the process.
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION |
PROCESS_VM_READ,
FALSE, processID);
if (NULL == hProcess)
return 1;
// Get a list of all the modules in this process.
PSIZE_T returnLength = 0;
//HMODULE moduleToRefresh = GetModuleHandleA(szModuleName);
HMODULE moduleToRefresh = LoadLibraryA(szModuleName);
LPBYTE moduleMath = (LPBYTE)moduleToRefresh;
MEMORY_BASIC_INFORMATION memInfo = { 0 };
while (_VirtualQuery(GetCurrentProcess(), (PVOID)moduleMath, MemoryBasicInformation, &memInfo, sizeof(memInfo), returnLength) == 0) {
if ((memInfo.Protect & PAGE_GUARD || memInfo.Protect == PAGE_NOACCESS) && moduleToRefresh != NULL && memInfo.State != MEM_FREE) {
printf("PAGE_GUARD/PAGE_NOACCESS Found. This can indicate VEH hooking. Removing guard from module '%s'\n", szModuleName);
printf("Size is %u\n", memInfo.RegionSize);
ULONG oldProtect;
_VirtualProtect(GetCurrentProcess(), (PVOID*)&memInfo.BaseAddress, (PSIZE_T)memInfo.RegionSize, PAGE_EXECUTE_READ, &oldProtect);
}
moduleMath += memInfo.RegionSize;
}
char szModName[MAX_PATH];
if (K32GetModuleFileNameExA(hProcess, moduleToRefresh, szModName,
sizeof(szModName) / sizeof(char)))
{
// Get file Bytes
FILE* pFile;
long lSize;
BYTE* buffer;
size_t result;
pFile = fopen(szModName, "rb");
// obtain file size:
fseek(pFile, 0, SEEK_END);
lSize = ftell(pFile);
rewind(pFile);
// allocate memory to contain the whole file:
buffer = (BYTE*)malloc(sizeof(BYTE) * lSize);
// copy the file into the buffer:
result = fread(buffer, 1, lSize, pFile);
fclose(pFile);
// Get memory Bytes
BYTE* buff;
buff = (BYTE*)malloc(sizeof(BYTE) * lSize);
_ReadProcessMemory(hProcess, moduleToRefresh, buff, lSize, NULL);
PIMAGE_NT_HEADERS64 NtHeader = ImageNtHeader(buff);
if (NtHeader != NULL) {
// Print the module name and handle value.
printf(TEXT("Refreshing %s\n"), szModName);
CHAR* log = (CHAR*)malloc(256);
snprintf(log, 255, TEXT("Refreshing %s\n"), szModName);
Log(&log);
free(log);
PIMAGE_SECTION_HEADER Section = IMAGE_FIRST_SECTION(NtHeader);
WORD NumSections = NtHeader->FileHeader.NumberOfSections;
for (WORD i = 0; i < NumSections; i++)
{
std::string secName(reinterpret_cast<char const*>(Section->Name), 5);
// Check if the memory is executable
if (secName.find(".text") != std::string::npos) {
break;
}
Section++; // If not executable check next section
}
// The first is the offset from the file on disk
// The second is the offset to .txt from memory
LPBYTE txtSectionFile = buffer + Section->PointerToRawData;
LPBYTE txtSectionMem = buff + Section->VirtualAddress;
LPBYTE txtSectionLoadedModule = (LPBYTE)moduleToRefresh + Section->VirtualAddress;
char* moduleChar = (char*)txtSectionLoadedModule;
//DWORD oldProtect;
ULONG oldProtect;
_VirtualProtect(GetCurrentProcess(), (PVOID*)&txtSectionLoadedModule, (PSIZE_T)Section->SizeOfRawData, PAGE_EXECUTE_READWRITE, &oldProtect);
for (int i = 0; i < Section->SizeOfRawData; i++) {
if ((char*)txtSectionMem[i] != (char*)txtSectionFile[i]) {
moduleChar[i] = txtSectionFile[i];
}
}
_VirtualProtect(GetCurrentProcess(), (PVOID*)&txtSectionLoadedModule, (PSIZE_T)Section->SizeOfRawData, oldProtect, &oldProtect);
free(buffer);
}
}
// Release the handle to the process.
CloseHandle(hProcess);
return 0;
}
+364
View File
@@ -0,0 +1,364 @@
#define _CRT_SECURE_NO_WARNINGS // Im a scrub
// Windows Libs
#include <windows.h>
#include <wininet.h>
#include <winnt.h>
#include <string>
#include <algorithm>
#include <psapi.h>
#include <intrin.h>
#pragma intrinsic(_ReturnAddress)
#include <dbghelp.h>
#pragma comment(lib, "dbghelp.lib")
// Third Party Libs
#include <MinHook.h>
#include <syscall.hpp>
static auto& syscall = freshycalls::Syscall::get_instance();
// Custom libs
#include "BaseDefs.h"
#include "Hollow.h"
#include "Refresh.h"
#if defined _M_X64
#pragma comment(lib, "libMinHook.x64.lib")
#elif defined _M_IX86
#pragma comment(lib, "libMinHook-x86.lib")
#endif
// Globals for monitoring
DWORD threadMonitor = NULL;
static std::string dllMonitor = "NULL";
BOOL jit = FALSE;
// Define NtQueryInformationThread function for user later
typedef NTSTATUS (WINAPI* NtQueryInformationThread_t)(HANDLE ThreadHandle, THREADINFOCLASS ThreadInformationClass, PVOID ThreadInformation, ULONG ThreadInformationLength, PULONG ReturnLength);
NtQueryInformationThread_t NtQueryInformationThread;
struct CallStackFrame
{
ULONG_PTR calledFrom;
ULONG_PTR stackAddr;
ULONG_PTR frameAddr;
ULONG_PTR origFrameAddr;
ULONG_PTR retAddr;
ULONG_PTR overwriteWhat;
};
static BOOL runAlloc = TRUE;
LPVOID(WINAPI* OldAlloc)(PVOID hHeap, ULONG dwFlags, SIZE_T dwBytes);
//Hooked Malloc
LPVOID WINAPI HookedAlloc(PVOID hHeap, ULONG dwFlags, SIZE_T dwBytes) {
LPVOID retPointer = OldAlloc(hHeap, dwFlags, dwBytes);
if (runAlloc) {
DWORD callerId = GetCurrentThreadId();
runAlloc = FALSE;
CHAR* log = (CHAR*)malloc(256);
HMODULE hModule;
char lpBaseName[256] = { 0 };
if (memcmp(_ReturnAddress(), "\x4c\x8b\xc0\x48\x85\xc0\x0f\x84", 8) == 0 || memcmp(_ReturnAddress(), "\x48\x8b\xd8\x48\x85\xc0\x0f\x84", 8) == 0) {
strcpy_s(lpBaseName, "ntdll.dll");
}
else {
LPCSTR data = (LPCSTR)_ReturnAddress();
if (::GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (LPCSTR)_ReturnAddress(), &hModule) == 1) {
::GetModuleBaseNameA(GetCurrentProcess(), hModule, lpBaseName, sizeof(lpBaseName));
}
else {
if (threadMonitor == NULL) {
threadMonitor = callerId;
}
snprintf(log, 255, "Suspicious Malloc() from thread with id:%d LPVOID:%p Heap Handle:%p Size: %i\n", callerId, retPointer, hHeap, dwBytes);
LogDetected(&log);
}
}
std::string modName = lpBaseName;
std::transform(modName.begin(), modName.end(), modName.begin(),
[](unsigned char c) { return tolower(c); });
if (dllMonitor != "NULL" && dllMonitor.find(modName) != std::string::npos) {
snprintf(log, 255, "Suspicious Malloc() from module with name:%s LPVOID:%p Heap Handle:%p %l Size: %i\n", dllMonitor.c_str(), retPointer, hHeap, dwBytes);
LogDetected(&log);
}
//snprintf(log, 255, "Suspicious Malloc() from thread with name:%s id:%d LPVOID:%p Heap Handle:%p Size: %i\n", modName, callerId, retPointer, hHeap, dwBytes);
//LogDetected(&log);
free(log);
runAlloc = TRUE;
}
return retPointer;
}
void(WINAPI* OldSleep)(DWORD dwMiliseconds);
//Hooked Sleep
void WINAPI HookedSleep(DWORD dwMiliseconds) {
DWORD callerId = GetCurrentThreadId();
CHAR* log = (CHAR*)malloc(256);
HMODULE hModule;
char lpBaseName[256] = { 0 };
if (memcmp(_ReturnAddress(), "\x4c\x8b\xc0\x48\x85\xc0\x0f\x84", 8) == 0 || memcmp(_ReturnAddress(), "\x48\x8b\xd8\x48\x85\xc0\x0f\x84", 8) == 0) {
strcpy_s(lpBaseName, "ntdll.dll");
}
else {
LPCSTR data = (LPCSTR)_ReturnAddress();
if (::GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (LPCSTR)_ReturnAddress(), &hModule) == 1) {
::GetModuleBaseNameA(GetCurrentProcess(), hModule, lpBaseName, sizeof(lpBaseName));
}
else {
if (threadMonitor == NULL) {
threadMonitor = callerId;
}
snprintf(log, 255, "Suspicious Sleep() from thread with id:%d Miliseconds: %d\n", callerId, dwMiliseconds);
LogDetected(&log);
}
}
std::string modName = lpBaseName;
std::transform(modName.begin(), modName.end(), modName.begin(),
[](unsigned char c) { return tolower(c); });
if (dllMonitor != "NULL" && dllMonitor.find(modName) != std::string::npos) {
snprintf(log, 255, "Suspicious Sleep() from module with name: %s Miliseconds: %d\n", dllMonitor.c_str(), dwMiliseconds);
LogDetected(&log);
}
free(log);
OldSleep(dwMiliseconds);
}
NTSTATUS(WINAPI* OldWaitForSingleObj)(HANDLE ObjectHandle, BOOLEAN Alertable, PLARGE_INTEGER TimeOut);
//Hooked NtWaitForSingleObject
NTSTATUS WINAPI HookedWaitForSingleObj(HANDLE ObjectHandle, BOOLEAN Alertable, PLARGE_INTEGER TimeOut) {
DWORD callerId = GetCurrentThreadId();
CHAR* log = (CHAR*)malloc(256);
HMODULE hModule;
char lpBaseName[256] = { 0 };
if (memcmp(_ReturnAddress(), "\x4c\x8b\xc0\x48\x85\xc0\x0f\x84", 8) == 0 || memcmp(_ReturnAddress(), "\x48\x8b\xd8\x48\x85\xc0\x0f\x84", 8) == 0) {
strcpy_s(lpBaseName, "ntdll.dll");
}
else {
LPCSTR data = (LPCSTR)_ReturnAddress();
if (::GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (LPCSTR)_ReturnAddress(), &hModule) == 1) {
::GetModuleBaseNameA(GetCurrentProcess(), hModule, lpBaseName, sizeof(lpBaseName));
}
else {
if (threadMonitor == NULL) {
threadMonitor = callerId;
}
snprintf(log, 255, "Suspicious NtWaitForSingleObject() from thread with id:%d Timeout: %u\n", callerId, TimeOut);
LogDetected(&log);
}
}
std::string modName = lpBaseName;
std::transform(modName.begin(), modName.end(), modName.begin(),
[](unsigned char c) { return tolower(c); });
if (dllMonitor != "NULL" && dllMonitor.find(modName) != std::string::npos) {
snprintf(log, 255, "Suspicious NtWaitForSingleObject() from module with name: %s Timeout: %u\n", dllMonitor.c_str(), TimeOut);
LogDetected(&log);
}
free(log);
return OldWaitForSingleObj(ObjectHandle, Alertable, TimeOut);
}
HINTERNET(WINAPI* OldInternetConnectA)(HINTERNET hConnect, LPCSTR lpszServerName, INTERNET_PORT nServerPort, LPCSTR lpszUserName, LPCSTR lpszPassword, DWORD dwService, DWORD dwFlags, DWORD_PTR dwContext);
//Hooked InternetConnectA
HINTERNET WINAPI HookedInternetConnectA(HINTERNET hConnect, LPCSTR lpszServerName, INTERNET_PORT nServerPort, LPCSTR lpszUserName, LPCSTR lpszPassword, DWORD dwService, DWORD dwFlags, DWORD_PTR dwContext) {
DWORD callerId = GetCurrentThreadId();
CHAR* log = (CHAR*)malloc(256);
HMODULE hModule;
char lpBaseName[256] = { 0 };
if (memcmp(_ReturnAddress(), "\x4c\x8b\xc0\x48\x85\xc0\x0f\x84", 8) == 0 || memcmp(_ReturnAddress(), "\x48\x8b\xd8\x48\x85\xc0\x0f\x84", 8) == 0) {
strcpy_s(lpBaseName, "ntdll.dll");
}
else {
LPCSTR data = (LPCSTR)_ReturnAddress();
if (::GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (LPCSTR)_ReturnAddress(), &hModule) == 1) {
::GetModuleBaseNameA(GetCurrentProcess(), hModule, lpBaseName, sizeof(lpBaseName));
}
else {
if (threadMonitor == NULL) {
threadMonitor = callerId;
}
snprintf(log, 255, "Suspicious InternetConnectA() from thread with id:%d Name: %s Creds: %s[%s]\n", callerId, lpszServerName, lpszUserName, lpszPassword);
LogDetected(&log);
}
}
std::string modName = lpBaseName;
std::transform(modName.begin(), modName.end(), modName.begin(),
[](unsigned char c) { return tolower(c); });
//printf("Suspicious InternetConnectA() from thread with id: %d, Name: %s\n", callerId, lpszServerName);
if (dllMonitor != "NULL" && dllMonitor.find(modName) != std::string::npos) {
snprintf(log, 255, "Suspicious InternetConnectA() from module with name: %s, Name: %s Creds: %s[%s]\n", dllMonitor.c_str(), lpszServerName, lpszUserName, lpszPassword);
LogDetected(&log);
}
free(log);
return OldInternetConnectA(hConnect, lpszServerName, nServerPort, lpszUserName, lpszPassword, dwService, dwFlags, dwContext);
}
HINTERNET(WINAPI* OldInternetConnectW)(HINTERNET hConnect, LPCWSTR lpszServerName, INTERNET_PORT nServerPort, LPCWSTR lpszUserName, LPCWSTR lpszPassword, DWORD dwService, DWORD dwFlags, DWORD_PTR dwContext);
//Hooked InternetConnectA
HINTERNET WINAPI HookedInternetConnectW(HINTERNET hConnect, LPCWSTR lpszServerName, INTERNET_PORT nServerPort, LPCWSTR lpszUserName, LPCWSTR lpszPassword, DWORD dwService, DWORD dwFlags, DWORD_PTR dwContext) {
DWORD callerId = GetCurrentThreadId();
CHAR* log = (CHAR*)malloc(256);
HMODULE hModule;
char lpBaseName[256] = { 0 };
if (memcmp(_ReturnAddress(), "\x4c\x8b\xc0\x48\x85\xc0\x0f\x84", 8) == 0 || memcmp(_ReturnAddress(), "\x48\x8b\xd8\x48\x85\xc0\x0f\x84", 8) == 0) {
strcpy_s(lpBaseName, "ntdll.dll");
}
else {
LPCSTR data = (LPCSTR)_ReturnAddress();
if (::GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (LPCSTR)_ReturnAddress(), &hModule) == 1) {
::GetModuleBaseNameA(GetCurrentProcess(), hModule, lpBaseName, sizeof(lpBaseName));
}
else {
if (threadMonitor == NULL) {
threadMonitor = callerId;
}
snprintf(log, 255, "Suspicious InternetConnectW() from thread with id:%d Name: %s Creds: %s[%s]\n", callerId, lpszServerName, lpszUserName, lpszPassword);
LogDetected(&log);
}
}
std::string modName = lpBaseName;
std::transform(modName.begin(), modName.end(), modName.begin(),
[](unsigned char c) { return tolower(c); });
if (dllMonitor != "NULL" && dllMonitor.find(modName) != std::string::npos) {
snprintf(log, 255, "Suspicious InternetConnectW() from module with name: %s, Name: %s Creds: %s[%s]\n", dllMonitor.c_str(), lpszServerName, lpszUserName, lpszPassword);
LogDetected(&log);
}
free(log);
return OldInternetConnectW(hConnect, lpszServerName, nServerPort, lpszUserName, lpszPassword, dwService, dwFlags, dwContext);
}
int main()
{
NtQueryInformationThread = (NtQueryInformationThread_t)GetProcAddress(LoadLibrary("ntdll.dll"), "NtQueryInformationThread");
// Sleep for sanity
//Sleep(100);
// Most of this stuff will break stuff so I commented it out.
// Refresh the most important DLLs in case they are hooked and detect any VEH hooks
//universalRefresher("ntdll.dll");
//universalRefresher("kernel32.dll");
//universalRefresher("kernelbase.dll");
//universalRefresher("msvcrt.dll");
// Break the exception dispatcher. It will always just immediately return. We do this AFTER the unhooking as that removes hooks and restores PAGE_EXECUTE_READ to the .text
/*ULONG oldProtect;
FARPROC addr = 0;
HMODULE libntdll = LoadLibraryA("ntdll.dll");
if (libntdll) {
addr = GetProcAddress(libntdll, "KiUserExceptionDispatcher");
}
if (addr != 0) {
char* moduleChar = (char*)addr;
_VirtualProtect(GetCurrentProcess(), (PVOID*)&addr, (PSIZE_T)1, PAGE_EXECUTE_READWRITE, &oldProtect);
moduleChar[0] = 0xC3;
_VirtualProtect(GetCurrentProcess(), (PVOID*)&addr, (PSIZE_T)1, oldProtect, &oldProtect);
}*/
// Detect any DLL or EXE hollows and any Inline Hooks
std::string Monitor = DetectHollowingAndHooks(GetCurrentProcessId());
if (Monitor != "NULL") {
dllMonitor = Monitor;
}
// Start our own hooks and log failures
CHAR* log = (CHAR*)malloc(256);
// Initialize MinHook.
if (MH_Initialize() != MH_OK)
{
snprintf(log, 255, "Failed to initalize minhook! Try again running as Admin.\n\n");
Log(&log);
return 1;
}
if (MH_CreateHookApiEx(
L"ntdll.dll", "RtlAllocateHeap", &HookedAlloc, &OldAlloc) != MH_OK)
{
snprintf(log, 255, "Failed to hook RtlAllocateHeap! Try again running as Admin.\n\n");
Log(&log);
return 1;
}
/*if (MH_CreateHookApiEx(
L"kernel32.dll", "Sleep", &HookedSleep, &OldSleep) != MH_OK)
{
snprintf(log, 255, "Failed to hook Sleep! Try again running as Admin.\n\n");
Log(&log);
return 1;
}*/
if (MH_CreateHookApiEx(
L"ntdll.dll", "NtWaitForSingleObject", &HookedWaitForSingleObj, &OldWaitForSingleObj) != MH_OK)
{
snprintf(log, 255, "Failed to hook NtWaitForSingleObject! Try again running as Admin.\n\n");
Log(&log);
return 1;
}
FARPROC InternetConnectA = GetProcAddress(LoadLibraryA("wininet.dll"), "InternetConnectA");
if (MH_CreateHook(InternetConnectA, &HookedInternetConnectA, reinterpret_cast<LPVOID*>(&OldInternetConnectA)) != MH_OK)
{
snprintf(log, 255, "Failed to hook InternetConnectA! Try again running as Admin.\n\n");
Log(&log);
return 1;
}
FARPROC InternetConnectW = GetProcAddress(LoadLibraryA("wininet.dll"), "InternetConnectW");
if (MH_CreateHook(InternetConnectW, &HookedInternetConnectA, reinterpret_cast<LPVOID*>(&OldInternetConnectW)) != MH_OK)
{
snprintf(log, 255, "Failed to hook InternetConnectW! Try again running as Admin.\n\n");
Log(&log);
return 1;
}
// Enable the hook for MessageBoxW.
if (MH_EnableHook(MH_ALL_HOOKS) != MH_OK)
{
snprintf(log, 255, "Failed to enable all hooks! Try again running as Admin.\n\n");
Log(&log);
return 1;
}
free(log);
return 0;
}
BOOL attached = FALSE;
#if defined(RELEASE_DLL) || defined(RELEASE_DLL64)
BOOL WINAPI DllMain(
HINSTANCE hinstDLL, // handle to DLL module
DWORD fdwReason, // reason for calling function
LPVOID lpReserved) // reserved
{
// Perform actions based on the reason for calling.
switch (fdwReason)
{
case DLL_PROCESS_ATTACH:
// Initialize once for each new process.
// Return FALSE to fail DLL load.
main();
break;
case DLL_THREAD_ATTACH:
// Do thread-specific initialization.
break;
case DLL_THREAD_DETACH:
// Do thread-specific cleanup.
break;
case DLL_PROCESS_DETACH:
// Perform any necessary cleanup.
break;
}
return TRUE; // Successful DLL_PROCESS_ATTACH.
}
#endif
+9
View File
@@ -1,2 +1,11 @@
# MalMemDetect
Detect strange memory regions and DLLs
Compile as a DLL and inject into a process to identify hollowed DLLs and unmapped memory region calls.
Sleep hook seems to break a few things so I left it in but commented, as well as a few other things that are left more as "Demos" and commented out.
Results by default will output to a file in C:\ drive.
+185
View File
@@ -0,0 +1,185 @@
/*
* MinHook - The Minimalistic API Hooking Library for x64/x86
* Copyright (C) 2009-2017 Tsuda Kageyu.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#if !(defined _M_IX86) && !(defined _M_X64) && !(defined __i386__) && !(defined __x86_64__)
#error MinHook supports only x86 and x64 systems.
#endif
#include <windows.h>
// MinHook Error Codes.
typedef enum MH_STATUS
{
// Unknown error. Should not be returned.
MH_UNKNOWN = -1,
// Successful.
MH_OK = 0,
// MinHook is already initialized.
MH_ERROR_ALREADY_INITIALIZED,
// MinHook is not initialized yet, or already uninitialized.
MH_ERROR_NOT_INITIALIZED,
// The hook for the specified target function is already created.
MH_ERROR_ALREADY_CREATED,
// The hook for the specified target function is not created yet.
MH_ERROR_NOT_CREATED,
// The hook for the specified target function is already enabled.
MH_ERROR_ENABLED,
// The hook for the specified target function is not enabled yet, or already
// disabled.
MH_ERROR_DISABLED,
// The specified pointer is invalid. It points the address of non-allocated
// and/or non-executable region.
MH_ERROR_NOT_EXECUTABLE,
// The specified target function cannot be hooked.
MH_ERROR_UNSUPPORTED_FUNCTION,
// Failed to allocate memory.
MH_ERROR_MEMORY_ALLOC,
// Failed to change the memory protection.
MH_ERROR_MEMORY_PROTECT,
// The specified module is not loaded.
MH_ERROR_MODULE_NOT_FOUND,
// The specified function is not found.
MH_ERROR_FUNCTION_NOT_FOUND
}
MH_STATUS;
// Can be passed as a parameter to MH_EnableHook, MH_DisableHook,
// MH_QueueEnableHook or MH_QueueDisableHook.
#define MH_ALL_HOOKS NULL
#ifdef __cplusplus
extern "C" {
#endif
// Initialize the MinHook library. You must call this function EXACTLY ONCE
// at the beginning of your program.
MH_STATUS WINAPI MH_Initialize(VOID);
// Uninitialize the MinHook library. You must call this function EXACTLY
// ONCE at the end of your program.
MH_STATUS WINAPI MH_Uninitialize(VOID);
// Creates a hook for the specified target function, in disabled state.
// Parameters:
// pTarget [in] A pointer to the target function, which will be
// overridden by the detour function.
// pDetour [in] A pointer to the detour function, which will override
// the target function.
// ppOriginal [out] A pointer to the trampoline function, which will be
// used to call the original target function.
// This parameter can be NULL.
MH_STATUS WINAPI MH_CreateHook(LPVOID pTarget, LPVOID pDetour, LPVOID *ppOriginal);
// Creates a hook for the specified API function, in disabled state.
// Parameters:
// pszModule [in] A pointer to the loaded module name which contains the
// target function.
// pszProcName [in] A pointer to the target function name, which will be
// overridden by the detour function.
// pDetour [in] A pointer to the detour function, which will override
// the target function.
// ppOriginal [out] A pointer to the trampoline function, which will be
// used to call the original target function.
// This parameter can be NULL.
MH_STATUS WINAPI MH_CreateHookApi(
LPCWSTR pszModule, LPCSTR pszProcName, LPVOID pDetour, LPVOID *ppOriginal);
// Creates a hook for the specified API function, in disabled state.
// Parameters:
// pszModule [in] A pointer to the loaded module name which contains the
// target function.
// pszProcName [in] A pointer to the target function name, which will be
// overridden by the detour function.
// pDetour [in] A pointer to the detour function, which will override
// the target function.
// ppOriginal [out] A pointer to the trampoline function, which will be
// used to call the original target function.
// This parameter can be NULL.
// ppTarget [out] A pointer to the target function, which will be used
// with other functions.
// This parameter can be NULL.
MH_STATUS WINAPI MH_CreateHookApiEx(
LPCWSTR pszModule, LPCSTR pszProcName, LPVOID pDetour, LPVOID *ppOriginal, LPVOID *ppTarget);
// Removes an already created hook.
// Parameters:
// pTarget [in] A pointer to the target function.
MH_STATUS WINAPI MH_RemoveHook(LPVOID pTarget);
// Enables an already created hook.
// Parameters:
// pTarget [in] A pointer to the target function.
// If this parameter is MH_ALL_HOOKS, all created hooks are
// enabled in one go.
MH_STATUS WINAPI MH_EnableHook(LPVOID pTarget);
// Disables an already created hook.
// Parameters:
// pTarget [in] A pointer to the target function.
// If this parameter is MH_ALL_HOOKS, all created hooks are
// disabled in one go.
MH_STATUS WINAPI MH_DisableHook(LPVOID pTarget);
// Queues to enable an already created hook.
// Parameters:
// pTarget [in] A pointer to the target function.
// If this parameter is MH_ALL_HOOKS, all created hooks are
// queued to be enabled.
MH_STATUS WINAPI MH_QueueEnableHook(LPVOID pTarget);
// Queues to disable an already created hook.
// Parameters:
// pTarget [in] A pointer to the target function.
// If this parameter is MH_ALL_HOOKS, all created hooks are
// queued to be disabled.
MH_STATUS WINAPI MH_QueueDisableHook(LPVOID pTarget);
// Applies all queued changes in one go.
MH_STATUS WINAPI MH_ApplyQueued(VOID);
// Translates the MH_STATUS to its name as a string.
const char * WINAPI MH_StatusToString(MH_STATUS status);
#ifdef __cplusplus
}
#endif
+87
View File
@@ -0,0 +1,87 @@
// Copyright (c) 2020 ElephantSe4l. All Rights Reserved.
// Released under MPL-2.0, see LICENCE for more information.
#ifndef FRESHYCALLS_FUNCTION_RESULT_HPP_
#define FRESHYCALLS_FUNCTION_RESULT_HPP_
#include <string>
#include "utils.hpp"
namespace freshycalls {
// Formats an error message. Will try to locate "{{result_as_hex}}" and replace it with the
// hexadecimal representation of the result of the function. It will also try to locate
// "{{result_msg}}" and replace it with the error message associated to the result. Returns
// the formatted message.
template<typename Result, typename... FormatArgs>
std::string FormatErrorMsg(Result result, std::string error_msg, bool is_ntstatus, FormatArgs... format_args) {
auto result_placeholder = error_msg.find("{{result_as_hex}}");
auto result_msg_placeholder = error_msg.find("{{result_msg}}");
if (result_placeholder != std::string::npos) {
// std::string("{{result_as_hex}}").length() == 17
error_msg.replace(result_placeholder, 17, "%#010x");
}
if (result_msg_placeholder != std::string::npos) {
// std::string("{{result_msg}}").length() == 14
error_msg.replace(result_msg_placeholder, 14, "%s");
}
if (result_placeholder != std::string::npos && result_msg_placeholder != std::string::npos) {
if (result_msg_placeholder < result_placeholder) {
return utils::FormatString(error_msg, utils::GetErrorMessage(result, is_ntstatus).data(), result, std::forward<FormatArgs>(format_args)...);
} else {
return utils::FormatString(error_msg, result, utils::GetErrorMessage(result, is_ntstatus).data(), std::forward<FormatArgs>(format_args)...);
}
} else if (result_placeholder != std::string::npos) {
return utils::FormatString(error_msg, result, std::forward<FormatArgs>(format_args)...);
} else if (result_msg_placeholder != std::string::npos) {
return utils::FormatString(error_msg, utils::GetErrorMessage(result, is_ntstatus).data(), std::forward<FormatArgs>(format_args)...);
}
return utils::FormatString(error_msg, std::forward<FormatArgs>(format_args)...);
}
// Represents the result of a function. If no expected value is given it will assume the "OK"
// value is 0.
template<typename Result>
struct FunctionResult {
Result result;
explicit FunctionResult(Result result) noexcept {
this->result = result;
}
// Throws if result isn't 0, otherwise it will return the result.
template<typename... FormatArgs>
Result OrDie(std::string_view error_msg, FormatArgs... format_args) {
if (result == 0) {
return result;
} else {
bool is_ntstatus = std::is_same_v<Result, uint32_t>;
throw std::runtime_error(FormatErrorMsg<Result>(result, error_msg.data(), is_ntstatus, std::forward<FormatArgs>(format_args)...));
}
}
// Throws if result isn't `expected_result`, otherwise it will return the result.
template<typename... FormatArgs>
Result ExpectedResultOrDie(Result expected_result, std::string_view error_msg, FormatArgs... format_args) {
if (result == expected_result) {
return result;
} else {
bool is_ntstatus = std::is_same_v<Result, uint32_t>;
throw std::runtime_error(FormatErrorMsg<Result>(result, error_msg.data(), is_ntstatus, std::forward<FormatArgs>(format_args)...));
}
}
};
}
#endif
+241
View File
@@ -0,0 +1,241 @@
// Copyright (c) 2020 ElephantSe4l. All Rights Reserved.
// Released under MPL-2.0, see LICENCE for more information.
#ifndef FRESHYCALLS_NATIVE_HPP_
#define FRESHYCALLS_NATIVE_HPP_
#include <cstdint>
namespace freshycalls::native {
struct LinkedList {
LinkedList *Flink;
LinkedList *Blink;
};
struct UnicodeString {
uint16_t Length;
uint16_t MaximumLength;
wchar_t *Buffer;
};
struct LdrDataEntry {
LinkedList InLoadOrderLinks;
LinkedList InMemoryOrderLinks;
LinkedList InInitializationOrderLinks;
void *DllBase;
void *EntryPoint;
uint32_t SizeOfImage;
LinkedList FullDllName;
LinkedList BaseDllName;
uint32_t Flags;
uint16_t LoadCount;
uint16_t TlsIndex;
};
struct LdrData {
uint32_t Length;
uint8_t Initialized;
void *SsHandle;
LinkedList InLoadOrderModuleList;
LinkedList InMemoryOrderModuleList;
union {
LinkedList InInitializationOrderModuleList;
LinkedList InProgressLinks;
};
void *EntryInProgress;
};
struct PEB {
bool InheritedAddressSpace;
bool ReadImageFileExecOptions;
bool BeingDebugged;
bool ImageUsesLargePages: 1;
bool IsProtectedProcess: 1;
bool IsImageDynamicallyRelocated: 1;
bool SkipPatchingUser32Forwarders: 1;
bool IsPackagedProcess: 1;
bool IsAppContainer: 1;
bool IsProtectedProcessLight: 1;
bool IsLongPathAwareProcess: 1;
void *Mutant;
void *ImageBaseAddress;
LdrData *Ldr;
void *ProcessParameters;
void *SubSystemData;
void *ProcessHeap;
void *FastPebLock;
void *AtlThunkSListPtr;
void *IFEOKey;
bool ProcessInJob: 1;
bool ProcessInitializing: 1;
bool ProcessUsingVEH: 1;
bool ProcessUsingVCH: 1;
bool ProcessUsingFTH: 1;
bool ProcessPreviouslyThrottled: 1;
bool ProcessCurrentlyThrottled: 1;
bool ProcessImagesHotPatched: 1;
uint32_t ReservedBits0: 24;
union {
void *KernelCallbackTable;
void *UserSharedInfoPtr;
};
uint32_t SystemReserved;
uint32_t AtlThunkSListPtr32;
void *ApiSetMap;
uint32_t TlsExpansionCounter;
void *TlsBitmap;
uint32_t TlsBitmapBits[2];
void *ReadOnlySharedMemoryBase;
void *HotpatchInformation;
void **ReadOnlyStaticServerData;
void *AnsiCodePageData;
void *OemCodePageData;
void *UnicodeCaseTableData;
uint32_t NumberOfProcessors;
uint32_t NtGlobalFlag;
uint64_t CriticalSectionTimeout;
size_t HeapSegmentReserve;
size_t HeapSegmentCommit;
size_t HeapDeCommitTotalFreeThreshold;
size_t HeapDeCommitFreeBlockThreshold;
uint32_t NumberOfHeaps;
uint32_t MaximumNumberOfHeaps;
void **ProcessHeaps;
void *GdiSharedHandleTable;
void *ProcessStarterHelper;
uint32_t GdiDCAttributeList;
void *LoaderLock;
uint32_t OSMajorVersion;
uint32_t OSMinorVersion;
uint16_t OSBuildNumber;
uint16_t OSCSDVersion;
uint32_t OSPlatformId;
uint32_t ImageSubsystem;
uint32_t ImageSubsystemMajorVersion;
uint32_t ImageSubsystemMinorVersion;
uint32_t *ActiveProcessAffinityMask;
uint32_t GdiHandleBuffer[60];
void *PostProcessInitRoutine;
void *TlsExpansionBitmap;
uint32_t TlsExpansionBitmapBits[32];
uint32_t SessionId;
};
struct DOSHeader {
uint16_t e_magic;
uint16_t e_cblp;
uint16_t e_cp;
uint16_t e_crlc;
uint16_t e_cparhdr;
uint16_t e_minalloc;
uint16_t e_maxalloc;
uint16_t e_ss;
uint16_t e_sp;
uint16_t e_csum;
uint16_t e_ip;
uint16_t e_cs;
uint16_t e_lfarlc;
uint16_t e_ovno;
uint16_t e_res[4];
uint16_t e_oemid;
uint16_t e_oeminfo;
uint16_t e_res2[10];
int32_t e_lfanew;
};
struct FileHeader {
uint16_t Machine;
uint16_t NumberOfSections;
uint32_t TimeDateStamp;
uint32_t PointerToSymbolTable;
uint32_t NumberOfSymbols;
uint16_t SizeOfOptionalHeader;
uint16_t Characteristics;
};
struct DataDirectory {
uint32_t VirtualAddress;
uint32_t Size;
};
enum DirectoryEntry : uint16_t {
kExport,
kImport,
kResource,
kException,
kSecurity,
kBaseReloc,
kDebug,
kArchitecture,
kGlobalPtr,
kTls,
kBoundImport,
kIAT,
kDelayImport,
kCOMDescriptor
};
struct OptionalHeader64 {
uint16_t Magic;
uint8_t MajorLinkerVersion;
uint8_t MinorLinkerVersion;
uint32_t SizeOfCode;
uint32_t SizeOfInitializedData;
uint32_t SizeOfUninitializedData;
uint32_t AddressOfEntryPoint;
uint32_t BaseOfCode;
uint64_t ImageBase;
uint32_t SectionAlignment;
uint32_t FileAlignment;
uint16_t MajorOperatingSystemVersion;
uint16_t MinorOperatingSystemVersion;
uint16_t MajorImageVersion;
uint16_t MinorImageVersion;
uint16_t MajorSubsystemVersion;
uint16_t MinorSubsystemVersion;
uint32_t Win32VersionValue;
uint32_t SizeOfImage;
uint32_t SizeOfHeaders;
uint32_t CheckSum;
uint16_t Subsystem;
uint16_t DllCharacteristics;
uint64_t SizeOfStackReserve;
uint64_t SizeOfStackCommit;
uint64_t SizeOfHeapReserve;
uint64_t SizeOfHeapCommit;
uint32_t LoaderFlags;
uint32_t NumberOfRvaAndSizes;
DataDirectory DataDirectory[16];
};
struct NTHeaders64 {
uint32_t Signature;
FileHeader FileHeader;
OptionalHeader64 OptionalHeader;
};
struct ExportDirectory {
uint32_t Characteristics;
uint32_t TimeDateStamp;
uint16_t MajorVersion;
uint16_t MinorVersion;
uint32_t Name;
uint32_t Base;
uint32_t NumberOfFunctions;
uint32_t NumberOfNames;
uint32_t AddressOfFunctions;
uint32_t AddressOfNames;
uint32_t AddressOfNameOrdinals;
};
}
#endif
+118
View File
@@ -0,0 +1,118 @@
// Copyright (c) 2020 ElephantSe4l. All Rights Reserved.
// Released under MPL-2.0, see LICENCE for more information.
#include "syscall.hpp"
#include <stdexcept>
#include "native.hpp"
#if defined(__GNUC__) && !defined(__clang__)
#define ALLOC_ON_CODE \
__attribute__((section(".text")))
#else
#define ALLOC_ON_CODE \
_Pragma("section(\".text\")") \
__declspec(allocate(".text"))
#endif
// Custom syscall stub. Receives the number of the service to call as the first argument (rcx).
// The remaining arguments act as the parameters of the service called. In order to pass them
// properly, the stub needs to move forward every parameter by one. (e.g. second -> first,
// third -> second... n -> n-1).
ALLOC_ON_CODE unsigned char manual_syscall_stub[] = {
0x48, 0x89, 0xC8, // mov rax, rcx
0x48, 0x89, 0xD1, // mov rcx, rdx
0x4C, 0x89, 0xC2, // mov rdx, r8
0x4D, 0x89, 0xC8, // mov r8, r9
0x4C, 0x8B, 0x4C, 0x24, 0x28, // mov r9, [rsp+28h]
0x49, 0x89, 0xCA, // mov r10, rcx
0x48, 0x83, 0xC4, 0x08, // add rsp, 8
0x0F, 0x05, // syscall
0x48, 0x83, 0xEC, 0x08, // sub rsp, 8
0xC3 // ret
};
// Custom syscall stub similar to `manual_syscall_stub` but this time it will act as a trampoline
// to another syscall instruction. Receives the number of the service to call as the first argument
// (rcx) but also requires the address of the syscall instruction as the second argument (rdx). As
// with `manual_syscall_stub` the stub needs to forward every argument but this time by two.
ALLOC_ON_CODE unsigned char masked_syscall_stub[] = {
0x41, 0x55, // push r13
0x41, 0x56, // push r14
0x49, 0x89, 0xD6, // mov r14, rdx
0x49, 0x89, 0xCD, // mov r13, rcx
0x4C, 0x89, 0xC1, // mov rcx, r8
0x4C, 0x89, 0xCA, // mov rdx, r9
0x4C, 0x8B, 0x44, 0x24, 0x38, // mov r8, [rsp+38h]
0x4C, 0x8B, 0x4C, 0x24, 0x40, // mov r9, [rsp+40h]
0x48, 0x83, 0xC4, 0x28, // add rsp, 28h
0x4C, 0x8D, 0x1D, 0x0C, 0x00, 0x00, 0x00, // lea r11, [rip+0x0C] ----
0x41, 0xFF, 0xD3, // call r11 |
0x48, 0x83, 0xEC, 0x28, // sub rsp, 28h |
0x41, 0x5E, // pop r14 |
0x41, 0x5D, // pop r13 |
0xC3, // ret |
// |
0x4C, 0x89, 0xE8, // mov rax, r13 <----
0x49, 0x89, 0xCA, // mov r10, rcx
0x41, 0xFF, 0xE6 // jmp r14
};
void freshycalls::Syscall::ExtractStubs() noexcept {
const auto peb = reinterpret_cast<native::PEB *>(__readgsqword(0x60));
// The first loaded module is the process' image, the second is ntdll
const auto ntdll_ldr_entry = reinterpret_cast<native::LdrDataEntry *>(peb->Ldr->InLoadOrderModuleList.Flink->Flink);
const auto ntdll_base = reinterpret_cast<uintptr_t>(ntdll_ldr_entry->DllBase);
const auto dos_header = reinterpret_cast<native::DOSHeader *>(ntdll_base);
const auto nt_headers = reinterpret_cast<native::NTHeaders64 *>(ntdll_base + dos_header->e_lfanew);
const auto
export_dir = reinterpret_cast<native::ExportDirectory *>(ntdll_base + nt_headers->OptionalHeader.DataDirectory[native::kExport].VirtualAddress);
const auto functions_table = reinterpret_cast<uint32_t *>(ntdll_base + export_dir->AddressOfFunctions);
const auto names_table = reinterpret_cast<uint32_t *>(ntdll_base + export_dir->AddressOfNames);
const auto names_ordinals_table = reinterpret_cast<uint16_t *>(ntdll_base + export_dir->AddressOfNameOrdinals);
std::string function_name;
uint16_t stub_ordinal;
uintptr_t stub_address;
for (size_t i = 0; i < export_dir->NumberOfNames; i++) {
function_name = reinterpret_cast<const char *>(ntdll_base + names_table[i]);
// Stubs starts with Nt but not with Ntdll
if (function_name.rfind("Nt", 0) == 0 && function_name.rfind("Ntdll", 0) == std::string::npos) {
stub_ordinal = names_ordinals_table[i];
stub_address = ntdll_base + functions_table[stub_ordinal];
stub_map.insert({stub_address, function_name});
}
}
}
[[nodiscard]] uintptr_t freshycalls::Syscall::GetStubAddr(std::string_view stub_name) {
for (const auto &pair : stub_map) {
if (pair.second == stub_name) {
return pair.first;
}
}
throw std::runtime_error(utils::FormatString("[sisyphus::Syscall::GetStubAddr] Stub \"%s\" not found!", stub_name.data()));
}
[[nodiscard]] uint32_t freshycalls::Syscall::GetSyscallNumber(std::string_view stub_name) {
const auto syscall_entry = syscall_map.find(stub_name.data());
if (syscall_entry == syscall_map.end()) {
throw std::runtime_error(utils::FormatString("[sisyphus::Syscall::GetSyscallNumber] Stub \"%s\" not found!", stub_name.data()));
}
return syscall_entry->second;
}
+181
View File
@@ -0,0 +1,181 @@
// Copyright (c) 2020 ElephantSe4l. All Rights Reserved.
// Released under MPL-2.0, see LICENCE for more information.
#if !defined(__x86_64) && !defined(_M_AMD64)
#error Only x64 compilations are supported
#endif
#ifndef FRESHYCALLS_SYSCALL_HPP_
#define FRESHYCALLS_SYSCALL_HPP_
// compiler intrinsics such as __readgsqword or __movsb
#if defined(__GNUC__) || defined(__GNUG__)
#include <psdk_inc/intrin-impl.h>
#else
#include <intrin.h>
#endif
#include <cstdint>
#include <string>
#include <map>
#include <unordered_map>
#include "function_result.hpp"
using StubMap = std::map<uintptr_t, std::string>;
using SyscallMap = std::unordered_map<std::string, uint32_t>;
using NtStatus = uint32_t;
extern uint8_t manual_syscall_stub[];
extern uint8_t masked_syscall_stub[];
namespace freshycalls {
// Set of utils related to syscalls such as dynamic numbers extraction or dynamic invocation.
//
// `Syscall` class is a singleton class. Service numbers differs between OS builds nor even
// between process. It doesn't make sense to have several instances of this class, it will
// always be the same.
// Remark: As it's a singleton, there's no (external) way to construct this class. You need
// to use the instance getter `get_instance()`
//
// Sample usage:
// auto& syscall = freshycalls::Syscall::get_instance();
// syscall.CallSyscall("NtTestAlert");
class Syscall {
private:
static inline StubMap stub_map;
static inline SyscallMap syscall_map;
// Iterates the export table of ntdll to find every syscall stub and builds an ordered map
// with them. Will build an ordered map because `ExtractSyscallsNumbers` needs it this way.
static void ExtractStubs() noexcept;
// `stub_map` is ordered from lowest to highest using the stub address. Syscalls numbers are
// assigned using this ordering too. The lowest stub address will be the stub with the lowest
// syscall number (0 in this case). We just need to iterate `stub_map` and iterate the syscall
// number on every iteration.
static inline void ExtractSyscallsNumbers() noexcept {
uint32_t syscall_no = 0;
for (const auto &pair : stub_map) {
syscall_map.insert({pair.second, syscall_no});
syscall_no++;
}
};
// Tries to locate the syscall instruction inside a stub using some known patterns. Returns
// the address of the instruction.
[[nodiscard]] static inline uintptr_t FindSyscallInstruction(uintptr_t stub_addr) noexcept {
uintptr_t instruction_addr;
// Since Windows 10 TH2
if (*(reinterpret_cast<unsigned char *>(stub_addr + 0x12)) == 0x0F &&
*(reinterpret_cast<unsigned char *>(stub_addr + 0x13)) == 0x05) {
instruction_addr = stub_addr + 0x12;
}
// From Windows XP to Windows 10 TH2
else if (*(reinterpret_cast<unsigned char *>(stub_addr + 0x8)) == 0x0F &&
*(reinterpret_cast<unsigned char *>(stub_addr + 0x9)) == 0x05) {
instruction_addr = stub_addr + 0x8;
} else {
instruction_addr = 0;
}
return instruction_addr;
};
// Calls the service associated to the `syscall_no` service number using the `stub_addr` stub and
// passing `args` as parameters. Returns a FunctionResult that represents the result of the call.
template<typename... ServiceArgs>
FunctionResult<NtStatus> InternalCaller(uint32_t syscall_no, uintptr_t stub_addr, ServiceArgs... args) noexcept {
using StubDef = NtStatus(__stdcall *)(uint32_t, ServiceArgs...);
StubDef stub = reinterpret_cast<decltype(stub)>(stub_addr);
NtStatus return_value = stub(syscall_no, std::forward<ServiceArgs>(args)...);
return FunctionResult<NtStatus>(return_value);
}
// Private constructor
Syscall() noexcept {
ExtractStubs();
ExtractSyscallsNumbers();
};
public:
// Disable any other constructor or assignment operator
Syscall(const Syscall &) = delete;
Syscall &operator=(const Syscall &) = delete;
Syscall(Syscall &&) = delete;
Syscall &operator=(Syscall &&) = delete;
// Singleton instance getter
static inline Syscall &get_instance() noexcept {
static Syscall instance;
return instance;
}
// Iterates `stub_map` in search of `stub_name` address. Before start iterating it lookups the cache.
[[nodiscard]] uintptr_t GetStubAddr(std::string_view stub_name);
// Iterates `syscall_map` in search of the associated service number of `stub_name`. Before start
// iterating it lookups the cache.
[[nodiscard]] uint32_t GetSyscallNumber(std::string_view stub_name);
// Gets the corresponding service number given the name of the stub and forwards to `InternalCaller`
// using the manual stub. Returns a FunctionResult that represents the result of the call.
template<typename... ServiceArgs>
FunctionResult<NtStatus> DirectCallSyscall(std::string_view stub_name, ServiceArgs... args) {
uint32_t syscall_no = GetSyscallNumber(stub_name);
return InternalCaller(syscall_no, reinterpret_cast<uintptr_t>(&manual_syscall_stub), std::forward<ServiceArgs>(args)...);
}
// Gets the corresponding service number given the name of the stub and decides when to use the
// manual or the masked stub depending on if it can find the syscall instruction inside the
// original stub. Returns a FunctionResult that represents the result of the call.
template<typename... ServiceArgs>
FunctionResult<NtStatus> CallSyscall(std::string_view stub_name, ServiceArgs... args) {
uint32_t syscall_no;
uintptr_t stub_addr;
uintptr_t syscall_inst_addr;
stub_addr = GetStubAddr(stub_name);
syscall_no = GetSyscallNumber(stub_name);
syscall_inst_addr = FindSyscallInstruction(stub_addr);
// If the syscall instruction has not been found, use the direct stub. To use the masked stub
// we need the instruction to be in the original stub.
if (!syscall_inst_addr) {
return InternalCaller(syscall_no, reinterpret_cast<uintptr_t>(&manual_syscall_stub), std::forward<ServiceArgs>(args)...);
}
return InternalCaller(syscall_no, reinterpret_cast<uintptr_t>(&masked_syscall_stub), syscall_inst_addr, std::forward<ServiceArgs>(args)...);
}
};
}
#endif //SISYPHUS_SYSCALL_SYSCALL_HPP_
+43
View File
@@ -0,0 +1,43 @@
// Copyright (c) 2020 ElephantSe4l. All Rights Reserved.
// Released under MPL-2.0, see LICENCE for more information.
#include "utils.hpp"
#include <Windows.h>
#include <stdexcept>
std::string freshycalls::utils::GetErrorMessage(uint32_t error_code, bool is_ntstatus) {
LPSTR error_msg_buffer{};
size_t error_msg_size{};
if (is_ntstatus) {
error_msg_size =
FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK,
(HMODULE) GetModuleHandle("ntdll.dll"),
error_code,
0,
reinterpret_cast<LPSTR>(&error_msg_buffer),
0,
nullptr);
if (error_msg_size == 0) {
throw std::runtime_error(utils::FormatString("[freshycalls::utils::GetErrorMessage] FormatMessageA failed to format (Error Code: %#010x)",
GetLastError()).data());
}
} else {
error_msg_size =
FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK,
nullptr,
error_code,
0,
reinterpret_cast<LPSTR>(&error_msg_buffer),
0,
nullptr);
if (error_msg_size == 0) {
throw std::runtime_error(utils::FormatString("[freshycalls::utils::GetErrorMessage] FormatMessageA failed to format (Error Code: %#010x)",
GetLastError()).data());
}
}
std::string error_msg(error_msg_buffer, error_msg_size);
return error_msg;
}
+37
View File
@@ -0,0 +1,37 @@
// Copyright (c) 2020 ElephantSe4l. All Rights Reserved.
// Released under MPL-2.0, see LICENCE for more information.
#ifndef FRESHYCALLS_UTILS_HPP_
#define FRESHYCALLS_UTILS_HPP_
#include <string>
#include <cstdint>
#include <stdexcept>
namespace freshycalls::utils {
// Tries to get the error message associated to `error_code` using `FormatMessageA`. Returns
// a string containing the message.
std::string GetErrorMessage(uint32_t error_code, bool is_ntstatus = false);
// Tries to format a string using `snprintf`. Returns the formatted string.
// WARNING: Be aware this function is REALLY bug prone as it takes a message template with n arguments and passes it
// directly to `snprintf`. Does not make any kind of check.
template<typename... FormatArgs>
std::string FormatString(std::string_view string_template, FormatArgs... format_args) {
const size_t string_size = snprintf(nullptr, 0, string_template.data(), std::forward<FormatArgs>(format_args)...);
if (string_size <= 0) {
throw std::runtime_error("[sisyphus::utils::FormatString] Formatted string size is negative or 0.");
}
auto formatted_string = new char[string_size + 1];
snprintf(formatted_string, string_size + 1, string_template.data(), std::forward<FormatArgs>(format_args)...);
return std::string(formatted_string);
}
}
#endif