I wanted to hook the WinApi funktion CreateFile, CloseHandle, ReadFile and WriteFile to decode a device protocol. So I searched for a hooking library in the internet. Unfortunately the only good one for c++ was detour from Mircrosoft which was free only for x86 non comercial use. As my laptop runs with a x64 Windows Vista this was no option for me. So I found easyhook, a c# hooking library. It was quite easy to achive the first success with it, but then I had a few small problems for which I want to describe the solutions here.
Lets asume you want to hook a function which easy hook which executes some hooking code before and after the original call:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
[UnmanagedFunctionPointer(...)] delegate bool DSomeFunc(...); [DllImport("somedll.dll")] static bool SomeFunc(...); static bool SomeFunc_Hooked(...){ some code before original call bool ret = SomeFunc(...); some code after original call return ret; } |
You will notice that the code after the original call never gets executed. This is because the DllImport gives you the already hooked function. So you are calling the hooked function from the hooked function. This is prevented by the easyhook lib, by returning the flow to the calling program. (which is a very good thing, because it would lead to a infinite loop)
To avoid this behaviour you have to get the unhooked function from the easyhook library.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
[UnmanagedFunctionPointer(...)] delegate bool DSomeFunc(...); private DSomeFunc SomeFuncPtr; static bool SomeFunc_Hooked(...){ some code before original call bool ret = SomeFuncPtr(...); some code after original call return ret; } |
And in the run method of your injected dll right after installing the hooks.
1 2 3 |
... SomeFuncPtr = LocalHook.GetProcDelegate<DSomeFunc>("somedll.dll", "SomeFunc"); ... |
Now your code after the original api call gets executed!
The second problem I encounterd was on hooking WriteFile:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
[UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Auto, SetLastError = true)] delegate bool DWriteFile( IntPtr hFile, byte[] lpBuffer, uint nNumberOfBytesToWrite, out uint lpNumberOfBytesWritten, [In] IntPtr lpOverlapped); [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true, CallingConvention = CallingConvention.StdCall)] static extern bool WriteFile( IntPtr hFile, byte[] lpBuffer, uint nNumberOfBytesToWrite, out uint lpNumberOfBytesWritten, [In] IntPtr lpOverlapped); static bool WriteFile_Hooked( IntPtr hFile, byte[] lpBuffer, uint nNumberOfBytesToWrite, out uint lpNumberOfBytesWritten, [In] IntPtr lpOverlapped) { for(uint i=0;i<nNumberOfBytesToWrite;i++){ do_something_with(lpBuffer[i]); } return WriteFile(hFile, lpBuffer, nNumberOfBytesToWrite, out lpNumberOfBytesWritten, lpOverlapped); } |
On every WriteFile call I got a exception in the loop for i=1, because if the call comes from a unmanaged app the byte array has no size and therefore the size 1 is assumed.
To fix the problem one has to use the marshal class and change all the byte arrays to pointers.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
[UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Auto, SetLastError = true)] delegate bool DWriteFile( IntPtr hFile, IntPtr lpBuffer, //changed uint nNumberOfBytesToWrite, out uint lpNumberOfBytesWritten, [In] IntPtr lpOverlapped); [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true, CallingConvention = CallingConvention.StdCall)] static extern bool WriteFile( IntPtr hFile, byte[] lpBuffer, //no need to change here because we are calling that uint nNumberOfBytesToWrite, out uint lpNumberOfBytesWritten, [In] IntPtr lpOverlapped); static bool WriteFile_Hooked( IntPtr hFile, IntPtr lpBuffer, //changed uint nNumberOfBytesToWrite, out uint lpNumberOfBytesWritten, [In] IntPtr lpOverlapped) { byte[] bytes = new byte[nNumberOfBytesToWrite]; for (uint i = 0; i < nNumberOfBytesToWrite; i++) bytes[i] = Marshal.ReadByte(lpBuffer, (int)i); for(uint i=0;i<nNumberOfBytesToWrite;i++){ do_something_with(bytes[i]); } return WriteFile(hFile, bytes, nNumberOfBytesToWrite, out lpNumberOfBytesWritten, lpOverlapped); } |
Now the hook works. The same works for hooking ReadFile but after calling the original function (also use tipp 1!)
For hooking I simply changed the FileMonInject example the source can be downloaded here:
C# Source