Leaked source code of windows server 2003
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.

648 lines
20 KiB

  1. /*++
  2. *
  3. * NTVDM v1.0
  4. *
  5. * Copyright (c) 2002, Microsoft Corporation
  6. *
  7. * VDPM.C
  8. * NTVDM Dynamic Patch Module support
  9. *
  10. * History:
  11. * Created 22-Jan-2002 by CMJones
  12. *
  13. --*/
  14. #define _VDPM_C_
  15. #define _DPM_COMMON_
  16. // The _VDPM_C_ definition allows the global instantiation of gDpmVdmFamTbls[]
  17. // and gDpmVdmModuleSets[] in NTVDM.EXE which are both defined in
  18. // mvdm\inc\dpmtbls.h
  19. //
  20. /* For the benefit of folks grepping for gDpmVdmFamTbls and gDpmVdmModuleSets:
  21. const PFAMILY_TABLE gDpmVdmFamTbls[] = // See above for true story.
  22. const PDPMMODULESETS gDpmVdmModuleSets[] = // See above for true story.
  23. */
  24. #include <nt.h>
  25. #include <ntrtl.h>
  26. #include <nturtl.h>
  27. #include <windows.h>
  28. #include <shlwapi.h>
  29. #include "shimdb.h"
  30. #include "dpmtbls.h"
  31. #include "vshimdb.h"
  32. #include "softpc.h"
  33. #include "sfc.h"
  34. #include "wowcmpat.h"
  35. #undef _VDPM_C_
  36. #undef _DPM_COMMON_
  37. extern DWORD dwDosCompatFlags;
  38. #define MAX_DOS_FILENAME 8+3+1+1 // max dos filename (incl. '.' char) + NULL
  39. #ifdef DBG
  40. #define VDBGPRINT(a) DbgPrint(a)
  41. #define VDBGPRINTANDBREAK(a) {DbgPrint(a); DbgBreakPoint();}
  42. #else
  43. #define VDBGPRINT(a)
  44. #define VDBGPRINTANDBREAK(a)
  45. #endif // DBG
  46. #define VMALLOC(s) (LocalAlloc(LPTR, s))
  47. #define VFREE(p) (LocalFree(p))
  48. // Global DATA
  49. // These can't be const because they will get changed when WOW and/or DOS loads.
  50. PFAMILY_TABLE *pgDpmVdmFamTbls = (PFAMILY_TABLE *)gDpmVdmFamTbls;
  51. PDPMMODULESETS *pgDpmModuleSets = (PDPMMODULESETS *)gDpmVdmModuleSets;
  52. LPSTR NeedToPatchSpecifiedModule(char *pszModuleName, PCMDLNPARMS pCmdLnParms);
  53. PFLAGINFOBITS GetFlagCommandLine(PVOID pFlagInfo, DWORD dwFlag, DWORD dwFlags);
  54. PCMDLNPARMS GetSdbCommandLineParams(LPWSTR pwszAppFilePath,
  55. DWORD *dwFlags,
  56. int *pNumCLP);
  57. // This function combines updates the Vdm tables with the WOW tables and sets
  58. // the global tables.
  59. // This will only be called when the WOWEXEC task initializes.
  60. void BuildGlobalDpmStuffForWow(PFAMILY_TABLE *pDpmWowFamTbls,
  61. PDPMMODULESETS *pDpmWowModuleSets)
  62. {
  63. // Update the task ptr to the family table array.
  64. DPMFAMTBLS() = pDpmWowFamTbls;
  65. pgDpmVdmFamTbls = pDpmWowFamTbls;
  66. // Change the pointer to the *process* module set array.
  67. pgDpmModuleSets = pDpmWowModuleSets;
  68. }
  69. char szAppPatch[] = "\\AppPatch\\";
  70. char szShimEngDll[] = "\\ShimEng.dll";
  71. // Called if the app requires Dynamic Patch Module(s) and/or shims to be linked
  72. // This returns void because if anything fails we can still run the app with
  73. // the default global tables.
  74. void InitTaskDpmSupport(int numHookedFams,
  75. PFAMILY_TABLE *pgDpmFamTbls,
  76. PCMDLNPARMS pCmdLnParms,
  77. PVOID hSdb,
  78. PVOID pSdbQuery,
  79. LPWSTR pwszAppFilePath,
  80. LPWSTR pwszAppModuleName,
  81. LPWSTR pwszTempEnv)
  82. {
  83. int i, len, wdLen;
  84. int cHookedFamilies = 0;
  85. int cApi = 0;
  86. char *pszDpmModuleName;
  87. NTSTATUS Status;
  88. HMODULE hMod;
  89. LPDPMINIT lpfnInit;
  90. PFAMILY_TABLE pFT;
  91. PFAMILY_TABLE *pTB;
  92. char szDpmModName[MAX_PATH];
  93. char szShimEng[MAX_PATH];
  94. // allocate an array of ptrs to family tables
  95. pTB = (PFAMILY_TABLE *)
  96. VMALLOC(numHookedFams * sizeof(PFAMILY_TABLE));
  97. if(!pTB) {
  98. VDBGPRINTANDBREAK("NTVDM::InitTaskDpmSupport:VMALLOC 1 failed\n");
  99. goto ErrorExit;
  100. }
  101. wdLen = GetSystemWindowsDirectory(szDpmModName, MAX_PATH-1);
  102. strcat(szDpmModName, szAppPatch);
  103. wdLen += (sizeof(szAppPatch)/sizeof(char)) - 1;
  104. for(i = 0; i < numHookedFams; i++) {
  105. // see if we want this patch module for this app
  106. if(pszDpmModuleName = NeedToPatchSpecifiedModule(
  107. (char *)pgDpmModuleSets[i]->DpmFamilyType,
  108. pCmdLnParms)) {
  109. szDpmModName[wdLen] = '\0'; // set back to "c:\windows\AppPatch\"
  110. // Append dpm module.dll to "C:\windows\AppPatch\"
  111. len = strlen(pszDpmModuleName) + wdLen;
  112. len++; // NULL char
  113. if(len > MAX_PATH) {
  114. goto UseGlobal;
  115. }
  116. strcat(szDpmModName, pszDpmModuleName);
  117. hMod = LoadLibrary(szDpmModName);
  118. if(hMod == NULL) {
  119. VDBGPRINT("NTVDM::InitTaskDpmSupport:LoadLibrary failed");
  120. goto UseGlobal;
  121. }
  122. lpfnInit = (LPDPMINIT)GetProcAddress(hMod, "DpmInitFamTable");
  123. if(lpfnInit) {
  124. // Call the family table init function & get a ptr to the
  125. // hooked family table for this task.
  126. pFT = (lpfnInit)(pgDpmFamTbls[i],
  127. hMod,
  128. (PVOID)hSdb,
  129. (PVOID)pSdbQuery,
  130. pwszAppFilePath,
  131. pgDpmModuleSets[i]);
  132. if(pFT) {
  133. pTB[i] = pFT;
  134. cHookedFamilies++;
  135. cApi += pFT->numHookedAPIs;
  136. }
  137. // else use the global table for this family
  138. else {
  139. VDBGPRINT("NTVDM::InitTaskDpmSupport: Init failed");
  140. goto UseGlobal;
  141. }
  142. }
  143. // else use the global table for this family
  144. else {
  145. VDBGPRINT("NTVDM::InitTaskDpmSupport:GetAddr failed");
  146. // If anything above fails just use the default global table for this family
  147. UseGlobal:
  148. VDBGPRINT(" -- Using global table entry\n");
  149. pTB[i] = pgDpmFamTbls[i];
  150. }
  151. }
  152. // else this task doesn't require this family patch -- use the global
  153. // table for this family
  154. else {
  155. pTB[i] = pgDpmFamTbls[i];
  156. }
  157. }
  158. // now patch the DPM tables into the VDM TIB for this task
  159. DPMFAMTBLS() = pTB;
  160. return;
  161. ErrorExit:
  162. FreeTaskDpmSupport(pTB, numHookedFams, pgDpmFamTbls);
  163. return;
  164. }
  165. VOID FreeTaskDpmSupport(PFAMILY_TABLE *pDpmFamTbls,
  166. int numHookedFams,
  167. PFAMILY_TABLE *pgDpmFamTbls)
  168. {
  169. int i;
  170. HMODULE hMod;
  171. LPDPMDESTROY lpfnDestroy;
  172. PFAMILY_TABLE pFT;
  173. // if this task is using the global tables, nothing to do
  174. if(!pDpmFamTbls || pDpmFamTbls == pgDpmFamTbls)
  175. return;
  176. // anything this task does from here on out gets to use the global tables
  177. DPMFAMTBLS() = pgDpmFamTbls;
  178. for(i = 0; i < numHookedFams; i++) {
  179. pFT = pDpmFamTbls[i];
  180. hMod = pFT->hMod;
  181. // only free the table if it isn't the global table for this family
  182. if(pFT && (pFT != pgDpmFamTbls[i])) {
  183. // call the DPM destroy function
  184. lpfnDestroy = (LPDPMDESTROY)GetProcAddress(hMod,
  185. "DpmDestroyFamTable");
  186. (lpfnDestroy)(pgDpmFamTbls[i], pFT);
  187. FreeLibrary(hMod);
  188. }
  189. }
  190. VFREE(pDpmFamTbls);
  191. }
  192. // This takes a pszFamilyType="DPMFIO" type string and extracts the asscociated
  193. // .dll from the DBU.XML command line parameter.
  194. // For example:
  195. // pCmdLnParms->argv[0]="DPMFIO=dpmfio2.dll"
  196. // It will return a ptr to "dpmfio2.dll" for the example above.
  197. // See the notes for DpmFamilyType in mvdm\inc\dpmtbls.h
  198. LPSTR NeedToPatchSpecifiedModule(char *pszFamilyType, PCMDLNPARMS pCmdLnParms)
  199. {
  200. int i;
  201. char **pArgv;
  202. char *p;
  203. if(pCmdLnParms) {
  204. pArgv = pCmdLnParms->argv;
  205. if(pArgv && pCmdLnParms->argc > 0) {
  206. for(i = 0; i < pCmdLnParms->argc; i++) {
  207. // find the '=' char
  208. p = strchr(*pArgv, '=');
  209. if(NULL != p) {
  210. // compare string up to, but not including, the '=' char
  211. if(!_strnicmp(*pArgv, pszFamilyType, p-*pArgv)) {
  212. // return ptr to char after the '=' char
  213. return(++p);
  214. }
  215. }
  216. else {
  217. // The command line params for the WOWCF2_DPM_PATCHES compat
  218. // flag aren't correct.
  219. VDBGPRINT("NTVDM::NeedToPatchSpecifiedModule: no '=' char!\n");
  220. }
  221. pArgv++;
  222. }
  223. }
  224. }
  225. return(NULL);
  226. }
  227. void InitGlobalDpmTables(PFAMILY_TABLE *pgDpmFamTbls,
  228. int numHookedFams)
  229. {
  230. int i, j;
  231. PVOID lpfn;
  232. HMODULE hMod;
  233. PFAMILY_TABLE pFT;
  234. // Build the table for each API family we hook.
  235. for(i = 0; i < numHookedFams; i++) {
  236. pFT = pgDpmFamTbls[i];
  237. pFT->hModShimEng = NULL;
  238. // For now we're assuming that the module is already loaded. We'll
  239. // have to deal with dynamically loaded modules in the future.
  240. hMod = GetModuleHandle((pgDpmModuleSets[i])->ApiModuleName);
  241. pFT->hMod = hMod;
  242. pFT->pDpmShmTbls = NULL;
  243. pFT->DpmMisc = NULL;
  244. if(hMod) {
  245. for(j = 0; j < pFT->numHookedAPIs; j++) {
  246. // get the *real* API address...
  247. lpfn = (PVOID)GetProcAddress(hMod,
  248. (pgDpmModuleSets[i])->ApiNames[j]);
  249. // ...and save it in the family table, otherwise we continue to
  250. // use the one we statically linked in the import table(s).
  251. if(lpfn) {
  252. pFT->pfn[j] = lpfn;
  253. }
  254. }
  255. }
  256. }
  257. }
  258. // Get the DOS app compat flags & the associated command line params from
  259. // the app compat SDB.
  260. PCMDLNPARMS InitVdmSdbInfo(LPCSTR pszAppName, DWORD *pdwFlags, int *pNumCLP)
  261. {
  262. int len;
  263. PCMDLNPARMS pCmdLnParms = NULL;
  264. NTSTATUS st;
  265. ANSI_STRING AnsiString;
  266. UNICODE_STRING UnicodeString;
  267. len = strlen(pszAppName);
  268. if((len > 0) && (len < MAX_PATH)) {
  269. if(RtlCreateUnicodeStringFromAsciiz(&UnicodeString, pszAppName)) {
  270. // Get the SDB compatibility flag command line parameters (not to be
  271. // confused with the DOS command line!)
  272. pCmdLnParms = GetSdbCommandLineParams(UnicodeString.Buffer,
  273. pdwFlags,
  274. pNumCLP);
  275. RtlFreeUnicodeString(&UnicodeString);
  276. }
  277. }
  278. return(pCmdLnParms);
  279. }
  280. // Gets the command line params associated with dwFlag (from WOWCOMPATFLAGS2
  281. // flag set). It is parsed into argv, argc form using ';' as the delimiter.
  282. PCMDLNPARMS GetSdbCommandLineParams(LPWSTR pwszAppFilePath,
  283. DWORD *dwFlags,
  284. int *pNumCLP)
  285. {
  286. int i, numFlags;
  287. BOOL fReturn = TRUE;
  288. NTVDM_FLAGS NtVdmFlags = { 0 };
  289. DWORD dwMask;
  290. WCHAR *pwszTempEnv = NULL;
  291. WCHAR *pwszAppModuleName;
  292. WCHAR szFileNameW[MAX_DOS_FILENAME];
  293. PFLAGINFOBITS pFIB = NULL;
  294. HSDB hSdb = NULL;
  295. SDBQUERYRESULT SdbQuery;
  296. WCHAR wszCompatLayer[COMPATLAYERMAXLEN];
  297. APPHELP_INFO AHInfo;
  298. PCMDLNPARMS pCLP;
  299. PCMDLNPARMS pCmdLnParms = NULL;
  300. *pNumCLP = 0;
  301. pwszTempEnv = GetEnvironmentStringsW();
  302. if(pwszTempEnv) {
  303. // Strip off the path (DOS apps use the filename.exe as Module name
  304. // in the SDB).
  305. pwszAppModuleName = wcsrchr(pwszAppFilePath, L'\\');
  306. if(pwszAppModuleName == NULL) {
  307. pwszAppModuleName = pwszAppFilePath;
  308. }
  309. else {
  310. pwszAppModuleName++; // advance past the '\' char
  311. }
  312. wcsncpy(szFileNameW, pwszAppModuleName, MAX_DOS_FILENAME);
  313. wszCompatLayer[0] = UNICODE_NULL;
  314. AHInfo.tiExe = 0;
  315. fReturn = ApphelpGetNTVDMInfo(pwszAppFilePath,
  316. szFileNameW,
  317. pwszTempEnv,
  318. wszCompatLayer,
  319. &NtVdmFlags,
  320. &AHInfo,
  321. &hSdb,
  322. &SdbQuery);
  323. if(fReturn) {
  324. *dwFlags = NtVdmFlags.dwWOWCompatFlags2;
  325. // find out how many compat flags are set for this app
  326. numFlags = 0;
  327. dwMask = 0x80000000;
  328. while(dwMask) {
  329. if(dwMask & *dwFlags) {
  330. numFlags++;
  331. }
  332. dwMask = dwMask >> 1;
  333. }
  334. if(numFlags) {
  335. // Alloc maximum number of CMDLNPARMS structs we *might* need.
  336. pCLP = (PCMDLNPARMS)VMALLOC(numFlags * sizeof(CMDLNPARMS));
  337. if(pCLP) {
  338. // Get all the command line params associated with all the
  339. // app compat flags associated with this app.
  340. numFlags = 0;
  341. dwMask = 0x80000000;
  342. while(dwMask) {
  343. if(dwMask & *dwFlags) {
  344. // Get command line params associated with this flag
  345. pFIB = GetFlagCommandLine(NtVdmFlags.pFlagsInfo,
  346. dwMask,
  347. *dwFlags);
  348. // If there are any, save them.
  349. if(pFIB) {
  350. pCLP[numFlags].argc = pFIB->dwFlagArgc;
  351. pCLP[numFlags].argv = pFIB->pFlagArgv;
  352. pCLP[numFlags].dwFlag = dwMask;
  353. VFREE(pFIB);
  354. numFlags++;
  355. }
  356. }
  357. dwMask = dwMask >> 1;
  358. }
  359. // Now alloc *actual* number of CMDLNPARMS structs we need.
  360. if(numFlags > 0) {
  361. pCmdLnParms =
  362. (PCMDLNPARMS)VMALLOC(numFlags * sizeof(CMDLNPARMS));
  363. if(pCmdLnParms) {
  364. // Save everything we found in one neat package.
  365. RtlCopyMemory(pCmdLnParms,
  366. pCLP,
  367. numFlags * sizeof(CMDLNPARMS));
  368. *pNumCLP = numFlags;
  369. }
  370. }
  371. VFREE(pCLP);
  372. }
  373. }
  374. // If we need Dynamic Patch Module support for this app...
  375. if((*dwFlags & WOWCF2_DPM_PATCHES) && (*pNumCLP > 0)) {
  376. for(i = 0; i < *pNumCLP; i++) {
  377. if(pCmdLnParms[i].dwFlag == WOWCF2_DPM_PATCHES) {
  378. InitTaskDpmSupport(NUM_VDM_FAMILIES_HOOKED,
  379. DPMFAMTBLS(),
  380. &pCmdLnParms[i],
  381. hSdb,
  382. &SdbQuery,
  383. pwszAppFilePath,
  384. pwszAppModuleName,
  385. pwszTempEnv);
  386. break;
  387. }
  388. }
  389. }
  390. FreeEnvironmentStringsW(pwszTempEnv);
  391. if (hSdb != NULL) {
  392. SdbReleaseDatabase(hSdb);
  393. }
  394. SdbFreeFlagInfo(NtVdmFlags.pFlagsInfo);
  395. }
  396. }
  397. return(pCmdLnParms);
  398. }
  399. // Retrieves the SDB command line associated with dwFlag. The command line is
  400. // parsed into argv, argc form based on ';' delimiters.
  401. PFLAGINFOBITS GetFlagCommandLine(PVOID pFlagInfo, DWORD dwFlag, DWORD dwFlags)
  402. {
  403. UNICODE_STRING uCmdLine = { 0 };
  404. OEM_STRING oemCmdLine = { 0 };
  405. LPWSTR lpwCmdLine = NULL;
  406. LPSTR pszTmp;
  407. PFLAGINFOBITS pFlagInfoBits = NULL;
  408. LPSTR pszCmdLine = NULL;
  409. LPSTR *pFlagArgv = NULL;
  410. DWORD dwFlagArgc;
  411. if(pFlagInfo == NULL || 0 == dwFlags) {
  412. return NULL;
  413. }
  414. if(dwFlags & dwFlag) {
  415. GET_WOWCOMPATFLAGS2_CMDLINE(pFlagInfo, dwFlag, &lpwCmdLine);
  416. // Convert to oem string
  417. if(lpwCmdLine) {
  418. RtlInitUnicodeString(&uCmdLine, lpwCmdLine);
  419. pszCmdLine = VMALLOC(uCmdLine.Length + 1);
  420. if(NULL == pszCmdLine) {
  421. goto GFIerror;
  422. }
  423. oemCmdLine.Buffer = pszCmdLine;
  424. oemCmdLine.MaximumLength = uCmdLine.Length + 1;
  425. oemCmdLine.Length = uCmdLine.Length/sizeof(WCHAR);
  426. RtlUnicodeStringToOemString(&oemCmdLine, &uCmdLine, FALSE);
  427. pFlagInfoBits = VMALLOC(sizeof(FLAGINFOBITS));
  428. if(NULL == pFlagInfoBits) {
  429. goto GFIerror;
  430. }
  431. pFlagInfoBits->pNextFlagInfoBits = NULL;
  432. pFlagInfoBits->dwFlag = dwFlag;
  433. pFlagInfoBits->dwFlagType = WOWCOMPATFLAGS2;
  434. pFlagInfoBits->pszCmdLine = pszCmdLine;
  435. // Parse commandline to argv, argc format
  436. dwFlagArgc = 1;
  437. pszTmp = pszCmdLine;
  438. while(*pszTmp) {
  439. if(*pszTmp == ';') {
  440. dwFlagArgc++;
  441. }
  442. pszTmp++;
  443. }
  444. pFlagInfoBits->dwFlagArgc = dwFlagArgc;
  445. pFlagArgv = VMALLOC(sizeof(LPSTR) * dwFlagArgc);
  446. if(NULL == pFlagArgv) {
  447. goto GFIerror;
  448. }
  449. pFlagInfoBits->pFlagArgv = pFlagArgv;
  450. pszTmp = pszCmdLine;
  451. while(*pszTmp) {
  452. if(*pszTmp == ';'){
  453. if(pszCmdLine != pszTmp) {
  454. *pFlagArgv++ = pszCmdLine;
  455. }
  456. else {
  457. *pFlagArgv++ = NULL;
  458. }
  459. *pszTmp = '\0';
  460. pszCmdLine = pszTmp+1;
  461. }
  462. pszTmp++;
  463. }
  464. *pFlagArgv = pszCmdLine;
  465. }
  466. }
  467. return pFlagInfoBits;
  468. GFIerror:
  469. if(pszCmdLine) {
  470. VFREE(pszCmdLine);
  471. }
  472. if(pFlagInfoBits) {
  473. VFREE(pFlagInfoBits);
  474. }
  475. if(pFlagArgv) {
  476. VFREE(pFlagArgv);
  477. }
  478. return NULL;
  479. }
  480. VOID FreeCmdLnParmStructs(PCMDLNPARMS pCmdLnParms, int cCmdLnParmStructs)
  481. {
  482. int i;
  483. if(pCmdLnParms) {
  484. for(i = 0; i < cCmdLnParmStructs; i++) {
  485. if(pCmdLnParms[i].argv) {
  486. if(pCmdLnParms[i].argv[0]) {
  487. // Free the command line string
  488. VFREE(pCmdLnParms[i].argv[0]);
  489. }
  490. // now free the argv array
  491. VFREE(pCmdLnParms[i].argv);
  492. }
  493. }
  494. // now free the entire command line parameters array
  495. VFREE(pCmdLnParms);
  496. }
  497. }