Skip to content

Commit 7c5326f

Browse files
MikeHolmanakroshg
authored andcommitted
Edge - ACG bypass using UnmapViewOfFile - Google, Inc.
Modifying page protections cross-process is an issue, as attacker could possibly unmap JIT code and map in their own read/write memory where we expect JIT code, and trick JIT process into making it executable. To avoid this we need something to replace our ZeroMemory/VirtualProtectEx(PAGE_NOACCESS) method of mock-decommit (since files have no decommit support), as this later requires a VirtualProtectEx(PAGE_READEXECUTE) to recommit. The solution is to use VirtualUnlockEx, which will serve the same function for us.
1 parent 6d5532d commit 7c5326f

File tree

4 files changed

+73
-43
lines changed

4 files changed

+73
-43
lines changed

lib/Common/CommonDefines.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,7 @@
324324

325325
#ifndef NTBUILD
326326
#define DELAYLOAD_SECTIONAPI 1
327+
#define DELAYLOAD_UNLOCKMEMORY 1
327328
#endif
328329

329330
#ifdef NTBUILD

lib/Common/Core/DelayLoadLibrary.cpp

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,4 +317,43 @@ NtdllLibrary::NTSTATUS NtdllLibrary::Close(_In_ HANDLE Handle)
317317
#endif
318318
}
319319

320+
#ifndef DELAYLOAD_UNLOCKMEMORY
321+
extern "C"
322+
WINBASEAPI
323+
NtdllLibrary::NTSTATUS
324+
WINAPI
325+
NtUnlockVirtualMemory(
326+
_In_ HANDLE ProcessHandle,
327+
_Inout_ PVOID *BaseAddress,
328+
_Inout_ PSIZE_T RegionSize,
329+
_In_ ULONG MapType
330+
);
331+
#endif
332+
333+
NtdllLibrary::NTSTATUS NtdllLibrary::UnlockVirtualMemory(
334+
_In_ HANDLE ProcessHandle,
335+
_Inout_ PVOID *BaseAddress,
336+
_Inout_ PSIZE_T RegionSize,
337+
_In_ ULONG MapType)
338+
{
339+
#ifdef DELAYLOAD_UNLOCKMEMORY
340+
if (m_hModule)
341+
{
342+
if (unlock == nullptr)
343+
{
344+
unlock = (PFnNtUnlockVirtualMemory)GetFunction("NtUnlockVirtualMemory");
345+
if (unlock == nullptr)
346+
{
347+
Assert(false);
348+
return -1;
349+
}
350+
}
351+
return unlock(ProcessHandle, BaseAddress, RegionSize, MapType);
352+
}
353+
return -1;
354+
#else
355+
return NtUnlockVirtualMemory(ProcessHandle, BaseAddress, RegionSize, MapType);
356+
#endif
357+
}
358+
320359
#endif // _WIN32

lib/Common/Core/DelayLoadLibrary.h

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ class NtdllLibrary : protected DelayLoadLibrary
3434
public:
3535
// needed for InitializeObjectAttributes
3636
static const ULONG OBJ_KERNEL_HANDLE = 0x00000200;
37+
static const ULONG MAP_PROCESS = 1;
3738

3839
typedef struct _UNICODE_STRING {
3940
USHORT Length;
@@ -105,6 +106,13 @@ class NtdllLibrary : protected DelayLoadLibrary
105106
typedef NTSTATUS(NTAPI *PFnNtClose)(_In_ HANDLE Handle);
106107
PFnNtClose close;
107108

109+
typedef NTSTATUS(NTAPI *PFnNtUnlockVirtualMemory)(
110+
_In_ HANDLE ProcessHandle,
111+
_Inout_ PVOID *BaseAddress,
112+
_Inout_ PSIZE_T RegionSize,
113+
_In_ ULONG MapType);
114+
PFnNtUnlockVirtualMemory unlock;
115+
108116
public:
109117
static NtdllLibrary* Instance;
110118

@@ -117,7 +125,8 @@ class NtdllLibrary : protected DelayLoadLibrary
117125
createSection(NULL),
118126
mapViewOfSection(NULL),
119127
unmapViewOfSection(NULL),
120-
close(NULL)
128+
close(NULL),
129+
unlock(nullptr)
121130
{
122131
this->EnsureFromSystemDirOnly();
123132
}
@@ -176,5 +185,12 @@ class NtdllLibrary : protected DelayLoadLibrary
176185
NTSTATUS Close(
177186
_In_ HANDLE Handle
178187
);
188+
189+
NTSTATUS UnlockVirtualMemory(
190+
_In_ HANDLE ProcessHandle,
191+
_Inout_ PVOID *BaseAddress,
192+
_Inout_ PSIZE_T RegionSize,
193+
_In_ ULONG MapType
194+
);
179195
};
180196
#endif

lib/Common/Memory/SectionAllocWrapper.cpp

Lines changed: 16 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,21 @@
1010
#ifdef NTDDI_WIN10_RS2
1111
#if (NTDDI_VERSION >= NTDDI_WIN10_RS2)
1212
#define USEFILEMAP2 1
13+
#define USEVIRTUALUNLOCKEX 1
1314
#endif
1415
#endif
1516

1617
namespace Memory
1718
{
19+
20+
void UnlockMemory(HANDLE process, LPVOID address, SIZE_T size)
21+
{
22+
#if USEVIRTUALUNLOCKEX
23+
VirtualUnlockEx(process, address, size);
24+
#else
25+
NtdllLibrary::Instance->UnlockVirtualMemory(process, &address, &size, NtdllLibrary::MAP_PROCESS);
26+
#endif
27+
}
1828

1929
void CloseSectionHandle(HANDLE handle)
2030
{
@@ -593,12 +603,6 @@ SectionAllocWrapper::AllocPages(LPVOID requestAddress, size_t pageCount, DWORD a
593603
return nullptr;
594604
}
595605
address = requestAddress;
596-
597-
if ((allocationType & MEM_COMMIT) == MEM_COMMIT)
598-
{
599-
const DWORD allocProtectFlags = AutoSystemInfo::Data.IsCFGEnabled() ? PAGE_EXECUTE_RO_TARGETS_INVALID : PAGE_EXECUTE_READ;
600-
address = VirtualAllocEx(this->process, address, dwSize, MEM_COMMIT, allocProtectFlags);
601-
}
602606
}
603607

604608
return address;
@@ -661,11 +665,7 @@ BOOL SectionAllocWrapper::Free(LPVOID lpAddress, size_t dwSize, DWORD dwFreeType
661665
ZeroMemory(localAddr, AutoSystemInfo::PageSize);
662666
FreeLocal(localAddr);
663667
}
664-
DWORD oldFlags = NULL;
665-
if (!VirtualProtectEx(this->process, lpAddress, dwSize, PAGE_NOACCESS, &oldFlags))
666-
{
667-
return FALSE;
668-
}
668+
UnlockMemory(this->process, lpAddress, dwSize);
669669
}
670670

671671
return TRUE;
@@ -924,37 +924,15 @@ LPVOID PreReservedSectionAllocWrapper::AllocPages(LPVOID lpAddress, DECLSPEC_GUA
924924
AssertMsg(freeSegmentsBVIndex < PreReservedAllocationSegmentCount, "Invalid BitVector index calculation?");
925925
AssertMsg(dwSize % AutoSystemInfo::PageSize == 0, "COMMIT is managed at AutoSystemInfo::PageSize granularity");
926926

927-
char * allocatedAddress = nullptr;
928-
929-
if ((allocationType & MEM_COMMIT) != 0)
930-
{
931-
#if defined(ENABLE_JIT_CLAMP)
932-
AutoEnableDynamicCodeGen enableCodeGen;
933-
#endif
934-
935-
const DWORD allocProtectFlags = AutoSystemInfo::Data.IsCFGEnabled() ? PAGE_EXECUTE_RO_TARGETS_INVALID : PAGE_EXECUTE_READ;
936-
allocatedAddress = (char *)VirtualAllocEx(this->process, addressToReserve, dwSize, MEM_COMMIT, allocProtectFlags);
937-
if (allocatedAddress == nullptr)
938-
{
939-
MemoryOperationLastError::RecordLastError();
940-
}
941-
}
942-
else
943-
{
944-
// Just return the uncommitted address if we didn't ask to commit it.
945-
allocatedAddress = addressToReserve;
946-
}
947-
948927
// Keep track of the committed pages within the preReserved Memory Region
949-
if (lpAddress == nullptr && allocatedAddress != nullptr)
928+
if (lpAddress == nullptr)
950929
{
951-
Assert(allocatedAddress == addressToReserve);
952930
Assert(requestedNumOfSegments != 0);
953931
freeSegments.ClearRange(freeSegmentsBVIndex, static_cast<uint>(requestedNumOfSegments));
954-
}
932+
}
955933

956-
PreReservedHeapTrace(_u("MEM_COMMIT: StartAddress: 0x%p of size: 0x%x * 0x%x bytes \n"), allocatedAddress, requestedNumOfSegments, AutoSystemInfo::Data.GetAllocationGranularityPageSize());
957-
return allocatedAddress;
934+
PreReservedHeapTrace(_u("MEM_COMMIT: StartAddress: 0x%p of size: 0x%x * 0x%x bytes \n"), addressToReserve, requestedNumOfSegments, AutoSystemInfo::Data.GetAllocationGranularityPageSize());
935+
return addressToReserve;
958936
}
959937
}
960938

@@ -989,11 +967,7 @@ PreReservedSectionAllocWrapper::Free(LPVOID lpAddress, size_t dwSize, DWORD dwFr
989967
FreeLocal(localAddr);
990968
}
991969

992-
DWORD oldFlags = NULL;
993-
if(!VirtualProtectEx(this->process, lpAddress, dwSize, PAGE_NOACCESS, &oldFlags))
994-
{
995-
return FALSE;
996-
}
970+
UnlockMemory(this->process, lpAddress, dwSize);
997971

998972
size_t requestedNumOfSegments = dwSize / AutoSystemInfo::Data.GetAllocationGranularityPageSize();
999973
Assert(requestedNumOfSegments <= MAXUINT32);

0 commit comments

Comments
 (0)