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.
2509 lines
63 KiB
2509 lines
63 KiB
/*++
|
|
|
|
Copyright (c) 2002 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
exts.c
|
|
|
|
Abstract:
|
|
|
|
This file implements the debugger extentions for shimeng/shims.
|
|
|
|
Environment:
|
|
|
|
User Mode
|
|
|
|
History:
|
|
|
|
03/14/2002 maonis Created
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
|
|
extern "C" {
|
|
#include "shimdb.h"
|
|
}
|
|
|
|
// We're using the high 4 bits of the TAGID to say what PDB the TAGID is from.
|
|
#define PDB_MAIN 0x00000000
|
|
#define PDB_TEST 0x10000000
|
|
#define PDB_LOCAL 0x20000000
|
|
|
|
// Used to get the tag ref from the tagid, the low 28 bits
|
|
#define TAGREF_STRIP_TAGID 0x0FFFFFFF
|
|
|
|
// Used to get the PDB from the tagid, the high 4 bits
|
|
#define TAGREF_STRIP_PDB 0xF0000000
|
|
|
|
BOOL
|
|
GetData(
|
|
IN ULONG64 Address,
|
|
IN OUT LPVOID ptr,
|
|
IN ULONG size)
|
|
{
|
|
BOOL b;
|
|
ULONG BytesRead = 0;
|
|
|
|
b = ReadMemory(Address, ptr, size, &BytesRead );
|
|
|
|
if (!b || BytesRead != size ) {
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//void
|
|
//GetAndCheckFieldValue(
|
|
// IN ULONG64 p,
|
|
// IN LPCSTR pszType,
|
|
// IN LPCSTR pszField,
|
|
// OUT ULONG64 value
|
|
// )
|
|
#define GET_AND_CHECK_FIELDVALUE(p, Type, Field, value) \
|
|
{ \
|
|
if (GetFieldValue(p, Type, Field, value)) { \
|
|
dprintf("failed to get the value of %s for %08x of type %s\n", \
|
|
Field, p, Type); \
|
|
goto EXIT; \
|
|
} \
|
|
}
|
|
|
|
#define GET_AND_CHECK_FIELDVALUE_DATA(p, Type, Field, data, length) \
|
|
{ \
|
|
ULONG64 value; \
|
|
GET_AND_CHECK_FIELDVALUE(p, Type, Field, value); \
|
|
if (!GetData(value, data, length)) { \
|
|
dprintf("failed to read in %d bytes at %08x\n", length, value); \
|
|
goto EXIT; \
|
|
} \
|
|
}
|
|
|
|
#define GET_AND_CHECK_DATA(value, data,length) \
|
|
{ \
|
|
if (!GetData(value, data, length)) { \
|
|
dprintf("failed to read in %d bytes at %08x\n", length, value); \
|
|
goto EXIT; \
|
|
} \
|
|
}
|
|
|
|
#define GET_SHIMINFO_eInExMode(p, eInExMode) \
|
|
GET_AND_CHECK_FIELDVALUE(p, "shimeng!tagSHIMINFO", "eInExMode", eInExMode);
|
|
|
|
#define GET_SHIMINFO_pFirstExclude(p, pFirstExclude) \
|
|
GET_AND_CHECK_FIELDVALUE(p, "shimeng!tagSHIMINFO", "pFirstExclude", pFirstExclude);
|
|
|
|
#define GET_SHIMINFO_pFirstInclude(p, pFirstInclude) \
|
|
GET_AND_CHECK_FIELDVALUE(p, "shimeng!tagSHIMINFO", "pFirstExclude", pFirstInclude);
|
|
|
|
#define GET_SHIMINFO_wszName(p, wszName) \
|
|
if (GetFieldData( \
|
|
p, \
|
|
"shimeng!tagSHIMINFO", \
|
|
"wszName", \
|
|
MAX_SHIM_NAME_LEN * sizeof(WCHAR), \
|
|
wszName)) \
|
|
{ \
|
|
dprintf("Failed to get the wszName field of shim info %08x\n", p); \
|
|
goto EXIT; \
|
|
}
|
|
|
|
#define SHIM_DEBUG_LEVEL_SYMBOL_SUFFIX "!ShimLib::g_DebugLevel"
|
|
#define SHIM_DEBUG_LEVEL_TYPE_SUFFIX "!ShimLib::DEBUGLEVEL"
|
|
|
|
typedef enum tagINEX_MODE {
|
|
INEX_UNINITIALIZED = 0,
|
|
EXCLUDE_SYSTEM32,
|
|
EXCLUDE_ALL,
|
|
INCLUDE_ALL
|
|
} INEX_MODE, *PINEX_MODE;
|
|
|
|
#define MAX_SHIM_NAME_LEN 64
|
|
|
|
#define MAX_SHIM_DLLS 8
|
|
#define MAX_SHIM_DEBUGLEVEL_SYMBOL_LEN 64
|
|
#define MAX_SHIM_DLL_BASE_NAME_LEN 32
|
|
|
|
typedef struct tagSHIMDLLINFO {
|
|
ULONG64 pDllBase;
|
|
char szDllBaseName[MAX_SHIM_DLL_BASE_NAME_LEN];
|
|
} SHIMDLLINFO, *PSHIMDLLINFO;
|
|
|
|
SHIMDLLINFO g_rgShimDllNames[MAX_SHIM_DLLS];
|
|
|
|
DWORD g_dwShimDlls = 0;
|
|
|
|
#define MAX_API_NAME_LEN 32
|
|
#define MAX_MODULE_NAME_LEN 16
|
|
#define MAX_DLL_IMAGE_NAME_LEN 128
|
|
|
|
char g_szSystem32Dir[MAX_DLL_IMAGE_NAME_LEN] = "";
|
|
DWORD g_dwSystem32DirLen = 0;
|
|
|
|
//
|
|
// Valid for the lifetime of the debug session.
|
|
//
|
|
|
|
WINDBG_EXTENSION_APIS ExtensionApis;
|
|
|
|
//
|
|
// Valid only during an extension API call
|
|
//
|
|
|
|
PDEBUG_ADVANCED g_ExtAdvanced;
|
|
PDEBUG_CLIENT g_ExtClient;
|
|
PDEBUG_CONTROL g_ExtControl;
|
|
PDEBUG_DATA_SPACES g_ExtData;
|
|
PDEBUG_REGISTERS g_ExtRegisters;
|
|
PDEBUG_SYMBOLS2 g_ExtSymbols;
|
|
PDEBUG_SYSTEM_OBJECTS3 g_ExtSystem;
|
|
|
|
// Queries for all debugger interfaces.
|
|
extern "C" HRESULT
|
|
ExtQuery(PDEBUG_CLIENT Client)
|
|
{
|
|
HRESULT Status;
|
|
|
|
if ((Status = Client->QueryInterface(__uuidof(IDebugAdvanced),
|
|
(void **)&g_ExtAdvanced)) != S_OK)
|
|
{
|
|
goto Fail;
|
|
}
|
|
if ((Status = Client->QueryInterface(__uuidof(IDebugControl),
|
|
(void **)&g_ExtControl)) != S_OK)
|
|
{
|
|
goto Fail;
|
|
}
|
|
if ((Status = Client->QueryInterface(__uuidof(IDebugDataSpaces),
|
|
(void **)&g_ExtData)) != S_OK)
|
|
{
|
|
goto Fail;
|
|
}
|
|
if ((Status = Client->QueryInterface(__uuidof(IDebugRegisters),
|
|
(void **)&g_ExtRegisters)) != S_OK)
|
|
{
|
|
goto Fail;
|
|
}
|
|
if ((Status = Client->QueryInterface(__uuidof(IDebugSymbols2),
|
|
(void **)&g_ExtSymbols)) != S_OK)
|
|
{
|
|
goto Fail;
|
|
}
|
|
if ((Status = Client->QueryInterface(__uuidof(IDebugSystemObjects),
|
|
(void **)&g_ExtSystem)) != S_OK)
|
|
{
|
|
goto Fail;
|
|
}
|
|
|
|
g_ExtClient = Client;
|
|
|
|
return S_OK;
|
|
|
|
Fail:
|
|
ExtRelease();
|
|
return Status;
|
|
}
|
|
|
|
// Cleans up all debugger interfaces.
|
|
void
|
|
ExtRelease(void)
|
|
{
|
|
g_ExtClient = NULL;
|
|
EXT_RELEASE(g_ExtAdvanced);
|
|
EXT_RELEASE(g_ExtControl);
|
|
EXT_RELEASE(g_ExtData);
|
|
EXT_RELEASE(g_ExtRegisters);
|
|
EXT_RELEASE(g_ExtSymbols);
|
|
EXT_RELEASE(g_ExtSystem);
|
|
}
|
|
|
|
// Normal output.
|
|
void __cdecl
|
|
ExtOut(PCSTR Format, ...)
|
|
{
|
|
va_list Args;
|
|
|
|
va_start(Args, Format);
|
|
g_ExtControl->OutputVaList(DEBUG_OUTPUT_NORMAL, Format, Args);
|
|
va_end(Args);
|
|
}
|
|
|
|
// Error output.
|
|
void __cdecl
|
|
ExtErr(PCSTR Format, ...)
|
|
{
|
|
va_list Args;
|
|
|
|
va_start(Args, Format);
|
|
g_ExtControl->OutputVaList(DEBUG_OUTPUT_ERROR, Format, Args);
|
|
va_end(Args);
|
|
}
|
|
|
|
// Warning output.
|
|
void __cdecl
|
|
ExtWarn(PCSTR Format, ...)
|
|
{
|
|
va_list Args;
|
|
|
|
va_start(Args, Format);
|
|
g_ExtControl->OutputVaList(DEBUG_OUTPUT_WARNING, Format, Args);
|
|
va_end(Args);
|
|
}
|
|
|
|
// Verbose output.
|
|
void __cdecl
|
|
ExtVerb(PCSTR Format, ...)
|
|
{
|
|
va_list Args;
|
|
|
|
va_start(Args, Format);
|
|
g_ExtControl->OutputVaList(DEBUG_OUTPUT_VERBOSE, Format, Args);
|
|
va_end(Args);
|
|
}
|
|
|
|
|
|
extern "C"
|
|
HRESULT
|
|
CALLBACK
|
|
DebugExtensionInitialize(PULONG Version, PULONG Flags)
|
|
{
|
|
IDebugClient *DebugClient;
|
|
PDEBUG_CONTROL DebugControl;
|
|
HRESULT Hr;
|
|
|
|
*Version = DEBUG_EXTENSION_VERSION(1, 0);
|
|
*Flags = 0;
|
|
|
|
|
|
if ((Hr = DebugCreate(__uuidof(IDebugClient),
|
|
(void **)&DebugClient)) != S_OK)
|
|
{
|
|
return Hr;
|
|
}
|
|
if ((Hr = DebugClient->QueryInterface(__uuidof(IDebugControl),
|
|
(void **)&DebugControl)) != S_OK)
|
|
{
|
|
return Hr;
|
|
}
|
|
|
|
ExtensionApis.nSize = sizeof (ExtensionApis);
|
|
if ((Hr = DebugControl->GetWindbgExtensionApis64(&ExtensionApis)) != S_OK) {
|
|
return Hr;
|
|
}
|
|
|
|
DebugControl->Release();
|
|
DebugClient->Release();
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
extern "C"
|
|
void
|
|
CALLBACK
|
|
DebugExtensionNotify(ULONG Notify, ULONG64 Argument)
|
|
{
|
|
return;
|
|
}
|
|
|
|
extern "C"
|
|
void
|
|
CALLBACK
|
|
DebugExtensionUninitialize(void)
|
|
{
|
|
return;
|
|
}
|
|
|
|
|
|
DllInit(
|
|
HANDLE hModule,
|
|
DWORD dwReason,
|
|
DWORD dwReserved
|
|
)
|
|
{
|
|
switch (dwReason) {
|
|
case DLL_THREAD_ATTACH:
|
|
break;
|
|
|
|
case DLL_THREAD_DETACH:
|
|
break;
|
|
|
|
case DLL_PROCESS_DETACH:
|
|
break;
|
|
|
|
case DLL_PROCESS_ATTACH:
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
This reads a ULONG64 value for the specified variable name from the debugee.
|
|
|
|
History:
|
|
|
|
03/14/2002 maonis Created
|
|
|
|
--*/
|
|
BOOL
|
|
GetVarValueULONG64(
|
|
IN LPCSTR pszVarName,
|
|
OUT ULONG64* pVarValue
|
|
)
|
|
{
|
|
ULONG64 VarAddr = GetExpression(pszVarName);
|
|
|
|
if (!VarAddr) {
|
|
dprintf("Failed to get the address of %s\n", pszVarName);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!ReadPointer(VarAddr, pVarValue)) {
|
|
dprintf("Failed to read the value of %s\n", pszVarName);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
This writes a ULONG64 value for the specified variable name from the debugee.
|
|
|
|
History:
|
|
|
|
03/14/2002 maonis Created
|
|
|
|
--*/
|
|
BOOL
|
|
SetVarValueULONG64(
|
|
IN LPCSTR pszVarName,
|
|
IN ULONG64 VarValue
|
|
)
|
|
{
|
|
ULONG64 VarAddr = GetExpression(pszVarName);
|
|
|
|
if (!VarAddr) {
|
|
dprintf("Failed to get the address of %s\n", pszVarName);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!WritePointer(VarAddr, VarValue)) {
|
|
dprintf("Failed to read the value of %s\n", pszVarName);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
Returns a string for the symbol that matches the value at
|
|
address dwAddr, or "".
|
|
|
|
History:
|
|
|
|
03/12/2002 maonis Created
|
|
|
|
--*/
|
|
void
|
|
PrintSymbolAtAddress(ULONG64 Addr)
|
|
{
|
|
CHAR szSymbol[128];
|
|
ULONG64 Displacement;
|
|
|
|
GetSymbol(Addr, szSymbol, &Displacement);
|
|
|
|
if (strcmp(szSymbol, "") != 0) {
|
|
|
|
dprintf(" (%s", szSymbol);
|
|
|
|
if (Displacement) {
|
|
dprintf("+%08x", Displacement);
|
|
}
|
|
|
|
dprintf(") ");
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
IsShimInitialized()
|
|
{
|
|
ULONG64 Value;
|
|
BOOL bIsShimInitialized;
|
|
|
|
if (GetVarValueULONG64("shimeng!g_bShimInitialized", &Value)) {
|
|
|
|
bIsShimInitialized = (BOOL)Value;
|
|
|
|
if (bIsShimInitialized) {
|
|
//dprintf("Shim has been initialized\n");
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
dprintf("Shim(s) have not been initialized\n");
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL
|
|
CheckForFullPath(
|
|
LPSTR pszPath
|
|
)
|
|
{
|
|
if (pszPath) {
|
|
|
|
LPSTR pszSlash = strchr(pszPath, L'\\');
|
|
|
|
if (pszSlash) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
dprintf("The module info is not yet fully available to us. Please "
|
|
"do .reload -s to load the module info.\n");
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
Get the module and the loaded module name. The former is the base name;
|
|
the latter has the full path.
|
|
|
|
Note that if the symbols are not loaded correctly, or if the load module
|
|
event hasn't occured, we can't get the full path to some modules. In this
|
|
case the user will be prompted to do a .reload -s to make the full path of
|
|
the loaded modules available.
|
|
|
|
--*/
|
|
HRESULT
|
|
GetDllNamesByIndexAndBase(
|
|
ULONG Index,
|
|
ULONG64 Base,
|
|
LPSTR pszModuleName,
|
|
DWORD dwModuleNameSize,
|
|
LPSTR pszImageName,
|
|
DWORD dwImageNameSize
|
|
)
|
|
{
|
|
HRESULT hr = g_ExtSymbols->GetModuleNames(
|
|
Index,
|
|
Base,
|
|
pszImageName,
|
|
dwImageNameSize,
|
|
NULL,
|
|
pszModuleName,
|
|
dwModuleNameSize,
|
|
NULL,
|
|
NULL,
|
|
0,
|
|
NULL);
|
|
|
|
if (hr == S_OK) {
|
|
|
|
if (pszImageName && !CheckForFullPath(pszImageName)) {
|
|
hr = E_FAIL;
|
|
}
|
|
|
|
} else {
|
|
dprintf(
|
|
"GetModuleName returned %08x for index %d, base %08x\n",
|
|
hr,
|
|
Index,
|
|
Base);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
GetDllNameByOffset(
|
|
PVOID pDllBase,
|
|
LPSTR pszModuleName,
|
|
DWORD dwModuleNameSize,
|
|
LPSTR pszImageName,
|
|
DWORD dwImageNameSize
|
|
)
|
|
{
|
|
ULONG64 Base = 0;
|
|
ULONG Index = 0;
|
|
HRESULT hr;
|
|
|
|
hr = g_ExtSymbols->GetModuleByOffset((ULONG64)pDllBase, 0, &Index, &Base);
|
|
|
|
if (hr != S_OK) {
|
|
|
|
dprintf("GetModuleByOffset returned %08x for dll base %08x\n", hr, pDllBase);
|
|
return hr;
|
|
}
|
|
|
|
if (Base) {
|
|
|
|
hr = GetDllNamesByIndexAndBase(
|
|
Index,
|
|
Base,
|
|
pszModuleName,
|
|
dwModuleNameSize,
|
|
pszImageName,
|
|
dwImageNameSize);
|
|
} else {
|
|
|
|
dprintf("GetModuleByOffset succeeded but couldn't get the base address?!\n");
|
|
hr = E_UNEXPECTED;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
GetDllImageNameByModuleName(
|
|
PCSTR pszModuleName, // dll name withOUT extension
|
|
PSTR pszImageName,
|
|
DWORD dwImageNameSize
|
|
)
|
|
{
|
|
ULONG64 Base = 0;
|
|
ULONG Index = 0;
|
|
HRESULT hr;
|
|
|
|
hr = g_ExtSymbols->GetModuleByModuleName(pszModuleName, 0, &Index, &Base);
|
|
|
|
if (hr != S_OK) {
|
|
|
|
dprintf("GetModuleByModuleName returned %08x for dll %s\n", hr, pszModuleName);
|
|
return hr;
|
|
}
|
|
|
|
if (Base) {
|
|
|
|
hr = GetDllNamesByIndexAndBase(
|
|
Index,
|
|
Base,
|
|
NULL,
|
|
0,
|
|
pszImageName,
|
|
dwImageNameSize);
|
|
|
|
//dprintf("the image name is %s, the size is %d\n", pszImageName, dwImageNameSize);
|
|
|
|
} else {
|
|
|
|
dprintf("GetModuleByModuleName succeeded but couldn't get the base address?!\n");
|
|
hr = E_UNEXPECTED;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
Prints out the names of the shims applied to this process.
|
|
|
|
History:
|
|
|
|
03/12/2002 maonis Created
|
|
|
|
--*/
|
|
DECLARE_API( shimnames )
|
|
{
|
|
ULONG64 Value, CurrentShimInfo;
|
|
DWORD dwShimsCount = 0;
|
|
DWORD dwShimInfoSize = 0;
|
|
DWORD i;
|
|
ULONG64 pDllBase;
|
|
DWORD dwHookedAPIs;
|
|
char szShimDllName[MAX_SHIM_DLL_BASE_NAME_LEN];
|
|
WCHAR wszShimName[MAX_SHIM_NAME_LEN];
|
|
|
|
INIT_API();
|
|
|
|
if (!IsShimInitialized()) {
|
|
goto EXIT;
|
|
}
|
|
|
|
if (!GetVarValueULONG64("shimeng!g_dwShimsCount", &Value)) {
|
|
dprintf("failed to get the number of shims applied to this process\n");
|
|
goto EXIT;
|
|
}
|
|
|
|
//
|
|
// The last entry is shimeng.dll which hooks getprocaddress, we don't need
|
|
// to show this to the user.
|
|
//
|
|
dwShimsCount = (DWORD)Value - 1;
|
|
|
|
dprintf("there are %d shim(s) applied to this process\n", dwShimsCount);
|
|
|
|
//
|
|
// Read the name of the shims.
|
|
//
|
|
if (!GetVarValueULONG64("shimeng!g_pShimInfo", &Value)) {
|
|
dprintf("failed to get the address of shiminfo\n");
|
|
goto EXIT;
|
|
}
|
|
|
|
CurrentShimInfo = Value;
|
|
|
|
dwShimInfoSize = GetTypeSize("shimeng!tagSHIMINFO");
|
|
|
|
dprintf(" #\t%-64s%-16s%-16s\n",
|
|
"shim name",
|
|
"shim dll",
|
|
"# of hooks");
|
|
|
|
for (i = 0; i < dwShimsCount; ++i) {
|
|
|
|
GET_AND_CHECK_FIELDVALUE(
|
|
CurrentShimInfo,
|
|
"shimeng!tagSHIMINFO",
|
|
"pDllBase",
|
|
pDllBase);
|
|
|
|
if (GetDllNameByOffset(
|
|
(PVOID)pDllBase,
|
|
szShimDllName,
|
|
sizeof(szShimDllName),
|
|
NULL,
|
|
0) != S_OK) {
|
|
|
|
goto EXIT;
|
|
}
|
|
|
|
GET_SHIMINFO_wszName(CurrentShimInfo, wszShimName);
|
|
|
|
GET_AND_CHECK_FIELDVALUE(
|
|
CurrentShimInfo,
|
|
"shimeng!tagSHIMINFO",
|
|
"dwHookedAPIs",
|
|
dwHookedAPIs);
|
|
|
|
dprintf("%2d\t%-64S%-16s%-16d\n\n",
|
|
i + 1,
|
|
wszShimName,
|
|
szShimDllName,
|
|
dwHookedAPIs);
|
|
|
|
CurrentShimInfo += dwShimInfoSize;
|
|
}
|
|
|
|
EXIT:
|
|
|
|
EXIT_API();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
Given the value of an enum var, prints out the name of that enum value.
|
|
|
|
eg:
|
|
|
|
enum TEST {TEST0, TEST1, TEST2};
|
|
|
|
given 0 we'll print out " ( TEST0 )".
|
|
|
|
--*/
|
|
void
|
|
PrintEnumVarName(
|
|
LPCSTR pszEnumTypeName,
|
|
ULONG ulValueOfEnum
|
|
)
|
|
{
|
|
ULONG64 Module;
|
|
ULONG ulTypeId;
|
|
CHAR szName[32];
|
|
HRESULT hr;
|
|
|
|
hr = g_ExtSymbols->GetSymbolTypeId(pszEnumTypeName, &ulTypeId, &Module);
|
|
|
|
if (hr != S_OK) {
|
|
dprintf("GetSymbolTypeId returned %08x for %s\n", hr, pszEnumTypeName);
|
|
return;
|
|
}
|
|
|
|
hr = g_ExtSymbols->GetConstantName(Module, ulTypeId, ulValueOfEnum, szName, MAX_PATH, NULL);
|
|
|
|
if (hr != S_OK) {
|
|
dprintf("GetConstantName failed to get the name of value %d\n", ulValueOfEnum);
|
|
return;
|
|
}
|
|
|
|
dprintf(" ( %s )", szName);
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
IN OUT ppszArgs - beginning of the arguments. Upon return this is advanced to pass
|
|
an argument X (using ' ' as the delimiter).
|
|
OUT pszArg - Upon return this points to the beginning of X.
|
|
|
|
History:
|
|
|
|
03/26/2002 maonis Created
|
|
|
|
--*/
|
|
BOOL
|
|
GetArg(
|
|
PCSTR* ppszArgs,
|
|
PCSTR* ppszArg
|
|
)
|
|
{
|
|
BOOL bIsSuccess = FALSE;
|
|
|
|
PCSTR pszArgs = *ppszArgs;
|
|
|
|
while (*pszArgs && *pszArgs == ' ') {
|
|
++pszArgs;
|
|
}
|
|
|
|
if (!*pszArgs) {
|
|
goto EXIT;
|
|
}
|
|
|
|
*ppszArg = pszArgs;
|
|
|
|
while (*pszArgs && *pszArgs != ' ') {
|
|
++pszArgs;
|
|
}
|
|
|
|
*ppszArgs = pszArgs;
|
|
bIsSuccess = TRUE;
|
|
|
|
EXIT:
|
|
|
|
return bIsSuccess;
|
|
}
|
|
|
|
DECLARE_API( debuglevel )
|
|
{
|
|
ULONG64 DebugLevel;
|
|
|
|
INIT_API();
|
|
|
|
if (!GetExpressionEx(args, &DebugLevel, NULL)) {
|
|
|
|
//
|
|
// If there's no args, we print out the current debug level.
|
|
//
|
|
if (GetVarValueULONG64("shimeng!g_DebugLevel", &DebugLevel)) {
|
|
dprintf("The current debug level is %d", DebugLevel);
|
|
PrintEnumVarName("shimeng!DEBUGLEVEL", (ULONG)DebugLevel);
|
|
dprintf("\n");
|
|
} else {
|
|
dprintf("Can't find shimeng!g_DebugLevel\n");
|
|
}
|
|
|
|
goto EXIT;
|
|
}
|
|
|
|
if (!DebugLevel) {
|
|
if (SetVarValueULONG64("shimeng!g_bDbgPrintEnabled", 0)) {
|
|
dprintf("Disabled debug spew\n");
|
|
} else {
|
|
dprintf("Failed to set shimeng!g_bDbgPrintEnabled to FALSE\n");
|
|
}
|
|
|
|
goto EXIT;
|
|
}
|
|
|
|
if (DebugLevel > 0) {
|
|
if (!SetVarValueULONG64("shimeng!g_bDbgPrintEnabled", 1)) {
|
|
dprintf("Failed to set shimeng!g_bDbgPrintEnabled to TRUE\n");
|
|
goto EXIT;
|
|
}
|
|
}
|
|
|
|
if (SetVarValueULONG64("shimeng!g_DebugLevel", DebugLevel)) {
|
|
|
|
dprintf("Debug level changed to %d", DebugLevel);
|
|
PrintEnumVarName("shimeng!DEBUGLEVEL", (ULONG)DebugLevel);
|
|
dprintf("\n");
|
|
} else {
|
|
|
|
dprintf("Failed to change the debug level\n");
|
|
}
|
|
|
|
EXIT:
|
|
|
|
EXIT_API();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
BOOL
|
|
GetAllShimDllNames()
|
|
{
|
|
ULONG64 Value, CurrentShimInfo;
|
|
DWORD dwShimsCount = 0;
|
|
DWORD i, j, dwShimInfoSize;
|
|
ULONG64 pDllBase;
|
|
BOOL bIsSuccess = FALSE;
|
|
|
|
g_dwShimDlls = 0;
|
|
|
|
if (!GetVarValueULONG64("shimeng!g_dwShimsCount", &Value)) {
|
|
dprintf("failed to get the number of shims applied to this process\n");
|
|
goto EXIT;
|
|
}
|
|
|
|
//
|
|
// The last entry is shimeng.dll which hooks getprocaddress, we don't need
|
|
// to show this to the user.
|
|
//
|
|
dwShimsCount = (DWORD)Value - 1;
|
|
|
|
if (!GetVarValueULONG64("shimeng!g_pShimInfo", &Value)) {
|
|
dprintf("failed to get the address of shiminfo\n");
|
|
goto EXIT;
|
|
}
|
|
|
|
CurrentShimInfo = Value;
|
|
|
|
dwShimInfoSize = GetTypeSize("shimeng!tagSHIMINFO");
|
|
|
|
for (i = 0; i < dwShimsCount; ++i) {
|
|
|
|
GET_AND_CHECK_FIELDVALUE(
|
|
CurrentShimInfo,
|
|
"shimeng!tagSHIMINFO",
|
|
"pDllBase",
|
|
pDllBase);
|
|
|
|
//
|
|
// Check if we've seen this dll yet.
|
|
//
|
|
for (j = 0; j < g_dwShimDlls; ++j)
|
|
{
|
|
if (g_rgShimDllNames[j].pDllBase == pDllBase) {
|
|
goto NextShim;
|
|
}
|
|
}
|
|
|
|
char szShimDllBaseName[MAX_SHIM_DLL_BASE_NAME_LEN];
|
|
if (SUCCEEDED(
|
|
GetDllNameByOffset(
|
|
(PVOID)pDllBase,
|
|
szShimDllBaseName,
|
|
sizeof(szShimDllBaseName),
|
|
NULL,
|
|
0))) {
|
|
|
|
if (g_dwShimDlls >= MAX_SHIM_DLLS) {
|
|
dprintf("%d shim dlls? too many\n", g_dwShimDlls);
|
|
} else {
|
|
|
|
g_rgShimDllNames[g_dwShimDlls].pDllBase = pDllBase;
|
|
|
|
StringCchCopy(
|
|
g_rgShimDllNames[g_dwShimDlls++].szDllBaseName,
|
|
MAX_SHIM_DLL_BASE_NAME_LEN,
|
|
szShimDllBaseName);
|
|
}
|
|
} else {
|
|
goto EXIT;
|
|
}
|
|
|
|
NextShim:
|
|
|
|
CurrentShimInfo += dwShimInfoSize;
|
|
}
|
|
|
|
bIsSuccess = TRUE;
|
|
|
|
EXIT:
|
|
|
|
if (!bIsSuccess) {
|
|
dprintf("Failed to get the debug level symbols for all loaded shim dlls\n");
|
|
}
|
|
|
|
return bIsSuccess;
|
|
}
|
|
|
|
enum SHIM_DEBUG_LEVEL_MODE {
|
|
PRINT_SHIM_DEBUG_LEVEL,
|
|
CHANGE_SHIM_DEBUG_LEVEL
|
|
};
|
|
|
|
void
|
|
ProcessShimDllDebugLevel(
|
|
PCSTR pszDllBaseName,
|
|
SHIM_DEBUG_LEVEL_MODE eShimDebugLevelMode,
|
|
ULONG64 DebugLevel
|
|
)
|
|
{
|
|
char szDebugLevelSymbol[MAX_SHIM_DEBUGLEVEL_SYMBOL_LEN];
|
|
char szDebugLevelType[MAX_SHIM_DEBUGLEVEL_SYMBOL_LEN];
|
|
|
|
StringCchCopy(
|
|
szDebugLevelSymbol,
|
|
MAX_SHIM_DEBUGLEVEL_SYMBOL_LEN,
|
|
pszDllBaseName);
|
|
|
|
StringCchCat(
|
|
szDebugLevelSymbol,
|
|
MAX_SHIM_DEBUGLEVEL_SYMBOL_LEN,
|
|
SHIM_DEBUG_LEVEL_SYMBOL_SUFFIX);
|
|
|
|
StringCchCopy(
|
|
szDebugLevelType,
|
|
MAX_SHIM_DEBUGLEVEL_SYMBOL_LEN,
|
|
pszDllBaseName);
|
|
|
|
StringCchCat(
|
|
szDebugLevelType,
|
|
MAX_SHIM_DEBUGLEVEL_SYMBOL_LEN,
|
|
SHIM_DEBUG_LEVEL_TYPE_SUFFIX);
|
|
|
|
if (eShimDebugLevelMode == PRINT_SHIM_DEBUG_LEVEL) {
|
|
|
|
ULONG64 DebugLevelTemp;
|
|
|
|
if (GetVarValueULONG64(szDebugLevelSymbol, &DebugLevelTemp)) {
|
|
dprintf("The debug level for %s.dll is %d", pszDllBaseName, DebugLevelTemp);
|
|
PrintEnumVarName(szDebugLevelType, (ULONG)DebugLevelTemp);
|
|
dprintf("\n");
|
|
} else {
|
|
dprintf("Failed to get the value of %s\n", szDebugLevelSymbol);
|
|
}
|
|
|
|
} else if (eShimDebugLevelMode == CHANGE_SHIM_DEBUG_LEVEL) {
|
|
|
|
if (SetVarValueULONG64(szDebugLevelSymbol, DebugLevel)) {
|
|
dprintf(
|
|
"Changed the debug level for %s.dll to %d",
|
|
pszDllBaseName,
|
|
DebugLevel);
|
|
PrintEnumVarName(szDebugLevelType, (ULONG)DebugLevel);
|
|
dprintf("\n");
|
|
} else {
|
|
dprintf("Failed to set %s to %d\n", szDebugLevelSymbol, DebugLevel);
|
|
}
|
|
|
|
} else {
|
|
dprintf("%d is an invalid arg to ProcessShimDllDebugLevel\n", eShimDebugLevelMode);
|
|
}
|
|
}
|
|
|
|
void
|
|
PrintAllShimsDebugLevel()
|
|
{
|
|
for (DWORD i = 0; i < g_dwShimDlls; ++i) {
|
|
ProcessShimDllDebugLevel(
|
|
g_rgShimDllNames[i].szDllBaseName,
|
|
PRINT_SHIM_DEBUG_LEVEL,
|
|
0);
|
|
}
|
|
}
|
|
|
|
void
|
|
ChangeAllShimsDebugLevel(
|
|
ULONG64 DebugLevel)
|
|
{
|
|
for (DWORD i = 0; i < g_dwShimDlls; ++i) {
|
|
ProcessShimDllDebugLevel(
|
|
g_rgShimDllNames[i].szDllBaseName,
|
|
CHANGE_SHIM_DEBUG_LEVEL,
|
|
DebugLevel);
|
|
}
|
|
}
|
|
|
|
DECLARE_API( sdebuglevel )
|
|
{
|
|
ULONG64 DebugLevel;
|
|
char szDllBaseName[MAX_SHIM_DLL_BASE_NAME_LEN];
|
|
char szDebugLevel[2];
|
|
PCSTR pszArg;
|
|
|
|
INIT_API();
|
|
|
|
if (!IsShimInitialized()) {
|
|
goto EXIT;
|
|
}
|
|
|
|
if (!GetAllShimDllNames()) {
|
|
goto EXIT;
|
|
}
|
|
|
|
//
|
|
// Get the dll name.
|
|
//
|
|
if (!GetArg(&args, &pszArg)) {
|
|
PrintAllShimsDebugLevel();
|
|
goto EXIT;
|
|
}
|
|
|
|
if (isdigit(*pszArg) && ((args - pszArg) == 1)) {
|
|
szDebugLevel[0] = *pszArg;
|
|
szDebugLevel[1] = 0;
|
|
DebugLevel = (ULONG64)atol(szDebugLevel);
|
|
ChangeAllShimsDebugLevel(DebugLevel);
|
|
goto EXIT;
|
|
}
|
|
|
|
//
|
|
// If we get here it means we have a dll base name.
|
|
//
|
|
StringCchCopyN(szDllBaseName, MAX_SHIM_DLL_BASE_NAME_LEN, pszArg, args - pszArg);
|
|
|
|
for (DWORD i = 0; i < g_dwShimDlls; ++i)
|
|
{
|
|
if (!_stricmp(szDllBaseName, g_rgShimDllNames[i].szDllBaseName)) {
|
|
|
|
if (GetArg(&args, &pszArg)) {
|
|
if (isdigit(*pszArg) && ((args - pszArg) == 1)) {
|
|
szDebugLevel[0] = *pszArg;
|
|
szDebugLevel[1] = 0;
|
|
DebugLevel = (ULONG64)atol(szDebugLevel);
|
|
|
|
ProcessShimDllDebugLevel(
|
|
szDllBaseName,
|
|
CHANGE_SHIM_DEBUG_LEVEL,
|
|
DebugLevel);
|
|
} else {
|
|
dprintf("You specified an invalid debug level value\n");
|
|
}
|
|
} else {
|
|
ProcessShimDllDebugLevel(
|
|
szDllBaseName,
|
|
PRINT_SHIM_DEBUG_LEVEL,
|
|
0);
|
|
}
|
|
|
|
goto EXIT;
|
|
}
|
|
}
|
|
|
|
if (i == g_dwShimDlls) {
|
|
dprintf("%s.dll is not loaded\n", szDllBaseName);
|
|
}
|
|
|
|
EXIT:
|
|
|
|
EXIT_API();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
DECLARE_API( loadshims )
|
|
{
|
|
INIT_API();
|
|
|
|
if (GetExpression("shimeng!SeiInit")) {
|
|
|
|
g_ExtControl->Execute(
|
|
DEBUG_OUTCTL_IGNORE,
|
|
"g shimeng!SeiInit;g@$ra", // stop right after SeiInit is executed.
|
|
DEBUG_EXECUTE_DEFAULT);
|
|
} else {
|
|
|
|
dprintf("wrong symbols for shimeng.dll - is shimeng.dll even loaded?\n");
|
|
}
|
|
|
|
EXIT_API();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Decription:
|
|
|
|
Given a HOOKAPI pointer pHook, this gets you pHook->pHookEx->pNext.
|
|
|
|
History:
|
|
|
|
03/20/2002 maonis Created
|
|
|
|
--*/
|
|
BOOL
|
|
GetNextHook(
|
|
ULONG64 Hook,
|
|
PULONG64 pNextHook
|
|
)
|
|
{
|
|
BOOL bIsSuccess = FALSE;
|
|
ULONG64 HookEx;
|
|
ULONG64 NextHook;
|
|
|
|
GET_AND_CHECK_FIELDVALUE(Hook, "shimeng!tagHOOKAPI", "pHookEx", HookEx);
|
|
GET_AND_CHECK_FIELDVALUE(HookEx, "shimeng!tagHOOKAPIEX", "pNext", NextHook);
|
|
|
|
*pNextHook = NextHook;
|
|
|
|
bIsSuccess = TRUE;
|
|
|
|
EXIT:
|
|
|
|
return bIsSuccess;
|
|
}
|
|
|
|
DECLARE_API( displaychain )
|
|
{
|
|
ULONG64 Value;
|
|
ULONG64 CurrentHookAPIArray;
|
|
ULONG64 CurrentHookAPI;
|
|
ULONG64 CurrentShimInfo;
|
|
ULONG64 Hook;
|
|
ULONG64 NextHook;
|
|
ULONG64 HookEx;
|
|
ULONG64 HookAddress;
|
|
ULONG64 PfnNew;
|
|
ULONG64 PfnOld;
|
|
ULONG64 TopOfChain;
|
|
DWORD i, j;
|
|
DWORD dwHookAPISize;
|
|
DWORD dwShimInfoSize;
|
|
DWORD dwHookedAPIs;
|
|
DWORD dwShimsCount;
|
|
|
|
INIT_API();
|
|
|
|
if (!IsShimInitialized()) {
|
|
goto EXIT;
|
|
}
|
|
|
|
if (!GetExpressionEx(args, &HookAddress, NULL)) {
|
|
dprintf("Usage: !displaychain address_to_check\n");
|
|
goto EXIT;
|
|
}
|
|
|
|
if (!GetVarValueULONG64("shimeng!g_dwShimsCount", &Value)) {
|
|
dprintf("failed to get the number of shims applied to this process\n");
|
|
goto EXIT;
|
|
}
|
|
|
|
dwShimsCount = (DWORD)Value - 1;
|
|
|
|
if (!GetVarValueULONG64("shimeng!g_pShimInfo", &Value)) {
|
|
dprintf("failed to get the address of shiminfo\n");
|
|
goto EXIT;
|
|
}
|
|
|
|
CurrentShimInfo = Value;
|
|
|
|
dwShimInfoSize = GetTypeSize("shimeng!tagSHIMINFO");
|
|
|
|
dwHookAPISize = GetTypeSize("shimeng!tagHOOKAPI");
|
|
|
|
if (!dwHookAPISize) {
|
|
|
|
dprintf("failed to get the HOOKAPI size\n");
|
|
goto EXIT;
|
|
}
|
|
|
|
if (!GetVarValueULONG64("shimeng!g_pHookArray", &Value)) {
|
|
dprintf("failed to get the address of shiminfo\n");
|
|
goto EXIT;
|
|
}
|
|
|
|
CurrentHookAPIArray = Value;
|
|
|
|
for (i = 0; i < dwShimsCount; ++i) {
|
|
|
|
//
|
|
// Get the number of hooks this shim has.
|
|
//
|
|
if (GetFieldValue(CurrentShimInfo, "shimeng!tagSHIMINFO", "dwHookedAPIs", dwHookedAPIs)) {
|
|
|
|
dprintf("failed to get the number of hooked APIs for shim #%d\n",
|
|
i);
|
|
|
|
goto EXIT;
|
|
}
|
|
|
|
if (!ReadPointer(CurrentHookAPIArray, &CurrentHookAPI)) {
|
|
dprintf("failed to get the begining of hook api array\n");
|
|
|
|
goto EXIT;
|
|
}
|
|
|
|
for (j = 0; j < dwHookedAPIs; ++j) {
|
|
|
|
GET_AND_CHECK_FIELDVALUE(CurrentHookAPI, "shimeng!tagHOOKAPI", "pfnNew", PfnNew);
|
|
|
|
if (HookAddress == PfnNew) {
|
|
|
|
//
|
|
// We found the address, now get top of the chain so we can print it.
|
|
//
|
|
GET_AND_CHECK_FIELDVALUE(CurrentHookAPI, "shimeng!tagHOOKAPI", "pHookEx", HookEx);
|
|
GET_AND_CHECK_FIELDVALUE(HookEx, "shimeng!tagHOOKAPIEX", "pTopOfChain", TopOfChain);
|
|
|
|
//dprintf("top of chain is %08x\n", TopOfChain);
|
|
|
|
Hook = TopOfChain;
|
|
|
|
GET_AND_CHECK_FIELDVALUE(Hook, "shimeng!tagHOOKAPI", "pfnNew", PfnNew);
|
|
|
|
dprintf(" %08x", PfnNew);
|
|
|
|
PrintSymbolAtAddress(PfnNew);
|
|
dprintf("\n");
|
|
|
|
while (TRUE) {
|
|
|
|
if (!GetNextHook(Hook, &NextHook)) {
|
|
dprintf("failed to get next hook\n");
|
|
goto EXIT;
|
|
}
|
|
|
|
if (!NextHook) {
|
|
|
|
//
|
|
// We are at the end of the chain, get the original API address.
|
|
//
|
|
GET_AND_CHECK_FIELDVALUE(Hook, "shimeng!tagHOOKAPI", "pfnOld", PfnOld);
|
|
dprintf(" -> %08x", PfnOld);
|
|
PrintSymbolAtAddress(PfnOld);
|
|
dprintf("\n");
|
|
|
|
break;
|
|
}
|
|
|
|
Hook = NextHook;
|
|
|
|
if (Hook) {
|
|
|
|
GET_AND_CHECK_FIELDVALUE(Hook, "shimeng!tagHOOKAPI", "pfnNew", PfnNew);
|
|
dprintf(" -> %08x", PfnNew);
|
|
PrintSymbolAtAddress(PfnNew);
|
|
dprintf("\n");
|
|
}
|
|
}
|
|
|
|
dprintf("\n");
|
|
|
|
goto EXIT;
|
|
}
|
|
|
|
CurrentHookAPI += dwHookAPISize;
|
|
}
|
|
|
|
CurrentShimInfo += dwShimInfoSize;
|
|
CurrentHookAPIArray += sizeof(ULONG_PTR);
|
|
}
|
|
|
|
EXIT:
|
|
|
|
EXIT_API();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
BOOL
|
|
CheckSymbols(
|
|
LPCSTR pszSymbolName
|
|
)
|
|
{
|
|
ULONG64 Value;
|
|
HRESULT hr = g_ExtSymbols->GetOffsetByName(pszSymbolName, &Value);
|
|
|
|
return (hr == S_OK);
|
|
}
|
|
|
|
#define CHECKSYM(s) if (!CheckSymbols(s)) bIsSymbolGood = FALSE; goto EXIT;
|
|
#define CHECKTYPE(t) if (!GetTypeSize(t)) bIsSymbolGood = FALSE; goto EXIT;
|
|
|
|
DECLARE_API ( shimengsym )
|
|
{
|
|
INIT_API();
|
|
|
|
BOOL bIsSymbolGood = TRUE;
|
|
|
|
//
|
|
// Check a few important structures and stuff.
|
|
//
|
|
CHECKSYM("shimeng!SeiInit");
|
|
CHECKSYM("shimeng!g_pHookArray");
|
|
CHECKSYM("shimeng!g_pShiminfo");
|
|
CHECKSYM("shimeng!g_dwShimsCount");
|
|
CHECKTYPE("shimeng!tagHOOKAPI");
|
|
CHECKTYPE("shimeng!tagSHIMINFO");
|
|
|
|
EXIT:
|
|
|
|
EXIT_API();
|
|
|
|
if (bIsSymbolGood) {
|
|
dprintf("shimeng symbols look good\n");
|
|
} else {
|
|
dprintf("You have wrong symbols for shimeng\n");
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
DECLARE_API ( displayhooks )
|
|
{
|
|
ULONG64 Value;
|
|
ULONG64 CurrentHookAPIArray;
|
|
ULONG64 CurrentHookAPI;
|
|
ULONG64 CurrentShimInfo;
|
|
ULONG64 FunctionName;
|
|
ULONG64 ModuleName;
|
|
ULONG64 ShimName;
|
|
ULONG64 PfnNew;
|
|
DWORD i, j;
|
|
DWORD dwHookAPISize;
|
|
DWORD dwShimInfoSize;
|
|
DWORD dwHookedAPIs;
|
|
DWORD dwShimsCount;
|
|
DWORD dwBytesRead;
|
|
char szAPIName[MAX_API_NAME_LEN];
|
|
char szModuleName[MAX_MODULE_NAME_LEN];
|
|
WCHAR wszShimName[MAX_SHIM_NAME_LEN];
|
|
char szShimName[MAX_SHIM_NAME_LEN];
|
|
LPCSTR pszShimName = args;
|
|
|
|
INIT_API();
|
|
|
|
if (!IsShimInitialized()) {
|
|
goto EXIT;
|
|
}
|
|
|
|
if (!pszShimName || !*pszShimName) {
|
|
|
|
dprintf("Usage: !displayhooks shimname\n");
|
|
goto EXIT;
|
|
}
|
|
|
|
if (!GetVarValueULONG64("shimeng!g_dwShimsCount", &Value)) {
|
|
dprintf("failed to get the number of shims applied to this process\n");
|
|
goto EXIT;
|
|
}
|
|
|
|
dwShimsCount = (DWORD)Value - 1;
|
|
|
|
if (!GetVarValueULONG64("shimeng!g_pShimInfo", &Value)) {
|
|
dprintf("failed to get the address of shiminfo\n");
|
|
goto EXIT;
|
|
}
|
|
|
|
CurrentShimInfo = Value;
|
|
|
|
dwShimInfoSize = GetTypeSize("shimeng!tagSHIMINFO");
|
|
|
|
dwHookAPISize = GetTypeSize("shimeng!tagHOOKAPI");
|
|
|
|
if (!dwHookAPISize) {
|
|
|
|
dprintf("failed to get the HOOKAPI size\n");
|
|
goto EXIT;
|
|
}
|
|
|
|
if (!GetVarValueULONG64("shimeng!g_pHookArray", &Value)) {
|
|
dprintf("failed to get the address of shiminfo\n");
|
|
goto EXIT;
|
|
}
|
|
|
|
CurrentHookAPIArray = Value;
|
|
|
|
dprintf("%-8s%-32s%-16s %8s\n",
|
|
"hook #",
|
|
"hook name",
|
|
"dll it's in",
|
|
"new addr");
|
|
|
|
for (i = 0; i < dwShimsCount; ++i) {
|
|
|
|
//
|
|
// Get the shim name.
|
|
//
|
|
if (GetFieldValue(CurrentShimInfo, "shimeng!tagSHIMINFO", "wszName", wszShimName)) {
|
|
|
|
dprintf("failed to get the shim name address for shim #%d\n",
|
|
i);
|
|
|
|
goto EXIT;
|
|
}
|
|
|
|
if (!WideCharToMultiByte(CP_ACP, 0, wszShimName, -1, szShimName, MAX_SHIM_NAME_LEN, NULL, NULL)) {
|
|
dprintf("failed to convert %S to ansi: %d\n", wszShimName, GetLastError());
|
|
goto EXIT;
|
|
}
|
|
|
|
if (lstrcmpi(szShimName, pszShimName)) {
|
|
goto TryNext;
|
|
}
|
|
|
|
//
|
|
// Get the number of hooks this shim has.
|
|
//
|
|
if (GetFieldValue(CurrentShimInfo, "shimeng!tagSHIMINFO", "dwHookedAPIs", dwHookedAPIs)) {
|
|
|
|
dprintf("failed to get the number of hooked APIs for shim #%d\n",
|
|
i);
|
|
|
|
goto EXIT;
|
|
}
|
|
|
|
if (!ReadPointer(CurrentHookAPIArray, &CurrentHookAPI)) {
|
|
dprintf("failed to get the begining of hook api array\n");
|
|
|
|
goto EXIT;
|
|
}
|
|
|
|
for (j = 0; j < dwHookedAPIs; ++j) {
|
|
|
|
GET_AND_CHECK_FIELDVALUE(CurrentHookAPI, "shimeng!tagHOOKAPI", "pszFunctionName", FunctionName);
|
|
|
|
if (!GetData(FunctionName, szAPIName, MAX_API_NAME_LEN)) {
|
|
dprintf("failed to read in the API name at %08x\n", FunctionName);
|
|
goto EXIT;
|
|
}
|
|
|
|
GET_AND_CHECK_FIELDVALUE(CurrentHookAPI, "shimeng!tagHOOKAPI", "pszModule", ModuleName);
|
|
|
|
if (!GetData(ModuleName, szModuleName, MAX_MODULE_NAME_LEN)) {
|
|
dprintf("failed to read in the module name at %08x\n", ModuleName);
|
|
goto EXIT;
|
|
}
|
|
|
|
GET_AND_CHECK_FIELDVALUE(CurrentHookAPI, "shimeng!tagHOOKAPI", "pfnNew", PfnNew);
|
|
|
|
dprintf("%-8d%-32s%-16s %08x\n",
|
|
j + 1,
|
|
szAPIName,
|
|
szModuleName,
|
|
PfnNew);
|
|
|
|
CurrentHookAPI += dwHookAPISize;
|
|
}
|
|
|
|
TryNext:
|
|
|
|
CurrentShimInfo += dwShimInfoSize;
|
|
CurrentHookAPIArray += sizeof(ULONG_PTR);
|
|
}
|
|
|
|
EXIT:
|
|
|
|
EXIT_API();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
This is a helper function for IsExcluded.
|
|
|
|
History:
|
|
|
|
03/26/2002 maonis Created
|
|
|
|
--*/
|
|
BOOL
|
|
GetModuleNameAndAPIAddress(
|
|
IN ULONG64 pHook,
|
|
OUT ULONG64* pFunctionAddress,
|
|
IN OUT LPSTR szModuleName
|
|
)
|
|
{
|
|
BOOL bIsSuccess = FALSE;
|
|
ULONG64 FunctionAddress;
|
|
|
|
GET_AND_CHECK_FIELDVALUE(
|
|
pHook,
|
|
"shimeng!tagHOOKAPI",
|
|
"pszFunctionName",
|
|
FunctionAddress);
|
|
|
|
GET_AND_CHECK_FIELDVALUE_DATA(
|
|
pHook,
|
|
"shimeng!tagHOOKAPI",
|
|
"pszModule",
|
|
szModuleName,
|
|
MAX_MODULE_NAME_LEN);
|
|
|
|
*pFunctionAddress = FunctionAddress;
|
|
bIsSuccess = TRUE;
|
|
|
|
EXIT:
|
|
|
|
return bIsSuccess;
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
This is modified from the SeiIsExcluded function in
|
|
%sdxroot%\windows\appcompat\shimengines\engiat\shimeng.c
|
|
|
|
--*/
|
|
BOOL
|
|
IsExcluded(
|
|
IN LPCSTR pszModule, // The module to test for exclusion.
|
|
IN ULONG64 pTopHookAPI, // The HOOKAPI for which we test for exclusion.
|
|
IN BOOL bInSystem32 // Whether the module is located in the System32 directory.
|
|
)
|
|
{
|
|
BOOL bExclude = TRUE;
|
|
BOOL bShimWantsToExclude = FALSE; // was there a shim that wanted to exclude?
|
|
ULONG64 pHook = pTopHookAPI;
|
|
ULONG64 pHookEx;
|
|
ULONG64 pShimInfo, pCurrentShimInfo;
|
|
ULONG64 pIncludeMod;
|
|
ULONG64 pExcludeMod;
|
|
ULONG64 eInExMode;
|
|
ULONG64 ModuleName;
|
|
ULONG64 FunctionName;
|
|
ULONG64 Temp;
|
|
DWORD dwCounter;
|
|
char szCurrentModuleName[MAX_MODULE_NAME_LEN];
|
|
char szCurrentAPIName[MAX_API_NAME_LEN];
|
|
WCHAR wszName[MAX_SHIM_NAME_LEN];
|
|
DWORD dwShimInfoSize = GetTypeSize("shimeng!tagSHIMINFO");
|
|
|
|
if (!GetVarValueULONG64("shimeng!g_pShimInfo", &pShimInfo)) {
|
|
dprintf("failed to get the address of shiminfo\n");
|
|
goto EXIT;
|
|
}
|
|
|
|
//
|
|
// The current process is to only exclude a chain if every shim in the chain wants to
|
|
// exclude. If one shim needs to be included, the whole chain is included.
|
|
//
|
|
while (pHook) {
|
|
|
|
GET_AND_CHECK_FIELDVALUE(pHook, "shimeng!tagHOOKAPI", "pHookEx", pHookEx);
|
|
|
|
if (!pHookEx) {
|
|
break;
|
|
}
|
|
|
|
GET_AND_CHECK_FIELDVALUE(pHookEx, "shimeng!tagHOOKAPIEX", "dwShimID", dwCounter);
|
|
|
|
//if (!GetData(pShimInfo + dwShimInfoSize * dwCounter, &si, dwShimInfoSize)) {
|
|
// dprintf("Failed to get the shiminfo for shim #%d\n", dwCounter + 1);
|
|
// goto EXIT;
|
|
//}
|
|
|
|
pCurrentShimInfo = pShimInfo + dwShimInfoSize * dwCounter;
|
|
|
|
GET_SHIMINFO_eInExMode(pCurrentShimInfo, eInExMode);
|
|
GET_SHIMINFO_wszName(pCurrentShimInfo, wszName);
|
|
|
|
switch (eInExMode) {
|
|
case INCLUDE_ALL:
|
|
{
|
|
//
|
|
// We include everything except what's in the exclude list.
|
|
//
|
|
GET_SHIMINFO_pFirstExclude(pCurrentShimInfo, pExcludeMod);
|
|
|
|
while (pExcludeMod != NULL) {
|
|
|
|
GET_AND_CHECK_FIELDVALUE_DATA(
|
|
pExcludeMod,
|
|
"shimeng!tagINEXMOD",
|
|
"pszModule",
|
|
szCurrentModuleName,
|
|
MAX_MODULE_NAME_LEN);
|
|
|
|
if (lstrcmpi(szCurrentModuleName, pszModule) == 0) {
|
|
|
|
if (!GetModuleNameAndAPIAddress(pTopHookAPI, &FunctionName, szCurrentModuleName)) {
|
|
goto EXIT;
|
|
}
|
|
|
|
if ((ULONG_PTR)FunctionName < 0x0000FFFF) {
|
|
dprintf(
|
|
"Module \"%s\" excluded for shim %S, API \"%s!#%d\","
|
|
"because it is in the exclude list (MODE: IA).\n",
|
|
pszModule,
|
|
wszName,
|
|
szCurrentModuleName,
|
|
FunctionName);
|
|
} else {
|
|
|
|
GET_AND_CHECK_DATA(FunctionName, szCurrentAPIName, MAX_API_NAME_LEN);
|
|
|
|
dprintf(
|
|
"Module \"%s\" excluded for shim %S, API \"%s!%s\","
|
|
"because it is in the exclude list (MODE: IA).\n",
|
|
pszModule,
|
|
wszName,
|
|
szCurrentModuleName,
|
|
szCurrentAPIName);
|
|
}
|
|
|
|
//
|
|
// this wants to be excluded, so we go to the next
|
|
// shim, and see if it wants to be included
|
|
//
|
|
bShimWantsToExclude = TRUE;
|
|
goto nextShim;
|
|
}
|
|
|
|
Temp = pExcludeMod;
|
|
GET_AND_CHECK_FIELDVALUE(Temp, "shimeng!tagINEXMOD", "pNext", pExcludeMod);
|
|
}
|
|
|
|
//
|
|
// we should include this shim, and therefore, the whole chain
|
|
//
|
|
bExclude = FALSE;
|
|
goto EXIT;
|
|
break;
|
|
}
|
|
|
|
case EXCLUDE_SYSTEM32:
|
|
{
|
|
//
|
|
// In this case, we first check the include list,
|
|
// then exclude it if it's in System32, then exclude it if
|
|
// it's in the exclude list.
|
|
//
|
|
GET_SHIMINFO_pFirstInclude(pCurrentShimInfo, pIncludeMod);
|
|
GET_SHIMINFO_pFirstExclude(pCurrentShimInfo, pExcludeMod);
|
|
|
|
//
|
|
// First, check the include list.
|
|
//
|
|
while (pIncludeMod != NULL) {
|
|
|
|
GET_AND_CHECK_FIELDVALUE_DATA(
|
|
pIncludeMod,
|
|
"shimeng!tagINEXMOD",
|
|
"pszModule",
|
|
szCurrentModuleName,
|
|
MAX_MODULE_NAME_LEN);
|
|
|
|
if (lstrcmpi(szCurrentModuleName, pszModule) == 0) {
|
|
|
|
//
|
|
// we should include this shim, and therefore, the whole chain
|
|
//
|
|
bExclude = FALSE;
|
|
goto EXIT;
|
|
}
|
|
|
|
Temp = pIncludeMod;
|
|
GET_AND_CHECK_FIELDVALUE(Temp, "shimeng!tagINEXMOD", "pNext", pIncludeMod);
|
|
}
|
|
|
|
//
|
|
// it wasn't in the include list, so is it in System32?
|
|
//
|
|
if (bInSystem32) {
|
|
|
|
if (!GetModuleNameAndAPIAddress(pTopHookAPI, &FunctionName, szCurrentModuleName)) {
|
|
goto EXIT;
|
|
}
|
|
|
|
if ((ULONG_PTR)FunctionName < 0x0000FFFF) {
|
|
dprintf(
|
|
"module \"%s\" excluded for shim %S, API \"%s!#%d\", because it is in System32.\n",
|
|
pszModule,
|
|
wszName,
|
|
szCurrentModuleName,
|
|
FunctionName);
|
|
} else {
|
|
|
|
GET_AND_CHECK_DATA(FunctionName, szCurrentAPIName, MAX_API_NAME_LEN);
|
|
|
|
dprintf(
|
|
"module \"%s\" excluded for shim %S, API \"%s!%s\", because it is in System32.\n",
|
|
pszModule,
|
|
wszName,
|
|
szCurrentModuleName,
|
|
szCurrentAPIName);
|
|
}
|
|
|
|
//
|
|
// this wants to be excluded, so we go to the next
|
|
// shim, and see if it wants to be included
|
|
//
|
|
bShimWantsToExclude = TRUE;
|
|
goto nextShim;
|
|
}
|
|
|
|
//
|
|
// it wasn't in System32, so is it in the exclude list?
|
|
//
|
|
while (pExcludeMod != NULL) {
|
|
|
|
if (!GetModuleNameAndAPIAddress(pTopHookAPI, &FunctionName, szCurrentModuleName)) {
|
|
goto EXIT;
|
|
}
|
|
|
|
GET_AND_CHECK_FIELDVALUE_DATA(
|
|
pExcludeMod,
|
|
"shimeng!tagINEXMOD",
|
|
"pszModule",
|
|
szCurrentModuleName,
|
|
MAX_MODULE_NAME_LEN);
|
|
|
|
if (lstrcmpi(szCurrentModuleName, pszModule) == 0) {
|
|
if ((ULONG_PTR)FunctionName < 0x0000FFFF) {
|
|
dprintf(
|
|
"module \"%s\" excluded for shim %S, API \"%s!#%d\", because it is in the exclude list (MODE: ES).\n",
|
|
pszModule,
|
|
wszName,
|
|
szCurrentModuleName,
|
|
FunctionName);
|
|
} else {
|
|
|
|
GET_AND_CHECK_DATA(FunctionName, szCurrentAPIName, MAX_API_NAME_LEN);
|
|
|
|
dprintf(
|
|
"module \"%s\" excluded for shim %S, API \"%s!%s\", because it is in the exclude list (MODE: ES).\n",
|
|
pszModule,
|
|
wszName,
|
|
szCurrentModuleName,
|
|
szCurrentAPIName);
|
|
}
|
|
|
|
//
|
|
// this wants to be excluded, so we go to the next
|
|
// shim, and see if it wants to be included
|
|
//
|
|
bShimWantsToExclude = TRUE;
|
|
goto nextShim;
|
|
}
|
|
|
|
Temp = pExcludeMod;
|
|
GET_AND_CHECK_FIELDVALUE(Temp, "shimeng!tagINEXMOD", "pNext", pExcludeMod);
|
|
}
|
|
|
|
//
|
|
// we should include this shim, and therefore, the whole chain
|
|
//
|
|
bExclude = FALSE;
|
|
goto EXIT;
|
|
break;
|
|
}
|
|
|
|
case EXCLUDE_ALL:
|
|
{
|
|
//
|
|
// We exclude everything except what is in the include list.
|
|
//
|
|
GET_SHIMINFO_pFirstInclude(pCurrentShimInfo, pIncludeMod);
|
|
|
|
while (pIncludeMod != NULL) {
|
|
|
|
GET_AND_CHECK_FIELDVALUE_DATA(
|
|
pIncludeMod,
|
|
"shimeng!tagINEXMOD",
|
|
"pszModule",
|
|
szCurrentModuleName,
|
|
MAX_MODULE_NAME_LEN);
|
|
|
|
if (lstrcmpi(szCurrentModuleName, pszModule) == 0) {
|
|
//
|
|
// we should include this shim, and therefore, the whole chain
|
|
//
|
|
bExclude = FALSE;
|
|
goto EXIT;
|
|
}
|
|
|
|
Temp = pIncludeMod;
|
|
GET_AND_CHECK_FIELDVALUE(Temp, "shimeng!tagINEXMOD", "pNext", pIncludeMod);
|
|
}
|
|
|
|
if (!GetModuleNameAndAPIAddress(pTopHookAPI, &FunctionName, szCurrentModuleName)) {
|
|
goto EXIT;
|
|
}
|
|
|
|
if ((ULONG_PTR)FunctionName < 0x0000FFFF) {
|
|
dprintf(
|
|
"module \"%s\" excluded for shim %S, API \"%s!#%d\", because it is not in the include list (MODE: EA).\n",
|
|
pszModule,
|
|
wszName,
|
|
szCurrentModuleName,
|
|
FunctionName);
|
|
} else {
|
|
|
|
GET_AND_CHECK_DATA(FunctionName, szCurrentAPIName, MAX_API_NAME_LEN);
|
|
|
|
dprintf(
|
|
"module \"%s\" excluded for shim %S, API \"%s!%s\", because it is not in the include list (MODE: EA).\n",
|
|
pszModule,
|
|
wszName,
|
|
szCurrentModuleName,
|
|
szCurrentAPIName);
|
|
}
|
|
|
|
//
|
|
// this wants to be excluded, so we go to the next
|
|
// shim, and see if it wants to be included
|
|
//
|
|
bShimWantsToExclude = TRUE;
|
|
goto nextShim;
|
|
break;
|
|
}
|
|
}
|
|
|
|
nextShim:
|
|
|
|
Temp = pHook;
|
|
if (!GetNextHook(Temp, &pHook)) {
|
|
dprintf("failed to get next hook\n");
|
|
goto EXIT;
|
|
}
|
|
}
|
|
|
|
EXIT:
|
|
if (!bExclude && bShimWantsToExclude) {
|
|
|
|
if (GetModuleNameAndAPIAddress(pTopHookAPI, &FunctionName, szCurrentModuleName)) {
|
|
|
|
if ((ULONG_PTR)FunctionName < 0x0000FFFF) {
|
|
dprintf(
|
|
"Module \"%s\" mixed inclusion/exclusion for "
|
|
"API \"%s!#%d\". Included.\n",
|
|
pszModule,
|
|
szCurrentModuleName,
|
|
FunctionName);
|
|
} else {
|
|
|
|
GET_AND_CHECK_DATA(FunctionName, szCurrentAPIName, MAX_API_NAME_LEN);
|
|
|
|
dprintf(
|
|
"Module \"%s\" mixed inclusion/exclusion for "
|
|
"API \"%s!%s\". Included.\n",
|
|
pszModule,
|
|
szCurrentModuleName,
|
|
szCurrentAPIName);
|
|
}
|
|
}
|
|
}
|
|
|
|
return bExclude;
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Decription:
|
|
|
|
The way we get the system32 directory is just to get the loaded image name for kernel32.dll.
|
|
|
|
History:
|
|
|
|
03/26/2002 maonis Created
|
|
|
|
--*/
|
|
BOOL
|
|
GetSystem32Directory()
|
|
{
|
|
if (g_dwSystem32DirLen) {
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL bIsSuccess = FALSE;
|
|
char szImageName[MAX_DLL_IMAGE_NAME_LEN];
|
|
PSTR pszBaseDllName = NULL;
|
|
DWORD dwLen = 0;
|
|
|
|
if (GetDllImageNameByModuleName("kernel32", szImageName, MAX_DLL_IMAGE_NAME_LEN) != S_OK) {
|
|
dprintf("can't get the dll path for kernel32.dll!!\n");
|
|
goto EXIT;
|
|
}
|
|
|
|
//
|
|
// Get the beginning of the base dll name.
|
|
//
|
|
dwLen = lstrlen(szImageName) - 1;
|
|
pszBaseDllName = szImageName + dwLen;
|
|
|
|
while (*pszBaseDllName && *pszBaseDllName != '\\') {
|
|
--pszBaseDllName;
|
|
}
|
|
|
|
if (!*pszBaseDllName) {
|
|
dprintf("%s doesn't contain a full path\n", szImageName);
|
|
goto EXIT;
|
|
}
|
|
|
|
++pszBaseDllName;
|
|
*pszBaseDllName = '\0';
|
|
|
|
StringCchCopy(g_szSystem32Dir, MAX_DLL_IMAGE_NAME_LEN, szImageName);
|
|
g_dwSystem32DirLen = (DWORD)(pszBaseDllName - szImageName);
|
|
|
|
bIsSuccess = TRUE;
|
|
|
|
EXIT:
|
|
|
|
return bIsSuccess;
|
|
}
|
|
|
|
BOOL
|
|
IsInSystem32(
|
|
PCSTR szModuleName,
|
|
BOOL* pbInSystem32
|
|
)
|
|
{
|
|
BOOL bIsSuccess = FALSE;
|
|
char szImageName[MAX_DLL_IMAGE_NAME_LEN];
|
|
|
|
if (!GetSystem32Directory()) {
|
|
goto EXIT;
|
|
}
|
|
|
|
if (GetDllImageNameByModuleName(szModuleName, szImageName, MAX_DLL_IMAGE_NAME_LEN) != S_OK) {
|
|
goto EXIT;
|
|
}
|
|
|
|
dprintf("the image name is %s\n", szImageName);
|
|
|
|
*pbInSystem32 = !_strnicmp(g_szSystem32Dir, szImageName, g_dwSystem32DirLen);
|
|
|
|
dprintf("%s %s in system32\n", szModuleName, (*pbInSystem32 ? "is" : "is not"));
|
|
|
|
bIsSuccess = TRUE;
|
|
|
|
EXIT:
|
|
|
|
return bIsSuccess;
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
!checkinex dllname apiname
|
|
|
|
dllname is without the .dll extention. eg:
|
|
|
|
!checkinex kernel32 createfilea
|
|
|
|
History:
|
|
|
|
03/26/2002 maonis Created
|
|
|
|
--*/
|
|
DECLARE_API ( checkex )
|
|
{
|
|
ULONG64 DllName;
|
|
char szAPIName[MAX_API_NAME_LEN];
|
|
char szCurrentAPIName[MAX_API_NAME_LEN];
|
|
char szModuleName[MAX_MODULE_NAME_LEN];
|
|
PCSTR pszModuleName, pszAPIName;
|
|
BOOL bInSystem32 = FALSE;
|
|
ULONG64 Value;
|
|
ULONG64 CurrentHookAPIArray;
|
|
ULONG64 CurrentHookAPI;
|
|
ULONG64 CurrentShimInfo;
|
|
ULONG64 FunctionName;
|
|
ULONG64 Hook;
|
|
ULONG64 NextHook;
|
|
ULONG64 HookEx;
|
|
ULONG64 HookAddress;
|
|
ULONG64 PfnNew;
|
|
ULONG64 PfnOld;
|
|
ULONG64 TopOfChain;
|
|
DWORD i, j;
|
|
DWORD dwHookAPISize;
|
|
DWORD dwShimInfoSize;
|
|
DWORD dwHookedAPIs;
|
|
DWORD dwShimsCount;
|
|
|
|
INIT_API();
|
|
|
|
if (!IsShimInitialized()) {
|
|
goto EXIT;
|
|
}
|
|
|
|
//
|
|
// Get the dll name.
|
|
//
|
|
if (!GetArg(&args, &pszModuleName)) {
|
|
dprintf("Usage: !checkinex dllname apiname\n");
|
|
goto EXIT;
|
|
}
|
|
|
|
StringCchCopyN(szModuleName, MAX_MODULE_NAME_LEN, pszModuleName, args - pszModuleName);
|
|
|
|
//
|
|
// Get the API name.
|
|
//
|
|
if (!GetArg(&args, &pszAPIName)) {
|
|
dprintf("Usage: !checkinex dllname apiname\n");
|
|
goto EXIT;
|
|
}
|
|
|
|
StringCchCopyN(szAPIName, MAX_API_NAME_LEN, pszAPIName, args - pszAPIName);
|
|
|
|
//
|
|
// Check to see if it's in system32.
|
|
//
|
|
if (!IsInSystem32(szModuleName, &bInSystem32)) {
|
|
dprintf("Failed to determine if %s is in system32 or not\n", szModuleName);
|
|
goto EXIT;
|
|
}
|
|
|
|
//
|
|
// Get the chain with this API.
|
|
//
|
|
if (!GetVarValueULONG64("shimeng!g_dwShimsCount", &Value)) {
|
|
dprintf("failed to get the number of shims applied to this process\n");
|
|
goto EXIT;
|
|
}
|
|
|
|
dwShimsCount = (DWORD)Value - 1;
|
|
|
|
if (!GetVarValueULONG64("shimeng!g_pShimInfo", &Value)) {
|
|
dprintf("failed to get the address of shiminfo\n");
|
|
goto EXIT;
|
|
}
|
|
|
|
CurrentShimInfo = Value;
|
|
|
|
dwShimInfoSize = GetTypeSize("shimeng!tageSHIMINFO");
|
|
|
|
dwHookAPISize = GetTypeSize("shimeng!tagHOOKAPI");
|
|
|
|
if (!dwHookAPISize) {
|
|
|
|
dprintf("failed to get the HOOKAPI size\n");
|
|
goto EXIT;
|
|
}
|
|
|
|
if (!GetVarValueULONG64("shimeng!g_pHookArray", &Value)) {
|
|
dprintf("failed to get the address of shiminfo\n");
|
|
goto EXIT;
|
|
}
|
|
|
|
CurrentHookAPIArray = Value;
|
|
|
|
for (i = 0; i < dwShimsCount; ++i) {
|
|
|
|
//
|
|
// Get the number of hooks this shim has.
|
|
//
|
|
if (GetFieldValue(CurrentShimInfo, "shimeng!tagSHIMINFO", "dwHookedAPIs", dwHookedAPIs)) {
|
|
|
|
dprintf("failed to get the number of hooked APIs for shim #%d\n",
|
|
i);
|
|
|
|
goto EXIT;
|
|
}
|
|
|
|
if (!ReadPointer(CurrentHookAPIArray, &CurrentHookAPI)) {
|
|
dprintf("failed to get the begining of hook api array\n");
|
|
|
|
goto EXIT;
|
|
}
|
|
|
|
for (j = 0; j < dwHookedAPIs; ++j) {
|
|
|
|
GET_AND_CHECK_FIELDVALUE(CurrentHookAPI, "shimeng!tagHOOKAPI", "pszFunctionName", FunctionName);
|
|
|
|
if (!GetData(FunctionName, szCurrentAPIName, MAX_API_NAME_LEN)) {
|
|
dprintf("failed to read in the API name at %08x\n", FunctionName);
|
|
}
|
|
|
|
if (!lstrcmpi(szAPIName, szCurrentAPIName)) {
|
|
|
|
//
|
|
// We found the API, now get top of the chain.
|
|
//
|
|
GET_AND_CHECK_FIELDVALUE(CurrentHookAPI, "shimeng!tagHOOKAPI", "pHookEx", HookEx);
|
|
GET_AND_CHECK_FIELDVALUE(HookEx, "shimeng!tagHOOKAPIEX", "pTopOfChain", TopOfChain);
|
|
//dprintf("top of chain is %08x\n", TopOfChain);
|
|
|
|
//
|
|
// Found the API, now see why this API is shimmed or unshimmed.
|
|
//
|
|
IsExcluded(szModuleName, TopOfChain, bInSystem32);
|
|
goto EXIT;
|
|
}
|
|
|
|
CurrentHookAPI += dwHookAPISize;
|
|
}
|
|
|
|
CurrentShimInfo += dwShimInfoSize;
|
|
CurrentHookAPIArray += sizeof(ULONG_PTR);
|
|
}
|
|
|
|
dprintf("No shims are hooking API %s\n", szAPIName);
|
|
|
|
EXIT:
|
|
|
|
EXIT_API();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
BOOL
|
|
GetExeNameWithFullPath(
|
|
PSTR pszExeName,
|
|
DWORD dwExeNameSize
|
|
)
|
|
{
|
|
BOOL bIsSuccess = FALSE;
|
|
HRESULT hr;
|
|
|
|
if ((hr = g_ExtSystem->GetCurrentProcessExecutableName(
|
|
pszExeName,
|
|
dwExeNameSize,
|
|
NULL)) == S_OK)
|
|
{
|
|
if (CheckForFullPath(pszExeName)) {
|
|
bIsSuccess = TRUE;
|
|
}
|
|
} else {
|
|
dprintf("GetCurrentProcessExecutableName returned %08x\n", hr);
|
|
}
|
|
|
|
return bIsSuccess;
|
|
}
|
|
|
|
void
|
|
ConvertMatchModeToString(
|
|
DWORD dwMatchMode,
|
|
LPSTR pszMatchMode,
|
|
DWORD dwLen
|
|
)
|
|
{
|
|
switch (dwMatchMode)
|
|
{
|
|
case MATCH_NORMAL:
|
|
StringCchCopy(pszMatchMode, dwLen, "NORMAL");
|
|
break;
|
|
|
|
case MATCH_EXCLUSIVE:
|
|
StringCchCopy(pszMatchMode, dwLen, "EXCLUSIVE");
|
|
break;
|
|
|
|
case MATCH_ADDITIVE:
|
|
StringCchCopy(pszMatchMode, dwLen, "ADDITIVE");
|
|
break;
|
|
|
|
default:
|
|
StringCchCopy(pszMatchMode, dwLen, "UNKNOWN");
|
|
}
|
|
}
|
|
|
|
void
|
|
ConvertDBLocationToString(
|
|
TAGREF trExe,
|
|
LPSTR pszDBLocation,
|
|
DWORD dwLen
|
|
)
|
|
{
|
|
switch (trExe & TAGREF_STRIP_PDB) {
|
|
case PDB_MAIN:
|
|
|
|
StringCchCopy(pszDBLocation, dwLen, "MAIN");
|
|
break;
|
|
|
|
case PDB_TEST:
|
|
|
|
StringCchCopy(pszDBLocation, dwLen, "TEST");
|
|
break;
|
|
|
|
//
|
|
// Everything else is local.
|
|
//
|
|
case PDB_LOCAL:
|
|
default:
|
|
|
|
StringCchCopy(pszDBLocation, dwLen, "CUSTOM");
|
|
break;
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
CheckEqualGUIDs(
|
|
const GUID* guid1,
|
|
const GUID* guid2
|
|
)
|
|
{
|
|
return (
|
|
((PLONG)guid1)[0] == ((PLONG)guid2)[0] &&
|
|
((PLONG)guid1)[1] == ((PLONG)guid2)[1] &&
|
|
((PLONG)guid1)[2] == ((PLONG)guid2)[2] &&
|
|
((PLONG)guid1)[3] == ((PLONG)guid2)[3]);
|
|
}
|
|
|
|
void
|
|
GetDBInfo(
|
|
PDB pdb,
|
|
LPWSTR pwszDBInfo,
|
|
DWORD dwLen
|
|
)
|
|
{
|
|
TAGID tiDatabase, tiDatabaseID;
|
|
GUID* pGuid;
|
|
WCHAR wszGuid[64];
|
|
NTSTATUS status;
|
|
|
|
tiDatabase = SdbFindFirstTag(pdb, TAGID_ROOT, TAG_DATABASE);
|
|
|
|
if (tiDatabase == TAGID_NULL) {
|
|
dprintf("Failed to find TAG_DATABASE\n");
|
|
return;
|
|
}
|
|
|
|
tiDatabaseID = SdbFindFirstTag(pdb, tiDatabase, TAG_DATABASE_ID);
|
|
|
|
if (tiDatabaseID == TAGID_NULL) {
|
|
dprintf("Failed to find TAG_DATABASE_ID\n");
|
|
return;
|
|
}
|
|
|
|
pGuid = (GUID*)SdbGetBinaryTagData(pdb, tiDatabaseID);
|
|
|
|
if (!pGuid) {
|
|
dprintf("Failed to read the GUID for this Database ID %08x\n", tiDatabaseID);
|
|
return;
|
|
}
|
|
|
|
if (CheckEqualGUIDs(pGuid, &GUID_SYSMAIN_SDB)) {
|
|
|
|
StringCchCopyW(pwszDBInfo, dwLen, L"sysmain.sdb");
|
|
|
|
} else if (CheckEqualGUIDs(pGuid, &GUID_MSIMAIN_SDB)) {
|
|
|
|
StringCchCopyW(pwszDBInfo, dwLen, L"msimain.sdb");
|
|
|
|
} else if (CheckEqualGUIDs(pGuid, &GUID_DRVMAIN_SDB)) {
|
|
|
|
StringCchCopyW(pwszDBInfo, dwLen, L"drvmain.sdb");
|
|
|
|
} else if (CheckEqualGUIDs(pGuid, &GUID_APPHELP_SDB)) {
|
|
|
|
StringCchCopyW(pwszDBInfo, dwLen, L"apphelp.sdb");
|
|
|
|
} else if (CheckEqualGUIDs(pGuid, &GUID_SYSTEST_SDB)) {
|
|
|
|
StringCchCopyW(pwszDBInfo, dwLen, L"systest.sdb");
|
|
|
|
} else {
|
|
//
|
|
// None of the above, so it's a custom sdb.
|
|
//
|
|
SdbGUIDToString(pGuid, wszGuid, CHARCOUNT(wszGuid));
|
|
|
|
StringCchCopyW(pwszDBInfo, dwLen, wszGuid);
|
|
StringCchCatW(pwszDBInfo, dwLen, L".sdb");
|
|
}
|
|
}
|
|
|
|
void
|
|
ShowSdbEntryInfo(
|
|
HSDB hSDB,
|
|
PSDBQUERYRESULT psdbQuery
|
|
)
|
|
{
|
|
DWORD dw, dwMatchMode;
|
|
TAGREF trExe;
|
|
PDB pdb;
|
|
TAGID tiExe, tiExeID, tiAppName, tiVendor, tiMatchMode;
|
|
GUID* pGuidExeID;
|
|
WCHAR wszGuid[64];
|
|
PWSTR pwszAppName, pwszVendorName;
|
|
char szMatchMode[8];
|
|
char szDBLocation[8];
|
|
WCHAR wszDBInfo[48];
|
|
|
|
dprintf("%-42s%-16s%-16s%-12s%-20s\n",
|
|
"EXE GUID",
|
|
"App Name",
|
|
"Vendor",
|
|
"Match Mode",
|
|
"Database");
|
|
|
|
for (dw = 0; dw < SDB_MAX_EXES; dw++) {
|
|
|
|
trExe = psdbQuery->atrExes[dw];
|
|
|
|
if (trExe == TAGREF_NULL) {
|
|
break;
|
|
}
|
|
|
|
if (!SdbTagRefToTagID(hSDB, trExe, &pdb, &tiExe) || pdb == NULL) {
|
|
dprintf("Failed to get tag id from tag ref\n");
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Get the GUID of this EXE tag.
|
|
//
|
|
tiExeID = SdbFindFirstTag(pdb, tiExe, TAG_EXE_ID);
|
|
|
|
if (tiExeID == TAGID_NULL) {
|
|
dprintf("Failed to get the name tag id\n");
|
|
return;
|
|
}
|
|
|
|
pGuidExeID = (GUID*)SdbGetBinaryTagData(pdb, tiExeID);
|
|
|
|
if (!pGuidExeID) {
|
|
dprintf("Cannot read the ID for EXE tag 0x%x.\n", tiExe);
|
|
return;
|
|
}
|
|
|
|
SdbGUIDToString(pGuidExeID, wszGuid, CHARCOUNT(wszGuid));
|
|
|
|
//
|
|
// Get the App Name for this Exe.
|
|
//
|
|
tiAppName = SdbFindFirstTag(pdb, tiExe, TAG_APP_NAME);
|
|
|
|
if (tiAppName != TAGID_NULL) {
|
|
pwszAppName = SdbGetStringTagPtr(pdb, tiAppName);
|
|
}
|
|
|
|
//
|
|
// Get the vendor Name for this Exe.
|
|
//
|
|
tiVendor = SdbFindFirstTag(pdb, tiExe, TAG_VENDOR);
|
|
|
|
if (tiVendor != TAGID_NULL) {
|
|
pwszVendorName = SdbGetStringTagPtr(pdb, tiVendor);
|
|
}
|
|
|
|
tiMatchMode = SdbFindFirstTag(pdb, tiExe, TAG_MATCH_MODE);
|
|
|
|
dwMatchMode = SdbReadWORDTag(pdb, tiMatchMode, MATCHMODE_DEFAULT_MAIN);
|
|
|
|
ConvertMatchModeToString(dwMatchMode, szMatchMode, sizeof(szMatchMode));
|
|
|
|
ConvertDBLocationToString(trExe, szDBLocation, sizeof(szDBLocation));
|
|
|
|
GetDBInfo(pdb, wszDBInfo, sizeof(wszDBInfo) /sizeof(wszDBInfo[0]));
|
|
|
|
dprintf("%-42S%-16S%-16S%-12s%16S (%s)\n",
|
|
wszGuid,
|
|
pwszAppName,
|
|
pwszVendorName,
|
|
szMatchMode,
|
|
wszDBInfo,
|
|
szDBLocation);
|
|
}
|
|
}
|
|
|
|
LPWSTR
|
|
AnsiToUnicode(
|
|
LPCSTR psz)
|
|
{
|
|
LPWSTR pwsz = NULL;
|
|
|
|
if (psz) {
|
|
int nChars = MultiByteToWideChar(CP_ACP, 0, psz, -1, NULL, 0);
|
|
|
|
pwsz = (LPWSTR)malloc(nChars * sizeof(WCHAR));
|
|
|
|
if (pwsz) {
|
|
nChars = MultiByteToWideChar(CP_ACP, 0, psz, -1, pwsz, nChars);
|
|
|
|
if (!nChars) {
|
|
dprintf("Failed to convert %s to unicode: %d\n", psz, GetLastError());
|
|
free(pwsz);
|
|
pwsz = NULL;
|
|
}
|
|
} else {
|
|
dprintf("Failed to allocate memory to convert %s to unicode\n", psz);
|
|
}
|
|
}
|
|
|
|
return pwsz;
|
|
}
|
|
|
|
DECLARE_API ( matchmode )
|
|
{
|
|
HSDB hSDB = NULL;
|
|
SDBQUERYRESULT sdbQuery;
|
|
BOOL bResult;
|
|
char szExeName[MAX_PATH];
|
|
LPWSTR pwszExeName = NULL;
|
|
|
|
INIT_API();
|
|
|
|
hSDB = SdbInitDatabase(0, NULL);
|
|
|
|
if (hSDB == NULL) {
|
|
dprintf("Failed to open the shim database.\n");
|
|
return NULL;
|
|
}
|
|
|
|
ZeroMemory(&sdbQuery, sizeof(SDBQUERYRESULT));
|
|
|
|
//
|
|
// Get the full path to the exe.
|
|
//
|
|
if (!GetExeNameWithFullPath(szExeName, sizeof(szExeName))) {
|
|
dprintf("failed to get exe name\n");
|
|
goto EXIT;
|
|
}
|
|
|
|
//dprintf("the exe name is %s\n", szExeName);
|
|
|
|
pwszExeName = AnsiToUnicode(szExeName);
|
|
|
|
if (!pwszExeName) {
|
|
goto EXIT;
|
|
}
|
|
|
|
bResult = SdbGetMatchingExe(hSDB, pwszExeName, NULL, NULL, 0, &sdbQuery);
|
|
|
|
if (!bResult) {
|
|
dprintf("Failed to get the matching info for this process\n");
|
|
goto EXIT;
|
|
} else {
|
|
ShowSdbEntryInfo(hSDB, &sdbQuery);
|
|
}
|
|
|
|
EXIT:
|
|
|
|
if (hSDB) {
|
|
SdbReleaseDatabase(hSDB);
|
|
}
|
|
|
|
if (pwszExeName) {
|
|
free(pwszExeName);
|
|
}
|
|
|
|
EXIT_API();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
DECLARE_API ( help )
|
|
{
|
|
dprintf("Help for extension dll shimexts.dll\n\n"
|
|
" !loadshims - This will stop right after the shims are loaded so if !shimnames\n"
|
|
" says the shims are not initialized, try this then !shimnames again.\n\n"
|
|
" !shimnames - It displays the name of the shims and how many APIs each shim hooks.\n\n"
|
|
" !debuglevel <val> - It changes the shimeng debug level to val (0 to 9).\n\n"
|
|
" !sdebuglevel <shim> <val>- It changes the shim's debug level to val (0 to 9). If no shim is\n"
|
|
" specified, it changes all shims'd debug level to val.\n\n"
|
|
" !displayhooks shim - Given the name of a shim, it displays the APIs it hooks.\n\n"
|
|
" !displaychain addr - If addr is one of the hooked API address, we print out the chain that\n"
|
|
" contains that addr.\n\n"
|
|
" !shimengsym - Checks if you have correct symbols for shimeng.\n\n"
|
|
" !checkex dllname apiname - It tells why this API called by this DLL is not shimmed.\n\n"
|
|
" !matchmode - It tells why this module is shimmed with these shims.\n\n"
|
|
" !help - It displays this help.\n"
|
|
);
|
|
|
|
return S_OK;
|
|
}
|