Jump to content

This site uses cookies. By continuing to browse the site you are agreeing to our use of cookies. Find out more here. X
X


Photo

Windows API Hooking, Injecting a DLL


  • Please log in to reply
45 replies to this topic

#1 wraithdu

wraithdu

    this noise inside my head

  • MVPs
  • 2,413 posts

Posted 06 January 2009 - 06:40 PM

IMPORTANT: For those who don't have them, please download and install the Microsoft VC++ 2008 SP1 Redistributable
If you don't have these runtimes, the example will crash.

This little project of mine got influnce from many places. First was monoceres' example of hooking a local API call by modifying the IAT. Next was Ward's MemoryDll UDF, which formed a basis for my method. Lastly was a CodeProject article here -

http://www.codeproject.com/KB/system/mini_hook_engine.aspx

which describes a hook engine, and from which I took the idea for the hook bridge, which allows a hooking function to call the real function without resetting the hook (makes this process thread-safe). I'm also using Distorm64 for the disassembly. The compiled distorm64 DLL is included and used via Ward's MemoryDll UDF.

What this archive contains -

Source for the testapp, example app, and DLL
The compiled DLL that is injected into the testapp to hook the MessageBoxW function
UDF source files include _WinApiHook (main UDF), and support files _Distorm, _Distorm_src (the DLL), _MemoryDll (from Ward)

In order to do remote hooking, a DLL must* be used. This method uses RtlCreateUserThread() to cause the remote process to call LoadLibraryA to load the remote DLL. Similarly, it can be made to call FreeLibrary, or any function in your injected DLL. This method can be used to retrieve a remote process's commandline, for example.

I hope the included sample DLL can show the functions needed and how to use them. 2 functions are requred for each API - 1 to do the actual hooking, and 1 to retrieve the Bridge address. This function must be called manually before setting the hook to provide the remote process with the address of the Bridge. Well, the 2nd function and manual call are only required if you want your hooking function to be able to call the original function.

Comments welcome!

* I said must above, but that's not 100% true. There is a way to do it without an external DLL using WriteProcessMemory, but it sill requires the binary data of compiled code, and the code must adhere to specific guidelines -
(among others, HERE under "Additional caveats")
- code must not use C/C++ runtimes
- code must not call other functions, unless they are also injected (those functions must adhere to the same guidelines)
This makes it pretty hard to do anything useful.

Update 1: 2009/01/06
- fixed small error in example
- added note about installing the VC++ 2008 SP1 Redistributable

Update 2: 2009/01/22
- fixed error in _HookApi_Get() trying to FreeLibrary the wrong address
- added function to find the base address of a module in a process (incorporated in main functions)
- _InjectDll and _FreeRemoteDll now search the remote process for the base address of kernel32.dll, instead of assuming the base address is constant in all processes
- removed compiled apps from archive to reduce its size
- _HookApi_Get creates hook backup by reading remote process memory, instead of assuming local code is identical

Update 3: 2009/09/25
- fixed virtual memory release errors: previously used _MemVirtualFree() with the $MEM_DECOMMIT flag instead of the correct $MEM_RELEASE flag
- changed inject/free functions to use RtlCreateUserThread() (original CreateRemoteThread option is still there, only commented out)
- minor changes to _MemoryDll UDF: now it allocates memory using _MemVirtualAlloc() with the $PAGE_EXECUTE_READWRITE flag to avoid any possible problems with DEP

Update 4: 2009/09/28
- added _GetPrivilege_SEDEBUG.au3 to zip file

Update 5: 2009/10/04
- updated injection functions to work on XP: it will use CreateRemoteThread() on XP and RtlCreateUserThread() on Vista+

Update 6: 2009/10/06
- new option in _InjectDll() and _FreeRemoteDll() to skip remotely locating the base address of 'kernel32.dll' - THIS IS DANGEROUS, USE AT YOUR OWN RISK!!!
- switched to use the CreateToolhelp32 API to enumerate remote modules (the PSAPI version is still included)
- much better error detection and return values in all UDFs

Update 7: 2009/10/06
- workaround for AutoIt < 3.3.1.0 bug

Attached Files


Edited by wraithdu, 06 October 2009 - 10:04 PM.








#2 JRSmile

JRSmile

    MCSE 2012R2

  • Active Members
  • PipPipPipPipPipPip
  • 461 posts

Posted 06 January 2009 - 08:01 PM

This little project of mine got influnce from many places. First was monoceres' example of hooking a local API call by modifying the IAT. Next was Ward's MemoryDll UDF, which formed a basis for my method. Lastly was a CodeProject article here -

http://www.codeproject.com/KB/system/mini_hook_engine.aspx

which describes a hook engine, and from which I took the idea for the hook bridge, which allows a hooking function to call the real function without resetting the hook (makes this process thread-safe). I'm also using Distorm64 for the disassembly. The compiled distorm64 DLL is included and used via Ward's MemoryDll UDF.

What this archive contains -

A compiled testapp (the app that will be hooked)
A compiled example app which will show both local and remote hooking
A compiled DLL that is injected into the testapp to hook the MessageBoxW function

Source for the testapp, example app, and DLL

UDF source files include _WinApiHook (main UDF), and support files _Distorm, _Distorm_src (the DLL), _MemoryDll (from Ward)

In order to do remote hooking, a DLL must* be used. This method uses CreateRemoteThread to cause the remote process to call LoadLibraryA to load the remote DLL. Similarly, it can be made to call FreeLibrary, or any function in your injected DLL. This method can be used to retrieve a remote process's commandline, for example.

I hope the included sample DLL can show the functions needed and how to use them. 2 functions are requred for each API - 1 to do the actual hooking, and 1 to retrieve the Bridge address. This function must be called manually before setting the hook to provide the remote process with the address of the Bridge. Well, the 2nd function and manual call are only required if you want your hooking function to be able to call the original function.

Comments welcome!

* I said must above, but that's not 100% true. There is a way to do it without an external DLL using WriteProcessMemory, but it sill requires the binary data of compiled code, and the code must adhere to specific guidelines -
(among others, HERE under "Additional caveats")
- code must not use C/C++ runtimes
- code must not call other functions, unless they are also injected (those functions must adhere to the same guidelines)
This makes it pretty hard to do anything useful.


the hooked msgboxw crashed the testap before the 10 seconds ran out.. im using vista.
ps: tested with adminrights without and with compatibility mode. everytime crash befor i could press the button.
pss: but anyway a nice and worthful try. keep on the good work.

Edited by JRSmile, 06 January 2009 - 08:06 PM.

$a=StringSplit("547275737420796F757220546563686E6F6C75737421","")For $b=1 To UBound($a)+(-1*-1*-1)step(2^4/8);&$b+=1*2/40*µ&Asc(4)Assign("c",Eval("c")&Chr(Dec($a[$b]&$a[$b+1])))''Chr("a")&"HI"Next;time_U&r34d,ths,U-may=get$the&c.l.u.e;b3st-regards,JRSmile;MsgBox(0x000000,"",Eval("c"));PiEs:d0nt+*b3.s4d.4ft3r.1st-try:-)

#3 wraithdu

wraithdu

    this noise inside my head

  • MVPs
  • 2,413 posts

Posted 06 January 2009 - 09:04 PM

That's very odd. I'm also on Vista SP1 32-bit, and it works fine here, both in compiled form and from Scite.

How are you running the test? You should extract the 2 compiled EXEs and the DLL to the same directory, and run ApiHookExample.exe.

Also works well on an XP box I just tested.

Edited by wraithdu, 06 January 2009 - 09:09 PM.


#4 yehia

yehia

    Universalist

  • Active Members
  • PipPipPipPipPip
  • 275 posts

Posted 06 January 2009 - 10:23 PM

crashes here too win xp sp2

#5 wraithdu

wraithdu

    this noise inside my head

  • MVPs
  • 2,413 posts

Posted 06 January 2009 - 10:59 PM

Are you running the compiled EXE as described above, or recompiling, or what? I can't seem to reproduce these crashes on Vista or XP.

#6 wraithdu

wraithdu

    this noise inside my head

  • MVPs
  • 2,413 posts

Posted 06 January 2009 - 11:30 PM

Ok, so I did get it to crash on an XP system I have at home. Unfortunately I have no idea why it would crash on some systems and not others. Any ideas would be welcome.

I've tested on 1 Vista sys (OK), 2 XP SP3 system (1 OK, 1 crash).

Edited by wraithdu, 06 January 2009 - 11:30 PM.


#7 wraithdu

wraithdu

    this noise inside my head

  • MVPs
  • 2,413 posts

Posted 07 January 2009 - 03:29 AM

Ok, duh, I figured it out. I did find one error in the example, but it wasn't necessarily related to the crash.

For those who had problems with the test crashing, please download the VC++ 2008 SP1 Redistributable and install the runtimes. The msgdll.dll was failing to load on systems without the runtimes and crashing the testapp. See first post for an updated archive and download link for the runtimes.

Edited by wraithdu, 07 January 2009 - 03:29 AM.


#8 wraithdu

wraithdu

    this noise inside my head

  • MVPs
  • 2,413 posts

Posted 07 January 2009 - 01:53 PM

Eh, 722 downloads??? That's gotta be a board error...there's not even that many views!

#9 trancexx

trancexx

    Queen F. Elizabeth MCXI

  • Active Members
  • PipPipPipPipPipPip
  • 6,246 posts

Posted 07 January 2009 - 03:27 PM

Eh, 722 downloads??? That's gotta be a board error...there's not even that many views!

Maybe the view counter is wrong. Ahhaaa!

I know that I have downloaded. That's 1.
Nothing crashed for me (XP SP3)
...... ......
.:oOOOOo:. .:oOOOOo:.
.:oOO:'':Oo:. .:oO:'':OOo:.
.:oO: 'Oo:oO' :Oo:.
:oO: 'o'
:Oo:
:oO: :Oo:
':oO: OT9AO0IEDrk :Oo:'
':oO: :Oo:'
':oO. .Oo:'
':oO
. .Oo:'
':oO. .Oo:'
':oO. .Oo:'
'oO:Oo'
'o' :kiss:





.
eMyvnE


#10 JRSmile

JRSmile

    MCSE 2012R2

  • Active Members
  • PipPipPipPipPipPip
  • 461 posts

Posted 07 January 2009 - 06:07 PM

Eh, 722 downloads??? That's gotta be a board error...there's not even that many views!


i downloaded 5 times: laptop, iphone,rootserver,work pc,svn-server... maybe others did the same?
$a=StringSplit("547275737420796F757220546563686E6F6C75737421","")For $b=1 To UBound($a)+(-1*-1*-1)step(2^4/8);&$b+=1*2/40*µ&Asc(4)Assign("c",Eval("c")&Chr(Dec($a[$b]&$a[$b+1])))''Chr("a")&"HI"Next;time_U&r34d,ths,U-may=get$the&c.l.u.e;b3st-regards,JRSmile;MsgBox(0x000000,"",Eval("c"));PiEs:d0nt+*b3.s4d.4ft3r.1st-try:-)

#11 wraithdu

wraithdu

    this noise inside my head

  • MVPs
  • 2,413 posts

Posted 07 January 2009 - 06:50 PM

LOL, holy crap! You do that for everyone's stuff, or is it just that good? HA!

#12 JRSmile

JRSmile

    MCSE 2012R2

  • Active Members
  • PipPipPipPipPipPip
  • 461 posts

Posted 08 January 2009 - 08:43 PM

this is my normal procedure if i find interresting code for further development: download, categorize (function name includes dlls used etc..) upload to svn, backup on nas and a zipped working example on all of my machines. sorry to dissapoint you :-)
$a=StringSplit("547275737420796F757220546563686E6F6C75737421","")For $b=1 To UBound($a)+(-1*-1*-1)step(2^4/8);&$b+=1*2/40*µ&Asc(4)Assign("c",Eval("c")&Chr(Dec($a[$b]&$a[$b+1])))''Chr("a")&"HI"Next;time_U&r34d,ths,U-may=get$the&c.l.u.e;b3st-regards,JRSmile;MsgBox(0x000000,"",Eval("c"));PiEs:d0nt+*b3.s4d.4ft3r.1st-try:-)

#13 arcker

arcker

    Universalist

  • Active Members
  • PipPipPipPipPipPip
  • 581 posts

Posted 09 January 2009 - 09:10 AM

that's the thing i tried to do when monoceres did his script for local api hook, but i never managed to do it, and whe i see the complexity of all, i understand why :)
do you think you can make a hook for the function SHFileOperation used in shell32.dll with the process explorer.exe ?
i'm trying to edit your code to make it woek but it's a little bit hard with the dll's part.
awesome job anyway :o that open lot of possibilities for monitoring.

Edited by arcker, 09 January 2009 - 09:10 AM.

-- Arck System _ Soon -- Ideas make everything

"La critique est facile, l'art est difficile"

 

Projects :

  • Au3Service : Run your exe as service V3 / Updated 29/07/2013 Get it Here

#14 wraithdu

wraithdu

    this noise inside my head

  • MVPs
  • 2,413 posts

Posted 09 January 2009 - 02:44 PM

Where exactly are you having trouble, injecting the DLL, hooking the API, or coding the DLL to do what you want? What do you want the hook to do? Also, are you hooking the Ansi or Unicode version of the function, or both?

FYI, the script already gets the SE_DEBUG privilege if it can, so you should be OK to open explorer. Be careful though, if ya mess up, you're taking explorer down with you :)

The basic DLL layout would be something like -

Plain Text         
// main.h #ifndef __MAIN_H__ #define __MAIN_H__ #include <windows.h> /*  To use this exported function of dll, include this header  *  in your project.  */ #ifdef BUILD_DLL     #define DLL_EXPORT __declspec(dllexport) WINAPI #else     #define DLL_EXPORT __declspec(dllimport) WINAPI #endif #ifdef __cplusplus extern "C" { #endif int DLL_EXPORT MySHFileOpW(LPSHFILEOPSTRUCT lpFileOp); int DLL_EXPORT GetSHFileOpW(void *p); #ifdef __cplusplus } #endif #endif // __MAIN_H__


Plain Text         
// main.cpp #include "main.h" int (WINAPI *pMySHFileOpW) (LPSHFILEOPSTRUCT); // pointer to the bridge // SHFileOpW hook function int DLL_EXPORT MySHFileOpW(LPSHFILEOPSTRUCT lpFileOp) {     if (pMySHFileOpW == NULL)         return 1;         // do something             // call the original SHFileOpW function through the Bridge     return pMySHFileOpW(lpFileOp); } // obtain the address to the Bridge int DLL_EXPORT GetSHFileOpW(void *p) {     // this function must be called before setting the hook to provide the hooking function with the bridge address     pMySHFileOpW = (int (WINAPI *)(LPSHFILEOPSTRUCT)) p;     return 0; } BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {     switch (fdwReason)     {         case DLL_PROCESS_ATTACH:             pMySHFileOpW = NULL;         case DLL_PROCESS_DETACH:         case DLL_THREAD_ATTACH:         case DLL_THREAD_DETACH:             break;     }     return TRUE; // succesful }


#15 arcker

arcker

    Universalist

  • Active Members
  • PipPipPipPipPipPip
  • 581 posts

Posted 09 January 2009 - 04:12 PM

thx for the answer !
i've a problem coding the dll, that's all ... for now
i want to hook, for now, just if the Api is called. After i will try to get the pointer information and launch my own "copyfile", lile supercopier does.

i'm using VC++ 2005
thx again :)

-- Arck System _ Soon -- Ideas make everything

"La critique est facile, l'art est difficile"

 

Projects :

  • Au3Service : Run your exe as service V3 / Updated 29/07/2013 Get it Here

#16 arcker

arcker

    Universalist

  • Active Members
  • PipPipPipPipPipPip
  • 581 posts

Posted 09 January 2009 - 05:48 PM

mmm the autoit code returns a bad parameter ( error 87 ) when using openprocess with explorer.exe....
don't know why.

-- Arck System _ Soon -- Ideas make everything

"La critique est facile, l'art est difficile"

 

Projects :

  • Au3Service : Run your exe as service V3 / Updated 29/07/2013 Get it Here

#17 wraithdu

wraithdu

    this noise inside my head

  • MVPs
  • 2,413 posts

Posted 09 January 2009 - 06:16 PM

This doesn't work for you and return a handle to explorer.exe?

#include <_WinApiHook.au3> $pid = ProcessExists("explorer.exe") ConsoleWrite("pid: " & $pid & @CRLF) $hProc = _GetProcHandle($pid) ConsoleWrite("hProc: " & $hProc & @CRLF) If $hProc Then _WinAPI_CloseHandle($hProc)


#18 arcker

arcker

    Universalist

  • Active Members
  • PipPipPipPipPipPip
  • 581 posts

Posted 10 January 2009 - 03:45 PM

yes this code works at home . ( not tested at work )
Now i try to run the test but it crashes explorer.exe. :/ ( so if there is just one explorer.exe, it crash the desktop but fortunately windows manage to restore him )
here is what i have :
AutoIt         
#NoTrayIcon #AutoIt3Wrapper_Change2CUI=y #include <_WinApiHook.au3> #include <_MemoryDll.au3> Local $MyMsgBox = DllCallbackRegister("_MyCopyFile", "none", "ptr") Local $pMyMsgBox = DllCallbackGetPtr($MyMsgBox) Local $targetmodule = "shell32.dll" Local $targetfunction = "SHFileOperationW" $DISTORM_DEBUG = True ;; remote hook test ;MsgBox(0 + 64, "Info", "Press OK.  You then have 10 seconds to test the Message button.") ;$pid = Run("explorer.exe") ;ProcessWait($pid) ;Sleep(500) ;test by using existing process $pid = ProcessExists("explorer.exe") ConsoleWrite("pid: " & $pid & @CRLF) ; for remote processes, the Bridge element will be NULL ; the BridgePtr will be a pointer in the remote process to the memory allocated $MyHook = _HookApi_Get($targetmodule, $targetfunction, $pid) ConsoleWrite("-------------" & @CRLF) ConsoleWrite("HookAddress: " & DllStructGetData($MyHook, "HookAddress") & @CRLF) ConsoleWrite("HookBak: " & DllStructGetData($MyHook, "HookBak") & @CRLF) ConsoleWrite("Bridge: " & DllStructGetData($MyHook, "Bridge") & @CRLF) ConsoleWrite("BridgePtr: " & DllStructGetData($MyHook, "BridgePtr") & @CRLF) ConsoleWrite("Status after get: " & DllStructGetData($MyHook, "Status") & @CRLF) ConsoleWrite("Process: " & DllStructGetData($MyHook, "Process") & @CRLF) ; verify bridge $hProcess = _GetProcHandle($pid) $s = DllStructCreate("byte[64]") DllCall("kernel32.dll", "int", "ReadProcessMemory", "ptr", $hProcess, "ptr", DllStructGetData($MyHook, "BridgePtr"), "ptr", DllStructGetPtr($s), "uint", 64, "uint*", 0) ConsoleWrite("-------------------" & @CRLF) ConsoleWrite("remote bridge: " & DllStructGetData($s, 1) & @CRLF) ; inject dll $hModule = _InjectDll(@ScriptDir & "\mine.dll", $pid) ;; set hook ; call the remote function to set the bridge address ; if you know the offset, then add it to $hModule ; if not, load the dll locally to get hMod and use GetProcAddress to get the funcaddres ; then the offset = funcaddress - hMod ; get the bridge setting function offset $hMod = _WinAPI_LoadLibrary("mine.dll") $fnAddress = _GetProcAddress($hMod, "_GetSHFileOp@4") $offset = $fnAddress - $hMod ; set the bridge $ret = DllCall("kernel32.dll", "ptr", "CreateRemoteThread", "ptr", $hProcess, "ptr", 0, "uint", 0, "ptr", $hModule + $offset, "ptr", DllStructGetData($MyHook, "BridgePtr"), "dword", 0, "ptr", 0) _WinAPI_WaitForSingleObject($ret[0]) _WinAPI_CloseHandle($ret[0]) ; get hook function offset $fnAddress = _GetProcAddress($hMod, "_MySHFileOp@16") $offset = $fnAddress - $hMod ; free the locally loaded library _WinAPI_FreeLibrary($hMod) _HookApi_Set($MyHook, $hModule + $offset) $s = 0 $s = DllStructCreate("byte[10]") DllCall("kernel32.dll", "int", "ReadProcessMemory", "ptr", $hProcess, "ptr", DllStructGetData($MyHook, "HookAddress"), "ptr", DllStructGetPtr($s), "uint", 10, "uint*", 0) ConsoleWrite("remote hook: " & DllStructGetData($s, 1) & @CRLF) _WinAPI_CloseHandle($hProcess) ; test out the hooked function Sleep(10000) MsgBox(0 + 64, "Info", "Time is up.  Please close any testapp message boxes, or it may crash.") ; unset the hook _HookApi_UnSet($MyHook) ; optionally free the injected library ; if the remote hooked message box is open when we do this, the remote app will crash ; leaving the injected library loaded will prevent the crash, and won't do any harm _FreeRemoteDll($hModule, $pid) MsgBox(0 + 64, "Unhooked", "MessageBoxW is now unhooked, and the dll has been unloaded.") DllCallbackFree($MyMsgBox) Func _MyCopyFile($LpFileOp)     MemoryFuncCall("int", DllStructGetData($MyHook, "BridgePtr"), "hwnd", 0, _                                 "wstr", "This is the hook intercepting the MessageBoxW call, then calling the bridge to the real function." & @CRLF & _                                 "Here's what we got:" & @CRLF & @CRLF & _                                                               "wstr", "Hooked Message Box", _                                 "uint", 48) EndFunc

thx for your great support ! :)

Edited by arcker, 10 January 2009 - 03:45 PM.

-- Arck System _ Soon -- Ideas make everything

"La critique est facile, l'art est difficile"

 

Projects :

  • Au3Service : Run your exe as service V3 / Updated 29/07/2013 Get it Here

#19 wraithdu

wraithdu

    this noise inside my head

  • MVPs
  • 2,413 posts

Posted 10 January 2009 - 10:54 PM

I don't have time right now, but I'll look at it soon.

#20 wraithdu

wraithdu

    this noise inside my head

  • MVPs
  • 2,413 posts

Posted 10 January 2009 - 11:00 PM

Could you post the source for your DLL, and if you can figure it out, at what step exactly Explorer is crashing.

From the name of your function, something doesn't look right. Both the SHFileOp hook function and the _Get function should only have 1 parameter, and so should be named at @4. Post the source and I'll look at it.

Vista or XP?

Edited by wraithdu, 10 January 2009 - 11:01 PM.





1 user(s) are reading this topic

0 members, 1 guests, 0 anonymous users