mirror of
https://github.com/waldo-irc/MalMemDetect
synced 2026-06-08 18:07:30 +00:00
First push
This commit is contained in:
Binary file not shown.
Binary file not shown.
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup />
|
||||
</Project>
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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.
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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_
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user