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.

641 lines
18 KiB

  1. /* cmdexec.c - Misc SCS routines for non-dos exec and re-entering
  2. * the DOS.
  3. *
  4. *
  5. * Modification History:
  6. *
  7. * Sudeepb 22-Apr-1992 Created
  8. */
  9. #include "cmd.h"
  10. #include <cmdsvc.h>
  11. #include <softpc.h>
  12. #include <mvdm.h>
  13. #include <ctype.h>
  14. #include <oemuni.h>
  15. #include <wowcmpat.h>
  16. //*****************************************************************************
  17. // IsWowAppRunnable
  18. //
  19. // Returns FALSE if the WOW-specific compatibility flags for the specified
  20. // task include the bit WOWCF_NOTDOSSPAWNABLE. This is done mostly for
  21. // "dual mode" executables, e.g., Windows apps that have a real program
  22. // as a DOS stub. Certain apps that are started via a DOS command shell,
  23. // for example PWB, really do expect to be started as a DOS app, not a WOW
  24. // app. For these apps, the compatibility bit should be set in the
  25. // registry.
  26. //
  27. //*****************************************************************************
  28. BOOL IsWowAppRunnable(LPSTR lpAppName)
  29. {
  30. BOOL Result = TRUE;
  31. LONG lError;
  32. HKEY hKey = 0;
  33. char szModName[9];
  34. char szHexAsciiFlags[12];
  35. DWORD dwType = REG_SZ;
  36. DWORD cbData = sizeof(szHexAsciiFlags);
  37. ULONG ul = 0;
  38. LPSTR pStrt, pEnd;
  39. SHORT len;
  40. lError = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  41. "Software\\Microsoft\\Windows NT\\CurrentVersion\\WOW\\Compatibility",
  42. 0,
  43. KEY_QUERY_VALUE,
  44. &hKey
  45. );
  46. if (ERROR_SUCCESS != lError) {
  47. goto Cleanup;
  48. }
  49. //
  50. // The following code strips the file name (<9 chars) out of a dos
  51. // path name.
  52. //
  53. pStrt = strrchr (lpAppName, '\\');
  54. if (pStrt==NULL)
  55. pStrt = lpAppName;
  56. else
  57. pStrt++;
  58. if ( (pEnd = strchr (pStrt, '.')) == NULL) {
  59. strncpy (szModName, pStrt, 9);
  60. szModName[8] = '\0';
  61. }
  62. else {
  63. len = (SHORT) (pEnd - pStrt);
  64. if (len>8) goto Cleanup;
  65. strncpy (szModName, pStrt, len);
  66. szModName[len] = '\0';
  67. }
  68. //
  69. // Look for the file name in the registry
  70. //
  71. lError = RegQueryValueEx( hKey,
  72. szModName,
  73. 0,
  74. &dwType,
  75. szHexAsciiFlags,
  76. &cbData
  77. );
  78. if (ERROR_SUCCESS != lError) {
  79. goto Cleanup;
  80. }
  81. if (REG_SZ != dwType) {
  82. goto Cleanup;
  83. }
  84. //
  85. // Force the string to lowercase for the convenience of sscanf.
  86. //
  87. _strlwr(szHexAsciiFlags);
  88. //
  89. // sscanf() returns the number of fields converted.
  90. //
  91. if (1 != sscanf(szHexAsciiFlags, "0x%lx", &ul)) {
  92. goto Cleanup;
  93. }
  94. if ((ul & WOWCF_NOTDOSSPAWNABLE) != 0)
  95. Result = FALSE;
  96. Cleanup:
  97. if (hKey) {
  98. RegCloseKey(hKey);
  99. }
  100. return Result;
  101. }
  102. /* cmdCheckBinary - check that the supplied binary name is a 32bit binary
  103. *
  104. *
  105. * Entry - Client (DS:DX) - pointer to pathname for the executable to be tested
  106. * Client (ES:BX) - pointer to parameter block
  107. *
  108. * EXIT - SUCCESS Client (CY) clear
  109. * FAILURE Client (CY) set
  110. * Client (AX) - error_not_enough_memory if command tail
  111. * cannot accomodate /z
  112. * - error_file_not_found if patname not found
  113. */
  114. VOID cmdCheckBinary (VOID)
  115. {
  116. LPSTR lpAppName;
  117. ULONG BinaryType = SCS_DOS_BINARY;
  118. PPARAMBLOCK lpParamBlock;
  119. PCHAR lpCommandTail,lpTemp;
  120. ULONG AppNameLen,CommandTailLen = 0;
  121. USHORT CommandTailOff,CommandTailSeg,usTemp;
  122. NTSTATUS Status;
  123. UNICODE_STRING Unicode;
  124. OEM_STRING OemString;
  125. ANSI_STRING AnsiString;
  126. if(DontCheckDosBinaryType){
  127. setCF(0);
  128. return; // DOS Exe
  129. }
  130. lpAppName = (LPSTR) GetVDMAddr (getDS(),getDX());
  131. Unicode.Buffer = NULL;
  132. AnsiString.Buffer = NULL;
  133. RtlInitString((PSTRING)&OemString, lpAppName);
  134. Status = RtlOemStringToUnicodeString(&Unicode,&OemString,TRUE);
  135. if ( NT_SUCCESS(Status) ) {
  136. Status = RtlUnicodeStringToAnsiString(&AnsiString, &Unicode, TRUE);
  137. }
  138. if ( !NT_SUCCESS(Status) ) {
  139. Status = RtlNtStatusToDosError(Status);
  140. }
  141. else if (GetBinaryType (AnsiString.Buffer,(LPLONG)&BinaryType) == FALSE)
  142. {
  143. Status = GetLastError();
  144. }
  145. if (Unicode.Buffer != NULL) {
  146. RtlFreeUnicodeString( &Unicode );
  147. }
  148. if (AnsiString.Buffer != NULL) {
  149. RtlFreeAnsiString( &AnsiString);
  150. }
  151. if (Status){
  152. setCF(1);
  153. setAX((USHORT)Status);
  154. return; // Invalid path
  155. }
  156. if (BinaryType == SCS_DOS_BINARY) {
  157. setCF(0);
  158. return; // DOS Exe
  159. }
  160. // Prevent certain WOW apps from being spawned by DOS exe's
  161. // This is for win31 compatibility
  162. else if (BinaryType == SCS_WOW_BINARY) {
  163. if (!IsWowAppRunnable(lpAppName)) {
  164. setCF(0);
  165. return; // Run as DOS Exe
  166. }
  167. }
  168. if (VDMForWOW && BinaryType == SCS_WOW_BINARY && IsFirstWOWCheckBinary) {
  169. IsFirstWOWCheckBinary = FALSE;
  170. setCF(0);
  171. return; // Special Hack for krnl286.exe
  172. }
  173. // dont allow running 32bit binaries from autoexec.nt. Reason is that
  174. // running non-dos binary requires that we should have read the actual
  175. // command from GetNextVDMCommand. Otherwise the whole design gets into
  176. // synchronization problems.
  177. if (IsFirstCall) {
  178. setCF(1);
  179. setAX((USHORT)ERROR_FILE_NOT_FOUND);
  180. return;
  181. }
  182. // Its a 32bit exe, replace the command with "command.com /z" and add the
  183. // original binary name to command tail.
  184. AppNameLen = strlen (lpAppName);
  185. lpParamBlock = (PPARAMBLOCK) GetVDMAddr (getES(),getBX());
  186. if (lpParamBlock) {
  187. CommandTailOff = FETCHWORD(lpParamBlock->OffCmdTail);
  188. CommandTailSeg = FETCHWORD(lpParamBlock->SegCmdTail);
  189. lpCommandTail = (PCHAR) GetVDMAddr (CommandTailSeg,CommandTailOff);
  190. if (lpCommandTail){
  191. CommandTailLen = *(PCHAR)lpCommandTail;
  192. lpCommandTail++; // point to the actual command tail
  193. if (CommandTailLen)
  194. CommandTailLen++; // For CR
  195. }
  196. // We are adding 3 below for "/z<space>" and anothre space between
  197. // AppName and CommandTail.
  198. if ((3 + AppNameLen + CommandTailLen ) > 128){
  199. setCF(1);
  200. setAX((USHORT)ERROR_NOT_ENOUGH_MEMORY);
  201. return;
  202. }
  203. }
  204. // copy the stub command.com name
  205. strcpy ((PCHAR)&pSCSInfo->SCS_ComSpec,lpszComSpec+8);
  206. lpTemp = (PCHAR) &pSCSInfo->SCS_ComSpec;
  207. lpTemp = (PCHAR)((ULONG)lpTemp - (ULONG)GetVDMAddr(0,0));
  208. usTemp = (USHORT)((ULONG)lpTemp >> 4);
  209. setDS(usTemp);
  210. usTemp = (USHORT)((ULONG)lpTemp & 0x0f);
  211. setDX((usTemp));
  212. // Form the command tail, first "3" is for "/z "
  213. pSCSInfo->SCS_CmdTail [0] = (UCHAR)(3 +
  214. AppNameLen +
  215. CommandTailLen);
  216. RtlCopyMemory ((PCHAR)&pSCSInfo->SCS_CmdTail[1],"/z ",3);
  217. strncpy ((PCHAR)&pSCSInfo->SCS_CmdTail[4],lpAppName,124);
  218. pSCSInfo->SCS_CmdTail[127] = '\0';
  219. if (CommandTailLen) {
  220. pSCSInfo->SCS_CmdTail[4+AppNameLen] = ' ';
  221. RtlCopyMemory ((PCHAR)((ULONG)&pSCSInfo->SCS_CmdTail[4]+AppNameLen+1),
  222. lpCommandTail,
  223. CommandTailLen);
  224. }
  225. else {
  226. pSCSInfo->SCS_CmdTail[4+AppNameLen] = 0xd;
  227. }
  228. // Set the parameter Block
  229. if (lpParamBlock) {
  230. STOREWORD(pSCSInfo->SCS_ParamBlock.SegEnv,lpParamBlock->SegEnv);
  231. STOREDWORD(pSCSInfo->SCS_ParamBlock.pFCB1,lpParamBlock->pFCB1);
  232. STOREDWORD(pSCSInfo->SCS_ParamBlock.pFCB2,lpParamBlock->pFCB2);
  233. }
  234. else {
  235. STOREWORD(pSCSInfo->SCS_ParamBlock.SegEnv,0);
  236. STOREDWORD(pSCSInfo->SCS_ParamBlock.pFCB1,0);
  237. STOREDWORD(pSCSInfo->SCS_ParamBlock.pFCB2,0);
  238. }
  239. lpTemp = (PCHAR) &pSCSInfo->SCS_CmdTail;
  240. lpTemp = (PCHAR)((ULONG)lpTemp - (ULONG)GetVDMAddr(0,0));
  241. usTemp = (USHORT)((ULONG)lpTemp & 0x0f);
  242. STOREWORD(pSCSInfo->SCS_ParamBlock.OffCmdTail,usTemp);
  243. usTemp = (USHORT)((ULONG)lpTemp >> 4);
  244. STOREWORD(pSCSInfo->SCS_ParamBlock.SegCmdTail,usTemp);
  245. lpTemp = (PCHAR) &pSCSInfo->SCS_ParamBlock;
  246. lpTemp = (PCHAR)((ULONG)lpTemp - (ULONG)GetVDMAddr(0,0));
  247. usTemp = (USHORT)((ULONG)lpTemp >> 4);
  248. setES (usTemp);
  249. usTemp = (USHORT)((ULONG)lpTemp & 0x0f);
  250. setBX (usTemp);
  251. setCF(0);
  252. return;
  253. }
  254. #define MAX_DIR 68
  255. VOID cmdCreateProcess ( PSTD_HANDLES pStdHandles )
  256. {
  257. VDMINFO VDMInfoForCount;
  258. STARTUPINFO StartupInfo;
  259. PROCESS_INFORMATION ProcessInformation;
  260. HANDLE hStd16In,hStd16Out,hStd16Err;
  261. CHAR CurDirVar [] = "=?:";
  262. CHAR Buffer [MAX_DIR];
  263. CHAR *CurDir = Buffer;
  264. DWORD dwRet;
  265. BOOL Status;
  266. NTSTATUS NtStatus;
  267. UNICODE_STRING Unicode;
  268. OEM_STRING OemString;
  269. LPVOID lpNewEnv=NULL;
  270. ANSI_STRING Env_A;
  271. // we have one more 32 executable active
  272. Exe32ActiveCount++;
  273. // Increment the Re-enterancy count for the VDM
  274. VDMInfoForCount.VDMState = INCREMENT_REENTER_COUNT;
  275. GetNextVDMCommand (&VDMInfoForCount);
  276. RtlZeroMemory((PVOID)&StartupInfo,sizeof(STARTUPINFO));
  277. StartupInfo.cb = sizeof(STARTUPINFO);
  278. CurDirVar [1] = chDefaultDrive;
  279. dwRet = GetEnvironmentVariable (CurDirVar,Buffer,MAX_DIR);
  280. if (dwRet == 0 || dwRet == MAX_DIR)
  281. CurDir = NULL;
  282. if ((hStd16In = (HANDLE) FETCHDWORD(pStdHandles->hStdIn)) != (HANDLE)-1)
  283. SetStdHandle (STD_INPUT_HANDLE, hStd16In);
  284. if ((hStd16Out = (HANDLE) FETCHDWORD(pStdHandles->hStdOut)) != (HANDLE)-1)
  285. SetStdHandle (STD_OUTPUT_HANDLE, hStd16Out);
  286. if ((hStd16Err = (HANDLE) FETCHDWORD(pStdHandles->hStdErr)) != (HANDLE)-1)
  287. SetStdHandle (STD_ERROR_HANDLE, hStd16Err);
  288. /*
  289. * Warning, pEnv32 currently points to an ansi environment.
  290. * The DOS is using an ANSI env which isn't quite correct.
  291. * If the DOS is changed to use an OEM env then we will
  292. * have to convert the env back to ansi before spawning
  293. * non-dos exes ?!?
  294. * 16-Jan-1993 Jonle
  295. */
  296. Env_A.Buffer = NULL;
  297. RtlInitString((PSTRING)&OemString, pCommand32);
  298. NtStatus = RtlOemStringToUnicodeString(&Unicode,&OemString,TRUE);
  299. if (NT_SUCCESS(NtStatus)) {
  300. NtStatus = RtlUnicodeStringToAnsiString((PANSI_STRING)&OemString, &Unicode, FALSE);
  301. RtlFreeUnicodeString( &Unicode );
  302. }
  303. if (!NT_SUCCESS(NtStatus)) {
  304. SetLastError(RtlNtStatusToDosError(NtStatus));
  305. Status = FALSE;
  306. }
  307. else {
  308. if (pEnv32 != NULL && !cmdXformEnvironment (pEnv32, &Env_A)) {
  309. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  310. Status = FALSE;
  311. }
  312. else {
  313. Status = CreateProcess (
  314. NULL,
  315. (LPTSTR)pCommand32,
  316. NULL,
  317. NULL,
  318. TRUE,
  319. CREATE_SUSPENDED | CREATE_DEFAULT_ERROR_MODE,
  320. Env_A.Buffer,
  321. (LPTSTR)CurDir,
  322. &StartupInfo,
  323. &ProcessInformation);
  324. }
  325. }
  326. if (Status == FALSE)
  327. dwExitCode32 = GetLastError ();
  328. if (hStd16In != (HANDLE)-1)
  329. SetStdHandle (STD_INPUT_HANDLE, SCS_hStdIn);
  330. if (hStd16Out != (HANDLE)-1)
  331. SetStdHandle (STD_OUTPUT_HANDLE, SCS_hStdOut);
  332. if (hStd16Err != (HANDLE)-1)
  333. SetStdHandle (STD_ERROR_HANDLE, SCS_hStdErr);
  334. if (Status) {
  335. ResumeThread (ProcessInformation.hThread);
  336. WaitForSingleObject(ProcessInformation.hProcess, (DWORD)-1);
  337. GetExitCodeProcess (ProcessInformation.hProcess, &dwExitCode32);
  338. CloseHandle (ProcessInformation.hProcess);
  339. CloseHandle (ProcessInformation.hThread);
  340. }
  341. if (Env_A.Buffer)
  342. RtlFreeAnsiString(&Env_A);
  343. // Decrement the Re-enterancy count for the VDM
  344. VDMInfoForCount.VDMState = DECREMENT_REENTER_COUNT;
  345. GetNextVDMCommand (&VDMInfoForCount);
  346. // one less 32 executable active
  347. Exe32ActiveCount--;
  348. // Kill this thread
  349. ExitThread (0);
  350. }
  351. VOID cmdExec32 (PCHAR pCmd32, PCHAR pEnv)
  352. {
  353. DWORD dwThreadId;
  354. HANDLE hThread;
  355. PSTD_HANDLES pStdHandles;
  356. pCommand32 = pCmd32;
  357. pEnv32 = pEnv;
  358. CntrlHandlerState = (CntrlHandlerState & ~CNTRL_SHELLCOUNT) |
  359. (((WORD)(CntrlHandlerState & CNTRL_SHELLCOUNT))+1);
  360. nt_block_event_thread(0);
  361. fSoftpcRedirectionOnShellOut = fSoftpcRedirection;
  362. fBlock = TRUE;
  363. pStdHandles = (PSTD_HANDLES) GetVDMAddr (getSS(), getBP());
  364. if((hThread = CreateThread (NULL,
  365. 0,
  366. (LPTHREAD_START_ROUTINE)cmdCreateProcess,
  367. pStdHandles,
  368. 0,
  369. &dwThreadId)) == FALSE) {
  370. setCF(0);
  371. setAL((UCHAR)GetLastError());
  372. nt_resume_event_thread();
  373. nt_std_handle_notification(fSoftpcRedirectionOnShellOut);
  374. fBlock = FALSE;
  375. CntrlHandlerState = (CntrlHandlerState & ~CNTRL_SHELLCOUNT) |
  376. (((WORD)(CntrlHandlerState & CNTRL_SHELLCOUNT))-1);
  377. return;
  378. }
  379. else
  380. CloseHandle (hThread);
  381. // Wait for next command to be re-entered
  382. RtlZeroMemory(&VDMInfo, sizeof(VDMINFO));
  383. VDMInfo.VDMState = NO_PARENT_TO_WAKE | RETURN_ON_NO_COMMAND;
  384. GetNextVDMCommand (&VDMInfo);
  385. if (VDMInfo.CmdSize > 0){
  386. setCF(1);
  387. IsRepeatCall = TRUE;
  388. }
  389. else {
  390. setCF(0);
  391. setAL((UCHAR)dwExitCode32);
  392. nt_resume_event_thread();
  393. nt_std_handle_notification(fSoftpcRedirectionOnShellOut);
  394. fBlock = FALSE;
  395. }
  396. CntrlHandlerState = (CntrlHandlerState & ~CNTRL_SHELLCOUNT) |
  397. (((WORD)(CntrlHandlerState & CNTRL_SHELLCOUNT))-1);
  398. return;
  399. }
  400. /* cmdExecComspec32 - Exec 32bit COMSPEC
  401. *
  402. *
  403. * Entry - Client (ES) - environment segment
  404. * Client (AL) - default drive
  405. *
  406. * EXIT - SUCCESS Client (CY) Clear - AL has return error code
  407. * FAILURE Client (CY) set - means DOS is being re-entered
  408. */
  409. VOID cmdExecComspec32 (VOID)
  410. {
  411. CHAR Buffer[MAX_PATH];
  412. DWORD dwRet;
  413. PCHAR pEnv;
  414. dwRet = GetEnvironmentVariable ("COMSPEC",Buffer,MAX_PATH);
  415. if (dwRet == 0 || dwRet >= MAX_PATH){
  416. setCF(0);
  417. setAL((UCHAR)ERROR_BAD_ENVIRONMENT);
  418. return;
  419. }
  420. pEnv = (PCHAR) GetVDMAddr ((USHORT)getES(),0);
  421. chDefaultDrive = (CHAR)(getAL() + 'A');
  422. cmdExec32 (Buffer,pEnv);
  423. return;
  424. }
  425. /* cmdExec - Exec a non-dos binary
  426. *
  427. *
  428. * Entry - Client (DS:SI) - command to execute
  429. * Client (AL) - default drive
  430. * Client (ES) - environment segment
  431. * Client (SS:BP) - Pointer to STD_HANDLES
  432. * Client (AH) - if 1 means do "cmd /c command" else "command"
  433. *
  434. * EXIT - SUCCESS Client (CY) Clear - AL has return error code
  435. * FAILURE Client (CY) set - means DOS is being re-entered
  436. */
  437. VOID cmdExec (VOID)
  438. {
  439. DWORD i;
  440. DWORD dwRet;
  441. PCHAR pCommandTail;
  442. PCHAR pEnv;
  443. CHAR Buffer[MAX_PATH];
  444. pCommandTail = (PCHAR) GetVDMAddr ((USHORT)getDS(),(USHORT)getSI());
  445. pEnv = (PCHAR) GetVDMAddr ((USHORT)getES(),0);
  446. for (i=0 ; i<124 ; i++) {
  447. if (pCommandTail[i] == 0x0d){
  448. pCommandTail[i] = 0;
  449. break;
  450. }
  451. }
  452. if (i == 124){
  453. setCF(0);
  454. setAL((UCHAR)ERROR_BAD_FORMAT);
  455. return;
  456. }
  457. chDefaultDrive = (CHAR)(getAL() + 'A');
  458. if (getAH() == 0) {
  459. cmdExec32 (pCommandTail,pEnv);
  460. }
  461. else {
  462. dwRet = GetEnvironmentVariable ("COMSPEC",Buffer,MAX_PATH);
  463. if (dwRet == 0 || dwRet >= MAX_PATH){
  464. setCF(0);
  465. setAL((UCHAR)ERROR_BAD_ENVIRONMENT);
  466. return;
  467. }
  468. if ((dwRet + 4 + strlen(pCommandTail)) > MAX_PATH) {
  469. setCF(0);
  470. setAL((UCHAR)ERROR_BAD_ENVIRONMENT);
  471. return;
  472. }
  473. strcat (Buffer, " /c ");
  474. strcat (Buffer, pCommandTail);
  475. cmdExec32 (Buffer,pEnv);
  476. }
  477. return;
  478. }
  479. /* cmdReturnExitCode - command.com has run a dos binary and returing
  480. * the exit code.
  481. *
  482. * Entry - Client (DX) - exit code
  483. * Client (AL) - current drive
  484. * Client (BX:CX) - RdrInfo address
  485. *
  486. * Exit
  487. * Client Carry Set - Reenter i.e. a new DOS binary to execute.
  488. * Client Carry Clear - This shelled out session is over.
  489. */
  490. VOID cmdReturnExitCode (VOID)
  491. {
  492. VDMINFO MyVDMInfo;
  493. PREDIRCOMPLETE_INFO pRdrInfo;
  494. RtlZeroMemory(&MyVDMInfo, sizeof(VDMINFO));
  495. MyVDMInfo.VDMState = RETURN_ON_NO_COMMAND;
  496. MyVDMInfo.ErrorCode = (ULONG)getDX();
  497. CntrlHandlerState = (CntrlHandlerState & ~CNTRL_SHELLCOUNT) |
  498. (((WORD)(CntrlHandlerState & CNTRL_SHELLCOUNT))+1);
  499. nt_block_event_thread(0);
  500. fBlock = TRUE;
  501. // a dos program just terminate, inherit its current directories
  502. // and tell base too.
  503. cmdUpdateCurrentDirectories((BYTE)getAL());
  504. // Check for any copying needed for redirection
  505. pRdrInfo = (PREDIRCOMPLETE_INFO) (((ULONG)getBX() << 16) + (ULONG)getCX());
  506. if (!cmdCheckCopyForRedirection (pRdrInfo, FALSE))
  507. MyVDMInfo.ErrorCode = ERROR_NOT_ENOUGH_MEMORY;
  508. GetNextVDMCommand (&MyVDMInfo);
  509. if (MyVDMInfo.CmdSize > 0){
  510. setCF(1);
  511. IsRepeatCall = TRUE;
  512. }
  513. else {
  514. setCF(0);
  515. setAL((UCHAR)dwExitCode32);
  516. nt_resume_event_thread();
  517. nt_std_handle_notification(fSoftpcRedirectionOnShellOut);
  518. fBlock = FALSE;
  519. }
  520. CntrlHandlerState = (CntrlHandlerState & ~CNTRL_SHELLCOUNT) |
  521. (((WORD)(CntrlHandlerState & CNTRL_SHELLCOUNT))-1);
  522. return;
  523. }