Script Events

Each script set has event-handler functions that can be defined to handle certain tool-specific events. To handle an event, you must simply create the correct function for that event. At the appropriate time, MHS will then call your script function to handle any events you have decided to handle. This system is similar to handling events in mIRC, for example, an ON TEXT event.

Defining a function with incorrect parameter lists will not cause system-related problems, however you may end up with garbage data in your script functions.

 

System

SYSTEM

Event Function Event Description Example
VOID On_StartUp( HWND hWnd ) Create this function to perform script actions immediately after MHS has loaded upon initializing.

VOID On_StartUp( HWND hWnd ) {
    Warning( "Starting: %.8X", hWnd );
}

After MHS has loaded, this will display a message box and display the hWnd of MHS’s main window.

VOID On_ShutDown( HWND hWnd ) Create this function to perform script actions immediately before MHS shuts down.

VOID On_ShutDown( HWND hWnd ) {
    Warning( "Closing: %.8X", hWnd );
}

When MHS is closing, a message box will appear and display the hWnd of MHS’s main window.

VOID On_OpenProcess( HANDLE hHandle, DWORD dwProcess )

Create this function to perform script actions after a process has been loaded. It is not required that you store hHandle and dwProcess to globals in your script because you can access these at any time using the correct API functions.

This is considered a global event. You can check the process name and perform per-process operations, however it is recommended that you use the per-process event On_Open_* (below).

This is called immediately before On_Open_*.

VOID On_OpenProcess( HANDLE hHandle, DWORD dwProcess ) {
    Warning( "Opening: %.8X %.8X", hHandle, dwProcess );
}

Upon opening a process, this will display a message box with the handle and process ID.

VOID On_Open_*( HANDLE hHandle, DWORD dwProcess )

This function is exactly like the above, except that the asterick is the name of the process being opened. This allows you to easily perform per-process operations when loading.

To create this function for a specific process, replace the asterick with the name of the process (including the extension, which is usually .EXE) in all upper-case characters. Replace any non-alphanumeric characters with underscores.

For example, to handle this event specifically for Minesweeper, you would write a function called On_Open_WINMINE_EXE( HANDLE hHandle, DWORD dwProcess ).

To handle this even for a process named L. Spiro Rocks.exe, write a function called On_Open_L__SPIRO_ROCKS_EXE( HANDLE hHandle, DWORD dwProcess ).

This is called immediately after On_OpenProcess.

VOID On_Open_WINMINE_EXE() {
    PrintF( "Opened Minesweeper." );
}

This prints a message when Minesweeper is opened in MHS.

Notice that the parameters are not required. This is true for all script-event functions.

VOID On_Close_*()

This function is called when a process is closed. As with the above function, the asterick is to be replaced by the process name in upper-case and with non-alphanumeric characters replaced with underscores.

For example, to handle this event specifically for Minesweeper, you would write a function called On_Close_WINMINE_EXE().

This is called directly before On_CloseProcess.

VOID On_Close_WINMINE_EXE() {
    PrintF( "Closed Minesweeper." );
}

This prints a message when MHS detaches from Minesweeper.

VOID On_CloseProcess()

Create this function to perform script actions after a process has been closed. Because processes can close unexpectedly, there is no event before processes close.

This is considered a global event and should not be used to perform events related to a specific process. To handle the closing of any specific process, handle the the On_Close_* event above.

VOID On_CloseProcess() {
    Warning( "Closing" );
}

After a process is closed, this will display a message box warning.

VOID On_HK_#( DWORD dw1, DWORD dw2 )

You can create functions that will be used with hotkeys (Tools/Options menu item).

The hash is to be replaced by a number of your choice. For example, On_HK_0 or On_HK_100.

After creating the function, you can go to the hotkeys options dialog and create a new key, assigning it functionScriptFunction”.

Set Parm 1 to the number you used in the function. For example, On_HK_0 needs Parm 1 to be 0, and On_HK_100 needs Parm 1 to be 100.

Enter any two numbers you wish for Parm 2 and Parm 3; these will be passed to the script function as dw1 and dw2.

Select any key combination you wish to activate the hotkey.

The hotkey options dialog should look something like this.

Here we have set Parm 1 to 0, indicating that the function On_HK_0 will be called when we press Ctrl-1.

These hotkey events are considered global events, and should not necessarily be used to initiate per-process operations.

 

VOID On_HK_0( DWORD dw1, DWORD dw2 ) {
    Warning( "Pressed hotkey with parms %u and %u.", dw1, dw2 );
}

Using the image to the left as our hotkey setup, pressing Ctrl-1 would call this function, which would show a message box displaying the two parameters passed to it by Parm 2 and Parm 3 in the dialog.

Remember that the hotkeys will not work until the Options dialog is closed, so you must save and close the dialog before trying to test the key.

Also remember to compile your scripts again, if you have added new On_HK_#functions.

VOID On_HK_*_#( DWORD dw1, DWORD dw2 )

This is exactly like the above, except that the asterick is replaced with the name of the current process, in all upper-case characters and with non-alphanumeric characters replaced with underscores.

This allows a single hotkey to perform different operations (or to perform nothing at all) based on the current process that is opened.

Select the CurProcScriptFunction hotkey function to use this event.

For example, if we assign Parm 1 to 2, then write script functions On_HK_WINMINE_EXE_2() and On_HK_DOOM3_EXE_2(), the hotkey will call one of these functions based off which process is currently opened. If neither process is opened, no script function is called at all. This organization allows the same hotkey to easily change its functionality on a per-process basis.

In all cases, if no process is opened, no script function is called.

VOID On_HK_WINMINE_EXE_0( DWORD dw1, DWORD dw2 ) {
    Warning( "Pressed hotkey with parms %u and %u while attached to Minesweeper.", dw1, dw2 );
}

This function would only be called if the current process is Minesweeper.

DEBUGGING

Event Function Event Description Example
BOOL On_BeginDebug( DWORD dwProcId, HANDLE hProc, DWORD dwThreadId, HANDLE hThread, BOOL bOpenedForDebug )

Create this function to perform script actions after the Debugger has attached to a process. This is called after the process loads its modules.

This function is called when the debugger attaches to the process, regardless of whether the process was already running or if it was opened for debugging.

Returning TRUE from this function causes the process to be paused. If the process was opened for debug, you can perform actions in MHS on the process before it has a chance to initialize. Returning FALSE, or not creating this function at all, will leave it up to the user to decide if the process will be paused when the user begins debugging from the Open for Debug window.

If the process has been paused, either by the result of this function or by a breakpoint, the process can be resumed by pressing F9 in the Disassembler. Open the Disassembler by pressing Ctrl-D (in the main MHS window) or by selecting the Tools/Disassembler menu item.

The bOpenedForDebug parameter allows your script to know if the process is has just been opened and has not yet initialized itself, or if it was already open before the Debugger was attached. This can sometimes be a very important factor in deciding what actions to take.

This is considered a global event and should not be used to handle per-process operations. To handle per-process operations, see On_BeginDebug_* below.

If either this function or On_BeginDebug_* returns TRUE, the process will be paused.

BOOL On_BeginDebug( DWORD dwProcId, HANDLE hProc, DWORD dwThreadId, HANDLE hThread, BOOL bOpenedForDebug ) {
    _TCHAR szBuffer[MAX_PATH];
    PrintF( "Debugging: %s (%s).",
        GetCurProcessName(),
        GetCurProcessPath( szBuffer ) );
    return TRUE;
}

 

This prints the process name and path, then returns TRUE so that the process will pause.

BOOL On_BeginDebug_*( DWORD dwProcId, HANDLE hProc, DWORD dwThreadId, HANDLE hThread, BOOL bOpenedForDebug )

This is exactly the same as above, except that the asterisk is replaced by the process name in upper-case and with non-alphanumeric characters replaced with underscores.

To create this function for a specific process, replace the asterick with the name of the process (including the extension, which is usually .EXE) in all upper-case characters. Replace any non-alphanumeric characters with underscores.

This allows you to more easily perform per-process operations when debugging begins.

For example, to handle the beginning of debugging Doom® 3, write a script function called On_BeginDebug_DOOM3_EXE().

This is called immediately after On_BeginDebug. If either this function or On_BeginDebug returns TRUE, the process will be paused.

BOOL On_BeginDebug_WINMINE_EXE( DWORD dwProcId, HANDLE hProc, DWORD dwThreadId, HANDLE hThread, BOOL bOpenedForDebug ) {
    _TCHAR szBuffer[MAX_PATH];
    PrintF( "Debugging Minesweeper." );
    return TRUE;
}

Here we do not need to check the name of the process, as we already know this function will only be called when debugging Minesweeper.

This allows us to more easily pause only certain processes and let others run.

VOID On_EndDebug_*()

This is exactly the same as On_EndDebug (below), except that the asterisk is replaced by the process name in upper-case and with non-alphanumeric characters replaced with underscores.

To create this function for a specific process, replace the asterick with the name of the process (including the extension, which is usually .EXE) in all upper-case characters. Replace any non-alphanumeric characters with underscores.

This allows you to more easily perform per-process operations when debugging ends.

For example, to handle the ending of debugging Doom® 3, write a script function called On_EndDebug_DOOM3_EXE().

This is called immediately before On_EndDebug.

VOID On_EndDebug_WINMINE_EXE() {
    PrintF( "Debugging of Minesweeper ended." );
}

This is called when Minesweeper is no longer being debugged.

VOID On_EndDebug()

MHS calls this function when the Debugger detaches from the process, after calling On_EndDebug_* above.

MHS detaches from the process due to any of 4 reasons:

#1: At the user’s request.

#2: The process closes (expectedly or not).

#3: MHS closes (expectedly).

#4: The process (or another process) employs a kernel-mode method for forcibly removing debuggers.

VOID On_EndDebug() {
    PrintF( "Debugging ended." );
}

This prints a message when debugging has ended.

VOID On_DBG_CreateThread(HANDLE hProc, DWORD dwProcId, DWORD dwThreadId)

This function is called when the target process creates a thread while the Debugger is attached. dwThreadId contains the ID of the new thread.

This is called immediately before On_DBG_CreateThread_*.

VOID On_DBG_CreateThread(HANDLE hProc, DWORD dwProcId, DWORD dwThreadId) {
    PrintF( "New thread created with ID %u.", dwThreadId );
}

This prints a message when a new thread is created in the target process while the Debugger is active.

VOID On_DBG_CreateThread_*(HANDLE hProc, DWORD dwProcId, DWORD dwThreadId)

This is the same as On_DBG_CreateThread above except that the asterisk is replaced by the process name in upper-case and with non-alphanumeric characters replaced with underscores.

To create this function for a specific process, replace the asterick with the name of the process (including the extension, which is usually .EXE) in all upper-case characters. Replace any non-alphanumeric characters with underscores.

This allows you to easily write a per-process function to handle thread creations.

This is called immediately after On_DBG_CreateThread.

VOID On_DBG_CreateThread_WINMINE_EXE(HANDLE hProc, DWORD dwProcId, DWORD dwThreadId) {
    PrintF( "Minesweeper created a new thread." );
}

This prints a message when Minesweeper creates a new thread while being debugged.

VOID On_DBG_ExitThread_*(HANDLE hProc, DWORD dwProcId, DWORD dwThreadId)

This is the same as On_DBG_CreateThread_* above except that the function is called when a thread exits.

This is called immediately before On_DBG_ExitThread.

VOID On_DBG_ExitThread_WINMINE_EXE(HANDLE hProc, DWORD dwProcId, DWORD dwThreadId) {
    PrintF( "A thread exited in Minesweeper." );
}

This prints a message when a thread in Minesweeper exits while being debugged.

VOID On_DBG_ExitThread(HANDLE hProc, DWORD dwProcId, DWORD dwThreadId)

This is the same as On_DBG_CreateThread above except that it is called when a thread exits.

This is called immediately after On_DBG_ExitThread_*.

VOID On_DBG_ExitThread(HANDLE hProc, DWORD dwProcId, DWORD dwThreadId) {
    PrintF( "Thread %u exited.", dwThreadId );
}

This prints a message when a thread exits in the target process while the Debugger is active.

VOID On_DBG_LoadDll(HANDLE hProc, DWORD dwProcId, CHAR * pcDllPath)

This is called when the target process loads a DLL while being debugged.

pcName indicates the full path of the module being loaded.

This is called immediately before On_DBG_*_Load_^.

VOID On_DBG_LoadDll(HANDLE hProc, DWORD dwProcId, CHAR * pcDllPath) {
    PrintF( "Dll %s loaded.", pcDllPath );
}

This prints a message when a DLL is loaded into the target process while the Debugger is active.

VOID On_DBG_*_LoadDll(HANDLE hProc, DWORD dwProcId, CHAR * pcDllPath)

This is the same as On_DBG_LoadDll above except that the asterisk is replaced by the process names in upper-case and with non-alphanumeric characters replaced with underscores.

To create this function for a specific process, replace the asterick with the name of the process (including the extension, which is usually .EXE) in all upper-case characters. Replace any non-alphanumeric characters with underscores.

This is called immediately after On_DBG_LoadDll.

VOID On_DBG_DOOM3_EXE_LoadDll(HANDLE hProc, DWORD dwProcId, CHAR * pcDllPath) {
    PrintF( "Doom3® has loaded module %s.", pcDllPath );
}

This prints a message when Doom3® loads any module.

VOID On_DBG_*_Load_^(HANDLE hProc, DWORD dwProcId, CHAR * pcDllPath)

This is the same as On_DBG_*_LoadDll above except that the asterisk and caret are replaced by the process and module names (respectively) in upper-case and with non-alphanumeric characters replaced with underscores.

To create this function for a specific process and module, replace the asterick with the name of the process (including the extension, which is usually .EXE) in all upper-case characters. Replace any non-alphanumeric characters with underscores. Follow the same method and replace the caret (^) with the DLL name.

This is called immediately after On_DBG_*_LoadDll.

VOID On_DBG_DOOM3_EXE_Load_GAMEX86_DLL(HANDLE hProc, DWORD dwProcId, CHAR * pcDllPath) {
    PrintF( "Doom3® has loaded its main module." );
}

This prints a message when Doom3® loads gamex86.dll.

VOID On_DBG_UnloadDll_*(HANDLE hProc, DWORD dwProcId, LPVOID lpvAddress)

This is the same as On_DBG_*_Load_^, except that it is called when a DLL is unloaded and the unloaded DLL is not part of the function name (that information is not available).

This is called immediately before On_DBG_UnloadDll.

VOID On_DBG_UnloadDll_DOOM3_EXE(HANDLE hProc, DWORD dwProcId, LPVOID lpvAddress) {
    PrintF( "Doom3® has unloaded a module from %.8X." );
}

This prints a message when Doom3® unloads a module.

VOID On_DBG_UnloadDll(HANDLE hProc, DWORD dwProcId, LPVOID lpvAddress)

This is the same as On_DBG_LoadDll except that it is called when a DLL is unloaded. The address of the DLL is passed rather than the path.

This is called immediately after On_DBG_UnloadDll_*.

VOID On_DBG_UnloadDll(HANDLE hProc, DWORD dwProcId, LPVOID lpvAddress) {
    PrintF( "The target process has unloaded a module from %.8X." );
}

This prints a message when the target process unloads a module.

Miscellaneous

Event Function Event Description Example
VOID On_GetKernel32Dll( CHAR * pcRet )

The system calls this script function when it needs to know the name of your Kernel32.dll file. If this function is not created then the default Kernel32.dll file name is used.

MHS uses this file for some of its anti-anti-cheat protections and this allows to you ensure a clean non-modified version is available. You only need to take advantage of this feature when any malicious software such as nProtect Game Guard try to overwrite your system files with those that have patches in them already. In that case you can put clean versions in another directory, change their names, and then supply these scripts to allow MHS access to the clean files.

Note that the copy file returned by this function should match the real file as closely as possible, with only the hooks removed.

VOID On_GetKernel32Dll( CHAR * pcRet ) {
    StrCpy( pcRet, "MyKern.dll" );
}

Whenever MHS needs to use Kernel32.dll, it will instead look for MyKern.dll.

VOID On_GetNtDllDll( CHAR * pcRet )

The system calls this script function when it needs to know the name of your NtDll.dll file. If this function is not created then the default NtDll.dll file name is used.

MHS uses this file for some of its anti-anti-cheat protections and this allows to you ensure a clean non-modified version is available. You only need to take advantage of this feature when any malicious software such as nProtect Game Guard try to overwrite your system files with those that have patches in them already. In that case you can put clean versions in another directory, change their names, and then supply these scripts to allow MHS access to the clean files.

Note that the copy file returned by this function should match the real file as closely as possible, with only the hooks removed.

VOID On_GetKernel32Dll( CHAR * pcRet ) {
    StrCpy( pcRet, "MyNtDll.dll" );
}

Whenever MHS needs to use NtDll.dll, it will instead look for MyKern.dll.

VOID On_GetUser32Dll( CHAR * pcRet )

The system calls this script function when it needs to know the name of your User32.dll file. If this function is not created then the default User32.dll file name is used.

MHS uses this file for some of its anti-anti-cheat protections and this allows to you ensure a clean non-modified version is available. You only need to take advantage of this feature when any malicious software such as nProtect Game Guard try to overwrite your system files with those that have patches in them already. In that case you can put clean versions in another directory, change their names, and then supply these scripts to allow MHS access to the clean files.

Note that the copy file returned by this function should match the real file as closely as possible, with only the hooks removed.

VOID On_GetUser32Dll( CHAR * pcRet ) {
    StrCpy( pcRet, "MyUser.dll" );
}

Whenever MHS needs to use User32.dll, it will instead look for MyUser.dll.

VOID On_GetGdi32Dll( CHAR * pcRet )

The system calls this script function when it needs to know the name of your Gdi32.dll file. If this function is not created then the default Gdi32.dll file name is used.

MHS uses this file for some of its anti-anti-cheat protections and this allows to you ensure a clean non-modified version is available. You only need to take advantage of this feature when any malicious software such as nProtect Game Guard try to overwrite your system files with those that have patches in them already. In that case you can put clean versions in another directory, change their names, and then supply these scripts to allow MHS access to the clean files.

Note that the copy file returned by this function should match the real file as closely as possible, with only the hooks removed.

VOID On_GetGdi32Dll( CHAR * pcRet ) {
    StrCpy( pcRet, "MyGdi.dll" );
}

Whenever MHS needs to use Gdi32.dll, it will instead look for MyGdi.dll.

VOID On_GetNtOsKrnlExe( CHAR * pcRet )

The system calls this script function when it needs to know the name of your NtOsKrnl.exe file. If this function is not created then the default NtOsKrnl.exe file name is used.

MHS uses this file for some of its anti-anti-cheat protections and this allows to you ensure a clean non-modified version is available. You only need to take advantage of this feature when any malicious software such as nProtect Game Guard try to overwrite your system files with those that have patches in them already. In that case you can put clean versions in another directory, change their names, and then supply these scripts to allow MHS access to the clean files.

Note that the copy file returned by this function should match the real file as closely as possible, with only the hooks removed.

VOID On_GetNtOsKrnlExe( CHAR * pcRet ) {
    StrCpy( pcRet, "MyNtOsKrnl.exe" );
}

Whenever MHS needs to use NtOsKrnl.exe, it will instead look for MyNtOsKrnl.exe.

VOID On_GetHalDll( CHAR * pcRet )

The system calls this script function when it needs to know the name of your Hal.dll file. If this function is not created then the default Hal.dll file name is used.

MHS uses this file for some of its anti-anti-cheat protections and this allows to you ensure a clean non-modified version is available. You only need to take advantage of this feature when any malicious software such as nProtect Game Guard try to overwrite your system files with those that have patches in them already. In that case you can put clean versions in another directory, change their names, and then supply these scripts to allow MHS access to the clean files.

Note that the copy file returned by this function should match the real file as closely as possible, with only the hooks removed.

VOID On_GetHalDll( CHAR * pcRet ) {
    StrCpy( pcRet, "MyHal.dll" );
}

Whenever MHS needs to use Hal.dll, it will instead look for MyHal.dll.

VOID On_GetWin32kSys( CHAR * pcRet )

The system calls this script function when it needs to know the name of your Win32k.sys file. If this function is not created then the default Win32k.sys file name is used.

MHS uses this file for some of its anti-anti-cheat protections and this allows to you ensure a clean non-modified version is available. You only need to take advantage of this feature when any malicious software such as nProtect Game Guard try to overwrite your system files with those that have patches in them already. In that case you can put clean versions in another directory, change their names, and then supply these scripts to allow MHS access to the clean files.

Note that the copy file returned by this function should match the real file as closely as possible, with only the hooks removed.

VOID On_GetWin32kSys( CHAR * pcRet ) {
    StrCpy( pcRet, "MyWin32k.sys" );
}

Whenever MHS needs to use Win32k.sys, it will instead look for MyWin32k.sys.

VOID On_GetWinDir( CHAR * pcRet )

Write this function to overwrite the Windows® directory MHS searches for some files related to anti-hacking. Since malicious software such as nProtect Game Guard often corrupt system files, this allows you to copy clean versions of those files to a new directory and let MHS use those.

VOID On_GetWinDir( CHAR * pcRet ) {
    StrCpy( pcRet, "D:\\MyWinDir" );
}

Causes MHS to search D:\MyWinDir instead of C:\Windows.

VOID On_GetSysDir( CHAR * pcRet )

Write this function to overwrite the Windows® system directory MHS searches for some files related to anti-hacking. Since malicious software such as nProtect Game Guard often corrupt system files, this allows you to copy clean versions of those files to a new directory and let MHS use those.

VOID On_GetSysDir( CHAR * pcRet ) {
    StrCpy( pcRet, "D:\\MyWinDir\\Sys" );
}

Causes MHS to search D:\MyWinDir\Sys instead of C:\Windows\System32.

VOID On_ProcessIsOpening( LPVOID lpvEproc, DWORD dwParentId, DWORD dwProcId )

This is called when any process opens while MHS is running, regardless of the state of MHS. This is called on a separate thread from the main thread, so if you plan to have this function interact with any other functions that may be running on the main thread from a Hotkey or Breakpoint, be sure to use the synchronized keyword appropriately.

This gives the script the chance to perform any work on the arbitrary process that it pleases before it has time to fully initialize.

VOID On_ProcessIsOpening( LPVOID lpvEproc, DWORD dwParentId, DWORD dwProcId ) {
    PrintF( "Process %u (EPROCESS: %.X8) opening." );
}

Prints a message showing the process ID and EPROCESS of each process that opens while MHS is running.

VOID On_ProcessIsClosing( LPVOID lpvEproc, DWORD dwParentId, DWORD dwProcId )

This is called when any process closes while MHS is running, regardless of the state of MHS. This is called on a separate thread from the main thread, so if you plan to have this function interact with any other functions that may be running on the main thread from a Hotkey or Breakpoint, be sure to use the synchronized keyword appropriately.

VOID On_ProcessIsClosing( LPVOID lpvEproc, DWORD dwParentId, DWORD dwProcId ) {
    PrintF( "Process %u (EPROCESS: %.X8) closing." );
}

Prints a message showing the process ID and EPROCESS of each process that closes while MHS is running.

Copyright © 2006 Shawn (L. Spiro) Wilcoxen