Skip to content

Commit

Permalink
DllBlock: hook VirtualAlloc/VirtualProtect for dynamic code exclusions (
Browse files Browse the repository at this point in the history
  • Loading branch information
dinhngtu authored May 26, 2024
1 parent b78c093 commit db69626
Show file tree
Hide file tree
Showing 3 changed files with 217 additions and 21 deletions.
189 changes: 168 additions & 21 deletions NanaZip.Shared/DllBlock.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,28 +13,51 @@

#include <Detours.h>

#include <mutex>
#include <array>
#include <vector>
#include <intrin.h>

#include "DllBlock.h"
#include "Mitigations.h"

#ifdef NDEBUG

namespace
{
enum DllFlags : unsigned int {
UnknownDll = 0,
DllNeedsBlocking = 1,
DllNeedsDynamicCodeOptout = 2,
};

static std::vector<std::pair<UINT_PTR, SIZE_T>> dynamic_code_ranges;
static std::mutex dynamic_code_range_lock;

using DllType = std::pair<const char*, DllFlags>;
static const std::array<DllType, 4> DllList = {
std::make_pair("BaseGUI.dll", DllNeedsDynamicCodeOptout),
std::make_pair("ExplorerPatcher.amd64.dll", DllNeedsBlocking),
std::make_pair("ExplorerPatcher.IA-32.dll", DllNeedsBlocking),
std::make_pair("TFMain64.dll", DllNeedsBlocking),
};

static decltype(NtMapViewOfSection)* RealNtMapViewOfSection = nullptr;
static decltype(NtQuerySection)* RealNtQuerySection = nullptr;
static decltype(NtUnmapViewOfSection)* RealNtUnmapViewOfSection = nullptr;
static decltype(VirtualAlloc)* RealVirtualAlloc = nullptr;
static decltype(VirtualAllocEx)* RealVirtualAllocEx = nullptr;
static decltype(VirtualProtect)* RealVirtualProtect = nullptr;
static decltype(VirtualProtectEx)* RealVirtualProtectEx = nullptr;

static bool DllNeedsBlocking(const char* dllName) {
if (!::_stricmp(dllName, "ExplorerPatcher.amd64.dll")) {
return true;
}
else if (!::_stricmp(dllName, "ExplorerPatcher.IA-32.dll")) {
return true;
}
else if (!::_stricmp(dllName, "TFMain64.dll")) {
// Translucent Flyouts
return true;
static DllFlags FindDll(const char* dllName)
{
for (auto it = DllList.begin(); it != DllList.end(); it++) {
if (!_stricmp(it->first, dllName)) {
return it->second;
}
}
return false;
return UnknownDll;
}

static inline bool CheckExtents(size_t viewSize, size_t offset, size_t size) {
Expand Down Expand Up @@ -72,6 +95,17 @@ namespace
return true;
}

static bool HandleIsCurrentProcess(HANDLE ProcessHandle) {
return ProcessHandle == GetCurrentProcess() || GetProcessId(ProcessHandle) == GetCurrentProcessId();
}

static bool ProtectionIsExecute(DWORD Protect) {
return Protect == PAGE_EXECUTE ||
Protect == PAGE_EXECUTE_READ ||
Protect == PAGE_EXECUTE_READWRITE ||
Protect == PAGE_EXECUTE_WRITECOPY;
}

static NTSTATUS NTAPI MyNtMapViewOfSection(
_In_ HANDLE SectionHandle,
_In_ HANDLE ProcessHandle,
Expand All @@ -98,13 +132,7 @@ namespace
InheritDisposition,
AllocationType,
Win32Protect);
if (ret < 0) {
return ret;
}
if (!(Win32Protect == PAGE_EXECUTE ||
Win32Protect == PAGE_EXECUTE_READ ||
Win32Protect == PAGE_EXECUTE_READWRITE ||
Win32Protect == PAGE_EXECUTE_WRITECOPY)) {
if (ret < 0 || !HandleIsCurrentProcess(ProcessHandle) || !ProtectionIsExecute(Win32Protect)) {
return ret;
}
SECTION_BASIC_INFORMATION sbi = { 0 };
Expand All @@ -116,11 +144,117 @@ namespace
if (!GetDllExportName(dllName, reinterpret_cast<const char*>(*BaseAddress), *ViewSize)) {
return ret;
}
bool needsBlocking = DllNeedsBlocking(dllName);
if (needsBlocking) {
DllFlags dllType = FindDll(dllName);
if (dllType & DllNeedsBlocking) {
RealNtUnmapViewOfSection(ProcessHandle, *BaseAddress);
return STATUS_ACCESS_DENIED;
}
else if (dllType & DllNeedsDynamicCodeOptout) {
std::lock_guard<std::mutex> lock(dynamic_code_range_lock);
dynamic_code_ranges.push_back(std::make_pair(reinterpret_cast<UINT_PTR>(*BaseAddress), *ViewSize));
}
return ret;
}

static NTSTATUS NTAPI MyNtUnmapViewOfSection(
_In_ HANDLE ProcessHandle,
_In_opt_ PVOID BaseAddress)
{
NTSTATUS ret = RealNtUnmapViewOfSection(ProcessHandle, BaseAddress);
if (ret < 0 || !HandleIsCurrentProcess(ProcessHandle)) {
return ret;
}
UINT_PTR ptr = reinterpret_cast<UINT_PTR>(BaseAddress);
{
std::lock_guard<std::mutex> lock(dynamic_code_range_lock);
for (auto it = dynamic_code_ranges.begin(); it != dynamic_code_ranges.end(); it++) {
if (ptr >= it->first && ptr < it->first + it->second) {
dynamic_code_ranges.erase(it);
break;
}
}
}
return ret;
}

static bool CallerUsesDynamicCode(void* pCaller)
{
UINT_PTR caller = reinterpret_cast<UINT_PTR>(pCaller);
std::lock_guard<std::mutex> lock(dynamic_code_range_lock);
for (auto it = dynamic_code_ranges.begin(); it != dynamic_code_ranges.end(); it++) {
if (caller >= it->first && caller < it->first + it->second) {
return true;
}
}
return false;
}

static _Ret_maybenull_ _Post_writable_byte_size_(dwSize) LPVOID WINAPI MyVirtualAlloc(
_In_opt_ LPVOID lpAddress,
_In_ SIZE_T dwSize,
_In_ DWORD flAllocationType,
_In_ DWORD flProtect)
{
if (!ProtectionIsExecute(flProtect) ||
!CallerUsesDynamicCode(_ReturnAddress())) {
return RealVirtualAlloc(lpAddress, dwSize, flAllocationType, flProtect);
}
// what do we even do if it fails? so, no error checking.
NanaZipSetThreadDynamicCodeOptout(TRUE);
LPVOID ret = RealVirtualAlloc(lpAddress, dwSize, flAllocationType, flProtect);
NanaZipSetThreadDynamicCodeOptout(FALSE);
return ret;
}

static _Ret_maybenull_ _Post_writable_byte_size_(dwSize) LPVOID WINAPI MyVirtualAllocEx(
_In_ HANDLE hProcess,
_In_opt_ LPVOID lpAddress,
_In_ SIZE_T dwSize,
_In_ DWORD flAllocationType,
_In_ DWORD flProtect)
{
if (!HandleIsCurrentProcess(hProcess) ||
!ProtectionIsExecute(flProtect) ||
!CallerUsesDynamicCode(_ReturnAddress())) {
return RealVirtualAllocEx(hProcess, lpAddress, dwSize, flAllocationType, flProtect);
}
NanaZipSetThreadDynamicCodeOptout(TRUE);
LPVOID ret = RealVirtualAllocEx(hProcess, lpAddress, dwSize, flAllocationType, flProtect);
NanaZipSetThreadDynamicCodeOptout(FALSE);
return ret;
}

static _Success_(return != FALSE) BOOL WINAPI MyVirtualProtect(
_In_ LPVOID lpAddress,
_In_ SIZE_T dwSize,
_In_ DWORD flNewProtect,
_Out_ PDWORD lpflOldProtect)
{
if (!ProtectionIsExecute(flNewProtect) ||
!CallerUsesDynamicCode(_ReturnAddress())) {
return RealVirtualProtect(lpAddress, dwSize, flNewProtect, lpflOldProtect);
}
NanaZipSetThreadDynamicCodeOptout(TRUE);
BOOL ret = RealVirtualProtect(lpAddress, dwSize, flNewProtect, lpflOldProtect);
NanaZipSetThreadDynamicCodeOptout(FALSE);
return ret;
}

static _Success_(return != FALSE) BOOL WINAPI MyVirtualProtectEx(
_In_ HANDLE hProcess,
_In_ LPVOID lpAddress,
_In_ SIZE_T dwSize,
_In_ DWORD flNewProtect,
_Out_ PDWORD lpflOldProtect)
{
if (!HandleIsCurrentProcess(hProcess) ||
!ProtectionIsExecute(flNewProtect) ||
!CallerUsesDynamicCode(_ReturnAddress())) {
return RealVirtualProtectEx(hProcess, lpAddress, dwSize, flNewProtect, lpflOldProtect);
}
NanaZipSetThreadDynamicCodeOptout(TRUE);
BOOL ret = RealVirtualProtectEx(hProcess, lpAddress, dwSize, flNewProtect, lpflOldProtect);
NanaZipSetThreadDynamicCodeOptout(FALSE);
return ret;
}
}
Expand All @@ -129,13 +263,26 @@ EXTERN_C BOOL WINAPI NanaZipBlockDlls()
{
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());

RealNtMapViewOfSection = static_cast<decltype(NtMapViewOfSection)*>(DetourFindFunction("ntdll.dll", "NtMapViewOfSection"));
RealNtQuerySection = static_cast<decltype(NtQuerySection)*>(DetourFindFunction("ntdll.dll", "NtQuerySection"));
RealNtUnmapViewOfSection = static_cast<decltype(NtUnmapViewOfSection)*>(DetourFindFunction("ntdll.dll", "NtUnmapViewOfSection"));
if (DetourAttach(&RealNtMapViewOfSection, MyNtMapViewOfSection) != NO_ERROR) {

RealVirtualAlloc = static_cast<decltype(VirtualAlloc)*>(DetourFindFunction("kernel32.dll", "VirtualAlloc"));
RealVirtualAllocEx = static_cast<decltype(VirtualAllocEx)*>(DetourFindFunction("kernel32.dll", "VirtualAllocEx"));
RealVirtualProtect = static_cast<decltype(VirtualProtect)*>(DetourFindFunction("kernel32.dll", "VirtualProtect"));
RealVirtualProtectEx = static_cast<decltype(VirtualProtectEx)*>(DetourFindFunction("kernel32.dll", "VirtualProtectEx"));

if (DetourAttach(&RealNtMapViewOfSection, MyNtMapViewOfSection) != NO_ERROR ||
DetourAttach(&RealNtUnmapViewOfSection, MyNtUnmapViewOfSection) != NO_ERROR ||
DetourAttach(&RealVirtualAlloc, MyVirtualAlloc) != NO_ERROR ||
DetourAttach(&RealVirtualAllocEx, MyVirtualAllocEx) != NO_ERROR ||
DetourAttach(&RealVirtualProtect, MyVirtualProtect) != NO_ERROR ||
DetourAttach(&RealVirtualProtectEx, MyVirtualProtectEx) != NO_ERROR) {
DetourTransactionAbort();
return FALSE;
}

DetourTransactionCommit();
return TRUE;
}
Expand Down
48 changes: 48 additions & 0 deletions NanaZip.Shared/Mitigations.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,14 @@

#include <Detours.h>

#include <atomic>

namespace
{
#ifdef NDEBUG
static std::atomic<decltype(::SetThreadInformation)*> SetThreadInformationPtr = nullptr;
#endif

static HMODULE GetKernel32ModuleHandle()
{
static HMODULE CachedResult = ::GetModuleHandleW(L"kernel32.dll");
Expand Down Expand Up @@ -105,13 +111,26 @@ EXTERN_C BOOL WINAPI NanaZipEnableMitigations()
{
PROCESS_MITIGATION_DYNAMIC_CODE_POLICY Policy = { 0 };
Policy.ProhibitDynamicCode = 1;
Policy.AllowThreadOptOut = 1;
if (!::SetProcessMitigationPolicyWrapper(
ProcessDynamicCodePolicy,
&Policy,
sizeof(PROCESS_MITIGATION_DYNAMIC_CODE_POLICY)))
{
return FALSE;
}

HMODULE ModuleHandle = ::GetKernel32ModuleHandle();
if (ModuleHandle)
{
using ProcType = decltype(::SetThreadInformation)*;
FARPROC ProcAddress = ::GetProcAddress(
ModuleHandle,
"SetThreadInformation");
SetThreadInformationPtr.store(
reinterpret_cast<ProcType>(ProcAddress),
std::memory_order_relaxed);
}
}
#endif // NDEBUG

Expand Down Expand Up @@ -151,3 +170,32 @@ EXTERN_C BOOL WINAPI NanaZipDisableChildProcesses()

return TRUE;
}

#ifdef NDEBUG

EXTERN_C BOOL WINAPI NanaZipSetThreadDynamicCodeOptout(BOOL optout)
{
using ProcType = decltype(::SetThreadInformation)*;
ProcType SetThreadInformationWrapper = SetThreadInformationPtr.load(std::memory_order_relaxed);
if (SetThreadInformationWrapper) {
DWORD ThreadPolicy = optout ? THREAD_DYNAMIC_CODE_ALLOW : 0;
return SetThreadInformationWrapper(
::GetCurrentThread(),
ThreadDynamicCodePolicy,
&ThreadPolicy,
sizeof(DWORD));
}
else {
return TRUE;
}
}

#else

EXTERN_C BOOL WINAPI NanaZipSetThreadDynamicCodeOptout(BOOL optout)
{
UNREFERENCED_PARAMETER(optout);
return TRUE;
}

#endif
1 change: 1 addition & 0 deletions NanaZip.Shared/Mitigations.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,6 @@

EXTERN_C BOOL WINAPI NanaZipEnableMitigations();
EXTERN_C BOOL WINAPI NanaZipDisableChildProcesses();
EXTERN_C BOOL WINAPI NanaZipSetThreadDynamicCodeOptout(BOOL optout);

#endif // !NANAZIP_SHARED_MITIGATIONS

0 comments on commit db69626

Please sign in to comment.