FishHook is a Windows inline hook platform, which supports x86 and x64 environment. You can write filter routines to monitor or alter the behavior of other programs's API. Also, you can write your own "fake" APIs in your own DLL, and FishHook will inject your DLL into the process and replace the target API with your "fake" one. Besides, FishHook provides a function to hook the child-processes created by a "hooked" process, which is useful for building a "sandbox" environment.
- Visual Studio 2010. The community version is free.
- Detours Express 3.0 x86
- Detours x64. Original Detours x64 is charged. Here is a third-party-made Detours x64 lib, which is based on mhook. A copy of it is uploaded here.
This repo includes a VS2010 solution. Open FishHook32.sln. Build x86 version of the project "FishHook32". This generates "FishHook32.dll". Then switch to x64 mode and build the project "FishHook32" again. This generates "FishHook64.dll". Finally, switch to x86 mode and build and run project FishHookTest, which is an example of FishHook.
A process that initializes FishHook and filters the API calls of hooked processes is called "debugger". Only one debugger is running at a time, and a debugger is a 32-bit process. This means even though you run FishHook on a 64 bit system, usually you should call FishHook APIs in a 32-bit debugger. (However, your custom "fake" APIs can be written in x64 code.)
The easiest way to use FishHook is to implement a filter. FishHook has implemented some "fake" APIs, such as CreateProcessInternalW and ZwSetValueKey. You should give FishHook a process-id to hook and the list of the APIs you want to hook. In addition, you should write a filter program in your 32-bit debugger. FishHook will replace the APIs with its built-in "fake" APIs. Once the hooked process calls the hooked API, the user-defined filter routine will be called, and you can monitor or alter the behavoir of the API in your filter. Note that the filter runs in the debugger's address space.
The other way to utilize FishHook is to use custom hooks. You should write the your "fake" APIs in DLLs, and pass the DLL and a process-id to FishHook. FishHook will inject your DLL and replace the target API with yours. Now you can do whatever you want. Note that your "fake" APIs will run in the hooked process's address space.
Some may want to hook the child-process created by a hooked process, which is a common way to build a sandbox or a monitor program. FishHook provides built-in "fake" APIs which will automatically hook the newly created process launched by a hooked program.
Create an x86 Win32 console program.
#include "stdafx.h"
#include "../FishHook32/exports.h"
#include <iostream>
#include <string>
using namespace std;
long __stdcall CallBackProc(SharedInfo* psInfo)
{
WCHAR* pathname;
WCHAR* keyname;
char* pstr;
switch(psInfo->type )
{
case FILTER_CREATE_PROCESS_PRE:
cout<<"CreateProcess @pid "<<psInfo->pid<<endl;
printf("str1: %ws\n",(wchar_t*)psInfo->data.strd.str1);
printf("str2: %ws\n",(wchar_t*)psInfo->data.strd.str2);
return 1; //change to 0 if you don't want to allow creating process
break;
case FILTER_CREATE_PROCESS_POST:
cout<<"CreateProcess @pid "<<psInfo->pid<<endl;
printf("str1: %ws\n",(wchar_t*)psInfo->data.strd.str1);
printf("str2: %ws\n",(wchar_t*)psInfo->data.strd.str2);
printf("New pid= %d\n" ,psInfo->data.intArray[253]);
return 1;
break;
default:
cout<<"???"<<endl;
}
return 1;
}
int _tmain(int argc, _TCHAR* argv[])
{
InitFishHook();
FishHookTypes id[]={HOOK_CreateProcessInternalW,HOOK_AicLaunchAdminProcess};
PROCESS_INFORMATION info;
STARTUPINFO si;
memset(&si,0,sizeof(si));
si.cb=sizeof(si);
si.wShowWindow =SW_SHOW;
si.dwFlags=STARTF_USESHOWWINDOW;
WCHAR path[]=L"cmd";
CreateProcess(NULL,path,NULL,NULL,0,CREATE_SUSPENDED|CREATE_NEW_CONSOLE,NULL,NULL,&si,&info);
printf("RET=%d",SetIATHookByAPC(info.hProcess,(HANDLE)info.dwProcessId,CallBackProc,id,2));
ResumeThread(info.hThread);
system("pause");
return 0;
}
This program writes a filter "CallBackProc" and then creates and hook a "cmd" procress. You should first call InitFishHook() before calling any FishHook APIs. If the cmd process creates a new process, CallBackProc is called and you can see some outputs in our debugger console. Note that all the processes created by the cmd process are hooked too.
Now we write a custom hook on Windows API MessageBoxW. First create a dll and add the following code.
extern "C" __declspec(dllimport) int (__stdcall *oldMessageBoxW)( HWND hWnd, LPWSTR lpText, LPCWSTR lpCaption,UINT uType);
int __stdcall myMessageBoxW( HWND hWnd, LPWSTR lpText, LPCWSTR lpCaption,UINT uType)
{
return oldMessageBoxW(hWnd,L"Hello",L"World",uType);
}
We wrote a "fake" MessageBoxW which changed the behavior of the API. Also, in the first line of code, we exported a variable "oldMessageBoxW" which holds the true address of "MessageBoxW". Compile the DLL into x64 and x86 version, "myhook.dll" and "myhook64.dll". We can find the exported name of myMessageBoxW in x86 DLL is changed to "_myMessageBoxW@16". In x64 version DLL, the exported name is unchanged, "myMessageBoxW".
We then write the debugger code. In a 32-bit program:
int _tmain(int argc, _TCHAR* argv[])
{
InitFishHook();
FishHookTypes id[]={HOOK_CreateProcessInternalW,HOOK_AicLaunchAdminProcess};
PROCESS_INFORMATION info;
STARTUPINFO si;
memset(&si,0,sizeof(si));
si.cb=sizeof(si);
si.wShowWindow =SW_SHOW;
si.dwFlags=STARTF_USESHOWWINDOW;
WCHAR path[]=L"cmd";
CreateProcess(NULL,path,NULL,NULL,0,CREATE_SUSPENDED|CREATE_NEW_CONSOLE,NULL,NULL,&si,&info);
SetCustomHook("MessageBoxW","user32.dll","_myMessageBoxW@16","C:\\path\\to\\myhook.dll","oldMessageBoxW",0);
SetCustomHook("MessageBoxW","user32.dll","myMessageBoxW","C:\\path\\to\\myhook64.dll","oldMessageBoxW",1);
printf("RET=%d",SetIATHookByAPC(info.hProcess,(HANDLE)info.dwProcessId,NULL,id,2));
ResumeThread(info.hThread);
system("pause");
return 0;
}
This program creates and hooks a "cmd" process. All child process created by it are also hooked. In all these processes, the API of MessageBoxW has been replaced by myMessageBoxW, no matter 32-bit or 64-bit the process is. You can try "regsvr32" in cmd console to check it out. (regsvr32 will orignially show a messagebox to tell you the usage of the program, but it will be replaced by our "Hello world" message.)
See Mannual.md