Giter Club home page Giter Club logo

mhook's Introduction

Mhook - a Windows API hooking library Build status

Introduction

This library was created as a free alternative to Microsoft Detours. It is originally developed by Marton Anka and currently is supported and developed by Apriorit.

How to use

// Include a header
#include <mhook-lib/mhook.h>

// Save the original function
typedef ULONG (WINAPI* _NtClose)(IN HANDLE Handle);
_NtClose TrueNtClose = (_NtClose)GetProcAddress(GetModuleHandle(L"ntdll"), "NtClose");

// Declare your function that will be handle a hook:
ULONG WINAPI HookNtClose(HANDLE hHandle) 
{
    printf("***** Call to NtClose(0x%p)\n", hHandle);
    return TrueNtClose(hHandle);
}

//...

// Set the hook 
BOOL isHookSet = Mhook_SetHook((PVOID*)&TrueNtClose, HookNtClose);

//...

// After finishing using the hook โ€“ remove it
Mhook_Unhook((PVOID*)&TrueNtClose);

You can also set a bunch of hooks in one call:

HOOK_INFO hooks[] =
{
    { (PVOID*)&TrueNtOpenProcess, HookNtOpenProcess },
    { (PVOID*)&TrueSelectObject, HookSelectobject },
    { (PVOID*)&Truegetaddrinfo, Hookgetaddrinfo },
    { (PVOID*)&TrueHeapAlloc, HookHeapAlloc },
    { (PVOID*)&TrueNtClose, HookNtClose }
};

int numberOfSetHooks = Mhook_SetHookEx(hooks, 5);
    
//...

// Removing hooks
int numberOfRemovedHooks = Mhook_UnhookEx(hooks, 5);

That way of setting multiple hooks is also much better in performance.

License

Mhook is freely distributed under an MIT license.

Version history

Version 2.5.1 (30 March 2018)

  • Fix #1: VirtualAlloc hooking reports anomaly
  • New #2: Add integration to vcpkg package
  • New #3: Add AppVeyor CI
  • Fix #4: Add ability to hook functions with call in first 5 bytes

Version 2.5 (20 Oct 2017)

  • 10x performance boost
  • CMake build system
  • Change tabs to spaces
  • Ability to hook functions with je/jne in the first 5 bytes
  • Fix hook recursion
  • Other fixes

Version 2.4 (05 Mar 2014, the last from the original author)

  • A number of improvements: hot patch location (mov edi, edi) handling, support for REX-prefixed EIP-relative jumps on x64, removal of compile-time limit on the number of hooks

Version 2.3 (15 Jan 2012)

  • A bugfix that allows hooking more API functions

Version 2.2 (27 Jun 2008)

  • Support for instructions using IP-relative addressing

Version 2.1 (15 Oct 2007)

  • Fixes

Version 2.0 (08 Jul 2007)

  • Built-in disassembler

Version 1.0 (24 Jun 2007)

  • Original release

Acknowledgements

Mhook contains a disassembler that is a stripped-down version of the excellent tDisasm package by Matt Conover. Thank you Matt! tDisasm comes with a BSD-style license and re-releasig a derivative of it under the MIT license has been confirmed to be OK by its author.

Alexandr Filenkov submitted bugfixes in Sept-2007. Michael Syrovatsky submitted fixes for IP-relative addressing in Jun-2008. Andrey Kubyshev submitted a bugfix in Jul-2011 and Jan-2013. John McDonald enabled unlimited hooks. Kasper Brandt provided a fix for hot patch function prologues.

mhook's People

Contributors

convery avatar dreamlayers avatar elizagamedev avatar grivus avatar ip-gpu avatar kalash- avatar martona avatar poizan42 avatar renenyffenegger avatar sergiusthebest avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

mhook's Issues

Hooking CopyFileA/W

Hi,
I build a sample (AppInitHook) and added global hooks for CopyFileA/W. Also I created simple application that call CopyFileA/W. Dll installs those hooks but the hooks aren't called when I run the application which call CopyFileA/W.

I didn't found special limitations for Kernel32.dll. Are there some limitations?

#include "mhook/mhook-lib/mhook.h"
#include <fstream>

typedef BOOL(WINAPI* _CopyFileA)(LPCSTR lpExistingFileName, LPCSTR lpNewFileName, BOOL bFailIfExists);
typedef BOOL(WINAPI* _CopyFileW)(LPCWSTR lpExistingFileName, LPCWSTR lpNewFileName, BOOL bFailIfExists);
typedef BOOL(WINAPI* _CopyFileExA)(LPCSTR lpExistingFileName, LPCSTR lpNewFileName, LPPROGRESS_ROUTINE lpProgressRoutine, LPVOID lpData, LPBOOL pbCancel, DWORD dwCopyFlags);
//////////////////////////////////////////////////////////////////////////
// Defines and typedefs

#define STATUS_SUCCESS  ((NTSTATUS)0x00000000L)

typedef struct _MY_SYSTEM_PROCESS_INFORMATION 
{
    ULONG                   NextEntryOffset;
    ULONG                   NumberOfThreads;
    LARGE_INTEGER           Reserved[3];
    LARGE_INTEGER           CreateTime;
    LARGE_INTEGER           UserTime;
    LARGE_INTEGER           KernelTime;
    UNICODE_STRING          ImageName;
    ULONG                   BasePriority;
    HANDLE                  ProcessId;
    HANDLE                  InheritedFromProcessId;
} MY_SYSTEM_PROCESS_INFORMATION, *PMY_SYSTEM_PROCESS_INFORMATION;

typedef NTSTATUS (WINAPI *PNT_QUERY_SYSTEM_INFORMATION)(
    __in       SYSTEM_INFORMATION_CLASS SystemInformationClass,
    __inout    PVOID SystemInformation,
    __in       ULONG SystemInformationLength,
    __out_opt  PULONG ReturnLength
    );

//////////////////////////////////////////////////////////////////////////
// Original function

PNT_QUERY_SYSTEM_INFORMATION OriginalNtQuerySystemInformation = 
    (PNT_QUERY_SYSTEM_INFORMATION)::GetProcAddress(::GetModuleHandle(L"ntdll"), "NtQuerySystemInformation");

_CopyFileA origCPAHandlerPtr = (_CopyFileA)::GetProcAddress(::GetModuleHandle(L"Kernel32"), "CopyFileA");
_CopyFileW origCPWHandlerPtr = (_CopyFileW)::GetProcAddress(::GetModuleHandle(L"Kernel32"), "CopyFileW");
_CopyFileExA origCopyFileExAPtr = (_CopyFileExA)::GetProcAddress(::GetModuleHandle(L"Kernel32"), "CopyFileExA");
//////////////////////////////////////////////////////////////////////////
// Hooked function

BOOL WINAPI HookCopyFileExA(
	LPCSTR             lpExistingFileName,
	LPCSTR             lpNewFileName,
	LPPROGRESS_ROUTINE lpProgressRoutine,
	LPVOID             lpData,
	LPBOOL             pbCancel,
	DWORD              dwCopyFlags
)
{
	std::ofstream("c:/tmp/test.txt", std::ios::app) << "CopyFileA" << std::endl;

	if (origCopyFileExAPtr == nullptr)
		return FALSE;

	return origCopyFileExAPtr(lpExistingFileName, lpNewFileName, lpProgressRoutine, lpData, pbCancel, dwCopyFlags);
}

BOOL WINAPI HookCopyFileA(LPCSTR lpExistingFileName, LPCSTR lpNewFileName, BOOL bFailIfExists)
{

	//	CallHandler::logCall("CopyFileA", lpExistingFileName, lpNewFileName);
	std::ofstream("c:/tmp/test.txt", std::ios::app) << "CopyFileA" << std::endl;

	if (origCPAHandlerPtr == nullptr)
		return FALSE;

	return origCPAHandlerPtr(lpExistingFileName, lpNewFileName, bFailIfExists);
}

BOOL WINAPI HookCopyFileW(LPCWSTR lpExistingFileName, LPCWSTR lpNewFileName, BOOL bFailIfExists)
{
	//getHandlers().getOriginFunctionPtr(L"Kernel32.dll", "CopyFileW");

//	CallHandler::logCall("CopyFileW", lpExistingFileName, lpNewFileName);

	std::ofstream("c:/tmp/test.txt", std::ios::app) << "CopyFileW" << std::endl;

	if (origCPWHandlerPtr == nullptr)
		return FALSE;

	return origCPWHandlerPtr(lpExistingFileName, lpNewFileName, bFailIfExists);
}

NTSTATUS WINAPI HookedNtQuerySystemInformation(
    __in       SYSTEM_INFORMATION_CLASS SystemInformationClass,
    __inout    PVOID                    SystemInformation,
    __in       ULONG                    SystemInformationLength,
    __out_opt  PULONG                   ReturnLength
    )
{
	std::ofstream("c:/tmp/test.txt", std::ios::app) << "NtQuerySystemInformation" << std::endl;

    NTSTATUS status = OriginalNtQuerySystemInformation(SystemInformationClass,
        SystemInformation,
        SystemInformationLength,
        ReturnLength);

    if (SystemProcessInformation == SystemInformationClass && STATUS_SUCCESS == status)
    {
        //
        // Loop through the list of processes
        //

        PMY_SYSTEM_PROCESS_INFORMATION pCurrent = NULL;
        PMY_SYSTEM_PROCESS_INFORMATION pNext    = (PMY_SYSTEM_PROCESS_INFORMATION)SystemInformation;
        
        do
        {
            pCurrent = pNext;
            pNext    = (PMY_SYSTEM_PROCESS_INFORMATION)((PUCHAR)pCurrent + pCurrent->NextEntryOffset);

            if (!wcsncmp(pNext->ImageName.Buffer, L"Calculator.exe", pNext->ImageName.Length))
            {
                if (0 == pNext->NextEntryOffset)
                {
                    pCurrent->NextEntryOffset = 0;
                }
                else
                {
                    pCurrent->NextEntryOffset += pNext->NextEntryOffset;
                }

                pNext = pCurrent;
            }            
        } 
        while(pCurrent->NextEntryOffset != 0);
    }

    return status;
}

//////////////////////////////////////////////////////////////////////////
// Entry point

BOOL WINAPI DllMain(
    __in HINSTANCE  hInstance,
    __in DWORD      Reason,
    __in LPVOID     Reserved
    )
{        
	std::ofstream("c:/tmp/test.txt", std::ios::app) << "DllMain" << std::endl;

    switch (Reason)
    {
    case DLL_PROCESS_ATTACH:
		std::ofstream("c:/tmp/test.txt", std::ios::app) << "DLL_PROCESS_ATTACH " << Mhook_SetHook((PVOID*)&origCPAHandlerPtr, HookCopyFileA) << std::endl;
		std::ofstream("c:/tmp/test.txt", std::ios::app) << "DLL_PROCESS_ATTACH " << Mhook_SetHook((PVOID*)&origCPWHandlerPtr, HookCopyFileW) << std::endl;
//        Mhook_SetHook((PVOID*)&OriginalNtQuerySystemInformation, HookedNtQuerySystemInformation);
		std::ofstream("c:/tmp/test.txt", std::ios::app) << "DLL_PROCESS_ATTACH " <<  Mhook_SetHook((PVOID*)&origCopyFileExAPtr, HookCopyFileExA) << std::endl;
        break;

    case DLL_PROCESS_DETACH:
		std::ofstream("c:/tmp/test.txt", std::ios::app) << "DLL_PROCESS_DETACH" << std::endl;
//        Mhook_Unhook((PVOID*)&OriginalNtQuerySystemInformation);
		Mhook_Unhook((PVOID*)&origCPAHandlerPtr);
		Mhook_Unhook((PVOID*)&origCPWHandlerPtr);
		Mhook_Unhook((PVOID*)&origCopyFileExAPtr);
        break;

	default:
		std::ofstream("c:/tmp/test.txt", std::ios::app) << "def: " << Reason << std::endl;
    }

    return TRUE;
}

And simple app:

#include <iostream>
#include <Windows.h>

int main()
{
	CopyFileA("c:/tmp/1.txt", "c:/tmp/2.txt", FALSE);
	CopyFileW(L"c:/tmp/1.txt", L"c:/tmp/2.txt", FALSE);
    std::cout << "Hello World!\n"; 
}

Breaking on unsupported RIP-addressing?

mhook/mhook-lib/mhook.cpp

Lines 1004 to 1013 in 2238938

else if ( (pins->OperandCount >= 1) && (pins->Operands[0].Flags & OP_IPREL) )
{
// unsupported rip-addressing
ODPRINTF((L"mhooks: DisassembleAndSkip: found unsupported OP_IPREL on operand %d", 0));
// dump instruction bytes to the debug output
for (DWORD i=0; i<pins->Length; i++)
{
ODPRINTF((L"mhooks: DisassembleAndSkip: instr byte %2.2d: 0x%2.2x", i, pLoc[i]));
}
break;

In this, and the following two cases, the break can (and does in my tests) cause the hook installation to fail. I have personally removed them on my end and it works well. But what's the reasoning for breaking here?

Potential access NULL pointer in mhook.c

Below function in mhook.c

static BOOL GetCurrentProcessSnapshot(PVOID* snapshot, PSYSTEM_PROCESS_INFORMATION* procInfo)
{
    // get a view of the threads in the system

    if (!CreateProcessSnapshot(snapshot))
    {
        ODPRINTF((L"mhooks: can't get process snapshot!"));
        return FALSE;
    }

    DWORD pid = GetCurrentProcessId();

    *procInfo = FindProcess(*snapshot, pid);
    return TRUE;
}

In case of FindProcess return NULL but this function is still return TRUE.

Solution:

...
return NULL != *procInfo

How to work with it?

Hiya, I used mhook(2.4) today, and it worked fine but the dll I created kept getting unloaded.

Now I found your project and I'm trying to figure out how to get started with it, the getting started is pretty small and I cant understand, if I use it with AppInit hooking method and I dont get how is it suppose to load the dll hooks if there is no dll entrypoint in your example test.

Can I have an explanation? do you guys have an irc/discord channel by any chance?

2 compiler errors when compiling with MingW on Windows and suggested fixes

Hello Team,

Just an FYI in order to get this to compile in mingw on Windows I had to make the following changes to mhook.c:

Line 262:

NTSTATUS(NTAPI PZwQuerySystemInformation)(
SYSTEM_INFORMATION_CLASS SystemInformationClass,
PVOID SystemInformation,
ULONG SystemInformationLength,
PULONG ReturnLength
);

#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L)
//remove this.... static PZwQuerySystemInformation fnZwQuerySystemInformation = NULL;

//put this instead
static decltype(PZwQuerySystemInformation) *fnZwQuerySystemInformation=NULL;

Line 762:
fnZwQuerySystemInformation = (decltype(PZwQuerySystemInformation))((void)GetProcAddress(GetModuleHandle("ntdll.dll"), "ZwQuerySystemInformation"));

The error I was getting:

c:\mhook-master>g++ mhook-lib\mhook.c disasm-lib\disasm.c disasm-lib\disasm_x86.c disasm-lib\cpu.c T:/hookdll/main.cpp -o t:/hookdll/mhooktest.dll -D_WIN32_WINNT=0x0502 -w -Wfatal-errors -fpermissive -shared
mhook-lib\mhook.c:261:25: error: typedef 'PZwQuerySystemInformation' is initialized (use 'decltype' instead)
261 | typedef NTSTATUS(NTAPI* PZwQuerySystemInformation)(

c:\mhook-master>g++ mhook-lib\mhook.c disasm-lib\disasm.c disasm-lib\disasm_x86.c disasm-lib\cpu.c T:/hookdll/main.cpp -o t:/hookdll/mhooktest.dll -w -Wfatal-errors -fpermissive -shared
mhook-lib\mhook.c: In function 'BOOL CreateProcessSnapshot(void**)':
mhook-lib\mhook.c:762:115: error: cannot convert 'const wchar_t*' to 'LPCSTR' {aka 'const char*'}
762 | fnZwQuerySystemInformation = (decltype(PZwQuerySystemInformation))((void)GetProcAddress(GetModuleHandle(L"ntdll.dll")....

Compiler info:

c:\mhook-master>g++ --version
g++ (Rev10, Built by MSYS2 project) 11.2.0
Copyright (C) 2021 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Hope this helps

Best Regards,

-Avery T

Support Windows for Arm64

Windows on Arm64 is a new and growing ecosystem. Could we add support for ARM64 architecture as well?

Add AppVeyor CI

AppVeyor offers a free windows-based CI for open-source projects. Let's use it.

VirtualAlloc hooking reports anomaly

On Win10 hooking VirtualAlloc gives the following error message/ warning: [0x7FFEF0CADFE0] ANOMALY: meaningless REX prefix used

The function after the skiped jumps:
https://pastebin.com/RRaQ8JG0

This doesn't happen on Win7 (I'd have to get back to you with the dissasemble on that if you need it). The hook works but I am not sure if I can simply ignore that or I have to "fix" that in some way in the library. Can you tell me why this is bad? http://wiki.osdev.org/X86-64_Instruction_Encoding#REX_prefix This site lists it as a legacy opcode. Can you clarify this please?

so many printf logs

Hi, This is a nice hook library, but also there's so many "junk" information in code.
Such as,

if (!SuppressErrors) printf("[0x%08I64X] ANOMALY: Unexpected operand size prefix\n", VIRTUAL_ADDRESS); \

after build in Release, there's so many log out there, not pretty, Why not use NDEBUG Micro to disable in release?
And also, assert function.

Hooking at one specific address and calling the original function causes a crash.

The original function before hook at one address is like this

image

I am hooking at one specific memory address as shown in the image below

image

The hooked function successfully hooking function gets called, but while trying call back hooking function (System/Original function) it crashes. I tried to hook at different location address it works. The different address location where I successfully hooked and successfully called hooking function (System/Original function), as shown in the below images.

Before hook.

image

After hook.

image

The code for hooked function and calling the hook function is as follows.

image

Your help is much appreciated. Please help me if I am making any mistake.

Unable to hook DestroyCaret function from user32 module

I want to hook CreateCaret and DestroyCaret functions from user32.dll. Hooking CreateCaret works ok, but DestroyCaret - no.

#include <Windows.h>

typedef BOOL(WINAPI *CreateCaretFuncPtr)(
	_In_ HWND hWnd,
	_In_opt_ HBITMAP hBitmap,
	_In_ int nWidth,
	_In_ int nHeight);

typedef BOOL(WINAPI *DestroyCaretFuncPtr)(VOID);

static CreateCaretFuncPtr CreateCaretOrigFuncPtr = NULL;
static DestroyCaretFuncPtr DestroyCaretOrigFuncPtr = NULL;

static BOOL WINAPI MyCreateCaret(
	_In_ HWND hWnd,
	_In_opt_ HBITMAP hBitmap,
	_In_ int nWidth,
	_In_ int nHeight)
{
	return CreateCaretOrigFuncPtr(hWnd, hBitmap, nWidth, nHeight);
}

static BOOL WINAPI MyDestroyCaret(VOID)
{
	return DestroyCaretOrigFuncPtr();
}

In main function I have:

HMODULE user32ModuleHandle = GetModuleHandle(L"User32");
CreateCaretOrigFuncPtr = (CreateCaretFuncPtr)GetProcAddress(user32ModuleHandle, "CreateCaret");
DestroyCaretOrigFuncPtr = (DestroyCaretFuncPtr)GetProcAddress(user32ModuleHandle, "DestroyCaret");
// will be true
bool createHookSetOk = Mhook_SetHook((PVOID*)&CreateCaretOrigFuncPtr, MyCreateCaret);
// will be false
bool destroyHookSetOk = Mhook_SetHook((PVOID*)&DestroyCaretOrigFuncPtr, MyDestroyCaret);

I studied a bit how mhook works under the hood and I see that the first call to DisassembleAndSkip in Mhook_SetHookEx returns instruction length of 5 bytes for CreateCaret, but 2 bytes for DestroyCaret. ollydbg shows that CreateCaret starts with MOV EAX,1032 (5 bytes long) while DestroyCaret is

759B38C7   6A 06            PUSH 6
759B38C9   E8 1F28FFFF      CALL USER32.759A60ED
759B38CE   C3               RETN

First instruction for DestroyCaret is PUSH which takes 2 bytes. So DisassembleAndSkip returns 2 bytes and we go to else block where a check for IsJumpPresentInFirstFiveBytes takes place. IsJumpPresentInFirstFiveBytes returns false because it checks only for conditional jumps (ITYPE_BRANCHCC). So trampoline is not created.

Could please anyone comment on this situation? Is this a known behavior?

In IsJumpPresentInFirstFiveBytes I tried to add a check for ITYPE_CALL and return true for that and after that I see that my hook is working (gets called). Is there any drawback of such modification?

OS is Windows 7. Application type is x86.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.