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.

548 lines
18 KiB

  1. /*++
  2. Copyright (c) 2000-2001 Microsoft Corporation
  3. Module Name:
  4. EmulateCreateProcess.cpp
  5. Abstract:
  6. This shim cleans up the StartupInfo data structure to prevent NT from
  7. access violating due to uninitialized members.
  8. It also performs a little cleanup of lpApplicationName and lpCommandLine
  9. Win9x uses short file names internally, so applications do not
  10. have any problem skipping the application name (first arg) on the command line;
  11. they typically skip to the first blank.
  12. History:
  13. 11/22/1999 v-johnwh Created
  14. 04/11/2000 a-chcoff Updated to quote lpCommandLine Properly.
  15. 05/03/2000 robkenny Skip leading white space in lpApplicationName and lpCommandLine
  16. 10/09/2000 robkenny Shim was placing quotes around lpCommandLine if it contained spaces,
  17. this is totally wrong. Since I could not find the app that required this,
  18. I removed it entirely from the shim.
  19. 03/09/2001 robkenny Merged in CorrectCreateProcess16Bit
  20. 03/15/2001 robkenny Converted to CString
  21. 05/21/2001 pierreys Changes to DOS file handling to match 9X more precisely
  22. --*/
  23. #include "precomp.h"
  24. IMPLEMENT_SHIM_BEGIN(EmulateCreateProcess)
  25. #include "ShimHookMacro.h"
  26. APIHOOK_ENUM_BEGIN
  27. APIHOOK_ENUM_ENTRY(CreateProcessA)
  28. APIHOOK_ENUM_ENTRY(CreateProcessW)
  29. APIHOOK_ENUM_ENTRY(WinExec)
  30. APIHOOK_ENUM_END
  31. BOOL g_bShortenExeOnCommandLine = FALSE;
  32. /*++
  33. Clean parameters so we don't AV
  34. --*/
  35. BOOL
  36. APIHOOK(CreateProcessA)(
  37. LPCSTR lpApplicationName,
  38. LPSTR lpCommandLine,
  39. LPSECURITY_ATTRIBUTES lpProcessAttributes,
  40. LPSECURITY_ATTRIBUTES lpThreadAttributes,
  41. BOOL bInheritHandles,
  42. DWORD dwCreationFlags,
  43. LPVOID lpEnvironment,
  44. LPCSTR lpCurrentDirectory,
  45. LPSTARTUPINFOA lpStartupInfo,
  46. LPPROCESS_INFORMATION lpProcessInformation
  47. )
  48. {
  49. DPFN(
  50. eDbgLevelSpew,
  51. "[CreateProcessA] (%s) (%s)\n",
  52. (lpApplicationName ? lpApplicationName : "null"),
  53. (lpCommandLine ? lpCommandLine : "null"));
  54. BOOL bStat = FALSE;
  55. DWORD dwBinaryType;
  56. CSTRING_TRY
  57. {
  58. CString csOrigAppName(lpApplicationName);
  59. CString csOrigCommand(lpCommandLine);
  60. CString csAppName(csOrigAppName);
  61. CString csCommand(csOrigCommand);
  62. // Skip leading blanks.
  63. csAppName.TrimLeft();
  64. csCommand.TrimLeft();
  65. // Clean up lpStartupInfo
  66. if (lpStartupInfo)
  67. {
  68. if (lpStartupInfo->lpReserved ||
  69. lpStartupInfo->cbReserved2 ||
  70. lpStartupInfo->lpReserved2 ||
  71. lpStartupInfo->lpDesktop ||
  72. ((lpStartupInfo->dwFlags & STARTF_USESTDHANDLES) == 0 &&
  73. (lpStartupInfo->hStdInput ||
  74. lpStartupInfo->hStdOutput ||
  75. lpStartupInfo->hStdError)))
  76. {
  77. LOGN(
  78. eDbgLevelError,
  79. "[CreateProcessA] Bad params. Sanitized.");
  80. }
  81. //
  82. // Make sure that the parameters that can cause an access violation are
  83. // set correctly
  84. //
  85. lpStartupInfo->lpReserved = NULL;
  86. lpStartupInfo->cbReserved2 = 0;
  87. lpStartupInfo->lpReserved2 = NULL;
  88. if ((lpStartupInfo->dwFlags & STARTF_USESTDHANDLES) == 0)
  89. {
  90. lpStartupInfo->hStdInput = NULL;
  91. lpStartupInfo->hStdOutput = NULL;
  92. lpStartupInfo->hStdError = NULL;
  93. }
  94. lpStartupInfo->lpDesktop = NULL;
  95. }
  96. AppAndCommandLine acl(csAppName, csCommand);
  97. // Win9X has a rather weird behavihor: if the app is non-Win32 (Non-Console
  98. // and non GUI), it will use CreateProcessNonWin32. This will first check
  99. // if it is for a batch file, and if so it will prepend "command /c" and
  100. // continue on. Then there is going to be some weird creation of a
  101. // REDIR32.EXE process, but that is just to make sure we have a new win32
  102. // context. It will then use ExecWin16Program. The biggest weirdness is in
  103. // its QuoteAppName call. This procedure make sure that if the appname has
  104. // a space and is in the cmdline, it gets quoted. The appname, in all cases,
  105. // is then discarded (it is expected that the first part of the command line
  106. // contains the app name). So if someone like in b#373980 passes ("command",
  107. // "setup", ... then 9X ends up dropping the command portion entirely since
  108. // it is not part of the commandline.
  109. // 16-bit process must have NULL lpAppName
  110. if (!csAppName.IsEmpty() &&
  111. GetBinaryTypeW(csAppName.Get(), &dwBinaryType) == TRUE)
  112. {
  113. switch (dwBinaryType)
  114. {
  115. case SCS_DOS_BINARY:
  116. // Implementing the process.c's QuoteAppName check.
  117. // If this function would return NULL, then only
  118. // the cmdline would be used. Otherwise the new
  119. // pszCmdFinal is used.
  120. // QuoteAppName
  121. // Look for white space in app name. If we find any then we have to
  122. // quote the app name portion of cmdline.
  123. //
  124. // LPSTR
  125. // KERNENTRY
  126. // QuoteAppName(
  127. // LPCSTR pszAppName,
  128. // LPCSTR pszCmdLine)
  129. // {
  130. // LPSTR pch;
  131. // LPSTR pszApp;
  132. // LPSTR pszCmdFinal = NULL;
  133. //
  134. // // Check that there is an app name, not already quoted in the cmd line.
  135. // if( pszAppName && pszCmdLine && (*pszCmdLine != '\"')) {
  136. // // search for white space
  137. // for( pszApp = (LPSTR)pszAppName; *pszApp > ' '; pszApp++) ;
  138. //
  139. // if( *pszApp) { // found white space
  140. // // make room for the original cmd line plus 2 '"' + 0 terminator
  141. // pch = pszCmdFinal = HeapAlloc( hheapKernel, 0,
  142. // CbSizeSz( pszCmdLine)+3);
  143. // if( pch) {
  144. // *pch++ = '\"'; // beginning dbl-quote
  145. // for( pszApp = (LPSTR)pszAppName;
  146. // *pszApp && *pszApp == *pszCmdLine;
  147. // pszCmdLine++)
  148. // *pch++ = *pszApp++;
  149. // if( !( *pszApp)) {
  150. // *pch++ = '\"'; // trailing dbl-quote
  151. // strcpy( pch, pszCmdLine);
  152. // } else {
  153. // // app name and cmd line did not match
  154. // HeapFree( hheapKernel, 0, pszCmdFinal);
  155. // pszCmdFinal = NULL;
  156. // }
  157. // }
  158. // }
  159. // }
  160. // return pszCmdFinal;
  161. //}
  162. if ( /* app name already checked to be non empty */ !csCommand.IsEmpty() && (csCommand.Get())[0]!='\"')
  163. {
  164. if (csAppName.Find(L' ')!=-1)
  165. {
  166. int iAppLength=csAppName.GetLength();
  167. if (csCommand.Find(csAppName)==0)
  168. {
  169. CString csCmdFinal=L"\"";
  170. csCmdFinal += csAppName;
  171. csCmdFinal += L"\"";
  172. csCmdFinal += csCommand.Mid(iAppLength);
  173. csCommand = csCmdFinal;
  174. LOGN( eDbgLevelSpew,
  175. "[CreateProcessA] Weird quoted case: cmdline %s converted to %S",
  176. lpCommandLine,
  177. csCommand.Get());
  178. }
  179. }
  180. }
  181. LOGN( eDbgLevelSpew,
  182. "[CreateProcessA] DOS file case: not using appname %s, just cmdline %s, converted to %S",
  183. lpApplicationName,
  184. lpCommandLine,
  185. csCommand.Get());
  186. csAppName.Empty();
  187. //
  188. // The old code in non-WOW case would do this.
  189. //
  190. if (g_bShortenExeOnCommandLine)
  191. {
  192. csCommand = acl.GetShortCommandLine();
  193. }
  194. break;
  195. case SCS_WOW_BINARY:
  196. //
  197. // This is the old code. Accoring to 9X, we should be doing
  198. // the same as DOS, but we obviously found an app that
  199. // needed this.
  200. //
  201. csCommand = csAppName;
  202. csCommand.GetShortPathNameW();
  203. csCommand += L' ';
  204. csCommand += acl.GetCommandlineNoAppName();
  205. csAppName.Empty();
  206. break;
  207. default:
  208. //
  209. // The old code in non-WOW case would do this.
  210. //
  211. if (g_bShortenExeOnCommandLine)
  212. {
  213. csCommand = acl.GetShortCommandLine();
  214. }
  215. break;
  216. }
  217. }
  218. else if (g_bShortenExeOnCommandLine)
  219. {
  220. csCommand = acl.GetShortCommandLine();
  221. }
  222. LPCSTR lpNewApplicationName = csAppName.GetAnsiNIE();
  223. LPSTR lpNewCommandLine = csCommand.GetAnsiNIE();
  224. // Log any changes
  225. if (csOrigAppName != csAppName)
  226. {
  227. LOGN(
  228. eDbgLevelError,
  229. "[CreateProcessA] Sanitized lpApplicationName (%s) to (%s)",
  230. lpApplicationName, lpNewApplicationName);
  231. }
  232. if (csOrigCommand != csCommand)
  233. {
  234. LOGN(
  235. eDbgLevelError,
  236. "[CreateProcessA] Sanitized lpCommandLine (%s) to (%s)",
  237. lpCommandLine, lpNewCommandLine);
  238. }
  239. bStat = ORIGINAL_API(CreateProcessA)(
  240. lpNewApplicationName,
  241. lpNewCommandLine,
  242. lpProcessAttributes,
  243. lpThreadAttributes,
  244. bInheritHandles,
  245. dwCreationFlags,
  246. lpEnvironment,
  247. lpCurrentDirectory,
  248. lpStartupInfo,
  249. lpProcessInformation);
  250. }
  251. CSTRING_CATCH
  252. {
  253. bStat = ORIGINAL_API(CreateProcessA)(
  254. lpApplicationName,
  255. lpCommandLine,
  256. lpProcessAttributes,
  257. lpThreadAttributes,
  258. bInheritHandles,
  259. dwCreationFlags,
  260. lpEnvironment,
  261. lpCurrentDirectory,
  262. lpStartupInfo,
  263. lpProcessInformation);
  264. }
  265. return bStat;
  266. }
  267. /*++
  268. Clean parameters so we don't AV
  269. --*/
  270. BOOL
  271. APIHOOK(CreateProcessW)(
  272. LPCWSTR lpApplicationName,
  273. LPWSTR lpCommandLine,
  274. LPSECURITY_ATTRIBUTES lpProcessAttributes,
  275. LPSECURITY_ATTRIBUTES lpThreadAttributes,
  276. BOOL bInheritHandles,
  277. DWORD dwCreationFlags,
  278. LPVOID lpEnvironment,
  279. LPCWSTR lpCurrentDirectory,
  280. LPSTARTUPINFOW lpStartupInfo,
  281. LPPROCESS_INFORMATION lpProcessInformation
  282. )
  283. {
  284. DPFN(
  285. eDbgLevelSpew,
  286. "[CreateProcessW] (%S) (%S)\n",
  287. (lpApplicationName ? lpApplicationName : L"null"),
  288. (lpCommandLine ? lpCommandLine : L"null"));
  289. BOOL bStat = FALSE;
  290. CSTRING_TRY
  291. {
  292. CString csAppName(lpApplicationName);
  293. CString csCommand(lpCommandLine);
  294. // Skip leading blanks.
  295. csAppName.TrimLeft();
  296. csCommand.TrimLeft();
  297. // Clean up lpStartupInfo
  298. if (lpStartupInfo)
  299. {
  300. if (lpStartupInfo->lpReserved ||
  301. lpStartupInfo->cbReserved2 ||
  302. lpStartupInfo->lpReserved2 ||
  303. lpStartupInfo->lpDesktop ||
  304. ((lpStartupInfo->dwFlags & STARTF_USESTDHANDLES) == 0 &&
  305. (lpStartupInfo->hStdInput ||
  306. lpStartupInfo->hStdOutput ||
  307. lpStartupInfo->hStdError)))
  308. {
  309. LOGN(
  310. eDbgLevelError,
  311. "[CreateProcessW] Bad params. Sanitized.");
  312. }
  313. //
  314. // Make sure that the parameters that can cause an access violation are
  315. // set correctly
  316. //
  317. lpStartupInfo->lpReserved = NULL;
  318. lpStartupInfo->cbReserved2 = 0;
  319. lpStartupInfo->lpReserved2 = NULL;
  320. if ((lpStartupInfo->dwFlags & STARTF_USESTDHANDLES) == 0)
  321. {
  322. lpStartupInfo->hStdInput = NULL;
  323. lpStartupInfo->hStdOutput = NULL;
  324. lpStartupInfo->hStdError = NULL;
  325. }
  326. lpStartupInfo->lpDesktop = NULL;
  327. }
  328. AppAndCommandLine acl(csAppName, csCommand);
  329. // 16-bit process must have NULL lpAppName
  330. if (!csAppName.IsEmpty() && IsImage16BitW(csAppName.Get()))
  331. {
  332. csCommand = csAppName;
  333. csCommand.GetShortPathNameW();
  334. csCommand += L' ';
  335. csCommand += acl.GetCommandlineNoAppName();
  336. csAppName.Empty();
  337. }
  338. else if (g_bShortenExeOnCommandLine)
  339. {
  340. csCommand = acl.GetShortCommandLine();
  341. }
  342. LPCWSTR lpNewApplicationName = csAppName.GetNIE();
  343. LPWSTR lpNewCommandLine = (LPWSTR) csCommand.GetNIE(); // stupid api doesn't take const
  344. // Log any changes
  345. if (lpApplicationName && lpNewApplicationName && _wcsicmp(lpApplicationName, lpNewApplicationName) != 0)
  346. {
  347. LOGN(
  348. eDbgLevelError,
  349. "[CreateProcessW] Sanitized lpApplicationName (%S) to (%S)",
  350. lpApplicationName, lpNewApplicationName);
  351. }
  352. if (lpCommandLine && lpNewCommandLine && _wcsicmp(lpCommandLine, lpNewCommandLine) != 0)
  353. {
  354. LOGN(
  355. eDbgLevelError,
  356. "[CreateProcessW] Sanitized lpCommandLine (%S) to (%S)",
  357. lpCommandLine, lpNewCommandLine);
  358. }
  359. bStat = ORIGINAL_API(CreateProcessW)(
  360. lpNewApplicationName,
  361. lpNewCommandLine,
  362. lpProcessAttributes,
  363. lpThreadAttributes,
  364. bInheritHandles,
  365. dwCreationFlags,
  366. lpEnvironment,
  367. lpCurrentDirectory,
  368. lpStartupInfo,
  369. lpProcessInformation);
  370. }
  371. CSTRING_CATCH
  372. {
  373. bStat = ORIGINAL_API(CreateProcessW)(
  374. lpApplicationName,
  375. lpCommandLine,
  376. lpProcessAttributes,
  377. lpThreadAttributes,
  378. bInheritHandles,
  379. dwCreationFlags,
  380. lpEnvironment,
  381. lpCurrentDirectory,
  382. lpStartupInfo,
  383. lpProcessInformation);
  384. }
  385. return bStat;
  386. }
  387. /*++
  388. Clean up the command line
  389. --*/
  390. UINT
  391. APIHOOK(WinExec)(
  392. LPCSTR lpCommandLine, // command line
  393. UINT uCmdShow // window style
  394. )
  395. {
  396. CSTRING_TRY
  397. {
  398. CString csOrigCommand(lpCommandLine);
  399. CString csCommand(csOrigCommand);
  400. csCommand.TrimLeft();
  401. LPCSTR lpNewCommandLine = csCommand.GetAnsi();
  402. if (csOrigCommand != csCommand)
  403. {
  404. LOGN(
  405. eDbgLevelError,
  406. "[WinExec] Sanitized lpCommandLine (%s) (%s)",
  407. lpCommandLine, lpNewCommandLine);
  408. }
  409. return ORIGINAL_API(WinExec)(lpNewCommandLine, uCmdShow);
  410. }
  411. CSTRING_CATCH
  412. {
  413. return ORIGINAL_API(WinExec)(lpCommandLine, uCmdShow);
  414. }
  415. }
  416. /*++
  417. Create the appropriate g_PathCorrector
  418. --*/
  419. void
  420. ParseCommandLine(
  421. const char* commandLine
  422. )
  423. {
  424. //
  425. // Force the default values.
  426. //
  427. g_bShortenExeOnCommandLine = FALSE;
  428. CString csCL(commandLine);
  429. if (csCL.CompareNoCase(L"+ShortenExeOnCommandLine") == 0)
  430. {
  431. g_bShortenExeOnCommandLine = TRUE;
  432. }
  433. }
  434. BOOL
  435. NOTIFY_FUNCTION(
  436. DWORD fdwReason
  437. )
  438. {
  439. if (fdwReason == DLL_PROCESS_ATTACH)
  440. {
  441. ParseCommandLine(COMMAND_LINE);
  442. }
  443. return TRUE;
  444. }
  445. /*++
  446. Register hooked functions
  447. --*/
  448. HOOK_BEGIN
  449. CALL_NOTIFY_FUNCTION
  450. APIHOOK_ENTRY(KERNEL32.DLL, CreateProcessA)
  451. APIHOOK_ENTRY(KERNEL32.DLL, CreateProcessW)
  452. APIHOOK_ENTRY(KERNEL32.DLL, WinExec)
  453. HOOK_END
  454. IMPLEMENT_SHIM_END