caf*_*end 10 assembly winapi win64 x86-64
用户空间程序如何在64位Windows(当前为XP-64)下配置"GS:"?
(通过configure,将GS:0设置为任意64位线性地址).
我正在尝试将"JIT"环境移植到最初为Win32开发的X86-64.
一个不幸的设计方面是相同的代码需要在多个用户空间线程(例如,"光纤")上运行.代码的Win32版本使用GS选择器,并生成正确的前缀来访问本地数据 - "mov eax,GS:[offset]"指向当前任务的正确数据.来自Win32版本的代码会将值加载到GS中,只要它有一个可以工作的值.
到目前为止,我已经能够发现64位窗口不支持LDT,因此Win32下使用的方法不起作用.但是,X86-64指令集包括"SWAPGS",以及在不使用传统分段的情况下加载GS的方法 - 但这仅适用于内核空间.
根据X64手册,即使Win64允许访问描述符 - 它没有 - 但是没有办法设置段基的高32位.设置这些的唯一方法是通过GS_BASE_MSR(和相应的FS_BASE_MSR - 在64位模式下忽略其他段基).WRMSR指令是Ring0,所以我不能直接使用它.
我希望有一个Zw*函数,允许我在用户空间或Windows API的其他一些黑暗角落中更改"GS:".我相信Windows仍然使用FS:对于自己的TLS,所以必须有一些机制?
程序在XP-32上显示"PASS",在XP-x64上不显示.
#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,0x24,0x04, // mov eax, ss:[sp+4]
0x65,0x8B,0x00, // mov eax, gs:[eax]
0xc3 }; // ret
unsigned char SetGS64[] =
{0x8E,0xe9, // mov gs, rcx
0xC3 }; // ret
unsigned char UseGS64[] =
{ 0x65,0x8B,0x01, // mov eax, gs:[rcx]
0xc3 };
typedef WORD(*fcnGetDS)(void);
typedef void(*fcnSetGS)(WORD);
typedef DWORD(*fcnUseGS)(LPVOID);
int (*NtSetLdtEntries)(DWORD, DWORD, DWORD, 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, &GetDS32, sizeof(GetDS32));
memcpy( UseGS, &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,0,0);
if (ret < 0) { exit(-1);}
SetGS(0x84);
}
if (UseGS(0) != 0x12345678) exit(-1);
printf("PASS\n");
}
Run Code Online (Sandbox Code Playgroud)
从来没有在 x64 代码中修改过 GS,所以我可能是错的,但是你不应该能够通过 PUSH/POP 或 LGS 修改 GS 吗?
更新:英特尔手册还说 mov SegReg, Reg 在 64 位模式下是允许的。