10 Stimmen

Verwendung von "GS:" in 64-Bit-Windows-Assembly (z. B. bei der Portierung von TLS-Code)

Wie kann ein User-Space-Programm "GS:" unter 64-Bit-Windows (derzeit XP-64) konfigurieren?
(Bei der Konfiguration ist GS:0 auf eine beliebige lineare 64-Bit-Adresse zu setzen).

Ich versuche, eine "JIT"-Umgebung auf X86-64 zu portieren, die ursprünglich für Win32 entwickelt wurde.

Ein unglücklicher Design-Aspekt ist, dass identischer Code in mehreren User-Space-Threads (z.B. "Fasern") laufen muss. Die Win32-Version des Codes verwendet hierfür den GS-Selektor und generiert das richtige Präfix für den Zugriff auf die lokalen Daten - "mov eax,GS:[offset]" zeigt auf die richtigen Daten für die aktuelle Aufgabe. Der Code der Win32-Version würde einen Wert in GS laden, wenn er nur einen Wert hätte, der funktionieren würde.

Bisher konnte ich feststellen, dass 64-Bit-Windows die LDT nicht unterstützt, so dass die unter Win32 verwendete Methode nicht funktioniert. Der X86-64-Befehlssatz enthält jedoch "SWAPGS" sowie eine Methode zum Laden von GS ohne Verwendung der Legacy-Segmentierung - dies funktioniert jedoch nur im Kernelbereich.

Laut den X64-Handbüchern gibt es, selbst wenn Win64 den Zugriff auf Deskriptoren erlauben würde - was nicht der Fall ist - keine Möglichkeit, die hohen 32-Bits der Segmentbasis zu setzen. Die einzige Möglichkeit, diese zu setzen, ist über GS_BASE_MSR (und die entsprechende FS_BASE_MSR - die anderen Segmentbasen werden im 64-Bit-Modus ignoriert). Der WRMSR-Befehl ist Ring0, so dass ich ihn nicht direkt verwenden kann.

Ich hoffe auf eine Zw*-Funktion, mit der ich "GS:" im Userspace ändern kann, oder auf eine andere dunkle Ecke der Windows-API. Ich glaube, Windows verwendet immer noch FS: für sein eigenes TLS, also muss ein Mechanismus vorhanden sein?


Dieser Beispielcode veranschaulicht das Problem. Ich entschuldige mich im Voraus für die Verwendung von Byte-Code - VS wird nicht Inline-Assembler für die 64-Bit-Kompilierung zu tun, und ich war versucht, dies als eine Datei für illustrative Zwecke zu halten.

Das Programm zeigt auf XP-32 "PASS" an, auf XP-x64 jedoch nicht.


#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");
}

0voto

Orwellophile Punkte 12151

x86-64 fügte der Adressierung keinen neuen Begriff hinzu - der bestehende Code macht ausgiebig Gebrauch von der skalierten Index- und Basisadressierung, mit dem GS-Term als drittem Begriff.

Ihre Frage verwirrt mich ein wenig, aber ich hoffe, dass dieser Assembler hilft. Ich habe ihn noch nicht in C-Code portiert, werde dies aber in Kürze tun:

Lesen __declspec(thread) Daten

    mov     ecx, cs:TlsIndex ; TlsIndex is a memory location 
                             ; containing a DWORD with the value 0
    mov     rax, gs:58h
    mov     edx, 830h
    mov     rax, [rax+rcx*8]
    mov     rax, [rdx+rax]
    retn

Tut mir leid, ich habe kein Beispiel für das Schreiben von Daten, das obige stammt aus einem disassemblierten Code, den ich zurückentwickle.

Update: Hier ist der äquiv. C-Code für das obige, obwohl ich ihn nicht geschrieben habe. Ich glaube, er wurde von NTAuthority und/oder citizenmp verfasst.

rage::scrThread* GetActiveThread()
{
    char* moduleTls = *(char**)__readgsqword(88);

    return *reinterpret_cast<rage::scrThread**>(moduleTls + 2096);
}

Und hier wird das Gleiche geschrieben:

void SetActiveThread(rage::scrThread* thread)
{
    char* moduleTls = *(char**)__readgsqword(88);
    *reinterpret_cast<rage::scrThread**>(moduleTls + 2096) = thread;
}

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X