Type | Function |
Thread-related functions | CreateThread |
ExitThread | |
SuspendThread | |
ResumeThread | |
TerminateThread | |
Critical Section functions | InitializeCriticalSection |
InitializeCriticalSectionAndSpinCount | DeleteCriticalSection | EnterCriticalSection | LeaveCriticalSection | SetCriticalSectionSpinCount | TryEnterCriticalSection |
Mutex functions | CreateMutexA |
CreateMutexW | |
OpenMutexA | OpenMutexW | ReleaseMutex |
Semaphore functions | CreateSemaphoreA |
CreateSemaphoreW | |
OpenSemaphoreA | |
OpenSemaphoreW | |
ReleaseSemaphore | |
Event functions | CreateEventA |
CreateEventW | |
OpenEventA | |
OpenEventW | |
PulseEvent | |
ResetEvent | |
SetEvent | |
Blocking functions | WaitForSingleObject |
WaitForSingleObjectEx | |
WaitForMultipleObjects | |
WaitForMultipleObjectsEx | |
MsgWaitForMultipleObjects | |
MsgWaitForMultipleObjectsEx | |
SignalObjectAndWait | |
Special functions | CloseHandle |
ExitProcess | |
GetProcAddress |
Figure 2 DeadlockDetection Option Flags
Flag | Limits To |
DDOPT_THREADS | Thread-related functions |
DDOPT_CRITSEC | Critical section functions |
DDOPT_MUTEX | Mutex functions |
DDOPT_SEMAPHORE | Semaphore functions |
DDOPT_EVENT | Event functions |
DDOPT_ALL | All hooked functions |
Thread ID | Return Address | C/R | Return Value | Function and Parameters |
0x000000E1 | [0x00401071] | R | 0x00000000 | InitializeCriticalSection 0x00404B18 |
0x000000E1 | [0x0040107C] | R | EnterCriticalSection 0x00404B18 | |
0x000000E1 | [0x0040107C] | R | 0x00000000 | EnterCriticalSection 0x00404B18 |
0x000000E1 | [0x00401087] | R | 0x00000000 | LeaveCriticalSection 0x00404B18 |
0x000000E1 | [0x00401092] | R | 0x00000000 | DeleteCriticalSection 0x00404B18 |
0x000000E1 | [0x004010C6] | R | 0x00000001 | InitializeCriticalSectionAndSpinCount 0x00404B30, 400 |
0x000000E1 | [0x004010FA] | R | 0x00000000 | SetCriticalSectionSpinCount 0x00404B30, 2048 |
0x000000E1 | [0x00401129] | R | TryEnterCriticalSection 0x00404B30 | |
0x000000E1 | [0x00401129] | R | 0x00000001 | TryEnterCriticalSection 0x00404B30 |
0x000000E1 | [0x00401144] | R | 0x00000028 | CreateEventA 0x00000000, 1, 0, 0x0040449C [Manual rest event] |
0x000000E1 | [0x00401151] | R | 0x00000001 | SetEvent 0x00000028 |
0x000000E1 | [0x0040115B] | R | 0x00000001 | ResetEvent 0x00000028 |
0x000000E1 | [0x0040116D] | R | 0x0000002C | OpenEventA 0x001F0003, 0, 0x004044B0 [Manual rest event] |
0x000000E1 | [0x00401181] | R | 0x00000030 | CreateEventW 0x00000000, 1, 0, 0x004044C4 [Wide Char event] |
/*----------------------------------------------------------------------
John Robbins - Oct '98 Microsoft Systems Journal Bugslayer Column
------------------------------------------------------------------------
The prototypes for all the hook functions and the prolog/epilog code.
----------------------------------------------------------------------*/
#ifndef _DD_FUNCS_H
#define _DD_FUNCS_H
/*//////////////////////////////////////////////////////////////////////
All of the hook functions are __declspec(naked) functions, therefore
I must provide the prolog and epilog. The reasons for providing custom
prolog and epilog are:
1. Functions written in C have no control over what registers are used
and when the compiler saves the original registers off. This means
that It is nearly impossible to get the return address. For the
DeadlockDetection project, the return address is very important.
2. I also wanted to hand the parameters to the extension DLL processing
function without having to do massive amounts of data copying on
each function call.
3. Since almost all of the hook functions do the same things, I set up
the common variables needed in all functions.
4. Hook functions cannot change any of the returned values, including
the value from GetLastError. By doing my own prolog and epilog, I
can make it much easier to handle returning the correct value. Also,
I need to restore the register values that normally would be there
after the real function call.
Your basic hook function looks like the following:
HANDLE NAKEDDEF DD_OpenEventA ( DWORD dwDesiredAccess ,
BOOL bInheritHandle ,
LPCSTR lpName )
{
// This must be declared at the top of the function. If the
// function has any local variables, declare them first.
HOOKFN_PROLOG ( ) ;
// Determine if this function class is being logged.
if ( TRUE == DoLogging ( DDOPT_EVENT ) )
{
// Call this macro to fill the DDEVENTINFO, stEvtInfo, that is
// part of each hook function. It comes from the HOOKFN_PROLOG
// macro.
FILL_EVENTINFO ( eOpenEventA ) ;
// This macro *MUST* be called before calling the real function
// or ESI and EDI will not be the same that the user set.
REAL_FUNC_PRE_CALL ( ) ;
// The real function call. The return value handling is part of
// the REAL_FUNC_POST_CALL processing so there is no need to
// manually set it.
OpenEventA ( dwDesiredAccess ,
bInheritHandle ,
lpName ) ;
// This macro *MUST* be called AFTER calling the real function.
// This saves off the register values and the last error value.
REAL_FUNC_POST_CALL ( ) ;
// Call this function to log the event.
ProcessEvent ( &stEvtInfo ) ;
}
else
{
// See the comments above. This is just the case where the
// function is not being logged.
REAL_FUNC_PRE_CALL ( ) ;
OpenEventA ( dwDesiredAccess ,
bInheritHandle ,
lpName ) ;
REAL_FUNC_POST_CALL ( ) ;
}
// This must be the last thing in the function. The parameter is
// a count of the number of functions so the stack gets cleaned up
// correctly. This takes care of setting all the registers to the
// same values that the real function returned.
HOOKFN_EPILOG ( 3 ) ;
}
//////////////////////////////////////////////////////////////////////*/
/*//////////////////////////////////////////////////////////////////////
The register state structure. I use this to ensure that ALL
registers are returned exactly as they came from the real function.
Note that EBP and ESP are handled as part of the prolog.
//////////////////////////////////////////////////////////////////////*/
typedef struct tag_REGSTATE
{
DWORD dwEAX ;
DWORD dwEBX ;
DWORD dwECX ;
DWORD dwEDX ;
DWORD dwEDI ;
DWORD dwESI ;
DWORD dwEFL ;
} REGSTATE , * PREGSTATE ;
/*//////////////////////////////////////////////////////////////////////
The common prolog code for all DD_* functions.
//////////////////////////////////////////////////////////////////////*/
#define HOOKFN_PROLOG() \
DDEVENTINFO stEvtInfo ; /* The event info for the function. */\
DWORD dwLastError ; /* The last error value. */\
REGSTATE stRegState ; /* The register state to ensure I */\
/* restore everything correctly. */\
{ \
__asm PUSH EBP /* Always save EBP explicitly */\
__asm MOV EBP , ESP /* Move the stack */\
__asm MOV EAX , ESP /* Get the stack ptr to calculate the*/\
/* return address and parameters. */\
__asm SUB ESP , __LOCAL_SIZE /* Save off space for the local vars.*/\
__asm ADD EAX , 04h + 04h /* Account for PUSH EBP, and the */\
/* return address. */\
/* Save start of params on the stack.*/\
__asm MOV [stEvtInfo.dwParams] , EAX \
__asm SUB EAX , 04h /* Get back to return addr. */\
__asm MOV EAX , [EAX] /* EAX now holds return address. */\
/* Save off the return address. */\
__asm MOV [stEvtInfo.dwAddr] , EAX \
__asm MOV dwLastError , 0 /* Initialize dwLastError. */\
/* Initialize the event info. */\
__asm MOV [stEvtInfo.eFunc] , eUNINITIALIZEDFE \
__asm MOV [stRegState.dwEDI] , EDI /* Save the two regs that need */\
__asm MOV [stRegState.dwESI] , ESI /* to be saved across func */\
/* calls. */\
}
/*//////////////////////////////////////////////////////////////////////
The common epilog code for all DD_* functions. iNumParams is the
number of parameters to the function. This is so the stack can be
restored for stdcall functions.
//////////////////////////////////////////////////////////////////////*/
#define HOOKFN_EPILOG(iNumParams) \
{ \
SetLastError ( dwLastError ) ; /* Set the real fn's last err val*/\
__asm ADD ESP , __LOCAL_SIZE /* Add back on the local vars. */\
__asm MOV EBX , [stRegState.dwEBX] /* Restore all the regs so */\
__asm MOV ECX , [stRegState.dwECX] /* this call looks identical */\
__asm MOV EDX , [stRegState.dwEDX] /* to what the intercepted */\
__asm MOV EDI , [stRegState.dwEDI] /* function would look like. */\
__asm MOV ESI , [stRegState.dwESI] \
__asm MOV EAX , [stRegState.dwEFL] \
__asm SAHF \
__asm MOV EAX , [stRegState.dwEAX] \
__asm MOV ESP , EBP /* Put ESP back. */\
__asm POP EBP /* Restore the saved EBP. */\
__asm RET iNumParams * 4 /* stdcall restore of the stack. */\
}
/*//////////////////////////////////////////////////////////////////////
This macro needs to be place IMMEDIATELY *BEFORE* ANY call to the
real function the hook function is handling. This takes care of setting
EDI and ESI to the original values passed in by the user.
//////////////////////////////////////////////////////////////////////*/
#define REAL_FUNC_PRE_CALL() \
{ \
__asm MOV EDI , [stRegState.dwEDI] /* Restore real EDI. */\
__asm MOV ESI , [stRegState.dwESI] /* Restore real ESI. */\
}
/*//////////////////////////////////////////////////////////////////////
This macro needs to be place IMMEDIATELY *AFTER* ANY call to the
real function the hook function is handling. This macro saves off the
register values on the return so that the function epilog can return
the exact same values as the real function.
//////////////////////////////////////////////////////////////////////*/
#define REAL_FUNC_POST_CALL() \
{ \
__asm MOV [stRegState.dwEAX] , EAX /* Save EAX value. */\
__asm MOV [stRegState.dwEBX] , EBX /* Save EBX value. */\
__asm MOV [stRegState.dwECX] , ECX /* Save ECX value. */\
__asm MOV [stRegState.dwEDX] , EDX /* Save EDX value. */\
__asm MOV [stRegState.dwEDI] , EDI /* Save EDI value. */\
__asm MOV [stRegState.dwESI] , ESI /* Save ESI value. */\
__asm XOR EAX , EAX /* Zero out EAX. */\
__asm LAHF /* Save flags into AH. */\
__asm MOV [stRegState.dwEFL] , EAX /* Save flags value. */\
} \
dwLastError = GetLastError ( ) ; /* Save the last error value.*/\
{ \
__asm MOV EAX , [stRegState.dwEAX] /* Restore EAX to orig value.*/\
/* Set return value for info.*/\
__asm MOV [stEvtInfo.dwRetValue] , EAX \
}
/*//////////////////////////////////////////////////////////////////////
A convenient macro to fills out the event information structure.
//////////////////////////////////////////////////////////////////////*/
#define FILL_EVENTINFO(eFn) \
stEvtInfo.eFunc = eFn ; \
stEvtInfo.ePrePost = ePostCall ; \
stEvtInfo.dwThreadId = GetCurrentThreadId ( )
/*//////////////////////////////////////////////////////////////////////
The declaration for all DD_* definitions.
//////////////////////////////////////////////////////////////////////*/
#define NAKEDDEF __declspec(naked)
/*//////////////////////////////////////////////////////////////////////
BIG NOTE BIG NOTE BIG NOTE BIG NOTE
All of the following prototypes look like cdecl functions. They are
not, they are all stdcall! The custom prolog and epilog ensure that
everything gets straightened out!
//////////////////////////////////////////////////////////////////////*/
////////////////////////////////////////////////////////////////////////
// The mandatory functions that have to be intercepted to make the
// system work.
HMODULE DD_LoadLibraryA ( LPCSTR lpLibFileName ) ;
HMODULE DD_LoadLibraryW ( LPCWSTR lpLibFileName ) ;
HMODULE DD_LoadLibraryExA ( LPCSTR lpLibFileName ,
HANDLE hFile ,
DWORD dwFlags ) ;
HMODULE DD_LoadLibraryExW ( LPCWSTR lpLibFileName ,
HANDLE hFile ,
DWORD dwFlags ) ;
VOID DD_ExitProcess ( UINT uExitCode ) ;
FARPROC DD_GetProcAddress ( HMODULE hModule , LPCSTR lpProcName ) ;
////////////////////////////////////////////////////////////////////////
// The thread specific functions.
HANDLE DD_CreateThread (LPSECURITY_ATTRIBUTES lpThreadAttributes ,
DWORD dwStackSize ,
LPTHREAD_START_ROUTINE lpStartAddress ,
LPVOID lpParameter ,
DWORD dwCreationFlags ,
LPDWORD lpThreadId ) ;
VOID DD_ExitThread ( DWORD dwExitCode ) ;
DWORD DD_SuspendThread ( HANDLE hThread ) ;
DWORD DD_ResumeThread ( HANDLE hThread ) ;
BOOL DD_TerminateThread ( HANDLE hThread , DWORD dwExitCode ) ;
////////////////////////////////////////////////////////////////////////
// Waiting and special functions.
DWORD DD_WaitForSingleObject ( HANDLE hHandle ,
DWORD dwMilliseconds ) ;
DWORD DD_WaitForSingleObjectEx ( HANDLE hHandle ,
DWORD dwMilliseconds ,
BOOL bAlertable ) ;
DWORD DD_WaitForMultipleObjects( DWORD nCount ,
CONST HANDLE * lpHandles ,
BOOL bWaitAll ,
DWORD dwMilliseconds ) ;
DWORD DD_WaitForMultipleObjectsEx( DWORD nCount ,
CONST HANDLE * lpHandles ,
BOOL bWaitAll ,
DWORD dwMilliseconds ,
BOOL bAlertable ) ;
DWORD DD_MsgWaitForMultipleObjects ( DWORD nCount ,
LPHANDLE pHandles ,
BOOL fWaitAll ,
DWORD dwMilliseconds,
DWORD dwWakeMask ) ;
DWORD DD_MsgWaitForMultipleObjectsEx ( DWORD nCount ,
LPHANDLE pHandles ,
DWORD dwMilliseconds ,
DWORD dwWakeMask ,
DWORD dwFlags ) ;
DWORD DD_SignalObjectAndWait ( HANDLE hObjectToSignal ,
HANDLE hObjectToWaitOn ,
DWORD dwMilliseconds ,
BOOL bAlertable ) ;
BOOL DD_CloseHandle ( HANDLE hObject ) ;
////////////////////////////////////////////////////////////////////////
// Critical Section functions.
VOID DD_InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
BOOL DD_InitializeCriticalSectionAndSpinCount (
LPCRITICAL_SECTION lpCriticalSection,
DWORD dwSpinCount );
VOID DD_DeleteCriticalSection(LPCRITICAL_SECTION lpCriticalSection ) ;
VOID DD_EnterCriticalSection ( LPCRITICAL_SECTION lpCriticalSection ) ;
VOID DD_LeaveCriticalSection ( LPCRITICAL_SECTION lpCriticalSection ) ;
DWORD DD_SetCriticalSectionSpinCount (
LPCRITICAL_SECTION lpCriticalSection,
DWORD dwSpinCount );
BOOL DD_TryEnterCriticalSection ( LPCRITICAL_SECTION lpCriticalSection);
////////////////////////////////////////////////////////////////////////
// Mutex functions.
HANDLE DD_CreateMutexA ( LPSECURITY_ATTRIBUTES lpMutexAttributes ,
BOOL bInitialOwner ,
LPCSTR lpName ) ;
HANDLE DD_CreateMutexW ( LPSECURITY_ATTRIBUTES lpMutexAttributes ,
BOOL bInitialOwner ,
LPCWSTR lpName ) ;
HANDLE DD_OpenMutexA ( DWORD dwDesiredAccess ,
BOOL bInheritHandle ,
LPCSTR lpName ) ;
HANDLE DD_OpenMutexW ( DWORD dwDesiredAccess ,
BOOL bInheritHandle ,
LPCWSTR lpName ) ;
BOOL DD_ReleaseMutex ( HANDLE hMutex ) ;
////////////////////////////////////////////////////////////////////////
// Semaphore functions.
HANDLE
DD_CreateSemaphoreA ( LPSECURITY_ATTRIBUTES lpSemaphoreAttributes ,
LONG lInitialCount ,
LONG lMaximumCount ,
LPCSTR lpName );
HANDLE
DD_CreateSemaphoreW ( LPSECURITY_ATTRIBUTES lpSemaphoreAttributes ,
LONG lInitialCount ,
LONG lMaximumCount ,
LPCWSTR lpName );
HANDLE DD_OpenSemaphoreA ( DWORD dwDesiredAccess ,
BOOL bInheritHandle ,
LPCSTR lpName ) ;
HANDLE DD_OpenSemaphoreW ( DWORD dwDesiredAccess ,
BOOL bInheritHandle ,
LPCWSTR lpName ) ;
BOOL DD_ReleaseSemaphore ( HANDLE hSemaphore ,
LONG lReleaseCount ,
LPLONG lpPreviousCount ) ;
////////////////////////////////////////////////////////////////////////
// Event functions.
HANDLE DD_CreateEventA ( LPSECURITY_ATTRIBUTES lpEventAttributes ,
BOOL bManualReset ,
BOOL bInitialState ,
LPCSTR lpName ) ;
HANDLE DD_CreateEventW ( LPSECURITY_ATTRIBUTES lpEventAttributes ,
BOOL bManualReset ,
BOOL bInitialState ,
LPCWSTR lpName ) ;
HANDLE DD_OpenEventA ( DWORD dwDesiredAccess ,
BOOL bInheritHandle ,
LPCSTR lpName ) ;
HANDLE DD_OpenEventW ( DWORD dwDesiredAccess ,
BOOL bInheritHandle ,
LPCWSTR lpName ) ;
BOOL DD_PulseEvent ( HANDLE hEvent ) ;
BOOL DD_ResetEvent ( HANDLE hEvent ) ;
BOOL DD_SetEvent ( HANDLE hEvent ) ;
#endif // _DD_FUNCS_H