Celceo - Self-Healing Systems

Command line gotchas in CreateProcess

| | Comments (0) | TrackBacks (0)

The type of the CreateProcess API functions, containing separate parameters for application name and command line, lets clients violate the well worn Windows convention of listing the application name as the first token in the command line.  But since this convention is so consistently upheld by Windows and application software, it's easy to assume it will be maintained by all clients and, similarly, it's easy to overlook potentially advantageous side effects of violating it.

Perhaps because of its Unix heritage, OpenOffice violates the convention aggressively -- its constituent modules always elide the application name in the command line, and they tend to launch each other regularly.  We aren't aware of other applications that do this, but certainly this is cause to be mindful of alternative command line conventions.

Additionally, Windows doesn't seem to mind if clients play liberally with the command line.  This has been useful to us in preventing a virtualized application from discovering its virtual location -- while we can intercept API calls that produce the process module path, it's somewhat less elegant to play with the memory that contains the command line (and derivations therefrom).  However, we can happily change the first token in the command line through the parent process to indicate the supposed location of the application, and everything works as desired.

Identifying resource access before faults

| | Comments (0) | TrackBacks (0)

In our world when a client application crashes, improper resource virtualization is sometimes the cause and, as a result, we need to see what resources an application used immediately preceding the crash.  Unfortunately, identifying the crash point is not as simple as discovering the last resource accessed when the Windows "Send Report" dialog appears -- Windows does a fair amount of work, both in-process and in dwwin.exe, when a fault occurs.  To help isolate resource usage around faults, we describe the normal resource access pattern one sees after a fault on Windows XP within the fauling process.

  • The faulting process accesses HLKM\Software\Microsoft\Windows NT\CurrentVersion\AeDebug
  • FaultRep.dll is loaded into the faulting process using LoadLibrary
  • UserEnv.dll, Winsta.dll, Netapi32.dll and Wtsapi32.dll are loaded
  • The faulting process checks HKLM\Software\Microsoft\Windows NT\CurrentVersion\Winlogon\ChkAccDebugLevel
  • Then it reads various values from HKLM\System\CurrentControlSet\Control\ProductOptions
  • The faulting process consults settings in HKLM\SOFTWARE\Microsoft\PCHealth\ErrorReporting to see how it should interact with the Dr. Watson error-reporting module and what data it should collect
  • It then gathers information on the loaded modules -- you will see file queries covering the process's main module as well as its active dependencies
  • It then creates a new temporary file (in %Local Settings%\Temp) ending in "_appcompat.txt"
  • Then it loads apphelp.dll
  • Next, the faulting process writes the fault's technical information to the _appcompat.txt file
  • It then recursively searches the main module's folder and its subfolders, storing data in the _appcompat.txt file
  • The faulting process then calls its unhandled exception filters, registered through SetUnhandledExceptionFilter
  • It then accesses various keys under HKLM\SOFTWARE\Policies\Microsoft\Windows\Safer\CodeIdentifiers
  • Finally, the fauling process launches Dr. Watson (at %System%\dwwin.exe)

Shortcuts and CommandLineFromMsiDescriptor

| | Comments (0) | TrackBacks (0)

Back in 1998, Microsoft introduced technologies in Windows Installer (supported by extensions to Explorer) that allow applications to leave portions uninstalled until another application or the user requests them.  This also allows applications to replace missing pieces before they start.

The way this works, at least for shortcuts, is as follows.  Windows Installer creates shortcuts that contain MSI descriptors rather than the usual target application paths.  When Explorer sees a shortcut of this form, it calls CommandLineFromMsiDescriptor, an undocumented method in Advapi32.dll, passing it the MSI descriptor which is a text blob.  CommandLineFromMsiDescriptor is a thin wrapper around MsiProvideComponentFromDescriptorW in MSI.dll.  It's not clear why Explorer doesn't call MsiProvideComponentFromDescriptorW directly, nor why Microsoft hasn't used the standard A/W naming convention for CommandLineFromMsiDescriptor (it uses Unicode parameters).

From there, the code unpacked the MSI descriptor into product, feature and component strings using MsiDecomposeDescriptor, another undocumented method in MSI.dll.  It then checks to make sure the components are all present on the system.  If they are not (which is often the case for our virtual applications), the code immediately begins replacing the missing resources by starting the installation process again.  This all happens before MsiProvideComponentFromDescriptorW returns -- the model here is that clients can call MSI functions withour worrying about error cases; MSI will do whatever it needs to make sure the call succeeds.

For our purposes, this is a bit of a problem.  We relocate virtual resources but don't modify shortcuts to them -- instead, we intercept calls to parse shortcuts and return the virtual locations.  We need simulate calls to CommandLineFromMsiDescriptor, hopefully without having to parse MSI descriptor blogs directly and wade through the complex system of registry keys MSI uses to register products, components, etc.

Fortunately, we can use MsiDecomposeDescriptor to handle the descriptor parsing, and then all we need is one more (documented!) function: MsiGetComponentPath.  This last function does all the hard work, looking up the path we need given the product and component names.  Additionally, it's not as aggressive as some of the other MSI functions -- it doesn't verify the component paths are valid before returning them.

And finally, one more note for anyone interested in hooking CommandLineFromMsiDescriptor.  The signature for this function is,

DWORD WINAPI CommandLineFromMsiDescriptor(LPCWSTR lpDescriptor, LPCWSTR lpCommandLine, DWORD *dwCommandLineLength);

Explorer doesn't call CommandLineFromMsiDescriptor using the common idiom, NULL lpCommandLine to get the necessary buffer size in dwCommandLineLength and then a second call with valid buffer.  Rather, it supplies a buffer it expects to be sufficient right off the bat.  This makes it slightly harder to discover what to return in cases where the buffer is too short.  And in honesty, we haven't taken the time to test all various return values to Explorer.  But from Wine's use of this method and from our own testing, we believe this function should return ERROR_SUCCESS in cases where the buffer is insufficient, setting dwCommandLineLength to the necessary size (in characters).  Wine assumes the size does not contain a terminating character, but we do.

AcGenral.dll shim hooks in Vista

| | Comments (0) | TrackBacks (0)

Vista introduces a very thoughtful "shim" facility to the Windows architecture.  This allows Microsoft (or others, potentially) to introduce code between applications and API calls -- much as we do in virtualization.  The goal of Vista's shims is specifically to provide compatibility for applications that were designed to run on earlier versions of Windows.  The canonical shim use case, for example, is to spoof the Windows version registry key and API calls to trick applications into thinking they're running on Vista predecessors.

However, Microsoft uses shims more widely than the application compatibility initiative implies.  It's fairly common for programmers to fail to specify security attributes when creating files and registry keys.  But Microsoft knows that certain folders should enforce access conventions through permission -- for example, Program Files should be writeable by installers and readable by all.  So Microsoft uses shim technology to resolve this problem, by modifying security attributes passed to file I/O and registry APIs very widely in order to enforce a more careful access policy than applications normally choose for themselves.

We found that AcGenral.dll, in %SystemRoot%\AppPatch, hooks calls early (that is, below our own hooks).  It inspects the path of resources that are about to be created and, if security attributes are not specified, chooses convention-enforcing attributes based on the path.  This caused problems for us initially because our virtualization relocates resources in a way that confuses AcGenral's policies.  To resolve this, on Vista we need to augment security attributes for virtualized resources in a way that matches AcGenral's policies for the resource location (not the virtual location, which AcGenral sees since it hooks beneath us).

Injection coverage on Vista with UAC

| | Comments (0) | TrackBacks (0)

We try to avoid injecting into system processes, but unfortunately it's necessary in some cases.  For example, Windows Installer spawns system-hosted msiexec processes that we must virtualize for installer encapsulation -- in order to cover these processes, we need to inject into services.exe so we can intercept its calls to CreateProcess and chain inject into the child msiexecs.  On Vista, we also need to cover the Application Information Service which launches all elevated processes on the system (n.b. installers).

On Vista with UAC enabled, getting into system processes is more complex than injecting in the normal way -- the CreateRemoteThread / LoadLibrary injection technique fails when run from other accounts.  We need to inject from the System account, and the only way to run under that account is as a service.  Since installers have the requisite privileges to install services, we have the opportunity we need to get the procedure started.  We'd like our service to perform the injection when it starts and shut down thereafter -- it would be preferable not to introduce an agent -- but unfortunately, we probably won't have the privileges to start the service in later sessions.  As a consequence, the service has to stay resident, waiting for injection commands.

We configure our service to start at boot time, and inside the service we create an event in the global namespace with world-accessible privileges.  (As an aside, global objects created within the system account are globally accessible, but those created in other accounts are not under Vista.)  The service waits on the event to perform an injection, and the UAC-restricted application simply sets the event when it wants to inject into system processes.  Interestingly, Vista's CreateRemoteThread restrictions work both ways -- user account-hosted code can't inject into system account-hosted processes, and our service can't inject into processes in user accounts.  So we not only advise the service to inject, but also inject ourselves in the normal way.

One can avoid all this complexity simply by using the AppInit_DLLs registry key which is (surprisingly, given Microsoft's security focus) still present in Vista.  For our purposes, however, we wanted greater control over which processes we cover and when we cover them.