///////////////////////
//
// excerpt from objidl.idl
//
//
[
local,
object,
uuid(1008c4a0-7613-11cf-9af1-0020af6e72f4)
]
interface IChannelHook : IUnknown
{
// called from GetBuffer prior to sending ORPC response
void ClientGetSize(
[in] REFGUID uExtent,
[in] REFIID riid,
[out] ULONG *pDataSize );
// called from GetBuffer to write ORPCTHIS extension
void ClientFillBuffer(
[in] REFGUID uExtent,
[in] REFIID riid,
[in, out] ULONG *pDataSize,
[in] void *pDataBuffer );
// called from SendReceive to read ORPCTHAT extension
void ClientNotify(
[in] REFGUID uExtent,
[in] REFIID riid,
[in] ULONG cbDataSize,
[in] void *pDataBuffer,
[in] DWORD lDataRep,
[in] HRESULT hrFault );
// called prior to Invoke to read ORPCTHIS extension
void ServerNotify(
[in] REFGUID uExtent,
[in] REFIID riid,
[in] ULONG cbDataSize,
[in] void *pDataBuffer,
[in] DWORD lDataRep );
// called from server-side GetBuffer
void ServerGetSize(
[in] REFGUID uExtent,
[in] REFIID riid,
[in] HRESULT hrFault,
[out] ULONG *pDataSize );
// called from server-side GetBuffer to write ORPCTHAT extension
void ServerFillBuffer(
[in] REFGUID uExtent,
[in] REFIID riid,
[in, out] ULONG *pDataSize,
[in] void *pDataBuffer,
[in] HRESULT hrFault );
};
Figure 4   SimpleChannelHook
common.h
///////////////////////////////////////////////////
//
// common.h - 1997, Don Box
//
// Common call/thread context structures used by hook
//
//
#ifndef _COMMON_H
#define _COMMON_H
#include <windows.h>
// information about a particular call site
struct NODE_INFO
{
DWORD pid;
DWORD tid;
DWORD ip;
};
// wire representation of ORPCTHIS extension
struct HOOK_THIS
{
GUID cid; // fake causality ID
NODE_INFO niDirect; // node of immediate caller
NODE_INFO niIndirect; // node of original caller
};
// wire representation of ORPCTHIS extension
struct HOOK_THAT
{
NODE_INFO niTarget; // node where call executed
};
// call contexts are a linked list of ORPCTHIS structs
struct CALL_CONTEXT : public HOOK_THIS
{
CALL_CONTEXT *pNext;
};
// thread context is a linked list of calls + an ORPCTHAT
struct THREAD_CONTEXT : public HOOK_THAT
{
NODE_INFO niThis;
CALL_CONTEXT *pCurrentCall;
};
// EXTENTID_HostHook is the ORPCTHIS/THAT extension ID
// {F23ADD53-4992-11d1-991C-006097585A3C}
DEFINE_GUID(EXTENTID_HostHook, 0xf23add53, 0x4992, 0x11d1, 0x99, 0x1c,
0x0, 0x60, 0x97, 0x58, 0x5a, 0x3c);
extern HINSTANCE g_hInstance;
// helper function to grab thread context from TLS
THREAD_CONTEXT *GetCurrentThreadContext(void);
#endif
Hook.h
///////////////////////////////////////////////////
//
// Hook.h - 1997, Don Box
//
// A host info channel hook
//
//
#ifndef _HOSTHOOK_H
#define _HOSTHOOK_H
#include <windows.h>
class HostHook : public IChannelHook
{
public:
STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
STDMETHODIMP_(ULONG) AddRef(void);
STDMETHODIMP_(ULONG) Release(void);
STDMETHODIMP_(void) ClientGetSize(REFGUID uExtent, REFIID riid,
ULONG *pDataSize);
STDMETHODIMP_(void) ClientFillBuffer(REFGUID uExtent, REFIID riid,
ULONG *pDataSize, void *pDataBuffer);
STDMETHODIMP_(void) ClientNotify(REFGUID uExtent, REFIID riid,
ULONG cbDataSize, void *pDataBuffer,
DWORD lDataRep, HRESULT hrFault);
STDMETHODIMP_(void) ServerNotify(REFGUID uExtent, REFIID riid,
ULONG cbDataSize, void *pDataBuffer,
DWORD lDataRep);
STDMETHODIMP_(void) ServerGetSize(REFGUID uExtent, REFIID riid,
HRESULT hrFault, ULONG *pDataSize);
STDMETHODIMP_(void) ServerFillBuffer(REFGUID uExtent, REFIID riid,
ULONG *pDataSize, void *pDataBuffer,
HRESULT hrFault);
};
#endif
Hook.cpp
///////////////////////////////////////////////////
//
// Hook.cpp - 1997, Don Box
//
// A host info channel hook
//
//
#include "hook.h"
#include <initguid.h>
#include "common.h"
#include <assert.h>
static NODE_INFO g_niThis;
DWORD g_dwTLSIndex;
HANDLE g_hheap;
HINSTANCE g_hInstance;
BOOL WINAPI DllMain(HINSTANCE h, DWORD dwReason, void *pv) {
// declare one process-wide channel hook instance
static HostHook s_theHook;
switch (dwReason)
{
case DLL_PROCESS_ATTACH:
{
// turn on sockets if not already enabled in this process
WSADATA wsad;
WSAStartup(MAKEWORD(1,1), &wsad);
// acquire a TLS slot
if ((g_dwTLSIndex = TlsAlloc()) == 0xFFFFFFFF)
return FALSE;
// register the process-wide channel hook
HRESULT hr = CoRegisterChannelHook(EXTENTID_HostHook, &s_theHook);
if (FAILED(hr))
return FALSE;
// initialize various globals including getting the current IP address
g_hInstance = h;
g_hheap = HeapCreate(0, 1, 0);
g_niThis.pid = GetCurrentProcessId();
char szHostName[1024];
int res = gethostname(szHostName, 1024);
if (res == 0) {
HOSTENT *p = gethostbyname(szHostName);
if (p)
memcpy(&g_niThis.ip, p->h_addr, 4);
}
}
break;
case DLL_THREAD_DETACH:
{
// clean up any thread/call context acquired for this thread
THREAD_CONTEXT *p = (THREAD_CONTEXT *)TlsGetValue(g_dwTLSIndex);
if (p)
{
CALL_CONTEXT *pc = p->pCurrentCall;
while (pc)
{
p->pCurrentCall = p->pCurrentCall->pNext;
HeapFree(g_hheap, 0, pc);
pc = p->pCurrentCall;
}
HeapFree(g_hheap, 0, p);
}
}
break;
case DLL_PROCESS_DETACH:
// release TLS slot and deallocate all memory
if (g_dwTLSIndex != 0xFFFFFFFF)
TlsFree(g_dwTLSIndex);
HeapDestroy(g_hheap);
break;
}
return TRUE;
}
// Helper routine to grab onto the current THREAD_CONTEXT
THREAD_CONTEXT *GetCurrentThreadContext(void)
{
// grab the pointer
void *pv = TlsGetValue(g_dwTLSIndex);
// if this is the first time through, alloc and init
if (pv == 0)
{
pv = HeapAlloc(g_hheap, 0, sizeof(THREAD_CONTEXT));
assert(pv);
TlsSetValue(g_dwTLSIndex, pv);
THREAD_CONTEXT& tc = *((THREAD_CONTEXT*)pv);
tc.pCurrentCall = 0;
tc.niThis = g_niThis;
tc.niThis.tid = GetCurrentThreadId();
tc.niTarget = tc.niThis;
}
return (THREAD_CONTEXT*)pv;
}
// Helper routine to push a new call context onto the current stack
// based (potentially) on a new incoming call
CALL_CONTEXT *PushCallContext(void *pvORPCTHIS)
{
// get TLS slot for this thread
THREAD_CONTEXT *pThreadContext = GetCurrentThreadContext();
assert(pThreadContext);
// allocate a new call context for this call
CALL_CONTEXT *pNewCallContext = (CALL_CONTEXT*)HeapAlloc(g_hheap, 0,
sizeof(CALL_CONTEXT));
assert(pNewCallContext);
// initialize new call context
if (pvORPCTHIS)
{
memcpy(static_cast<HOOK_THIS*>(pNewCallContext), pvORPCTHIS,
sizeof(HOOK_THIS));
}
else if (pThreadContext->pCurrentCall)
{
*pNewCallContext = *pThreadContext->pCurrentCall;
pNewCallContext->niDirect = pThreadContext->niThis;
}
else
{
CoCreateGuid(&pNewCallContext->cid);
pNewCallContext->niDirect = pNewCallContext->niIndirect =
pThreadContext->niThis;
}
// push context onto stack
pNewCallContext->pNext = pThreadContext->pCurrentCall;
pThreadContext->pCurrentCall = pNewCallContext;
return pNewCallContext;
}
// Helper routine to pop current call context from the thread stack
void PopCallContext(void *pvORPCTHAT)
{
THREAD_CONTEXT *pThreadContext = GetCurrentThreadContext();
assert(pThreadContext);
HOOK_THAT *pht = pThreadContext;
if (pvORPCTHAT)
*pht = *(HOOK_THAT*)pvORPCTHAT;
else
pThreadContext->niTarget = pThreadContext->niThis;
// pop the current call context
if (pThreadContext->pCurrentCall)
{
CALL_CONTEXT *pctx = pThreadContext->pCurrentCall;
pThreadContext->pCurrentCall = pThreadContext->pCurrentCall->pNext;
HeapFree(g_hheap, 0, pctx);
}
}
STDMETHODIMP HostHook::QueryInterface(REFIID riid, void **ppv)
{
if (riid == IID_IUnknown)
*ppv = static_cast<IUnknown*>(this);
else if (riid == IID_ChannelHook)
*ppv = static_cast<IChannelHook*>(this);
else
return (*ppv = 0), E_NOINTERFACE;
reinterpret_cast<IUnknown*>(*ppv)->AddRef();
return S_OK;
}
STDMETHODIMP_(ULONG) HostHook::AddRef(void)
{
return 2;
}
STDMETHODIMP_(ULONG) HostHook::Release(void)
{
return 1;
}
// called in client prior to making a call
STDMETHODIMP_(void) HostHook::ClientGetSize(REFGUID uExtent, REFIID riid,
ULONG *pDataSize)
{
assert(uExtent == EXTENTID_HostHook);
*pDataSize = sizeof(HOOK_THIS);
}
// called in client prior to making a call
STDMETHODIMP_(void) HostHook::ClientFillBuffer(REFGUID uExtent, REFIID riid,
ULONG *pDataSize,
void *pDataBuffer)
{
assert(uExtent == EXTENTID_HostHook);
CALL_CONTEXT *pContext = PushCallContext(0);
HOOK_THIS *pORPCTHIS = (HOOK_THIS *)pDataBuffer;
*pORPCTHIS = *pContext;
*pDataSize = sizeof(HOOK_THIS);
}
// called in client just after a call completes
STDMETHODIMP_(void) HostHook::ClientNotify(REFGUID uExtent, REFIID riid,
ULONG cbDataSize, void *pDataBuffer,
DWORD lDataRep, HRESULT hrFault)
{
assert(uExtent == EXTENTID_HostHook);
assert(lDataRep == NDR_LOCAL_DATA_REPRESENTATION);
PopCallContext(pDataBuffer);
}
// called in server just prior to invoking a call
STDMETHODIMP_(void) HostHook::ServerNotify(REFGUID uExtent, REFIID riid,
ULONG cbDataSize, void *pDataBuffer,
DWORD lDataRep)
{
assert(uExtent == EXTENTID_HostHook);
assert(lDataRep == NDR_LOCAL_DATA_REPRESENTATION);
PushCallContext(pDataBuffer);
}
// called in server just after invoking a call
STDMETHODIMP_(void) HostHook::ServerGetSize(REFGUID uExtent, REFIID riid,
HRESULT hrFault, ULONG *pDataSize)
{
*pDataSize = sizeof(HOOK_THAT);
}
// called in server just after invoking a call
STDMETHODIMP_(void) HostHook::ServerFillBuffer(REFGUID uExtent, REFIID riid,
ULONG *pDataSize,
void *pDataBuffer, HRESULT hrFault)
{
assert(uExtent == EXTENTID_HostHook);
THREAD_CONTEXT *pThreadContext = GetCurrentThreadContext();
HOOK_THAT *pORPCTHAT = (HOOK_THAT *)pDataBuffer;
pORPCTHAT->niTarget = pThreadContext->niThis;
*pDataSize = sizeof(HOOK_THAT);
PopCallContext(0);
}
Figure 5 CallInfo.idl
interface ICallInfo : IUnknown {
[propget] HRESULT OriginalProcessID([out, retval] long *pVal);
[propget] HRESULT OriginalThreadID([out, retval] long *pVal);
[propget] HRESULT OriginalHostID([out, retval] long *pVal);
[propget] HRESULT OriginalHostName([out, retval] BSTR *pVal);
[propget] HRESULT DirectProcessID([out, retval] long *pVal);
[propget] HRESULT DirectThreadID([out, retval] long *pVal);
[propget] HRESULT DirectHostID([out, retval] long *pVal);
[propget] HRESULT DirectHostName([out, retval] BSTR *pVal);
[propget] HRESULT PseudoCausalityID([out, retval] GUID *pguid);
}
Figure 6 CallInfo
callinfo.h
///////////////////////////////////////////////////
//
// callinfo.h - 1997, Don Box
//
// External accessor to host info stored by channel hook
//
//
#ifndef _CALLINFO_H
#define _CALLINFO_H
#include <windows.h>
#include "hosthook.h"
class CallInfo : public ICallInfo, public IClientCallInfo
{
public:
STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
STDMETHODIMP_(ULONG) AddRef(void);
STDMETHODIMP_(ULONG) Release(void);
STDMETHODIMP get_OriginalProcessID(long *pVal);
STDMETHODIMP get_OriginalThreadID(long *pVal);
STDMETHODIMP get_OriginalHostID(long *pVal);
STDMETHODIMP get_OriginalHostName(BSTR *pVal);
STDMETHODIMP get_DirectProcessID(long *pVal);
STDMETHODIMP get_DirectThreadID(long *pVal);
STDMETHODIMP get_DirectHostID(long *pVal);
STDMETHODIMP get_DirectHostName(BSTR *pVal);
STDMETHODIMP get_PseudoCausalityID(GUID *pguid);
STDMETHODIMP get_TargetProcessID(long *pVal);
STDMETHODIMP get_TargetThreadID(long *pVal);
STDMETHODIMP get_TargetHostID(long *pVal);
STDMETHODIMP get_TargetHostName(BSTR *pVal);
};
#endif
callinfo.cpp
///////////////////////////////////////////////////
//
// callinfo.cpp - 1997, Don Box
//
// External accessor to host info stored by channel hook
//
//
#include "callinfo.h"
#include "common.h"
#include "hosthook_i.c"
// helper routine to convert IP address to a BSTR
BSTR GetNameFromIPAddress(DWORD dwIP)
{
HOSTENT *p = gethostbyaddr((const char*)&dwIP, 4, AF_INET);
if (p && p->h_name)
{
int cch = lstrlenA(p->h_name);
BSTR bstr = SysAllocStringLen(0, cch);
if (bstr)
MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, p->h_name, cch + 1,
bstr, cch + 1);
return bstr;
}
return 0;
}
STDMETHODIMP CallInfo::QueryInterface(REFIID riid, void **ppv)
{
if (riid == IID_IUnknown)
*ppv = static_cast<ICallInfo*>(this);
else if (riid == IID_ICallInfo)
*ppv = static_cast<ICallInfo*>(this);
else if (riid == IID_IClientCallInfo)
*ppv = static_cast<IClientCallInfo*>(this);
else
return (*ppv = 0), E_NOINTERFACE;
reinterpret_cast<IUnknown*>(*ppv)->AddRef();
return S_OK;
}
STDMETHODIMP_(ULONG) CallInfo::AddRef(void)
{
return 2;
}
STDMETHODIMP_(ULONG) CallInfo::Release(void)
{
return 1;
}
STDMETHODIMP CallInfo::get_OriginalProcessID(long *pVal)
{
THREAD_CONTEXT *pctx = GetCurrentThreadContext();
NODE_INFO& rni = pctx->pCurrentCall ? pctx->pCurrentCall->niIndirect :
pctx->niThis;
*pVal = rni.pid;
return S_OK;
}
STDMETHODIMP CallInfo::get_OriginalThreadID(long *pVal)
{
THREAD_CONTEXT *pctx = GetCurrentThreadContext();
NODE_INFO& rni = pctx->pCurrentCall ? pctx->pCurrentCall->niIndirect :
pctx->niThis;
*pVal = rni.tid;
return S_OK;
}
STDMETHODIMP CallInfo::get_OriginalHostID(long *pVal)
{
THREAD_CONTEXT *pctx = GetCurrentThreadContext();
NODE_INFO& rni = pctx->pCurrentCall ? pctx->pCurrentCall->niIndirect :
pctx->niThis;
*pVal = rni.ip;
return S_OK;
}
STDMETHODIMP CallInfo::get_OriginalHostName(BSTR *pVal)
{
THREAD_CONTEXT *pctx = GetCurrentThreadContext();
NODE_INFO& rni = pctx->pCurrentCall ? pctx->pCurrentCall->niIndirect :
pctx->niThis;
*pVal = GetNameFromIPAddress(rni.ip);
return S_OK;
}
STDMETHODIMP CallInfo::get_DirectProcessID(long *pVal)
{
THREAD_CONTEXT *pctx = GetCurrentThreadContext();
NODE_INFO& rni = pctx->pCurrentCall ? pctx->pCurrentCall->niDirect :
pctx->niThis;
*pVal = rni.pid;
return S_OK;
}
STDMETHODIMP CallInfo::get_DirectThreadID(long *pVal)
{
THREAD_CONTEXT *pctx = GetCurrentThreadContext();
NODE_INFO& rni = pctx->pCurrentCall ? pctx->pCurrentCall->niDirect :
pctx->niThis;
*pVal = rni.tid;
return S_OK;
}
STDMETHODIMP CallInfo::get_DirectHostID(long *pVal)
{
THREAD_CONTEXT *pctx = GetCurrentThreadContext();
NODE_INFO& rni = pctx->pCurrentCall ? pctx->pCurrentCall->niDirect :
pctx->niThis;
*pVal = rni.ip;
return S_OK;
}
STDMETHODIMP CallInfo::get_DirectHostName(BSTR *pVal)
{
THREAD_CONTEXT *pctx = GetCurrentThreadContext();
NODE_INFO& rni = pctx->pCurrentCall ? pctx->pCurrentCall->niDirect :
pctx->niThis;
*pVal = GetNameFromIPAddress(rni.ip);
return S_OK;
}
STDMETHODIMP CallInfo::get_PseudoCausalityID(GUID *pguid)
{
THREAD_CONTEXT *pctx = GetCurrentThreadContext();
if (pctx->pCurrentCall)
*pguid = pctx->pCurrentCall->cid;
else
*pguid = GUID_NULL;
return S_OK;
}
STDMETHODIMP CallInfo::get_TargetProcessID(long *pVal)
{
THREAD_CONTEXT *pctx = GetCurrentThreadContext();
NODE_INFO& rni = pctx->niTarget;
*pVal = rni.pid;
return S_OK;
}
STDMETHODIMP CallInfo::get_TargetThreadID(long *pVal)
{
THREAD_CONTEXT *pctx = GetCurrentThreadContext();
NODE_INFO& rni = pctx->niTarget;
*pVal = rni.tid;
return S_OK;
}
STDMETHODIMP CallInfo::get_TargetHostID(long *pVal)
{
THREAD_CONTEXT *pctx = GetCurrentThreadContext();
NODE_INFO& rni = pctx->niTarget;
*pVal = rni.ip;
return S_OK;
}
STDMETHODIMP CallInfo::get_TargetHostName(BSTR *pVal)
{
THREAD_CONTEXT *pctx = GetCurrentThreadContext();
NODE_INFO& rni = pctx->niTarget;
*pVal = GetNameFromIPAddress(rni.ip);
return S_OK;
}