Link to home
Start Free TrialLog in
Avatar of Blacksoulman
Blacksoulman

asked on

Api Hooks on Win9x with WriteProcessMemory()

Now Aphex made a hooking unit(afxCodeHook) in delphi but sadly it only works on NT systems.  I can inject the library fine in win9x using elicz elirt library.  But actually writing to the import address table seems to be a problem.  Ive tried to fix it by changing the protection attributes but it didnt do much.  Here is the section where it fails:

        if Address = OldAddress then
        begin
        EnterCriticalSection(CritSect);
       if VirtualProtect(Address, sizeof(Address),PAGE_EXECUTE_READWRITE, @dwOldProtect) then
        try
         WriteProcessMemory(GetCurrentProcess, ImportCode, @NewAddress, SizeOf(dword), BytesWritten);
        finally
         VirtualProtect(Address, sizeof(Address),dwOldProtect, nil);
         LeaveCriticalSection(CritSect);
        end;
        end;
        Inc(ImportCode);
      end;
      Inc(ImageImportEntry);
    end;
  end;

Is there something I can test to make sure its writing correctly to right address?
Avatar of Madshi
Madshi

Some comments about your code:

(1) For what purpose is that critical section? I don't see much sense in that.
(2) If VirtualProtect fails, the critical section will never be unlocked again, which will probably sooner or later deadlock your program.
(3) Why using WriteProcessMemory? Why not directly writing to the memory? You want to overwrite memory in your own process, don't you? WriteProcessMemory is for writing into other processes. Sure, you can use it for your own process, too, but what is the sense of it?

You say "writing to the IAT seems to be a problem". What problem? Do you get access violations? Or does the hook simply not work? Or what? Could you please be a bit more specific?

--------------------------

Anyway, if you just want a quick and easy solution, have a look at my Delphi package "madCodeHook". It's free for non-commercial usage and works equally fine in win9x and winNT. Here's the latest beta version:

http://madshi.net/madCollectionBeta.exe

Some demos are included.
Avatar of Blacksoulman

ASKER

Thank you for replying.  Ive added in critical section because some hooking tutorials/examples (like some from rootkit.com) do that so no other threads can access the memory page while you are writing to it. Its just some things ive have been trying to make work on 9x.  

The hook simply does not work.  It finds all the imported modules even seems to be writing something because Ive done
byteswritten :=0
VirtualProtect(Address, sizeof(Address),PAGE_EXECUTE_READWRITE, @dwOldProtect) then
 WriteProcessMemory(GetCurrentProcess, ImportCode, @NewAddress, SizeOf(dword), BytesWritten);
if byteswritten > 0 then
messagebox(0,'written','test',0);  
(and i get the msg box.)

I have not seen another api for writing to the IAT accept CopyMemory().  I have tried that too but the params are wrong because it crashes.
CopyMemory(Address, NewAddress,5); ???

Yes Ive used your collection and it works perfectly but its just a little bulky and I need something smaller and simple.
ASKER CERTIFIED SOLUTION
Avatar of Madshi
Madshi

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
Well I encountered a runtime error on XP with that piece of code.  The only thing i changed was TPPointer to ^Pointer since thats how you have it defined in your unit.  But this is the closest ill get probably and no one else is as familiar with this as you so obviously you get the points.
Hmm i wish the forum had an edit button.  I got the runtime error cause i placed a messagebox after while p1^ <> nil do begin
to find out why it wasnt loading my function.  Took it out and no runtime but function stil not loaded.  I put a msg box in myfindnextfileW to tell me if it gets loaded.  But i never got the msg box.  I think its failing in same area...

while p1^ <> nil do begin
          if (p1^ = old) and VirtualProtect(p1, 4, PAGE_EXECUTE_READWRITE, @c1) then
          p1^ := new;
FindNextFileW isn't supported in win9x. Maybe that's the problem?

Without knowing more details about which APIs you want to hook for what purpose, I can't help any further.
heh nono Im testing this on winxp first...Sorry should have said that before.  When it works on XP then ill go and test on winME.
Without knowing more details about which APIs you want to hook for what purpose, I can't help any further.

I don't know any details about what you're doing and what exactly fails.
any apis!  FindNextFileW was just an example.  I just want to be able to put a msg box in the hooked function so I know it works.  Eventually im going to monitor certain api calls(I dont know which ones Im going to monitor yet) on my network so I have more control when people are on it.
You said in your original question, that the sources you got from "Aphex" do work in NT. Did you get them to work in NT? And my function does not work in NT? I can only say that I'm using exactly that function and it works perfectly fine for me in both NT and 9x.

How exactly did you find out that it does not work? You hooked the API in your own process? Or system wide? And executed the API in your own process? Or in another process?

Give me some more infos, currently I have not the slightest idea how I can help you, since I have no idea what goes wrong. Neither do I know what exactly you are doing (process wide hooking or system wide hooking etc).
The dll is loaded in one process example; Explorer.  And trying to hook the apis in that.

"Now Aphex made a hooking unit(afxCodeHook) in delphi but sadly it only works on NT systems" - meaning it works on NT.  

"I can inject the library fine in win9x using elicz elirt library.  But actually writing to the import address table seems to be a problem...Its just some things ive have been trying to make work on 9x. "- meaning it doesnt work on windows 98, and ME.

"I put a msg box in myfindnextfileW(with your code)  to tell me if it gets loaded.  But i never got the msg box.  I think its failing in same area..."  Example:

function FindNextFileWHookProc(hFindFile: THandle; var lpFindFileData: TWIN32FindDataW): BOOL; stdcall;
begin
  MessageBox(0, 'my function ran','test',0);
 Result := FindNextFileWNextHook(hFindFile, lpFindFileData);
end;

  So if the function is loaded im gonna get the msg box. But i dont on either os (with your code).

Now with afxCodeHook this is how you implement:

HookCode(@FindNextFileW, @FindNextFileWHookProc, @FindNextFileWNextHook);(works on NT)

With yours I did :
  kmodule:=GetModuleHandle(nil);
  PatchImportTable(kmodule,@FindNextFileW, @FindNextFileWHookProc);

Ah, finally I get to see some details...   :-)

Okay, the reason for the failure is very probably that you missed what I said here:

"For successful IAT patching you need to patch each and every module in your process. However, in win9x you're not allowed to patch system modules (modules whose handle is bigger than $80000000)"

Patching just the main module (GetModuleHandle(nil)) isn't enough. You have to loop through all modules and patch each and every one (except the system modules in win9x).

Aphex code must do that internally, too, if it works correctly. So I guess you can use his dll enumeration code together with my patching function.
When you say modules you are talking bout a handle to an imported dll like kernel32.dll right?  Because GetModuleHandle(nil) gets a handle on the process not the imported dlls right?  For example:findnextfileW is in kernel32.dll. But why would i have to loop through all the modules to intercept ONE api call if its only found in one module.  
An API is exported by one module (module = dll or exe). E.g. FindNextFileW is exported by kernel32.dll. Each process contains of one exe module and several dll modules, which are either dynamically or statically linked by the exe module. Now each of the dll module may have imported FindNextFileW. If e.g. "user32.dll" calls "FindNextFileW", your only chance the get notification about this is by patching the import table of "user32.dll". In other words: You have to patch the import table of all modules (exe + dlls) in your process. That is the very base logic of import table patching.

madCodeHook uses a completely different approach.
oh ok.  Well im not too concerned about other imported modules calling an api from another dll.  Just the processes.
Ok well im going to try and combine your code and aphexs to see if it works.  Have you seen his unit before? Maybe i should have told you to just take a look at it before. I believe everything is done correctly until writeprocessmemory().
I've just looked at his code. He does not enumerate the modules of the process. But when patching the import table he checks for dlls which are listed in the import table and hooks those, too. That means he hooks all dlls, which are statically linked. But he does not hook dynamically linked dlls.

His code looks more or less alright. The WriteProcessMemory call is a bit strange, but it should work nevertheless.

There are 2 possible reasons why his unit fails on 9x. Either you need to hook dynamically loaded dlls, too. Or import table patching is just not good enough in this specific situation.

Try this:

type
  TDAModule = array of cardinal;

// returns all modules of the current process
function GetModuleList : TDAModule;
var p1, p2 : pointer;
    mbi    : TMemoryBasicInformation;
    arrCh  : array [0..MAX_PATH] of char;
    i1     : integer;
begin
  SetLength(result, 10);
  i1 := 0;
  p1 := nil;
  p2 := nil;
  while VirtualQueryEx(GetCurrentProcess, p1, mbi, sizeOf(mbi)) = sizeOf(mbi) do begin
    if (mbi.State = MEM_COMMIT) and
       (mbi.AllocationBase <> p2) and (mbi.AllocationBase = mbi.BaseAddress) and
       (GetModuleFileName(dword(mbi.AllocationBase), arrCh, MAX_PATH) > 0) then begin
      if i1 = Length(result) then
        SetLength(result, i1 * 2);
      result[i1] := dword(mbi.AllocationBase);
      inc(i1);
    end;
    p2 := mbi.AllocationBase;
    dword(p1) := dword(p1) + mbi.RegionSize;
  end;
  SetLength(result, i1);
end;

procedure PatchImportTables(old, new: pointer);
var i1      : integer;
    modules : TDAModule;
begin
  modules := GetModuleList;
  for i1 := 0 to high(modules) do
    if (GetVersion and $80000000 = 0) or (modules[i1] < $80000000) then
      PatchImportTable(modules[i1], old, new);
end;

Then call PatchImportTables instead of PatchImportTable. This will hook all modules of the current process (excluding the system modules in 9x).

P.S: Please instead of "@FindNextFileW" use "GetProcAddress(kmodule, 'FindNextFileW')". Furthermore in win9x please run your exe *outside* of the IDE. In XP you can run it inside or outside.
Thanks very much.  It worked on both os.  Now im going to try change afxcodehook a bit by studying your code.  
If I ever release any of this code so others can use it or learn from it I will be sure to give you credits.
Ok, thanks.