|
|
/*++
Copyright (c) 2000-2001 Microsoft Corporation
Module Name:
EmulateCreateProcess.cpp
Abstract:
This shim cleans up the StartupInfo data structure to prevent NT from access violating due to uninitialized members.
It also performs a little cleanup of lpApplicationName and lpCommandLine
Win9x uses short file names internally, so applications do not have any problem skipping the application name (first arg) on the command line; they typically skip to the first blank.
History:
11/22/1999 v-johnwh Created 04/11/2000 a-chcoff Updated to quote lpCommandLine Properly. 05/03/2000 robkenny Skip leading white space in lpApplicationName and lpCommandLine 10/09/2000 robkenny Shim was placing quotes around lpCommandLine if it contained spaces, this is totally wrong. Since I could not find the app that required this, I removed it entirely from the shim. 03/09/2001 robkenny Merged in CorrectCreateProcess16Bit 03/15/2001 robkenny Converted to CString 05/21/2001 pierreys Changes to DOS file handling to match 9X more precisely
--*/
#include "precomp.h"
IMPLEMENT_SHIM_BEGIN(EmulateCreateProcess) #include "ShimHookMacro.h"
APIHOOK_ENUM_BEGIN APIHOOK_ENUM_ENTRY(CreateProcessA) APIHOOK_ENUM_ENTRY(CreateProcessW) APIHOOK_ENUM_ENTRY(WinExec) APIHOOK_ENUM_END
BOOL g_bShortenExeOnCommandLine = FALSE;
/*++
Clean parameters so we don't AV
--*/
BOOL APIHOOK(CreateProcessA)( LPCSTR lpApplicationName, LPSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCSTR lpCurrentDirectory, LPSTARTUPINFOA lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation ) { DPFN( eDbgLevelSpew, "[CreateProcessA] (%s) (%s)\n", (lpApplicationName ? lpApplicationName : "null"), (lpCommandLine ? lpCommandLine : "null")); BOOL bStat = FALSE; DWORD dwBinaryType;
CSTRING_TRY { CString csOrigAppName(lpApplicationName); CString csOrigCommand(lpCommandLine);
CString csAppName(csOrigAppName); CString csCommand(csOrigCommand); // Skip leading blanks.
csAppName.TrimLeft(); csCommand.TrimLeft(); // Clean up lpStartupInfo
if (lpStartupInfo) { if (lpStartupInfo->lpReserved || lpStartupInfo->cbReserved2 || lpStartupInfo->lpReserved2 || lpStartupInfo->lpDesktop || ((lpStartupInfo->dwFlags & STARTF_USESTDHANDLES) == 0 && (lpStartupInfo->hStdInput || lpStartupInfo->hStdOutput || lpStartupInfo->hStdError))) { LOGN( eDbgLevelError, "[CreateProcessA] Bad params. Sanitized."); } //
// Make sure that the parameters that can cause an access violation are
// set correctly
//
lpStartupInfo->lpReserved = NULL; lpStartupInfo->cbReserved2 = 0; lpStartupInfo->lpReserved2 = NULL; if ((lpStartupInfo->dwFlags & STARTF_USESTDHANDLES) == 0) { lpStartupInfo->hStdInput = NULL; lpStartupInfo->hStdOutput = NULL; lpStartupInfo->hStdError = NULL; } lpStartupInfo->lpDesktop = NULL; } AppAndCommandLine acl(csAppName, csCommand); // Win9X has a rather weird behavihor: if the app is non-Win32 (Non-Console
// and non GUI), it will use CreateProcessNonWin32. This will first check
// if it is for a batch file, and if so it will prepend "command /c" and
// continue on. Then there is going to be some weird creation of a
// REDIR32.EXE process, but that is just to make sure we have a new win32
// context. It will then use ExecWin16Program. The biggest weirdness is in
// its QuoteAppName call. This procedure make sure that if the appname has
// a space and is in the cmdline, it gets quoted. The appname, in all cases,
// is then discarded (it is expected that the first part of the command line
// contains the app name). So if someone like in b#373980 passes ("command",
// "setup", ... then 9X ends up dropping the command portion entirely since
// it is not part of the commandline.
// 16-bit process must have NULL lpAppName
if (!csAppName.IsEmpty() && GetBinaryTypeW(csAppName.Get(), &dwBinaryType) == TRUE) { switch (dwBinaryType) { case SCS_DOS_BINARY:
// Implementing the process.c's QuoteAppName check.
// If this function would return NULL, then only
// the cmdline would be used. Otherwise the new
// pszCmdFinal is used.
// QuoteAppName
// Look for white space in app name. If we find any then we have to
// quote the app name portion of cmdline.
//
// LPSTR
// KERNENTRY
// QuoteAppName(
// LPCSTR pszAppName,
// LPCSTR pszCmdLine)
// {
// LPSTR pch;
// LPSTR pszApp;
// LPSTR pszCmdFinal = NULL;
//
// // Check that there is an app name, not already quoted in the cmd line.
// if( pszAppName && pszCmdLine && (*pszCmdLine != '\"')) {
// // search for white space
// for( pszApp = (LPSTR)pszAppName; *pszApp > ' '; pszApp++) ;
//
// if( *pszApp) { // found white space
// // make room for the original cmd line plus 2 '"' + 0 terminator
// pch = pszCmdFinal = HeapAlloc( hheapKernel, 0,
// CbSizeSz( pszCmdLine)+3);
// if( pch) {
// *pch++ = '\"'; // beginning dbl-quote
// for( pszApp = (LPSTR)pszAppName;
// *pszApp && *pszApp == *pszCmdLine;
// pszCmdLine++)
// *pch++ = *pszApp++;
// if( !( *pszApp)) {
// *pch++ = '\"'; // trailing dbl-quote
// strcpy( pch, pszCmdLine);
// } else {
// // app name and cmd line did not match
// HeapFree( hheapKernel, 0, pszCmdFinal);
// pszCmdFinal = NULL;
// }
// }
// }
// }
// return pszCmdFinal;
//}
if ( /* app name already checked to be non empty */ !csCommand.IsEmpty() && (csCommand.Get())[0]!='\"') { if (csAppName.Find(L' ')!=-1) { int iAppLength=csAppName.GetLength();
if (csCommand.Find(csAppName)==0) { CString csCmdFinal=L"\""; csCmdFinal += csAppName; csCmdFinal += L"\""; csCmdFinal += csCommand.Mid(iAppLength);
csCommand = csCmdFinal;
LOGN( eDbgLevelSpew, "[CreateProcessA] Weird quoted case: cmdline %s converted to %S", lpCommandLine, csCommand.Get()); }
} }
LOGN( eDbgLevelSpew, "[CreateProcessA] DOS file case: not using appname %s, just cmdline %s, converted to %S", lpApplicationName, lpCommandLine, csCommand.Get());
csAppName.Empty(); //
// The old code in non-WOW case would do this.
//
if (g_bShortenExeOnCommandLine) { csCommand = acl.GetShortCommandLine(); } break;
case SCS_WOW_BINARY: //
// This is the old code. Accoring to 9X, we should be doing
// the same as DOS, but we obviously found an app that
// needed this.
//
csCommand = csAppName; csCommand.GetShortPathNameW(); csCommand += L' '; csCommand += acl.GetCommandlineNoAppName(); csAppName.Empty(); break;
default: //
// The old code in non-WOW case would do this.
//
if (g_bShortenExeOnCommandLine) { csCommand = acl.GetShortCommandLine(); } break; } } else if (g_bShortenExeOnCommandLine) { csCommand = acl.GetShortCommandLine(); } LPCSTR lpNewApplicationName = csAppName.GetAnsiNIE(); LPSTR lpNewCommandLine = csCommand.GetAnsiNIE(); // Log any changes
if (csOrigAppName != csAppName) { LOGN( eDbgLevelError, "[CreateProcessA] Sanitized lpApplicationName (%s) to (%s)", lpApplicationName, lpNewApplicationName); } if (csOrigCommand != csCommand) { LOGN( eDbgLevelError, "[CreateProcessA] Sanitized lpCommandLine (%s) to (%s)", lpCommandLine, lpNewCommandLine); } bStat = ORIGINAL_API(CreateProcessA)( lpNewApplicationName, lpNewCommandLine, lpProcessAttributes, lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment, lpCurrentDirectory, lpStartupInfo, lpProcessInformation); } CSTRING_CATCH { bStat = ORIGINAL_API(CreateProcessA)( lpApplicationName, lpCommandLine, lpProcessAttributes, lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment, lpCurrentDirectory, lpStartupInfo, lpProcessInformation); }
return bStat; }
/*++
Clean parameters so we don't AV
--*/
BOOL APIHOOK(CreateProcessW)( LPCWSTR lpApplicationName, LPWSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCWSTR lpCurrentDirectory, LPSTARTUPINFOW lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation ) { DPFN( eDbgLevelSpew, "[CreateProcessW] (%S) (%S)\n", (lpApplicationName ? lpApplicationName : L"null"), (lpCommandLine ? lpCommandLine : L"null"));
BOOL bStat = FALSE;
CSTRING_TRY { CString csAppName(lpApplicationName); CString csCommand(lpCommandLine); // Skip leading blanks.
csAppName.TrimLeft(); csCommand.TrimLeft(); // Clean up lpStartupInfo
if (lpStartupInfo) { if (lpStartupInfo->lpReserved || lpStartupInfo->cbReserved2 || lpStartupInfo->lpReserved2 || lpStartupInfo->lpDesktop || ((lpStartupInfo->dwFlags & STARTF_USESTDHANDLES) == 0 && (lpStartupInfo->hStdInput || lpStartupInfo->hStdOutput || lpStartupInfo->hStdError))) { LOGN( eDbgLevelError, "[CreateProcessW] Bad params. Sanitized."); } //
// Make sure that the parameters that can cause an access violation are
// set correctly
//
lpStartupInfo->lpReserved = NULL; lpStartupInfo->cbReserved2 = 0; lpStartupInfo->lpReserved2 = NULL; if ((lpStartupInfo->dwFlags & STARTF_USESTDHANDLES) == 0) { lpStartupInfo->hStdInput = NULL; lpStartupInfo->hStdOutput = NULL; lpStartupInfo->hStdError = NULL; } lpStartupInfo->lpDesktop = NULL; } AppAndCommandLine acl(csAppName, csCommand); // 16-bit process must have NULL lpAppName
if (!csAppName.IsEmpty() && IsImage16BitW(csAppName.Get())) { csCommand = csAppName; csCommand.GetShortPathNameW(); csCommand += L' '; csCommand += acl.GetCommandlineNoAppName(); csAppName.Empty(); } else if (g_bShortenExeOnCommandLine) { csCommand = acl.GetShortCommandLine(); } LPCWSTR lpNewApplicationName = csAppName.GetNIE(); LPWSTR lpNewCommandLine = (LPWSTR) csCommand.GetNIE(); // stupid api doesn't take const
// Log any changes
if (lpApplicationName && lpNewApplicationName && _wcsicmp(lpApplicationName, lpNewApplicationName) != 0) { LOGN( eDbgLevelError, "[CreateProcessW] Sanitized lpApplicationName (%S) to (%S)", lpApplicationName, lpNewApplicationName); } if (lpCommandLine && lpNewCommandLine && _wcsicmp(lpCommandLine, lpNewCommandLine) != 0) { LOGN( eDbgLevelError, "[CreateProcessW] Sanitized lpCommandLine (%S) to (%S)", lpCommandLine, lpNewCommandLine); } bStat = ORIGINAL_API(CreateProcessW)( lpNewApplicationName, lpNewCommandLine, lpProcessAttributes, lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment, lpCurrentDirectory, lpStartupInfo, lpProcessInformation); } CSTRING_CATCH { bStat = ORIGINAL_API(CreateProcessW)( lpApplicationName, lpCommandLine, lpProcessAttributes, lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment, lpCurrentDirectory, lpStartupInfo, lpProcessInformation); } return bStat; }
/*++
Clean up the command line
--*/
UINT APIHOOK(WinExec)( LPCSTR lpCommandLine, // command line
UINT uCmdShow // window style
) { CSTRING_TRY { CString csOrigCommand(lpCommandLine); CString csCommand(csOrigCommand); csCommand.TrimLeft();
LPCSTR lpNewCommandLine = csCommand.GetAnsi(); if (csOrigCommand != csCommand) { LOGN( eDbgLevelError, "[WinExec] Sanitized lpCommandLine (%s) (%s)", lpCommandLine, lpNewCommandLine); } return ORIGINAL_API(WinExec)(lpNewCommandLine, uCmdShow); } CSTRING_CATCH { return ORIGINAL_API(WinExec)(lpCommandLine, uCmdShow); } }
/*++
Create the appropriate g_PathCorrector
--*/ void ParseCommandLine( const char* commandLine ) { //
// Force the default values.
//
g_bShortenExeOnCommandLine = FALSE;
CString csCL(commandLine); if (csCL.CompareNoCase(L"+ShortenExeOnCommandLine") == 0) { g_bShortenExeOnCommandLine = TRUE; } }
BOOL NOTIFY_FUNCTION( DWORD fdwReason ) { if (fdwReason == DLL_PROCESS_ATTACH) { ParseCommandLine(COMMAND_LINE); }
return TRUE; }
/*++
Register hooked functions
--*/
HOOK_BEGIN
CALL_NOTIFY_FUNCTION APIHOOK_ENTRY(KERNEL32.DLL, CreateProcessA) APIHOOK_ENTRY(KERNEL32.DLL, CreateProcessW) APIHOOK_ENTRY(KERNEL32.DLL, WinExec)
HOOK_END
IMPLEMENT_SHIM_END
|