Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

519 lines
16 KiB

  1. #include <nt.h>
  2. #include <ntrtl.h>
  3. #include <nturtl.h>
  4. #include <windows.h>
  5. //
  6. // constants for the registry
  7. const char c_szReg[] = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion";
  8. const char c_szVal[] = "CommonFilesDir";
  9. const char c_szPath[] = "\\Microsoft Shared\\MSLU\\";
  10. //
  11. // For our static list of DLLs we use
  12. struct DLLMap
  13. {
  14. const char *szDLLName;
  15. HMODULE hMod;
  16. };
  17. // Godot must be first in this list. The
  18. // rest can be in any order you like.
  19. struct DLLMap m_rgDLLMap [] =
  20. {
  21. {"unicows.dll", NULL },
  22. {"kernel32.dll", NULL },
  23. {"user32.dll", NULL },
  24. {"gdi32.dll", NULL },
  25. {"advapi32.dll", NULL }, // Must be kept in positon #4
  26. {"shell32.dll", NULL },
  27. {"comdlg32.dll", NULL },
  28. {"version.dll", NULL },
  29. {"mpr.dll", NULL },
  30. {"rasapi32.dll", NULL },
  31. {"winmm.dll", NULL },
  32. {"winspool.drv", NULL },
  33. {"avicap32.dll", NULL },
  34. {"secur32.dll", NULL },
  35. {"oleacc.dll", NULL },
  36. {"oledlg.dll", NULL },
  37. {"sensapi.dll", NULL },
  38. {"msvfw32.dll", NULL },
  39. {NULL, NULL }
  40. };
  41. //
  42. // An ENUM of possible platforms
  43. typedef enum
  44. {
  45. PlatformUntested, // Value at initialization
  46. PlatformUnicode, // Unicode (Windows NT 4.0, Windows 2000, Windows XP)
  47. PlatformNotUnicode // Non-Unicode (Windows 95, Windows 98, Windows ME)
  48. } GODOTUNICODE;
  49. // helper defines
  50. #define OUR_MAX_DRIVE 3 /* max. length of drive component */
  51. #define OUR_MAX_DIR 256 /* max. length of path component */
  52. // Get proc address forward declares
  53. FARPROC GetProcAddressInternal(HINSTANCE, PCHAR);
  54. PVOID ImageEntryToDataC(PVOID Base, USHORT DirectoryEntry, PULONG Size);
  55. USHORT GetOrdinal(PSZ Name, ULONG cNames, PVOID DllBase, PULONG NameTable, PUSHORT NameOrdinalTable);
  56. // other forward declares
  57. HMODULE LoadGodot(void);
  58. void driveandpathC(register const char *path, char *drive, char *dir);
  59. BOOL strcmpiC(LPSTR sz1, LPSTR sz2);
  60. char * strncpyC(char * dest, const char * source, size_t count);
  61. // Our global pfn that caller can set to
  62. // get a callback for the loading of Godot
  63. HMODULE (__stdcall *_PfnLoadUnicows)(void);
  64. #pragma intrinsic(strcmp, strlen, strcat)
  65. /*-------------------------------
  66. ResolveThunk
  67. The master loader function, used for all APIs on all platforms.
  68. -------------------------------*/
  69. void ResolveThunk(char *Name, char *Function, FARPROC *Ptr, FARPROC Override, FARPROC FailPtr)
  70. {
  71. static GODOTUNICODE UniPlatform = PlatformUntested;
  72. static HINSTANCE m_hinst = 0;
  73. FARPROC fp = NULL;
  74. HMODULE hMod;
  75. if (PlatformUntested == UniPlatform)
  76. {
  77. // We do not know if we are Unicode yet; lets find out since
  78. // this is pretty crucial in determining where we call from
  79. // Use GetVersion instead of GetVersionEx; its faster and it
  80. // tells us the only thing we care about here: Are we on NT?
  81. DWORD dwVersion = GetVersion();
  82. if (dwVersion < 0x80000000)
  83. UniPlatform = PlatformUnicode; // WinNT/Win2K/WinXP/...
  84. else
  85. UniPlatform = PlatformNotUnicode; // Win95/Win98/WinME
  86. }
  87. if (UniPlatform == PlatformUnicode)
  88. {
  89. int iMod;
  90. if(m_hinst == 0)
  91. {
  92. // Init our HINST var. Since we do not know the name of the binary
  93. // we are in we cannot use LoadLibrary(binaryname) and since we
  94. // may not be an EXE we cannot use GetModuleHandle(NULL), either.
  95. // Our method may seem like a hack, but it is the method that the
  96. // Shell uses to do the same thing (per RaymondC).
  97. // NOTE: Only needed for the NT case
  98. MEMORY_BASIC_INFORMATION mbi;
  99. VirtualQuery(&ResolveThunk, &mbi, sizeof mbi);
  100. m_hinst = (HINSTANCE)mbi.AllocationBase;
  101. }
  102. // Skip the first item in the array, which is the Godot dll.
  103. for(iMod=1 ; m_rgDLLMap[iMod].szDLLName != NULL ; iMod++)
  104. {
  105. if(strcmp(Name, m_rgDLLMap[iMod].szDLLName) == 0)
  106. {
  107. // This is the dll: see if we have loaded it yet
  108. if(m_rgDLLMap[iMod].hMod == 0)
  109. {
  110. // go ahead and load it
  111. hMod = LoadLibraryA(Name);
  112. if(InterlockedExchange((LPLONG)&m_rgDLLMap[iMod].hMod, (LONG)hMod) != 0)
  113. {
  114. // Some other thread beat us to it, lets unload our instance
  115. FreeLibrary(hMod);
  116. }
  117. }
  118. fp = GetProcAddressInternal(m_rgDLLMap[iMod].hMod, Function);
  119. break;
  120. }
  121. }
  122. // Should be impossible to get here, since we control dll names, etc.
  123. }
  124. else // (UniPlatform == PlatformNotUnicode)
  125. {
  126. // Check to see if they are overriding this function.
  127. // If they are, use the override and we will go no further.
  128. if(Override)
  129. {
  130. fp = Override;
  131. }
  132. else
  133. {
  134. if(m_rgDLLMap[0].hMod == 0)
  135. {
  136. if(hMod = LoadGodot())
  137. {
  138. if(InterlockedExchange((LPLONG)&m_rgDLLMap[0].hMod, (LONG)hMod) != 0)
  139. {
  140. // Some other thread beat us to it, lets unload our instance
  141. FreeLibrary(hMod);
  142. }
  143. }
  144. }
  145. if(m_rgDLLMap[0].hMod)
  146. {
  147. fp = GetProcAddressInternal(m_rgDLLMap[0].hMod, Function);
  148. }
  149. }
  150. }
  151. if (fp)
  152. {
  153. InterlockedExchangePointer(&(*Ptr), fp);
  154. }
  155. else
  156. {
  157. InterlockedExchangePointer(&(*Ptr), FailPtr);
  158. }
  159. // Flush cached information for the code we just modified (NT only). Note that
  160. // we call FlushInstructionCache on Win9x too, but only with a NULL hinst (to
  161. // force the i-cache to be flushed).
  162. FlushInstructionCache(m_hinst, Ptr, sizeof(FARPROC));
  163. return;
  164. }
  165. typedef LONG (_stdcall *PFNrokea) (HKEY, LPCSTR, DWORD, REGSAM, PHKEY);
  166. typedef LONG (_stdcall *PFNrqvea) (HKEY, LPCSTR, LPDWORD, LPDWORD, LPBYTE, LPDWORD);
  167. /*-------------------------------
  168. LoadGodot
  169. This function will never be called on NT, and will be called
  170. only once on Win9x if Godot can be found. If it cannot, we will
  171. call it every time, but what is the point of optimizing when
  172. every string API is going to fail? That would be like holding
  173. their hand when they walked the Green Mile!
  174. -------------------------------*/
  175. HMODULE LoadGodot(void)
  176. {
  177. HMODULE hMod = 0;
  178. if(_PfnLoadUnicows)
  179. hMod = (_PfnLoadUnicows());
  180. if(hMod==0)
  181. {
  182. hMod = LoadLibraryA(m_rgDLLMap[0].szDLLName);
  183. if(hMod)
  184. {
  185. // We want to disallow the ability to run Godot from the windows or
  186. // windows/system directory. The reason for this is that we want to
  187. // avoid a little private version of DLL Hell by having people stick
  188. // the DLL in such a shared location.
  189. // Note that if Godot itself is in the win or winsys dir with the
  190. // component, we do not fail validation; if we did, then no downlevel
  191. // system component could ever use Godot!
  192. char szGodotPath[MAX_PATH + 1];
  193. char szProcessPath[MAX_PATH + 1];
  194. char szWindowsPath[MAX_PATH + 1];
  195. char szSystemPath[MAX_PATH + 1];
  196. char drive[OUR_MAX_DRIVE];
  197. char dir[OUR_MAX_DIR];
  198. if(0 == GetSystemDirectory(szSystemPath, MAX_PATH))
  199. szSystemPath[0] = 0;
  200. if(0 == GetWindowsDirectory(szWindowsPath, MAX_PATH))
  201. szWindowsPath[0] = 0;
  202. if(0 == GetModuleFileName(hMod, szGodotPath, MAX_PATH))
  203. szGodotPath[0] = 0;
  204. if(0 == GetModuleFileName(GetModuleHandle(NULL), szProcessPath, MAX_PATH))
  205. szProcessPath[0] = 0;
  206. driveandpathC(szProcessPath, drive, dir);
  207. szProcessPath[strlen(drive) + strlen(dir) + 1] = 0;
  208. driveandpathC(szGodotPath, drive, dir);
  209. szGodotPath[strlen(drive) + strlen(dir) + 1] = 0;
  210. if(((strcmpiC(szWindowsPath, szGodotPath) == 0) &&
  211. (strcmpiC(szWindowsPath, szProcessPath) != 0)) ||
  212. ((strcmpiC(szSystemPath, szGodotPath) == 0) &&
  213. (strcmpiC(szSystemPath, szProcessPath) != 0)))
  214. {
  215. // We failed validation on the library we loaded.
  216. // Lets pretend we never loaded anything at all.
  217. FreeLibrary(hMod);
  218. hMod = 0;
  219. }
  220. }
  221. if(!hMod)
  222. {
  223. // Our straight attempt at load failed, so now we
  224. // fall back and try to load via the shared location:
  225. //
  226. // $(PROGRAM FILES)\$(COMMON FILES)\Microsoft Shared\MSLU
  227. HKEY hkey = NULL;
  228. HMODULE hModAdvapi = LoadLibraryA("advapi32.dll");
  229. // We delay load registry functions to keep from adding
  230. // an advapi32 dependency in case the user has none.
  231. PFNrokea pfnROKE;
  232. PFNrqvea pfnRQVE;
  233. if(InterlockedExchange((LPLONG)&m_rgDLLMap[4].hMod, (LONG)hModAdvapi) != 0)
  234. {
  235. // Some other thread beat us to it, lets unload our instance
  236. FreeLibrary(hModAdvapi);
  237. hModAdvapi = 0;
  238. }
  239. if(m_rgDLLMap[4].hMod)
  240. {
  241. pfnROKE = (PFNrokea)GetProcAddressInternal(m_rgDLLMap[4].hMod, "RegOpenKeyExA");
  242. pfnRQVE = (PFNrqvea)GetProcAddressInternal(m_rgDLLMap[4].hMod, "RegQueryValueExA");
  243. if(pfnROKE && pfnRQVE)
  244. {
  245. if (ERROR_SUCCESS == pfnROKE(HKEY_LOCAL_MACHINE, c_szReg, 0, KEY_QUERY_VALUE, &hkey))
  246. {
  247. char szName[MAX_PATH + 1];
  248. DWORD cb = MAX_PATH;
  249. szName[0] = '\0';
  250. if (ERROR_SUCCESS == pfnRQVE(hkey, c_szVal, NULL, NULL, (LPBYTE)szName, &cb))
  251. {
  252. // Call to get common files dir succeeded, lets build the path now
  253. strcat(szName, c_szPath);
  254. strcat(szName, m_rgDLLMap[0].szDLLName);
  255. hMod = LoadLibraryA(szName);
  256. }
  257. RegCloseKey(hkey);
  258. }
  259. }
  260. }
  261. }
  262. }
  263. return(hMod);
  264. }
  265. //
  266. //
  267. // Our own little GetProcAddress, taken from the actual NT
  268. // code base via the industrious coding skills of BryanT.
  269. //
  270. //
  271. /*-------------------------------
  272. GetProcAddressInternal
  273. -------------------------------*/
  274. FARPROC GetProcAddressInternal(HINSTANCE hDll, PCHAR szName)
  275. {
  276. PIMAGE_EXPORT_DIRECTORY pED;
  277. ULONG Size;
  278. PULONG NameTable, AddressTable;
  279. PUSHORT NameOrdinalTable;
  280. ULONG Ordinal, Address;
  281. if (!hDll || !szName)
  282. {
  283. // Bogus args
  284. return NULL;
  285. }
  286. pED = (PIMAGE_EXPORT_DIRECTORY)ImageEntryToDataC(hDll, IMAGE_DIRECTORY_ENTRY_EXPORT, &Size);
  287. if (!pED)
  288. {
  289. // No exports - very strange
  290. return NULL;
  291. }
  292. NameTable = (PULONG)((ULONG_PTR)hDll + (ULONG)pED->AddressOfNames);
  293. NameOrdinalTable = (PUSHORT)((ULONG_PTR)hDll + (ULONG)pED->AddressOfNameOrdinals);
  294. Ordinal = GetOrdinal(szName, pED->NumberOfNames, hDll, NameTable, NameOrdinalTable);
  295. if ((ULONG)Ordinal >= pED->NumberOfFunctions)
  296. {
  297. // No matches
  298. return NULL;
  299. }
  300. AddressTable = (PULONG)((ULONG_PTR)hDll + (ULONG)pED->AddressOfFunctions);
  301. Address = (ULONG_PTR)hDll + AddressTable[Ordinal];
  302. if ((Address > (ULONG_PTR)pED) && (Address < ((ULONG_PTR)pED + Size)))
  303. {
  304. // This is a forwarder - Ignore for now.
  305. return NULL;
  306. }
  307. return (FARPROC)Address;
  308. }
  309. /*-------------------------------
  310. GetOrdinal
  311. -------------------------------*/
  312. USHORT GetOrdinal(PSZ Name, ULONG cNames, PVOID DllBase, PULONG NameTable, PUSHORT NameOrdinalTable)
  313. {
  314. LONG High, Low, Middle, Result;
  315. // Lookup the import name in the name table using a binary search.
  316. Low = 0;
  317. High = cNames - 1;
  318. while (High >= Low)
  319. {
  320. // Compute the next probe index and compare the import name
  321. // with the export name entry.
  322. Middle = (Low + High) >> 1;
  323. Result = strcmp(Name, (PCHAR)((ULONG_PTR)DllBase + NameTable[Middle]));
  324. if (Result < 0)
  325. {
  326. High = Middle - 1;
  327. }
  328. else if (Result > 0)
  329. {
  330. Low = Middle + 1;
  331. }
  332. else
  333. {
  334. break;
  335. }
  336. }
  337. // If the high index is less than the low index, then a matching
  338. // table entry was not found. Otherwise, get the ordinal number
  339. // from the ordinal table.
  340. if (High < Low)
  341. return((USHORT)-1);
  342. else
  343. return(NameOrdinalTable[Middle]);
  344. }
  345. /*-------------------------------
  346. ImageEntryToDataC
  347. -------------------------------*/
  348. PVOID ImageEntryToDataC(PVOID Base, USHORT Entry, PULONG Size)
  349. {
  350. PIMAGE_NT_HEADERS NtHeader;
  351. ULONG Address;
  352. // Note that orindarily the line below would be a call
  353. // to RtlpImageNtHeader. In this case, however, we do
  354. // not need to be quite so generic since we control the
  355. // image.
  356. NtHeader = (PIMAGE_NT_HEADERS)((PCHAR)Base + ((PIMAGE_DOS_HEADER)Base)->e_lfanew);
  357. if ((!NtHeader) ||
  358. (Entry >= NtHeader->OptionalHeader.NumberOfRvaAndSizes) ||
  359. (!(Address = NtHeader->OptionalHeader.DataDirectory[ Entry ].VirtualAddress)))
  360. {
  361. *Size = 0;
  362. return NULL;
  363. }
  364. *Size = NtHeader->OptionalHeader.DataDirectory[ Entry ].Size;
  365. return((PVOID)((ULONG_PTR)Base + Address));
  366. }
  367. //
  368. //
  369. // Our helper functions that we use to keep from having a C runtime dependency
  370. // Note that such a dependency in unicows.lib could be a nightmare for users!
  371. //
  372. //
  373. /*-------------------------------
  374. strcmpiC
  375. -------------------------------*/
  376. BOOL strcmpiC(LPSTR sz1, LPSTR sz2)
  377. {
  378. return(CSTR_EQUAL == CompareStringA(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
  379. NORM_IGNORECASE,
  380. sz1,
  381. -1,
  382. sz2,
  383. -1));
  384. }
  385. /*-------------------------------
  386. driveandpathC
  387. Mostly stolen from the VC runtime's splitpath functions, though
  388. trimmed since we only care about the drive and the dir.
  389. -------------------------------*/
  390. void driveandpathC(register const char *path, char *drive, char *dir)
  391. {
  392. register char *p;
  393. char *last_slash = '\0';
  394. char *dot = '\0';
  395. unsigned len;
  396. if ((strlen(path) >= (OUR_MAX_DRIVE - 2)) && (*(path + OUR_MAX_DRIVE - 2) == ':')) {
  397. if (drive)
  398. {
  399. strncpyC(drive, path, OUR_MAX_DRIVE - 1);
  400. *(drive + OUR_MAX_DRIVE-1) = '\0';
  401. }
  402. path += OUR_MAX_DRIVE - 1;
  403. }
  404. else if (drive) {
  405. *drive = '\0';
  406. }
  407. for (last_slash = '\0', p = (char *)path; *p; p++)
  408. {
  409. if (*p == '/' || *p == '\\')
  410. last_slash = p + 1;
  411. else if (*p == '.')
  412. dot = p;
  413. }
  414. if (last_slash)
  415. {
  416. if (dir)
  417. {
  418. len = ((char *)last_slash - (char *)path) / sizeof(char);
  419. if ((OUR_MAX_DIR - 1) < len)
  420. len = (OUR_MAX_DIR - 1);
  421. strncpyC(dir, path, len);
  422. *(dir + len) = '\0';
  423. }
  424. path = last_slash;
  425. }
  426. else if (dir) {
  427. *dir = '\0';
  428. }
  429. }
  430. /*-------------------------------
  431. strncpyC
  432. Our little version of strncpy
  433. -------------------------------*/
  434. char * strncpyC(char * dest, const char * source, size_t count)
  435. {
  436. char *start = dest;
  437. while (count && (*dest++ = *source++)) /* copy string */
  438. count--;
  439. if (count) /* pad out with zeroes */
  440. while (--count)
  441. *dest++ = '\0';
  442. return(start);
  443. }