Files
bb107-MemoryModulePP/MemoryModule/MmpTls.cpp
T
2022-10-01 17:28:23 +08:00

840 lines
26 KiB
C++

#include "stdafx.h"
#include <cassert>
#include <algorithm>
#include <3rdparty/Detours/detours.h>
//
// ThreadLocalStoragePointer Tls indexs
// [0, MMP_START_TLS_INDEX) Reserved for ntdll loader
// [MMP_START_TLS_INDEX, MMP_MAXIMUM_TLS_INDEX) Reserved for MemoryModule
//
#define MMP_START_TLS_INDEX 0x80 //128
#define MMP_MAXIMUM_TLS_INDEX 0x100 //256
#define MMP_TLSP_INDEX_BUFFER_SIZE (MMP_MAXIMUM_TLS_INDEX / 8) //32
#if (((MMP_START_TLS_INDEX | MMP_MAXIMUM_TLS_INDEX) & 7) || (MMP_START_TLS_INDEX >= MMP_MAXIMUM_TLS_INDEX))
#error "MMP_START_TLS_INDEX must be smaller than MMP_MAXIMUM_TLS_INDEX, and both are 8-bit aligned."
#endif
#define MmpAllocateTlsp() (RtlAllocateHeap(\
RtlProcessHeap(),\
HEAP_ZERO_MEMORY,\
sizeof(PVOID)* MMP_MAXIMUM_TLS_INDEX\
))
typedef struct _TLS_VECTOR {
union
{
ULONG Length;
HANDLE ThreadId;
};
struct _TLS_VECTOR* PreviousDeferredTlsVector;
PVOID ModuleTlsData[ANYSIZE_ARRAY];
} TLS_VECTOR, * PTLS_VECTOR;
typedef struct _TLS_ENTRY {
LIST_ENTRY TlsEntryLinks;
IMAGE_TLS_DIRECTORY TlsDirectory;
PLDR_DATA_TABLE_ENTRY ModuleEntry;
} TLS_ENTRY, * PTLS_ENTRY;
typedef struct _MMP_TLSP_RECORD {
LIST_ENTRY InMmpThreadLocalStoragePointer;
HANDLE UniqueThread;
// PEB->ThreadLocalStoragePointer allocated by ntdll!Ldr
PVOID* TlspLdrBlock;
// PEB->ThreadLocalStoragePointer allocated by MemoryModulePP
PVOID* TlspMmpBlock;
}MMP_TLSP_RECORD, * PMMP_TLSP_RECORD;
typedef struct _THREAD_CONTEXT {
PTHREAD_START_ROUTINE ThreadStartRoutine;
LPVOID ThreadParameter;
}THREAD_CONTEXT, * PTHREAD_CONTEXT;
PVOID NTAPI MmpQuerySystemInformation(
_In_ SYSTEM_INFORMATION_CLASS SystemInformationClass,
_Out_opt_ LPDWORD ReturnLength) {
if (ReturnLength)*ReturnLength = 0;
NTSTATUS status;
PVOID buffer = nullptr;
ULONG len = 0;
do {
RtlFreeHeap(
RtlProcessHeap(),
0,
buffer
);
buffer = nullptr;
if (len) {
len *= 2;
buffer = RtlAllocateHeap(
RtlProcessHeap(),
0,
len
);
if (!buffer)return nullptr;
}
status = NtQuerySystemInformation(
SystemInformationClass,
buffer,
len,
&len
);
if (NT_SUCCESS(status))break;
} while (status == STATUS_INFO_LENGTH_MISMATCH);
if (ReturnLength)*ReturnLength = len;
return buffer;
}
DWORD NTAPI MmpGetThreadCount() {
DWORD result = 0;
auto pid = NtCurrentProcessId();
auto spi = PSYSTEM_PROCESS_INFORMATION(MmpQuerySystemInformation(SystemProcessInformation, nullptr));
if (spi) {
auto p = spi;
while (true) {
if (p->UniqueProcessId == pid) {
result = p->NumberOfThreads;
break;
}
if (!p->NextEntryOffset)break;
p = PSYSTEM_PROCESS_INFORMATION(LPSTR(p) + p->NextEntryOffset);
}
RtlFreeHeap(RtlProcessHeap(), 0, spi);
}
return result;
}
DWORD NTAPI MmpUserThreadStart(LPVOID lpThreadParameter) {
THREAD_CONTEXT Context;
bool success = false;
PMMP_TLSP_RECORD record = nullptr;
__try {
RtlCopyMemory(
&Context,
lpThreadParameter,
sizeof(Context)
);
RtlFreeHeap(RtlProcessHeap(), 0, lpThreadParameter);
}
__except (EXCEPTION_EXECUTE_HANDLER) {
return GetExceptionCode();
}
if (!NtCurrentTeb()->ThreadLocalStoragePointer) {
goto __skip_tls;
}
//
// Allocate and replace ThreadLocalStoragePointer for new thread
//
EnterCriticalSection(&MmpGlobalDataPtr->MmpTls.MmpTlspLock);
record = PMMP_TLSP_RECORD(RtlAllocateHeap(RtlProcessHeap(), 0, sizeof(MMP_TLSP_RECORD)));
if (record) {
record->TlspLdrBlock = (PVOID*)NtCurrentTeb()->ThreadLocalStoragePointer;
record->TlspMmpBlock = (PVOID*)MmpAllocateTlsp();
record->UniqueThread = NtCurrentThreadId();
if (record->TlspMmpBlock) {
auto size = CONTAINING_RECORD(record->TlspLdrBlock, TLS_VECTOR, ModuleTlsData)->Length;
RtlCopyMemory(
record->TlspMmpBlock,
record->TlspLdrBlock,
size * sizeof(PVOID)
);
NtCurrentTeb()->ThreadLocalStoragePointer = record->TlspMmpBlock;
InsertTailList(&MmpGlobalDataPtr->MmpTls.MmpThreadLocalStoragePointer, &record->InMmpThreadLocalStoragePointer);
success = true;
}
else {
RtlFreeHeap(RtlProcessHeap(), 0, record);
}
}
LeaveCriticalSection(&MmpGlobalDataPtr->MmpTls.MmpTlspLock);
//
// Handle MemoryModule Tls data
//
if (success) {
RtlAcquireSRWLockShared(&MmpGlobalDataPtr->MmpTls.MmpTlsListLock);
auto ThreadLocalStoragePointer = (PVOID*)NtCurrentTeb()->ThreadLocalStoragePointer;
PLIST_ENTRY entry = MmpGlobalDataPtr->MmpTls.MmpTlsList.Flink;
while (entry != &MmpGlobalDataPtr->MmpTls.MmpTlsList) {
PTLS_ENTRY tls = CONTAINING_RECORD(entry, TLS_ENTRY, TlsEntryLinks);
auto len = tls->TlsDirectory.EndAddressOfRawData - tls->TlsDirectory.StartAddressOfRawData;
PVOID data = RtlAllocateHeap(RtlProcessHeap(), 0, len);
if (!len) {
success = false;
break;
}
RtlCopyMemory(
data,
PVOID(tls->TlsDirectory.StartAddressOfRawData),
len
);
ThreadLocalStoragePointer[tls->TlsDirectory.Characteristics] = data;
entry = entry->Flink;
}
RtlReleaseSRWLockShared(&MmpGlobalDataPtr->MmpTls.MmpTlsListLock);
}
if (!success) {
return ERROR_NOT_ENOUGH_MEMORY;
}
EnterCriticalSection(&MmpGlobalDataPtr->MmpTls.MmpTlspLock);
++MmpGlobalDataPtr->MmpTls.MmpActiveThreadCount;
LeaveCriticalSection(&MmpGlobalDataPtr->MmpTls.MmpTlspLock);
__skip_tls:
return Context.ThreadStartRoutine(Context.ThreadParameter);
}
NTSTATUS NTAPI HookNtCreateThread(
_Out_ PHANDLE ThreadHandle,
_In_ ACCESS_MASK DesiredAccess,
_In_opt_ POBJECT_ATTRIBUTES ObjectAttributes,
_In_ HANDLE ProcessHandle,
_Out_ PCLIENT_ID ClientId,
_In_ PCONTEXT ThreadContext,
_In_ PVOID InitialTeb,
_In_ BOOLEAN CreateSuspended) {
CONTEXT Context = *ThreadContext;
PTHREAD_CONTEXT _Context = PTHREAD_CONTEXT(RtlAllocateHeap(RtlProcessHeap(), 0, sizeof(*_Context)));
NTSTATUS status;
if (!_Context)return STATUS_NO_MEMORY;
#ifndef _WIN64
_Context->ThreadStartRoutine = PTHREAD_START_ROUTINE(Context.Eax);
_Context->ThreadParameter = LPVOID(Context.Ebx);
Context.Eax = DWORD(MmpUserThreadStart);
Context.Ebx = DWORD(_Context);
#else
_Context->ThreadStartRoutine = PTHREAD_START_ROUTINE(Context.Rcx);
_Context->ThreadParameter = LPVOID(Context.Rdx);
Context.Rcx = ULONG64(MmpUserThreadStart);
Context.Rdx = ULONG64(_Context);
#endif
status = MmpGlobalDataPtr->MmpTls.Hooks.OriginNtCreateThread(
ThreadHandle,
DesiredAccess,
ObjectAttributes,
ProcessHandle,
ClientId,
&Context,
(PINITIAL_TEB)InitialTeb,
CreateSuspended
);
if (!NT_SUCCESS(status)) {
RtlFreeHeap(RtlProcessHeap(), 0, _Context);
}
return status;
}
NTSTATUS NTAPI HookNtCreateThreadEx(
_Out_ PHANDLE ThreadHandle,
_In_ ACCESS_MASK DesiredAccess,
_In_opt_ POBJECT_ATTRIBUTES ObjectAttributes,
_In_ HANDLE ProcessHandle,
_In_ PVOID StartRoutine,
_In_opt_ PVOID Argument,
_In_ ULONG CreateFlags,
_In_ SIZE_T ZeroBits,
_In_ SIZE_T StackSize,
_In_ SIZE_T MaximumStackSize,
_In_opt_ PVOID AttributeList) {
PTHREAD_CONTEXT Context = PTHREAD_CONTEXT(RtlAllocateHeap(RtlProcessHeap(), 0, sizeof(*Context)));
if (!Context) {
return STATUS_NO_MEMORY;
}
Context->ThreadStartRoutine = PTHREAD_START_ROUTINE(StartRoutine);
Context->ThreadParameter = Argument;
NTSTATUS status = MmpGlobalDataPtr->MmpTls.Hooks.OriginNtCreateThreadEx(
ThreadHandle,
DesiredAccess,
ObjectAttributes,
ProcessHandle,
MmpUserThreadStart,
Context,
CreateFlags,
ZeroBits,
StackSize,
MaximumStackSize,
(PPS_ATTRIBUTE_LIST)AttributeList
);
if (!NT_SUCCESS(status)) {
RtlFreeHeap(RtlProcessHeap(), 0, Context);
}
return status;
}
VOID NTAPI HookLdrShutdownThread(VOID) {
PLIST_ENTRY entry;
PMMP_TLSP_RECORD record = nullptr;
//
// Find our tlsp record
//
EnterCriticalSection(&MmpGlobalDataPtr->MmpTls.MmpTlspLock);
entry = MmpGlobalDataPtr->MmpTls.MmpThreadLocalStoragePointer.Flink;
while (entry != &MmpGlobalDataPtr->MmpTls.MmpThreadLocalStoragePointer) {
auto p = CONTAINING_RECORD(entry, MMP_TLSP_RECORD, InMmpThreadLocalStoragePointer);
if (p->UniqueThread == NtCurrentThreadId()) {
assert(p->TlspMmpBlock == NtCurrentTeb()->ThreadLocalStoragePointer);
//
// Restore tlsp
//
NtCurrentTeb()->ThreadLocalStoragePointer = p->TlspLdrBlock;
RemoveEntryList(&p->InMmpThreadLocalStoragePointer);
record = p;
break;
}
entry = entry->Flink;
}
--MmpGlobalDataPtr->MmpTls.MmpActiveThreadCount;
LeaveCriticalSection(&MmpGlobalDataPtr->MmpTls.MmpTlspLock);
//
// Free MemoryModule Tls data
//
RtlAcquireSRWLockExclusive(&MmpGlobalDataPtr->MmpTls.MmpTlsListLock);
if (record) {
auto TlspMmpBlock = (PVOID*)record->TlspMmpBlock;
entry = MmpGlobalDataPtr->MmpTls.MmpTlsList.Flink;
while (entry != &MmpGlobalDataPtr->MmpTls.MmpTlsList) {
auto p = CONTAINING_RECORD(entry, TLS_ENTRY, TlsEntryLinks);
RtlFreeHeap(RtlProcessHeap(), 0, TlspMmpBlock[p->TlsDirectory.Characteristics]);
entry = entry->Flink;
}
RtlFreeHeap(RtlProcessHeap(), 0, TlspMmpBlock);
}
else {
if (MmpGlobalDataPtr->MmpTls.MmpTlsList.Flink != &MmpGlobalDataPtr->MmpTls.MmpTlsList) {
assert(false);
}
}
RtlReleaseSRWLockExclusive(&MmpGlobalDataPtr->MmpTls.MmpTlsListLock);
//
// Call the original function
//
MmpGlobalDataPtr->MmpTls.Hooks.OriginLdrShutdownThread();
}
BOOL NTAPI PreHookNtSetInformationProcess() {
DWORD CurrentTlsPointerSize = CONTAINING_RECORD(NtCurrentTeb()->ThreadLocalStoragePointer, TLS_VECTOR, ModuleTlsData)->Length;
DWORD CurrentThreadCount = MmpGetThreadCount();
DWORD ProcessTlsInformationLength = sizeof(PROCESS_TLS_INFORMATION) + (CurrentThreadCount - 1) * sizeof(THREAD_TLS_INFORMATION);
BOOL success = TRUE;
NTSTATUS status;
auto ProcessTlsInformation = PPROCESS_TLS_INFORMATION(RtlAllocateHeap(
RtlProcessHeap(),
HEAP_ZERO_MEMORY,
ProcessTlsInformationLength * 2
));
if (ProcessTlsInformation) {
ProcessTlsInformation->OperationType = ProcessTlsReplaceVector;
ProcessTlsInformation->Reserved = 0;
ProcessTlsInformation->TlsVectorLength = CurrentTlsPointerSize;
ProcessTlsInformation->ThreadDataCount = CurrentThreadCount;
for (DWORD i = 0; i < CurrentThreadCount; ++i) {
auto& current = ProcessTlsInformation->ThreadData[i];
current.TlsVector = (PVOID*)MmpAllocateTlsp();
if (!current.TlsVector) {
for (DWORD j = 0; j < i; ++j) {
RtlFreeHeap(RtlProcessHeap(), 0, ProcessTlsInformation->ThreadData[j].TlsVector);
}
success = FALSE;
break;
}
}
if (success) {
auto tmpTlsInformation = PPROCESS_TLS_INFORMATION(LPBYTE(ProcessTlsInformation) + ProcessTlsInformationLength);
RtlCopyMemory(
tmpTlsInformation,
ProcessTlsInformation,
ProcessTlsInformationLength
);
status = NtSetInformationProcess(
NtCurrentProcess(),
PROCESSINFOCLASS::ProcessTlsInformation,
ProcessTlsInformation,
ProcessTlsInformationLength
);
if (NT_SUCCESS(status)) {
EnterCriticalSection(&MmpGlobalDataPtr->MmpTls.MmpTlspLock);
for (DWORD i = 0; i < CurrentThreadCount; ++i) {
auto const& LdrTls = ProcessTlsInformation->ThreadData[i];
auto const& MmpTls = tmpTlsInformation->ThreadData[i];
auto record = PMMP_TLSP_RECORD(RtlAllocateHeap(RtlProcessHeap(), 0, sizeof(MMP_TLSP_RECORD)));
assert(record);
record->TlspLdrBlock = LdrTls.TlsVector;
record->TlspMmpBlock = MmpTls.TlsVector;
record->UniqueThread = LdrTls.ThreadId;
InsertTailList(&MmpGlobalDataPtr->MmpTls.MmpThreadLocalStoragePointer, &record->InMmpThreadLocalStoragePointer);
}
LeaveCriticalSection(&MmpGlobalDataPtr->MmpTls.MmpTlspLock);
}
}
RtlFreeHeap(RtlProcessHeap(), 0, ProcessTlsInformation);
}
return success;
}
NTSTATUS NTAPI HookNtSetInformationProcess(
_In_ HANDLE ProcessHandle,
_In_ PROCESSINFOCLASS ProcessInformationClass,
_In_reads_bytes_(ProcessInformationLength) PVOID ProcessInformation,
_In_ ULONG ProcessInformationLength) {
if (ProcessInformationClass != ProcessTlsInformation) {
return MmpGlobalDataPtr->MmpTls.Hooks.OriginNtSetInformationProcess(
ProcessHandle,
ProcessInformationClass,
ProcessInformation,
ProcessInformationLength
);
}
auto ProcessTlsInformation = PPROCESS_TLS_INFORMATION(ProcessInformation);
auto hProcess = ProcessHandle ? ProcessHandle : NtCurrentProcess();
auto TlsLength = ProcessInformationLength;
PPROCESS_TLS_INFORMATION Tls = nullptr;
NTSTATUS status = STATUS_SUCCESS;
do {
if (ProcessTlsInformation->OperationType >= MaxProcessTlsOperation) {
status = STATUS_INVALID_PARAMETER;
break;
}
//
// Allocate new buffer to change it
//
Tls = PPROCESS_TLS_INFORMATION(RtlAllocateHeap(RtlProcessHeap(), 0, ProcessInformationLength));
if (Tls) {
RtlCopyMemory(
Tls,
ProcessInformation,
ProcessInformationLength
);
}
else {
status = STATUS_NO_MEMORY;
break;
}
//
// Convert ReplaceVector to ReplaceIndex
//
if (ProcessTlsInformation->OperationType == ProcessTlsReplaceVector) {
// from MemoryModulePP
if (!ProcessHandle) {
status = STATUS_INVALID_PARAMETER;
break;
}
// reserved 0x50 PVOID for ntdll loader
if (ProcessTlsInformation->TlsVectorLength >= MMP_START_TLS_INDEX) {
status = STATUS_NO_MEMORY;
break;
}
Tls->OperationType = ProcessTlsReplaceIndex;
for (auto i = 0; i < Tls->ThreadDataCount; ++i) {
Tls->ThreadData[i].TlsModulePointer = Tls->ThreadData[i].TlsVector[ProcessTlsInformation->TlsVectorLength];
}
}
else {
if (ProcessHandle) {
if (ProcessTlsInformation->TlsIndex >= MMP_START_TLS_INDEX) {
status = STATUS_NO_MEMORY;
break;
}
}
else {
if (ProcessTlsInformation->TlsIndex < MMP_START_TLS_INDEX || ProcessTlsInformation->TlsIndex >= MMP_MAXIMUM_TLS_INDEX) {
status = STATUS_NO_MEMORY;
break;
}
}
}
status = MmpGlobalDataPtr->MmpTls.Hooks.OriginNtSetInformationProcess(
hProcess,
ProcessInformationClass,
Tls,
TlsLength
);
//
// Modify our mapping
//
EnterCriticalSection(&MmpGlobalDataPtr->MmpTls.MmpTlspLock);
for (auto i = 0; i < Tls->ThreadDataCount; ++i) {
bool found = false;
PLIST_ENTRY entry = MmpGlobalDataPtr->MmpTls.MmpThreadLocalStoragePointer.Flink;
// Find thread-spec tlsp
while (entry != &MmpGlobalDataPtr->MmpTls.MmpThreadLocalStoragePointer) {
PMMP_TLSP_RECORD j = CONTAINING_RECORD(entry, MMP_TLSP_RECORD, InMmpThreadLocalStoragePointer);
if (ProcessTlsInformation->OperationType == ProcessTlsReplaceVector) {
if (j->TlspMmpBlock[ProcessTlsInformation->TlsVectorLength] == ProcessTlsInformation->ThreadData->TlsVector[ProcessTlsInformation->TlsVectorLength]) {
found = true;
// Copy old data to new pointer
RtlCopyMemory(
ProcessTlsInformation->ThreadData[i].TlsVector,
j->TlspMmpBlock,
sizeof(PVOID) * ProcessTlsInformation->TlsVectorLength
);
// Swap the tlsp
std::swap(
j->TlspLdrBlock,
ProcessTlsInformation->ThreadData[i].TlsVector
);
}
}
else {
if (j->TlspMmpBlock[ProcessTlsInformation->TlsIndex] == ProcessTlsInformation->ThreadData[i].TlsModulePointer) {
found = true;
if (ProcessHandle) {
j->TlspLdrBlock[ProcessTlsInformation->TlsIndex] = ProcessTlsInformation->ThreadData[i].TlsModulePointer;
}
ProcessTlsInformation->ThreadData[i].TlsModulePointer = Tls->ThreadData[i].TlsModulePointer;
}
}
if (found)break;
entry = entry->Flink;
}
//assert(found);
if (found) {
ProcessTlsInformation->ThreadData[i].Flags = Tls->ThreadData[i].Flags;
ProcessTlsInformation->ThreadData[i].ThreadId = Tls->ThreadData[i].ThreadId;
}
}
LeaveCriticalSection(&MmpGlobalDataPtr->MmpTls.MmpTlspLock);
} while (false);
RtlFreeHeap(RtlProcessHeap(), 0, Tls);
return status;
}
NTSTATUS NTAPI MmpAcquireTlsIndex(_Out_ PULONG TlsIndex) {
*TlsIndex = -1;
ULONG Index = RtlFindClearBitsAndSet(&MmpGlobalDataPtr->MmpTls.MmpTlsBitmap, 1, 0);
if (Index != -1) {
*TlsIndex = Index;
return STATUS_SUCCESS;
}
return STATUS_INSUFFICIENT_RESOURCES;
}
NTSTATUS NTAPI MmpAllocateTlsEntry(
_In_ PIMAGE_TLS_DIRECTORY lpTlsDirectory,
_In_ PLDR_DATA_TABLE_ENTRY lpModuleEntry,
_Out_ PULONG lpTlsIndex,
_Out_ PTLS_ENTRY* lpTlsEntry) {
PTLS_ENTRY Entry = nullptr;
IMAGE_TLS_DIRECTORY TlsDirectory;
ULONG Length = 0;
NTSTATUS status;
DWORD TlsIndex;
__try {
RtlCopyMemory(
&TlsDirectory,
lpTlsDirectory,
sizeof(IMAGE_TLS_DIRECTORY)
);
*PULONG(TlsDirectory.AddressOfIndex) = 0;
*lpTlsIndex = 0;
*lpTlsEntry = nullptr;
}
__except (EXCEPTION_EXECUTE_HANDLER) {
return GetExceptionCode();
}
Entry = (PTLS_ENTRY)RtlAllocateHeap(
NtCurrentPeb()->ProcessHeap,
HEAP_ZERO_MEMORY,
sizeof(TLS_ENTRY)
);
if (!Entry) {
return STATUS_NO_MEMORY;
}
status = MmpAcquireTlsIndex(&TlsIndex);
if (!NT_SUCCESS(status)) {
RtlFreeHeap(NtCurrentPeb()->ProcessHeap, 0, Entry);
return status;
}
RtlCopyMemory(
&Entry->TlsDirectory,
&TlsDirectory,
sizeof(IMAGE_TLS_DIRECTORY)
);
Entry->ModuleEntry = lpModuleEntry;
Entry->TlsDirectory.Characteristics =
*PULONG(Entry->TlsDirectory.AddressOfIndex) = TlsIndex;
RtlAcquireSRWLockExclusive(&MmpGlobalDataPtr->MmpTls.MmpTlsListLock);
InsertTailList(&MmpGlobalDataPtr->MmpTls.MmpTlsList, &Entry->TlsEntryLinks);
RtlReleaseSRWLockExclusive(&MmpGlobalDataPtr->MmpTls.MmpTlsListLock);
*lpTlsEntry = Entry;
*lpTlsIndex = TlsIndex;
return STATUS_SUCCESS;
}
NTSTATUS NTAPI MmpReleaseTlsEntry(_In_ PLDR_DATA_TABLE_ENTRY lpModuleEntry) {
RtlAcquireSRWLockExclusive(&MmpGlobalDataPtr->MmpTls.MmpTlsListLock);
for (auto entry = MmpGlobalDataPtr->MmpTls.MmpTlsList.Flink; entry != &MmpGlobalDataPtr->MmpTls.MmpTlsList; entry = entry->Flink) {
auto p = CONTAINING_RECORD(entry, TLS_ENTRY, TlsEntryLinks);
if (p->ModuleEntry == lpModuleEntry) {
RemoveEntryList(&p->TlsEntryLinks);
RtlClearBit(&MmpGlobalDataPtr->MmpTls.MmpTlsBitmap, p->TlsDirectory.Characteristics);
RtlFreeHeap(RtlProcessHeap(), 0, p);
break;
}
}
RtlReleaseSRWLockExclusive(&MmpGlobalDataPtr->MmpTls.MmpTlsListLock);
return STATUS_SUCCESS;
}
NTSTATUS NTAPI MmpHandleTlsData(_In_ PLDR_DATA_TABLE_ENTRY lpModuleEntry) {
PIMAGE_TLS_DIRECTORY lpTlsDirectory;
ULONG DirectorySize;
NTSTATUS status;
ULONG TlsIndex;
PTLS_ENTRY TlsEntry;
lpTlsDirectory = (PIMAGE_TLS_DIRECTORY)RtlImageDirectoryEntryToData(
lpModuleEntry->DllBase,
TRUE,
IMAGE_DIRECTORY_ENTRY_TLS,
&DirectorySize
);
if (!lpTlsDirectory || !DirectorySize) {
return STATUS_SUCCESS;
}
status = MmpAllocateTlsEntry(
lpTlsDirectory,
lpModuleEntry,
&TlsIndex,
&TlsEntry
);
if (!NT_SUCCESS(status)) {
return STATUS_INSUFFICIENT_RESOURCES;
}
auto ThreadCount = MmpGlobalDataPtr->MmpTls.MmpActiveThreadCount;
auto success = true;
auto Length = sizeof(PROCESS_TLS_INFORMATION) + (ThreadCount - 1) * sizeof(THREAD_TLS_INFORMATION);
auto ProcessTlsInformation = PPROCESS_TLS_INFORMATION(RtlAllocateHeap(RtlProcessHeap(), HEAP_ZERO_MEMORY, Length));
if (!ProcessTlsInformation) {
MmpReleaseTlsEntry(lpModuleEntry);
return STATUS_NO_MEMORY;
}
ProcessTlsInformation->OperationType = ProcessTlsReplaceIndex;
ProcessTlsInformation->Reserved = 0;
ProcessTlsInformation->TlsIndex = TlsIndex;
ProcessTlsInformation->ThreadDataCount = ThreadCount;
for (DWORD i = 0; i < ThreadCount; ++i) {
auto& current = ProcessTlsInformation->ThreadData[i];
current.TlsModulePointer = RtlAllocateHeap(
RtlProcessHeap(),
0,
lpTlsDirectory->EndAddressOfRawData - lpTlsDirectory->StartAddressOfRawData
);
if (!current.TlsModulePointer) {
for (DWORD j = 0; j < i; ++j) {
RtlFreeHeap(RtlProcessHeap(), 0, ProcessTlsInformation->ThreadData[j].TlsModulePointer);
}
success = false;
break;
}
RtlCopyMemory(
current.TlsModulePointer,
PVOID(lpTlsDirectory->StartAddressOfRawData),
lpTlsDirectory->EndAddressOfRawData - lpTlsDirectory->StartAddressOfRawData
);
}
if (!success) {
MmpReleaseTlsEntry(lpModuleEntry);
return STATUS_NO_MEMORY;
}
status = NtSetInformationProcess(
nullptr, // hack
PROCESSINFOCLASS::ProcessTlsInformation,
ProcessTlsInformation,
Length
);
for (DWORD i = 0; i < ProcessTlsInformation->ThreadDataCount; ++i) {
RtlFreeHeap(RtlProcessHeap(), 0, ProcessTlsInformation->ThreadData[i].TlsModulePointer);
}
RtlFreeHeap(RtlProcessHeap(), 0, ProcessTlsInformation);
return status;
}
BOOL NTAPI MmpTlsInitialize() {
auto tls = CONTAINING_RECORD(NtCurrentTeb()->ThreadLocalStoragePointer, TLS_VECTOR, TLS_VECTOR::ModuleTlsData);
if (tls && tls->Length > MMP_START_TLS_INDEX) {
RtlRaiseStatus(STATUS_NOT_SUPPORTED);
return FALSE;
}
//
// Capture thread count
//
MmpGlobalDataPtr->MmpTls.MmpActiveThreadCount = MmpGetThreadCount();
//
// Initialize tlsp
//
InitializeCriticalSection(&MmpGlobalDataPtr->MmpTls.MmpTlspLock);
InitializeListHead(&MmpGlobalDataPtr->MmpTls.MmpThreadLocalStoragePointer);
//
// Initialize tls list
//
InitializeListHead(&MmpGlobalDataPtr->MmpTls.MmpTlsList);
RtlInitializeSRWLock(&MmpGlobalDataPtr->MmpTls.MmpTlsListLock);
PULONG buffer = PULONG(RtlAllocateHeap(RtlProcessHeap(), HEAP_ZERO_MEMORY, MMP_TLSP_INDEX_BUFFER_SIZE));
if (!buffer) RtlRaiseStatus(STATUS_NO_MEMORY);
RtlFillMemory(buffer, MMP_START_TLS_INDEX / 8, -1);
RtlInitializeBitMap(&MmpGlobalDataPtr->MmpTls.MmpTlsBitmap, buffer, MMP_MAXIMUM_TLS_INDEX);
if (NtCurrentTeb()->ThreadLocalStoragePointer) {
if (!PreHookNtSetInformationProcess()) {
RtlRaiseStatus(STATUS_UNSUCCESSFUL);
}
}
//
// Hook functions
//
MmpGlobalDataPtr->MmpTls.Hooks.OriginNtCreateThread = NtCreateThread;
MmpGlobalDataPtr->MmpTls.Hooks.OriginNtCreateThreadEx = NtCreateThreadEx;
MmpGlobalDataPtr->MmpTls.Hooks.OriginLdrShutdownThread = LdrShutdownThread;
MmpGlobalDataPtr->MmpTls.Hooks.OriginNtSetInformationProcess = NtSetInformationProcess;
DetourTransactionBegin();
DetourUpdateThread(NtCurrentThread());
DetourAttach((PVOID*)&MmpGlobalDataPtr->MmpTls.Hooks.OriginNtCreateThread, HookNtCreateThread);
DetourAttach((PVOID*)&MmpGlobalDataPtr->MmpTls.Hooks.OriginNtCreateThreadEx, HookNtCreateThreadEx);
DetourAttach((PVOID*)&MmpGlobalDataPtr->MmpTls.Hooks.OriginLdrShutdownThread, HookLdrShutdownThread);
DetourAttach((PVOID*)&MmpGlobalDataPtr->MmpTls.Hooks.OriginNtSetInformationProcess, HookNtSetInformationProcess);
DetourTransactionCommit();
return TRUE;
}