|
|
/* cmdexec.c - Misc SCS routines for non-dos exec and re-entering
* the DOS. * * * Modification History: * * Sudeepb 22-Apr-1992 Created */
#include "cmd.h"
#include <cmdsvc.h>
#include <softpc.h>
#include <mvdm.h>
#include <ctype.h>
#include <oemuni.h>
#include <wowcmpat.h>
//*****************************************************************************
// IsWowAppRunnable
//
// Returns FALSE if the WOW-specific compatibility flags for the specified
// task include the bit WOWCF_NOTDOSSPAWNABLE. This is done mostly for
// "dual mode" executables, e.g., Windows apps that have a real program
// as a DOS stub. Certain apps that are started via a DOS command shell,
// for example PWB, really do expect to be started as a DOS app, not a WOW
// app. For these apps, the compatibility bit should be set in the
// registry.
//
//*****************************************************************************
BOOL IsWowAppRunnable(LPSTR lpAppName) { BOOL Result = TRUE; LONG lError; HKEY hKey = 0; char szModName[9]; char szHexAsciiFlags[12]; DWORD dwType = REG_SZ; DWORD cbData = sizeof(szHexAsciiFlags); ULONG ul = 0; LPSTR pStrt, pEnd; SHORT len;
lError = RegOpenKeyEx( HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows NT\\CurrentVersion\\WOW\\Compatibility", 0, KEY_QUERY_VALUE, &hKey );
if (ERROR_SUCCESS != lError) { goto Cleanup; }
//
// The following code strips the file name (<9 chars) out of a dos
// path name.
//
pStrt = strrchr (lpAppName, '\\');
if (pStrt==NULL) pStrt = lpAppName; else pStrt++;
if ( (pEnd = strchr (pStrt, '.')) == NULL) strncpy (szModName, pStrt, 9);
else { len = (SHORT) (pEnd - pStrt); if (len>8) goto Cleanup; strncpy (szModName, pStrt, len); szModName[len] = '\0'; }
//
// Look for the file name in the registry
//
lError = RegQueryValueEx( hKey, szModName, 0, &dwType, szHexAsciiFlags, &cbData );
if (ERROR_SUCCESS != lError) { goto Cleanup; }
if (REG_SZ != dwType) { goto Cleanup; }
//
// Force the string to lowercase for the convenience of sscanf.
//
_strlwr(szHexAsciiFlags);
//
// sscanf() returns the number of fields converted.
//
if (1 != sscanf(szHexAsciiFlags, "0x%lx", &ul)) { goto Cleanup; }
if ((ul & WOWCF_NOTDOSSPAWNABLE) != 0) Result = FALSE;
Cleanup: if (hKey) { RegCloseKey(hKey); }
return Result; }
/* cmdCheckBinary - check that the supplied binary name is a 32bit binary
* * * Entry - Client (DS:DX) - pointer to pathname for the executable to be tested * Client (ES:BX) - pointer to parameter block * * EXIT - SUCCESS Client (CY) clear * FAILURE Client (CY) set * Client (AX) - error_not_enough_memory if command tail * cannot accomodate /z * - error_file_not_found if patname not found */
VOID cmdCheckBinary (VOID) {
LPSTR lpAppName; ULONG BinaryType; PPARAMBLOCK lpParamBlock; PCHAR lpCommandTail,lpTemp; ULONG AppNameLen,CommandTailLen = 0; USHORT CommandTailOff,CommandTailSeg,usTemp; NTSTATUS Status; UNICODE_STRING Unicode; OEM_STRING OemString; ANSI_STRING AnsiString;
if(DontCheckDosBinaryType){ setCF(0); return; // DOS Exe
}
lpAppName = (LPSTR) GetVDMAddr (getDS(),getDX());
Unicode.Buffer = NULL; AnsiString.Buffer = NULL; RtlInitString((PSTRING)&OemString, lpAppName); Status = RtlOemStringToUnicodeString(&Unicode,&OemString,TRUE); if ( NT_SUCCESS(Status) ) { Status = RtlUnicodeStringToAnsiString(&AnsiString, &Unicode, TRUE); } if ( !NT_SUCCESS(Status) ) { Status = RtlNtStatusToDosError(Status); } else if (GetBinaryType (AnsiString.Buffer,(LPLONG)&BinaryType) == FALSE) { Status = GetLastError(); }
if (Unicode.Buffer != NULL) { RtlFreeUnicodeString( &Unicode ); } if (AnsiString.Buffer != NULL) { RtlFreeAnsiString( &AnsiString); }
if (Status){ setCF(1); setAX((USHORT)Status); return; // Invalid path
}
if (BinaryType == SCS_DOS_BINARY) { setCF(0); return; // DOS Exe
} // Prevent certain WOW apps from being spawned by DOS exe's
// This is for win31 compatibility
else if (BinaryType == SCS_WOW_BINARY) { if (!IsWowAppRunnable(lpAppName)) { setCF(0); return; // Run as DOS Exe
} }
if (VDMForWOW && BinaryType == SCS_WOW_BINARY && IsFirstWOWCheckBinary) { IsFirstWOWCheckBinary = FALSE; setCF(0); return; // Special Hack for krnl286.exe
}
// dont allow running 32bit binaries from autoexec.nt. Reason is that
// running non-dos binary requires that we should have read the actual
// command from GetNextVDMCommand. Otherwise the whole design gets into
// synchronization problems.
if (IsFirstCall) { setCF(1); setAX((USHORT)ERROR_FILE_NOT_FOUND); return; }
// Its a 32bit exe, replace the command with "command.com /z" and add the
// original binary name to command tail.
AppNameLen = strlen (lpAppName);
lpParamBlock = (PPARAMBLOCK) GetVDMAddr (getES(),getBX());
if (lpParamBlock) { CommandTailOff = FETCHWORD(lpParamBlock->OffCmdTail); CommandTailSeg = FETCHWORD(lpParamBlock->SegCmdTail);
lpCommandTail = (PCHAR) GetVDMAddr (CommandTailSeg,CommandTailOff);
if (lpCommandTail){ CommandTailLen = *(PCHAR)lpCommandTail; lpCommandTail++; // point to the actual command tail
if (CommandTailLen) CommandTailLen++; // For CR
}
// We are adding 3 below for "/z<space>" and anothre space between
// AppName and CommandTail.
if ((3 + AppNameLen + CommandTailLen ) > 128){ setCF(1); setAX((USHORT)ERROR_NOT_ENOUGH_MEMORY); return; } }
// copy the stub command.com name
strcpy ((PCHAR)&pSCSInfo->SCS_ComSpec,lpszComSpec+8); lpTemp = (PCHAR) &pSCSInfo->SCS_ComSpec; lpTemp = (PCHAR)((ULONG)lpTemp - (ULONG)GetVDMAddr(0,0)); usTemp = (USHORT)((ULONG)lpTemp >> 4); setDS(usTemp); usTemp = (USHORT)((ULONG)lpTemp & 0x0f); setDX((usTemp));
// Form the command tail, first "3" is for "/z "
pSCSInfo->SCS_CmdTail [0] = (UCHAR)(3 + AppNameLen + CommandTailLen); RtlCopyMemory ((PCHAR)&pSCSInfo->SCS_CmdTail[1],"/z ",3); strcpy ((PCHAR)&pSCSInfo->SCS_CmdTail[4],lpAppName); if (CommandTailLen) { pSCSInfo->SCS_CmdTail[4+AppNameLen] = ' '; RtlCopyMemory ((PCHAR)((ULONG)&pSCSInfo->SCS_CmdTail[4]+AppNameLen+1), lpCommandTail, CommandTailLen); } else { pSCSInfo->SCS_CmdTail[4+AppNameLen] = 0xd; }
// Set the parameter Block
if (lpParamBlock) { STOREWORD(pSCSInfo->SCS_ParamBlock.SegEnv,lpParamBlock->SegEnv); STOREDWORD(pSCSInfo->SCS_ParamBlock.pFCB1,lpParamBlock->pFCB1); STOREDWORD(pSCSInfo->SCS_ParamBlock.pFCB2,lpParamBlock->pFCB2); } else { STOREWORD(pSCSInfo->SCS_ParamBlock.SegEnv,0); STOREDWORD(pSCSInfo->SCS_ParamBlock.pFCB1,0); STOREDWORD(pSCSInfo->SCS_ParamBlock.pFCB2,0); }
lpTemp = (PCHAR) &pSCSInfo->SCS_CmdTail; lpTemp = (PCHAR)((ULONG)lpTemp - (ULONG)GetVDMAddr(0,0)); usTemp = (USHORT)((ULONG)lpTemp & 0x0f); STOREWORD(pSCSInfo->SCS_ParamBlock.OffCmdTail,usTemp); usTemp = (USHORT)((ULONG)lpTemp >> 4); STOREWORD(pSCSInfo->SCS_ParamBlock.SegCmdTail,usTemp);
lpTemp = (PCHAR) &pSCSInfo->SCS_ParamBlock; lpTemp = (PCHAR)((ULONG)lpTemp - (ULONG)GetVDMAddr(0,0)); usTemp = (USHORT)((ULONG)lpTemp >> 4); setES (usTemp); usTemp = (USHORT)((ULONG)lpTemp & 0x0f); setBX (usTemp);
setCF(0); return; }
#define MAX_DIR 68
VOID cmdCreateProcess ( PSTD_HANDLES pStdHandles ) {
VDMINFO VDMInfoForCount; STARTUPINFO StartupInfo; PROCESS_INFORMATION ProcessInformation; HANDLE hStd16In,hStd16Out,hStd16Err; CHAR CurDirVar [] = "=?:"; CHAR Buffer [MAX_DIR]; CHAR *CurDir = Buffer; DWORD dwRet; BOOL Status; NTSTATUS NtStatus; UNICODE_STRING Unicode; OEM_STRING OemString; LPVOID lpNewEnv=NULL; ANSI_STRING Env_A;
// we have one more 32 executable active
Exe32ActiveCount++;
// Increment the Re-enterancy count for the VDM
VDMInfoForCount.VDMState = INCREMENT_REENTER_COUNT; GetNextVDMCommand (&VDMInfoForCount);
RtlZeroMemory((PVOID)&StartupInfo,sizeof(STARTUPINFO)); StartupInfo.cb = sizeof(STARTUPINFO);
CurDirVar [1] = chDefaultDrive;
dwRet = GetEnvironmentVariable (CurDirVar,Buffer,MAX_DIR);
if (dwRet == 0 || dwRet == MAX_DIR) CurDir = NULL;
if ((hStd16In = (HANDLE) FETCHDWORD(pStdHandles->hStdIn)) != (HANDLE)-1) SetStdHandle (STD_INPUT_HANDLE, hStd16In);
if ((hStd16Out = (HANDLE) FETCHDWORD(pStdHandles->hStdOut)) != (HANDLE)-1) SetStdHandle (STD_OUTPUT_HANDLE, hStd16Out);
if ((hStd16Err = (HANDLE) FETCHDWORD(pStdHandles->hStdErr)) != (HANDLE)-1) SetStdHandle (STD_ERROR_HANDLE, hStd16Err);
/*
* Warning, pEnv32 currently points to an ansi environment. * The DOS is using an ANSI env which isn't quite correct. * If the DOS is changed to use an OEM env then we will * have to convert the env back to ansi before spawning * non-dos exes ?!? * 16-Jan-1993 Jonle */
Env_A.Buffer = NULL;
RtlInitString((PSTRING)&OemString, pCommand32); NtStatus = RtlOemStringToUnicodeString(&Unicode,&OemString,TRUE); if (NT_SUCCESS(NtStatus)) { NtStatus = RtlUnicodeStringToAnsiString((PANSI_STRING)&OemString, &Unicode, FALSE); RtlFreeUnicodeString( &Unicode ); } if (!NT_SUCCESS(NtStatus)) { SetLastError(RtlNtStatusToDosError(NtStatus)); Status = FALSE; } else { if (pEnv32 != NULL && !cmdXformEnvironment (pEnv32, &Env_A)) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); Status = FALSE; } else { Status = CreateProcess ( NULL, (LPTSTR)pCommand32, NULL, NULL, TRUE, CREATE_SUSPENDED | CREATE_DEFAULT_ERROR_MODE, Env_A.Buffer, (LPTSTR)CurDir, &StartupInfo, &ProcessInformation); } }
if (Status == FALSE) dwExitCode32 = GetLastError ();
if (hStd16In != (HANDLE)-1) SetStdHandle (STD_INPUT_HANDLE, SCS_hStdIn);
if (hStd16Out != (HANDLE)-1) SetStdHandle (STD_OUTPUT_HANDLE, SCS_hStdOut);
if (hStd16Err != (HANDLE)-1) SetStdHandle (STD_ERROR_HANDLE, SCS_hStdErr);
if (Status) { ResumeThread (ProcessInformation.hThread); WaitForSingleObject(ProcessInformation.hProcess, (DWORD)-1); GetExitCodeProcess (ProcessInformation.hProcess, &dwExitCode32); CloseHandle (ProcessInformation.hProcess); CloseHandle (ProcessInformation.hThread); }
if (Env_A.Buffer) RtlFreeAnsiString(&Env_A);
// Decrement the Re-enterancy count for the VDM
VDMInfoForCount.VDMState = DECREMENT_REENTER_COUNT; GetNextVDMCommand (&VDMInfoForCount);
// one less 32 executable active
Exe32ActiveCount--;
// Kill this thread
ExitThread (0); }
VOID cmdExec32 (PCHAR pCmd32, PCHAR pEnv) {
DWORD dwThreadId; HANDLE hThread; PSTD_HANDLES pStdHandles;
pCommand32 = pCmd32; pEnv32 = pEnv;
CntrlHandlerState = (CntrlHandlerState & ~CNTRL_SHELLCOUNT) | (((WORD)(CntrlHandlerState & CNTRL_SHELLCOUNT))+1);
nt_block_event_thread(0); fSoftpcRedirectionOnShellOut = fSoftpcRedirection; fBlock = TRUE;
pStdHandles = (PSTD_HANDLES) GetVDMAddr (getSS(), getBP()); if((hThread = CreateThread (NULL, 0, (LPTHREAD_START_ROUTINE)cmdCreateProcess, pStdHandles, 0, &dwThreadId)) == FALSE) { setCF(0); setAL((UCHAR)GetLastError()); nt_resume_event_thread(); nt_std_handle_notification(fSoftpcRedirectionOnShellOut); fBlock = FALSE; CntrlHandlerState = (CntrlHandlerState & ~CNTRL_SHELLCOUNT) | (((WORD)(CntrlHandlerState & CNTRL_SHELLCOUNT))-1); return; } else CloseHandle (hThread);
// Wait for next command to be re-entered
RtlZeroMemory(&VDMInfo, sizeof(VDMINFO)); VDMInfo.VDMState = NO_PARENT_TO_WAKE | RETURN_ON_NO_COMMAND; GetNextVDMCommand (&VDMInfo); if (VDMInfo.CmdSize > 0){ setCF(1); IsRepeatCall = TRUE; } else { setCF(0); setAL((UCHAR)dwExitCode32); nt_resume_event_thread(); nt_std_handle_notification(fSoftpcRedirectionOnShellOut); fBlock = FALSE; }
CntrlHandlerState = (CntrlHandlerState & ~CNTRL_SHELLCOUNT) | (((WORD)(CntrlHandlerState & CNTRL_SHELLCOUNT))-1); return; }
/* cmdExecComspec32 - Exec 32bit COMSPEC
* * * Entry - Client (ES) - environment segment * Client (AL) - default drive * * EXIT - SUCCESS Client (CY) Clear - AL has return error code * FAILURE Client (CY) set - means DOS is being re-entered */
VOID cmdExecComspec32 (VOID) {
CHAR Buffer[MAX_PATH]; DWORD dwRet; PCHAR pEnv;
dwRet = GetEnvironmentVariable ("COMSPEC",Buffer,MAX_PATH);
if (dwRet == 0 || dwRet >= MAX_PATH){ setCF(0); setAL((UCHAR)ERROR_BAD_ENVIRONMENT); return; }
pEnv = (PCHAR) GetVDMAddr ((USHORT)getES(),0);
chDefaultDrive = (CHAR)(getAL() + 'A');
cmdExec32 (Buffer,pEnv);
return; }
/* cmdExec - Exec a non-dos binary
* * * Entry - Client (DS:SI) - command to execute * Client (AL) - default drive * Client (ES) - environment segment * Client (SS:BP) - Pointer to STD_HANDLES * Client (AH) - if 1 means do "cmd /c command" else "command" * * EXIT - SUCCESS Client (CY) Clear - AL has return error code * FAILURE Client (CY) set - means DOS is being re-entered */
VOID cmdExec (VOID) {
DWORD i; DWORD dwRet; PCHAR pCommandTail; PCHAR pEnv; CHAR Buffer[MAX_PATH];
pCommandTail = (PCHAR) GetVDMAddr ((USHORT)getDS(),(USHORT)getSI()); pEnv = (PCHAR) GetVDMAddr ((USHORT)getES(),0); for (i=0 ; i<124 ; i++) { if (pCommandTail[i] == 0x0d){ pCommandTail[i] = 0; break; } }
if (i == 124){ setCF(0); setAL((UCHAR)ERROR_BAD_FORMAT); return; }
chDefaultDrive = (CHAR)(getAL() + 'A');
if (getAH() == 0) { cmdExec32 (pCommandTail,pEnv); } else { dwRet = GetEnvironmentVariable ("COMSPEC",Buffer,MAX_PATH);
if (dwRet == 0 || dwRet >= MAX_PATH){ setCF(0); setAL((UCHAR)ERROR_BAD_ENVIRONMENT); return; }
if ((dwRet + 4 + strlen(pCommandTail)) > MAX_PATH) { setCF(0); setAL((UCHAR)ERROR_BAD_ENVIRONMENT); return; }
strcat (Buffer, " /c "); strcat (Buffer, pCommandTail); cmdExec32 (Buffer,pEnv); }
return; }
/* cmdReturnExitCode - command.com has run a dos binary and returing
* the exit code. * * Entry - Client (DX) - exit code * Client (AL) - current drive * Client (BX:CX) - RdrInfo address * * Exit * Client Carry Set - Reenter i.e. a new DOS binary to execute. * Client Carry Clear - This shelled out session is over. */
VOID cmdReturnExitCode (VOID) { VDMINFO VDMInfo; PREDIRCOMPLETE_INFO pRdrInfo;
RtlZeroMemory(&VDMInfo, sizeof(VDMINFO)); VDMInfo.VDMState = RETURN_ON_NO_COMMAND; VDMInfo.ErrorCode = (ULONG)getDX();
CntrlHandlerState = (CntrlHandlerState & ~CNTRL_SHELLCOUNT) | (((WORD)(CntrlHandlerState & CNTRL_SHELLCOUNT))+1);
nt_block_event_thread(0); fBlock = TRUE;
// a dos program just terminate, inherit its current directories
// and tell base too.
cmdUpdateCurrentDirectories((BYTE)getAL());
// Check for any copying needed for redirection
pRdrInfo = (PREDIRCOMPLETE_INFO) (((ULONG)getBX() << 16) + (ULONG)getCX());
if (!cmdCheckCopyForRedirection (pRdrInfo, FALSE)) VDMInfo.ErrorCode = ERROR_NOT_ENOUGH_MEMORY;
GetNextVDMCommand (&VDMInfo); if (VDMInfo.CmdSize > 0){ setCF(1); IsRepeatCall = TRUE; } else { setCF(0); setAL((UCHAR)dwExitCode32); nt_resume_event_thread(); nt_std_handle_notification(fSoftpcRedirectionOnShellOut); fBlock = FALSE; }
CntrlHandlerState = (CntrlHandlerState & ~CNTRL_SHELLCOUNT) | (((WORD)(CntrlHandlerState & CNTRL_SHELLCOUNT))-1);
return; }
|