Link to home
Start Free TrialLog in
Avatar of r3L4x
r3L4x

asked on

Hooking the Export Address Table (EAT) - C++

Ok, i have designed a hooking system that i am trying to get to work!
Heres what it can do:
- It will cycle through the EAT of kernel32 for the desired API DeleteFileA once loaded
- It attempts to patch (unsuccessfuly - so far!) that address to point to MyDeleteFileA, an exported function. I am assuming this has to be exported and i connot just patch it will a local function address - this is the EAT after all!

This is where i get confused - when i record the ProcAddress of DeleteFileA to the one after it has been patched, they are the same, but it still crashes! I Have a feeling VirtualProtect is touching somthing other than i want it to..

Another thing, calling GetProcAddress with MyDeleteFileA and my dll yields a 9 digit number, while doing the same with kernel32 and DeleteFileA has a 10 digit one..

I chose DeleteFile because of the simple parameters!

just for refrence, this IS a dll project, and my dll's name is "DLL.dll"

#include <windows.h>
#include <stdio.h>
#include "dll EAT hook.h"

BOOL __stdcall MyDeleteFileA(LPCTSTR File);
BOOL HookEAT();

void Entry(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
      if (ul_reason_for_call == DLL_PROCESS_ATTACH)
      {
            HookEAT();
      }
}


BOOL __stdcall MyDeleteFileA(LPCTSTR File)
{
      char * blah;
      blah = new char[strlen(File) + 10];
      sprintf(blah, "Delete?\n%s", File);
      MessageBox(0, blah, "", 0);
      
      return 1;
}

BOOL HookEAT()
{
      HINSTANCE hInstance;
    hInstance = GetModuleHandle("kernel32.dll");

      IMAGE_DOS_HEADER *dosHeader;
    dosHeader = (IMAGE_DOS_HEADER *)hInstance;

    if (dosHeader->e_magic != IMAGE_DOS_SIGNATURE)
    {
         //MessageBox(0, "1", "", 0);    
            return false;
    }

    IMAGE_NT_HEADERS *ntHeaders = (IMAGE_NT_HEADERS *)(((BYTE *)dosHeader) + dosHeader->e_lfanew);

    if (ntHeaders->Signature != 0x00004550)
    {
         //MessageBox(0, "2", "", 0);
            return false;
    }
    IMAGE_OPTIONAL_HEADER *optionalHeader = &ntHeaders->OptionalHeader;
    IMAGE_DATA_DIRECTORY *dataDirectory = &optionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
     
    IMAGE_EXPORT_DIRECTORY *Exp;
    Exp = (IMAGE_EXPORT_DIRECTORY *)((DWORD)dosHeader + dataDirectory->VirtualAddress);

    int count = 1;

    ULONG * NameAddr = (ULONG*)((BYTE*) hInstance + Exp->AddressOfNames);
      USHORT * OrdinalAddr = (USHORT*)((BYTE*) hInstance + Exp->AddressOfNameOrdinals);
    for(count = 0; count < Exp->NumberOfNames; count++)
      {
        char* functionname = (char *)((BYTE *) hInstance + NameAddr[count]);
            USHORT * Ordinal = (USHORT *)OrdinalAddr[count];
            if (lstrcmpi("DeleteFileA", functionname) == 0){
                  DWORD actProtect, oldProtect;
                  DWORD* Target;
                  Target = (DWORD*)GetProcAddress(GetModuleHandle("kernel32.dll"), "DeleteFileA");
                  if (VirtualProtect(Target,  sizeof(DWORD), PAGE_EXECUTE_READWRITE, &actProtect))
                  {
                        //*Target = (DWORD)GetProcAddress(GetModuleHandle("DLL.dll"), "MyDeleteFileA");
                        *Target = (DWORD)MyDeleteFileA;
                  }
                  VirtualProtect(Target, sizeof(DWORD), actProtect, &oldProtect);
                    
                    // sizeof(DWORD)
                   // DeleteFile("C:\\Loader.exe");
                  char blah [150];
                  sprintf(blah, "Fake Addr: %d\nReal Addr: %d\nName: %s\nCount: %d\nOrdinal: %d", (DWORD)MyDeleteFileA, (DWORD)GetProcAddress(GetModuleHandle("kernel32.dll"), "DeleteFileA"), functionname, count, Ordinal);
                  MessageBox(0, blah, 0,0);
            }  
      }
      return true;
}


/////////////////////////////////////

dll EAT hook.def:
LIBRARY DLL_EAT_HOOK

EXPORTS
  MyDeleteFileA


/////////////////////////////////////

dll EAT hook.h:

#ifndef DLL_EAT_HOOK_H
#define DLL_EAT_HOOK_H

BOOL __stdcall MyDeleteFileA(LPCTSTR File);
#endif

///////////////////////////////////

thanks!
Avatar of cookre
cookre
Flag of United States of America image

The attempted address replacement occurs only if the VirtualProtect() is successful.  There is no diagnostic if the VirtualProtect() fails.  That might be useful.
Avatar of r3L4x
r3L4x

ASKER

Yeah, but its testing for that, thats what this line is for:

if (VirtualProtect(Target,  sizeof(DWORD), PAGE_EXECUTE_READWRITE, &actProtect))
{
...

and if it failed in the first place, it wouldnt still crash!
9-digit, 10-digit, different areas of RAM.  
Which showed up in the display?
What sort of crash?
Avatar of r3L4x

ASKER

It crashes with a general protection fault.

I have another program i was using to test it:

void main()
{
      
      DWORD Old = (DWORD)GetProcAddress(GetModuleHandle("kernel32.dll"), "DeleteFileA");
      LoadLibrary("C:\\DLL.dll");
      DWORD Orig = (DWORD)GetProcAddress(GetModuleHandle("DLL.dll"), "MyDeleteFileA");
      DWORD New = (DWORD)GetProcAddress(GetModuleHandle("kernel32.dll"), "DeleteFileA");
      char blah[150];
      sprintf(blah, "My Patch Addr: %d\nOrig Addr: %d\nNew (After Patch): %d", Orig, Old, New);
      
      MessageBox(0, blah, 0,0);
      DeleteFile("C:\\blah.txt"); //for testing
      return;
}

and here is an example of what it returns:
My Patch Addr: 268439712
Orig Addr: 2011633530
New (After Patch): 2011633530

the address to MyDeleteFileA is only 9 digits, the addr to DeleteFileA is 10, would this be a cause for concearn?
>> the address to MyDeleteFileA is only 9 digits, the addr to DeleteFileA is 10, would this be a cause for concearn?

No.  System dlls are loaded between 0x70000000 and 0x78000000 (1879048192 and 2013265920  decimal).  Dlls you make have a default base address of 0x10000000 (268435456 decimal).  If you load more than one of your own dlls, all but the first need to be relocated.  If you watch the debug output window as they load, you will see the relocation messages.  If you rebase your app or set non-default base addresses, you can improve load time quite a bit.  Microsoft suggests 0x60000000 to 0x68000000 (http://msdn.microsoft.com/library/default.asp?url=/library/en-us/tools/tools/rebase.asp).

Also, your approach is not entirely accurate.  The loader resolves import addresses and updates the import address table as modules are loaded.  Even if you are successful in replacing the export address entry, any module already loaded will retain the original address in its import table since it's already been manipulated.  You'd have to go through previously loaded modules and update the import addresses.  Changing the export address after the fact will have no effect except for those modules loaded subsequently or if someone is later doing a GetProcAddress.
Avatar of r3L4x

ASKER

it does make sence, i never thought about that :(

so would there be any other way to do one mass change on the IAT or will i have to individually patch each module to point to the new address?

I guess the only advantage with this over iat patching is that newly loaded dlls automatically have the filter address?
>> I guess the only advantage with this over iat patching is that newly loaded dlls automatically have the filter address?

Correct.  Or anyone doing a GetProcAddress.

The other approach other than patching all the IAT's would be to get the proc address and then go there and put a jump to your function.  This makes the original function completely unavailable, but it will redirect ANY call to your function and avoids patching IAT's or the export table.  I've never tried it, but it seems plausible.
Avatar of r3L4x

ASKER

Ok. thanks for clearing that up!
I have one more question (hopefuly the last!) About the IAT. I have been using this code, and it seems to do just fine for me, but i almost know its written wrong. The reason is that when i want to hook somthing, say, exported by kernel32, it find it and hooks it. but shouldnt it not even work, since that function wouldnt be imported by kernel32?
Lets just say i am fuzzy on the type of hooking this is doing.

////////////////////////////////

struct ImageImportEntry
{
   DWORD characteristics,
         timeDateStamp;
   WORD  majorVersion,
         minorVersion;
   DWORD name,
         lookupTable;
};

struct ImportFunction
{
   WORD jumpInstruction;
   void *addressOfPointerToFunction;
};

void * HookAddress(char* ExportingDLL, char* FunctionName, void* TargetAddress, void* NewAddress)
{

      HINSTANCE hInstance;
      hInstance = GetModuleHandle(NULL);

      HMODULE dll;
      dll = GetModuleHandle(ExportingDLL);

      void *Temp;
      Temp = GetProcAddress(dll, FunctionName);
      if (Temp == NULL){
            return NULL;
      }

      void *OldAddress;
      OldAddress = FunctionAddress(Temp);

      IMAGE_DOS_HEADER *dosHeader;
      dosHeader = (IMAGE_DOS_HEADER *)hInstance;

      if (dosHeader->e_magic != IMAGE_DOS_SIGNATURE)
      {
            return NULL;
      }

      IMAGE_NT_HEADERS *ntHeaders = (IMAGE_NT_HEADERS *)(((BYTE *)dosHeader) + dosHeader->e_lfanew);

      if (ntHeaders->Signature != 0x00004550)
      {
            return NULL;
      }
//MessageBox(0, "1", "", 0);
      IMAGE_OPTIONAL_HEADER *optionalHeader = &ntHeaders->OptionalHeader;
      IMAGE_DATA_DIRECTORY *dataDirectory = &optionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];

      // Look for the beginning of the function pointers
      ImageImportEntry *imageImportEntry = (ImageImportEntry *)((DWORD)dosHeader + dataDirectory->VirtualAddress);
      DWORD endImportEntry = (DWORD)imageImportEntry + dataDirectory->Size;
      
      if (endImportEntry == NULL) {return NULL;};
//MessageBox(0, "2", "", 0);
      bool found = false;

      while (imageImportEntry->name)
      {
            if (imageImportEntry->lookupTable > endImportEntry)
                  break;
            if (imageImportEntry->lookupTable)
            {
//MessageBox(0, "3", "", 0);
                  DWORD *importCode = (DWORD *)((DWORD)dosHeader + imageImportEntry->lookupTable);

                  while (*importCode)
                  {
                        if (*importCode == (DWORD)TargetAddress)
                        {
                              found = true;
                              // Overwrite old function pointer
                              DWORD actProtect, oldProtect;
                              if (VirtualProtect(importCode, sizeof(DWORD), PAGE_EXECUTE_READWRITE, &actProtect))
                              {
                                    *importCode = (DWORD)NewAddress;
                              }

            // Restore old Protection
                              VirtualProtect(importCode, sizeof(DWORD), actProtect, &oldProtect);
                        }
                  importCode++;
                  }
//MessageBox(0, "4", "", 0);
            }
            imageImportEntry++;
      }
//MessageBox(0, "5", "", 0);
      return OldAddress;
}

void * FunctionAddress(void *address)
{
      // This is just for stability i guess :-/

      ImportFunction *function = (ImportFunction *)address;

      if (function->jumpInstruction == 0x25FF)
            return function->addressOfPointerToFunction;

      return address;
}

///////////////////////////

called like
HookAddress("kernel32.dll", "CreateProcessA", CreateProcess, MyCreateProcess);

MyCreateProcess being a local function using the __stdcall convention.

thanks!!
ASKER CERTIFIED SOLUTION
Avatar of drichards
drichards

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of r3L4x

ASKER

thankyou for the tips and help, i will considet this case closed!
Avatar of r3L4x

ASKER

I just wish he included some code to go with his explinations (besides his pedump thing), but it is still very helpful