C++ HOOK Global API (MessageBox for example)

Keywords: Linker

Global Hook does not necessarily need to use Dll, such as global mouse hook, keyboard hook do not need Dll, but to hook the API, you need the assistance of Dll, put Dll's code directly below: (Note that MFC DLL is used here)

// Test_Dll(mfc).cpp: An initialization routine for defining DLLs.
//

#include "stdafx.h"
#include "Test_Dll(mfc).h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

#pragma region My code

#define  UM_WNDTITLE WM_USER+100	// Custom messages (message identifiers for private window classes)

// Global shared variables (shared data between multiple processes)
#pragma data_seg(".Share")
	HWND g_hWnd = NULL;				// Main window handle
	HHOOK hhk = NULL;				// Mouse hook handle
	HINSTANCE hInst = NULL;			// This dll instance handle
#pragma data_seg()
#pragma comment(linker, "/section:.Share,rws")


// global variable
HANDLE hProcess=NULL;				// process handle
BOOL bIsInjected=FALSE;				// Is injection complete
typedef int (WINAPI *MsgBoxA)(HWND hWnd,LPCSTR lpText,LPCSTR lpCaption,UINT uType);			// Declare an alias MsgBox A
typedef int (WINAPI *MsgBoxW)(HWND hWnd,LPCWSTR lpText,LPCWSTR lpCaption,UINT uType);		// Declare an alias MsgBox W
MsgBoxA oldMsgBoxA=NULL;			// Save the address of the original function
MsgBoxW oldMsgBoxW=NULL;			// Save the address of the original function
FARPROC pfMsgBoxA=NULL;				// A remote pointer to the address of the original function
FARPROC pfMsgBoxW=NULL;				// A remote pointer to the address of the original function
BYTE OldCodeA[5];					// Old system API entry code
BYTE NewCodeA[5];					// API code to jump (jmp xxx)
BYTE OldCodeW[5];					// Old system API entry code
BYTE NewCodeW[5];					// API code to jump (jmp xxx)
int WINAPI MyMessageBoxA(HWND hWnd,LPCSTR lpText,LPCSTR lpCaption,UINT uType);				// Our own MesageBox A function
int WINAPI MyMessageBoxW(HWND hWnd,LPCWSTR lpText,LPCWSTR lpCaption,UINT uType);			// Our own MesageBox W function


// Open the hook (modify the first 5 bytes of the API)
void HookOn() 
{ 
	// Verify that the process handle is empty
	ASSERT(hProcess!=NULL);

	DWORD dwTemp = 0,		// Modified memory protection properties
		dwOldProtect,		// Previous memory protection properties
		dwRet = 0,			// Memory write success flag, 0 unsuccessful, 1 successful
		dwWrite;			// Number of bytes written to process memory
 
	// Change virtual memory protection
	VirtualProtectEx(		
		hProcess,			// process handle
		pfMsgBoxA,			// A pointer to the address of the protected area
		5,					// The byte size of the area to be changed
		PAGE_READWRITE,		// Memory protection type, PAGE_READWRITE: Readable and Writable
		&dwOldProtect		// Receive the original memory protection properties
		); 

	// Determine whether memory was successfully written
	dwRet = WriteProcessMemory(
		hProcess,			// process handle
		pfMsgBoxA,			// A pointer to a write address
		NewCodeA,			// Pointer to the buffer where the written content is stored
		5,					// Number of bytes written
		&dwWrite			// The number of bytes received to the process
		);
	if (0==dwRet||0==dwWrite){
		TRACE("NewCodeA Write failure");	// Logging information
	}	

	// Recovery of memory protection status
	VirtualProtectEx(hProcess,pfMsgBoxA,5,dwOldProtect,&dwTemp);

	// Ibid., operate on the remaining Message Box W
	VirtualProtectEx(hProcess,pfMsgBoxW,5,PAGE_READWRITE,&dwOldProtect); 
	dwRet=WriteProcessMemory(hProcess,pfMsgBoxW,NewCodeW,5,&dwWrite);
	if (0==dwRet||0==dwWrite){TRACE("NewCodeW Write failure");}
	VirtualProtectEx(hProcess,pfMsgBoxW,5,dwOldProtect,&dwTemp);
}

// Close the hook (modify the first 5 bytes of the API)
void HookOff()
{ 
	// Verify that the process handle is empty
	ASSERT(hProcess!=NULL);

	DWORD dwTemp = 0,			// Modified memory protection properties
		dwOldProtect = 0,		// Previous memory protection properties
		dwRet = 0,				// Memory write success flag, 0 unsuccessful, 1 successful
		dwWrite = 0;			// Number of bytes written to process memory

	// Change virtual memory protection
	VirtualProtectEx(
		hProcess,				// process handle
		pfMsgBoxA,				// A pointer to the address of the protected area
		5,						// The byte size of the area to be changed
		PAGE_READWRITE,			// Memory protection type, PAGE_READWRITE: Readable and Writable
		&dwOldProtect			// Receive the original memory protection properties
		); 

	dwRet = WriteProcessMemory(
		hProcess,				// process handle
		pfMsgBoxA,				// A pointer to a write address
		OldCodeA,				// Pointer to the buffer where the written content is stored
		5,						// Number of bytes written
		&dwWrite				// The number of bytes received to the process
		); 
	if (0==dwRet||0==dwWrite){
		TRACE("OldCodeA Write failure");	// Logging information
	}

	// Recovery of memory protection status
	VirtualProtectEx(hProcess,pfMsgBoxA,5,dwOldProtect,&dwTemp); 

	// Ibid., operate on the remaining Message Box W
	VirtualProtectEx(hProcess,pfMsgBoxW,5,PAGE_READWRITE,&dwOldProtect); 
	WriteProcessMemory(hProcess,pfMsgBoxW,OldCodeW,5,&dwWrite); 
	if (0==dwRet||0==dwWrite){TRACE("OldCodeW Write failure");}
	VirtualProtectEx(hProcess,pfMsgBoxW,5,dwOldProtect,&dwTemp);  
}

// Code injection
void Inject()
{
	// If not injected
	if (!bIsInjected){ 

		//Guarantee that only one call is made
		bIsInjected=TRUE;

		// Get the function address
		HMODULE hmod=::LoadLibrary(_T("User32.dll"));
		oldMsgBoxA=(MsgBoxA)::GetProcAddress(hmod,"MessageBoxA");	// The original Message Box A address
		pfMsgBoxA=(FARPROC)oldMsgBoxA;								// A pointer to the original Message Box A address
		oldMsgBoxW=(MsgBoxW)::GetProcAddress(hmod,"MessageBoxW");	// The original Message Box W address
		pfMsgBoxW=(FARPROC)oldMsgBoxW;								// A pointer to the original Message Box W address
  
		// If the pointer is empty, the run will end
		if (pfMsgBoxA==NULL){MessageBox(NULL,_T("cannot get MessageBoxA()"),_T("error"),0);return;}
		if (pfMsgBoxW==NULL){MessageBox(NULL,_T("cannot get MessageBoxW()"),_T("error"),0);return;}

		// Save the entry code in the original API to OldCodeA[], OldCodeW[]
		_asm 
		{ 
			lea edi,OldCodeA		; hold OldCodeA Address to edi
			mov esi,pfMsgBoxA		; hold MessageBoxA Address to esi
			cld						; Directional Sign Position Reset
			movsd					; Duplication of twins
			movsb					; Copy byte
		}
		_asm 
		{ 
			lea edi,OldCodeW		; Operate in the same way MessageBoxW
			mov esi,pfMsgBoxW
			cld 
			movsd 
			movsb 
		}

		// Change the first byte of the original API to jmp
		NewCodeA[0]=0xe9;			
		NewCodeW[0]=0xe9;			

		// Compute the address to follow after jmp
		_asm 
		{ 
			lea eax,MyMessageBoxA				; take MyMessageBoxA Address to eax
			mov ebx,pfMsgBoxA					; take MessageBoxA Address to ebx
			sub eax,ebx							; Calculation jmp The address to follow
			sub eax,5 
			mov dword ptr [NewCodeA+1],eax 
		} 
		_asm 
		{ 
			lea eax,MyMessageBoxW				; Operate in the same way MessageBoxW
			mov ebx,pfMsgBoxW
			sub eax,ebx 
			sub eax,5 
			mov dword ptr [NewCodeW+1],eax 
		} 
 
		// Start Hook
		HookOn(); 
	}
}

// Fake Message Box A
int WINAPI MyMessageBoxA(HWND hWnd,LPCSTR lpText,LPCSTR lpCaption,UINT uType)
{
	int nRet = 0;		

	// Restore Hook first, or it will cause a dead cycle
	HookOff();

	// Verify that MessageBox A fails (failure returns 0)
	nRet = ::MessageBoxA(hWnd,"Hook MessageBoxA",lpCaption,uType);
	//NRet=:: MessageBox A (hWnd, lpText, lpCaption, uType); // Call the original function (if you want to operate in a dark box)

	// HookOn again, or only once
	HookOn();

	return nRet;
}

// Fake Message Box W
int WINAPI MyMessageBoxW(HWND hWnd,LPCWSTR lpText,LPCWSTR lpCaption,UINT uType)
{
	int nRet = 0;

	// Restore Hook first, or it will cause a dead cycle
	HookOff();

	// Verify whether Message Box W failed (failure returns 0)
	nRet = ::MessageBoxW(hWnd,_T("Hook MessageBoxW"),lpCaption,uType);
	//NRet=:: Message Box W (hWnd, lpText, lpCaption, uType); // Call the original function (if you want to operate in a dark box)

	// HookOn again, or only once
	HookOn();

	return nRet;
}
#pragma endregion

#pragma region Ignore
//
//TODO: If this DLL is dynamically linked relative to MFC DLL,
//		Any calls exported from this DLL
//		MFC functions must add AFX_MANAGE_STATE macros
//		The front of the function.
//
//		For example:
//
//		extern "C" BOOL PASCAL EXPORT ExportedFunction()
//		{
//			AFX_MANAGE_STATE(AfxGetStaticModuleState());
//			// Here is the body of ordinary functions.
//		}
//
//		This macro precedes any MFC call
//		It's important to appear in every function. This means
//		It must be the first statement in a function
//		Appear, even before all object variables are declared,
//		This is because their constructors may generate MFC
//		DLL call.
//
//		For other details,
//		Refer to MFC Technical Notes 33 and 58.
//

// CTest_DllmfcApp

BEGIN_MESSAGE_MAP(CTest_DllmfcApp, CWinApp)
END_MESSAGE_MAP()


// Construction of CTest_DllmfcApp

CTest_DllmfcApp::CTest_DllmfcApp()
{
	// TODO: Add the construction code here.
	// Place all the important initializations in InitInstance
}


// The only CTest_DllmfcApp object

CTest_DllmfcApp theApp;
#pragma endregion

// Program entry
BOOL CTest_DllmfcApp::InitInstance()
{
	CWinApp::InitInstance();

	#pragma region My code
	
	// Get the dll instance handle itself
	hInst = AfxGetInstanceHandle();

	// Get the process ID for calling dll
	DWORD dwPid = ::GetCurrentProcessId();
	
	// Get the process handle for calling dll
	hProcess = ::OpenProcess(PROCESS_ALL_ACCESS,0,dwPid);

	// Start injection
	Inject();

	#pragma endregion

	return TRUE;
}

// Program export
int CTest_DllmfcApp::ExitInstance()
{
	// TODO: Adding dedicated code and/or calling base classes here

	#pragma region My code
	
	// Restore API s for other processes 
	HookOff();

	#pragma endregion

	return CWinApp::ExitInstance();
}

#pragma region My code

// Mouse hook callback
LRESULT CALLBACK MouseProc(
						   int nCode,      // Hook code
						   WPARAM wParam,  // Message Identifier
						   LPARAM lParam   // Coordinates of cursors
						   ){
	if (nCode==HC_ACTION){
		
		// Send the window handle where the hook is located to the main program
		::SendMessage(
			g_hWnd,											// Window handle for receiving messages
			UM_WNDTITLE,									// Messages sent
			wParam,											// Additional message information
			(LPARAM)(((PMOUSEHOOKSTRUCT)lParam)->hwnd)		// Additional message information, here is the window handle of the window in which the mouse is located
			);
		/* 
		typedef struct tagMOUSEHOOKSTRUCT { // Mouse event information structure passed to WH_MOUSE
			POINT     pt;					// xy coordinates of cursors
			HWND      hwnd;					// Window handle corresponding to cursor
			UINT      wHitTestCode;			// Whether to hit or not
			ULONG_PTR dwExtraInfo;			// Message correlation
		} MOUSEHOOKSTRUCT, *PMOUSEHOOKSTRUCT, *LPMOUSEHOOKSTRUCT;
		*/
	}

	// Tell the message to the next hook
	return CallNextHookEx(
		hhk,		// Hook handle, mouse hook here
		nCode,		
		wParam,
		lParam
		);
}

// Installation hook
BOOL WINAPI StartHook(HWND hWnd)
{
	// Get the main window handle where the mouse is located
	g_hWnd = hWnd;
	
	// Get the mouse hook handle
	hhk = ::SetWindowsHookEx(
		WH_MOUSE,		// Hook Type
		MouseProc,		// Pointer to callback function
		hInst,			// dll handle, here is the instance handle for this dll
		NULL			// Represents all threads associated with your desktop
		);
	
	// Determine whether SetWindows HookEx was successfully executed
	if (hhk==NULL){return FALSE;} 
	else{return TRUE;}
}

// Unloading hook
VOID WINAPI StopHook()
{
	// Here only the API itself is restored.
	HookOff();

	
	if (hhk!=NULL)
	{
		// UnHook mouse hook
		UnhookWindowsHookEx(hhk);

		// Uninstall dll
		FreeLibrary(hInst);
	}
}

#pragma endregion

Because there's no code folding here, so it's not very intuitive. I'll put a folded picture here:
Add an export function to the. def file: (usually just below the. cpp file)

; Test_Dll(mfc).def : statement DLL Module parameters.

LIBRARY

EXPORTS
StartHook
StopHook
    ; This can be an explicit export

Then start writing the code that calls Dll: (MFC project is used here, because the global mouse hook needs m_hWnd in CWnd)
Because I think most of the global HOOK needs to hide themselves and then silently execute, which conflicts with the style of window interaction mode of MFC, so I hide the window of MFC here. Specific methods can be referred to: https://blog.csdn.net/Simon798/article/details/99063945

HINSTANCE g_hInst;		// Global variable, same as HMODULE

void CTest_MFCDlg::HOOK()
{
	// TODO: Add control notification handler code here

	// Loading DLL (depending on the actual path of your dll, relative path is recommended)
	g_hInst = ::LoadLibrary(_T("E:\\MyFiles\\Programing\\vs2012\\MyPrograms\\Test_Dll(mfc)\\Debug\\Test_Dll(mfc).dll"));

	// Determine whether loading is successful
	if (g_hInst==NULL){AfxMessageBox(_T("Load dll fail"));}

	// alias declarations
	typedef BOOL (WINAPI* StartHook)(HWND hWnd);

	// Call the export function StartHook in dll
	StartHook Hook = (StartHook)::GetProcAddress(g_hInst,"StartHook");

	// Determine whether the derived function was successfully invoked
	if (Hook==NULL){AfxMessageBox(_T("StartHook Call failed"));}

	// Start Hook
	Hook(m_hWnd);
}
void CTest_MFCDlg::UNHOOK()
{
	// TODO: Add control notification handler code here
	
	// Check whether UnHook is required
	if (g_hInst==NULL){return;}

	// alias declarations
	typedef VOID (WINAPI* StopHook)();

	// Call the derived function StopHook in dll
	StopHook UnHook = (StopHook)::GetProcAddress(g_hInst,"StopHook");
	
	// Determine whether the derived function was successfully invoked
	if (UnHook==NULL){AfxMessageBox(_T("StopHook Call failed"));return;}

	// Start UnHook
	UnHook();

	// Uninstall dll
	FreeLibrary(g_hInst);

	// Reset g_hInst to facilitate the next UnHook judgment
	g_hInst=NULL;
}

// Form creation events
int CTest_MFCDlg::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	if (CDialogEx::OnCreate(lpCreateStruct) == -1)
		return -1;

	// TODO: Add your dedicated creation code here
	
	// When the program is opened, the HOOK() function is executed. UNHOOK is not demonstrated here.
	HOOK();
}

void CTest_MFCDlg::OnWindowPosChanging(WINDOWPOS* lpwndpos)
{
	CDialogEx::OnWindowPosChanging(lpwndpos);

	// TODO: Add message handler code here

	// Hide form
	lpwndpos->flags &= ~SWP_SHOWWINDOW;
    CDialog::OnWindowPosChanging(lpwndpos);
}

Design sketch:

Posted by abduljan on Sun, 06 Oct 2019 00:08:33 -0700