Download [UPDATE] Hooking Class v0.78

Hexui Undetected CSGO Cheats Sinkicheat PUBG Cheat

Solaire

Respected Hacker
Dank Tier VIP
Dec 15, 2013
1,051
16,353
62
UPDATE:
I've added a trampoline detouring class, as well as separated them into different classes using inheritance. There is a small memory leak when you unhook and rehook with a trampoline detour. It allocates the memory for the trampoline (Generally around 5-9 bytes) every time, but doesn't free it. It's very minor, and won't be noticed unless you unhook/rehook incredibly fast.

I just quickly wrote up a hooking class, and thought I'd share it.
Be warned, it is most likely detected (Though I honestly have no clue)

Big thanks to @Superspinne for helping me understand how the hooking function works, as well as helping me solve the issues with the trampoline hook.
Credits to lowHertz for the Mid-Function hooking tutorial.

C++:
#include <Windows.h>
#include <tlhelp32.h>
#include <Psapi.h>

class Hook
{
protected:
    void * m_hkAddy;
    void * m_hkFnctAddy;
    BYTE * m_restoreBytes;
    DWORD m_len;
    bool m_isHooked;
public:
    Hook(void * hkAddy, void * hkFnctAddy, DWORD len);
    ~Hook();

    virtual bool Restore() = 0;
    virtual void Unload() = 0;
};

class trpHook : public Hook
{
    virtual bool Restore();
public:
    trpHook(void * hkAddy, void * hkFnctAddy, DWORD len);
    void * trpHook::ToggleHook();
    void * CreateDetour();
    virtual void Unload();
};

class plainHook : public Hook
{
    virtual bool Restore();
    bool CreateDetour();
    DWORD FindPattern();
    char * m_pattern;
    char * m_module;
    char * m_mask;    
public:
    plainHook(char * module, void * hkFnctAddy, char * pattern, char * mask, DWORD len);
    plainHook(void * hkAddy, void * hkFnctAddy, DWORD len);
    virtual void ToggleHook();
    virtual void Unload();
};

C++:
#include <iostream>
#include <algorithm>
#include <vector>
#include "Hook.h"

Hook::Hook(void * hkAddy, void * hkFnctAddy, DWORD len)
{
    m_isHooked = false;
    m_hkAddy = hkAddy;
    m_len = len;
    m_hkFnctAddy = hkFnctAddy;
    m_restoreBytes = new BYTE[200];
}

Hook::~Hook()
{
    delete m_restoreBytes;
    Unload();
}

trpHook::trpHook(void * hkAddy, void * hkFnctAddy, DWORD len) :
Hook(hkAddy, hkFnctAddy, len)
{ }

void * trpHook::CreateDetour()
{
    void * pTrp;
    DWORD oldProtect, Bkup, relativeAddy;

    if (m_hkAddy == NULL || m_len < 5)
    {
        return nullptr;
    }

    if (!VirtualProtect(m_hkAddy, m_len, PAGE_EXECUTE_READWRITE, &oldProtect))
    {
        return nullptr;
    }

    //Allocate a spot of memory for pTrp
    pTrp = VirtualAlloc(0, m_len + 5, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    //Copy the bytes into the beginning of pTrp for when we're returning.
    memcpy(pTrp, m_hkAddy, m_len);
    //Copy the bytes into m_restoreBytes for when we want to undo the hook
    memcpy(m_restoreBytes, m_hkAddy, m_len);
    //NOP out the start of the function
    memset(m_hkAddy, 0x90, m_len);

    /*PLACING OUR JUMP AT THE ORIGINAL FUNCTION*/

    //Get relative address, place jmp, put relative address on next 4 bytes
    relativeAddy = ((DWORD)m_hkFnctAddy - (DWORD)m_hkAddy) - 5;
    *(BYTE*)m_hkAddy = 0xE9;
    *(DWORD*)((DWORD)m_hkAddy + 0x1) = relativeAddy;

    /*PLACING OUR JUMP AT THE TRAMPOLINE*/

    //Get relative address, place jmp, put relative address on next 4 bytes
    DWORD relAddy = ((DWORD)m_hkAddy - (DWORD)pTrp) - 5;
    *(BYTE*)((DWORD)pTrp + m_len) = 0xE9;
    *(DWORD*)((DWORD)pTrp + m_len + 0x1) = relAddy;

    //Restore whatever protection there was
    if (!VirtualProtect(m_hkAddy, m_len, oldProtect, &Bkup))
    {
        return nullptr;
    }

    return pTrp;
}

bool trpHook::Restore()
{
    DWORD oldProtect, Bkup;

    if (m_restoreBytes == NULL)
    {
        return false;
    }

    if (!VirtualProtect(m_hkAddy, m_len, PAGE_EXECUTE_READWRITE, &oldProtect))
    {
        return false;
    }

    memcpy(m_hkAddy, m_restoreBytes, m_len);

    if (!VirtualProtect(m_hkAddy, m_len, oldProtect, &Bkup))
    {
        return false;
    }

    return true;
}

void * trpHook::ToggleHook()
{
    if (m_isHooked == false)
    {
        void * pTemp = CreateDetour();

        if (pTemp != nullptr)
        {
            m_isHooked = true;
            return pTemp;
        }

        else
        {
            MessageBoxA(NULL, "Failed to hook", "ERROR", MB_OK);
        }
    }

    else if (m_isHooked == true)
    {
        if (!Restore())
        {
            MessageBoxA(NULL, "Either failed to restore or bytes are already restored", "ERROR", MB_OK);
        }

        else
        {
            m_isHooked = false;
        }
    }
}

void trpHook::Unload()
{
    if (m_isHooked == true)
    {
        if (!Restore())
        {
            MessageBoxA(NULL, "Either failed to restore or bytes are already restored", "ERROR", MB_OK);
        }

        else
        {
            m_isHooked = false;
        }
    }
}

plainHook::plainHook(void * hkAddy, void * hkFnctAddy, DWORD len) : 
Hook(hkAddy, hkFnctAddy, len)
{ }

plainHook::plainHook(char * module, void * hkFnctAddy, char * pattern, char * mask, DWORD len) : 
m_module(module), m_pattern(pattern), m_mask(mask),
Hook((BYTE*)FindPattern(), hkFnctAddy, len)
{ }

bool plainHook::CreateDetour()
{
    DWORD oldProtect, Bkup, relativeAddy;

    if (m_hkAddy == NULL || m_len == NULL)
    {
        return false;
    }

    if (m_len < 5)
    {
        return false;
    }

    if (!VirtualProtect(m_hkAddy, m_len, PAGE_EXECUTE_READWRITE, &oldProtect))
    {
        return false;
    }

    memcpy(m_restoreBytes, m_hkAddy, m_len);
    memset(m_hkAddy, 0x90, m_len);

    //Get relative address, place jmp, put relative address on next 4 bytes
    relativeAddy = ((DWORD)m_hkFnctAddy - (DWORD)m_hkAddy) - 5;
    *(BYTE*)m_hkAddy = 0xE9;
    *(DWORD*)((DWORD)m_hkAddy + 0x1) = relativeAddy;

    if (!VirtualProtect(m_hkAddy, m_len, oldProtect, &Bkup))
    {
        return false;
    }

    return true;
}

bool plainHook::Restore()
{
    DWORD oldProtect, Bkup;

    if (m_restoreBytes == NULL)
    {
        return false;
    }

    if (!VirtualProtect(m_hkAddy, m_len, PAGE_EXECUTE_READWRITE, &oldProtect))
    {
        return false;
    }

    memcpy(m_hkAddy, m_restoreBytes, m_len);

    if (!VirtualProtect(m_hkAddy, m_len, oldProtect, &Bkup))
    {
        return false;
    }

    return true;
}

DWORD plainHook::FindPattern()
{
    //Credits to Fleep for this information
    MODULEINFO modInfo = { 0 };

    //Returns a handle to the specified module
    HMODULE hModule = GetModuleHandle(m_module);

    //Places all of the needed information in modInfo
    GetModuleInformation(GetCurrentProcess(), hModule, &modInfo, sizeof(MODULEINFO));

    //The load address of the module
    DWORD base = (DWORD)modInfo.lpBaseOfDll;
    //The size of the linear space that the module occupies, in bytes.
    DWORD size = (DWORD)modInfo.SizeOfImage;

    //Our vector of bools
    std::vector<bool> isFound(strlen(m_mask), false);
    auto iter = isFound.end();
    iter--;

    //Our pattern length for looping
    DWORD patternLength = (DWORD)strlen(m_mask);

    for (DWORD i = 0; i < size - patternLength; i++)
    {
        bool found = true;

        for (DWORD j = 0; j < patternLength; j++)
        {
            //If at any point one of them is false, found will become false
            if ((m_mask[j] == '?') || (m_pattern[j] == *(char*)(base + i + j)))
            {
                isFound[j] = true;
            }

            else
            {
                isFound[j] = false;
                break;
            }
        }

        //Check and make sure every element is true
        for (auto c : isFound)
        {
            //If not, we continue looping
            if (c == false)
            {
                found = false;
            }
        }

        if (found)
        {
            //Returns the base of the module + the offset
            return base + i;
        }
    }

    return NULL;
}

void plainHook::ToggleHook()
{
    if (m_isHooked == false)
    {
        if (!CreateDetour())
        {
            MessageBoxA(NULL, "Failed to hook", "ERROR", MB_OK);
        }

        else
        {
            m_isHooked = true;
        }
    }

    else if (m_isHooked == true)
    {
        if (!Restore())
        {
            MessageBoxA(NULL, "Either failed to restore or bytes are already restored", "ERROR", MB_OK);
        }

        else
        {
            m_isHooked = false;
        }
    }
}

void plainHook::Unload()
{
    if (m_isHooked == true)
    {
        if (!Restore())
        {
            MessageBoxA(NULL, "Either failed to restore or bytes are already restored", "ERROR", MB_OK);
        }

        else
        {
            m_isHooked = false;
        }
    }
}

Applying The Hooks
Let's say we've reversed a function, and there's an instruction we want to detour. Let's also say this is the takeDamage function form AssaultCube.
C++:
ac_client.exe+29D1D - 2B F8                 - sub edi,eax
ac_client.exe+29D1F - 29 7B 04              - sub [ebx+04],edi //Health Subtraction (jmp here)
ac_client.exe+29D22 - 8B C7                 - mov eax,edi
ac_client.exe+29D24 - 5F                    - pop edi //Where we'll return to
ac_client.exe+29D25 - 5E                    - pop esi
ac_client.exe+29D26 - 8B E5                 - mov esp,ebp
ac_client.exe+29D28 - 5D                    - pop ebp
ac_client.exe+29D29 - C2 0400               - ret 0004
First, let's write our hooked function:
C++:
//Our return point
DWORD Return = 0x429D24;

//For checking if we're the ones taking damage
DWORD * Address = (DWORD*)(0x50F4F4);
DWORD * player = *(DWORD**)Address + (0xF4 / sizeof(DWORD));

__declspec(naked) void _TakeDamageFunct()
{
    __asm
    {
        //Are we the ones taking damage?
        cmp ebx,player
        //If yes, jump to Player and take no damage
        je Player
        //The enemy takes damage
        jmp Enemy

    Enemy:
        //Enemy takes damage
        sub[ebx + 04], edi
        mov eax,edi
        //Go to the exit
        jmp Exit

    Player:
        //NOP out sub[ebx + 04],edi for good measure
        nop
        nop
        nop
        mov eax, edi
        jmp Exit

    Exit:
        //Return to the point after our original jump
        jmp [Return]
    }
}
Then, in our main thread:
C++:
DWORD WINAPI Engine(LPVOID param)
{
    //Declaring our hook
    plainHook hkTakeDamage((BYTE*)0x429D1F, (DWORD)_TakeDamageFunct, 0x5);

    while(1)
    {
        if(GetAsyncKeyState(VK_NUMPAD1) & 1)
        {
            hkTakeDamage.ToggleHook();
        }

        //Freeing the DLL
        if (GetAsyncKeyState(VK_NUMPAD9) & 1)
        {
            //Get our module
            HMODULE myModule = GetModuleHandleA("OurDLLName.dll");

            //If that succeeded
            if (myModule)
            {
                //Unload the hook if it isn't already to prevent crashes
                hktakeDamage.Unload();

                FreeLibraryAndExitThread(myModule, NULL);
            }
        }
    }
}
To use the pattern type hook, we start with our declaration of the hook object.
C++:
plainHook hktakeDamage("ac_client.exe", "\x29\x7B\x04\x8B\xC7\x5F\x5E\x8B\xE5\x5D", "xxxxxxxxxx", (DWORD)hkTakeDamage, 0x5);
The first parameter is the module that contains the address we want to find. The second is our array of bytes. The third is the mask. The fourth is our hook function (See above for when I coded that). The last is the byte length.

That's all you need to do when declaring a PatternScan hook, you can use it just like the other one by adding a toggle hotkey.

For the trpHook you do things a little bit differently:

Function we're hooking:
C++:
int testFunction(int a, int b, int c)
{
    int d = a + b;
    int e = b + c;
    int f = a + c;

    int sum = d + e + f;

    return sum;
}
Start of that function:

C++:
55                    PUSH EBP
8B EC                 MOV EBP,ESP
81 EC CC 00 00 00 SUB ESP,0CC
Our function
C++:
typedef int(*tFunct)(int, int, int);
tFunct oFunct = nullptr;
int hkFunct(int a, int b, int c)
{
    a = 50;
    b = 50;
    c = 50;

    return oFunct(a, b, c);
}
In our main thread:
C++:
DWORD WINAPI Engine(LPVOID param)
{
    DWORD Mod = (DWORD)GetModuleHandleA("hookTest.exe");
    trpHook hook((void*)(Mod + 0x15270), hkFunct, 9);
    
    while (1)
    {
        if (GetAsyncKeyState(VK_NUMPAD2) & 1)
        {
            oFunct = (tFunct)hook.ToggleHook(oFunct);
        }

        if (GetAsyncKeyState(VK_NUMPAD9) & 1)
        {
            hook.Unload();
            HMODULE myModule = GetModuleHandleA("hooking.dll");

            if (myModule)
            {
                FreeLibraryAndExitThread(myModule, NULL);
            }
        }
    }
}
Any tips, suggestions, or ideas are greatly appreciated. Also, if you find a spot where I explained something incorrectly, or you want me to explain better, please tell me!

Changelog:
v0.70
- Added normal detour, and a hook toggle
v0.78
- Added pattern scanning
- Added Unload function
v0.84
- Separated classes using inheritance
- Added trampoline detour

TODO:
- Faster pattern scanning
- Remove the small memory leak

Hooking Class v0.78
 
Last edited:

Solaire

Respected Hacker
Dank Tier VIP
Dec 15, 2013
1,051
16,353
62
I've added pattern scanning, an unloading method, and a destructor to prevent memory leaks.
 
Last edited:

Solaire

Respected Hacker
Dank Tier VIP
Dec 15, 2013
1,051
16,353
62
I've added a trampoline detouring class, as well as separated them into different classes using inheritance. There is a small memory leak when you unhook and rehook with a trampoline detour. It allocates the memory for the trampoline (Generally around 5-9 bytes) every time, but doesn't free it. It's very minor, and won't be noticed unless you unhook/rehook incredibly fast.

C++:
/*HOOK.H*/

//Had to put these here since there were issues with Psapi.h
#include <Windows.h>
#include <tlhelp32.h>
#include <Psapi.h>

class Hook
{
private:
	//Will probably use this at some point, the
	//constructors assign the type currently and that's it
	enum HookType
	{
		PATTERN,
		DIRECT
	}m_hkType;

	DWORD m_hkFnctAddy;
	DWORD m_len;
	BYTE * m_restoreBytes;
	BYTE * m_hkAddy;

	char * m_pattern;
	char * m_module;
	char * m_mask;

	DWORD FindPattern();

	bool m_isHooked;
	bool CreateDetour();
	bool Restore();
public:
	Hook(BYTE * hkAddy, DWORD hkFnctAddy, DWORD len);
	Hook(char * module, char * pattern, char * mask, DWORD hkFnctAddy, DWORD len);
	~Hook();

	void ToggleHook();
	void Unload();
};

/*HOOK.CPP*/

#include <iostream>
#include <algorithm>
#include <vector>
#include "Hook.h"

Hook::Hook(BYTE * hkAddy, DWORD hkFnctAddy, DWORD len)
{
	m_hkType = DIRECT;

	m_isHooked = false;
	m_hkAddy = hkAddy;
	m_len = len;
	m_hkFnctAddy = hkFnctAddy;
	m_restoreBytes = new BYTE;
}

Hook::Hook(char * module, char * pattern, char * mask, DWORD hkFnctAddy, DWORD len)
{
	m_hkType = PATTERN;

	m_isHooked = false;
	m_module = module;
	m_pattern = pattern;
	m_mask = mask;
	m_hkAddy = (BYTE*)FindPattern();
	m_hkFnctAddy = hkFnctAddy;
	m_len = len;
	m_restoreBytes = new BYTE;
}

Hook::~Hook()
{
	delete m_restoreBytes;
	Unload();
}

DWORD Hook::FindPattern()
{
	//Credits to Fleep for this information
	MODULEINFO modInfo = { 0 };

	//Returns a handle to the specified module
	HMODULE hModule = GetModuleHandle(m_module);

	//Places all of the needed information in modInfo
	GetModuleInformation(GetCurrentProcess(), hModule, &modInfo, sizeof(MODULEINFO));

	//The load address of the module
	DWORD base = (DWORD)modInfo.lpBaseOfDll;
	//The size of the linear space that the module occupies, in bytes.
	DWORD size = (DWORD)modInfo.SizeOfImage;

	//Our vector of bools
	std::vector<bool> isFound(strlen(m_mask), false);
	auto iter = isFound.end();
	iter--;

	//Our pattern length for looping
	DWORD patternLength = (DWORD)strlen(m_mask);

	for (DWORD i = 0; i < size - patternLength; i++)
	{
		bool found = true;

		for (DWORD j = 0; j < patternLength; j++)
		{
			//If at any point one of them is false, found will become false
			if ((m_mask[j] == '?') || (m_pattern[j] == *(char*)(base + i + j)))
			{
				isFound[j] = true;
			}

			else
			{
				isFound[j] = false;
			}
		}

		//Check and make sure every element is true
		for (auto c : isFound)
		{
			//If not, we continue looping
			if (c == false)
			{
				found = false;
			}
		}

		if (found)
		{
			//Returns the base of the module + the offset
			return base + i;
		}
	}

	return NULL;
}

bool Hook::CreateDetour()
{
	DWORD oldProtect, Bkup, relativeAddy;

	if (m_hkAddy == NULL || m_len == NULL)
	{
		return false;
	}

	if (m_len < 5)
	{
		return false;
	}

	if (!VirtualProtect(m_hkAddy, m_len, PAGE_EXECUTE_READWRITE, &oldProtect))
	{
		return false;
	}

	memcpy(m_restoreBytes, m_hkAddy, m_len);

	relativeAddy = ((DWORD)m_hkFnctAddy - (DWORD)m_hkAddy) - 5;

	*m_hkAddy = 0xE9;

	*((DWORD *)(m_hkAddy + 0x1)) = relativeAddy;

	for (unsigned int i = 0x5; i < m_len; i++)
	{
		*(m_hkAddy + i) = 0x90;
	}

	if (!VirtualProtect(m_hkAddy, m_len, oldProtect, &Bkup))
	{
		return false;
	}

	return true;
}

bool Hook::Restore()
{
	DWORD oldProtect, Bkup;

	if (m_restoreBytes == NULL)
	{
		return false;
	}

	if (!VirtualProtect(m_hkAddy, m_len, PAGE_EXECUTE_READWRITE, &oldProtect))
	{
		return false;
	}

	memcpy(m_hkAddy, m_restoreBytes, m_len);

	if (!VirtualProtect(m_hkAddy, m_len, oldProtect, &Bkup))
	{
		return false;
	}

	return true;
}

void Hook::ToggleHook()
{
	if (m_isHooked == false)
	{
		if (!CreateDetour())
		{
			MessageBoxA(NULL, "Failed to hook", "ERROR", MB_OK);
		}

		else
		{
			m_isHooked = true;
		}
	}

	else if (m_isHooked == true)
	{
		if (!Restore())
		{
			MessageBoxA(NULL, "Either failed to restore or bytes are already restored", "ERROR", MB_OK);
		}

		else
		{
			m_isHooked = false;
		}
	}
}

void Hook::Unload()
{
	if (m_isHooked == true)
	{
		if (!Restore())
		{
			MessageBoxA(NULL, "Either failed to restore or bytes are already restored", "ERROR", MB_OK);
		}

		else
		{
			m_isHooked = false;
		}
	}
}
 
Last edited by a moderator:
Attention! Before you post:

Read the How to Ask Questions Guide
99% of questions are answered in the Beginner's Guide, do it before asking a question.

No Hack Requests. Post in the correct section.  Search the forum first. Read the rules.

How to make a good post:

  • Fill out the form correctly
  • Tell us the game name & coding language
  • Post everything we need to know to help you
  • Ask specific questions, be descriptive
  • Post errors, line numbers & screenshots
  • Post code snippets using code tags
  • If it's a large project, zip it up and attach it

If you do not comply, your post may be deleted.  We want to help, please make a good post and we will do our best to help you.

Community Mods