on x86-based windos possible (tested on xp and win 8.1 x86) set several descriptors in LDT table and use this. this can be done via NtSetInformationProcess
with ProcessLdtInformation
(undocumented) or, if we need set only 1 or 2 selectors - more easy use undocumented api:
EXTERN_C
__declspec(dllimport)
NTSTATUS
NTAPI
NtSetLdtEntries
(
__in_opt ULONG Selector1,
__in SEGMENT_ENTRY LdtEntry1,
__in_opt ULONG Selector2,
__in SEGMENT_ENTRY LdtEntry2
);
so we need allocate 1 or more SEGMENT_ENTRY
(or LDT_ENTRY
- declared in winnt.h), allocate memory for segment, and call api. I did not pay much attention for 16 bit code and fill actual descriptors, check only memory fill via LDT selector (assigned to ES) and then read it via "plain" selector.
typedef struct SEGMENT_ENTRY
{
ULONG LimitLow : 16;
ULONG BaseLow : 16;
ULONG BaseMid : 8;
ULONG Type : 4;
ULONG IsGegment : 1;// = 1
ULONG DPL : 2;
ULONG P : 1;// Present
ULONG LimitHi : 4;
ULONG AVL : 1;// Available For software use
ULONG L : 1;// Long-mode segment
ULONG D : 1;// Default operand size
ULONG G : 1;// Granularity
ULONG BaseHi : 8;
}*PSEGMENT_ENTRY;
typedef struct PROCESS_LDT_INFORMATION
{
ULONG StartSelector;
ULONG Length;
SEGMENT_ENTRY LdtEntries[];
} *PPROCESS_LDT_INFORMATION;
EXTERN_C
__declspec(dllimport)
NTSTATUS
NTAPI
NtSetLdtEntries
(
__in_opt ULONG Selector1,
IN SEGMENT_ENTRY LdtEntry1,
__in_opt ULONG Selector2,
IN SEGMENT_ENTRY LdtEntry2
);
NTSTATUS TestLdt()
{
PVOID BaseAddress = 0;
SIZE_T RegionSize = 0x100000;//1mb
NTSTATUS status = NtAllocateVirtualMemory(NtCurrentProcess(), &BaseAddress, 0, &RegionSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (0 <= status)
{
#if 1
SEGMENT_ENTRY LdtEntry = {};
LdtEntry.LimitLow = 0xffff;
LdtEntry.BaseLow = ((ULONG_PTR)BaseAddress) & 0xFFFF;
LdtEntry.BaseMid = ((ULONG_PTR)BaseAddress >> 16) & 0xff;
LdtEntry.BaseHi = ((ULONG_PTR)BaseAddress >> 24) & 0xff;
LdtEntry.P = 1;
LdtEntry.DPL = 3;
LdtEntry.IsGegment = 1;
LdtEntry.Type = 2;//ldt
status = NtSetLdtEntries(8, LdtEntry, 0, LdtEntry);
#else
const ULONG cb = sizeof(PROCESS_LDT_INFORMATION) + 1 * sizeof(LDT_ENTRY);
PPROCESS_LDT_INFORMATION LdtInfo = (PPROCESS_LDT_INFORMATION)alloca(cb);
LdtInfo->Length = 1 * sizeof(LDT_ENTRY);
LdtInfo->StartSelector = 8;
SEGMENT_ENTRY* LdtEntry = LdtInfo->LdtEntries;
LdtEntry->LimitLow = 0xffff;
LdtEntry->BaseLow = ((ULONG_PTR)BaseAddress) & 0xFFFF;
LdtEntry->BaseMid = ((ULONG_PTR)BaseAddress >> 16) & 0xff;
LdtEntry->BaseHi = ((ULONG_PTR)BaseAddress >> 24) & 0xff;
LdtEntry->L = 0;
LdtEntry->D = 0;
LdtEntry->G = 0;
LdtEntry->AVL = 0;
LdtEntry->P = 1;
LdtEntry->DPL = 3;
LdtEntry->IsGegment = 1;
LdtEntry->Type = 2;//ldt
status = NtSetInformationProcess(NtCurrentProcess(), ProcessLdtInformation, LdtInfo, cb);
#endif
if (0 <= status)
{
DbgPrint("%s\n", BaseAddress); // print empty string
#ifdef _X86_
__asm {
push edi
mov ax,0xf
mov dx,es
mov es,ax
mov ecx,32
mov al,0x33
xor edi,edi
rep stosb
mov es,dx
pop edi
}
#endif
DbgPrint("%s\n", BaseAddress);// print 33333333...
}
NtFreeVirtualMemory(NtCurrentProcess(), &BaseAddress, &RegionSize, MEM_RELEASE);
}
return status;
}
however This is valid only on x86-based windows systems.
if you call this on any x64 windows you got error STATUS_NOT_IMPLEMENTED
. here windows not support LDT at all. and this can not be changed (even by modify system files. ?!)
more info - Local Descriptor Table on x64