IAT(Import Address Table) API's Hooking in C++


API hooking in executing code always be exciting thing for developers, as this will leads you to intercept the call by other process and get the data or change. In Hooking we redirect the existing code flow to our custom code.



There are many techniques to achieve Hooking API. Here ill take example of Hooking Internet Explorer using SetWindowHookEx(). Will be hooking IAT(import address table) for IE8. Using SetWindowHookEx() API will be injecting the custom dll to IE process. In My example ill be hooking
following API's which is responsible for rendering Text on IE window.
       
              DLL NAME        API Hooked
                  --------------      ------------
                  USER32.DLL     MessageBoxW


Ill have 2 projects one Injected (dll which will loaded by injector.exe process to inject the code in IE) and injector (exe). Here is the flow to implement such code

-Expose your function to intercept (lets say dll name is INJ.dll)
-Load the dll ( INJ.dll) which has custom function definition by different process.(lets say P1)
-Get the threadID of intercepting process (here its IE), in Process P1
-Get the address of function from IAT of IE loaded dlls.
-Replace the address of function in IAT with INJ.dll functions.
-Replace back to original address of all function in IAT. 

 Sample code snippet:

///injected.h///
extern "C" {
  INJECTED_API LRESULT CALLBACK fninjected(int code,WPARAM WPARAM,LPARAM lParam);

typedef int (WINAPI *_TempMessageBoxW)(HWND,LPCWSTR,LPCWSTR,UINT);
int WINAPI CustomMessageBox(HWND,LPCWSTR,LPCWSTR,UINT);
BOOL ManipulateAddressPointer(PDWORD pAddress,DWORD);
void Wrapper();
}

///injected.cpp///
_TempMessageBoxW mbOrig;

extern "C"{
INJECTED_API LRESULT CALLBACK fninjected(int code,WPARAM wParam,LPARAM lParam)
{
Wrapper();
return CallNextHookEx(NULL,code,wParam,lParam);
}
}
void Wrapper()
{
     MessageBox(NULL,L"Process is Attached, go ahead for further      debugging",L"Alert!",MB_OK);//Used for Attching Debugger to      IE

   PDWORD OldFunc = GetFunctionAddress(L"USER32.DLL","MessageBoxW");
     
  if(!OldFunc)
  {
    //MessageBox(NULL,L"failed to get the function address",L"Failed",MB_OK);
    return ;
  }  

  DWORD OrigAddress = *OldFunc;
  DWORD NewAddress = (DWORD)&CustomMessageBox;  
  
  mbOrig = (_TempMessageBoxW)*OldFunc; 
  ManipulateAddressPointer(OldFunc,NewAddress);

// Now test the code for any MessageBox call by IE  
MessageBoxW(NULL,L"ok using original MB",L"To check",MB_OK);
  
  ManipulateAddressPointer(OldFunc,OrigAddress);
 //"successfully reverted the original addresss"
}

BOOL ManipulateAddressPointer(PDWORD pAddress,DWORD location){
  BOOL rv=TRUE;
  DWORD oldProtect = 0;
  DWORD Stub = 0;
  BOOL bReplacePossible = TRUE;
  BOOL bAttributeChange = FALSE;

  if(IsBadWritePtr(pAddress,sizeof(*pAddress)))
  {
    if(VirtualProtect(pAddress,sizeof(DWORD),PAGE_READWRITE,&oldProtect))
    {
      bAttributeChange = TRUE;
      if( IsBadWritePtr( pAddress, sizeof(*pAddress) )  == TRUE )
      {
        //MessageBox(NULL,L"Fail to change the protection attributes",L"Fail",MB_OK);
        bReplacePossible = FALSE;
        rv = FALSE;
      }
    }
  }

  if(bReplacePossible )
      *(DWORD*)pAddress=location;    
  
  //revert back the protection if requireed
  if(bAttributeChange)
    VirtualProtect(pAddress,sizeof(*pAddress),oldProtect,&Stub);

  return rv;

}

int WINAPI CustomMessageBox(HWND wHandle,LPCWSTR text,LPCWSTR title,UINT type)
{
  return mbOrig(NULL,L"You will always see this",L"No Matter What you try",MB_OK);
}

// importing the fucntino address.. from IAT table
PDWORD GetFunctionAddress(LPCWSTR moduleName,char* functionName )
{

PDWORD fpPlaceholders;
HMODULE module = GetModuleHandle(moduleName);

IMAGE_IMPORT_DESCRIPTOR* importDescriptor = GetImportTable(module);

BOOL bFound = FALSE;
while (importDescriptor->Name && bFound != TRUE)
{
  int n = 0;
  IMAGE_THUNK_DATA* thunkData = (IMAGE_THUNK_DATA*)
    ((BYTE*)module + importDescriptor->OriginalFirstThunk);

  while (thunkData->u1.Function )
  {
    char* importFunctionName = (char*)((BYTE*)module +
      (DWORD)thunkData->u1.AddressOfData + 2);
       
    if(strcmp(importFunctionName, functionName) == 0)
    {
      //store the address to the vector
      fpPlaceholders = (DWORD*)((BYTE*)module + importDescriptor->FirstThunk) + n;
      bFound = TRUE;
      break;
    }
    n++;
    thunkData++;
  } importDescriptor++;
}
return fpPlaceholders;

}

Now we have to write one process which will get the ThreadID of
IE process and load this injected.dll

///injector.cpp///
int _tmain(int argc, _TCHAR* argv[])
{
HMODULE hmod;
HHOOK hhook;
FARPROC procAddr;
  MessageBox(NULL,L"waiting to connect with IE",L"Wait",MB_OK);
HWND hwnd=GetIEWindowHandle();
DWORD threadID=GetThreadIdFromIEWindowHandle(hwnd);

if(threadID<=0){
MessageBox(NULL,L"Could Not Get thread id",L"Message",MB_OK);
return -1;
}
hmod=LoadLibrary(L"injected.dll");
if(!hmod){
    DWORD error = GetLastError();
    wchar_t temp[5];
    wsprintf(temp,L"%d",error);
    MessageBox(NULL,temp,L"Message",MB_OK);
return 1;
}
#ifdef _WIN64
procAddr=GetProcAddress(hmod,"fninjected");
#else
procAddr=GetProcAddress(hmod,"_fninjected@12");
#endif
if(!procAddr){
MessageBox(NULL,L"Get ProcAddress Failed",L"Message",MB_OK);
return 2;
}
MessageBox(NULL,L"Press Ok to Hook",L"Message",MB_OK);
hhook=SetWindowsHookEx(WH_GETMESSAGE,(HOOKPROC)procAddr,hmod,threadID);
if(!hhook){
MessageBox(NULL,L"SetWindowsHookEx Failed",L"Message",MB_OK);
return 3;
}
MessageBox(NULL,L"Press Ok to UnHook when its done",L"Message",MB_OK);
UnhookWindowsHookEx(hhook);
FreeLibrary(hmod);

return 0;

}
// getting IE handle using CoCreateInstance...
HWND GetIEWindowHandle(void){
HRESULT hr;
IShellWindows *pShell;
long count=0;
VARIANT vtItem;
IDispatch *pDispatch;
IWebBrowser2 *pBrowser;
SHANDLE_PTR hwnd;
vtItem.vt=VT_I4;
wchar_t className[1024]={0};
BOOL found=FALSE;
hr=CoInitializeEx(NULL,COINIT_APARTMENTTHREADED);

if(!SUCCEEDED(hr))return NULL;
hr=CoCreateInstance(CLSID_ShellWindows,NULL,CLSCTX_SERVER,IID_IShellWindows,(LPVOID*)&pShell);
if(!SUCCEEDED(hr))return NULL;
hr=pShell->get_Count(&count);
for(long iter=0;iter<count;iter++){
vtItem.lVal=iter;
hr=pShell->Item(vtItem,&pDispatch);
if(!SUCCEEDED(hr)||!pDispatch)continue;
if(!SUCCEEDED(pDispatch->QueryInterface(IID_IWebBrowser2,(void**)&pBrowser)))continue;
pBrowser->get_HWND(&hwnd);
GetClassName((HWND)hwnd,className,1023);
if(!wcscmp(className,L"IEFrame")){
found=TRUE;
break;
}
}
CoUninitialize();
return (HWND)hwnd;
}

//getting thread ID using IE Handle
DWORD GetThreadIdFromIEWindowHandle(HWND IEWindowHandle){
DWORD dwTargetPid;
EnumChildWindows(IEWindowHandle,DoSomethingHelper,0);
return GetWindowThreadProcessId(WindowHandle,&dwTargetPid);
}



Now its time to run above messy code,just make sure IE is already executing before triggering injector.exe. Once its hooked, if IE will pop any messagebox it throws your custom message box.



Hope this will help you to get something useful.

1 comment:

  1. how we can change the behavior for mspaint ? I am working on mspaint plugin which provide more filter for image editing.

    ReplyDelete