如何在64位Windows上准确修改本地描述符表?

如何在64位Windows上准确修改本地描述符表?,windows,x86-64,16-bit,mmu,Windows,X86 64,16 Bit,Mmu,其目的是通过modify_ldt()系统调用为16位寻址启用16位段,就像在64位Linux内核上一样 我无法找到Cygwin是否提供了一个包装器,我几乎不知道它是关于NtSetLdtEntries之类的东西: typedef struct { ULONG Start; ULONG Length; LDT_ENTRY LdtEntries[1]; } PROCESS_LDT_INFORMATION, *PPROCESS_LDT_INFORMATION; 请注意,这与vm

其目的是通过
modify_ldt()
系统调用为16位寻址启用16位段,就像在64位Linux内核上一样

我无法找到Cygwin是否提供了一个包装器,我几乎不知道它是关于
NtSetLdtEntries
之类的东西:

typedef struct
{
    ULONG Start;
    ULONG Length;
    LDT_ENTRY LdtEntries[1];
} PROCESS_LDT_INFORMATION, *PPROCESS_LDT_INFORMATION;
请注意,这与vm86模式无关(这是Microsoft在32位系统上使用的不同方法)。如上所述,这种方式在Linux上用于以保护模式运行16位代码,而无需任何仿真。有关更多信息,请参阅

当然,如果默认情况下不支持,则可以修改系统文件。

在基于x86的windos上(在xp和win 8.1 x86上测试),可以在LDT表中设置几个描述符并使用它。这可以通过
NtSetInformationProcess
ProcessLdtInformation
(未记录)来完成,或者,如果我们只需要设置1或2个选择器-更易于使用的未记录api:

EXTERN_C
__declspec(dllimport)
NTSTATUS
NTAPI
NtSetLdtEntries
(
 __in_opt ULONG Selector1,
 __in SEGMENT_ENTRY LdtEntry1,
 __in_opt ULONG Selector2,
 __in SEGMENT_ENTRY LdtEntry2
 );
因此,我们需要分配1个或多个
段\ u条目(或在winnt.h中声明),为段分配内存,并调用api。我没有太注意16位代码和填充实际描述符,只通过LDT选择器(分配给ES)检查内存填充,然后通过“普通”选择器读取

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;
}
typedef结构段\u条目
{
乌龙有限公司:16家;;
乌龙-巴塞洛:16;
乌龙巴塞米德:8;
乌龙型4例;
ULONG IsGegment:1;//=1
乌龙DPL:2;
ULONG P:1;//当前
乌龙利米提:4;
ULONG AVL:1;//可供软件使用
ULONG L:1;//长模式段
ULONG D:1;//默认操作数大小
ULONG G:1;//粒度
乌龙巴塞:8;
}*分段录入;
类型定义结构进程\u LDT\u信息
{
乌隆星选仪;
乌龙长度;
段/分录第十项[];
}*程序及资料;
外部
__declspec(德林波特)
NTSTATUS
恩塔皮
NtSetLdtEntries
(
__在_optulong选择器1中,
在段_条目LdtEntry1中,
__在_optulong选择器2中,
在段_条目LdtEntry2中
);
NTSTATUS TestLdt()
{
PVOID基址=0;
大小\u T区域大小=0x100000;//1mb
NTSTATUS status=NtAllocateVirtualMemory(NtCurrentProcess(),&BaseAddress,0,&RegionSize,MEM\u COMMIT,PAGE\u EXECUTE\u READWRITE);
如果(0>16)&0xff;
LdtEntry.BaseHi=((ULONG_PTR)BaseAddress>>24)和0xff;
Ldentry.P=1;
LdtEntry.DPL=3;
LdtEntry.IsGegment=1;
LdtEntry.Type=2;//ldt
状态=ntsetldtentry(8,LdtEntry,0,LdtEntry);
#否则
const ULONG cb=sizeof(过程LDT信息)+1*sizeof(LDT条目);
p进程\u LDT\u信息LdtInfo=(进程\u LDT\u信息)alloca(cb);
LdtInfo->Length=1*sizeof(LDT\u条目);
LdtInfo->StartSelector=8;
分段输入*LdtEntry=LdtInfo->LdtEntry;
LdtEntry->LimitLow=0xffff;
LdtEntry->BaseLow=((ULONG_PTR)BaseAddress)&0xFFFF;
LdtEntry->BaseMid=((ULONG_PTR)BaseAddress>>16)和0xff;
LdtEntry->BaseHi=((ULONG_PTR)BaseAddress>>24)和0xff;
Ldentry->L=0;
LdtEntry->D=0;
LdtEntry->G=0;
LdtEntry->AVL=0;
LdtEntry->P=1;
LdtEntry->DPL=3;
LdtEntry->IsGegment=1;
LdtEntry->Type=2;//ldt
状态=NtSetInformationProcess(NtCurrentProcess(),ProcessLdtInformation,LdtInfo,cb);
#恩迪夫

如果(0@RbMm)为什么 ? 请注意,我不是说Vm86。目标是16位寻址而不是实模式。我描述的方式适用于Linux64位。@RbMm Longmode仍然使用CS确定代码段,使用新的L位在32位和64位子模式之间切换,并且仍然可以使用Sz位.t启用很少使用的16位模式任何windows x64都不支持这一点。在xp上的x86上-这正是可以实现的,是后者-不检查。64位xp-不存在。存在64位win 2003:)再看看-@RbMm:Harold是对的,这在CPU级别上是绝对可能的。长模式内核可以运行16位用户空间。它不是vm86,它就像32位兼容模式(在64位内核下),但默认操作数大小和地址大小为16位。就像32位内核下的16位代码描述符一样。这不是实模式,因此只能通过使用段选择器索引到LDT/GDT来设置段基。但是,是的,我非常确定它们可以是非零的,就像在compat模式下一样。@RbMm:是的,我知道你已经说过x64 Windows doesn不支持这样做。我想你或其他未来的读者可能有兴趣知道x86-64可以做什么,而Windows可以做什么。哦,我只是读了更多的评论,我看到OP还没有理解这一点。对不起,我不是想堆积和添加那些声称这应该是可能的评论,因为硬件可以做到。显然是的您还需要操作系统的支持,这样才能安全地进行操作ᴍᴍᴜ 不是Windows。所以它是受支持的。可能不受支持的是从userland修改它。@user2284570,不是这是x64 Windows不支持的,无论是从用户模式还是内核模式。在GDT中开始时必须是LDT的选择器,但在这里没有这样的选择器,并且您不能修改GDT本身。它需要由内核代码设置。支持相同的设置就像ᴘᴀᴇ 32位:包括驱动程序在内的第三方代码无法访问它。@user2284570-同样,这是cpu支持的,但没有api(从用户或内核模式)在windows中执行此操作。如果您自己尝试执行此操作,将与op系统冲突。因此,不会发生任何事件或系统崩溃。例如,cpu也支持关闭分页。但您不能在windows中执行此操作windows@user2284570-但问题是关于windows,而不是reactOS。您不能在x64 windos中执行此操作,不是因为这不是