3 * Code to detect the surrounding DOS/Windows environment and support routines to work with it
4 * (C) 2009-2012 Jonathan Campbell.
5 * Hackipedia DOS library.
7 * This code is licensed under the LGPL.
8 * <insert LGPL legal text here>
16 #include <conio.h> /* this is where Open Watcom hides the outp() etc. functions */
26 #include <hw/cpu/cpu.h>
27 #include <hw/dos/dos.h>
28 #include <hw/dos/doswin.h>
29 #include <hw/dos/dosntvdm.h>
31 #if defined(TARGET_WINDOWS) && TARGET_MSDOS == 32 && !defined(WIN386)
33 /* this library of code deals with the problem of getting ordinal-only exported functions out of KERNEL32.DLL in
34 * Windows 9x/ME. GetProcAddress() won't do it for us, so instead, we forcibly read it out from memory ourself.
35 * That's what you get for being a jack-ass about Win16 compatibility Microsoft */
37 /* Note that this code would work under Windows 9x/ME and NT/2000/XP, but it is only needed for 9x/ME.
38 * Windows XP SP2 and later change the handle slightly to try and confuse code like this (they take the HANDLE
39 * value of the module and set the least significant bit), and I have reason to believe Microsoft will eventually
40 * outright change the interface to make the handle an actual opaque handle someday (while of course making it
41 * utterly impossible for programs like us to get to the API functions we need to do our fucking job). Because they're
42 * Microsoft, and that's what they do with the Windows API. */
44 /* How to use: Use the 32-bit GetModuleHandle() function to get the HMODULE value. In Windows so far, this HMODULE
45 * value is literally the linear memory address where Windows loaded (or mapped) the base of the DLL image, complete
46 * with MS-DOS header and PE image. This hack relies on that to then traverse the PE structure directly and forcibly
47 * retrieve from the ordinal export table the function we desire. */
49 /* returns: DWORD* pointer to PE image's export ordinal table, *entries is filled with the number of entries, *base
50 * is filled with the ordinal number of the first entry. */
52 static IMAGE_NT_HEADERS *Win32ValidateHModuleMSDOS_PE_Header(BYTE *p) {
53 if (!memcmp(p,"MZ",2)) {
54 /* then at offset 0x3C there should be the offset to the PE header */
55 DWORD offset = *((DWORD*)(p+0x3C));
56 if (offset < 0x40 || offset > 0x10000) return NULL;
58 if (IsBadReadPtr(p,4096)) return NULL;
59 if (!memcmp(p,"PE\0\0",4)) {
60 /* wait, before we celebrate, make sure it's sane! */
61 IMAGE_NT_HEADERS *pp = (IMAGE_NT_HEADERS*)p;
63 if (pp->FileHeader.Machine != IMAGE_FILE_MACHINE_I386)
65 if (pp->FileHeader.SizeOfOptionalHeader < 88) /* <- FIXME */
67 if (pp->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC)
77 static IMAGE_DATA_DIRECTORY *Win32GetDataDirectory(IMAGE_NT_HEADERS *p) {
78 return p->OptionalHeader.DataDirectory;
81 DWORD *Win32GetExportOrdinalTable(HMODULE mod,DWORD *entries,DWORD *base,DWORD *base_addr) {
82 IMAGE_EXPORT_DIRECTORY *exdir;
83 IMAGE_DATA_DIRECTORY *dir;
84 IMAGE_NT_HEADERS *ptr;
86 /* Hack for Windows XP SP2: Clear the LSB, the OS sets it for some reason */
87 mod = (HMODULE)((DWORD)mod & 0xFFFFF000UL);
90 if (mod == NULL) return NULL;
92 /* the module pointer should point an image of the DLL in memory. Right at the pointer we should see
93 * the letters "MZ" and the MS-DOS stub EXE header */
94 ptr = Win32ValidateHModuleMSDOS_PE_Header((BYTE*)mod);
95 if (ptr == NULL) return NULL;
97 /* OK, now locate the Data Directory. The number of entries is in ptr->OptionalHeader.NumberOfRvaAndSizes */
98 dir = Win32GetDataDirectory(ptr);
99 if (ptr == NULL) return NULL;
101 /* the first directory is the Export Address Table */
102 exdir = (IMAGE_EXPORT_DIRECTORY*)((DWORD)mod + (DWORD)dir->VirtualAddress);
103 if (IsBadReadPtr(exdir,2048)) return NULL;
106 *entries = exdir->NumberOfFunctions;
107 *base_addr = (DWORD)mod;
108 return (DWORD*)((DWORD)mod + exdir->AddressOfFunctions);
111 int Win32GetOrdinalLookupInfo(HMODULE mod,Win32OrdinalLookupInfo *info) {
112 DWORD *x = Win32GetExportOrdinalTable(mod,&info->entries,&info->base,&info->base_addr);
113 if (x == NULL) return 0;
118 void *Win32GetOrdinalAddress(Win32OrdinalLookupInfo *nfo,unsigned int ord) {
119 if (nfo == NULL || nfo->table == NULL) return NULL;
120 if (ord < nfo->base) return NULL;
121 if (ord >= (nfo->base+nfo->entries)) return NULL;
122 return (void*)((char*)nfo->table[ord-nfo->base] + nfo->base_addr);