3 * Library for reading the Global Descriptor Table if possible.
4 * (C) 2009-2012 Jonathan Campbell.
5 * Hackipedia DOS library.
7 * This code is licensed under the LGPL.
8 * <insert LGPL legal text here>
10 * Compiles for intended target environments:
12 * - Windows 3.0/3.1/95/98/ME
13 * - Windows NT 3.1/3.51/4.0/2000/XP/Vista/7
17 * If the host OS is loose, or unprotected, this library can abuse the
18 * loophole to read the contents of the Global Descriptor Table. Note
19 * that this is possible under Windows 3.0/3.1/95/98/ME because they
20 * leave it out in the open. Windows NT does NOT let us do this!! Neither
25 #include <conio.h> /* this is where Open Watcom hides the outp() etc. functions */
33 #include <hw/cpu/cpu.h>
34 #include <hw/dos/dos.h>
35 #include <hw/dos/doswin.h>
36 #include <hw/cpu/gdt_enum.h>
38 unsigned char cpu_gdtlib_can_read = 0,cpu_gdtlib_can_write = 0,cpu_gdtlib_result = 0xFF;
39 uint16_t cpu_gdtlib_ldtr = 0;
40 struct x86_gdtr cpu_gdtlib_gdtr;
41 struct cpu_gdtlib_entry cpu_gdtlib_ldt_ent;
42 #if TARGET_MSDOS == 16 && defined(TARGET_WINDOWS)
43 uint16_t cpu_gdtlib_gdt_sel = 0; /* selector for reading GDT table */
44 uint16_t cpu_gdtlib_ldt_sel = 0; /* selector for reading LDT table */
47 #if TARGET_MSDOS == 32 && defined(TARGET_WINDOWS)
48 uint32_t cpu_gdtlib_lin_bias = 0UL;
51 unsigned int cpu_gdtlib_gdt_entries(struct x86_gdtr *r) {
52 if (r->limit == 0xFFFFU) return 0x2000;
53 return ((unsigned int)r->limit + 1U) >> 3;
56 unsigned int cpu_gdtlib_ldt_entries(struct cpu_gdtlib_entry *r) {
57 return ((unsigned int)(r->limit + 1UL)) >> 3UL;
60 int cpu_gdtlib_read_ldtr(uint16_t *sel) {
61 if (!cpu_gdtlib_can_read)
64 #if TARGET_MSDOS == 16 && !defined(TARGET_WINDOWS) && !defined(TARGET_OS2)
65 /* in what is likely a strange inversion of priorities, the Win 9x kernel allows
66 * DOS programs like us to read the GDTR register. But... attempts to read the LDTR
67 * are not permitted. */
68 if (windows_mode == WINDOWS_ENHANCED)
72 #if TARGET_MSDOS == 16
73 # if defined(__LARGE__) || defined(__COMPACT__)
103 int cpu_gdtlib_read_gdtr(struct x86_gdtr *raw) {
104 if (!cpu_gdtlib_can_read)
107 #if TARGET_MSDOS == 16
108 # if defined(__LARGE__) || defined(__COMPACT__)
138 int cpu_gdtlib_init() {
139 if (cpu_gdtlib_result == 0xFF) {
141 cpu_gdtlib_can_read = cpu_gdtlib_can_write = 0;
142 if (cpu_basic_level < 0) cpu_probe();
143 if (cpu_basic_level < 3) return (cpu_gdtlib_result=0);
147 #if defined(TARGET_OS2)
148 /* OS/2 16-bit or 32-bit: Not gonna happen */
149 return (cpu_gdtlib_result=0);
150 #elif TARGET_MSDOS == 16 && !defined(TARGET_WINDOWS)
153 * - We're in real mode, and there is no such GDT installed
154 * - We're in v86 mode, we might be able to locate the GDT and read it
155 * - We're in v86 mode and VCPI is present: uhhhmmmm.... we'll we could enter via VCPI but that only installs our OWN GDT tables, so...
156 * - We're in v86 mode under Windows 3.x/9x/ME: DPMI is likely available, we could take advantage of the 9x kernel's permissiveness to read the GDT
157 * - We're in v86 mode under Windows NT/2000/XP: don't try, not gonna happen. */
158 if (windows_mode == WINDOWS_NT)
159 return (cpu_gdtlib_result=0);
160 if (windows_mode == WINDOWS_REAL)
161 return (cpu_gdtlib_result=0);
162 if (!cpu_v86_active) /* if there's no v86 mode, then there's no protected mode GDT table */
163 return (cpu_gdtlib_result=0);
164 if (!dpmi_lin2fmemcpy_init())
165 return (cpu_gdtlib_result=0);
167 /* FIXME: Windows 3.0: This code hangs */
168 if (windows_mode == WINDOWS_ENHANCED && windows_version < 0x30A) /* Anything less than Windows 3.1 */
169 return (cpu_gdtlib_result=0);
171 cpu_gdtlib_can_read = 1;
172 return (cpu_gdtlib_result=1);
173 #elif TARGET_MSDOS == 32 && !defined(TARGET_WINDOWS)
176 * - Pure DOS mode is active, the DOS extender did not enable paging, and we're ring 0. we can just read the GDT directly. No problem.
177 * - Pure DOS mode with EMM386.EXE. The DOS extender used VCPI to enter protected mode, we just have to locate the GDT somehow.
178 * Usually it ends up in the first 1MB (DOS area) where 1:1 mapping is still retained. We can read it
179 * - Windows 3.x/9x/ME DOS Box: We're running at ring 3, but we can take advantage of the 9x kernel's apparent permissiveness to
180 * locate the GDT and read it.
181 * - Windows NT/2000/XP: Not gonna happen. Even reading the control register will fault our application. */
182 if (windows_mode == WINDOWS_NT)
183 return (cpu_gdtlib_result=0);
185 cpu_gdtlib_can_read = 1;
186 return (cpu_gdtlib_result=1);
187 #elif TARGET_MSDOS == 16 && defined(TARGET_WINDOWS)
188 /* 16-bit Win16 application:
190 * - Windows 3.x/9x/ME: As a Win16 app we can use Win16 functions like AllocateSelector(), etc. to give ourself access to the GDT.
191 * - Windows NT/2000/XP: We might be able to use AllocateSelector(), etc. NTVDM.EXE allows "SGDT" but result seems to point to garbage
193 * For obvious reasons, we do not attempt to read the GDT in real-mode Windows 3.0 because there is no GDT */
194 if (windows_mode == WINDOWS_NT)
195 return (cpu_gdtlib_result=0);
196 if (windows_mode == WINDOWS_REAL)
197 return (cpu_gdtlib_result=0);
199 cpu_gdtlib_can_read = 1;
200 return (cpu_gdtlib_result=1);
201 #elif TARGET_MSDOS == 32 && defined(TARGET_WINDOWS)
202 /* 32-bit Windows application:
204 * - Windows 3.1/9x/ME: We can use "sgdt" to read the GDTR, and then just refer to that location in memory.
205 * - Windows NT/2000/XP: Not gonna happen */
206 if (windows_mode == WINDOWS_NT)
207 return (cpu_gdtlib_result=0);
218 /* #1: We need to know what Watcom Win386's data selector bias is */
219 cpu_gdtlib_lin_bias = GetSelectorBase(s);
221 /* #2: Whatever the base is, Win386 usually sets the limit to 0xFFFFFFFF.
222 * The base is usually something like 0x80302330, and the GDT is usually
223 * something like 0x80112000, so if we have to rollover BACKWARDS we
224 * want to make sure the limit is 0xFFFFFFFF */
225 if (GetSelectorLimit(s) != 0xFFFFFFFFUL)
226 return (cpu_gdtlib_result=0);
229 /* Windows 3.1 with Win32s sets the flat selectors to base = 0xFFFF0000 for whatever reason.
230 * So to properly read the GDT we have to compensate for that */
231 if (windows_mode == WINDOWS_ENHANCED && windows_version < 0x35F)
232 cpu_gdtlib_lin_bias = 0xFFFF0000UL;
235 cpu_gdtlib_can_read = 1;
236 return (cpu_gdtlib_result=1);
240 return cpu_gdtlib_result;
243 void cpu_gdtlib_free() {
244 #if TARGET_MSDOS == 16 && defined(TARGET_WINDOWS)
245 if (cpu_gdtlib_gdt_sel != 0) {
246 FreeSelector(cpu_gdtlib_gdt_sel);
247 cpu_gdtlib_gdt_sel=0;
249 if (cpu_gdtlib_ldt_sel != 0) {
250 FreeSelector(cpu_gdtlib_ldt_sel);
251 cpu_gdtlib_ldt_sel=0;
256 int cpu_gdtlib_ldt_read_entry(struct cpu_gdtlib_entry *e,unsigned int i) {
257 if (i >= cpu_gdtlib_ldt_entries(&cpu_gdtlib_ldt_ent))
259 if (!cpu_gdtlib_can_read)
262 #if defined(TARGET_OS2)
264 #elif TARGET_MSDOS == 16 && !defined(TARGET_WINDOWS)
265 { /* 16-bit real mode DOS */
266 unsigned char tmp[8];
268 if (dpmi_lin2fmemcpy(tmp,cpu_gdtlib_ldt_ent.base + (i << 3),8) == 0)
271 e->limit = (uint32_t)( *((uint16_t*)(tmp+0)) );
272 e->base = (uint32_t)( *((uint32_t*)(tmp+2)) & 0xFFFFFFUL );
274 e->granularity = tmp[6];
275 e->base |= ((uint32_t)tmp[7]) << 24UL;
276 e->limit |= ((uint32_t)e->granularity & 0xFUL) << 16UL;
278 #elif TARGET_MSDOS == 32 && !defined(TARGET_WINDOWS)
279 { /* 32-bit protected mode DOS */
280 unsigned char *p = (unsigned char*)cpu_gdtlib_ldt_ent.base + (i << 3);
281 e->limit = (uint32_t)( *((uint16_t*)(p+0)) );
282 e->base = (uint32_t)( *((uint32_t*)(p+2)) & 0xFFFFFFUL );
284 e->granularity = p[6];
285 e->base |= ((uint32_t)p[7]) << 24UL;
286 e->limit |= ((uint32_t)e->granularity & 0xFUL) << 16UL;
288 #elif TARGET_MSDOS == 16 && defined(TARGET_WINDOWS)
289 { /* 16-bit Win16 app */
290 unsigned char far *p;
292 if (cpu_gdtlib_ldt_sel == 0) {
295 cpu_gdtlib_ldt_sel = AllocSelector(myds);
296 if (cpu_gdtlib_ldt_sel == 0) return 0;
297 SetSelectorLimit(cpu_gdtlib_ldt_sel,(DWORD)cpu_gdtlib_ldt_ent.limit);
298 SetSelectorBase(cpu_gdtlib_ldt_sel,(DWORD)cpu_gdtlib_ldt_ent.base);
301 if (cpu_gdtlib_ldt_sel != 0) {
302 p = MK_FP(cpu_gdtlib_ldt_sel,i << 3);
303 e->limit = (uint32_t)( *((uint16_t far*)(p+0)) );
304 e->base = (uint32_t)( *((uint32_t far*)(p+2)) & 0xFFFFFFUL );
306 e->granularity = p[6];
307 e->base |= ((uint32_t)p[7]) << 24UL;
308 e->limit |= ((uint32_t)e->granularity & 0xFUL) << 16UL;
311 #elif TARGET_MSDOS == 32 && defined(TARGET_WINDOWS)
312 { /* 32-bit Windows app */
313 unsigned char *p = (unsigned char*)cpu_gdtlib_ldt_ent.base + (i << 3) - cpu_gdtlib_lin_bias;
314 e->limit = (uint32_t)( *((uint16_t*)(p+0)) );
315 e->base = (uint32_t)( *((uint32_t*)(p+2)) & 0xFFFFFFUL );
317 e->granularity = p[6];
318 e->base |= ((uint32_t)p[7]) << 24UL;
319 e->limit |= ((uint32_t)e->granularity & 0xFUL) << 16UL;
324 #if !defined(TARGET_OS2)
325 if (e->granularity & 0x80)
326 e->limit = (e->limit << 12UL) | 0xFFFUL;
332 int cpu_gdtlib_gdt_read_entry(struct cpu_gdtlib_entry *e,unsigned int i) {
333 if (i >= cpu_gdtlib_gdt_entries(&cpu_gdtlib_gdtr))
335 if (!cpu_gdtlib_can_read)
338 #if defined(TARGET_OS2)
340 #elif TARGET_MSDOS == 16 && !defined(TARGET_WINDOWS)
341 { /* 16-bit real mode DOS */
342 unsigned char tmp[8];
344 if (dpmi_lin2fmemcpy(tmp,cpu_gdtlib_gdtr.base + (i << 3),8) == 0)
347 e->limit = (uint32_t)( *((uint16_t*)(tmp+0)) );
348 e->base = (uint32_t)( *((uint32_t*)(tmp+2)) & 0xFFFFFFUL );
350 e->granularity = tmp[6];
351 e->base |= ((uint32_t)tmp[7]) << 24UL;
352 e->limit |= ((uint32_t)e->granularity & 0xFUL) << 16UL;
354 #elif TARGET_MSDOS == 32 && !defined(TARGET_WINDOWS)
355 { /* 32-bit protected mode DOS */
356 unsigned char *p = (unsigned char*)cpu_gdtlib_gdtr.base + (i << 3);
357 e->limit = (uint32_t)( *((uint16_t*)(p+0)) );
358 e->base = (uint32_t)( *((uint32_t*)(p+2)) & 0xFFFFFFUL );
360 e->granularity = p[6];
361 e->base |= ((uint32_t)p[7]) << 24UL;
362 e->limit |= ((uint32_t)e->granularity & 0xFUL) << 16UL;
364 #elif TARGET_MSDOS == 16 && defined(TARGET_WINDOWS)
365 { /* 16-bit Win16 app */
366 unsigned char far *p;
368 if (cpu_gdtlib_gdt_sel == 0) {
371 cpu_gdtlib_gdt_sel = AllocSelector(myds);
372 if (cpu_gdtlib_gdt_sel == 0) return 0;
373 SetSelectorLimit(cpu_gdtlib_gdt_sel,(DWORD)cpu_gdtlib_gdtr.limit);
374 SetSelectorBase(cpu_gdtlib_gdt_sel,(DWORD)cpu_gdtlib_gdtr.base);
377 if (cpu_gdtlib_gdt_sel != 0) {
378 p = MK_FP(cpu_gdtlib_gdt_sel,i << 3);
379 e->limit = (uint32_t)( *((uint16_t far*)(p+0)) );
380 e->base = (uint32_t)( *((uint32_t far*)(p+2)) & 0xFFFFFFUL );
382 e->granularity = p[6];
383 e->base |= ((uint32_t)p[7]) << 24UL;
384 e->limit |= ((uint32_t)e->granularity & 0xFUL) << 16UL;
387 #elif TARGET_MSDOS == 32 && defined(TARGET_WINDOWS)
388 { /* 32-bit Windows app */
389 unsigned char *p = (unsigned char*)cpu_gdtlib_gdtr.base + (i << 3) - cpu_gdtlib_lin_bias;
390 e->limit = (uint32_t)( *((uint16_t*)(p+0)) );
391 e->base = (uint32_t)( *((uint32_t*)(p+2)) & 0xFFFFFFUL );
393 e->granularity = p[6];
394 e->base |= ((uint32_t)p[7]) << 24UL;
395 e->limit |= ((uint32_t)e->granularity & 0xFUL) << 16UL;
400 #if !defined(TARGET_OS2)
401 if (e->granularity & 0x80)
402 e->limit = (e->limit << 12UL) | 0xFFFUL;
408 int cpu_gdtlib_empty_gdt_entry(struct cpu_gdtlib_entry *e) {
409 if (e->limit == 0 && e->base == 0 && e->access == 0 && e->granularity == 0)
415 #define cpu_gdtlib_empty_ldt_entry cpu_gdtlib_empty_gdt_entry
417 int cpu_gdtlib_entry_is_special(struct cpu_gdtlib_entry *e) {
418 return !(e->access & 0x10);
421 int cpu_gdtlib_entry_is_executable(struct cpu_gdtlib_entry *e) {
422 return (e->access & 0x08);
425 int cpu_gdtlib_read_current_regs() {
426 #if TARGET_MSDOS == 16 && defined(TARGET_WINDOWS)
427 if (cpu_gdtlib_ldt_sel != 0) {
428 FreeSelector(cpu_gdtlib_ldt_sel);
429 cpu_gdtlib_ldt_sel=0;
433 /* it matters if we can't read the GDTR */
434 if (!cpu_gdtlib_read_gdtr(&cpu_gdtlib_gdtr))
437 /* but if we can't read the LDTR, we don't care */
438 memset(&cpu_gdtlib_ldt_ent,0,sizeof(cpu_gdtlib_ldt_ent));
440 cpu_gdtlib_read_ldtr(&cpu_gdtlib_ldtr);
445 int cpu_gdtlib_prepare_to_read_ldt() {
446 if (cpu_gdtlib_ldtr == 0) return 0;
447 if (cpu_gdtlib_ldt_ent.limit == 0 && cpu_gdtlib_ldt_ent.access == 0) {
448 if (cpu_gdtlib_gdt_read_entry(&cpu_gdtlib_ldt_ent,cpu_gdtlib_ldtr>>3) == 0)
450 if (cpu_gdtlib_empty_gdt_entry(&cpu_gdtlib_ldt_ent))