winapi – 如何在64位Windows程序集中使用“GS:”(例如,移植TLS代码)

前端之家收集整理的这篇文章主要介绍了winapi – 如何在64位Windows程序集中使用“GS:”(例如,移植TLS代码)前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
64位 @L_502_0@(目前为XP-64),用户空间程序如何配置“GS:”?
(通过配置,在任意64位线性地址设置GS:0).

我试图将一个“JIT”环境移植到最初为Win32开发的X86-64中.

一个不幸的设计方面是,相同的代码需要在多个用户空间线程(例如,“光纤”)上运行. Win32版本的代码使用GS选择器,并生成正确的前缀来访问本地数据 – “mov eax,GS:[offset]”指向当前任务的正确数据.来自Win32版本的代码会将值加载到GS中,如果只有一个值可以工作.

到目前为止,我已经能够发现64位窗口不支持LDT,所以在Win32下使用的方法将不起作用.然而,X86-64指令集包括“SWAPGS”,以及一种在不使用传统分段的情况下加载GS的方法,但这仅适用于内核空间.

根据X64手册,即使Win64允许访问描述符 – 它没有 – 没有办法设置段32的高32位.设置它们的唯一方法是通过GS_BASE_MSR(和相应的FS_BASE_MSR – 其他段基准在64位模式下被忽略). WRMSR指令是Ring0,所以我不能直接使用它.

我希望有一个Zw *功能,可以让我改变用户空间中的“GS:”或Windows API的其他一些黑暗角落.我相信Windows仍然使用FS:为自己的TLS,所以一些机制必须可用?

此示例代码说明了该问题.我提前道歉使用字节代码 – VS将不会做64位编译的内联汇编,并且我试图将它作为一个文件保持为说明目的.

该程序在XP-32上显示“PASS”,XP-x64上不显示“PASS”.

#include <windows.h>
#include <string.h>
#include <stdio.h>


unsigned char GetDS32[] = 
            {0x8C,0xD8,// mov eax,ds
             0xC3};         // ret

unsigned char SetGS32[] =
            {0x8E,0x6C,0x24,0x04,// mov gs,ss:[sp+4] 
             0xC3 };                // ret

unsigned char UseGS32[] = 
           { 0x8B,0x44,ss:[sp+4] 
             0x65,0x8B,0x00,gs:[eax] 
             0xc3 };                // ret

unsigned char SetGS64[] =
            {0x8E,0xe9,rcx
             0xC3 };                // ret

unsigned char UseGS64[] =       
           { 0x65,0x01,gs:[rcx]
             0xc3 };

typedef WORD(*fcnGetDS)(void);
typedef void(*fcnSetGS)(WORD);
typedef DWORD(*fcnUseGS)(LPVOID);
int (*NtSetLdtEntries)(DWORD,DWORD,DWORD);

int main( void )
{
    SYSTEM_INFO si;
    GetSystemInfo(&si);
    LPVOID p = VirtualAlloc(NULL,1024,MEM_COMMIT|MEM_TOP_DOWN,PAGE_EXECUTE_READWRITE);
    fcnGetDS GetDS = (fcnGetDS)((LPBYTE)p+16);
    fcnUseGS UseGS = (fcnUseGS)((LPBYTE)p+32);
    fcnSetGS SetGS = (fcnSetGS)((LPBYTE)p+48);
    *(DWORD *)p = 0x12345678;

    if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) 
    {
        memcpy( GetDS,&GetDS32,sizeof(GetDS32));
        memcpy( UseGS,&UseGS64,sizeof(UseGS64));
        memcpy( SetGS,&SetGS64,sizeof(SetGS64));
    }
    else
    {
        memcpy( GetDS,&UseGS32,sizeof(UseGS32));
        memcpy( SetGS,&SetGS32,sizeof(SetGS32));
    }

    SetGS(GetDS());
    if (UseGS(p) != 0x12345678) exit(-1);

    if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) 
    {
        // The gist of the question - What is the 64-bit equivalent of the following code
    }
    else
    {
        DWORD base = (DWORD)p;
        LDT_ENTRY ll;
        int ret;
        *(FARPROC*)(&NtSetLdtEntries) = GetProcAddress(LoadLibrary("ntdll.dll"),"NtSetLdtEntries");
        ll.BaseLow = base & 0xFFFF;
        ll.HighWord.Bytes.BaseMid = base >> 16;
        ll.HighWord.Bytes.BaseHi = base >> 24;
        ll.LimitLow = 400;     
        ll.HighWord.Bits.LimitHi = 0;
        ll.HighWord.Bits.Granularity = 0;
        ll.HighWord.Bits.Default_Big = 1; 
        ll.HighWord.Bits.Reserved_0 = 0;
        ll.HighWord.Bits.Sys = 0; 
        ll.HighWord.Bits.Pres = 1;
        ll.HighWord.Bits.Dpl = 3; 
        ll.HighWord.Bits.Type = 0x13; 
        ret = NtSetLdtEntries(0x80,*(DWORD*)&ll,*((DWORD*)(&ll)+1),0);
        if (ret < 0) { exit(-1);}
        SetGS(0x84);
    }
    if (UseGS(0) != 0x12345678) exit(-1);
    printf("PASS\n");
}
您可以直接通过 SetThreadcontext API修改线程上下文.但是,您需要确保线程在上下文更改时未运行.或者 suspend修改另一个线程的上下文,或者触发一个假的SEH异常并修改SEH处理程序中的线程上下文.然后操作系统将为您更改线程上下文并重新安排线程.

更新:

第二种方法的示例代码

__try
{
    __asm int 3 // trigger fake exception
}
__except(filter(GetExceptionCode(),GetExceptionInformation()))
{
}

int filter(unsigned int code,struct _EXCEPTION_POINTERS *ep)
{
    ep->ContextRecord->SegGs = 23;
    ep->ContextRecord->Eip++;
    return EXCEPTION_CONTINUE_EXECUTION;
}

try块中的指令基本上引发了一个软件异常. OS然后将控制权转移到修改线程上下文的过滤程序,有效地告诉操作系统跳过int3指令并继续执行.这是一个黑客,但其所有记录的功能:)

猜你在找的Windows相关文章