Script Search

Script Searches are the single most powerful search types available/possible. They allow you to define your own logic for how values are added to the result list during searches and sub searches. MHS will perform its normal searching routines, managing buffers and the result list, but for each value it checks it will call your user-defined script function, which will determine whether the address is added to the list or not. Your script function can perform any type of evaluation you desire on the data to decide if it should be added, then return non-zero to add it to the list or 0 to move on without adding it.

 

Overview

To perform a search, MHS needs to know 3 things: how large the data is that will be stored, how to align the search, and how to decide which values to add.

The Buffer details indicate the data size. The Alignment details indicate the alignment. And the Callback Function indicates the user-defined script function that decides how addresses are added to the list.

There are two methods for deciding the size of items that can be added to the list: fixed and variable. Fixed sizes never change. For example, if we want to find DWORD (unsigned long) values, we would be searching data with the fixed size of 4 bytes. Variable sizes are used when each item added to the list might be a different size. For example, wildcard string searches match data with unpredictable sizes (see Wildcards and Regular Expressions). Be careful to note that fixed and variable searches change several aspects of the search and sub search. See The Peculiarities for details.

Alignment is straight-forward. It is simply the number of bytes to skip between each check. So alignment 1 will check bytes 0, 1, 2, 3, and 4, while alignment 4 will check bytes 0, and 4.

The following chart explains each option.

Option Meaning
Variable Data Size

The size of each item to be added to the list may change between each item.

The size of each item is defined by the return of the scipt function specified in Callback Function.

Fixed Data Size Each item to be added to the list is the same size.
Data Size If Fixed Data Size is selected, this is the size of each item that will be added to the list, in bytes.
1 Byte The search will check every byte.
2 Bytes The search will check every 2nd byte, or every even number.
4 Bytes The search will check every 4th byte.
Custom The search will check every Xth byte where X is defined by the user.
Callback Function

The user-defined script function that will be used to check each address to determine if it should be added to the result list or not.

If Variable Data Size is checked, this script function should return the actual size of the item, or 0 if the item should not be added.

If Fixed Data Size is checked, this script function can return any non-zero number to indicate the value should be added to the result list, or 0 if the item should not be added.

If the supplied script function does not exist in the System script set, the search will fail.

 

The script function has the format: INT [function]( LPVOID lpvAddress, LPVOID lpvBuffer, INT iSize )

lpvAddress is the address of the value being checked (often not needed).

lpvBuffer is a pointer to the actual data to be checked.

iSize is the size of the buffer to which lpvBuffer points.

Result-Display Function

The user-defined script function that will be used to decode each value in the result list for display in the Found Address List.

This is always optional.

If no function is supplied, or the supplied function is invalid, the system decodes the values in the result list as a hex string.

 

The script function has the format: VOID [function]( LPVOID lpvAddress, LPVOID lpvBuffer, DWORD dwLength, CHAR * pcReturn, INT iMaxLength )

lpvAddress is the address of the value being decoded/displayed.

lpvBuffer is a pointer to the actual data being decoded.

dwLength is the length of the buffer to which lpvBuffer points.

pcReturn is a pointer to the buffer that will be displayed on the screen. It is the job of this function to fill this buffer with text that describes the item being decoded (held in lpvBuffer).

iMaxLength is the length of the character array in pcReturn. Never write past the end of the buffer. Always us SNPrintF() to avoid writing past the end of the buffer.

Setup Function

This specified a user-defined scipt function that itself will provide the search details.

 

The script function has the format: VOID [function]( INT * piDataSize, INT * piAlign, CHAR ** ppcCallback, CHAR ** ppcDecoder )

piDataSize points to the integer that is to be filled with the size of the items that will be added to the result list. Specify 0 for none. Example: (*piDataSize) = sizeof( DWORD );

piAlign points to the integer that is to be filled with the alignment of the search. For example: (*piAlign) = 4;

ppcCallback specifies the script function used to decide which addresses will be added to the result list. For example: (*ppcCallback) = "MultiRangeSearch";

ppcDecoder specifies the script function used to decode the added values for display in the Found Address List. For example: (*ppcDecoder) = "MultiRangeDecoder";

Basic

Indicates a basic set of options will be used for simplicity.

Advanced Indicates all normal options are available for full customization.
Programmatic

Indicates that the search details will be filled by the function in Setup Function rather than by the data in the dialog.

The purpose of this is to allow advanced programmers to easily organize groups of search settings. It also allows the programmer to define the size of the data type accurately using sizeof() rather than typing it manually.

From The starting address of the search.
To The ending address of the search.

 

Not So Hard

Script Searches seem extremely advanced at first, and they are, but they can also be very basic and easy to use even for beginners. The Script Search dialog is broken into 3 parts to allow people of all skill levels to use them.

The first level is Basic. Here you only need to worry about 2 fields, and one that is optional.

The size of the search is fixed, and you get to decide how large each value is that will be added to the result list. For example, if you want to find unsigned short values, their size is 2. If you want to find unsigned long values, their size is 4.

The second field is the Callback Function, which is required. This is a function you must write in the script to decide when addresses should be added to the result list. If this function is not supplied or is not defined, the search will fail.

The third field is optional. If you wish to write a script function that will decode your data when it is displayed in the Found Address List, you can specify that function in the Result-Display Function edit.

 

In Advanced mode, all options are available. Each option is covered above.

 

In Programmatic mode, you are only required to specify a setup function you have written in the script.

This is for advanced users who want to keep sets of related script options together, which allows organizing and cataloging them.

This is an example of a setup function:

VOID MultiRangeSearchSetup( INT * piDataSize,
        INT * piAlign,
        CHAR ** ppcCallback,
        CHAR ** ppcDecoder ) {
    (*piDataSize) = sizeof( DWORD );
    (*piAlign) = 4;
    (*ppcCallback) = "MultiRangeSearch";
    (*ppcDecoder) = "MultiRangeDecoder";
}

 

Sub Searching

You can also use scripts to filter results from the result list, using any criteria for the filter you desire.

After a Script Search, press Ctrl-Shift-D or press the Sub Search button to display the Script Sub Search dialog.

The Callback Function specifies the function used to check items during the sub search.

This function has the following format:

INT [function]( LPVOID lpvAddress, LPVOID lpvCur, DWORD dwCurSize, LPVOID lpvOld, DWORD dwOldSize )

lpvAddress is the address of the item. This is rarely used during the check.

lpvCur is a pointer to the item as it is at the time of the search. dwCurSize indicates the size of the data here (see The Peculiarities for exceptions) .

lpvOld is a pointer to the item as it was during the last search (or sub search) (see The Peculiarities for exceptions). dwOldSize is the size of the item as it was during the last search (or sub search), which will be the same as dwCurSize if the data size is fixed (see The Peculiarities).

The function returns any non-zero number to indicate that the item should remain in the result list, or 0 to indicate that the item should be removed.

 

The Peculiarities

There are certain operational aspects that change depending on the data-type size method. The following chart explains them all.

  Variable Fixed
Internal Storage Method

The size of the item (determined by the return of the Callback Function during the search) is stored instead of a copy of the item itself.

This means all items in the list consume 4 bytes, regardless of how large the actual returned item is.

The result list leaves space for each item to be copied into it. This means larger items consume more space than smaller ones, and a copy of each item is saved.
Callback Return Value

The return value from the function specified in the Callback Function field determines the size of the data that will be added to the result list.

A return of 0 indicates the item will not be added to the list.

The Callback Function can return any non-zero value to indicate the item should be added to the list.

A return of 0 indicates the item will not be added to the list.

Sub Searching

Because a copy of the item is not stored, it is not possible to perform sub searches that compare the item state with its previous state. For example, you can not perform Same, Different, Increased, or Decreased comparisons.

The lpvCur and lpvOld values in the sub search script function will always be the same.

dwCurSize will be the size of the current buffer and dwOldSize will be the size of the item.

Because a copy of each item is stored, you can perform comparisons between the items in their current states and their previous states. For example, you can perform Same, Different, Increased, and Decreased comparisons.

lpvCur holds the item in its current form while lpvOld holds the item in its pervious form (at the time of the last search or sub search).

dwCurSize and dwOldSize will always be the same, because the item will be the same size from its last state to its current state.

The most common type of searching involves fixed data sizes, which allows checking how items have changed since the last search (or sub search) was performed, and using this to decide if the items should remain in the list.

 

Shell Code To Get You Started

This is an example set of code to allow you to easily get started making script searches.

const DWORD SIZE_OF_ITEM = sizeof( DWORD );
INT UserSearch( LPVOID lpvAddress, LPVOID lpvBuffer, INT iSize ) {
    // Cast to a standard DWORD value.  iSize  is always equal to SIZE_OF_ITEM here.
    DWORD dwThis = *(DWORD *)lpvBuffer;
 
    // Add your checks here to determine if the DWORD value should be added
    //  to the list.
 
    return 0;   // Value not added.
}
 
VOID UserDecoder( LPVOID lpvAddress, LPVOID lpvBuffer, DWORD dwLength, CHAR * pcReturn, INT iMaxLength ) {
    // Display a DWORD in decimal and hex.
    DWORD dwValue = *(DWORD *)lpvBuffer;
 
    SNPrintF( pcReturn, iMaxLength, "%u (%.8X)", dwValue , dwValue );
}
 
VOID UserSearchSetup( INT * piDataSize, INT * piAlign, CHAR ** ppcCallback, CHAR ** ppcDecoder ) {
    (*piDataSize) = SIZE_OF_ITEM;
    (*piAlign) = 4;
    (*ppcCallback) = "UserSearch";
    (*ppcDecoder) = "UserDecoder";
}
 
INT UserSubSearch( LPVOID lpvAddress, LPVOID lpvCur, DWORD dwCurSize, LPVOID lpvOld, DWORD dwOldSize ) {
    return 0;   // Item removed from the list.
}

Copy and paste this into your scripts and modify as desired. If you copy it into a new file, be sure to add it to the System scripts with Ctrl-D.

Be sure to recompile.

After opening the Script Search dialog, check Programmatic and enter UserSearchSetup into the Setup Function field. Finally, when you wish to perform a Sub Search, enter UserSubSearch.

Copyright © 2006 Shawn (L. Spiro) Wilcoxen