/** * @file dump_selectors.c * * Dump all the x86 system structures available to a program running * in ring 3. * * Copyright 2004 Jason Spence */ #include #ifdef _WIN32 typedef unsigned char uint8_t; typedef unsigned short uint16_t; typedef unsigned int uint32_t; #else # include #endif // Requestor privilege level. 0 = highest (supervisor), 3 = lowest. #define RPL_MASK 0x3 // Table index. 0 = GDT, 1 = LDT. #define TI_MASK 0x4 // Selector index. #define SI_MASK 0xfff8 #define DESCRIPTOR_SIZE 8 #define CODE_MASK 0x8 #define CONFORMING_MASK 0x4 #define READ_MASK 0x2 #define ACCESSED_MASK 0x1 #define EXPAND_DOWN_MASK 0x4 #define WRITE_MASK 0x2 #define ACCESSED_MASK 0x1 #define get_limit(segment, out) \ ({ \ uint32_t zf; \ \ asm(" movw %2, %%bx\n" \ " movzx %%bx, %%ebx\n" \ " lsl %%ebx, %%ebx\n" \ " movl %%ebx, %1\n" \ " sete %0\n" : \ "=m" (zf), "=m" (out): "r" (segment): \ "%ebx"); \ zf ? 1 : 0; \ }) static char * system_descriptor_names[] = { "(Reserved Type)", "16-Bit TSS (Available)", "LDT", "16-Bit TSS (Busy)", "16-Bit Call Gate", "Task Gate", "16-Bit Interrupt Gate", "16-Bit Trap Gate", "Reserved", "32-Bit TSS (Available)", "Reserved", "32-Bit TSS (Busy)", "32-Bit Call Gate", "Reserved", "32-Bit Interrupt Gate", "32-Bit Trap Gate" }; /* x86 Linear address. See IASDM $3.7.1. */ typedef struct _l_addr_t { unsigned int offset:12; unsigned int table:10; unsigned int dir:10; } l_addr_t; // Both sgdt and sidt write 6 bytes to a given memory location, which // is why we're packing. // "dtr" stands for descriptor table register here. #pragma pack(1) /* XXX IASDM V2 $3.5.1 wants this to be on an odd word boundary so the base is aligned */ typedef struct _dtr_t { uint16_t limit; l_addr_t base; } dtr_t; #pragma pack() typedef struct _sd2_t { unsigned int base2:8; unsigned int segment_type:4; /* see IASDM $3.4.3.1 */ unsigned int descriptor_type:1; /* 0 = system segment, 1 = code or data */ unsigned int dpl:2; unsigned int present:1; /* 1 = segment in memory, 0 = not */ unsigned int limit:4; /* contents are undefined */ unsigned int avl:1; unsigned int unused:1; unsigned int default_op_size:1; /* 0 = 16-bit, 1 = 32-bit */ unsigned int granularity:1; /* 0 = bytes, 1 = 4k pages */ unsigned int base3:8; } sd2_t; //#pragma pack() /* 31-22: directory 21-12: table 11-0: offset */ typedef struct _selectors_t { uint16_t cs; uint16_t ds; uint16_t es; uint16_t fs; uint16_t gs; uint16_t ss; } selectors_t; #include "dump_selectors.h" #ifndef _WIN32 void get_selectors(selectors_t * ret) { asm("movw %%cs, %0\n" "movw %%ds, %1\n" "movw %%es, %2\n" "movw %%fs, %3\n" "movw %%gs, %4\n" "movw %%ss, %5\n" : "=m" (ret->cs), "=m" (ret->ds), "=m" (ret->es), "=m" (ret->fs), "=m" (ret->gs), "=m" (ret->ss) ); } #else void get_selectors(selectors_t * r) { uint16_t tmp; _asm { mov tmp, cs } r->cs = tmp; _asm { mov tmp, ds } r->ds = tmp; _asm { mov tmp, es } r->es = tmp; _asm { mov tmp, fs } r->fs = tmp; _asm { mov tmp, gs } r->gs = tmp; _asm { mov tmp, ss } r->ss = tmp; } #endif /* _WIN32 */ void dump_selector(uint16_t sel) { sd2_t tmp; uint32_t limit; if(! (sel & TI_MASK) && ! (sel & SI_MASK)) { printf(" Invalid GDT index 0\n"); return; } printf(" Table index: %s\n", (sel & TI_MASK) ? "LDT" : "GDT"); printf(" Selector index: %i\n", (sel & SI_MASK) >> 3); if(get_limit(sel, limit)) printf(" Limit: 0x%x", limit); printf(" Requester privilege level: Ring %i\n", sel & RPL_MASK); memset(&tmp, 0, sizeof(tmp)); if(get_rights(sel, &tmp)) { printf(" Type: "); if(tmp.descriptor_type) { if(tmp.segment_type & CODE_MASK) { printf("Code "); if(tmp.segment_type & CONFORMING_MASK) printf("Conforming "); if(tmp.segment_type & READ_MASK) printf("Read "); if(tmp.segment_type & ACCESSED_MASK) printf("Accessed "); } else { printf("Data "); if(tmp.segment_type & EXPAND_DOWN_MASK) printf("Expand-Down "); if(tmp.segment_type & WRITE_MASK) printf("Write "); if(tmp.segment_type & ACCESSED_MASK) printf("Accessed "); } } else { printf("%s", system_descriptor_names[tmp.segment_type]); } printf("\n"); printf(" DPL: Ring %i\n", tmp.dpl); if(tmp.present) { printf(" Granularity: %s\n", tmp.granularity ? "4KB pages" : "bytes"); printf(" Default operation size: %s\n", tmp.default_op_size ? "32-Bit" : "16-Bit"); printf(" AVL bit: %i\n", tmp.avl); printf(" Limit (undefined): %i\n", tmp.limit); } else { printf(" (Segment is listed as not present)\n"); } } } #ifndef _WIN32 void get_gdt(dtr_t * ret) { if(ret == NULL) return; asm("sgdt %0": "=m" (*ret)); } #else void get_gdt(dtr_t * r) { dtr_t tmp; if(r == NULL) return; _asm { sgdt tmp; } *r = tmp; } #endif /* _WIN32 */ #ifndef _WIN32 void get_idt(dtr_t * ret) { if(ret == NULL) return; asm("sidt %0": "=m" (*ret)); } #else void get_idt(dtr_t * r) { dtr_t tmp; if(r == NULL) return; _asm { sidt tmp; } *r = tmp; } #endif /* _WIN32 */ #ifndef _WIN32 void get_ldt(uint16_t * ldt) { if(ldt == NULL) return; asm("sldt %0" : "=m" (*ldt)); } #else void get_ldt(uint16_t * ldt) { uint16_t tmp; if(ldt == NULL) return; _asm { sldt tmp; } *ldt = tmp; } #endif /* _WIN32 */ #ifndef _WIN32 void get_tr(uint16_t * ret) { if(ret == NULL) return; asm("str %0" : "=m" (*ret)); } #else void get_tr(uint16_t * r) { uint16_t tmp; if(r == NULL) return; _asm { str tmp; } *r = tmp; } #endif /* _WIN32 */ #ifndef _WIN32 uint32_t get_rights(uint16_t selector, sd2_t * ret) { sd2_t tmp; uint8_t zf; if(ret == NULL) return 0; asm(" lar %2, %0\n" " sete %1\n" : "=r" (tmp), "=g" (zf) : "m" (selector)); *ret = tmp; return zf; } #else uint32_t get_rights(uint16_t selector, sd2_t * r) { sd2_t tmp; uint8_t zf; if(r == NULL) return 0; _asm { lar edx, selector; mov tmp, edx; sete zf; } *r = tmp; return zf; } #endif /* _WIN32 */ int main(void) { dtr_t d; uint16_t t; uint16_t l; selectors_t s; memset(&d, 0, sizeof(d)); get_gdt(&d); printf("gdt base = [%x %x %x]H (linear)\n", d.base.dir, d.base.table, d.base.offset); if(d.limit == 0) { printf("SGDT reports 0 entries; this is supposed to be impossible.\n"); } else { printf("gdt size = 0x%x bytes ", d.limit + 1); if((d.limit + 1) % 8 != 0) printf("(invalid size)\n"); else printf("(0x%x entries)\n", (d.limit + 1) / 8); } memset(&d, 0, sizeof(d)); get_idt(&d); printf("idt base = [%x %x %x]H (linear)\n", d.base.dir, d.base.table, d.base.offset); if(d.limit == 0) { printf("SIDT reports 0 entries; this is supposed to be impossible.\n"); } else { printf("idt size = 0x%x bytes ", d.limit + 1); if((d.limit + 1) % 8 != 0) printf("(invalid size)\n"); else printf("(0x%x entries)\n", (d.limit + 1) / 8); } memset(&l, 0, sizeof(l)); get_ldt(&l); printf("Selector for current LDT: 0x%x\n", l); memset(&t, 0, sizeof(t)); get_tr(&t); printf("Selector for current task: 0x%x\n", t); get_selectors(&s); printf("cs = 0x%x\n", s.cs); dump_selector(s.cs); printf("ds = 0x%x\n", s.ds); dump_selector(s.ds); printf("es = 0x%x\n", s.es); dump_selector(s.es); printf("fs = 0x%x\n", s.fs); dump_selector(s.fs); printf("gs = 0x%x\n", s.gs); dump_selector(s.gs); printf("ss = 0x%x\n", s.ss); dump_selector(s.ss); return 0; }