code_device/twain/dsm/hook.cpp

855 lines
26 KiB
C++

/*
* [Overview]
* The contents of this module allow the new TWAINDSM.DLL (tied to the 2.x version
* of the TWAIN Specifiction) to hook functions that might try to access the 1.x
* TWAIN_32.DLL. We need this because the DG_CONTROL/DAT_NULL/MSG_* triplets are
* sent from the data source (driver) to the application through the data source
* manager (DSM). The technique used was for a driver to issue a LoadLibrary or
* a GetModuleHandle, which would result in it attaching to the same TWAIN_32.DLL
* first loaded by the application. We have to force the driver to attach to the
* new TWAINDSM.DLL instead.
*
*
* [Diagram]
* This is the problem we have...
* +-----+ +--------------+ +--------------+
* | app | <-------> | TWAINDSM.DLL | --------> | |
* +-----+ +--------------+ | |
* | driver (1.x) |
* +--------------+ | |
* error <----- | TWAIN_32.DLL | <-------- | |
* +--------------+ +--------------+
*
* By hooking the appropriate function, we change the driver's request to get
* the DSM_Entry function from TWAIN_32.DLL into the DSM_Entry function from
* DSM_ TWAINDSM.DLL
* +-----+ +--------------+ +--------------+
* | app | <-------> | TWAINDSM.DLL | <-------> | |
* +-----+ +--------------+ | |
* | driver (1.x) |
* +--------------+ | |
* | TWAIN_32.DLL | | |
* +--------------+ +--------------+
*
* Note that TWAIN_32.DLL is still in the picture. This is intentional,
* because we're (indirectly) hooking GetProcAddress. The driver still
* does a LoadLibrary or GetModuleHandle on TWAIN_32.DLL. This design
* was selected because it results in the smallest possible hook code,
* and makes the smallest possible change to the 1.x driver. Everything
* runs the same as a TWAIN_32.DLL session, we just return a pointer to
* a different DSM_Entry function.
*
*
* [Hooked Functions]
* This is the ntdll.dll function we're hooking from kernel32.dll...
* LdrGetProcedureAddress (or LdrGetProcedureAddressForCaller)
* This gives us full coverage for the following function...
* GetProcAddress
*
*
* [Solution]
* The hook code is derived from the BugSlayer HookImportedFunctionByName function
* described in the August 1998 MSJ. The code has been considerably simplified to
* meet the limited needs of the DSM.
*
*
* [Risks]
* 32-bit Windows applications using TWAINDSM.DLL will never be able to access
* the TWAIN_32.DLL DSM_Entry function. Since this is by design, it's not so
* much a risk as "the plan", but it still deserves to be mentioned.
*
* We're not fully loading the TWAIN_32.DLL, because we don't have control over
* it, and we can't be sure what it's doing in DllMain. This could be a problem
* with a very badly behaved application. The scenerio is so convoluted, though,
* that it seems a good risk to keep TWAIN_32.DLL as uninvolved as possible.
*
* The application will crash if it does a FreeLibrary() on the DSM without first
* doing all the necessary MSG_CLOSEDS and MSG_CLOSEDSM calls. This can be
* mitigated by hooking LdrUnloadDll() and watching for an attempt to unload the
* DSM. But this adds complexity that rewards extremely bad coding behavior, so
* it's not going to be added unless we have a lot of bad actors we have to deal
* with.
*
* Hooks should be avoided whenever possible. Therefore the DSM code only installs
* the hook if the application attempts to DG_CONTROL/DAT_IDENTITY/MSG_OPENDS a 1.x
* driver on a 32-bit Windows system.
*
* This system is only designed to work with Window 2000 and higher, there is no
* intention of supporting either Windows NT or any of the Windows 9x platforms.
* Nor is there a reason to, since they don't have a file protection scheme (save for
* WinME, but hopefully nobody is still using that)...
*/
#include "dsm.h"
/**
* This entire file is only used for 32-bit Windows systems, so there is no need to
* compile it for anything else...
*/
#if TWNDSM_OS_64BIT
#pragma message( "hook code disabled for 64-bit builds..." )
#elif TWNDSM_OS_32BIT
#pragma message( "hook code enabled for 32-bit builds..." )
/**
* We use this to build pointers from the various data structures we
* have to navigate to set up the hooks...
*/
#define MakePtr(cast,ptr,AddValue) (cast)((DWORD_PTR)(ptr)+(DWORD_PTR)(AddValue))
/**
* Things we do with our Hook function...
*/
enum EHOOK
{
HOOK_ATTACH = 0,
HOOK_DETACH = 1
};
/**
* Need this for our Ldr functions...
*/
typedef struct _ANSI_STRING {
USHORT Length;
USHORT MaximumLength;
PSTR Buffer;
} ANSI_STRING, *PANSI_STRING;
/**
* typedefs of our hooked functions, so we can cast them nice when we make
* our calls...
*/
typedef NTSYSAPI DWORD (NTAPI *LdrGetProcedureAddress_t)
(
__in HMODULE ModuleHandle,
__in_opt PANSI_STRING FunctionName,
__in_opt WORD Oridinal,
__out PVOID *FunctionAddress
);
typedef NTSYSAPI DWORD (NTAPI *LdrGetProcedureAddressForCaller_t)
(
__in HMODULE ModuleHandle,
__in_opt PANSI_STRING FunctionName,
__in_opt WORD Oridinal,
__out PVOID *FunctionAddress,
__in BOOL bValue,
__in PVOID *CallbackAddress
);
/**
* Forward declarations for our functions, so we can build our m_proc table...
*/
DWORD NTAPI LocalLdrGetProcedureAddress
(
__in HMODULE ModuleHandle,
__in_opt PANSI_STRING FunctionName,
__in_opt WORD Oridinal,
__out PVOID *FunctionAddress
);
DWORD NTAPI LocalLdrGetProcedureAddressForCaller
(
__in HMODULE ModuleHandle,
__in_opt PANSI_STRING FunctionName,
__in_opt WORD Oridinal,
__out PVOID *FunctionAddress,
__in BOOL bValue,
__in PVOID *CallbackAddress
);
/**
* The entry point we want to return to the caller, instead of the one they
* thought they were getting from TWAIN_32.DLL...
*/
extern DSMENTRY DSM_Entry
(
TW_IDENTITY *_pOrigin,
TW_IDENTITY *_pDest,
TW_UINT32 _DG,
TW_UINT16 _DAT,
TW_UINT16 _MSG,
TW_MEMREF _pData
);
/**
* The hook class...
*/
class CTwHook
{
public:
// Initialize our piece-of-data...
CTwHook()
{
memset(&pod,0,sizeof(pod));
}
// The destructor cleans us all up (but Hook does the real work)...
~CTwHook()
{
Hook(HOOK_DETACH);
memset(&pod,0,sizeof(pod));
}
// The workhorse, this is where the hooking fun really takes place...
bool Hook
(
EHOOK _ehook
);
// Determine if this DS ID has been hooked
bool DSID_Is_Hooked
(
TW_UINT32 DSID
);
// Add this DS ID as being hooked
void Hook_Add_DSID
(
TW_UINT32 DSID
);
// Remove this DS ID as being hooked
bool Hook_Remove_DSID
(
TW_UINT32 DSID
);
private:
// We use a pod so that we don't have to worry about initialization
// issues...
struct Pod
{
PROC pOriginal; // The original procedure we found
TW_UINT32 HookedDSs[MAX_NUM_DS];
} pod;
};
/**
* This is where we're keeping our stuff, it has global scope in this module so we can
* use it in our hook functions. Even though this is static, every attempt has been
* made to make it thread safe. We also have a reference counter, since the application
* may choose to load more than one 1.x driver...
*/
static int s_iHookCount = 0;
static CTwHook *s_ptwhook = (CTwHook*)NULL;
/**
* We start by loading these functions using GetProcAddress, since they are
* wacky NTDLL things that are undocumented. Later on we change them to the
* actual pointers to the Ldr functions, and this is what allows us to be
* used safely, no matter what the state of the CTwHook object is in...
*/
static LdrGetProcedureAddress_t OriginalLdrGetProcedureAddress = 0;
static LdrGetProcedureAddressForCaller_t OriginalLdrGetProcedureAddressForCaller = 0;
/**
* A static value for the TWAIN_32.DLL we load, which provides us a very
* simple way to see if someone is working with this DLL...
*/
static HMODULE s_hmoduleTWAIN32 = 0;
/**
* A static value for the DSMEntry of the TWAIN_32.DLL we load,
* which provides us a way to call the original TWAIN_32 if we
* did not want to hook this data souce.
*/
static DSMENTRYPROC TWAIN32_DSMEntry = 0;
/**
* The entry point we want to return to the caller, instead of the one they
* thought they were getting from TWAIN_32.DLL...
*/
DSMENTRY DSM_HookedEntry
(
TW_IDENTITY *_pOrigin,
TW_IDENTITY *_pDest,
TW_UINT32 _DG,
TW_UINT16 _DAT,
TW_UINT16 _MSG,
TW_MEMREF _pData
)
{
if ( ((_DAT == DAT_NULL)
|| (_DAT == DAT_CALLBACK && _MSG == MSG_INVOKE_CALLBACK))
&& _pOrigin
&& s_ptwhook
&& s_ptwhook->DSID_Is_Hooked(_pOrigin->Id))
{
return (DSM_Entry(_pOrigin,_pDest,_DG,_DAT,_MSG,_pData));
}
return (TWAIN32_DSMEntry(_pOrigin,_pDest,_DG,_DAT,_MSG,_pData));
}
/**
* Derived from John Robbins' BugSlayer code in MSJ (so the 'I' in the comments in this
* function refers to John), this function hooks or unhooks the stuff we want to examine.
* Note that this code is optimized to hook the Ldr functions in ntdll.dll. If you want
* to hook something else, especially if it's in a different DLL, you're going to have
* some work to do...
* @param[in] EHOOK _ehook we're hooking or unhooking
* @return true or false
*/
bool CTwHook::Hook
(
EHOOK _ehook
)
{
UINT uResult;
BOOL boolResult;
PIMAGE_IMPORT_DESCRIPTOR pImportDesc;
PIMAGE_THUNK_DATA pOrigThunk;
PIMAGE_THUNK_DATA pRealThunk;
PIMAGE_IMPORT_BY_NAME pByName;
bool bDoHook;
MEMORY_BASIC_INFORMATION mbi_thunk;
DWORD_PTR *pTemp;
DWORD dwOldProtect;
PIMAGE_DOS_HEADER pDOSHeader;
PIMAGE_NT_HEADERS pNTHeader;
PSTR szCurrMod;
HMODULE hmodule;
char szTwain32[MAX_PATH];
const char *szFunctionName;
// Initialize stuff when we're doing an attach...
if (_ehook == HOOK_ATTACH)
{
// If we don't find TWAIN_32.DLL, then we're not going to
// make life any better by doing the hooks, so bail. Also,
// We're not going to allow any flavor of GetProcAddress
// to access this library, which is why we're using the
// extra flag, to keep TWAIN_32.DLL from loading anything
// other than itself.
//
// BUG ALERT
// ~~~~~~~~~
// There is a potential bug here, but one that depends
// on really bad behavior. If the application loads
// TWAINDSM.DLL, then loads TWAIN_32.DLL, then unloads
// TWAINDSM.DLL, then does a GetProcAddress for DSM_Entry
// with TWAIN_32.DLL, it's going to go ka-boom. If this
// is a problem, then go back to using ::LoadLibrary()...
memset(szTwain32,0,sizeof(szTwain32));
uResult = ::GetWindowsDirectory(szTwain32,sizeof(szTwain32)-1);
if (!uResult)
{
return(false);
}
SSTRCAT(szTwain32,sizeof(szTwain32)-1,"\\TWAIN_32.DLL");
s_hmoduleTWAIN32 = ::LoadLibraryEx(szTwain32,NULL,DONT_RESOLVE_DLL_REFERENCES);
if (0 == s_hmoduleTWAIN32)
{
return(false);
}
// Load the undocumented routine, we're going for both, if we find
// LdrGetProcedureAddressForCaller, then we'll prefer it over LdrGetProcedureAddress...
szFunctionName = "";
OriginalLdrGetProcedureAddress = 0;
OriginalLdrGetProcedureAddressForCaller = 0;
hmodule = GetModuleHandle("ntdll.dll");
if (hmodule)
{
szFunctionName = "LdrGetProcedureAddressForCaller";
OriginalLdrGetProcedureAddressForCaller = (LdrGetProcedureAddressForCaller_t)GetProcAddress(hmodule,szFunctionName);
if (!OriginalLdrGetProcedureAddressForCaller)
{
szFunctionName = "LdrGetProcedureAddress";
OriginalLdrGetProcedureAddress = (LdrGetProcedureAddress_t)GetProcAddress(hmodule,szFunctionName);
}
}
}
// If we're detaching, then make sure our reference to TWAIN_32.DLL is gone...
else
{
if (s_hmoduleTWAIN32)
{
::FreeLibrary(s_hmoduleTWAIN32);
s_hmoduleTWAIN32 = 0;
}
// If we don't have an old pointer, then we're done...
if (NULL == pod.pOriginal)
{
return (true);
}
// Pick the name...
if (OriginalLdrGetProcedureAddressForCaller)
{
szFunctionName = "LdrGetProcedureAddressForCaller";
}
else
{
szFunctionName = "LdrGetProcedureAddress";
}
}
// This is where we'll be hooking into Ldr functions, the calls
// themselves are in ntdll.dll. Kernel32 calls them, and that's
// where we can take advantage of the DLL indirection to do this
// spiffy DLL injection thingy...
// Starting with Windows7 the hooking has moved from kernel32.dll
// to a new library kernelbase.dll. Atempt kernelbase first
// if it fails then we are not Windows7 and try kernel32
hmodule = GetModuleHandle("kernelbase.dll");
if ( NULL == hmodule )
{
hmodule = GetModuleHandle("kernel32.dll");
}
// Get the DOS header...
pDOSHeader = (PIMAGE_DOS_HEADER)hmodule;
// Is this the MZ header?
if ( !pDOSHeader
|| (TRUE == IsBadReadPtr(pDOSHeader,sizeof(IMAGE_DOS_HEADER)))
|| (IMAGE_DOS_SIGNATURE != pDOSHeader->e_magic))
{
return (false);
}
// Get the PE header.
pNTHeader = MakePtr(PIMAGE_NT_HEADERS,pDOSHeader,pDOSHeader->e_lfanew);
// Is this a real PE image?
if ( (TRUE == IsBadReadPtr(pNTHeader,sizeof(IMAGE_NT_HEADERS)))
|| (IMAGE_NT_SIGNATURE != pNTHeader->Signature))
{
return (false);
}
// If there is no imports section, leave now.
if (0 == pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress)
{
return (false);
}
// Get the pointer to the imports section.
pImportDesc = MakePtr(PIMAGE_IMPORT_DESCRIPTOR,pDOSHeader,pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
// Loop through the import module descriptors looking for the
// ntdll.dll module, which is where the Ldr functions live...
while (NULL != pImportDesc->Name)
{
szCurrMod = MakePtr(PSTR,pDOSHeader,pImportDesc->Name);
if (0 == _stricmp(szCurrMod,"ntdll.dll"))
{
// Found it.
break;
}
// Look at the next one.
pImportDesc++ ;
}
// If the name is NULL, then the module is not imported.
if (NULL == pImportDesc->Name)
{
return (false);
}
// Get the original thunk information for this DLL. I can't use
// the thunk information stored in pImportDesc->FirstThunk
// because the loader has already changed that array to fix up
// all the imports. The original thunk gives me access to the
// function names.
pOrigThunk = MakePtr(PIMAGE_THUNK_DATA,hmodule,pImportDesc->OriginalFirstThunk);
// Get the array the pImportDesc->FirstThunk points to because
// I'll do the actual bashing and hooking there.
pRealThunk = MakePtr(PIMAGE_THUNK_DATA,hmodule,pImportDesc->FirstThunk);
// Determines whether I hook the function
bDoHook = false;
// Loop through and find the function to hook.
while (NULL != pOrigThunk->u1.Function)
{
// Look only at functions that are imported by name, not those
// that are imported by ordinal value.
if (IMAGE_ORDINAL_FLAG != (pOrigThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG))
{
// Look at the name of this imported function.
pByName = MakePtr(PIMAGE_IMPORT_BY_NAME,hmodule,pOrigThunk->u1.AddressOfData);
// We found it, so scoot...
if ( pByName->Name
&& ('\0' != pByName->Name[0])
&& (0 == _stricmp(szFunctionName,(char*)pByName->Name)))
{
bDoHook = true;
break;
}
// Increment both tables, and continue...
pOrigThunk++;
pRealThunk++;
}
}
// If we found something, then hook or unhook it, as appropriate...
if (true == bDoHook)
{
// I found a function to hook. Now I need to change
// the memory protection to writable before I overwrite
// the function pointer. Note that I'm now writing into
// the real thunk area!
VirtualQuery(pRealThunk,&mbi_thunk,sizeof(MEMORY_BASIC_INFORMATION));
if (FALSE == VirtualProtect(mbi_thunk.BaseAddress,mbi_thunk.RegionSize,PAGE_EXECUTE_READWRITE,&mbi_thunk.Protect))
{
return(false);
}
// Save the original address, if we're hooking...
if (_ehook == HOOK_ATTACH)
{
// This cast should make the compiler happy about the change in the word size...
pod.pOriginal = (PROC)(INT_PTR)pRealThunk->u1.Function;
if (OriginalLdrGetProcedureAddressForCaller)
{
OriginalLdrGetProcedureAddressForCaller = (LdrGetProcedureAddressForCaller_t)pod.pOriginal;
}
else
{
OriginalLdrGetProcedureAddress = (LdrGetProcedureAddress_t)pod.pOriginal;
}
}
// Microsoft has two different definitions of the
// PIMAGE_THUNK_DATA fields as they are moving to
// support Win64. The W2K RC2 Platform SDK is the
// latest header, so I'll use that one and force the
// Visual C++ 6 Service Pack 3 headers to deal with it.
// Hook the new function if we're hooking, or the original function
// if we're closing...
if (_ehook == HOOK_ATTACH)
{
pTemp = (DWORD_PTR*)&pRealThunk->u1.Function;
if (OriginalLdrGetProcedureAddressForCaller)
{
*pTemp = (DWORD_PTR)LocalLdrGetProcedureAddressForCaller;
}
else
{
*pTemp = (DWORD_PTR)LocalLdrGetProcedureAddress;
}
}
else
{
pTemp = (DWORD_PTR*)&pRealThunk->u1.Function;
*pTemp = (DWORD_PTR)(pod.pOriginal);
}
// Change the protection back to what it was before I
// overwrote the function pointer.
boolResult = VirtualProtect(mbi_thunk.BaseAddress,mbi_thunk.RegionSize,mbi_thunk.Protect,&dwOldProtect);
if (boolResult == FALSE)
{
// Okay, this isn't good, but we're not going to do
// anything about it, because presumably this isn't
// the end of the world...
}
}
// All done...
return(true);
}
/**
* Determine if the DS has been hooked by checking the ID
* @param[in] DSID The DS ID to check to see if it has been hooked
* @return true or false
*/
bool CTwHook::DSID_Is_Hooked(TW_UINT32 DSID)
{
int count = min(MAX_NUM_DS,s_iHookCount);
for (int i=0; i<count; i++)
{
if (pod.HookedDSs[i] == DSID)
{
return (true);
}
}
return (false);
}
/**
* Add this DS to the list of being hooked
* @param[in] DSID The DS ID to add to the list
* @note We allow an application to open the DSM several times and each
* application to open several DSs. (The application must use a different
* name each time it loads the DSM). It is possible for a DS to be opened
* several times by different applications. (Although most DSs only support
* one connection.) Therefore we need to allow multiple instances of the
* same DSID. No duplicate check needed.
*/
void CTwHook::Hook_Add_DSID(TW_UINT32 DSID)
{
pod.HookedDSs[s_iHookCount] = DSID;
s_iHookCount++;
}
/**
* Remove this DS from the list of being hooked
* @param[in] DSID The DS ID to remove from the list
* @return true or false
*/
bool CTwHook::Hook_Remove_DSID(TW_UINT32 DSID)
{
int count = min(MAX_NUM_DS, s_iHookCount);
for(int i=0; i<count; i++)
{
if(pod.HookedDSs[i] == DSID)
{
while(i+1<count)
{
pod.HookedDSs[i] = pod.HookedDSs[i+1];
}
pod.HookedDSs[i] = 0;
s_iHookCount--;
return true;
}
}
// Should never get here. It means we are trying to
// remove a DS that was never hooked.
kLOG((kLOGERR,"Trying to removing a hook for a DSID (%d) that was never added.", DSID ));
return false;
}
/**
* The undocumented LdrGetProcedureAddress...
* @param[in] PHMODULE ModuleHandle we're using to get a function pointer
* @param[in_opt] PANSI_STRING FunctionName of thing we're trying to find
* @param[in_opt] WORD Ordinal, or the number of the things we're trying to find
* @param[out] PVOID *FunctionAddress being sent back to the caller
* @return DWORD
*/
DWORD NTAPI LocalLdrGetProcedureAddress
(
__in HMODULE ModuleHandle,
__in_opt PANSI_STRING FunctionName,
__in_opt WORD Ordinal,
__out PVOID *FunctionAddress
)
{
// See if the caller is asking for TWAIN_32.DLL, and if so, then dive in
// and return our DSM_Entry. This works because attempts to load the
// same DLL more than once just bump up the reference count. Or put
// another way, if function A does a LoadLibrary("xyz") and then function
// B does a LoadLibrary("xzy") the HMODULE values returned will be found
// to be the same.
//
// We don't have to check the FunctionName or the Ordinal, there's only
// one possible return from TWAIN_32.DLL (thank you initial designers),
// and I haven't throught of a good security reason why I need to bother
// checking...
if (ModuleHandle == s_hmoduleTWAIN32)
{
// Get and store the original address in case we need it
(OriginalLdrGetProcedureAddress(ModuleHandle,FunctionName,Ordinal,(PVOID*)&TWAIN32_DSMEntry));
// Return the address to our own function
*FunctionAddress = ::DSM_HookedEntry;
return (ERROR_SUCCESS);
}
// Otherwise let the call continue unmolested...
return (OriginalLdrGetProcedureAddress(ModuleHandle,FunctionName,Ordinal,FunctionAddress));
}
/**
* The undocumented LdrGetProcedureAddressForCaller...
* @param[in] PHMODULE ModuleHandle we're using to get a function pointer
* @param[in_opt] PANSI_STRING FunctionName of thing we're trying to find
* @param[in_opt] WORD Ordinal, or the number of the things we're trying to find
* @param[out] PVOID *FunctionAddress being sent back to the caller
* @param[in] BOOL bValue
* @param[in] PVOID *CallbackAddress
* @return DWORD
*/
DWORD NTAPI LocalLdrGetProcedureAddressForCaller
(
__in HMODULE ModuleHandle,
__in_opt PANSI_STRING FunctionName,
__in_opt WORD Ordinal,
__out PVOID *FunctionAddress,
__in BOOL bValue,
__in PVOID *CallbackAddress
)
{
// See if the caller is asking for TWAIN_32.DLL, and if so, then dive in
// and return our DSM_Entry. This works because attempts to load the
// same DLL more than once just bump up the reference count. Or put
// another way, if function A does a LoadLibrary("xyz") and then function
// B does a LoadLibrary("xzy") the HMODULE values returned will be found
// to be the same.
//
// We don't have to check the FunctionName or the Ordinal, there's only
// one possible return from TWAIN_32.DLL (thank you initial designers),
// and I haven't throught of a good security reason why I need to bother
// checking...
if (ModuleHandle == s_hmoduleTWAIN32)
{
// Get and store the original address in case we need it
(OriginalLdrGetProcedureAddressForCaller(ModuleHandle,FunctionName,Ordinal,(PVOID*)&TWAIN32_DSMEntry,bValue,CallbackAddress));
// Return the address to our own function
*FunctionAddress = ::DSM_HookedEntry;
return (ERROR_SUCCESS);
}
// Otherwise let the call continue unmolested...
return (OriginalLdrGetProcedureAddressForCaller(ModuleHandle,FunctionName,Ordinal,FunctionAddress,bValue,CallbackAddress));
}
/**
* Install the hooks and load the library, if hooks are already installed,
* then just load the library...
*/
HMODULE InstallTwain32DllHooks
(
const char* const _lib,
const bool _hook,
const TW_UINT32 _DSID
)
{
HMODULE hmodule;
CTwHook *ptwhook;
DWORD dwResult;
// Init stuff...
ptwhook = (CTwHook*)NULL;
// We hook before we load the library so that we can intercept calls
// to the Ldr functions during DllMain. But we only do the hook if
// it's the first time we've been here, and we've been asked to do
// hooking (which we won't be if the DSM is doing GetFirst/GetNext to
// enumerate the drivers)...
if (_hook)
{
// If we already have a hook in place, then bump up our
// reference counter...
if ( (s_iHookCount > 0)
&& ((CTwHook*)NULL != s_ptwhook))
{
s_ptwhook->Hook_Add_DSID(_DSID);
}
// Otherwise load the beastie...
else
{
// Allocate our object...
s_iHookCount = 0;
ptwhook = new CTwHook();
if (ptwhook)
{
// Do the hook...
if (ptwhook->Hook(HOOK_ATTACH))
{
// This activates our hooking functions to look for
// attempts to get DSM_Entry...
s_ptwhook = ptwhook;
s_ptwhook->Hook_Add_DSID(_DSID);
}
// No joy, cleanup...
else
{
delete ptwhook;
ptwhook = (CTwHook*)NULL;
s_ptwhook = (CTwHook*)NULL;
}
}
}
}
// Load the library the caller asked for...
hmodule = ::LoadLibrary(_lib);
// If we hooked for this module, and the LoadLibrary failed, then
// undo the hook...
if ( (NULL == hmodule)
&& ((CTwHook*)NULL != ptwhook)
&& _hook)
{
dwResult = ::GetLastError();
s_ptwhook->Hook_Remove_DSID(_DSID);
if (s_iHookCount <= 0)
{
s_ptwhook = (CTwHook*)NULL;
delete ptwhook;
s_iHookCount = 0;
}
::SetLastError(dwResult);
}
// All done...
return(hmodule);
}
/**
* Uninstall the hooks (if needed), and free the library, if hooks are not currently
* installed, then just free the library...
*/
BOOL UninstallTwain32DllHooks
(
const HMODULE _hmodule,
const bool _unhook,
const TW_UINT32 _DSID
)
{
if(_unhook)
{
if (s_ptwhook)
{
// Remove the DS from the list and decrement the count...
s_ptwhook->Hook_Remove_DSID(_DSID);
// If we're at zero, then cleanup. I'm probably being a bit
// paranoid about the cleanup scheme, but I like being paranoid
// when it comes to hooks...
if (s_iHookCount <= 0)
{
CTwHook *ptwhook = s_ptwhook;
s_ptwhook = (CTwHook*)NULL;
delete ptwhook;
s_iHookCount = 0;
}
}
}
// Free the library...
return(::FreeLibrary(_hmodule));
}
// TWDSM_OS_64BIT/TWNDSM_OS_32BIT
#else
#error error, we need to be either 32-bit or 64-bit...
#endif