Как программа пользовательского пространства может настроить "GS:" под 64-разрядную Windows (в настоящее время XP-64)?
(С помощью configure установите GS: 0 на произвольный 64-битный линейный адрес).
Я пытаюсь перенести среду JIT на X86-64, которая была первоначально разработана для Win32.
Одним из неудачных аспектов дизайна является то, что идентичный код должен выполняться на нескольких потоках пользовательского пространства (например, "волокна" ). В версии кода 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 *, которая позволяет мне изменять "GS:" в пользовательском пространстве или в каком-либо другом темном углу Windows API. Я считаю, что Windows по-прежнему использует FS: для своего собственного TLS, поэтому какой-то механизм должен быть доступен?
Этот пример кода иллюстрирует проблему. Я заранее извиняюсь за использование байтового кода - VS не будет делать встроенную сборку для 64-битного компиляции, и я пытался сохранить это как один файл для иллюстративных целей.
Программа отображает "PASS" на XP-32 и не работает на 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");
}