This commit is contained in:
Boring
2020-02-10 00:59:04 +08:00
parent 06023a00e4
commit 3236290b2c
8 changed files with 854 additions and 85 deletions
+8 -4
View File
@@ -52,6 +52,7 @@ int MemoryLoadString(HMEMORYMODULE, UINT, LPTSTR, int);
*/
int MemoryLoadStringEx(HMEMORYMODULE, UINT, LPTSTR, int, WORD);
NTSTATUS NTAPI NtLoadDllMemory(
OUT HMEMORYMODULE* BaseAddress,
IN LPVOID BufferAddress,
@@ -59,7 +60,7 @@ NTSTATUS NTAPI NtLoadDllMemory(
);
/*
NtLoadDllMemoryEx dwFlags
NtLoadDllMemoryEx dwFlags
*/
//If this flag is specified, all subsequent flags will be ignored.
@@ -77,12 +78,15 @@ NTSTATUS NTAPI NtLoadDllMemory(
//If this flag is specified, DllName and DllFullName cannot be nullptr,
// they can be arbitrary strings without having to be correct file names and paths.
//Otherwise, DllName and DllFullName will use random names if they are nullptr.
//For compatibility with GetModuleHandle, DllName and DllFullName should be guaranteed to always end in .dll
//For compatibility with GetModuleHandle, DllName and DllFullName should be guaranteed to always end in ".dll"
#define LOAD_FLAGS_USE_DLL_NAME 0x00000004
//Dont call LdrpHandleTlsData routine if this flag is specified.
#define LOAD_FLAGS_NOT_HANDLE_TLS 0x00000008
NTSTATUS NTAPI NtLoadDllMemoryExW(
OUT HMEMORYMODULE* BaseAddress,
OUT PLDR_DATA_TABLE_ENTRY* LdrEntry OPTIONAL,
OUT PVOID* LdrEntry OPTIONAL,
IN DWORD dwFlags,
IN LPVOID BufferAddress,
IN size_t BufferSize,
@@ -91,7 +95,7 @@ NTSTATUS NTAPI NtLoadDllMemoryExW(
);
NTSTATUS NTAPI NtLoadDllMemoryExA(
OUT HMEMORYMODULE* BaseAddress,
OUT PLDR_DATA_TABLE_ENTRY* LdrEntry OPTIONAL,
OUT PVOID* LdrEntry OPTIONAL,
IN DWORD dwFlags,
IN LPVOID BufferAddress,
IN size_t BufferSize,
+3
View File
@@ -30,6 +30,9 @@
<ClInclude Include="NativeFunctionsInternal.h" />
<ClInclude Include="rtltype.h" />
</ItemGroup>
<ItemGroup>
<None Include="..\README.md" />
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<ProjectGuid>{5B1F46DB-036E-4A50-AF5F-F5D6584D42C6}</ProjectGuid>
@@ -42,4 +42,9 @@
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="..\README.md">
<Filter>Resource Files</Filter>
</None>
</ItemGroup>
</Project>
+194 -24
View File
@@ -274,15 +274,15 @@ static NTSTATUS NTAPI NtRemoveModuleBaseAddressIndexNode(IN PLDR_DATA_TABLE_ENTR
static bool NTAPI NtInitializeLdrDataTableEntry(
OUT PLDR_DATA_TABLE_ENTRY LdrEntry,
IN DWORD dwFlags,
IN PVOID BaseAddress,
IN ULONG SizeofImage,
IN DWORD TimeDateStamp,
IN UNICODE_STRING &DllBaseName,
IN UNICODE_STRING &DllFullName,
IN PVOID EntryPoint) {
IN UNICODE_STRING &DllFullName) {
UNREFERENCED_PARAMETER(dwFlags);
RtlZeroMemory(LdrEntry, NtLdrDataTableEntrySize());
PIMAGE_NT_HEADERS headers = RtlImageNtHeader(BaseAddress);
if (!headers)return false;
bool FlagsProcessed = false;
switch (NtWindowsVersion()) {
case win10:
@@ -300,11 +300,16 @@ static bool NTAPI NtInitializeLdrDataTableEntry(
entry->LoadReason = LoadReasonDynamicLoad;
if (!NT_SUCCESS(NtInsertModuleBaseAddressIndexNode(LdrEntry, BaseAddress)))return false;
if (!(entry->DdagNode = (decltype(entry->DdagNode))NtAllocateLdrpHeap(sizeof(_LDR_DDAG_NODE))))return false;
NtInitializeListEntry(&entry->NodeModuleLink);
NtInitializeListEntry(&entry->DdagNode->Modules);
NtInitializeSingleEntry(&entry->DdagNode->CondenseLink);
//NtInitializeListEntry(&entry->NodeModuleLink);
//NtInitializeListEntry(&entry->DdagNode->Modules);
entry->NodeModuleLink.Flink = &entry->DdagNode->Modules;
entry->NodeModuleLink.Blink = &entry->DdagNode->Modules;
entry->DdagNode->Modules.Flink = &entry->NodeModuleLink;
entry->DdagNode->Modules.Blink = &entry->NodeModuleLink;
entry->DdagNode->State = LdrModulesReadyToRun;
entry->DdagNode->LoadCount = 0;
NtInitializeSingleEntry(&entry->DdagNode->CondenseLink);
}
case win7: {
@@ -325,12 +330,13 @@ static bool NTAPI NtInitializeLdrDataTableEntry(
}
case xp: {
LdrEntry->DllBase = BaseAddress;
LdrEntry->SizeOfImage = SizeofImage;
LdrEntry->TimeDateStamp = TimeDateStamp;
LdrEntry->SizeOfImage = headers->OptionalHeader.SizeOfImage;
LdrEntry->TimeDateStamp = headers->FileHeader.TimeDateStamp;
LdrEntry->BaseDllName = DllBaseName;
LdrEntry->FullDllName = DllFullName;
LdrEntry->EntryPoint = EntryPoint;
if (headers->OptionalHeader.DllCharacteristics & IMAGE_FILE_DLL)LdrEntry->Flags |= LDRP_IMAGE_DLL;
LdrEntry->EntryPoint = (PVOID)((size_t)BaseAddress + headers->OptionalHeader.AddressOfEntryPoint);
if (!FlagsProcessed) LdrEntry->Flags = LDRP_IMAGE_DLL | LDRP_ENTRY_INSERTED | LDRP_ENTRY_PROCESSED | LDRP_PROCESS_ATTACH_CALLED;
NtInitializeListEntry(&LdrEntry->HashLinks);
return true;
}
default:return false;
@@ -355,6 +361,7 @@ static bool NTAPI NtFreeLdrDataTableEntry(IN PLDR_DATA_TABLE_ENTRY LdrEntry) {
NtFreeLdrpHeap(LdrEntry->FullDllName.Buffer);
RemoveEntryList(&LdrEntry->InLoadOrderLinks);
RemoveEntryList(&LdrEntry->InMemoryOrderLinks);
RemoveEntryList(&LdrEntry->InInitializationOrderLinks);
RemoveEntryList(&LdrEntry->HashLinks);
NtFreeLdrpHeap(LdrEntry);
return true;
@@ -508,9 +515,10 @@ static VOID NTAPI NtInsertMemoryTableEntry(IN PLDR_DATA_TABLE_ENTRY LdrEntry) {
/* Insert into other lists */
InsertTailList(&PebData->InLoadOrderModuleList, &LdrEntry->InLoadOrderLinks);
InsertTailList(&PebData->InMemoryOrderModuleList, &LdrEntry->InMemoryOrderLinks);
InsertTailList(&PebData->InInitializationOrderModuleList, &LdrEntry->InInitializationOrderLinks);
}
static NTSTATUS NTAPI NtMapDllMemory(IN HMEMORYMODULE ViewBase, IN PCWSTR DllName OPTIONAL,
static NTSTATUS NTAPI NtMapDllMemory(IN HMEMORYMODULE ViewBase, IN DWORD dwFlags, IN PCWSTR DllName OPTIONAL,
IN PCWSTR lpFullDllName OPTIONAL, OUT PLDR_DATA_TABLE_ENTRY* DataTableEntry OPTIONAL) {
UNICODE_STRING FullDllName, BaseDllName;
@@ -526,11 +534,7 @@ static NTSTATUS NTAPI NtMapDllMemory(IN HMEMORYMODULE ViewBase, IN PCWSTR DllNam
return STATUS_NO_MEMORY;
}
if (!NtInitializeLdrDataTableEntry(LdrEntry, ViewBase,
NtHeaders->OptionalHeader.SizeOfImage,
NtHeaders->FileHeader.TimeDateStamp,
BaseDllName, FullDllName,
(PVOID)(NtHeaders->OptionalHeader.AddressOfEntryPoint + NtHeaders->OptionalHeader.ImageBase))) {
if (!NtInitializeLdrDataTableEntry(LdrEntry, dwFlags, ViewBase, BaseDllName, FullDllName)) {
NtFreeLdrpHeap(LdrEntry);
NtFreeLdrpHeap(BaseDllName.Buffer);
NtFreeLdrpHeap(FullDllName.Buffer);
@@ -548,16 +552,25 @@ NTSTATUS NTAPI NtLoadDllMemory(OUT HMEMORYMODULE* BaseAddress, IN LPVOID BufferA
NTSTATUS NTAPI NtLoadDllMemoryExW(
OUT HMEMORYMODULE* BaseAddress,
OUT PLDR_DATA_TABLE_ENTRY* LdrEntry OPTIONAL,
OUT PVOID* LdrEntry OPTIONAL,
IN DWORD dwFlags,
IN LPVOID BufferAddress,
IN size_t BufferSize,
IN LPCWSTR DllName OPTIONAL,
IN LPCWSTR DllFullName OPTIONAL) {
if (IsBadReadPtr(BufferAddress, BufferSize) || IsBadWritePtr(BaseAddress, sizeof(HMEMORYMODULE)))return STATUS_ACCESS_VIOLATION;
*BaseAddress = nullptr;
PMEMORYMODULE module = nullptr;
NTSTATUS status = STATUS_SUCCESS;
PLDR_DATA_TABLE_ENTRY ModuleEntry = nullptr;
__try {
if (IsBadReadPtr(BufferAddress, BufferSize))status = STATUS_ACCESS_VIOLATION;
*BaseAddress = nullptr;
if (LdrEntry)*LdrEntry = nullptr;
}
__except (EXCEPTION_EXECUTE_HANDLER) {
status = GetExceptionCode();
}
if (!NT_SUCCESS(status))return status;
if (dwFlags & LOAD_FLAGS_NOT_MAP_DLL) {
dwFlags &= LOAD_FLAGS_NOT_MAP_DLL;
@@ -612,28 +625,44 @@ NTSTATUS NTAPI NtLoadDllMemoryExW(
}
module->loadFromNtLoadDllMemory = true;
if (dwFlags & LOAD_FLAGS_NOT_MAP_DLL) return STATUS_SUCCESS;
status = NtMapDllMemory(*BaseAddress, DllName, DllFullName, LdrEntry);
status = NtMapDllMemory(*BaseAddress, dwFlags, DllName, DllFullName, &ModuleEntry);
if (!NT_SUCCESS(status)) {
NtUnloadDllMemory(*BaseAddress);
*BaseAddress = nullptr;
return status;
}
module->MappedDll = true;
if (LdrEntry)*LdrEntry = ModuleEntry;
if (!(dwFlags & LOAD_FLAGS_NOT_USE_REFERENCE_COUNT))module->UseReferenceCount = true;
if (dwFlags & LOAD_FLAGS_NOT_ADD_INVERTED_FUNCTION)return STATUS_SUCCESS;
status = RtlInsertInvertedFunctionTable((PVOID)module->codeBase, RtlImageNtHeader(*BaseAddress)->OptionalHeader.SizeOfImage);
if (!NT_SUCCESS(status)) {
NtUnloadDllMemory(*BaseAddress);
*BaseAddress = nullptr;
if (LdrEntry)*LdrEntry = nullptr;
return status;
}
module->InsertInvertedFunctionTableEntry = true;
if (dwFlags & LOAD_FLAGS_NOT_HANDLE_TLS)return STATUS_SUCCESS;
status = LdrpHandleTlsData(ModuleEntry);
if (!NT_SUCCESS(status)) {
NtUnloadDllMemory(*BaseAddress);
*BaseAddress = nullptr;
if (LdrEntry)*LdrEntry = nullptr;
return status;
}
return STATUS_SUCCESS;
}
NTSTATUS NtLoadDllMemoryExA(
OUT HMEMORYMODULE* BaseAddress,
OUT PLDR_DATA_TABLE_ENTRY* LdrEntry OPTIONAL,
OUT PVOID* LdrEntry OPTIONAL,
IN DWORD dwFlags,
IN LPVOID BufferAddress,
IN size_t BufferSize,
@@ -907,7 +936,7 @@ static NTSTATUS NTAPI RtlProtectMrdata(IN SIZE_T Protect) {
NTSTATUS NTAPI RtlInsertInvertedFunctionTable(IN PVOID BaseAddress, IN size_t ImageSize) {
static auto table = PRTL_INVERTED_FUNCTION_TABLE(RtlFindLdrpInvertedFunctionTable());
if (!table)return STATUS_NOT_SUPPORTED;
bool need_virtual_protect = RtlIsWindowsVersionOrGreater(10, 0, 0);
bool need_virtual_protect = RtlIsWindowsVersionOrGreater(8, 3, 0);
NTSTATUS status;
if (need_virtual_protect) {
@@ -940,3 +969,144 @@ NTSTATUS NTAPI RtlRemoveInvertedFunctionTable(IN PVOID ImageBase) {
return STATUS_SUCCESS;
}
static NTSTATUS NTAPI LdrpHandleTlsDataXp(PLDR_DATA_TABLE_ENTRY LdrEntry) {
return STATUS_NOT_SUPPORTED;
}
NTSTATUS NTAPI LdrpHandleTlsData(IN PLDR_DATA_TABLE_ENTRY LdrEntry) {
typedef NTSTATUS(__thiscall* _PTR_WIN8_1)(PLDR_DATA_TABLE_ENTRY LdrEntry);
typedef NTSTATUS(__stdcall* _PTR_WIN)(PLDR_DATA_TABLE_ENTRY LdrEntry);
union _FUNCTION_SET {
_PTR_WIN8_1 Win8_1_OrGreater;
_PTR_WIN Default;
_FUNCTION_SET() {
this->Default = nullptr;
}
operator bool() {
return this->Default != nullptr;
}
};
static _FUNCTION_SET _LdrpHandleTlsData{};
static bool stdcall = false;
if (_LdrpHandleTlsData)
return stdcall ? _LdrpHandleTlsData.Default(LdrEntry) : _LdrpHandleTlsData.Win8_1_OrGreater(LdrEntry);
DWORD Versions[3]{};
LPCVOID Feature = nullptr;
BYTE Size = 0;
WORD OffsetOfFunctionBegin = 0;
RtlGetNtVersionNumbers(Versions, Versions + 1, Versions + 2);
switch (Versions[0]) {
case 10: {
if (Versions[1])return STATUS_NOT_SUPPORTED;
//RS3
if (Versions[2] >= 16299) {
Size = 7;
//19H2
if (Versions[2] >= 18363)Feature = "\x74\x33\x44\x8D\x43\x09";
//RS5
else if (Versions[2] >= 17763) Feature = "\x8b\xc1\x8d\x4d\xbc\x51";
//RS4
else if (Versions[2] >= 17134) Feature = "\x33\xf6\x85\xc0\x79\x03";
//RS3
else Feature = "\x8b\xc1\x8d\x4d\xac\x51";
#ifdef _WIN64
//RS6(19H1)
if (Versions[2] >= 18362) OffsetOfFunctionBegin = 0x46;
//RS4
else if (Versions[2] >= 17134) OffsetOfFunctionBegin = 0x44;
//RS3
else OffsetOfFunctionBegin = 0x43;
#else
//RS6(19H1)
if (Versions[2] >= 18362) OffsetOfFunctionBegin = 0x2E;
//RS5
else if (Versions[2] >= 17763) OffsetOfFunctionBegin = 0x2C;
//RS3,4
else OffsetOfFunctionBegin = 0x18;
#endif
break;
}
//RS2
else if (Versions[2] >= 15063) {
Size = 7;
#ifdef _WIN64
OffsetOfFunctionBegin = 0x43;
Feature = "\x74\x33\x44\x8d\x43\x09";
#else
OffsetOfFunctionBegin = 0x18;
Feature = "\x8b\xc1\x8d\x4d\xbc\x51";
#endif
break;
}
// NO BREAK
}
case 6: {
switch (Versions[1]) {
//8.1
case 3: {
#ifdef _WIN64
Size = 10;
OffsetOfFunctionBegin = 0x43;
Feature = "\x44\x8d\x43\x09\x4c\x8d\x4c\x24\x38";
#else
Size = 8;
OffsetOfFunctionBegin = 0x1B;
Feature = "\x50\x6a\x09\x6a\x01\x8b\xc1";
#endif
break;
}
//8
case 2: {
#ifdef _WIN64
Size = 9;
OffsetOfFunctionBegin = 0x49;
Feature = "\x48\x8b\x79\x30\x45\x8d\x66\x01";
#else
Size = 7;
OffsetOfFunctionBegin = 0xC;
Feature = "\x8b\x45\x08\x89\x45\xa0";
#endif
break;
}
//7
case 1: {
#ifdef _WIN64
Size = 12;
OffsetOfFunctionBegin = 0x27;
Feature = "\x41\xb8\x09\x00\x00\x00\x48\x8d\x44\x24\x38";
#else
Size = 9;
OffsetOfFunctionBegin = 0x14;
Feature = "\x74\x20\x8d\x45\xd4\x50\x6a\x09";
#endif
break;
}
default:return STATUS_NOT_SUPPORTED;
}
break;
}
default: {
_LdrpHandleTlsData.Default = LdrpHandleTlsDataXp;
stdcall = true;
return LdrpHandleTlsDataXp(LdrEntry);
}
}
HMODULE ntdll = GetModuleHandleW(L"ntdll.dll");
PIMAGE_NT_HEADERS headers = RtlImageNtHeader(ntdll);
if (!Feature || !headers)return STATUS_NOT_SUPPORTED;
Size--;
for (size_t i = 0; i < headers->OptionalHeader.SizeOfCode - Size; ++i) {
if (RtlCompareMemory((PBYTE)ntdll + i, Feature, Size) == Size) {
_LdrpHandleTlsData.Default = (_PTR_WIN)((PBYTE)ntdll + i - OffsetOfFunctionBegin);
break;
}
}
if (!_LdrpHandleTlsData)return STATUS_NOT_SUPPORTED;
stdcall = !RtlIsWindowsVersionOrGreater(6, 3, 0);
return stdcall ? _LdrpHandleTlsData.Default(LdrEntry) : _LdrpHandleTlsData.Win8_1_OrGreater(LdrEntry);
}
+7 -2
View File
@@ -393,6 +393,7 @@ NTSTATUS NTAPI NtLoadDllMemory(
/*
NtLoadDllMemoryEx dwFlags
*/
//If this flag is specified, all subsequent flags will be ignored.
//Also, will be incompatible with Win32 API.
#define LOAD_FLAGS_NOT_MAP_DLL 0x10000000
@@ -408,13 +409,16 @@ NTSTATUS NTAPI NtLoadDllMemory(
//If this flag is specified, DllName and DllFullName cannot be nullptr,
// they can be arbitrary strings without having to be correct file names and paths.
//Otherwise, DllName and DllFullName will use random names if they are nullptr.
//For compatibility with GetModuleHandle, DllName and DllFullName should be guaranteed to always end in .dll
//For compatibility with GetModuleHandle, DllName and DllFullName should be guaranteed to always end in ".dll"
#define LOAD_FLAGS_USE_DLL_NAME 0x00000004
//Dont call LdrpHandleTlsData routine if this flag is specified.
#define LOAD_FLAGS_NOT_HANDLE_TLS 0x00000008
NTSTATUS NTAPI NtLoadDllMemoryExW(
OUT HMEMORYMODULE* BaseAddress,
OUT PLDR_DATA_TABLE_ENTRY* LdrEntry OPTIONAL,
OUT PVOID* LdrEntry OPTIONAL,
IN DWORD dwFlags,
IN LPVOID BufferAddress,
IN size_t BufferSize,
@@ -449,3 +453,4 @@ typedef struct _RTL_INVERTED_FUNCTION_TABLE {
NTSTATUS NTAPI RtlInsertInvertedFunctionTable(IN PVOID BaseAddress, IN size_t ImageSize);
NTSTATUS NTAPI RtlRemoveInvertedFunctionTable(IN PVOID ImageBase);
NTSTATUS NTAPI LdrpHandleTlsData(IN PLDR_DATA_TABLE_ENTRY LdrEntry);
+5
View File
@@ -8,6 +8,11 @@ MemoryModulePP, used to load a DLL from memory. MemoryModulePP is compatible wit
**This repository is under development.**
# NewFeatures
- Compatible with Win32 API: LoadStringA/W
- Support for TLS(Thread Local Storage)
- DllMain can receive four types of notifications
# Features
- Compatible with Win32 API (GetModuleHandleA/W/Ex GetModuleFileNameA/W/Ex GetProcAddress)
- Support for C ++ exceptions and SEH
+536 -33
View File
@@ -1,35 +1,538 @@
struct FuncInfoHeader
{
union
{
struct
{
uint8_t isCatch : 1; // 1 if this represents a catch funclet, 0 otherwise
uint8_t isSeparated : 1; // 1 if this function has separated code segments, 0 otherwise
uint8_t BBT : 1; // Flags set by Basic Block Transformations
uint8_t UnwindMap : 1; // Existence of Unwind Map RVA
uint8_t TryBlockMap : 1; // Existence of Try Block Map RVA
uint8_t EHs : 1; // EHs flag set
uint8_t NoExcept : 1; // NoExcept flag set
uint8_t reserved : 1;
};
uint8_t value;
};
};
struct FuncInfo4
{
FuncInfoHeader header;
uint32_t bbtFlags; // flags that may be set by BBT processing
#include <Windows.h>
#include "../MemoryModule/NativeFunctionsInternal.h"
int32_t dispUnwindMap; // Image relative offset of the unwind map
int32_t dispTryBlockMap; // Image relative offset of the handler map
int32_t dispIPtoStateMap; // Image relative offset of the IP to state map
uint32_t dispFrame; // displacement of address of function frame wrt establisher frame, only used for catch funclets
typedef struct _THREAD_TLS_INFORMATION {
ULONG Flags;
union {
PVOID* TlsVector;
PVOID TlsModulePointer;
};
HANDLE ThreadId;
} THREAD_TLS_INFORMATION, * PTHREAD_TLS_INFORMATION;
typedef enum _PROCESS_TLS_INFORMATION_TYPE {
ProcessTlsReplaceIndex,
ProcessTlsReplaceVector,
MaxProcessTlsOperation
} PROCESS_TLS_INFORMATION_TYPE, * PPROCESS_TLS_INFORMATION_TYPE;
typedef struct _PROCESS_TLS_INFORMATION {
ULONG Reserved; // Reserved bitmask
ULONG OperationType;
ULONG ThreadDataCount;
union {
ULONG TlsIndex;
ULONG TlsVectorLength;
};
THREAD_TLS_INFORMATION ThreadData[ANYSIZE_ARRAY];
} PROCESS_TLS_INFORMATION, * PPROCESS_TLS_INFORMATION;
// Need struct name
typedef struct _TLS_VECTOR {
union {
ULONG Length;
HANDLE ThreadId;
};
struct _TLS_VECTOR* PreviousDeferredTlsVector;
PVOID ModuleTlsData[ANYSIZE_ARRAY];
} TLS_VECTOR, * PTLS_VECTOR;
// Need struct name
typedef struct _TLS_RECLAIM_TABLE_ENTRY {
PTLS_VECTOR TlsVector;
RTL_SRWLOCK Lock;
} TLS_RECLAIM_TABLE_ENTRY, * PTLS_RECLAIM_TABLE_ENTRY;
// Need struct name
typedef struct _TLS_ENTRY {
LIST_ENTRY TlsEntryLinks;
IMAGE_TLS_DIRECTORY TlsDirectory;
PLDR_DATA_TABLE_ENTRY ModuleEntry;
} TLS_ENTRY, * PTLS_ENTRY;
//0x10 bytes (sizeof)
typedef struct _RTL_BITMAP {
ULONG SizeOfBitMap; //0x0
ULONG* Buffer; //0x8
}RTL_BITMAP, * PRTL_BITMAP;
VOID RtlClearBit(
PRTL_BITMAP BitMapHeader,
ULONG BitNumber
);
VOID RtlInitializeBitMap(
PRTL_BITMAP BitMapHeader,
PULONG BitMapBuffer,
ULONG SizeOfBitMap
);
ULONG RtlFindClearBitsAndSet(
PRTL_BITMAP BitMapHeader,
ULONG NumberToFind,
ULONG HintIndex
);
VOID RtlClearBits(
PRTL_BITMAP BitMapHeader,
ULONG StartingIndex,
ULONG NumberToClear
);
VOID RtlSetBit(
PRTL_BITMAP BitMapHeader,
ULONG BitNumber
);
BOOLEAN RemoveEntryList(
PLIST_ENTRY Entry
);
VOID NTAPI RtlAcquireSRWLockExclusive(IN OUT PRTL_SRWLOCK SRWLock);
VOID NTAPI RtlReleaseSRWLockExclusive(IN OUT PRTL_SRWLOCK SRWLock);
NTSTATUS NTAPI NtSetInformationProcess(
IN HANDLE ProcessHandle,
IN ULONG ProcessInformationClass,
IN PVOID ProcessInformation,
IN ULONG ProcessInformationLength);
#define ProcessTlsInformation ProcessResourceManagement
PUCHAR NtdllBaseTag = 0;
ULONG LdrpActiveThreadCount = 0;
ULONG LdrpPotentialTlsLeaks = 0;
RTL_BITMAP LdrpTlsBitmap;
LIST_ENTRY LdrpTlsList;
TLS_RECLAIM_TABLE_ENTRY LdrpDelayedTlsReclaimTable[16];
ULONG LdrpStaticTlsBitmapVector[4];
ULONG LdrpActualBitmapSize = 0;
VOID LdrpInit() {
RtlCopyMemory(&LdrpTlsBitmap, NtCurrentPeb()->TlsBitmap, sizeof(RTL_BITMAP));
PROCESS_TLS_INFORMATION pti;
}
VOID LdrpReleaseTlsIndex(ULONG TlsIndex) {
RtlClearBit(&LdrpTlsBitmap, TlsIndex);
}
#define LDRP_BITMAP_INCREMENT (0x27 - sizeof( PVOID ))
NTSTATUS LdrpAcquireTlsIndex(PULONG TlsIndex, PBOOLEAN AllocatedBitmap) {
ULONG Length;
ULONG Index;
PULONG NewBitmapBuffer;
Length = LdrpTlsBitmap.SizeOfBitMap;
if (Length == 0) {
//
// If we're the first caller, then we shall need to be initializing the
// bitmap.
//
// This implies that we don't need to expand as by definition, there
// shall exist space for ourselves at the start of the bitmap now.
//
RtlInitializeBitMap(&LdrpTlsBitmap, LdrpStaticTlsBitmapVector, 4);
LdrpActualBitmapSize = 1;
}
else {
Index = RtlFindClearBitsAndSet(&LdrpTlsBitmap, 1, 0);
//
// If we found space in the existing bitmap then there is no reason to
// expand buffers, so we'll just return with the existing data.
//
if (Index != 0xFFFFFFFF) {
*TlsIndex = Index;
*AllocatedBitmap = FALSE;
return STATUS_SUCCESS;
}
//
// Check if we need to grow the bitmap itself or if the bitmap still
// has space.
//
if (((LdrpTlsBitmap.SizeOfBitMap + LDRP_BITMAP_INCREMENT) >> 5) > LdrpActualBitmapSize) {
//
// We'll need to grow it. Let's go do so now.
//
//
// BUG: We set the new size before checking the allocation. If we
// fail, then we leave the TLS variables in an inconsistant state.
//
LdrpActualBitmapSize = (Length + LDRP_BITMAP_INCREMENT) >> 5;
NewBitmapBuffer = (PULONG)RtlAllocateHeap(GetProcessHeap(), (ULONG_PTR)((PUCHAR)NtdllBaseTag + 0x000C0000), LdrpActualBitmapSize);
if (!NewBitmapBuffer) return STATUS_NO_MEMORY;
//
// Copy the contents of the previous buffer into the new one.
//
RtlCopyMemory(NewBitmapBuffer, LdrpTlsBitmap.Buffer, Length + 7);
//
// Free the old buffer if it wasn't the initial static buffer.
//
if (LdrpTlsBitmap.Buffer != LdrpStaticTlsBitmapVector) {
RtlFreeHeap(GetProcessHeap(), 0, LdrpTlsBitmap.Buffer);
}
//
// Reinitialize the bitmap as we've changed the buffer pointer.
//
RtlInitializeBitMap(&LdrpTlsBitmap, NewBitmapBuffer, Length + 4);
}
else {
LdrpTlsBitmap.SizeOfBitMap += 4;
}
}
RtlClearBits(&LdrpTlsBitmap, Length + 1, 3);
RtlSetBit(&LdrpTlsBitmap, Length);
*TlsIndex = Index;
*AllocatedBitmap = TRUE;
return STATUS_SUCCESS;
}
NTSTATUS LdrpAllocateTlsEntry(PIMAGE_TLS_DIRECTORY TlsDirectory, PLDR_DATA_TABLE_ENTRY ModuleEntry, PULONG TlsIndex, PBOOLEAN AllocatedBitmap, PTLS_ENTRY* TlsEntry) {
PTLS_ENTRY Entry = nullptr;
NTSTATUS Status;
__try {
Entry = (PTLS_ENTRY)RtlAllocateHeap(GetProcessHeap(), (ULONG_PTR)((PUCHAR)NtdllBaseTag + 0x000C0000), sizeof(TLS_ENTRY));
if (!Entry) return STATUS_NO_MEMORY;
Status = STATUS_SUCCESS;
RtlCopyMemory(&Entry->TlsDirectory, TlsDirectory, sizeof(IMAGE_TLS_DIRECTORY));
}
__except (EXCEPTION_EXECUTE_HANDLER) {
//
// Also print string and complain.
//
Status = GetExceptionCode();
}
if (!NT_SUCCESS(Status)) {
RtlFreeHeap(GetProcessHeap(), 0, Entry);
return Status;
}
//
// Validate that the TLS directory entry is sane.
//
if (Entry->TlsDirectory.StartAddressOfRawData < Entry->TlsDirectory.EndAddressOfRawData) {
RtlFreeHeap(GetProcessHeap(), 0, Entry);
return STATUS_INVALID_IMAGE_FORMAT;
}
Entry->ModuleEntry = ModuleEntry;
//
// Insert the entry into our list.
//
InsertTailList(&LdrpTlsList, &Entry->TlsEntryLinks);
if (AllocatedBitmap) {
Status = LdrpAcquireTlsIndex(TlsIndex, AllocatedBitmap);
if (!NT_SUCCESS(Status)) {
//
// BUG: We don't remove the entry from LdrpTlsList
//
RtlFreeHeap(GetProcessHeap(), 0, Entry);
return Status;
}
}
else {
*TlsIndex += 1;
}
//
// We reuse the 'Characteristics' field for the real TLS index.
//
Entry->TlsDirectory.Characteristics = *TlsIndex;
__try {
*(PULONG)Entry->TlsDirectory.AddressOfIndex = *TlsIndex;
}
__except (EXCEPTION_EXECUTE_HANDLER) {
Status = GetExceptionCode();
}
if (!NT_SUCCESS(Status)) {
if (AllocatedBitmap) {
LdrpReleaseTlsIndex(*TlsIndex);
if (*AllocatedBitmap) LdrpTlsBitmap.SizeOfBitMap -= 4;
}
//
// BUG: We don't remove the entry from LdrpTlsList
//
RtlFreeHeap(GetProcessHeap(), 0, Entry);
return Status;
}
if (TlsEntry) *TlsEntry = Entry;
return STATUS_SUCCESS;
}
PTLS_ENTRY __fastcall LdrpFindTlsEntry(PLDR_DATA_TABLE_ENTRY ModuleEntry) {
PTLS_ENTRY TlsEntry;
PLIST_ENTRY ListHead;
ListHead = &LdrpTlsList;
for (TlsEntry = CONTAINING_RECORD(LdrpTlsList.Flink, TLS_ENTRY, TlsEntryLinks);
&TlsEntry->TlsEntryLinks != ListHead;
TlsEntry = CONTAINING_RECORD(TlsEntry->TlsEntryLinks.Flink, TLS_ENTRY, TlsEntryLinks)) {
if (TlsEntry->ModuleEntry == ModuleEntry) return TlsEntry;
}
return 0;
}
NTSTATUS LdrpReleaseTlsEntry(PLDR_DATA_TABLE_ENTRY ModuleEntry) {
PTLS_ENTRY TlsEntry;
//
// Find the corresponding TLS_ENTRY for this module entry.
//
TlsEntry = LdrpFindTlsEntry(ModuleEntry);
if (!TlsEntry) return STATUS_NOT_FOUND;
//
// Remove it from the global list of outstanding TLS entries.
//
RemoveEntryList(&TlsEntry->TlsEntryLinks);
//
// Deallocate the TLS index.
//
LdrpReleaseTlsIndex(TlsEntry->TlsDirectory.Characteristics);
//
// Deallocate the TLS_ENTRY object itself.
//
RtlFreeHeap(GetProcessHeap(), 0, TlsEntry);
//
// We're done.
//
return STATUS_SUCCESS;
}
PVOID* __fastcall LdrpGetNewTlsVector(ULONG TlsBitmapLength) {
PTLS_VECTOR TlsVector;
TlsVector = (PTLS_VECTOR)RtlAllocateHeap(GetProcessHeap(), (ULONG_PTR)((PUCHAR)NtdllBaseTag + 0x000C0000),
sizeof(TLS_VECTOR) + (sizeof(PVOID) * TlsBitmapLength) - sizeof(PVOID));
if (!TlsVector) return 0;
TlsVector->Length = TlsBitmapLength;
RtlZeroMemory(TlsVector->ModuleTlsData, TlsBitmapLength * sizeof(PVOID));
return TlsVector->ModuleTlsData;
}
VOID LdrpQueueDeferredTlsData(PVOID TlsVector, PVOID ThreadId) {
PTLS_VECTOR RealTlsVector;
PTLS_RECLAIM_TABLE_ENTRY ReclaimEntry;
RealTlsVector = CONTAINING_RECORD(TlsVector, TLS_VECTOR, ModuleTlsData);
RealTlsVector->ThreadId = ThreadId;
ReclaimEntry = &LdrpDelayedTlsReclaimTable[((ULONG_PTR)(ThreadId) >> 2) & 0xF];
RtlAcquireSRWLockExclusive(&ReclaimEntry->Lock);
RealTlsVector->PreviousDeferredTlsVector = ReclaimEntry->TlsVector;
ReclaimEntry->TlsVector = RealTlsVector;
RtlReleaseSRWLockExclusive(&ReclaimEntry->Lock);
}
#define SIZEOF_TLS_INFO(_ThreadCount_) (_ThreadCount_==0)?sizeof(PROCESS_TLS_INFORMATION)-sizeof(THREAD_TLS_INFORMATION):(_ThreadCount_-1)*sizeof(THREAD_TLS_INFORMATION)+sizeof(PROCESS_TLS_INFORMATION)
NTSTATUS LdrpHandleTlsData(PLDR_DATA_TABLE_ENTRY ModuleEntry) {
PIMAGE_TLS_DIRECTORY TlsDirectory;
ULONG DirectorySize;
ULONG TlsIndex;
HANDLE Heap;
PPROCESS_TLS_INFORMATION TlsInfo;
PROCESS_TLS_INFORMATION OneThreadTlsInfo;
NTSTATUS Status;
BOOLEAN AllocatedBitmap;
PTLS_ENTRY TlsEntry;
ULONG TlsBitmapLength;
SIZE_T TlsRawDataLength;
ULONG ThreadIndex;
PVOID TlsData = nullptr;
PVOID* TlsVector;
PTHREAD_TLS_INFORMATION ThreadTlsData;
ULONG ThreadsCleanedUp;
if (LdrpActiveThreadCount == 0) return STATUS_SUCCESS;
TlsDirectory = (PIMAGE_TLS_DIRECTORY)RtlImageDirectoryEntryToData(ModuleEntry->DllBase, TRUE, IMAGE_DIRECTORY_ENTRY_TLS, &DirectorySize);
if (!TlsDirectory) return STATUS_SUCCESS;
Heap = NtCurrentPeb()->ProcessHeap;
TlsInfo = LdrpActiveThreadCount == 1 ? &OneThreadTlsInfo :
(decltype(TlsInfo))RtlAllocateHeap(Heap, (ULONG)NtdllBaseTag + 0x000C0000, SIZEOF_TLS_INFO(LdrpActiveThreadCount));
if (!TlsInfo) return STATUS_NO_MEMORY;
do {
TlsBitmapLength = LdrpTlsBitmap.SizeOfBitMap;
Status = LdrpAllocateTlsEntry(TlsDirectory, ModuleEntry, &TlsIndex, &AllocatedBitmap, &TlsEntry);
if (!NT_SUCCESS(Status)) break;
TlsInfo->ThreadDataCount = LdrpActiveThreadCount;
if (AllocatedBitmap) {
TlsInfo->OperationType = ProcessTlsReplaceVector;
TlsInfo->TlsVectorLength = TlsBitmapLength;
TlsBitmapLength = LdrpTlsBitmap.SizeOfBitMap;
}
else {
TlsInfo->OperationType = ProcessTlsReplaceIndex;
TlsInfo->TlsIndex = TlsIndex;
}
Status = STATUS_SUCCESS;
ThreadsCleanedUp = 0;
//
// Calculate the size of the raw TLS data for this module.
//
TlsRawDataLength = TlsEntry->TlsDirectory.EndAddressOfRawData - TlsEntry->TlsDirectory.StartAddressOfRawData;
//
// Prepare data for each running thread.
//
for (ThreadIndex = 0; ThreadIndex < TlsInfo->ThreadDataCount; ++ThreadIndex) {
TlsData = RtlAllocateHeap(Heap, (ULONG_PTR)((PUCHAR)NtdllBaseTag + 0x000C0000), TlsRawDataLength);
if (!TlsData) {
Status = STATUS_NO_MEMORY;
break;
}
__try {
RtlCopyMemory(TlsData, (PVOID)TlsEntry->TlsDirectory.StartAddressOfRawData, TlsRawDataLength);
}
__except (EXCEPTION_EXECUTE_HANDLER) {
Status = GetExceptionCode();
}
if (!NT_SUCCESS(Status)) {
RtlFreeHeap(Heap, 0, TlsData);
break;
}
if (AllocatedBitmap) {
TlsVector = LdrpGetNewTlsVector(TlsBitmapLength);
if (!TlsVector) {
RtlFreeHeap(Heap, 0, TlsData);
break;
}
TlsVector[TlsIndex] = TlsData;
TlsInfo->ThreadData[ThreadIndex].TlsVector = TlsVector;
}
else {
TlsInfo->ThreadData[ThreadIndex].TlsModulePointer = TlsData;
}
TlsInfo->ThreadData[ThreadIndex].Flags = 0;
}
//
// This is awkward; all the 'break' above really are either goto or
// __leave, but we aren't using those. This is really supposed to
// just happen on normal for loop exit.
//
if (ThreadIndex == TlsInfo->ThreadDataCount) {
TlsInfo->Reserved = 0;
Status = NtSetInformationProcess(GetCurrentProcess(), ProcessTlsInformation, TlsInfo,
TlsInfo->ThreadDataCount * sizeof(THREAD_TLS_INFORMATION) + sizeof(PROCESS_TLS_INFORMATION) - sizeof(THREAD_TLS_INFORMATION));
}
//
// Let's handle each thread that we replaced, as the
// ProcessTlsInformation call fills our buffer with the old data
// after performing a swap.
//
for (ThreadTlsData = &TlsInfo->ThreadData[ThreadIndex]; ThreadIndex > 0;) {
ThreadIndex -= 1;
ThreadTlsData -= 1;
if (ThreadTlsData->Flags & 0x2) {
if (!ThreadTlsData->TlsVector) continue;
if (!AllocatedBitmap) {
RtlFreeHeap(Heap, 0, ThreadTlsData->TlsVector);
continue;
}
else {
LdrpQueueDeferredTlsData(ThreadTlsData->TlsVector, ThreadTlsData->ThreadId);
continue;
}
}
else {
if (ThreadTlsData->Flags & 0x1) {
++LdrpPotentialTlsLeaks;
continue;
}
else {
++ThreadsCleanedUp;
if (AllocatedBitmap) {
TlsData = ThreadTlsData->TlsVector[TlsIndex];
RtlFreeHeap(Heap, 0, CONTAINING_RECORD(ThreadTlsData->TlsVector, TLS_VECTOR, ModuleTlsData));
}
RtlFreeHeap(Heap, 0, TlsData);
continue;
}
}
}
if (!NT_SUCCESS(Status)) {
LdrpReleaseTlsEntry(ModuleEntry);
if (AllocatedBitmap) LdrpTlsBitmap.SizeOfBitMap -= 4;
}
else if (ThreadsCleanedUp > 0) {
LdrpActiveThreadCount -= ThreadsCleanedUp;
}
} while (0);
if (TlsInfo != &OneThreadTlsInfo) RtlFreeHeap(Heap, 0, TlsInfo);
if (!NT_SUCCESS(Status)) return Status;
ModuleEntry->TlsIndex = 0xFFFF;
return STATUS_SUCCESS;
}
//struct UNKNOWN {
// PVOID unknown1; //+0x0
// PVOID unknown2; //+0x8
// PVOID unknown3; //+0x10
// struct {
// DWORD dwFlags; //+0x14
// DWORD unknown4; //+0x18
// };
// PWSTR DllName; //+0x20
// PVOID unknown[11];
//};
//
////#include "../MemoryModule/Native.h"
//
////size = 0xC0 + DllName->Length + sizeof(wchar_t)
//typedef struct _ALLOCATE_ENTRY_PARAMETER {
// UNICODE_STRING DllName; //+0x0
// UNKNOWN* unknown_structure; //+0x10
// PVOID reserved1; //+0x18
// struct {
// DWORD ProcessStatus; //+0x20
// DWORD reserved2; //+0x24
// };
// PVOID reserved3; //+0x28
// PVOID reserved4; //+0x30
// PVOID LdrEntry; //+0x38
// PVOID reserved[16]; //+0x40
// BYTE UnicodeStringBuffer[1]; //+0xC0
//}ALLOCATE_ENTRY_PARAMETER, * PALLOCATE_ENTRY_PARAMETER;
};
struct EHRegistrationNode {
/* void * stackPtr */ // Stack ptr at entry to try (below address point)
EHRegistrationNode* pNext; // Next node in the chain
void* frameHandler; // The handler function for this frame
int state; // The current state of this function
};
+96 -22
View File
@@ -1,10 +1,16 @@
#include "../MemoryModule/NativeFunctionsInternal.h"
//#include "../MemoryModule/LoadDllMemoryApi.h"
#ifndef NT_SUCCESS
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
#endif
#include <cstdio>
#pragma warning(disable:4996)
int main() {
//GetProcAddress(LoadLibraryA("a.dll"), "thread")();
LPVOID buffer;
size_t size;
FILE* f = fopen("d.dll", "rb");
FILE* f = fopen("a.dll", "rb");
if (!f)return 0;
_fseeki64(f, 0, SEEK_END);
if (!(size = _ftelli64(f))) {
@@ -23,37 +29,105 @@ int main() {
_exception exception = nullptr;
if (!NT_SUCCESS(NtLoadDllMemoryExW(&m1, nullptr, 0, buffer, size, L"kernel64", nullptr))) goto end;
if (!NT_SUCCESS(NtLoadDllMemoryExW(&_m1, nullptr, 0, buffer, size, L"kernel64.dll", nullptr))) goto end;
if (!NT_SUCCESS(NtLoadDllMemoryExW(&m2, nullptr, 0, buffer, size, L"kernel128.dll", L"\\?\\kernel512.dll"))) goto end;
//if (!NT_SUCCESS(NtLoadDllMemoryExW(&_m1, nullptr, 0, buffer, size, L"kernel64.dll", nullptr))) goto end;
//if (!NT_SUCCESS(NtLoadDllMemoryExW(&m2, nullptr, 0, buffer, size, L"kernel128.dll", L"\\?\\kernel512.dll"))) goto end;
char t[100];
LoadStringA((HINSTANCE)m1, 101, t, 100);
printf("%s\n", t);
hModule = GetModuleHandleA("kernel64.dll");
GetModuleFileNameA(hModule, name, MAX_PATH);
if (hModule)test = GetProcAddress(hModule, "test");
if (hModule)test = GetProcAddress(hModule, "thread");
printf("m1:\n\tHMEMORYMODULE\t= 0x%p\n\tHMODULE\t\t= 0x%p\n\tModuleFileName\t= %s\n\ttest\t\t= 0x%p\n\n", m1, hModule, name, test);
if (test)test();
if (test) test();
GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (LPCSTR)test, &hModule);
GetModuleFileNameA(hModule, name, MAX_PATH);
test = GetProcAddress(hModule, "test");
printf("_m1:\n\tHMEMORYMODULE\t= 0x%p\n\tHMODULE\t\t= 0x%p\n\tModuleFileName\t= %s\n\ttest\t\t= 0x%p\n\n", _m1, hModule, name, test);
if (test)test();
//GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (LPCSTR)test, &hModule);
//GetModuleFileNameA(hModule, name, MAX_PATH);
//test = GetProcAddress(hModule, "test");
//printf("_m1:\n\tHMEMORYMODULE\t= 0x%p\n\tHMODULE\t\t= 0x%p\n\tModuleFileName\t= %s\n\ttest\t\t= 0x%p\n\n", _m1, hModule, name, test);
//if (test)test();
hModule = GetModuleHandleA("kernel128");
GetModuleFileNameA(hModule, name, MAX_PATH);
if (hModule)exception = (_exception)GetProcAddress(hModule, "exception");
printf("m2:\n\tHMEMORYMODULE\t= 0x%p\n\tHMODULE\t\t= 0x%p\n\tModuleFileName\t= %s\n\ttest\t\t= 0x%p\n\n", m2, hModule, name, test);
if (exception) {
exception(0);
exception(1);
exception(2);
exception(3);
}
//hModule = GetModuleHandleA("kernel128");
//GetModuleFileNameA(hModule, name, MAX_PATH);
//if (hModule)exception = (_exception)GetProcAddress(hModule, "exception");
//printf("m2:\n\tHMEMORYMODULE\t= 0x%p\n\tHMODULE\t\t= 0x%p\n\tModuleFileName\t= %s\n\ttest\t\t= 0x%p\n\n", m2, hModule, name, test);
//if (exception) {
// exception(0);
// exception(1);
// exception(2);
// exception(3);
//}
end:
delete[]buffer;
if (m1)NtUnloadDllMemory(m1);
if (_m1)NtUnloadDllMemory(_m1);
if (m2)NtUnloadDllMemory(m2);
//if (_m1)NtUnloadDllMemory(_m1);
//if (m2)NtUnloadDllMemory(m2);
return 0;
}
//#include <cstdio>
//#include "../MemoryModule/NativeFunctionsInternal.h"
//
//bool c;
//static thread_local int x = -1;
//
//DWORD WINAPI Thread(PVOID) {
// printf("[1] x = %d\n", x);
// x = 0;
// c = true;
// while (c)Sleep(100);
// return x;
//}
//
//int main() {
// x = 1;
// c = false;
// HANDLE hThread = CreateThread(nullptr, 0, Thread, nullptr, 0, nullptr);
// DWORD ex = 0;
// if (hThread) {
// while (!c)Sleep(100);
// printf("[0] x = %d\n", x);
// c = false;
// WaitForSingleObject(hThread, 0xffffffff);
// GetExitCodeThread(hThread, &ex);
// CloseHandle(hThread);
// printf("[0] Exit = %d\n", ex);
// }
//
// PLIST_ENTRY entry = &NtCurrentPeb()->Ldr->InLoadOrderModuleList;
// PLDR_DATA_TABLE_ENTRY_WIN7 data = nullptr;
//
// while (entry != entry->Flink) {
// entry = entry->Flink;
// data = CONTAINING_RECORD(entry, LDR_DATA_TABLE_ENTRY_WIN7, InLoadOrderLinks);
// }
//
// return 0;
//}
//#include "../MemoryModule/Native.h"
//#include <cstdio>
//
//static thread_local int x = 0xffccffdd;
//
//DWORD WINAPI Thread(PVOID) {
// printf("[1] ThreadLocalStoragePointer = %p\n", NtCurrentTeb()->ThreadLocalStoragePointer);
// return x == 0xffccffdd ? 0 : 1;
//}
//
//int main() {
// x = 2;
// printf("[0] ThreadLocalStoragePointer = %p\n", NtCurrentTeb()->ThreadLocalStoragePointer);
// HANDLE hThread = CreateThread(nullptr, 0, Thread, nullptr, 0, nullptr);
// DWORD ret = -1;
// if (hThread) {
// WaitForSingleObject(hThread, 0xffffffff);
// GetExitCodeThread(hThread, &ret);
// CloseHandle(hThread);
// return ret;
// }
// return -1;
//}