1007 lines
26 KiB
C++
1007 lines
26 KiB
C++
|
/*++
|
||
|
|
||
|
Copyright (c) 2000 Microsoft Corporation
|
||
|
|
||
|
Module Name:
|
||
|
|
||
|
WoWTask.cpp
|
||
|
|
||
|
Abstract:
|
||
|
|
||
|
Functions that retrieve process-history related information from
|
||
|
16-bit environment. This includes the retrieval of the correct
|
||
|
__PROCESS_HISTORY that was passed in from the parent (32-bit)process
|
||
|
and tracing the process history through WOW
|
||
|
|
||
|
Notes:
|
||
|
|
||
|
|
||
|
History:
|
||
|
|
||
|
10/26/00 VadimB Created
|
||
|
|
||
|
--*/
|
||
|
|
||
|
|
||
|
#include "precomp.h"
|
||
|
|
||
|
IMPLEMENT_SHIM_BEGIN(Win2kPropagateLayer)
|
||
|
#include "ShimHookMacro.h"
|
||
|
|
||
|
#include "Win2kPropagateLayer.h"
|
||
|
|
||
|
|
||
|
typedef struct tagFINDWOWTASKDATA {
|
||
|
BOOL bFound;
|
||
|
DWORD dwProcessId;
|
||
|
DWORD dwThreadId;
|
||
|
WORD hMod16;
|
||
|
WORD hTask16;
|
||
|
|
||
|
} FINDWOWTASKDATA, *PFINDWOWTASKDATA;
|
||
|
|
||
|
|
||
|
//
|
||
|
// Dynamically Linked apis
|
||
|
//
|
||
|
// from WOW32.dll
|
||
|
//
|
||
|
|
||
|
typedef LPVOID (WINAPI *PFNWOWGetVDMPointer)(DWORD vp,
|
||
|
DWORD dwBytes,
|
||
|
BOOL fProtectedMode);
|
||
|
|
||
|
//
|
||
|
// from vdmdbg.dll - defined in the header file
|
||
|
//
|
||
|
//
|
||
|
|
||
|
typedef INT (WINAPI *PFNVDMEnumTaskWOW)(DWORD dwProcessId,
|
||
|
TASKENUMPROC fp,
|
||
|
LPARAM lparam);
|
||
|
|
||
|
//
|
||
|
// Api importing -- modules
|
||
|
//
|
||
|
WCHAR g_wszWOW32ModName[] = L"wow32.dll";
|
||
|
WCHAR g_wszVdmDbgModName[] = L"VdmDbg.dll";
|
||
|
|
||
|
//
|
||
|
// Api importing - module handles and function pointers
|
||
|
//
|
||
|
|
||
|
HMODULE g_hWow32;
|
||
|
HMODULE g_hVdmDbg;
|
||
|
BOOL g_bInitialized; // set to true when imports are initialized
|
||
|
|
||
|
PFNWOWGetVDMPointer g_pfnWOWGetVDMPointer;
|
||
|
PFNVDMEnumTaskWOW g_pfnVDMEnumTaskWOW;
|
||
|
|
||
|
extern BOOL* g_pSeparateWow;
|
||
|
|
||
|
|
||
|
//
|
||
|
// function in this module to import apis
|
||
|
//
|
||
|
|
||
|
BOOL ImportWowApis(VOID);
|
||
|
|
||
|
|
||
|
//
|
||
|
// Marcro to access 16-bit memory
|
||
|
//
|
||
|
|
||
|
|
||
|
#define SEGPTR(seg,off) ((g_pfnWOWGetVDMPointer)((((ULONG)seg) << 16) | (off), 0, TRUE))
|
||
|
|
||
|
//
|
||
|
// task enum proc, called back from vdmdbg
|
||
|
//
|
||
|
|
||
|
BOOL WINAPI MyTaskEnumProc(
|
||
|
DWORD dwThreadId,
|
||
|
WORD hMod16,
|
||
|
WORD hTask16,
|
||
|
LPARAM lParam
|
||
|
)
|
||
|
{
|
||
|
PFINDWOWTASKDATA pFindData = (PFINDWOWTASKDATA)lParam;
|
||
|
|
||
|
if (dwThreadId == pFindData->dwThreadId) {
|
||
|
pFindData->hMod16 = hMod16;
|
||
|
pFindData->hTask16 = hTask16;
|
||
|
pFindData->bFound = TRUE;
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL FindWowTask(
|
||
|
DWORD dwProcessId,
|
||
|
DWORD dwThreadId,
|
||
|
PFINDWOWTASKDATA pFindData
|
||
|
)
|
||
|
{
|
||
|
RtlZeroMemory(pFindData, sizeof(*pFindData));
|
||
|
|
||
|
pFindData->dwProcessId = dwProcessId;
|
||
|
pFindData->dwThreadId = dwThreadId;
|
||
|
|
||
|
g_pfnVDMEnumTaskWOW(dwProcessId, (TASKENUMPROC)MyTaskEnumProc, (LPARAM)pFindData);
|
||
|
|
||
|
return pFindData->bFound;
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// get the pointer to task database block from hTask
|
||
|
//
|
||
|
|
||
|
|
||
|
PTDB
|
||
|
GetTDB(
|
||
|
WORD wTDB
|
||
|
)
|
||
|
{
|
||
|
PTDB pTDB;
|
||
|
|
||
|
pTDB = (PTDB)SEGPTR(wTDB, 0);
|
||
|
if (NULL == pTDB || TDB_SIGNATURE != pTDB->TDB_sig) {
|
||
|
LOGN(
|
||
|
eDbgLevelError,
|
||
|
"[GetTDB] TDB is invalid for task 0x%x",
|
||
|
(DWORD)wTDB);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
return pTDB;
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// GetModName
|
||
|
// wTDB - TDB entry
|
||
|
// szModName - pointer to the buffer that receives module name
|
||
|
// buffer should be at least 9 characters long
|
||
|
//
|
||
|
// returns FALSE if the entry is invalid
|
||
|
|
||
|
|
||
|
BOOL
|
||
|
GetModName(
|
||
|
WORD wTDB,
|
||
|
PCH szModName
|
||
|
)
|
||
|
{
|
||
|
PTDB pTDB;
|
||
|
PCH pch;
|
||
|
|
||
|
pTDB = GetTDB(wTDB);
|
||
|
if (NULL == pTDB) {
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
RtlCopyMemory(szModName, pTDB->TDB_ModName, 8 * sizeof(CHAR)); // we have modname now
|
||
|
szModName[8] = '\0';
|
||
|
|
||
|
pch = &szModName[8];
|
||
|
while (--pch >= szModName && *pch == ' ') {
|
||
|
*pch = 0;
|
||
|
}
|
||
|
|
||
|
if( pch < szModName ) {
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// ShimGetTaskFileName
|
||
|
// IN wTask - 16-bit task handle
|
||
|
// Returns:
|
||
|
// Fully qualified exe that is running in this task's context
|
||
|
//
|
||
|
|
||
|
|
||
|
PSZ
|
||
|
ShimGetTaskFileName(
|
||
|
WORD wTask
|
||
|
)
|
||
|
{
|
||
|
PSZ pszFileName = NULL;
|
||
|
PTDB pTDB;
|
||
|
|
||
|
pTDB = GetTDB(wTask);
|
||
|
if (NULL == pTDB) {
|
||
|
// this is really bad -- the module is invalid, debug output is generated by GetTDB
|
||
|
return pszFileName;
|
||
|
}
|
||
|
|
||
|
if (NULL == pTDB->TDB_pModule) {
|
||
|
LOGN(
|
||
|
eDbgLevelError,
|
||
|
"[ShimGetTaskFileName] module pointer is NULL for 0x%x",
|
||
|
(DWORD)wTask);
|
||
|
return pszFileName;
|
||
|
}
|
||
|
|
||
|
pszFileName = (PSZ)SEGPTR(pTDB->TDB_pModule, (*(WORD *)SEGPTR(pTDB->TDB_pModule, 10)) + 8);
|
||
|
return pszFileName;
|
||
|
}
|
||
|
|
||
|
|
||
|
PSZ
|
||
|
ShimGetTaskEnvptr(
|
||
|
WORD hTask16
|
||
|
)
|
||
|
{
|
||
|
PTDB pTDB = GetTDB(hTask16);
|
||
|
PSZ pszEnv = NULL;
|
||
|
PDOSPDB pPSP;
|
||
|
|
||
|
if (NULL == pTDB) {
|
||
|
LOGN( eDbgLevelError,
|
||
|
"[ShimGetTaskEnvptr] Bad TDB entry 0x%x", hTask16);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Prepare environment data - this buffer is used when we're starting a new task from the
|
||
|
// root of the chain (as opposed to spawning from an existing 16-bit task)
|
||
|
//
|
||
|
|
||
|
pPSP = (PDOSPDB)SEGPTR(pTDB->TDB_PDB, 0); // psp
|
||
|
|
||
|
if (pPSP != NULL) {
|
||
|
pszEnv = (PCH)SEGPTR(pPSP->PDB_environ, 0);
|
||
|
}
|
||
|
|
||
|
return pszEnv;
|
||
|
}
|
||
|
|
||
|
|
||
|
// IsWowExec
|
||
|
// IN wTDB - entry into the task database
|
||
|
// Returns:
|
||
|
// TRUE if this particular entry points to WOWEXEC
|
||
|
//
|
||
|
// Note:
|
||
|
// WOWEXEC is a special stub module that always runs on NTVDM
|
||
|
// new tasks are spawned by wowexec (in the most typical case)
|
||
|
// it is therefore the "root" module and it's environment's contents
|
||
|
// should not be counted, since we don't know what was ntvdm's parent process
|
||
|
//
|
||
|
|
||
|
BOOL
|
||
|
IsWOWExec(
|
||
|
WORD wTDB
|
||
|
)
|
||
|
{
|
||
|
PTDB pTDB;
|
||
|
CHAR szModName[9];
|
||
|
|
||
|
pTDB = GetTDB(wTDB);
|
||
|
if (NULL == pTDB) {
|
||
|
LOGN(
|
||
|
eDbgLevelError,
|
||
|
"[IsWOWExec] Bad TDB entry 0x%x",
|
||
|
(DWORD)wTDB);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
if (!GetModName(wTDB, szModName)) { // can we get modname ?
|
||
|
LOGN(
|
||
|
eDbgLevelError,
|
||
|
"[IsWOWExec] GetModName failed.");
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
return (0 == _strcmpi(szModName, "wowexec")); // is the module named WOWEXEC ?
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// ImportWowApis
|
||
|
// Function imports necessary apis from wow32.dll and vdmdbg.dll
|
||
|
//
|
||
|
//
|
||
|
|
||
|
BOOL
|
||
|
ImportWowApis(
|
||
|
VOID
|
||
|
)
|
||
|
{
|
||
|
g_hWow32 = LoadLibraryW(g_wszWOW32ModName);
|
||
|
|
||
|
if (g_hWow32 == NULL) {
|
||
|
LOGN(
|
||
|
eDbgLevelError,
|
||
|
"[ImportWowApis] Failed to load wow32.dll Error 0x%x",
|
||
|
GetLastError());
|
||
|
goto Fail;
|
||
|
}
|
||
|
|
||
|
g_pfnWOWGetVDMPointer = (PFNWOWGetVDMPointer)GetProcAddress(g_hWow32, "WOWGetVDMPointer");
|
||
|
|
||
|
if (g_pfnWOWGetVDMPointer == NULL) {
|
||
|
LOGN(
|
||
|
eDbgLevelError,
|
||
|
"[ImportWowApis] Failed to get address of WOWGetVDMPointer Error 0x%x",
|
||
|
GetLastError());
|
||
|
goto Fail;
|
||
|
}
|
||
|
|
||
|
g_hVdmDbg = LoadLibraryW(g_wszVdmDbgModName);
|
||
|
|
||
|
if (g_hVdmDbg == NULL) {
|
||
|
LOGN(
|
||
|
eDbgLevelError,
|
||
|
"[ImportWowApis] Failed to load vdmdbg.dll Error 0x%x",
|
||
|
GetLastError());
|
||
|
goto Fail;
|
||
|
}
|
||
|
|
||
|
g_pfnVDMEnumTaskWOW = (PFNVDMEnumTaskWOW)GetProcAddress(g_hVdmDbg, "VDMEnumTaskWOW");
|
||
|
|
||
|
if (g_pfnVDMEnumTaskWOW == NULL) {
|
||
|
LOGN(
|
||
|
eDbgLevelError,
|
||
|
"[ImportWowApis] Failed to get address of VDMEnumTaskWOW Error 0x%x",
|
||
|
GetLastError());
|
||
|
goto Fail;
|
||
|
}
|
||
|
|
||
|
g_bInitialized = TRUE;
|
||
|
|
||
|
return TRUE;
|
||
|
|
||
|
Fail:
|
||
|
|
||
|
if (g_hWow32) {
|
||
|
FreeLibrary(g_hWow32);
|
||
|
g_hWow32 = NULL;
|
||
|
}
|
||
|
if (g_hVdmDbg) {
|
||
|
FreeLibrary(g_hVdmDbg);
|
||
|
g_hVdmDbg = NULL;
|
||
|
}
|
||
|
g_pfnWOWGetVDMPointer = NULL;
|
||
|
g_pfnVDMEnumTaskWOW = NULL;
|
||
|
|
||
|
return FALSE;
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
/////////////////////////////////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
//
|
||
|
// WOWTaskList
|
||
|
//
|
||
|
// We maintain a shadow list of running wow tasks complete with respective process history and
|
||
|
// inherited process history
|
||
|
//
|
||
|
//
|
||
|
|
||
|
typedef struct tagWOWTASKLISTITEM* PWOWTASKLISTITEM;
|
||
|
|
||
|
typedef struct tagWOWTASKLISTITEM {
|
||
|
|
||
|
WORD hTask16; // 16-bit tdb entry
|
||
|
|
||
|
DWORD dwThreadId; // thread id of the task
|
||
|
|
||
|
WOWENVDATA EnvData; // environment data (process history, compat layer, etc)
|
||
|
|
||
|
PWOWTASKLISTITEM pTaskNext;
|
||
|
|
||
|
} WOWTASKLISTITEM;
|
||
|
|
||
|
PWOWTASKLISTITEM g_pWowTaskList;
|
||
|
|
||
|
|
||
|
/*++
|
||
|
|
||
|
FindWowTaskInfo
|
||
|
|
||
|
IN hTask16 16-bit task's handle
|
||
|
IN dwThreadId OPTIONAL 32-bit thread id of the task, might be 0
|
||
|
|
||
|
Returns: pointer to the task information structure
|
||
|
|
||
|
--*/
|
||
|
|
||
|
PWOWTASKLISTITEM
|
||
|
FindWowTaskInfo(
|
||
|
WORD hTask16,
|
||
|
DWORD dwThreadId
|
||
|
)
|
||
|
{
|
||
|
PWOWTASKLISTITEM pTask = g_pWowTaskList;
|
||
|
|
||
|
while (NULL != pTask) {
|
||
|
|
||
|
if (hTask16 == pTask->hTask16) {
|
||
|
|
||
|
if (dwThreadId == 0 || dwThreadId == pTask->dwThreadId) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pTask = pTask->pTaskNext;
|
||
|
}
|
||
|
|
||
|
return pTask;
|
||
|
}
|
||
|
|
||
|
/*++
|
||
|
|
||
|
UpdateWowTaskList
|
||
|
|
||
|
IN hTask16 16-bit task's handle
|
||
|
|
||
|
Returns: True if the task was added successfully
|
||
|
Note: wowexec is not among the "legitimate" tasks
|
||
|
|
||
|
--*/
|
||
|
|
||
|
|
||
|
BOOL
|
||
|
UpdateWowTaskList(
|
||
|
WORD hTask16
|
||
|
)
|
||
|
{
|
||
|
PTDB pTDB;
|
||
|
WORD wTaskParent;
|
||
|
PWOWTASKLISTITEM pTaskParent = NULL;
|
||
|
LPSTR lpszFileName;
|
||
|
PSZ pszEnv;
|
||
|
WOWENVDATA EnvData;
|
||
|
PWOWENVDATA pData = NULL;
|
||
|
DWORD dwLength;
|
||
|
PWOWTASKLISTITEM pTaskNew;
|
||
|
PCH pBuffer;
|
||
|
PDOSPDB pPSP;
|
||
|
BOOL bSuccess;
|
||
|
|
||
|
//
|
||
|
// see that we are initialized, import apis
|
||
|
//
|
||
|
if (!g_bInitialized) { // first call, link apis
|
||
|
bSuccess = ImportWowApis();
|
||
|
if (!bSuccess) {
|
||
|
LOGN(
|
||
|
eDbgLevelError,
|
||
|
"[UpdateWowTaskList] Failed to import apis.");
|
||
|
return FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// If this task is WOWEXEC -- just return, it's not an error condition, but we don't need
|
||
|
// wowexec in our list
|
||
|
//
|
||
|
|
||
|
if (IsWOWExec(hTask16)) { // this is ok, we don't want wowexec
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// next, see what the parent item is, to do so -- access it through TDB
|
||
|
//
|
||
|
|
||
|
pTDB = GetTDB(hTask16);
|
||
|
if (NULL == pTDB) {
|
||
|
LOGN(
|
||
|
eDbgLevelError,
|
||
|
"[UpdateWowTaskList] Bad TDB entry 0x%x",
|
||
|
hTask16);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Prepare environment data - this buffer is used when we're starting a new task from the
|
||
|
// root of the chain (as opposed to spawning from an existing 16-bit task)
|
||
|
//
|
||
|
|
||
|
RtlZeroMemory(&EnvData, sizeof(EnvData));
|
||
|
pData = &EnvData;
|
||
|
|
||
|
wTaskParent = pTDB->TDB_Parent;
|
||
|
|
||
|
if (IsWOWExec(wTaskParent) || GetTDB(wTaskParent) == NULL) {
|
||
|
//
|
||
|
// Root task, extract process history, compat layer, etc
|
||
|
//
|
||
|
pszEnv = NULL;
|
||
|
pPSP = (PDOSPDB)SEGPTR(pTDB->TDB_PDB, 0); // psp
|
||
|
|
||
|
if (pPSP != NULL) {
|
||
|
pszEnv = (PCH)SEGPTR(pPSP->PDB_environ, 0);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// we have a pointer to the current environment here, pData is initialized
|
||
|
//
|
||
|
if (pszEnv != NULL) {
|
||
|
pData->pszProcessHistory = ShimFindEnvironmentVar(g_szProcessHistoryVar,
|
||
|
pszEnv,
|
||
|
&pData->pszProcessHistoryVal);
|
||
|
|
||
|
pData->pszCompatLayer = ShimFindEnvironmentVar(g_szCompatLayerVar,
|
||
|
pszEnv,
|
||
|
&pData->pszCompatLayerVal);
|
||
|
|
||
|
pData->pszShimFileLog = ShimFindEnvironmentVar(g_szShimFileLogVar,
|
||
|
pszEnv,
|
||
|
&pData->pszShimFileLogVal);
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
//
|
||
|
// Not a root task, find parent process
|
||
|
//
|
||
|
pTaskParent = FindWowTaskInfo(wTaskParent, 0); // we can't determine which thread owns the task
|
||
|
|
||
|
if (pTaskParent == NULL) {
|
||
|
//
|
||
|
// something is very wrong
|
||
|
// we can't inherit
|
||
|
//
|
||
|
|
||
|
LOGN(
|
||
|
eDbgLevelError,
|
||
|
"[UpdateWowTaskList] Task 0x%x is not root but parent not listed 0x%x",
|
||
|
(DWORD)hTask16,
|
||
|
(DWORD)wTaskParent);
|
||
|
//
|
||
|
// we still allow building up process history. The initial variables will be empty
|
||
|
//
|
||
|
|
||
|
|
||
|
} else {
|
||
|
//
|
||
|
// inherit everything from the parent and add it's module name (later)
|
||
|
//
|
||
|
|
||
|
pData = &pTaskParent->EnvData;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Get the filename involved
|
||
|
//
|
||
|
//
|
||
|
|
||
|
lpszFileName = ShimGetTaskFileName(hTask16);
|
||
|
|
||
|
//
|
||
|
// now calculate how much space is required to hold all of the data
|
||
|
//
|
||
|
|
||
|
dwLength = sizeof(WOWTASKLISTITEM) +
|
||
|
(NULL == pData->pszProcessHistory ? 0 : (strlen(pData->pszProcessHistory) + 1) * sizeof(CHAR)) +
|
||
|
(NULL == pData->pszCompatLayer ? 0 : (strlen(pData->pszCompatLayer) + 1) * sizeof(CHAR)) +
|
||
|
(NULL == pData->pszShimFileLog ? 0 : (strlen(pData->pszShimFileLog) + 1) * sizeof(CHAR)) +
|
||
|
(NULL == pData->pszCurrentProcessHistory ? 0 : (strlen(pData->pszCurrentProcessHistory) + 2) * sizeof(CHAR)) +
|
||
|
(NULL == lpszFileName ? 0 : (strlen(lpszFileName) + 1) * sizeof(CHAR));
|
||
|
|
||
|
|
||
|
pTaskNew = (PWOWTASKLISTITEM)ShimMalloc(dwLength);
|
||
|
|
||
|
if (pTaskNew == NULL) {
|
||
|
LOGN(
|
||
|
eDbgLevelError,
|
||
|
"[UpdateWowTaskList] failed to allocate 0x%x bytes",
|
||
|
dwLength);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
RtlZeroMemory(pTaskNew, dwLength);
|
||
|
|
||
|
//
|
||
|
// now this entry has to be setup
|
||
|
// process history is first
|
||
|
//
|
||
|
|
||
|
pBuffer = (PCH)(pTaskNew + 1);
|
||
|
// Keep track of how much of the buffer is left.
|
||
|
size_t cchRemaining = dwLength - sizeof(WOWTASKLISTITEM);
|
||
|
|
||
|
pTaskNew->hTask16 = hTask16;
|
||
|
pTaskNew->dwThreadId = GetCurrentThreadId();
|
||
|
|
||
|
if (pData->pszProcessHistory != NULL) {
|
||
|
|
||
|
//
|
||
|
// Copy process history. The processHistoryVal is a pointer into the buffer
|
||
|
// pointed to by pszProcessHistory: __PROCESS_HISTORY=c:\foo;c:\docs~1\install
|
||
|
// then pszProcessHistoryVal will point here ---------^
|
||
|
//
|
||
|
// we are copying the data and moving the pointer using the calculated offset
|
||
|
|
||
|
pTaskNew->EnvData.pszProcessHistory = pBuffer;
|
||
|
StringCchCopyExA(pTaskNew->EnvData.pszProcessHistory, cchRemaining, pData->pszProcessHistory, NULL, &cchRemaining, 0);
|
||
|
pTaskNew->EnvData.pszProcessHistoryVal = pTaskNew->EnvData.pszProcessHistory +
|
||
|
(INT)(pData->pszProcessHistoryVal - pData->pszProcessHistory);
|
||
|
//
|
||
|
// There is enough space in the buffer to accomodate all the strings, so
|
||
|
// move pointer past current string to point at the "empty" space
|
||
|
//
|
||
|
|
||
|
pBuffer += strlen(pData->pszProcessHistory) + 1;
|
||
|
}
|
||
|
|
||
|
if (pData->pszCompatLayer != NULL) {
|
||
|
pTaskNew->EnvData.pszCompatLayer = pBuffer;
|
||
|
StringCchCopyExA(pTaskNew->EnvData.pszCompatLayer, cchRemaining, pData->pszCompatLayer, NULL, &cchRemaining, 0);
|
||
|
pTaskNew->EnvData.pszCompatLayerVal = pTaskNew->EnvData.pszCompatLayer +
|
||
|
(INT)(pData->pszCompatLayerVal - pData->pszCompatLayer);
|
||
|
pBuffer += strlen(pData->pszCompatLayer) + 1;
|
||
|
}
|
||
|
|
||
|
if (pData->pszShimFileLog != NULL) {
|
||
|
pTaskNew->EnvData.pszShimFileLog = pBuffer;
|
||
|
StringCchCopyExA(pTaskNew->EnvData.pszShimFileLog, cchRemaining, pData->pszShimFileLog, NULL, &cchRemaining, 0);
|
||
|
pTaskNew->EnvData.pszShimFileLogVal = pTaskNew->EnvData.pszShimFileLog +
|
||
|
(INT)(pData->pszShimFileLogVal - pData->pszShimFileLog);
|
||
|
pBuffer += strlen(pData->pszShimFileLog) + 1;
|
||
|
}
|
||
|
|
||
|
if (pData->pszCurrentProcessHistory != NULL || lpszFileName != NULL) {
|
||
|
//
|
||
|
// Now process history
|
||
|
//
|
||
|
pTaskNew->EnvData.pszCurrentProcessHistory = pBuffer;
|
||
|
|
||
|
if (pData->pszCurrentProcessHistory != NULL) {
|
||
|
StringCchCopyExA(pTaskNew->EnvData.pszCurrentProcessHistory, cchRemaining, pData->pszCurrentProcessHistory, NULL, &cchRemaining, 0);
|
||
|
if (lpszFileName != NULL ) {
|
||
|
StringCchCatExA(pTaskNew->EnvData.pszCurrentProcessHistory, cchRemaining, ";", NULL, &cchRemaining, 0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (lpszFileName != NULL) {
|
||
|
StringCchCatA(pTaskNew->EnvData.pszCurrentProcessHistory, cchRemaining, lpszFileName);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
LOGN(
|
||
|
eDbgLevelInfo,
|
||
|
"[UpdateWowTaskList] Running : \"%s\"",
|
||
|
lpszFileName);
|
||
|
|
||
|
LOGN(
|
||
|
eDbgLevelInfo,
|
||
|
"[UpdateWowTaskList] ProcessHistory : \"%s\"",
|
||
|
pTaskNew->EnvData.pszCurrentProcessHistory);
|
||
|
|
||
|
LOGN(
|
||
|
eDbgLevelInfo,
|
||
|
"[UpdateWowTaskList] BaseProcessHistory: \"%s\"",
|
||
|
pTaskNew->EnvData.pszProcessHistory);
|
||
|
|
||
|
LOGN(
|
||
|
eDbgLevelInfo,
|
||
|
"[UpdateWowTaskList] CompatLayer : \"%s\"",
|
||
|
pTaskNew->EnvData.pszCompatLayer);
|
||
|
|
||
|
|
||
|
//
|
||
|
// We are done, link the entry into the list
|
||
|
//
|
||
|
pTaskNew->pTaskNext = g_pWowTaskList;
|
||
|
|
||
|
g_pWowTaskList = pTaskNew;
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*++
|
||
|
|
||
|
CleanupWowTaskList
|
||
|
|
||
|
IN hTask16 16-bit task handle that is to be removed from the list of running tasks
|
||
|
|
||
|
Returns : TRUE if the function succeeds
|
||
|
|
||
|
--*/
|
||
|
|
||
|
BOOL
|
||
|
CleanupWowTaskList(
|
||
|
WORD hTask16
|
||
|
)
|
||
|
{
|
||
|
PWOWTASKLISTITEM pTask = g_pWowTaskList;
|
||
|
PWOWTASKLISTITEM pTaskPrev = NULL;
|
||
|
|
||
|
while (pTask != NULL) {
|
||
|
|
||
|
if (pTask->hTask16 == hTask16) {
|
||
|
// this is the item
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
pTaskPrev = pTask;
|
||
|
pTask = pTask->pTaskNext;
|
||
|
}
|
||
|
|
||
|
if (pTask == NULL) {
|
||
|
LOGN(
|
||
|
eDbgLevelError,
|
||
|
"[CleanupWowTaskList] Failed to locate task information for 0x%x",
|
||
|
(DWORD)hTask16);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
|
||
|
if (pTaskPrev == NULL) {
|
||
|
|
||
|
g_pWowTaskList = pTask->pTaskNext;
|
||
|
|
||
|
} else {
|
||
|
|
||
|
pTaskPrev->pTaskNext = pTask->pTaskNext;
|
||
|
|
||
|
}
|
||
|
|
||
|
ShimFree(pTask);
|
||
|
|
||
|
return TRUE;
|
||
|
|
||
|
}
|
||
|
|
||
|
/*++
|
||
|
|
||
|
ShimRetrieveVariablesEx
|
||
|
|
||
|
IN pData Structure that receives pointers to all the relevant environment information
|
||
|
for the calling thread. The threads are scheduled non-preemptively by user and
|
||
|
threadid is used to identify the calling 16-bit task
|
||
|
All the real work on information retrieval is done in UpdateWowTaskList
|
||
|
|
||
|
Returns: TRUE if success
|
||
|
--*/
|
||
|
|
||
|
BOOL
|
||
|
ShimRetrieveVariablesEx(
|
||
|
PWOWENVDATA pData
|
||
|
)
|
||
|
{
|
||
|
DWORD dwProcessId = GetCurrentProcessId();
|
||
|
DWORD dwThreadId = GetCurrentThreadId();
|
||
|
PWOWTASKLISTITEM pTask;
|
||
|
FINDWOWTASKDATA FindData;
|
||
|
WORD hTask;
|
||
|
BOOL bSuccess;
|
||
|
|
||
|
RtlZeroMemory(pData, sizeof(*pData));
|
||
|
|
||
|
if (!g_bInitialized) { // first call, link apis
|
||
|
bSuccess = ImportWowApis();
|
||
|
if (!bSuccess) {
|
||
|
LOGN(
|
||
|
eDbgLevelError,
|
||
|
"[ShimRetrieveVariablesEx] Failed to import apis.");
|
||
|
return FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!FindWowTask(dwProcessId, dwThreadId, &FindData)) {
|
||
|
LOGN(
|
||
|
eDbgLevelError,
|
||
|
"[ShimRetrieveVariablesEx] Task not found ProcessId 0x%x ThreadId 0x%x",
|
||
|
dwProcessId,
|
||
|
dwThreadId);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
hTask = FindData.hTask16;
|
||
|
|
||
|
pTask = FindWowTaskInfo(hTask, dwThreadId);
|
||
|
|
||
|
if (pTask == NULL) {
|
||
|
LOGN(
|
||
|
eDbgLevelError,
|
||
|
"[ShimRetrieveVariablesEx] Failed to locate wow task.");
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Found this one. Copy the info.
|
||
|
//
|
||
|
RtlMoveMemory(pData, &pTask->EnvData, sizeof(*pData));
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
/*++
|
||
|
ShimThisProcess
|
||
|
|
||
|
Function invokes Shim Engine for dynamic shimming of the current process
|
||
|
Which happens to be ntvdm, naturally. This ntvdm is a separate ntvdm
|
||
|
(which is insured through various checks in CheckAndShimNTVDM)
|
||
|
|
||
|
--*/
|
||
|
|
||
|
BOOL
|
||
|
ShimThisProcess(
|
||
|
HMODULE hModShimEngine,
|
||
|
HSDB hSDB,
|
||
|
SDBQUERYRESULT* pQueryResult
|
||
|
)
|
||
|
{
|
||
|
typedef BOOL (WINAPI *PFNDynamicShim)(LPCWSTR , HSDB , SDBQUERYRESULT*, LPCSTR, LPDWORD);
|
||
|
PFNDynamicShim pfnDynamicShim = NULL;
|
||
|
WCHAR wszFileName[MAX_PATH];
|
||
|
DWORD dwLength;
|
||
|
DWORD dwDynamicToken = 0;
|
||
|
|
||
|
pfnDynamicShim = (PFNDynamicShim) GetProcAddress(hModShimEngine, "SE_DynamicShim");
|
||
|
if (NULL == pfnDynamicShim) {
|
||
|
LOGN( eDbgLevelError,
|
||
|
"[ShimThisProcess] failed to obtain dynamic shim proc address\n");
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
dwLength = GetModuleFileNameW(GetModuleHandle(NULL), wszFileName, CHARCOUNT(wszFileName));
|
||
|
if (!dwLength || dwLength == CHARCOUNT(wszFileName)) {
|
||
|
LOGN( eDbgLevelError,
|
||
|
"[ShimThisProcess] failed to obtain module file name\n");
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
return pfnDynamicShim(wszFileName, hSDB, pQueryResult, NULL, &dwDynamicToken);
|
||
|
}
|
||
|
|
||
|
/*++
|
||
|
CheckAndShimNTVDM
|
||
|
|
||
|
Procedure checks ntvdm application for having to be shimmed. If an application is located in
|
||
|
appcompat database, this ntvdm would have to be running as a separate ntvdm (explorer is shimmed as
|
||
|
well, as a result it will have checked the binary first and set the separate vdm flag in CreateProcess)
|
||
|
|
||
|
Further, this call comes through InitTask (intercepted between ntvdm and user32) -- as a parameter it
|
||
|
takes hTask16 - which we're able to use to retrieve application's environment and other important
|
||
|
information.
|
||
|
--*/
|
||
|
|
||
|
|
||
|
BOOL
|
||
|
CheckAndShimNTVDM(
|
||
|
WORD hTask16
|
||
|
)
|
||
|
{
|
||
|
HMODULE hModShimEngine;
|
||
|
CString csTaskFileName;
|
||
|
PSZ pszEnv = NULL;
|
||
|
PTDB pTDB = NULL;
|
||
|
PVOID pEnvNew = NULL;
|
||
|
BOOL bSuccess = FALSE;
|
||
|
BOOL bMatch;
|
||
|
BOOL bNewEnv = FALSE;
|
||
|
HSDB hSDB;
|
||
|
NTSTATUS Status;
|
||
|
SDBQUERYRESULT QueryResult;
|
||
|
DWORD dwFlags;
|
||
|
|
||
|
hModShimEngine = GetModuleHandle(TEXT("shim.dll"));
|
||
|
if (hModShimEngine == NULL) {
|
||
|
// impossible -- shim.dll is not injected!!!
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
if (g_pSeparateWow != NULL && *g_pSeparateWow == FALSE) {
|
||
|
//
|
||
|
// not a separate wow
|
||
|
//
|
||
|
LOGN( eDbgLevelError,
|
||
|
"[CheckAndShimNTVDM] running in shared wow, no shimming\n");
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
if (!g_bInitialized) { // first call, link apis
|
||
|
bSuccess = ImportWowApis();
|
||
|
if (!bSuccess) {
|
||
|
LOGN( eDbgLevelError,
|
||
|
"[CheckAndShimNTVDM] Failed to import apis.\n");
|
||
|
return FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (IsWOWExec(hTask16)) {
|
||
|
LOGN( eDbgLevelError,
|
||
|
"[CheckAndShimNTVDM] not touching wowexec\n");
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
csTaskFileName = ShimGetTaskFileName(hTask16);
|
||
|
if (csTaskFileName.IsEmpty()) {
|
||
|
LOGN( eDbgLevelError,
|
||
|
"[CheckAndShimNTVDM] failed to get the filename for task 0x%lx\n", hTask16);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// init database
|
||
|
//
|
||
|
hSDB = SdbInitDatabase(0, NULL);
|
||
|
|
||
|
if (hSDB == NULL) {
|
||
|
LOGN( eDbgLevelError,
|
||
|
"[CheckAndShimNTVDM] failed to init shim database\n");
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// process history please --
|
||
|
// if we end up here, we are a separate ntvdm
|
||
|
// running with a process history in the env, was retrieved in init
|
||
|
//
|
||
|
|
||
|
pTDB = GetTDB(hTask16);
|
||
|
if (NULL == pTDB) {
|
||
|
LOGN( eDbgLevelError, "[UpdateWowTaskList] Bad TDB entry 0x%x", hTask16);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Prepare environment data - this buffer is used when we're starting a new task from the
|
||
|
// root of the chain (as opposed to spawning from an existing 16-bit task)
|
||
|
//
|
||
|
|
||
|
pszEnv = ShimGetTaskEnvptr(hTask16);
|
||
|
if (NULL != pszEnv) {
|
||
|
Status = ShimCloneEnvironment(&pEnvNew, (LPVOID)pszEnv, FALSE);
|
||
|
if (!NT_SUCCESS(Status)) {
|
||
|
LOGN( eDbgLevelError,
|
||
|
"[CheckAndShimNTVDM] cannot clone environment 0x%lx\n", Status);
|
||
|
pEnvNew = NULL;
|
||
|
bNewEnv = TRUE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// if this call has come the way of VDM - we need to carry over our environment stuff
|
||
|
// which is stored separately in this shim
|
||
|
//
|
||
|
// should the call to ShimCloneEnvironment fail, we will have pEnvNew == NULL
|
||
|
// and bNewEnv = TRUE, as a result, we shall try again to clone the environment
|
||
|
|
||
|
|
||
|
dwFlags = CREATE_UNICODE_ENVIRONMENT;
|
||
|
pEnvNew = ShimCreateWowEnvironment_U(pEnvNew, &dwFlags, bNewEnv);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// run detection please
|
||
|
//
|
||
|
|
||
|
bMatch = SdbGetMatchingExe(hSDB,
|
||
|
(LPCWSTR)csTaskFileName,
|
||
|
NULL, // we can give out module name as well -- but WHY?
|
||
|
(LPCWSTR)pEnvNew,
|
||
|
0,
|
||
|
&QueryResult);
|
||
|
|
||
|
if (bMatch) {
|
||
|
bSuccess = ShimThisProcess(hModShimEngine, hSDB, &QueryResult);
|
||
|
}
|
||
|
|
||
|
if (pEnvNew != NULL) {
|
||
|
ShimFreeEnvironment(pEnvNew);
|
||
|
}
|
||
|
|
||
|
return bSuccess;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
IMPLEMENT_SHIM_END
|
||
|
|