About

MHS is a utility for searching, viewing, and modifying the RAM of other processes, and for disassembling and debugging other processes.

MHS sports the fastest and most efficient searches available, an advanced, colorful, and easy-to-use real-time Hex Editor, a Debugger with unique features, a Disassembler, and an extensive scripting language (L. Spiro Script) yielding unlimited potential.

The array of tools offered in MHS can make hacking any game easy.

Here is a compact list of tools and features:

  • Searching
    • Data-Type Searches
      • Fastest searches available.
      • Search for types char, byte, short, unsigned short, int, long, unsigned long, 64-bit integer, float, and double.
      • Search for exact values, values not equal to, ranges, greater than, less than, and unknown.
    • Group Searches
      • Find unordered sets of data.
      • Find relative lists of data.
    • String Searches
      • Find hex strings, ASCII strings, Unicode strings, Wildcard strings, and Regular-Expression strings.
      • Boyer Moore Algorithm for fast searching.
    • Pointer Searches
      • Fastest search possible.
      • Quickly find both static and dynamic pointers.
    • Script Searches
      • The most powerful searches possible.
      • You have full control over what values are found during a search.
      • Able to replicate all search types available in all software, now and forever.
    • All addresses shown after a search and without delay; no need to view “only the first 100” returns.
  • Converter
    • Convert from any type to any other type, both big and little endian.
  • RAM Watcher
    • View the RAM of the target process in real-time.
    • Multiple display types shown simultaneously.
  • Real-Time Expression Evaluator
    • Evaluates even the most complex of expressions.
    • Shows expression results in real-time; especially useful for following changing pointer locations or changing expressions.
  • Hex Editor
    • Edit files and RAM.
    • Files open instantly, regardless of size, and RAM is shown in real-time.
    • Multiple display types show you RAM and files in characters, bytes, shorts, ints, floats, doubles, and more.
    • Full undo/redo.
    • Many options and full customization.
  • Debugger
    • Breakpoint functionality can be assigned by the user, and breakpoints can call user-defined script functions for the ultimate do-what-you-want.
    • Hardware breakpoints.
    • Read/write software breakpoints (watchpoints).
    • The Debugger issues debugging events that can be handled by scripts, allowing the user to perform any and all operations he or she desires at key times during debugging.
  • Disassembler
    • Shows names of known functions.
    • Logging (to be finished).
    • Addresses of all imported/exported functions shown.
    • Auto-Hack shows you every read, write, or access to an address, and extremely advanced features will be coming soon (automatic back-tracking down to the root pointer).
    • Exlanations of ASM instructions are provided in real-time, explaining what each instruction is going to do and offering previews of the results.
    • Process threads are updated in real-time and useful information about them is displayed.
  • Injection Manager
    • Complete and feature-rich injection suite.
    • Code caves can be found automatically, defined by the user, or created.
    • Code preview shows you the code before injecting.
    • Automatically adds the JMP back to the original code and adds the overwritten code to the code cave.
    • Injections are automatically saved, and options allow to inject automatically when the process is reloaded later.
    • Automatic injections are always safe; injections are verified before being automatically injected.
  • Script Editor
    • Syntax coloring.
    • Code folding.
    • Functions listed and easily navigated.
  • Hotkeys
    • Many assignable keys and functions.
    • Two hotkey implementations in case the game blocks one or the other.
  • Stability
    • MHS is extremely stable. Currently there are no known issues.

But the biggest feature in MHS is that it is constantly updated.

Copyright © 2006 Shawn (L. Spiro) Wilcoxen


Getting Started

Open Process

To open a process for scanning, debugging, and/or hacking, press Ctrl-P or select the File/Open Process menu item.

You can show the main processes as well as the hidden processes. You can also sort by process name or process ID by clicking on the respective header.

You may refresh the lists by pressing Refresh.

To open a process, either double-click it in the list or select it and hit OK.

Open Recent

After opening a process, it will be added to the recent history, allowing you to easily open it again in the future by selecting it from the File/Open Recent sub menu. Opening a process this way does not load it in debug mode. The list supports 16 entries; new processes loaded after the list is full will cause the bottom entries to be lost.

If the process is not already open, it will be executed before attaching.


Open for Debug

MHS allows you to open a process and begin debugging it immediately before it has a chance to perform any start-up execution. This is especially useful for watching the initialization routines of other processes. Open the Open for Debug dialog by selecting the File/Open for Debug menu item.

Getting Started

The Open for Debug dialog allows you to browse for and open any executable file. The file path may be relative to the working directoy of MHS. For example ..\Game.exe might be valid if Game.exe is one directory above the MHS executable. All paths are stored for easy access later.

You can optionally supply a command line to be passed to the new process on execution. All command lines are remembered and are associated with the process on which they were last used, so when you select a process that you have selected before, the last command line you used on that process will be shown as well.

Pausing the Process

You have the option of pausing the process when it is loaded. You can perform this by either checking the Pause After Load check, or by writing the On_BeginDebug() script function and returning TRUE. By returning TRUE from the On_BeginDebug() script function, the process will be paused, even if Pause After Load is not checked. The script function allows you to check the process name and perform per-process operations, including any modifications to the process’ data or code, and setting up any script variables you would like to use during the debugging of the process.

For more information on the On_BeginDebug event, see Script Events.


Options

Select the Tools/Options menu item to open the General page of the Options Dialog. Here you can modify the general options in MHS.

Refresh Rates

Several features in MHS use timers to repeatedly update themselves. This page allows you to set the rates at which they do so. All rates are in times per second.

See Found Addresses, Modifying Values, and Expression Evaluator for more information on each feature.

View Settings

These settings change how data types are presented in certain dialogs.

Option | Effect
Data Types as Code Names | Names of types are presented in C++ code form. For example, Unsigned Long becomes unsigned long, and 64-bit Integer becomes __int64.
Data-Type Sizes | Sizes of types are shown next to their names.
Data-Type Ranges | Ranges of types are shown next to their names.

The windows affected by these settings are the Data-Type Search, Group Search, Sub Search, and Modify Values dialogs.

The changes are shown in the combo boxes.

The left image shows the default types and the right image shows the same combo box with the Data Types as Code Names and Data-Type Sizes set.

Others

The following list explains the other options pages.

  • General Search: Options for seaches. See Search Options for more information.
  • Hotkeys: Allows to create hotkeys for any purposes. See Hotkeys for more information.
  • Env. Variables: Allows to create environment variables and view existing ones. See Environment Variables for more information.
  • Programs: Allows to create shortcuts to other useful tools on your PC and run them easily via the Tools menu. See Programs for more information.
  • Disassembler: Options for the Disassembler and Debugger. See Disassembler - Options for more information.
  • Script: Options for the script environment. See Using the Code Editor for more information.


Kernel Options

The Kernel page of the Options dialog allows you to modify the settings of the kernel-mode driver included with MHS. This dialog can be opened by selecting the Tools/Options menu item and then selecting Kernel from the list on the left.

Reading/Writing Process Memory

MHS comes with custom functions for reading and writing the memory of the target process. This section allows you to use either, neither, or both of these functions. Using the custom functions is required in order to read/write kernel RAM. Furthermore, the custom functions are much more difficult to detect by anti-cheat devices, and therefore provide a lower chance of being blocked.

Stability

The following table explains the stability of the functions.

Reading From/Writing To | Single-Processor | Multi-Processor | Explanation
User-Mode Addresses (Typically 0x00010000 to 0x7FFEFFFF) | 100% Stable | 100% Stable | The custom functions are always safe for accessing normal user-mode RAM.
Kernel-Mode Addresses (Above 0x7FFF0000) 100% Stable 99% Stable

When kernel RAM is accessed, the Windows® pagetable must be manually checked to ensure the read/write is allowed. Locks are put in place on the current processor so that the system state can not change after checking the pagetable but before performing the read/write.

This lock, however, can only be made on the active processor. On multi-processor machines, it is possible for a thread on another processor to cause a state change after the pagetable has been checked but before the read/write.

However, the time between the check and the read/write is extremely small, and the state change is very unlikely be at the same area that is currently being read/written. In order to cause problems, a thread on another processor must deallocate at least 0x1000 bytes (to cause a full page to become invalid) at the same location where the read/write is to take place, and this must be done within the 7-instruction gap between the pagetable check and the actual read/write.


Properties

The Properties dialog shows detailed properties of the target process. You can access this dialog by selecting the File/Properties menu item or pressing Alt-Enter.

Chunks

This tab shows the full chunk information for the user-mode RAM of the target process. Modules are also shown along with their sections. Physical addresses and PTE properties are shown as well if the kernel-mode driver is loaded. If it is not, these fields are omitted from the display entirely.

Simplified Chunks

This tab is absent if the kernel-mode driver is not loaded. This tab shows chunks by a different set of criterion and includes kernel chunks as well.

If a chunk is shown in green, it can be read and written. If in blue, only read. If white, it is not accessable.

EPROCESS

This shows the EPROCESS structure for the target process. This tab is absent of the kernel driver is not loaded. Furthermore, the information in this tab can not be shown if the custom function for reading the target RAM is not used. See Kernel Options for details.


About Searching

To find the values in the game that you want to modify, you need to search for them. Initially, you may find thousands of results after a search, but using sub searches and clever thinking, you can narrow that number down to something much more manageable, such as two or even one.

MHS offers the following search types:

  • Data-Type Search
  • Pointer Search
  • String Search
  • Group Search
  • Expression Search
  • Script Search

Some of these searches allow you to perform a Sub Search.

For details on a specific search, click one of the links above.

General Searching Concepts

The ultimate goal of a search is to find a single value in the game that you want to modify, however the values are often so generic that searching for them directly can return millions of results. From here, the user must find a way to eliminate as many of these values as possible to find the correct one. This is done by using sub searches, which search only through the existing list of matches created by a previous search (or sub search) and eliminates returns from the list if they do not match the specified criteria. Sub searches are repeated until either no returns are left, or until the correct result becomes obvious within the list. Finding values in your games typically means a search followed by multiple sub searches.

Highlighted are the search types available in MHS. Each of these menu items allows you to begin a new search. Starting a new search will erase all previous search results (note that simply clicking a menu item here and viewing the set-up dialog for that search will not result in the loss of any searches).

Ctrl-Shift-S will load the last search type you performed, or Data-Type Search if no searches have been performed.

To quickly start a new search, press the New Search button:

The type of search it creates depends on the last search you performed. If no previous searches were performed, it shows the Data-Type Search dialog.

The first search in a set is called the initial search.

Narrowing it Down-Sub Searches

After your initial search, you may end up with literally millions of returns-far too many to check one-by-one. You will recognize very quickly the need for eliminating many of the useless fake results. Sub Searches allow you to remove some of the results from a previous search if they fail to meet certain criteria.

Highlighted is the Sub Search menu item. This is not available if no initial search was performed, and some types of initial searches do not support sub searches.

You can also use the Sub Search button:

See Sub Search for more information.

For information on what to do after a search, see Found Addresses.


Found Addresses

After a search or sub search, if there are any results, they will be displayed in the Found Addresses lists.

Dockable Address List

Dockable Address List

The dockable address list is used for quick access to the list of values returned by a search or sub search. The list has no limit on the number of items it can display, except limits imposed by Windows® itself, and the list is filled immediately, without creating lag after searches.

If you want to add addresses from this list to the main list, double click them. Note that after some searches, values can not be directly added, and instead double clicking will show the RAM Watcher. Multiple values can be selected and added at once by holding Shift or Ctrl.

To load the Hex Editor on one of the addresses, select it and press H.

All results display an address and a value. Some search results then display additional information related to the search type. The Value column shows the value at the time of the search while the Current Value shows the values in real-time. Green text indicates static addresses.

If you want to move the window without allowing it to dock, hold Ctrl while you drag it. This applies to all dockable windows in MHS.

If you close this window and want it back, use the Window/Found Addresses menu item.

This window also contains shortcut buttons for the last search, sub search, and adding values, respectively.


Search Options

There are many search options available to help increase the performance of MHS’s scan speed, and the options may perform differently from one computer to the next.

The following table explains each option.

Option Meaning
MEM_IMAGE

Include chunks with type MEM_IMAGE.

When executable files and modules are loaded into RAM, they are broken into parts and mapped over the RAM. The sections used to map them over RAM are given the MEM_IMAGE type. Executable files are usually loaded to the same address on each start-up, making its MEM_IMAGE sections static.

MEM_IMAGE types are always considered static, even if the module owning them moves in RAM, because each MEM_IMAGE section that is part of the module will always start at the same offset from the base of the module, thus any address in the module that lies inside any of its MEM_IMAGE chunks can always be calculated.
MEM_PRIVATE

Include chunks with type MEM_PRIVATE.

MEM_PRIVATE chunks are chunks of memory allocated by the target process that are not shared by other processes. Static pointers are always found in MEM_IMAGE chunks, and usually point to dynamically allocated MEM_PRIVATE chunks.
MEM_MAPPED

Include chunks with type MEM_MAPPED.

MEM_MAPPED chunks are chunks that are mapped to files on hard drives. Operations (read/write) performed on these chunks in RAM are also performed on the relative files on the hard drive, making these chunks slow to search. They generally have nothing to do with the values in RAM you want to find and are best left out of searches.

Anything Readable

Includes all readable chunks. This is roughly the same as checking MEM_IMAGE, MEM_PRIVATE, and MEM_MAPPED, except that it creates the chunk list (to search) using a different algorithm. This option can not be used with the other chunk options.

The algorithm used by this option is slower than the normal algorithm but provides one particular benefit which may be of great importance depending on the target process. The algorithm used by this option is extremely difficult for the target process to detect and block, and therefore this may be the only way to search the RAM of the target process.

If this opion is not used, and the algorithm that generates the normal chunk list returns 0 chunks, the chunk list is automatically regenerated using this algorithm.
Pause Target Process

Pause the target process before searching. This improves speed considerably on processes that consume many computer resources.

Do not use this option on system processes such as csrss.exe.
Buffer Size

The size of the internal buffer used to hold found addresses in RAM. When this buffer is full, the data must be flushed to a file to free RAM for more returns while the search continues.

Flushing has a considerable impact on speed. The fastest searches are obtained by creating a buffer large enough to hold all returns in RAM, which will never cause a flush to take place.

Flushing never occurs in Unknown searches or in Exact Value searches where the value is not a 0 or a -1.
Precache

Used only in Sub Searches. While in a Sub Search, data from the previous search may need to be loaded into RAM from disk (if the initial search did not flush any data, the values will already be in RAM and this option will have no effect). This option will cause a second thread to run in the background which will load the next buffer from the file while the current buffer is being scanned. Optimally, the scan will finish with its current buffer and the precached buffer will be loaded and ready to go for the scan to continue.

The size of the precached buffer is the same as the size set in Buffer Size. This means that the total RAM needed for this option is double Buffer Size.
Postflush

When there are enough returns in a search to overflow the Buffer Size, the buffer must be flushed to disk. This option allows the flushing to take place on a second thread in the background so the scan can continue smoothly.

This can happen in both Searches and Sub Searches. Using this option will require double the RAM specified in Buffer Size, but only if a flush takes place.
Thread Priority

Set the thread priority for the scan. Different processors respond to this option differently. AMD® processors rarely show a difference between the priorities, while Pentium® processors show slight improvement with higher priorities.

Using higher thread priorities can sometimes negate the benefits of using Postflush and Precache. This is because the scanning thread may block the flushing/caching threads, not allowing them to perform their operations by the time the scanning thread is ready for the next flush/cache. When this happens, the scanning thread automatically adjusts its priority to give the other threads room to do their jobs, however the scanning thread must wait for them to finish before it can continue.

Users should experiment to find out if higher priorities are beneficial to them.

Use Epsilon (Float/Double Searches)

Rounding errors in floating-point numbers (types Float and Double) cause some searches to find no matches even though you typed the exact number you saw. For example, you search for 1.3509 but the actual value is 1.350901002.

Epsilon is used to account for this. When you search for a floating-point number, all values within Epsilon range of that number will be added to the list, allowing you to account for the tiny rounding errors to which CPU’s are proned.

Smart Epsilon

It is common for rounding errors to be present in CPU math, especially with infinite decimal places. However there are sets of numbers that typically round off very well and accurately. For example, the number 1.0 is often represented accurately on machines, with no trailing decimal places.

When Smart Epsilon is checked, Epsilon will be disabled for searches that are detected as rounding very well

Details

  • Buffers
    • Because the searches can depend heavily on threads, the actual options vary greatly between processors. Pentium® users may find a smaller buffer (0x01 to 0x05), with Precaching and Postflushing enabled, to be very smooth and at nearly no loss in speed. AMD® users will probably want to use a middle-sized buffer (0x10-0x25). These numbers are also subject to change given resource availability and other factors, and experimentation is necessary to find the best options for you.
  • Flushing
    • Flushing is the process of writing the values stored in RAM to the hard drive, and then removing the values from RAM to make room for more values. Buffer Size is the primary factor in deciding how often the internal buffer must be flushed.
    • If the Search Type is set to Unknown, no flushing takes place.
    • If the Search Type is set to Exact Value, and the value to find is not a 0 or a -1, no flushing takes place. If the value is a 0 or a -1, flushing occurs as needed.
  • Storage Efficiency
    • All memory scanners use two methods for storing found addresses internally.
      • The base method is to store address/value pairs where the address takes 4 bytes and the value takes as many bytes as needed for the search type. For example, each found address in a byte search would require 5 bytes to store. This is the least efficient in terms of space.
      • The other method is used only in Unknown searches where the address is stored once and the entire chunk of returns follows. This is the most efficient means possible of storing the returns, however it only works in Unknown searches.
    • MHS uses a third type to store returns, as well as the previous two.
      • The third method allows MHS to store values equally fast as the first method but using much less storage space. The details regarding the inner workings of this method are strictly confidential.
      • The special storage method allows the same number of returns to be stored in up to 50% less space, which causes flushing to occur less frequently, and less disk space to be used. Because flushing is the largest factor in slowing search speed, and because this method causes substantially fewer flushes with no loss in general search speed, this method is responsible for giving MHS the fastest data-type searches possible.
      • In Data-Type searches, this method is only used in Not Equal To, Greater Than, Lower Than, and Range searches, and Exact Value searches if the value is 0 or -1. In all other types of searches aside from Data-Type, this method is always used.
      • Only this method is capable of flushing and caching, thus Precaching and Postflushing only work on the search types listed above.
  • Other
    • In Data-Type searches, if a Range search is used and the specified range covers the full range of the data type, the search is internally changed to an Unknown search for better performance.
    • To increase performance, if the first Sub Search is a Same as Original sub search, the search type switches internally to a Same as Before sub search. This avoids rebuffering the initial results, which consumes more RAM and causes slower searching.
    • To benchmark MHS against other software, select the Range search in both software and specify data-type Byte with a range from 0 to 254—this range is meant to get a huge number of returns without causing MHS to switch to an Unknown search type. Be sure similar options are being used in both software and that a similar number of results is found.


Data-Type Search

The Data-Type Search allows you to find values stored as one of the basic data types—char/byte, short/unsigned short, int, long/unsigned long, float, and double. The following chart shows the sizes and usefulness of each type.

Data-Type Information
Type | Size/Range | Usefulness
char | 1 byte, -128 to 127 | Same as byte, but shifted down to handle negative numbers.
byte | 1 byte, 0 to 255 | Old games often used bytes to store character stats to save space. Any time your stats can only be from 0 to 99 or from 0 to 255, they are stored as bytes.
short | 2 bytes, -32,768 to 32,767 | Same as unsigned short, but shifted down to handle negative numbers.
unsigned short | 2 bytes, 0 to 65,535 | An unsigned short is large enough to hold most of the values you would need to find a game. If int searches fail, fall back to short searches, if the value is in the range of a short. Some scanners call this a word.
long | 4 bytes, -2,147,483,648 to 2,147,483,647 | The most common type for storing health, lives, energy, ammo, etc. This is because it helps to maintain a 4-byte boundary which improves game speed.
unsigned long | 4 bytes, 0 to 4,294,967,295 | Same as long, but holds no negative numbers, and instead holds higher numbers. Usually there is no need to use long instead of unsigned long, and vice-versa. Some scanners call this a dword.
int | 4 bytes, -2,147,483,648 to 2,147,483,647 | Exactly the same as long.
__int64 | 8 bytes, -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 | A rare 64-bit integer type used usually in specialized software. Games rarely use these.
unsigned __int64 | 8 bytes, 0 to 18,446,744,073,709,551,615 | Same as __int64 except the range is fully positive. This is also called a qword.
float | 4 bytes, (+/-)3.4E+38 | In 3-D games, player positions and directions are almost always stored in floats, and always in triplets—that is, 3 consecutive floats called a vector. Sometimes health is also stored in a float, and usually from 0 to 1. Perfect Dark and GoldenEye 007 are examples of games that store player health and armor in floats.
double | 8 bytes, (+/-)1.7E+308 | Old Flash games are “typeless” and as such they store everything in doubles, which are capable of storing all numbers represented by each of the previous types. This data type is very uncommon elsewhere because it takes more storage space and is more CPU-intensive than floats.

To begin a Data-Type Search, you must fill out a few parameters in the Data-Type Search dialog, shown below.

The following chart describes each option.

Option

Description
Data Type | Select the type of data to find.

Value to Find

Find Values Not Equal to

From

Find Values Greater Than

Find Values Lower than

Let Me Handle This

This has different meanings depending in the Search Type.

Search Type | Meaning
Exact Value | Find the exact value specified.
Not Equal To | Finds values that are not equal to the value specified.
Range | Specifies the bottom of the range of the numbers to find, inclusively.
Lower Than | Find all values lower than this.
Greater Than | Find all values greater than this.
Unknown | Unused.
To | Only used in Range searches, and specifies the top of the range to find, inclusively.
Evaluation Type

Specifies the type of search to perform.

Search Type | Meaning
Exact Value | Find the exact value specified in Value to Find.
Not Equal To | Find all numbers not equal to the value specified in Find Values Not Equal to.
Range | Find all numbers between From and To, inclusively.
Lower Than | Find all numbers lower than the value specified in Find Values Greater Than.
Greater Than | Find all numbers greater than the value specified in Find Values Lower Than.
Unknown | Find all numbers.
Aligned | The search is aligned based on the size of the data type in Type of Data. For example, if you perform an int search, this will scan only every 4 bytes. If you perform a byte or char search, aligned searches have no meaning.
Enable “Same as Original” Sub Search | Using the Same as Original sub search requires extra resources since the initial search must be stored to disk. If you do not need the Same as Original sub search, it is best to uncheck this.
From | Specifies the starting address from which to search.
To | Specifies the ending address at which to stop searching (exclusive).

Additional Notes

Range searches always go from the lower number to the higher number, regardless of the order in which you enter them.

The search itself always starts at the lower address and goes to the higher address, regardless of the order in which they are entered.

All search data entered into the dialog is saved if you click OK and perform a search, which allows you to repeat searches without having to fill out every box again.

Only if you perform a search will the previous results be lost.

Tips

  • The int/long type is the most common data type and is used to hold most values in most games.
  • It is less risky to search for values using the smallest type capable of containing all valid numbers the value can have. For example, if the number can only range from 0 to 99, a char or byte search will always find the value (if possible), while all other types may fail to find the value, even though they are large enough to hold every number from 0 to 99.
  • All 3-D games use float to store character positions.
  • Player positions in 3-D games can be found by searching for unknown (Search Type) floats (Type of Data), then jumping to a higher platform and sub searching for all values that have increased. Continue moving to higher and lower platforms and performing the appropriate sub search until there are only a few results. Often you will have multiple copies. See the following tip.
  • Sometimes, no matter how many times you search and sub search, you end up with two or more results that all behave the same way. One of the values is the real value, which, if modified, will change data in your game. The others are copies the game has made for any number of reasons. The most common reason for copying data is for display purposes. Usually one will be real and one will be a copy used to print the display on the screen. To find out which is real, modify them one-by-one and then change their values in the game. For example, if your health is at 23 and you change one of the values to 100, you will see 100 on your screen in the game. If you then take damage and the value drops to 22, you have changed the display number, and not the real value. Change another value to 100 and take damage. If it drops to 99 instead of 21, that value is the real value.


Group Search

Group Searches are used when you know that several values are in the same region of memory, but you don’t know their order, or if you know their order but don’t know their values.

I Know the Values are Close Together

There are two sets of options to use if you know the values but don’t know how close they are together.

In this case, you must specify the group size and enter the values that are to be found within that range. The following chart explains the options related to this type of search. A group is a series of bytes that are to be checked for containing the specified elements.

Option | Meaning
Total Group Elements | Total size of the group, in elements. For example, if you search for ints and set the Total Group Elements to 3, the total bytes in the group is 12, or 4 × 3. See below for details on how this is used during the actual search.
Values to Find | Each value to find within the group, where each value is separated by a space and is of the type specified in Treat Elements in List as. Values may appear more than once in the list. Each value is an element.
Ordered | If you know the order of the elements, set this option. The order of the values in the groups will match the order of the elements listed in Values to Find. This can not be used with Count Elements.
Count Elements | Use this to indicate that the elements in Values to Find are to appear in the group the same number of times as they appear in Values to Find. For example, if Values to Find contains the number 13 two times, the groups returned in the search will also contain 13 two times—no more and no fewer. This can not be used with Ordered because order is ignored when counting.
Treat Elements in List as | Each element in Values to Find is of this type. For an explanation of each data type, see Data Types.
List is in Hex | Each element in Values to Find is in hexadecimal form.

Total Group Elements indicates the number of elements in the group. If each element is of type short and there are 5 elements, the group size is 2 × 5, or 10. The scanner will then buffer sets of 10 bytes (a group) and check to see if they match the specified criteria, adding them to the Found Addresses list if they match. If a group (set of 10 bytes in this case) matches the specified criteria, it means all values listed in Values to Find are inside that set of 10 bytes.

For example, if we search using the criteria specified in the picture above, with Total Group Elements set to 5 and a Values to Find of 45, 56, and 12, each as bytes, we may get the following list of returns:

In each group of 5 bytes, we can find 45, 56, and 12. Because of the options used, these bytes can appear in any order and any number of times, as shown above.

I Know the Order of the Values, but not the Values

Sometimes you can see a pattern in the game but not know the values of the data used to represent the pattern. This raises need for a special type of Group Search called the Relative List Order search, sometimes called the USD search.

It gets the name USD from the values you must enter into the Values to Find. The following chart explains their meanings. Letters are not case-sensitive

Letter | Meaning | Description
U | Unknown | The element in the list is unknown.
S | Same | The element in the list is the same as the previous element.
D | Different | The element in the list is different from the previous element.

Each letter in Values to Find is an element of the type specified in Treat Elements in List as. Each element is based off the element before it in the list, except for unknown elements. Because of this, the list must begin with a U.

Using the above picture and searching for lists of bytes with the USD string ussdssdds, we may get the following return.

The first value is unknown. The next value is the same as that value, and so is the next. The next value is different from the previous, but the following two values are the same as it. The next value is different, and the next value is different again, followed at last by a value that is the same as it.

But How is This Useful?

It depends on your needs and interests. USD searches are important in various types of game-resource hacking. If you wanted to make extremely accurate maps for Super Mario World, you can use USD searches to get the tile information used to make each map in the game. Remember, not all hacking is meant to hurt others.

But resource hacking isn’t the only use for USD searches. An example that appeals to a larger range of people is the idea of hacking grid-based games, such as Tetrinet 2. Using USD searches is an easy way to find the board data—use the colors of the blocks to create a list order. Once you have found the board in RAM, you can make online bots play your game for you, using MHS’s scripting capabilities.

Common Options

Option | Description
Search Every Four Bytes | Searches starting on every four bytes instead of every byte. This does not change how it checks elements inside a group. Improves speed but may fail to return some results.
From | Specifies the starting address from which to search.
To | Specifies the ending address at which to stop searching (exclusive).

Additional Notes

The search itself always starts at the lower address and goes to the higher address, regardless of the order in which they are entered.

All search data entered into the dialog is saved if you click OK and perform a search, which allows you to repeat searches without having to fill out every box again.

Only if you perform a search will the previous results be lost.

Relative List-Order searches allow Sub Searching. See Sub Search.

Tips

  • USD searches are useful for finding any kind of tile-based information in games, including textures, bitmaps, and map data.
  • Finding game resources can be useful for extracting data to be used in your own personal projects. Such projects may include making maps or converting textures to be used in mods.
  • Finding tile-based map information can be useful for making bots.
  • Group Search can be used to find your player class more quickly by allowing you to specify your health, armor, ammo, etc., all at once inside the group.


Expression Search

The Expression Search is an advanced type of search with a broad range of uses. It is used to add customized criteria to the scanner which determines which addresses are added to the Found Address list. In this respect it is similar to the Script Search, but its functionality is more specialized, causing it to be slightly more limited, but faster at scanning and easier to use.

The Concept

An expression is supplied within the Expression Search dialog. The expression will be used during the search to determine which addresses will be added to the Found Address list. If the result of the expression is 0, the expression is said to be false. If the result is any other value, the expression is said to be true. The following illustrates this using purely constant expressions:

Expression | Result | Explanation
0 | false | This expression is simply a number and thusly resolves into the value of that number. Thus the expression resolves to 0, and is considered false.
32 | true | This expression is also just a number. The expression resolves to 32, which is not 0, and is therefore true.
12 * 0 | false | The expression resolves to 0 via multiplication, so the result is false.
32 >= 89 | false | The expression compares 32 with 89 using the greater-than-or-equal-to operator (>=). Since 32 is not greater than or equal to 89, the expression resolves to 0, which is false.
32 != 89 | true | 32 does not equal 89, so the result of the expression is 1, which is true.
(3 == 3) && 2 != (1 + 2) | true | First the left side is evaluated. 3 equals 3, so the value left of the && operator is 1 (true). Since this is not false, the right side of the && operator is evaluated. 1 + 2 is 3. 2 does not equal 3, so this side also evaluates to 1. The expression breaks down into 1 && 1, or true && true, which finally resolves simply to true.

Refer to the Expression Evaluator for a list of all valid operators.

Obviously, however, these expressions always result in the same values, and thus are always either true or false. Clearly this is not useful in searches, since the scanner would either add every address it checked or none of them at all.

The scanner checks each address in the target process one-by-one. In order to decide if the address being checked by the scanner should be added to the Found Address list, we would need to somehow incorperate the value at that address into our expression. We do this using ??. In our expression, ?? will be replaced by the value at the address currently being checked by the scanner. The same rules from above apply; if the expression finally results in 0 the address is not added. Otherwise it is.

The following table provides examples:

Expression | Explanation
?? | The value at the current address (while scanning) is the result of the expression, just as above where a raw number is the result of the expression. If the value at the current address is 0, the address will not be added, otherwise it will. This is the same as doing a Data-Type Search for values not equal to 0.
?? > 0 && ?? <= 100 | If the value at the current address is greater than 0 and less than or equal to 100 then the address will be added to the list. This is the same as doing a Data-Type Search for values in the range from 1 to 100.
(?? >= 'a' && ?? <= 'z') || (?? >= 'A' && ?? <= 'Z') | If the value at the current address is between ‘a’ and ‘z’ or ‘A’ and ‘Z’ the address is added. This finds all letters, but you would typically specify char as the type in the Expression Search dialog so that ?? will be treated as a single-byte character. This uses multiple range checks and so it can not be replicated by any other search except the Script Search.
[??] == 100 | The value at the current address (while scanning) is treated as a pointer. The [] operator returns the value at the address where ?? points. If that value is equal to 100, the address is added to the list. In the Expression Search dialog, treat ?? as either an unsigned long or a pointer.
f[[[[??]]+7Ch]+190h] == 1.0f | This is a more complicated version of the above. This treats ?? as the root of a Complex Address (technically speaking, all expressions are valid Complex Addresses, so we could have used this term above, but for the sake of clarity we reserve it only for long/difficult expressions such as this that often appear as an actual Complex Address), and, after evaluating the whole Complex Address, if the result (treated as a float due to the f[] operator) is equal to 1.0f, the address is added to the list. ?? should again be treated as a pointer in the Expression Search dialog.
f[[[[??]]+7Ch]+190h] >= 0.01f && f[[[[??]]+7Ch]+190h] <= 1.0f | Performs multiple comparisons on the same Complex Address as above. If the result of the Complex Address is between 0.01f and 1.0f inclusively, the address is added.

Searching

The following table explains each option in the Expression Search dialog.

Field | Meaning
Expression | The expression to use during the search.
Treat ?? (Current Value) As | Specifies how to evaluate the ?? keyword in the expression. This can be very important. If the value at the current address is 0x3F800000, treating ?? as an unsigned long will yield 1,065,353,216, while treating it as a float will yield 1.0f.
Alignment | Specifies the alignment of the search.
General Search Options | Shows the Search Options dialog.
From | Specifies the starting address from which to search.
To | Specifies the ending address at which to stop searching (exclusive).


String Search

String Searches allow you to find text and byte arrays in the target process. Strings in String Searches do not need to be NULL-terminated.

The following chart explains each option in the String Search dialog.

Option

Description
String to Find | This is where you enter the actual string you want to find. If you have selected type Hex String, you must enter the string in hexadecimal form, with each hexadecimal number separated by a space. For example, 34 AA EF would be a valid hex string.
Type
Option | Description
ASCII | The String to Find will be found as typed, after any escaped sequences are resolved.
Unicode | The String to Find’s escape sequences will be resolved, then the string will be converted to Unicode, and that string will be found.
Hex String | Indicates that String to Find is in hex format.
Wildcard | Indicates that String to Find has wildcard characters ? and/or *. See Wildcards and Regular Expressions.
Regular Expression | Indicates that String to Find is a regular-expression string. See Wildcards and Regular Expressions.
Hex Bytes | Displays the actual hex bytes that will be found.
Match Case | Indicates that the case should be matched. Does not apply to hex strings.
Match Whole Word Only | Indicates that only whole words should be matched.
Aligned | The scan starts on every 4th address rather than every address.
From | Specifies the starting address from which to search.
To | Specifies the ending address at which to stop searching (exclusive).

Additional Notes

The search itself always starts at the lower address and goes to the higher address, regardless of the order in which they are entered.

All search data entered into the dialog is saved if you click OK and perform a search, which allows you to repeat searches without having to fill out every box again.

Only if you perform a search will the previous results be lost.


Pointer Search

Pointers are what games use to keep track of the locations of chunks of memory that have been dynamically allocated. If you need a detailed explanation on what pointers are and how to work around them, see About Pointers. Pointer Searches allow you to find pointers to dynamic game objects, or objects that move in memory. This is often called dynamic memory allocation, or DMA.

For the remainder of this page, for the sake of explanation, we will assume we are hacking a 3-D first-person shooter game that stores its lists of players dynamically. Typically in this type of a game, a person will have found his or her character’s health as an int from 0 to 100, but the person will notice that when he or she dies, the address for the health is no longer valid; the player’s health has moved to a new location and must be located again.

What has actually happened is that the chunk of RAM that contained the player’s character data was deallocated by the game and then reallocated when the player respawned. It has not just moved the health, but the ammo, position, energy, and everything else related to the character.

There is, however, one thing that did not move. No matter how complicated a game is, it still needs a way to find the data for each character, no matter when or where the data moves. All games, without exception (as guaranteed by the x86 architecture), have static (non-moving) data. Static pointers (pointers that do not move) are there specifically to tell the game where these sets of moving data are going when they move.

Be careful to note that static data is often defined as data that does not move, however on the Windows® platform there is no such thing as data that does not move. All static data is stored in relation to the module that contains it, and Windows® guarantees that all modules in a process can be loaded to another address if system memory is running low. For simplification, we will assume the game is always loaded at 0x00400000.

Properties of Pointers

Pointers have several properties that allow us to locate them. The most important property is that they always point to the base of any given data set (called a structure). Consider the following example.

The red rectangles outline a player structure containing our health, ammo, and some other values we don’t understand. The actual addresses of these values is irrelevant at this point; they are always referenced as an offset from the structure base, however we don’t yet know where the base is. We do, however, know that the game has a pointer to this structure somewhere, and that pointer will point to the base of it. We also know that structures are never large. That means if we have a value inside a structure (health and ammo here), then the base of the structure is never far away. In the picture above, the first value in the structure that we understand is the health. This means the base of the structure is before the address of the health, but not far before the address of the health.

It is usually safe to assume that no structure in your game will be larger than 0x10000 bytes. That means if we have the health of our player, the base of the structure will never be lower than [Address of Health] - 0x10000.

Using What We Know

Now we have enough information to perform a Pointer Search. The full search options are explained below, but for now we will only discuss the options used in this example.

Let’s assume the address of Health in the picture above is 0x02BE2C04. Given what we know about structures, the base of the structure must then be between 0x02BD2C04 and 0x02BE2C04. For the sake of simplicity, let’s round 0x02BD2C04 down to 0x02BD0000. In the Pointer Search dialog, we would select Range in the Search Type, then enter 2BD0000 and 2BE2C04 into the Points Between and And edit boxes.

This means we are searching for all pointers that point to locations between 2BD0000 and 2BE2C04, inclusively. The base of our structure is around there somewhere and there is a pointer somewhere that points to it.

We only want to find pointers that won’t move, so we select Only Find Static Pointers. The search will automatically save the offset from 2BE2C04. This will be explained later.

After performing the search, we may get several returns. As stated before, the structures are usually small, so the base of our structure should be near Health. Using the Found Addresses dialog (Search/View Results), we will see a column entitled Distance from 02BE2C04. This is how we can tell how far away pointers are from our Health (address 0x02BE2C04). Negative numbers indicate that the pointer is pointing to a location lower than 2BE2C04. A distance of 0 indicates that the pointer is pointing directly at 2BE2C04. Positive numbers should never be in the list, because pointers to our structure will never point higher than the address of Health (2BE2C04).

The dockable Found Addresses list shows this offset.

For our example, we will assume there is a pointer at address 0x0045EFC0 whose distance is -0xC from 2BE2C04 (our Health). This happens to be very close, though the average distance can be around -0x200 to -0x500, and in some cases farther. If you find a pointer that has a distance between -0x500 and 0x0, there is a very high chance it is the correct pointer to your structure.

Double-click the pointer in the Found Addresses list to add it to the main list. This is the result:

The base of our player structure can always be found by following the pointer at address 0x0045EFC0. If we add 0xC to that location, we have our player health. If we add 0x1C to the base address, we have our player ammo.

The Options at Our Disposal

The following chart explains each option in the Pointer Search dialog.

Option

Description

Target

Targets Other Than

Target From

Find Targets Greater Than

Find Targets Lower Than

Let Me Handle This

The meaning of this depends on the Search Type.

Evaluation Type | Meaning
Exact Value | Find pointers that point to this exact location.
Not Equal To | Finds pointers that do not point to the value specified.
Range | Find pointers that point between this location and the location specified in the And box, inclusively.
Lower Than | Find pointers that point to locations below this location, exclusively.
Greater Than | Find pointers that point to locations above this location, exclusively.

It must be in hexadecimal form. Other restrictions apply as outlined in Additional Notes.

To | In a Range Search Type, this is the top of the range where pointers can point, inclusively. It must be in hexadecimal form.
Evaluation Type

Specifies the type of search to perform.

Evaluation Type | Meaning
Exact Value | Find pointers that point to the exact location specified in Target.
Not Equal To | Find all pointers that do not point to the location specified in Targets Other Than.
Range | Find all pointers that point to locations between Target From and To, inclusively.
Lower Than | Find all pointers that point to locations below the location specified in Find Targets Greater Than.
Greater Than | Find all pointers that point to locations above the location specified in Find Targets Lower Than.
Unknown | Find all pointers.
Only Find Static Pointers | Only static pointers (pointers that do not move when the game is restarted) will be found. Static pointers may move only if the module in which they are located moves. For example, the gamex86.dll module in Doom® 3 moves each time the game is restarted. Static means the pointers move with the module, and are always the same distance from the base of the module.
Enable “Same as Original” Sub Search | Using the Same as Original sub search requires extra resources since the initial search must be stored to disk. If you do not need the Same as Original sub search, it is best to uncheck this.
From | Specifies the starting address from which to search.
To | Specifies the ending address at which to stop searching (exclusive).

Additional Notes

All fields must be entered in hexadecimal form.

The Points To field must follow the rules of a valid pointer, which means it must be divisible by 4 and must be a readable address in the target process.

Range searches always go from the lower number to the higher number, regardless of the order in which you enter them.

The search itself always starts at the lower address and goes to the higher address, regardless of the order in which they are entered.

All search data entered into the dialog is saved if you click OK and perform a search, which allows you to repeat searches without having to fill out every box again.

Only if you perform a search will the previous results be lost.

Tips

  • The most common options are to use a Range Search Type with Only Find Static Pointers and Save Offset From checked.
  • The And value in a Range search should always be the lowest known address in the structure to which you are trying to find a pointer.
  • The Save Offset From value should always be the same as the And value in a Range search.


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.


Sub Search

Sub Searches allow you to filter invalid values from the list of returns to help find the valid values. Sub Searches are repeated until enough values have been eliminated that only a few remain, so that the few remaining ones can be examined manually to determine which are worth saving and wcich are not.

Sub Searches use the original data-type information used in the original search. For example, if the initial search was for float values, the Sub Search will be performed on floats as well. Not all initial search types support Sub Searches.

To perform a Sub Search, press Ctrl-Shift-D, press the Sub Search button, or select the Search/Sub Search menu item.

Data-Type Search, Pointer Search

After performing a Data-Type initial search (see Data-Type Search) or a Pointer Search (see Pointer Search), you can perform a Sub Search. The following chart explains each option in the Sub Search dialog.

Search Type Name Description
Exact Value

Out of the current search results, this finds all values that match the value specified in Value to Find.

Values that do not match are removed from the current search results.
Not Equal To

Out of the current search results, this finds all values that do not match the value specified in Values Not Equal to.

Values that match are removed from the current search results.
Increased

Out of the current search results, this finds all values that have increased since the last scan.

Values that have not increased are removed from the current search results.
Decreased

Out of the current search results, this finds all values that have decreased since the last scan.

Values that have not decreased are removed from the current search results.
In Range

Out of the current search results, this finds all values in the range specified by From and To, inclusively.

Values that are not in this range are removed from the current search results.
Same as Before

Out of the current search results, this finds all values that are the same as they were in the last scan.

Values that are not the same are removed from the current search results.
Different from Before

Out of the current search results, this finds all values that have changed since the last scan.

Values that are not different are removed from the current search results.
Changed By

Out of the current search results, this finds all values that have changed by the amount specified in Changed By.

Values that have not changed by this amount are removed from the current search results.
Same as Original

Out of the current search results, this finds all values that are the same as they were in the initial search.

Values that are not the same as they were in the initial search are removed from the current search results.

This search type is optional, and must be enabled at the time of the initial search by checking the Enable “Same as Original” Sub Search button.

USD Search

After performing a relative list-order Group Search (see Group Search), you can you can perform a Sub Search.

Simply specify the new USD string in the USD List field. The string does not need to be the same length as the original string, though it is common.

From the previous list of results, those that do not match the USD string supplied here will be eliminated.

Script Search

For information on how to perform a sub search after a Script Search, see Script Search.


Modifying Values

After finding and saving values, it is normal that you may want to modify them in some way. These values are called Stored Addresses (or Stored Values), and they are composed of a description (name), type, value, value (or values) when locked, and address (base and final). In MHS, addresses can be calculated 3 different ways. There are 5 total locking methods, and many Stored Addresses can be modified at once.

To modify Stored Addresses, select the address(es) you wish to modify and either double-click them or right-click one of them and select Modify.

The means of modifying Stored Values are so extensive they each have their own sections.

  • Main Modifications
  • Address Modifications
  • Locking Modifications
  • Hotkey Key Modifications
  • Auto-Assemble Modifications

Deleting Stored Addresses

To delete Stored Addresses, select the items in the main list and press Del, or right-click them items and select the Delete.

Alternatively you can press the X toolbar button to delete the selected items, or select the trash icon to delete all items.

Inserting Addresses Manually

Addresses can be added manually by selecting the Search/Insert Address menu item or pressing Ins on the main list. You may also press the New toolbar button.

Saving/Loading Stored Addresses

Stored Addresses can be saved by hitting the Save icon on the toolbar or by selecting the File/Save (Ctrl-S) or File/Save As (Ctrl-A) menu items.

You can also save only certain Stored Addresses by selecting them in the main list and then selecting the File/Save Selected As (Ctrl-Shift-A).

You can reload saved addresses by clicking the Open toolbar button, or by selecting the File/Open Save File (Ctrl-O) menu item.

Multiple copies of Stored Addresses may exist in the list. Care should be taken to ensure they do not overlap each other with active locks as it is possible that the values may be locked multiple times per cycle, or that they are partially overwritten by another lock, yielding undesired results.

Moving Addresses

Selected addresses can be moved by a specific amount by right-clicking and selecting Move Selected from the pop-up menu. The offset may be positive or negative. This function moves values by adding (or subtracting) the amount specified to (or from) the Simple Address of the selected items. This means that if the Stored Value is using Complex Addresses or Script Addresses, the value will not be moved by this operation (however its Simple Address is still changed, and if the Simple Address is used in the GetFinalAddress() script function the Stored Value will still be able to move).

Auto-Hack

An Auto-Hack session can be started (or continued) by right-clicking items from the list and selecting Find What Accesses This Address, Find What Read This Address, or Find What Writes This Address. If software breakpoints are being used for Auto-Hack, only Find What Read This Address and Find What Writes This Address will be available. If hardware breakpoint are being used, Find What Accesses This Address and Find What Writes This Address will be available.

The Debugger is attached automatically if it is not already.


Main Modifications

The main properties of a Stored Value are its description, value, type, lock type, lock value, and address. There is also a value indicating whether the lock is actually active or not. The main modification panel does not allow editing of addresses, as there are far too many options for addresses.

The general rule of thumb is that any field left blank, or any check left in an indeterminate state, will leave that field unmodified in the item(s) being modified.

Any fields initialized to blank or indeterminate implies that multiple values are being modified and they do not all have the same value for that field.

General

The following chart explains the general options.

Option | Meaning
Description | Indicates the name/description of the item(s). This can be any string and any length. If left blank, the current descriptions for the Stored Addresses being modified remain unchanged. If multiple items are being modified and they do not all share the same description, this initializes to blank.
Add Array Indices to Multiple Items | If multiple Stored Addresses are being modified at once, this will add a bracketed array index after each item, in the order in which they appear in the main list.

Value

The following chart explains the value options.

Option

Meaning
Cur Value

The current value of the item(s) being modified. If multiple items are being modified and they do not all have the same text-decoded value, this is initialized to blank. If left (or set to) blank, each item being modified retains its current value.

It is possible for multiple values being modified to have different types. In such a case, the value here, if any, is cast to the type of each item individually. For example, if modifying two values and one is a byte and the other a float, a Cur Value of 3 would be cast to a byte for the first item (0x03) and a float for the second (0x40400000).

String types are binary in nature, and escape sequences are used to indicate any special characters in the string.

If a nonstandard type is selected, the Cur Value should be expressed as a hexadecimal string.

If the type is Pointer or Void, the Cur Value should be expressed as a hexadecimal numeric. For example, CAFEBABE. Note that Pointer types resolve to Void.

Type | The type of the item(s) being modified. If multiple items are being modified and not all of their types are the same, this will initially be blank. Leaving it blank will cause each item to retain its current type.
Display as Unicode | If the Type is set to String, the value of the item(s) can optionally use Unicode decoding for display. If this check is left indeterminate while modifying multiple items, all items will retain their current Unicode settings.

Lock

The following chart explains the lock options.

Option

Meaning
Lock Type

Indicates the type of lock to use on the item.

Option | Meaning
Exact | The exact value supplied in the Exact Value field is written to RAM repeatedly to lock the item.
Range | During locking, the current value of the item is obtained. If the value is lower than Low, Low is written to RAM. If the value is higher than High, High is written to RAM.
No Lower Than | During locking, the current value of the item is obtained. If the value is lower than Minimum Value, Minimum Value is written to RAM.
No Greater Than | During locking, the current value of the item is obtained. If the value is greater than Maximum Value, Maximum Value is written to RAM.
Lock (Indeterminate Check Leaves Item Locks As They Are) Determines if the lock is active or not. Leave in an indeterminate state to leave item locks as they are (applies only to modifying multiple Stored Addresses at once).

Miscellaneous

The base address, final address, and base module are shown for ease in copy/paste.

For more information on address calculation, base addresses, and final addresses, see Address Modifications.


Address Modifications

Address calculation is one of the largest parts of Stored Addresses. MHS offers 3 methods for calculating them. Each method offers different pros and cons. The best method should be chosen on a per-item basis.

First 2 of 3 Methods

Before discussing the actual options, it is important to understand base addresses, final addresses, and complex addresses.

Base Address vs. Final Address

Address calculation is normally broken into two parts: the base address and the final address. The base address is the first address when following multiple pointers down a path (a pointer tree). Usually this is a module.exe+offset combination, but is also commonly a simple address. After the base address, be it a module.exe+offset combination or a direct address, if no more processing needs to be done to calculate the address of the item, then the base address and final address are the same.

In items that are multiple pointers “deep” (items that require a series of pointers to be examined and followed to get to the target address) must do extra processing after the base address. This processing is used to get the final address.

Complex Addresses

The term complex address actually refers to the format of a text string used to calculate addresses that are multiple pointers deep, or require other forms of extra processing. The string format allows basic math operators in conjunction with module names, which are replacements for their actual addresses, and the [ ] bracket operators, which indicate that after the value inside the brackets is determined, that value should then be considered an address inside the target process and the value at that address should be obtained. In MHS, all valid expressions can be used as complex addresses.

For example, winmine.exe+0x5334 might give us the value 0x01005334. [winmine.exe+0x5334] would be turned into [0x01005334]. Then 0x01005334 is considered an address inside the target process and the value is read from that address (in DWORD form), which might return the value 30.

Typically when writing a complex address, you want to leave off the outside brackets, because we aren’t interested in the value at that address, but the actual address itself. There may be exceptions, however, and you may use the outside brackets as needed. The Normal Address dialog reminds you to omit the outside brackets, however you may break this rule as you see fit.

Simple

MHS has built-in functionality for making the computation of a simple address extremely fast and efficient.

The simple address can be either a direct address or a module.exe+offset combination. Both require the same amount of time to calculate (one operation), so choosing a direct address instead of a module.exe+offset address is never better. Find Static Offset When Possible will calculate the module.exe+offset format for you. If unchecked, or if no module.exe+offset format can be made, the direct address is used.

This method can only calculate the base address. When using only this method, the base address and final address will always be the same.

Complex

MHS has a built-in parser for quickly calculating complex addresses.

A complex address stores both the base and the extended information for the final address. Any expression can be used to get the final address, and nothing special must be done to denote any part of the expression as being the base address.

Examples of valid expressions include:
winmine.exe + 0x5334
[[[[tutorial.exe+C4208]+C]+14h]]+18LH
([[Project64.exe+D6A3C]+62988lh]&0xFFFFFF)+[Project64.exe+0xD6A3C]+2D4

Resolving complex addresses into actual addresses is fast and efficient. Text parsing is performed only once and all values are simplified when possible. Expressions that do not have module names or [ ] operators are all calculated at the exact same speed, equivalent to the speed of the calculation of the simple addresses. Therefore, parsing the expression 45 * 2 + 0x01005334 is just as fast as parsing the expression 0x100538E; both take only one operation, and therefore both are also just as fast as parsing the simple address module.exe+100538E, which is again just as fast as parsing the direct address 0x0100538E.

Complex addresses contain both the base and final addresses. When a complex address is being used, the simple addresses are not used at all, however the data for the simple addresses remains and can be modified. When the complex address is disabled, the simple address is used again. The data for the complex address remains and can be modified, however it is only actually used when enabled.

Although complex addresses contain both the base address and the final address, the base address is in no way denoted inside the expression, and therefore the entire expression is technically implemented as the base address.

Scripted Address

Scripted addresses are the most complicated address-calculating systems available. They allow you to perform any operations required to get the address of the item.

You can write scripts to get the base address, final address, or both. Complex addresses override simple addresses and scripted addresses override both. However, both simple addresses and complex addresses are used only for base calculation, and it is therefore possible to use either the simple or complex address in conjunction with the scripted final address.

As mentioned before, the information for a simple address remains, even when simple addresses are not being used. When complex addresses are used, the information in the simple address is simply not used at all. However, when the scripted base address is used, the module, offset, and result from the simple address is passed to the GetBaseAddress function.

Finally, the base address is passed to the GetFinalAddress function, no matter which method was used to calculate it. Therefore, it is possible to use the scripted final address with the base (parameter aBase) calculated as a direct address (from the simple address), module.exe+offset combination (from the simple address), complex address (from the complex address) or as a scripted base address.

The GetBaseAddress and GetFinalAddress functions should simply return the base address and final address of the item. In all cases, the final address is what is used to determine the actual address of the item, therefore it is not required to write both functions. You could write only GetBaseAddress, and its value will be used as the final address also, or you could simply write GetFinalAddress, and even though there is no base address it would still return the only value that matters in the end: the final address.

The functions are written as follows:

MHS_ADDRESS GetBaseAddress( _TCHAR * ptcModule, DWORD dwOffset, MHS_ADDRESS aResult )
MHS_ADDRESS GetFinalAddress( MHS_ADDRESS aBase )

ptcModule is the module specified in the simple address. dwOffset is the offset supplied in the simple address. aResult is the result of ptcModule+dwOffset, where ptcModule has been converted to the actual base address of the module. These values are taken from the simple address data, even if the simple address is not being used. This data can greatly enhance the speed of calculating the base address.

aBase is the previously calculated base address. The value here could have come from any method available for calculating addresses. If simple addresses are used, it will be the direct address used, or the result of the module.exe+offset combination. If complex addresses are used, it will be the result of the expression supplied for the complex address. If the scripted base address is used, it will be the return of the GetBaseAddress function.

The parameters in GetBaseAddress and GetFinalAddress are optional, however they must remain in the correct order when used. For example, it is okay to omit aResult, but dwOffset may only be omitted if aResult is omitted, and all parameters that are used must be in the correct order. It is actually advised that unneeded parameters be omitted, as this will actually make it slightly faster to call the script function (though the difference is nearly infinitely small).

Pros and Cons

The following chart explains the pros and cons of each method.

Method | Pros | Cons
Simple Address | Easiest to use/understand. Never slower, and usually faster than the other methods. Allows data to follow modules as they move in memory. Usually consumes the least amount of disk and RAM space. | Only allows one depth of addressing—unable to follow pointers to the target.
Complex Address | Easiest way to map a series of pointers that must be followed to the target address. Consume less RAM and disk space than script addresses. Always faster than the equivalent script address. Allows all standard mathematical operations, enabling it to track virtually every pointer tree, including encrypted ones. | Unable to follow some pointer trees if extensive encryptions have been placed on them. Unable to use logical branches while following pointer trees.
Script Address | The end-all-be-all method for calculating the target address; no encryption is too great. Allows logical branching while calculating the pointer path. | Consumes much more RAM than the other two. However, if the script lock is already being used, adding script addresses consumes relatively little extra RAM, sometimes less extra RAM than the RAM needed for a complex address. Typically, though not always, consumes more disk space than complex addresses.


Locking Modifications

Locked values are values that are prevented from changing, or are allowed to change only under certain circumstances. Other software sometimes call this freezing. MHS has 4 built-in locking modes and a script lock to allow any other type of locking the user would like.

The 4 main locking modes are on the Main tab of the Modify Address dialog.

Lock Type | Description
Exact | The value specified in Exact Value is written to RAM repeatedly at the final address.
Range | If the value at the final address goes below the Low value, the Low value is written to RAM. If the value at the final address goes higher than the High value, the High value is written to RAM.
No Lower Than | If the value at the final address goes below the Minimum Value value, the Minimum Value value is written to RAM.
No Greater Than | If the value at the final address goes above the Maximum Value value, the Maximum Value value is written to RAM.

Script Locks

Script locks allow you to create any type of lock you wish. Because script locks are run by scripts, you can incorporate any kind of logic you need, access any area in RAM you desire, and even perform other actions not necessarily related to the locking functionality. It is even possible to lock multiple addresses at once within a single script lock for a single stored address. To create a script lock, simply write the Lock() function in the Script Lock tab editor and check the Use Script to Apply Lock check.

Typically the lock is performed by using extern variables (see Externals) declared at the address passed to the function and modified as desired. In this image, e_dwVal is declared at the address given to the function, then checked for being under 100, in which case e_dwVal is set to 100, which modifies the RAM in the target process respectively.

aAddress is the final address calculated by any of the 3 methods for calculating addresses (see Address Modifications).

iItemSize is the size of the item. If the stored value is a standard type, such as float, unsigned long, char, etc., this is the standard size of that type. If the stored value is a non-standard or string type, this value is the length of the non-standard type or string. Strings are always binary, so this length includes any NULL characters within the string.

iItemSize is typically ignored in the script, especially when the stored value is a standard type and the size is always known beforehand.

If the script lock is enabled, the script is used instead of the other lock modes. However, the data for the other lock modes is valid and will be used again if the script lock is disabled.

Locking Values

When stored values are locked, the icon next to them appears green.
You can lock and unlock multiple stored addresses at once by selecting them and then hitting either the Lock or Unlock toolbar button.


Hotkey Key Modifications

The Hotkey Key allows you to associate stored values with Hotkeys. Hotkeys can be used to lock or unlock stored values.

When the designated Hotkey is pressed, all stored addresses with the matching Hotkey Key will be locked or unlocked. This means you can give the same Hotkey Key to multiple stored addresses to lock or unlock them all together.

This dialog allows you to specify the Hotkey Key for the selected stored addresses and also allows you to quickly set the lock and unlock Hotkeys for the specified Hotkey Key.

Quickset Hotkeys
Section | Item | Description
Lock | Key | The key to hit to activate the Hotkey which will lock all stored addresses with the specified Hotkey Key.
 | Mod | The modifier key which must also be held to activate the Hotkey.
 | Polling Method | If checked, the Hotkey is added using the Polling Method. Otherwise it uses the normal method. See Hotkeys for more details.
 | Quick Add (Lock) | Adds a Hotkey that will lock all stored addresses with the specified Hotkey Key. The added Hotkey will be activated via the Key and Mod buttons on the keyboard.
Unlock | Key | The key to hit to activate the Hotkey which will unlock all stored addresses with the specified Hotkey Key.
 | Mod | The modifier key which must also be held to activate the Hotkey.
 | Polling Method | If checked, the Hotkey is added using the Polling Method. Otherwise it uses the normal method. See Hotkeys for more details.
 | Quick Add (Unlock) | Adds a Hotkey that will unlock all stored addresses with the specified Hotkey Key. The added Hotkey will be activated via the Key and Mod buttons on the keyboard.


Auto-Assemble Modifications

Auto-Assemble scripts can be used to provide extra functionality to Stored Addresses (see Auto-Assembler). This is typically used to allow an alternative method for locking values but is not limited to such functionality.

Auto-Assemble Used to Change an In-Game Constant

There are 3 sections in Auto-Assemble scripts used with Stored Addresses: [GLOBAL], [ENABLE], and [DISABLE]. The default section is [GLOBAL]. Sections may appear in any order within the script, but the resulting code may not be desirable if the sections are not followed immediately by a label.

The first time a section appears, the code in the section begins at address the address of the Stored Value unless a label is used to specify otherwise. All code in a single section (even if interrupted by another section and resumed later) is sequential unless labels are used to specify non-sequential addresses. For example, if the [ENABLE] section has 4 commands, then the [DISABLE] section begins and has 4 more commands, then another [ENABLE] section appears, the code added to the second [ENABLE] section is ultimately added to the end of the code in the first [ENABLE] section. The second [ENABLE] section does not resume where the [DISABLE] section ended, but where the previous [ENABLE] section ended.

Options

The Auto-Assemble script is not used unless Use Auto-Assembler for Locking is checked. If not checked, the script is verified and compiled but not injected. If the Auto-Assembler script is used, regular locking mechanisms are disabled, leaving only the script to perform locking and unlocking.

The Preview button allows you to verify the script and to see a preview of what will be injected. Note that if the same script is applied to many Stored Addresses at once, and if the script allocates memory, each of the Stored Addresses will have memory allocated and so each script will be using different addresses for some things, so the preview will only be accurate for one of the items, but should suffice.

If multiple Stored Addresses are being edited at once, the supplied script will be applied to each of them. You may leave the edit area blank to allow each Stored Address to keep its current script. This means that it is not possible to remove the script from multiple Stored Addresses at once. If the edit area is blank and only one Stored Address is being edited, the script for that item is removed. Set the Use Auto-Assembler for Locking to indeterminate to allow each item to keep their current settings for using the script or not.

[GLOBAL]

This is the default section. The code in this section is injected as long as the Stored Address is using Auto-Assemble for locking, whether the actual item itself is locked or not.

The code starts in this section and remains until another section is specified. You can use the [GLOBAL] tag in the script to return to this section later. As mentioned above, all [GLOBAL] sections resume where the previous [GLOBAL] section left off.

[ENABLE]

The code in this section is injected once when the item is locked. This is not uninjected when the item is unlocked, unless the [DISABLE] section has code to manually uninject it.

[DISABLE]

The code in this section is injected once when the item is unlocked. This is typically used to clean up any messes made by the [ENABLE] code.

Miscellaneous

Other Auto-Assemble commands such as Alloc() and DeAlloc() work the same way as with regular Auto-Assembler scripts. Allocations are only made once when the script is compiled and are only deallocated when the script is fully closed (the Stored Address is deleted or MHS detaches from the target process).

In any case where the script is discarded, the script cleans up after itself with the exception of allocated addresses that have not been used in a DeAlloc() command. All modified RAM, however, is reset to what it was before the script was used. The script is discarded when the Stored Address is removed from the main list, a new script is written for it, or when MHS detaches from the target process for any reason.


Hotkeys

Hotkeys allow you to activate special features at the press of buttons. You can select any key combination for hotkeys. The hotkey settings can be found by pressing Ctrl-K or by selecting the Tools/Hotkeys menu item.

Functionality

Hotkeys contain several useful built-in functions and a function that will call a script function written by the user. Most hotkeys are used to call script functions, as most script functions can only be called through hotkeys.

Built-in functions include showing of dialogs and performing quick sub searches. With hotkeys, you can quickly perform a Same as Before, Different from Before, Increased, Decreased, and Same as Original sub search.

Polling Method

The largest mystery in the hotkeys is the polling method. Hotkeys can be registered in two ways because it is common for one method to be blocked by some games. The standard method for registering a hotkey (non-polling) is through Windows® directly. This method registers the key inside Windows® and allows Windows® to keep track of when the key is pressed, then relay the keypress back to MHS to call your hotkey function. However, this can be caught by other programs as well, and Windows® only allows a key combination to be registered once. The polling method uses a second thread that repeatedly polls the keyboard to manually determine if the hotkey combination is being pressed.

The following table explains the pros and cons of each method.

Method | Pros | Cons
Non-Polling | More responsive. Registered keys are never missed. | Blocked by many games. Only one function can be assigned to a key combination. Key combinations used here override the hotkeys used by MHS, so sometimes shortcuts no longer work.
Polling Method | Multiple functions can be assigned to a single key combination. Not blocked by many games. | Sometimes offers slower response time. Sometimes keys are missed because of the iterative polling.

Options

The following table explains each option.

Option

Description
New

Creates a new hotkey ready to be edited.

Delete

Deletes the selected hotkey from the list.

Up

Moves the selected hotkey up in the list. This only has effect on hotkeys that use the polling method. Polled hotkeys will be triggered in the order shown in the list if multiple hotkeys are activated at once (by assigning multiple functions to a single key combination).
Down | Same as above, but moves the selected hotkey down.
Key | The primary key used to activate the hotkey.
Mod | The optional modifier that must also be held to activate the hotkey.
Function

The function that will be called when the hotkey is activated. For information on making a hotkey call a user-defined script function, see Script Events.

Each function has different parameters (up to 3) whose descriptions appear when the function is selected.

Parm 1 | The first function-specific parameter. If the function uses this, the value you enter will be passed to the function when it is activated.
Parm 2 | The second function-specific parameter. If the function uses this, the value you enter will be passed to the function when it is activated.
Parm 3 | The third function-specific parameter. If the function uses this, the value you enter will be passed to the function when it is activated.
Poll Method

Determines if the hotkey will using polling rather than the normal Windows® hotkey system.

Functions

Hotkeys support many different functions, and each behave differently and accept different parameters (up to 3).

Each time a function is selected it will show the descriptions for the parameters it uses. The following table explains each function and its parameters.

Function | Description | Parm 1 | Parm 2 | Parm 2
NoAction | Does nothing. |  |  |
Poke | Writes values to RAM. | The address where the write will take place. | The value to write. | The number of bytes to write (up to 4).
Beep | Makes a beeping sound incidating the hotkey has been pressed. | The length of the beep. |  |
LoadConverter | Loads the Converter. |  |  |
LoadHexEditor | Loads the Hex Editor. |  |  |
LoadDisassembler | Loads the Disassembler. |  |  |
LoadScriptEditor | Loads the Script Editor. |  |  |
ScriptFunction | Calls a user-written script function. | The number of the hotkey script to call. See Script Events. | The first parameter to pass to the script function. | The second parameter to pass to the script function.
SubSearchSameAsBefore | Performs a Same as Before Sub Search. | If this is 0, the results dialog is not shown. Otherwise it is shown after the search completes. |  |
SubSearchDifFromBefore | Performs a Different from Before Sub Search. | If this is 0, the results dialog is not shown. Otherwise it is shown after the search completes. |  |
SubSearchIncreased | Performs an Increased Sub Search. | If this is 0, the results dialog is not shown. Otherwise it is shown after the search completes. |  |
SubSearchDecreased | Performs a Decreased Sub Search. | If this is 0, the results dialog is not shown. Otherwise it is shown after the search completes. |  |
SubSearchSameAsOriginal | Performs a Same as Original Sub Search. | If this is 0, the results dialog is not shown. Otherwise it is shown after the search completes. |  |
LockStoredAddresses | Locks all stored addresses whose Hotkey Keys match Parm 1. | The Hotkey Key to match when locking stored addresses. If this is 0, all stored addresses are locked. Otherwise, only the stored addresses whose Hotkey Keys match this value are locked. |  |
UnlockStoredAddresses | Unlocks all stored addresses whose Hotkey Keys match Parm 1. | The Hotkey Key to match when unlocking stored addresses. If this is 0, all stored addresses are unlocked. Otherwise, only the stored addresses whose Hotkey Keys match this value are unlocked. |  |


Environment Variables

MHS defines several environment variables and allows you to define your own additional ones. Environment variables can be used in several places. Environment variables are like basic macros that are replaced inside a string with a certain value. Some system environment variables have values that may change several times while MHS is running. Add, edit, or view environment variables by selecting the Tools/Options menu item and selecting the Env. Variables category.

Using Environment Variables

Environment variables can be used with the Preprocessor and inside the Disassembler path options. To use an environment variable inside a string, enclose the variable name inside percent signs (%…%) or inside dollar-sign prefixed parentheses ($(…)). For example, if the variable name is VCInstallDir, we can use it in a string by typing $(VCInstallDir)includes or %VCInstallDir%includes.

Names of environment variables can only contain numbers, letters, and underscores, and the first character of the name must not be a number.

System Environment Variables

The system defines several environment variables for you. These values change depending on certain things, and some of them change several times while MHS is running.

Variable | Meaning
MHSPath | The fully qualified path to the MHS executable, including the file name.
MHSDir | The directory where the MHS executable resides.
MHSProfile | The fully qualified path of the DefProf.ini file used by MHS to keep track of system-wide settings.
TargetName | The name of the target process, or an empty string if there is no target process.
TargetPath | Fully qualified path to the target process, or an empty string if there is no target process.
TargetDir | The directory in which the target process resides, or an empty string if there is no target process.

User Environment Variables

Users may add their own environment variables, following the rules listed above. There are no restrictions on the values of the variables except that the length must be shorter than 2,048 characters. User environment variables are static and never change.

Users may not override system-defined environment variables. If the user defines the same variable twice, the previous definition is overwritten.

Selecting an environment variable from the list places its name and value into the edit boxes for ease in editing and for the ability to copy/paste the values elsewhere.


Converter

The Converter is one of the most useful tools for general hacking. It allows you to quickly convert from any type to any other type, in both big and little endian. The left side is Intel® (little endian) and the right side is Motorola® (big endian).

Alternative displays change the output for strings, floats, and doubles. Strings will be represented using control-escape sequences where appropriate to create a string that can be copied into code. Floats and doubles will be represented in scientific notation. The Converter can be opened by either selecting the Tools/Converter menu item or by hitting the Converter toolbar button.

Additional Notes

The Converter may be used to convert integers to hexadecimal for code. Because the notation used when writing hexadecimal numbers in code is reversed of how they are stored, you should get the hexadecimal sequence from the side opposite the one used to enter the number you wish to convert. For example, in the picture above, the 1,113,128,960 integer is represented as 0x42590000 (taken from the opposite side) and the 22,850 integer is 0x5942. This applies to all types but String and Binary, whose hexadecimal equivilants will be on the same side.


RAM Watcher

The RAM Watcher allows you to view RAM in real-time. The display allows you to see the specified segment of RAM in multiple types at once.

he display types are, from left to right, hexadecimal, decimal, character, short, unsigned short, integer, unsigned long, float, void, and binary. The RAM Watcher can be opened by either selecting the Tools/RAM Watcher menu item, or by pressing Ctrl-R.

Options

The following chart explains the options in the RAM Watcher.

Option | Meaning
Total | The number of addresses to watch.
From | The address from which to begin watching.
Jump | The amount by which to jump when pressing Page Up and Page Down.

Additional Notes

Pressing Page Up and Page Down allows you to scroll up and down the RAM by the amount specified in Up/Down Jump.

To modify an address, right-click it and select Modify from the pop-up menu.

To add an address to the main list, right-click it and select Add Address.


Struct Builder

Memory Hacking Software comes with over 1,800 predefined structures and over 4,500 predefined typedefs, and fully supports adding custom structures and typedefs as well. The Data Center acts as the gateway to the structure editor and typedef editor. Access the Data Center by pressing Ctrl-T or by selecting the Tools/Structure Builder menu item from the main window.

Use of Structures

Structures are used by game and software programmers to group related data. The order in which programmers add data to structures in their games or software is also the order in which they will appear in RAM. Since they are together in the structure, they will also appear together in RAM, and always in the same order. Therefore, in the most basic case, structures in Memory Hacking Software are used to give meaning to sets of data, so that each address may be displayed correctly based on its type. This is very helpful while reversing parts of games; the player data (health, weapon, magic, etc.) will be organized into a structure that the user can map using Memory Hacking Software’s StructBuilder. The user can then apply this structure over the RAM in the Hex Editor to view all the data related to his or her player easily.

Memory Hacking Software structures allow extra functionality for mapping structures with variable sizes as well. Array lengths can be dynamic, changing size based on the value in another structure member, which allows dynamic mapping of all kinds of data.

Use of Typedefs

Typedefs simply give alternate names to types (structures or other typedefs), and include information regarding the number of pointers and the elements in an array. These are not required for making structures, but most predefined structures use them and they can help in the same way they help in real coding—by wrapping type, pointer, and array information into a single name.

Like in real coding, if a structure member has a type that is a typedef, and both the structure member and is type contain pointers, the total number of pointers for the member is the sum of both its own pointers and the pointers in its type.

Editing/Creating Structures

The Structures tab allows you to either start creating a new structure or edit an existing one.

To create a new structure, enter its name into the Name edit and click Create. The name must be valid and must not already be used by either a custom structure (or typedef) or a predefined structure (or typedef).

The Modify Existing list shows both predefined and custom structures. All are available for editing, which can be done either by double-clicking the item in the list or by selecting it and pressing Modify.

To remove a custom structure, select it in the list and right-click and select the Delete Selected pop-up menu item.

When modifying a predefined structure, the structure is converted to an editable format and from that point forward the system will use your modified version instead of the predefined version. The predefined version still exists, however, and can never be removed from the system. Therefore, if you delete your modified version later, the structure will still appear in the list as the original predefined version. It can be modified again as you please.

Editing/Creating Typedefs

The Typedefs tab allows you to either start creating a new typedef or edit an existing one.

To create a new typedef, enter its name into the Name edit and click Create. The name must be valid and must not already be used by either a custom typedef (or structure) or a predefined typedef (or structure).

The Modify Existing list shows both predefined and custom typedefs. All are available for editing, which can be done either by double-clicking the item in the list or by selecting it and pressing Modify.

When modifying a predefined typedef, the typedef is converted to an editable format and from that point forward the system will use your modified version instead of the predefined version. The predefined version still exists, however, and can never be removed from the system. Therefore, if you delete your modified version later, the typedef will still appear in the list as the original predefined version. It can be modified again as you please.

Editing a Structure

When selecting an item or creating a new item in the Structures tab of the Data Center, the Structure Builder window is shown.

The name can be changed by entering a new name in the Name edit.

The number of members in the structure and the size of the structure is shown in the Info group.

When finished modifying the structure (see below), press OK to accept the changes or Cancel to discard them.

The New tab is used to add new members to the structure. The following table explains each option.

Field | Meaning
Name | The name of the member to be added. Must be a valid identifier.
Bitfield | Determines if the item is a bitfield or not. This changes the meaning of the Pointers field.
Offset | Offset of the member inside the structure.
Auto-Offset | If checked, items are added to the exact end of the structure. Otherwise, the value in the Offset field is used to determine the offset within the structure.
Array | If checked, the item is an array and Elements needs to be filled. Not valid for bitfields.
Elements | The number of elements in the array.
Pointers | The number of pointers in the item. If the item is a typedef and the typedef contains pointers, the final number of pointers for this item is the sum of this number and the number in its typedef.
Bits | If Bitfield is checked, this specifies the number of bits in the item.
Type | The type of the item. Can be any basic type, structure, or typedef.
Add | Adds the item to the structure.
Insert | If checked, the item pushes trailing members down.

The Modify tab is used to modify existing members. The following table explains each option.

Field | Meaning
Name | The name of the member. Must be a valid identifier.
Bitfield | Determines if the item is a bitfield or not. This changes the meaning of the Pointers field.
Offset | Offset of the member inside the structure.
Array | If checked, the item is an array and Elements needs to be filled. Not valid for bitfields.
Elements | The number of elements in the array.
Pointers | The number of pointers in the item. If the item is a typedef and the typedef contains pointers, the final number of pointers for this item is the sum of this number and the number in its typedef.
Bits | If Bitfield is checked, this specifies the number of bits in the item.
Type | The type of the item. Can be any basic type, structure, or typedef.
Update | Applies the changes to the item. The item is not modified until this is pressed.

The Dynamic Array tab is used to apply a dynamic array to a member. The following table explains each option.

Field | Meaning
Name | The name of the member. Can not be edited.
Dynamic Array | The dynamic-array expression.
Use Dynamic Array | If checked, the dynamic array will be used instead of the basic array settings.
Apply | Applies the changes to the item. The item is not modified until this is pressed.

Dyamic arrays are arrays with variable lengths. The length of the array usually depends on a value somewhere else in the structure. The expression therefore allows to specify the names of other structure members and includes the special “::” operator to access members nested inside other members.

If the expression is constant it will be rejected; although a constant expression is technically valid, it is much more efficient to specify constant array lengths using the basic array settings. Dynamic array lengths imply a small amount of overhead (though map look-up tables and binary searches are used to ensure the overhead is minimal).

Caution: It is not guaranteed that the structure will always be applied to data that matches the structure. In the picture, we use AddressOfNewExeHeader to determine the array length, but if the value of AddressOfNewExeHeader is high when the structure is applied to RAM or to a file, it will result in a very long array which can cause Memory Hacking Software to stop responding for a long time while it maps an item to every index in the array. To avoid this, sanitize the array length with a % or & operator as shown in the picture.

Editing a Typedef

When selecting an item or creating a new item in the Typedefs tab of the Data Center, the Typedef Editor window is shown. The following table explains each field:

Field | Meaning
Name | The name of the typedef.
Array | Whether the typedef contains array information. If so, Elements must also be filled.
Elements | The number of elements in the array, if it is to contain array information.
Pointers | Number of pointers in the typedef.
Type | The base type of the typedef.
OK | Applies the changes to the typedef. The typedef is not modified until this is pressed.
Cancel | Closes the window without applying the changes. The typedef is not modified.


Expression Evaluator

The Real-Time Expression Evaluator evaluates interactive expressions able to use values from the target process. It evaluates all C/C++ mathematical operators and parenthesis can be used to enforce order of evaluation. It also accepts square bracket ([ ]) operators to indicate a value should be obtained from the target process.

The Expression Evaluator dialog can be docked onto the main window. All expressions added are remembered. While typing expressions, current values are shown in the box below. Add expressions to the list with the + button. Remove selected expressions with the - button.

Expressions can be copied from the list to the clipboard by right-clicking them in the list and selecting Copy Expression.

Operators

The Real-Time Expression Evaluator supports the following operators.

  • Math
    • - (minus) 34 - 3
    • + (plus) 60 + 4
    • * (multiplication) 32 * 23
    • / (division) 1024 / 32
    • % (modulus) 512 % 8
  • Unary Prefixes
    • - (unary negation) -120
    • + (unary positive) +56
    • ! (unary NOT) !90
    • ~ (unary one’s compliment) ~32
  • Bitwise Operators
    • & (bitwise AND) 67 & 15
    • | (bitwise OR) 1024 | 1
    • ^ (bitwise XOR) 37 ^ 1
    • << (bitwise shift left) 1 << 3
    • >> (bitwise shift right) 16 >> 4
  • Equality Operators
    • < (less than) 1 < 0
    • > (greater than) 1 > 0
    • <= (less than or equal) 67 <= 90
    • >= (greater than or equal) 89 >= 50
    • == (equals) 45 == 32
    • != (not equal to) 45 != 32
  • Logical Operators
    • && (AND) 45 && 32
    • || (OR) 0 || 23

Examples of basic expressions using these operators:

  • 32 + 56 (result = 88)
  • 32 + 56 * 3 (result = 200)
  • -5 - 90 * 16 % 7 (result = -10)
  • 67 * 5 & 34 (result = 2)
  • 0 && 21 (result = 0)
  • 0 || 21 (result = 1)

Order of Evaluation

Expressions are evaluated with standard operator precedence.

  • ( ) and [ ]
  • ~, !, - (unary), and + (unary)
  • *, /, and %
  • - and +
  • << and >>
  • <, >, <=, and >=
  • &
  • ^
  • |
  • &&
  • ||
  • ?: (conditional)

Numeric Formats

The expression evaluator supports all standard numeric formats.

  • Decimal
    • A number using only digits from 0 to 9 that does not begin with a 0. For example, 56, 897, and 86878 are all decimal numbers.
  • Octal
    • A number using only digits from 0 to 7. The first digit must be a 0. For example, 0, 076, and 0534 are all octal numbers.
  • Hexadecimal
    • A number using only digits from 0 to 9 and characters from A to F (case insensitive). For example, C, C001, and 0100533a are all hexadecimal numbers.
    • Any of the above formats with an 0x or 0X prefix, or an h or H postfix. For example, 0x0, 0x67, 89h, and 07A89DEH are all hexadecimal numbers.
  • Floating-Point
    • A number using only digits from 0 to 9 and with a decimal (.), optional exponent, and optional f or F postfix. For example, 0.0, 3.1415963, and 88.5e-3f are all floating-point numbers.
  • Extra
    • The l or L postfix may be used on any number. This has no effect on the number itself but allows compliance with C/C++ numeric formats.
    • Characters are also resolved into numeric constants. For example, 'E' resolves to 69. Escape sequences are also allowed, so '\x2E' resolves to 46 (0x2E), and '\r' resolves to 13. The l or L prefix is allowed on character constants, however this has no effect on the character or its value.

Working With the Target Process

Values used in the expression can come from the target process. These expressions are considered interactive because the values can change at any time. These expressions are evaluated in real-time to show their current values at all times.

The following explains the operators and key information for getting values from the target process.

  • Square brackets ([ ]) indicate that a value should be obtained from the target process. The expression inside the brackets indicates the address from where to get the value. Any numeric value inside the brackets is converted to an unsigned integral value and treated as the address in the target process from where the value is obtained.
    • [0x01005334] gets the value from the target process at address 0x01005334.
    • [0x01005334] * [0x01005338] gets the values from 0x01005334 and 0x01005338 in the target process and multiplies them for the final result.
    • [[0x01005334]+0x44] gets the value from 0x01005334 in the target process, adds 0x44 to it, and then gets that value from that address in the target process.
  • By default the value obtained from the target process is in DWORD format, however prefixes on the brackets can be used to change this.
    • b[ ] gets a byte value.
    • w[ ] gets a word value.
    • [ ] (no prefix) gets a dword value.
    • q[ ] gets qword value.
    • f[ ] gets a float value.
    • d[ ] gets a double value.
  • Module names can be specified in two formats.
    • Format: [identifier].[identifier]. This is the normal format which can be used to specify modules containing only alpha-numeric characters and underscores, and that contain an extension.
    • The second format is a quote-encapsulated C/C++ string, including escape sequences.
    • Given the above two formats, the following are valid module identifiers:
      • lspiro.exe
      • winmine.exe
      • gamex86.dll
      • "winmine.exe"
      • "\xFFgame.ocx"
      • "Great Game.exe"
  • Special operators are available for getting the size of a module and its ending address.
    • Format: SO( MODULE )
      • MODULE can be any module name in any format specified above. This gets the size of the loaded module in bytes.
    • Format: EO( MODULE )
      • This gets the ending address of the module. This value is inclusive. An example value returned by this operator could be 0x0042CFFF.

Module names are resolved into the actual address of the module (unless used inside a special operator) as a 64-bit unsigned integer type. This means an expression such as gamex86.dll+0x154C is valid. gamex86.dll becomes a numeric value and is added to 0x154C for the final result.

When used inside a special operator (SO or EO), the result is not the module base but the return of the operator as a 64-bit unsigned integer type.

Evaluation Types

Numeric values are ultimately expressed in any of 3 ways: unsigned 64-bit integers, signed 64-bit integers, and 64-bit doubles. Any evaluations on floating-point values in the expression have the result of a floating-point type. Next, if any evaluation takes place on a signed value (created only by using the - unary prefix), the result is signed. Finally, all other values are unsigned integral types. The results of <, >, <=, >=, ==, !=, &&, and || operators are always a 64-bit unsigned integer with a value of either 0 (false) or 1 (true).

Options

The speed of the real-time updating can be adjusted in the General options menu. Go to Tools/Options/General and adjust the speed at which the Expression Evaluator is updated.


Hex Editor

The Hex Editor allows you to edit binary files and view/edit RAM in real-time. The Hex Editor can be opened by pressing Ctrl-H, selecting the Tools/Hex Editor menu item, or by hitting the Hex Editor toolbar button. The topics in this section cover the individual areas of the Hex Editor.


Hex Editor - Layout

This page explains each area of the Hex Editor layout. The Hex Editor is divided into two modes: Viewing Mode and Editing Mode.

Viewing Mode

The Viewing Mode is used to view RAM or files. This is the general working mode of the Hex Editor.

Number | Name | Description
Navigator | Allows changing of the Hex Editor view area. See Hex Editor - Helper/Navigation for details.
Helper Tabs | Various additional assistance provided by the Helper. See Hex Editor - Helper/Info, Hex Editor - Helper/Conversions, and Hex Editor - Helper/Bookmarks.
Chunk Boundary Line | A line that displays chunk boundaries in RAM. Not used when mapping files. Default color is green.
Selection | The currently selected bytes. Default color is dark blue. Text inside the selection area is inverted.
Cursor | The cursor (or caret) is represented by an inverse-color line under the selected byte, with a bent left side.
Address Area

The addresses of the left-most bytes on a line. There are 16 bytes on a line, so the addresses increment by 16.

Addresses can be displayed in 4 ways. See Hex Addresses below.
Hex Area | Each byte value is shown in hexadecimal form.
Data Area | Each value is displayed in the selected data type.
Pointers | Pointers are highlighted. Purple is the default color for pointers. This applies only to RAM.
Selection Start | Displays the address of the start of a selection. If there is no selection, it displays the cursor position.
Selection Length | Displays the length of the selection. If there is no selection, this is blank.
Edit-Insertion Mode

Displays the insertion mode that will be performed when edits are made.

Symbol | Meaning
[OVR] | If an edit is confirmed, the edit will overwrite the bytes under it. This is always used for RAM.
[INS] | If an edit is confirmed, the edit will be inserted.

Switch edit-insertion modes by hitting Ins.

This should not be confused with the insertion mode used during editing. The insertion mode used during editing (INS/OVR) depicts how bytes are entered into the edit itself, while this edit-insertion mode depicts how the entire edit itself is written once confirmed.

Ruler | Displays the lowest 4 bits of the addresses under each ruler segment, in hexadecimal form.

Editing Mode

To enter Editing Mode, simply start typing into either the Hex Area or the Data Area. Press Enter to confirm an edit and Escape to cancel an edit.

Number | Name | Description
Edit Box | A preview box showing the contents of the edit you wish to make. It has a border and its insides are lightened, clearly indicating the editing region. The region sizes itself based on the size of the edit you are making.
Caret

Text is entered at the caret position. Whether the text is inserted or overwrites depends on the edit mode, which is not to be confused with the edit-insertion mode, used to determine whether the entire edit as a whole is inserted or overwrites.

If editing begins in the Hex Area, the input for the edit must be in hex. If editing begins in the Data Area, the input for the edit is in characters. If in the Data Area, valid input includes escape sequences. For example, typing “\t” will result in a tab character being entered into the edit. Rules of escape sequences apply, so to type a “\” without allowing it to become part of an escape sequence, enter “\\” instead.

Edit Selection | Click and drag your mouse inside the Edit Box to select parts of your edit.
Background Selection | The background selection (the selection of bytes from the source file or RAM, not related to your edit) is shown a bit greyed out. You can still select data behind the Edit Box by holding Ctrl while dragging over the data you wish to select.
Background Selection (Under Edit Box) | If the edit-insertion mode (not to be confused with the edit mode) is [OVR], you can select areas directly under the Edit Box by holding Ctrl while dragging the mouse. If the edit-insertion mode is [INS], there is technically nothing under the Edit Box, so dragging a selection under this area will either select all or none of the area behind the Edit Box.
Next Byte | The first byte that will not be affected by your edit is shown as transparent under the Edit Box. Typing over this byte will cause the Edit Box to extend one square, showing the next byte as transparent.

While in Editing Mode, the edit-insertion mode is no longer displayed and the edit mode is. Note the difference: the edit-insertion mode has square brackets while the edit mode (shown here) does not. The edit mode can be INS or OVR, and indicates whether values are inserted or overwrite inside the Edit Box. When an edit is complete and you confirm by pressing Enter, the edit-insertion mode determines whether the full edit itself is inserted or overwrites.

Simply entering Edit Mode does not cause any external changes—neither files nor RAM are modified in any way until the edit is confirmed by pressing Enter.

All modifications made in the Hex Editor can be undone with Ctrl-Z and redone with Ctrl-Y.

Hex Addresses

Addresses can be shown in hexadecimal or decimal, and in offsets or as absolute values. The following chart explains each mode.

Mode | Description | Keys
Hexadecimal | Addresses are shown in hexadecimal form. This is the standard form. | To toggle between hexadecimal and decimal displays, hold Ctrl and Shift and click in the address area. You can also directly select the display type you prefer by using the View/Addresses menu.
Decimal | Addresses are shown in decimal form.
Hexadecimal/Offsets | Addresses are displayed as offsets from the origin in hexadecimal form. | To switch to viewing as offsets, hold Ctrl and click in the address area. The origin is determined by the address clicked. To go back to the normal view, click in the address area without holding Ctrl.
Decimal/Offsets | Addresses are displayed as offsets from the origin in decimal form.


Hex Editor - Layers

The Hex Editor display is broken into layers that are stacked on top of each other. By default, the display contains a base layer (for all primary displays) and a global layer (which displays all global variables declared in the Disassembler).

Just imagine Hex Editor layers in the same way as Adobe® Photoshop® layers, where each layer stacks on top of each other to form the whole image. In the Hex Editor, the base layer is often the only layer you will see.

The Base Layer

The base layer is responsible for all of the default display of the Hex Editor. It draws the hex area and the data area. Depending on the display selection, it will draw the data area as characters, bytes, ints, shorts, longs, floats, etc. It highlights pointers and changed bytes. And drawing the data area in characters, it will show either ASCII or ASCII and ANSI (characters above 127). It will color the display area according to the color options you have chosen. For example, by default, static RAM is drawn as blue. The color for the static area is a system option, but the base layer is responsible for using that color to draw the background of the Hex Editor.

The Global Layer

The global layer only draws globals you have defined in the Disassembler. It is directly on top of the base layer, so it is drawn over the base layer. It displays their names and values.

Custom Layers

Each layer (including the default ones listed above) is implemented as a plug-in. Users can write their own DLL plug-ins to add new types of layers to display information. This feature is currently under development, so it is not available yet.

Layer Options

Each layer (including custom layers) can optionally have its own set of options. The global layer currently has no options, though it will in the future. The base layer options can be found under the View/Base Options menu item.

The base-layer options are divided into two parts: global options (Defaults) and per-layer (Current Layer) options.

The global options (Defaults) are used when new Hex Editor windows are opened. These specify the default options applied to that window.

You can change the options for each layer indivually by using the per-layer (Current Layer) options in the bottom half of the dialog. The options chosen here will only be applied to the Hex Editor window.

The following chart explains each option.

Option | Meaning
ASCII | Show only printable characters from 0 to 127 in the data area. Only has meaning if the Data Type is Char.
ASCII+ANSI | Show only printable characters from 0 to 255 in the data area. Only has meaning if the Data Type is Char.
Data Type | The type of data to be displayed in the data area. Types are char, byte, short, unsigned short, long, unsigned long, int, float, double, and pointer. Type pointer is used to display the data as 32-bit hexadecimal numbers.
Show Pointers | Determines whether or not pointers are highlighted when viewing RAM.
Show Changed | Determines whether or not changed values are highlighted.


Hex Editor - Options

The Hex Editor provides many options for customization.

The Font

Select the View/Font menu item to change the font. You may select any font, style, size, effect, script, and color you wish.

The Colors

Select the View/Colors menu item to change the colors. Click on the colored box of the item whose color you wish to change.

The Hex Editor blends colors that overlap, with the exception of the Chunks (RAM Only) line and the Selection. This is why Pointers (RAM Only) will appear lighter than shown in the dialog, as they are blended with the background color, which is usually white.

The following table explains each color.

Color | Meaning
Background | The base background color.
Text | The base text color. Can also be changed from the Font options.
Pointers (RAM Only) | Color used to highlight pointers in RAM.
Executable (RAM Only) | Background color used to display RAM that is static/executable. Overrides the Background color.
Unreadable (RAM Only) | Background color used to display RAM that is unreadable. Overrides the Background color.
Breakpoint (RAM Only) | Color used to highlight breakpoints in RAM. Currently not used.
Selection | Color used to highlight the selected area.
Changed | Color used to highlight bytes that have changed.
Chunks (RAM Only) | Color of the line used to draw the chunk boundaries in RAM.
Out of Bounds | Color used to highlight areas past the end of files and areas outside of the view range. For example, if the view range starts at 0x4, the display will show starting at 0x0 because it rounds to the nearest 16, but the first 4 bytes will be this color because they are not meant to be part of the view area.

The Ruler

Toggle the Ruler on and off by checking or unchecking the View/Ruler menu item.

The Cell Widths

The data area can be resized to allow larger data types to be fully shown.

Here we have changed the data type to short, and the data area isn’t large enough to display each short entirely.

Right-click the Hex Editor to see the pop-up menu. Select the Set Cell Widths menu item.

Select the new width for the cells. The default is 1.25.

Cell widths are multipliers of the font width, so if you select a larger font, the cells will be spaced further apart to compensate. The cell width is the result of taking this number and multiplying it by the width of the widest character in the font.

Now the data area is large enough to display its contents.


Hex Editor - Utilities

The Hex Editor provides several useful utilities to help navigate and work with files and RAM.

Bookmarks

Bookmarks indicate that certain bytes in a file or RAM are significant. Bookmarks can be interpreted as any standard type, and in the future they will also support interpretation as types defined by templates. Bookmarks are assigned colors and stand out from the normal display. Bookmarks can be browsed using the Bookmarks tab in the Helper.

To add a bookmark, press Ctrl-B or select the Search/Add/Edit Bookmark menu item. If a bookmark exists at the address where the caret is, the bookmark will be modified. Otherwise, a new bookmark will be created at the address of the caret.

Here you can specify the name, type, color, and array size of the bookmark, and determine if it follows the mouse or not.

The following chart explains each option.

Option | Meaning
Name | Name of the bookmark.
Type | Interpretation of the bookmark.
Array Size | If an array of the specified type is desired, supply the array size, or 1 for no array.
Move Bookmark With Cursor | If checked, the bookmark will move to each location where the mouse is clicked.
Color | Color of the bookmark.

To remove a bookmark, click the address of the bookmark or select the bookmark in the Helper, then either press Ctrl-Shift-B or select the Search/Remove Bookmark menu item.

To remove all bookmarks, press Ctrl-Shift-F2 or select the Search/Clear All Bookmarks menu item.

To go to the next bookmark after the cursor, press F2 or select the Search/Next Bookmark menu item.

To go to the previous bookmark, press Ctrl-F2 or select the Search/Previous Bookmark menu item.

Find

The Find utility allows you to find values of various types of data and supports wildcard (? and *) and regular-expression searches (see Wildcards and Regular Expressions).

Access the Find utility by pressing Ctrl-F or by selecting the Search/Find... menu item.

You can seach up and down, find all cases or the next/previous case, search the entire file/RAM or just the selection, and all with any type of data.

If Find All is checked, the primary button reads Find All. If Find All is not checked, the primary button reads Find Next if the search direction is Down and Find Previous if the search direction is Up.

The following chart explains each option.

Option | Meaning
Type | The type of data to find.
Value

The value to find. The format of the value depends on the selected Type.

Type | Format
ASCII String | Any text string.
Unicode String | Same as above.
Hex Bytes | A hex string where each hex value should be separated by a space.
Signed Byte | An integer value in the range of a char. See Data Types.
Unsigned Byte | An integer value in the range of a byte. See Data Types.
Signed Short | An integer value in the range of a short. See Data Types.
Unsigned Short | An integer value in the range of an unsigned short . See Data Types.
Signed Long | An integer value in the range of a long. See Data Types.
Unsigned Long | An integer value in the range of an unsigned long . See Data Types.
Signed 64-bit Int | An integer value in the range of a 64-bit integer . See Data Types.
Unsigned 64-bit Int | An integer value in the range of an unsigned 64-bit integer . See Data Types.
Float | A floating-point value.
Double | A floating-point value.
Hex Bytes | Shows the actual hex bytes that will be found in the file or RAM.
Match Case | If using an ASCII String or Unicode String search, this indicates that the string is case-sensitive. This is not used in regular-expression searches.
Match Whole Word Only | If using an ASCII String or Unicode String search, this indicates that only full words will be found. For example, a search for ro would find a match in [lo-ro.] but would not find a match in [L. Spiro], because the ro here is part of a longer word. This is not used in regular-expression searches.
Use Wildcards | Only used with ASCII String searches. This indicates that the string in the Value box is in wildcard fomat. See Wildcards and Regular Expressions.
Use Regular Expressions

Only used with ASCII String searches. This indicates that the string in the Value box is in regular-expression fomat. See Wildcards and Regular Expressions.

Regular expressions always scan from the top and go down.

Find All | This option will cause the search to cover the entire range specified and each item that is found will be bookmarked and highlighted. The bookmarks work the same as bookmarks set by the user except that they are not added to the Helper bookmark list.
Up | Specifies that the search should go upwards instead of downwards. Does not apply in Find All searches because they always scan the entire range, starting from the top down.
Down | Specifies that the search should go downwards. Does not apply in Find All searches because they always scan the entire range, starting from the top down.
Entire File | Specifies that the scan will cover the entire file or RAM.
Selection | Specifies that the scan will only cover the selected area.

To quickly find the next occurrence, press F3 or select the Search/Find Next menu item.

To quickly find the previous occurrence, press Ctrl-F3 or select the Search/Find Previous menu item.

Replace

Press Ctrl-R or select the Search/Replace menu item to load the Replace dialog. The options here are exactly the same as in the Find dialog, with a few exceptions.

The primary difference is that when matches are found, they are replaced with the value in the Replace With section. No bookmarks are set when replacing all occurrences.

If Replace All is checked, the primary button reads Replace All. If Replace All is not checked, the primary button read Replace Next if the search direction is Down and Replace Prev if the search direction is Up.

To perform the replacing action again, press F4 or select the Search/Replace Again menu item.

Find/Replace Timeout

If a scan takes longer than 30 seconds, a dialog will appear to give you some options.

You can halt the operation, wait and be prompted again later, or give the operation all the time it needs. If you give the operation its time, you have another option to be notified by a dialog when the operation is completed. This option also makes a beep to alert you that the operation is done.

Find/Replace Options

Pressing the Options button on the Find and Replace dialogs displays the options for the find/replace operation.

The following chart explains each option.

Option | Meaning
Maximum Wildcard Match Length | The maximum number of characters matched by the * wildcard character. See Wildcards and Regular Expressions.
Bookmark Color for “Find All” | Specifies the color of the bookmarks added by the Find All operation.
Macro Name (Must Begin With '$') | When creating a regular-expression macro (see Wildcards and Regular Expressions), this is the name of the macro, including the prefixed $.
Macro Definition | This is the regular expression associated with the macro name in Macro Name (Must Begin With '$').
Add | Adds your custom regular-expression macro to the list. If the macro exists it is overwritten. System-defined regular -expression macros can not be overwritten.
Remove | Remove the selected macro from the list. System-defined regular-expression macros can not be removed.

Go to...

The Go to... utility allows you to go to a specific address in the file or RAM.

You can enter the address or offset in either hex or decimal.

To go directly to an address, select From Beginning of File and enter the address directly. Otherwise the address field is an offset, either from the current position or from the end of the file. From End of File is not available in RAM.

If From End of File is checked, the offset in the Address field implies the number of bytes before the end of the file where you would like to go. For example, entering 5 here would take you to the position 5 bytes before the end of the file.


Hex Editor - Helper/Navigation

The Helper dockable window allows you to navigate the file/RAM being viewed. The Hex Editor can navigate anywhere within a 4-gigabyte range, even if that implies viewing regions outside of the range of the file or RAM.

Navigation is composed of a From number and a Total number. The From value indicates the starting address from which the view is mapped. The Total value indicates the total number of addresses to map into the view. This system allows viewing large files or areas of RAM without creating a tiny scrollbar, which would result in very clunky scrolling.

When loading files, the view is mapped over the entire file by default. When loading a process, the view is mapped over 0x10000 bytes from 0x00400000 by default.


Hex Editor - Helper/Info

The Info tab in the Helper displays extra information related to the current file or process. If editing a file, information regarding the file path, size, edited size, creation time, and modification time is displayed.

If modifying a process, the process chunks are listed along with modules. Chunks that belong to modules are indented.

Controls

Right-click the listview to access a pop-up menu that allows you to copy text from the cells of the listview.


Hex Editor - Helper/Conversions

The Conversions tab in the Helper displays the selected location cast to multiple types at once.

The following chart explains each cast type.

Type | Description
Char | Shows the byte at the cursor position as a character and a decimal. Non-printable characters are shown as escape sequences and some characters also display a description.
Byte | Shows the byte at the cursor position in decimal form.
Short | Shows the first 2 bytes at the cursor position in decimal form.
Unsigned Short | Shows the first 2 bytes at the cursor position in decimal form.
Long | Shows the first 4 bytes at the cursor position in decimal form.
Unsigned Long | Shows the first 4 bytes at the cursor position in decimal form.
Int 64 | Shows the first 8 bytes at the cursor position in decimal form.
Unsigned Int 64 | Shows the first 8 bytes at the cursor position in decimal form.
Float | Shows the first 4 bytes at the cursor position in float form.
Double | Shows the first 8 bytes at the cursor position in double form.
String | Shows all bytes from the cursor position up to the first NULL byte or 256 characters as a character string.
Printable String | Shows the first 256 bytes from the cursor position as a string that can be copied into code, where non-printable characters are displayed as escape sequences.
Void * | Shows the first 4 bytes from the cursor position as a generic pointer in hexadecimal form.
3-D Float Vector | Shows the first 12 bytes from the cursor position as a 3-D vector composed of floats X, Y, and Z.

Controls

Right-click the listview to access a pop-up menu that allows you to copy text from the cells of the listview.


Hex Editor - Helper/Bookmarks

The Bookmarks tab in the Helper displays all bookmarks in the current Hex Editor window, along with its address, type, name, and size. Bookmarks that have been added as arrays can have their array elements collapsed and retracted to avoid clutter.

Controls

Right-click the listview to access a pop-up menu that allows you to copy text from the cells of the listview.

Click an item in the list to select it in the current Hex Editor.


Hex Editor - Templates

Dynamic templates (or structures) can be mapped to RAM or to files in the Hex Editor. For information on how to create structures/templates, see Struct Builder.

When a structure/template has been mapped, it will appear in the Template dockable window.

Each Hex Editor tab may have one structure/template mapped at a time. To map a structure/template, click the Load Template toolbar button or right-click the desired address and select the Templates/Add Here item from the pop-up menu.

The Select Structure dialog will appear, allowing you to select any predefined or custom structure and to specify the address where it should be applied in RAM or in a file.

The address can be any valid expression.

Pressing OK causes the selected structure/template to be dynamically mapped at the specified address.

To move the template to a new location, right-click at the new address and select the Templates/Move Here item from the pop-up menu.

Functionality

When a structure/template is mapped, it becomes easy to see the types and values of the RAM or file using the Template dockable window. Use this as a reference while studying the target process or a file.

Click or otherwise select any item in the Template dockable window to highlight the respective item in RAM.


Hex Editor - Conversions

The conversion tools supplied with MHS can be used to convert bytes between ASCII format and EBCDIC format or vice versa. Select the Tools/Convert/ASCII to EBCDIC or Tools/Convert/EBCDIC to ASCII menu items to perform the conversion.

Conversion tools also exist to convert between ASCII format (1 byte per character) and UNICODE format (2 bytes per character) or vise versa. Use the ' Tools/Convert/ASCII to UNICODE or Tools/Convert/UNICODE to ASCII menu items to perform these conversions. Note that when converting to UNICODE, bytes will be inserted into the file (for example a string which is 4 bytes long in ASCII would be 8 bytes long in UNICODE). Similarly when converting from UNICODE to ASCII, bytes will be removed from the file. When working with RAM, a conversion from ASCII to UNICODE will overwrite the bytes beyond the selection, and a conversion from UNICODE to ASCII will fill the empty space inside the selection with 0’s. When working with RAM, the selection is always used.

If no bytes are selected in the file when the conversion is performed, the conversion will be applied to every byte in the file. If a selection is made, the Convert Selection Only? dialog box will be displayed. In the dialog box, press Yes to convert only the selected bytes (the default), or No to convert the whole file. Clicking Cancel will close the dialog.


Hex Editor - Operations

The Operation tool provides any easy way to apply mathematical operations to a set of bytes. Open the Operation dialog by clicking the Tools/Operations menu item and selecting an operation from the list.

The Operation treats the bytes in the file as an array. Select the data type of the array by choosing a type from the Treat Data As drop-down list. Enter a number into the Operand field using any of the formats described in the Introduction to Number Systems. Note that the operand is assumed to be hex if Hex is checked, or decimal if Decimal is checked. How the Operand is applied to the data is dependant upon which operation is selected. The following list describes each operation in C notation, assuming that X[I] represents each value in the file to be modified.

  • Assign: X[I] = Operand.
  • Add: X[I] += Operand (this is equivalent to X[I] = X[I] + Operand).
  • Subtract: X[I] -= Operand (this is equivalent to X[I] = X[I] - Operand).
  • Multiply: X[I] *= Operand (this is equivalent to X[I] = X[I] * Operand).
  • Divide: X[I] /= Operand (this is equivalent to X[I] = X[I] / Operand).
  • Negate: X[I] = -X[I].
  • Modulus: X[I] = X[I] % Operand (the modulus operator % computes the remainder after dividing X[I] by the Operand).
  • Set Minimum: Sets a minimum limit for X[I]. If X[I] is less than the Operand, X[I] is set to the Operand.
  • Set Maximum: Sets a maximum limit for X[I]. If X[I] is greater than the Operand, X[I] is set to the Operand.
  • Swap Bytes: Swap the bytes of X[I].
  • Binary And: X[I] &= Operand.
  • Binary Or: X[I] |= Operand.
  • Binary Xor: X[I] ^= Operand.
  • Binary Invert: X[I] = ~X[I].
  • Shift Left: X[I] <<= Operand.
  • Shift Right: X[I] >>= Operand.
  • Rotate Left: Similar to Shift Left except that bytes shifted off of X[I] will be added to the right side of X[I].
  • Rotate Right: Similar to Shift Right except that bytes shifted off of X[I] will be added to the left side of X[I].
  • Upper-Case: Converts all lower-case letters to upper-case. All other characters will be unchanged.
  • Lower-Case: Converts all upper-case letters to lower-case. All other characters will be unchanged.

Note that the Operand is not used for some operations and some operations can only be used on certain data types.

If a file is open and no bytes are selected, the operation will be applied to the whole file. If editing RAM, a selection must be made before an operation can work. If a selection is made, the operation will be applied to the selected bytes if Selection is checked, or the whole file if Entire File is checked (these fields are not available while editing RAM; the operation automatically takes place on the selected bytes). A description of the selected operation is shown in the Description box.

The Advanced box contains two fields: Operand Step and Skip Bytes. If a value is entered in Operand Step, that value will be added to the Operand after modifying each value in the file or RAM. The Operand Step can be used to easily perform a number of complex operations, including building arrays. For example, select 256 bytes and perform an Assign operation on the bytes with an Operand of 0 and an Operand Step of 1. The result will be an array with the values from 0 to 255.

If a value is entered in the Skip Bytes field, then that number of bytes will be skipped after each value is modified in the file. This feature can be used to skip over bytes that should not be modified. For example, if a binary file contains a series of employee records containing an integer ID number followed by a 32-character name, the Skip Bytes field can be used to modify the ID number without modifying the name. Use an Add operation on integers with 1 as the Operand and 40 as the Skip Bytes value.

More complex operations can be performed with scripts. See Introduction to Scripts.


Disassembler

MHS comes with a powerful Disassembler and built-in debugger. To open the Disassembler, press Ctrl-D or select the Tools/Disassembler menu item.

The Disassembler features:

  • Helpful Color Highlighting
  • Quick Go-To Links
  • Many Decoding/Display Options
  • All Instructions Decoded
  • Symbol Names Resolved
  • Loads OllyDbg UDD Files
  • Multiple Tabs
  • ASM Hints

The Helper Window also provides information on:

  • All Loaded Modules
  • All Imports/Exports Used By Those Modules
  • All Kernel Functions Exported by NtOsKrnl.exe and Hal.dll.
  • All primary SDT Functions.
  • All Breakpoints
  • Registers (While Debugging Only)
  • All Threads (While Debugging Only)
  • The Auto-Hack Feature

Controls

  • Ctrl-I - Inject Code: Displays the Inject Code dialog. See Injection Manager for more details.
  • Ctrl-G - Go To…: Allows you to go directly to an address.
  • Ctrl-P - Open Current Process: Opens the current process in a new tab. If there is no process opened, no action is performed.
  • Ctrl-D - Debug: Starts the debugger.
  • Ctrl-Shift-D - Detach Debugger: Stops debugging.
  • Ctrl-Z - Undo: All operations performed in the Disassembler can be undone.
  • Ctrl-Y - Redo: All operations undone can be redone.
  • Ctrl-F4 - Close: Closes the current Disassembler tab.
  • Ctrl-Shift-F4 - Close All: Closes all Disassembler tabs.
  • Ctrl-C - Copy: Copies the selected lines of disassembly as text, including the address, code, disassembly, and extra.

Links

Mousing over some operand arguments cause them to turn blue. These are links that can be quickly followed. Hold Ctrl and left-click the link to go to its target. Hold Shift to go to the target in a new tab.

Some links can not be followed, and no action will be performed when clicked.

Some links point to data rather than to code, so clicking them will open a new tab in the Hex Editor.

JMP/CALL Targets

Mousing over a JMP or CALL instruction causes it, its target, and the code between to be highlighted. This makes it easy to see where a JMP or CALL will go.

You can lock the highlighted area by holding Ctrl and left-clicking while the highlight is shown. After this you can move your mouse away from the instruction and the highlight will remain. You can unlock the highlight by holding Ctrl and left-clicking again anywhere on the Disassembler control.


Disassembler - Imports/Exports

The Imports and Exports Helper tabs show the modules and the functions they import and export. Exported functions are functions modules expose to other modules for their own uses. To use the functions other modules export, a module must import them.

Exports

The Exports tab displays every function in every module loaded by the target process. While debugging, this list is kept up-to-date as modules are loaded and unloaded (with spam protection to avoid consuming resources when the target rapidly unloads and reloads modules).

Modules are shown by file name and internal name (format: file [internal]). These names do not always match. For example, some may have case differences and some may be entirely different names.

Click a + (or double-click the row) to expand the module and display its functions. Each function has an address and an ordinal. You may notice several functions with the same address. These are typically stub functions that do nothing but return, but are always functions that perform the exact same actions, instruction-for-instruction, and so have been stored to one location in the code and share the same address.

Ordinals are technically an alternative way to call an imported function, however they are virtually never used.

By right-clicking a function, you can go directly to that function in the current code window, or you can set a software execute breakpoint on the function.

Imports

Imports are usually more useful, as they show you what functions the target process (and all other modules) will actually be using. Modules such as ntdll.dll export hundreds of functions while only a handful are ever actually used by any given application.

Here, every module is listed in the target process. By expanding a module, you will see all the modules to which that module is statically linked (the modules from which the source module imports some functions). By expanding one of those modules, you will see the functions from that module used by the original module.

For example, module LuxorAmun.exe loads module luxoramunres.dll and from that module it uses function mainDLLInit.

By right-clicking a function, you can go directly to that function in the current code window, or you can set a software execute breakpoint on the function.


Disassembler - Kernel Functions

The Kernel Functions tab shows all functions exported by NtOsKrnl.exe and Hal.dll along with their respective addresses.

From the right-click pop-up menu, you may copy the function name or address to the clipboard, or you may go to the function in the current or new disassembler tab.


Disassembler - SDT Functions

The SDT Functions tab of the Disassembler shows the main SDT functions, their indices in the SSDT array, and their respective addresses. If any entry has been hooked, it’s new address is shown along with its real address. This makes it easy to see when malicious programs are trying to interfere with the normal operations of other programs, such as MHS.

From the right-click pop-up menu, you may copy the function name or address to the clipboard, or you may go to the function in the current or new disassembler tab.


Disassembler - Options

The Disassembler has many options for customization. You can change appearances, fonts, decodings, and certain aspects of functionality. Select the Disassembler category in the Options Dialog (Tools/Options from the main window) or select the Tools/Options menu item from the Disassembler window.

Misc.

The Disassembler can load data not only from its own file format but also from UDD files from OllyDbg. In the Misc. tab you can specify the directories where this data is stored. Environment Variables are allowed here.

Auto-Hack can use software or hardware breakpoints. The following table lists the uses of each.

Type | Pros | Cons
Software | Any number of them may be set. This allows any number of Auto-Hack items. Results are shown on the instruction that reads/writes/accesses the data before the read/write/access actually took place, so registers can be shown correctly. | Slows down the target process substantially. Often renders games unplayable.
Hardware | Very fast; no noticeable slowdown in the target process. | Only 4 are allowed. Results are shown on the next instruction instead of the instruction that actually read/wrote/accessed the data. Although MHS uses an advanced algorithm to correct this, it can not provide accurate information on the registers at the actual time of the access.

Font/Colors

The Font/Colors tab allows you to change the font and colors.

While most Disassemblers limit you to using only fixed-width fonts, in MHS you are not limited in which fonts or styles you may use.

  • Selection: The color shown on items that are selected.
  • Focused Item: The color shown on the item that has focus. The focused item is the item last clicked.
  • Link: Clickable links (in square brackets ([ ]) in the disassembly) are shown in this color.
  • EIP: The current instruction during Single Stepping is shown in this color.
  • JMP/CALL Start: When mousing over a JMP or CALL instruction, this is the color shown on that instruction.
  • JMP/CALL Middle: When mousing over a JMP or CALL instruction, this is the color shown between the start and end locations.
  • JMP/CALL Target: When mousing over a JMP or CALL instruction, this is the color shown at the target of the JMP/CALL.

Output

Force IDEAL Decoding
  • IDEAL syntax enforces that every property of a memory operand be included inside the brackets, which is in contrast to MASM syntax where only the address must be inside square brackets.
  • For example, from MASM to IDEAL, MOV EAX, DWORD PTR SS:[EAX+EBP+20] would become MOV EAX, [DWORD SS:EAX+EBP+20].
Use Lower-Case Output
  • Commands, registers, and modifiers will be displayed in lower-case. No change is made to labels or constants.
Tab Betweeb Mnemonics and Arguments
  • Commands are padded with spaces such that the arguments begin on the 8th column. This only appears correctly in fixed-width fonts.
Extra Space Between Arguments
  • Inserts an extra space after the comma separating operands.
Show Default Segments
  • 80x86 commands always use some segment register when accessing memory. If the segment register is different from the default for the command, it must be explicitly supplied. When enabled, this option displays the segment register in all cases, instead of only when it is different from the default.
Show Memory Sizes
  • When set, sizes of operands are always shown. Otherwise, sizes are only shown when it is not clear which size is being used.
Show NEAR Modifiers
  • When set, the NEAR modifier is shown on all intrasegmental (code within the same region) jumps and calls.
Short Form of String Commands
  • Some string commands are very long when represented in long form. This allows them to be described with much shorter output.
  • For example, MOVS DWORD PTR ES:[EDI], DWORD PTR DS:[ESI] would become MOVSD, and REP STOS DWORD PTR ES:[EDI] would become REP STOSB.
Show Symbolic Addresses
  • Allows decoding of constants as symbolic names (for example, NULL is a symbolic name for 0).
Method to Decode Size-Sensitive 16/32 Mnemonics
  • Allows to select the decoding method for commands whose mnemonics depends on the sizes of the address data.

Warnings

Some commands or conditions are rare encounters in valid Windows®, but still valid. Usually encounters with these commands or conditions, however, indicate invalid code. These options allow you to decide what is flagged as potentially being invalid code.
Accept FAR Calls
  • To allow direct calls to procedures in other regions of code. Very rare.
Decode VxD Calls (Windows® ‘95/Windows® ‘98)
  • DOS-based Windows® ‘95/Windows® ‘98 use INT 20 followed by a DWORD containing the code of the requested service to communicate to virtual devices. This is not to be found inside user-mode applications but inside device drivers.
Accept Privileged Commands
  • Some commands can be executed only by device drivers (due to privilege levels) and not by user-mode applications.
Accept I/O Commands
  • Windows® NT does not allow direct access to I/O (in/out) ports, however some drivers can modify the I/O Permission Bit Map to allow access, and Windows® ‘95/Windows® ‘98 are not as strict. Check this if you feel the target process may be accessing I/O ports directly.
Accept Shifts Out of Range 1-31
  • Compilers typically ensure constant shifts are within a valid range (0-31) and shifts of 0 are omitted. Although theoretically possible, shifts outside the range from 1 to 31 are usually the result of data being interpreted as code rather than valid code.
Accept Superfluous Prefixes
  • Allows repeated or contradictory prefixes on commands. Usually not useful to allow.
Accept LOCK Prefixes
  • Applications usually do not use the LOCK prefix themselves, instead relying on EnterCriticalSection or other system functions.
Accept Unaligned Stack Operations
  • Applications typically keep the stack aligned on DWORD’s (multiples of 4). To lose this alignment for any but very special purposes is guaranteed to cause crashes. If code such as INC ESP or ADD ESP, 7 is seen, it is almost certainly the result of data being interpreted as code.
Accept Modifications of Segment Registers
  • Applications typically do not modify segment registers.
Assume NT-Base Operating System
  • Disassembler will assume certain NT-based coding practices are employed.


Disassembler - Debugger

MHS includes a Debugger with very powerful features (and more to come). The Debugger is packaged inside the Disassembler. Press Ctrl-D to open the Disassembler, then press Ctrl-D or select the File/Debug menu item to start debugging. Select the File/Detach Debugger menu item or press Ctrl-Shift-D to stop debugging at any time. This does not close the target process on machines that support this feature.

Controls

  • F7 - Step Into: Single-steps in code. When CALL instructions are reached, the single-step goes into the called function.
  • F8 - Step Over: Single-steps in code. When a CALL instruction is reached, the single-step stays inside the current function, skipping over the called function.
  • F9 - Resume: Stops single-stepping and resumes the process.
  • Right-Click/Run to Cursor: Executes the code until it reaches the line of code where the mouse last right-clicked.


Disassembler - Debugger - Breakpoints

Breakpoints are used to stop the target process at certain points or when the target process reads, writes, or accesses certain addresses. Breakpoints that stop on read, write, or access are commonly called watchpoints.

Adding Breakpoints

Right-click in the Disassembler on the address at which you want to place a breakpoint and select the Breakpoints menu item, then select the type of breakpoint you want to add.

To add a breakpoint by manually setting its parameters, select the Add Breakpoint Here menu item at the bottom.

Breakpoints are added to the Breakpoints tab in the Helper. Here you can toggle breakpoints on and off, modify them, remove them, save them, and load more breakpoints from files.

This tab shows the address of the breakpoint, its name, and the functions it will call when hit, if any.
Key

: The breakpoint is active.
: The breakpoint is inactive. No action is performed when the breakpoint is hit.
: The breakpoint is active. This is a hardware breakpoint.
: The breakpoint is inactive. This is a hardware breakpoint.

Hardware vs. Software

There are several important differences between hardware and software breakpoints. It can be very helpful to use the correct one for the correct situation. The following table illustrates the differences and shows when you would want to use each type.

Type | Pros | Cons | Suggestion
Hardware | Access and write breaks are very fast. RAM is not modified; they are harder to detect. | Can only have 4 per thread maximum. The break happens on the next instruction, instead of on the instruction that caused the break. Access/write breakpoints can only span one address (software read/write breakpoints can cover large ranges). | Use hardware breakpoints for access/write operations unless you need a breakpoint to cover a large range.
Software | Any number may be set. Execution breaks are very fast. Read/write breakpoints can cover a large range. | Read/write breakpoints are very slow; the game typically can not run at a reasonable frame rate while these are set. RAM is modified, so detactability is higher. | Always use software breakpoints when setting executable breakpoints unless you absolutely must avoid modifying the game RAM.

Creating/Modifying Breakpoints

To create a new breakpoint, right-click a line in the Disassembler and select the Breakpoints/Add Breakpoint Here menu item. To modify an existing one, right-click it in the Breakpoints tab in the Helper and select the Modify menu item from the popup.

Setting | Description
Name | The name or description of the breakpoint. Required.
Address | The address of the breakpoint.
Active | Whether or not the breakpoint is active. If inactive, it performs no operations when hit, but it is not removed from the target process.
Break On | Execute | The target process will be stopped when the specified address is executed.
 | Read | The target process will be stopped when any address from Address to (Address + Coverage) is read.
 | Write | The target process will be stopped when any address from Address to (Address + Coverage) is written, except when hardware breakpoints are used, in which case the break happens only on Address.
 | Access | The target process will be stopped when the exact address in Address is read or written.
Hardware | Determines if the breakpoint should be hardware or not. When enabled, the Read button becomes Access.
Coverage | While using software read or write breakpoints, this specifies the range of addresses to include in the breakpoint. If any address from Address to (Address + Coverage) is read or written, a break will occur.
Prolog Function | The first function to call when the breakpoint is hit. The function is called before the breakpoint has been unset and before the EIP has been adjusted.
Callback Function | The second function to call when the breakpoint is hit. The function is called after the breakpoint has been unset; the original instruction has been written back to RAM (if software executable breakpoint), and the EIP has been adjusted.
Epilog Function | The third function to call when the breakpoint is hit. The function is called after the break has ended and the target process has been resumed. This is used the least among the 3, direct access to the thread context is not supplied to breakpoint handlers.
Parm | A numeric parameter to pass to its respective function. Among the built-in functions, only the Sys Beep and Script Function use this, but all custom functions may also use this if desired.
Condition | An optional condition to apply to the breakpoint. When using a condition, the breakpoint will only call the Prolog, Callback, and Epilog functions when the condition evaluates to a non-zero value.
Use Condition | Determines whether or nor the applied condition is to be used. If not, the breakpoint acts as if it has no condition and always breaks.

Press OK to add the breakpoint.

Built-In Breakpoint Functions

  • Do Nothing: Performs no action. Default for Prolog and Epilog. Parm is not used.
  • Sys Beep: Beeps for Parm milliseconds.
  • Single Step: Shows the Disassembler and allows the user to single step through the code. Default for Callback. Parm is not used.
  • Set CF: If Parm is 0, this sets the CF flag to 0, otherwise it sets it to 1.
  • Set PF: If Parm is 0, this sets the PF flag to 0, otherwise it sets it to 1.
  • Set AF: If Parm is 0, this sets the AF flag to 0, otherwise it sets it to 1.
  • Set ZF: If Parm is 0, this sets the ZF flag to 0, otherwise it sets it to 1.
  • Set SF: If Parm is 0, this sets the SF flag to 0, otherwise it sets it to 1.
  • Set OF: If Parm is 0, this sets the OF flag to 0, otherwise it sets it to 1.
  • Script Function: Calls a script function where Parm is used to determine which script function to call. The function called will be named On_BP_Parm() where Parm in the function name is the number specified in Parm in the dialog box.
  • Cur Proc Script Function: Same as above, except that the function called is in the format On_BP_CURPROC_EXE_Parm(), where CURPROC_EXE is the name of the current process (in all caps, with special characters replaced by underscores), and Parm is again the numeric value specified in the dialog. Because the name of the current process is in the script function, the script function will only be called for that process.
  • Custom Function…: Not actually a function itself. Select this to open the Add Function dialog.

Script Function

You can call your own script functions when a breakpoint is hit. This is usually preferred over creating breakpoint-handling DLL’s because of the ease in writing the functions and calling them through breakpoints.

Script Events offer the same functionality as most of the 10 optional functions listed above. Use On_BeginDebug in place of the DLL function MHS_StartUp (or StartUp), and On_EndDebug in place of the DLL function MHS_CleanUp (or CleanUp). Use On_OpenProcess in place of MHS_ProcessAttach and On_CloseProcess in place of MHS_ProcessDetach, but note however that these functions are called regardless of whether the Debugger is active or not.

To call a script function when a breakpoint is hit, name the function On_BP_#, where # is any decimal number. Then, in the Add Breakpoint dialog, set the Prolog, Callback, or Epilog function Parm to the number used in the script function name.

For example, if your script function is named On_BP_34, you can assign this script function to be called as the Callback Function of a breakpoint by setting the Callback Function to Script Function and setting its Parm to 34.

Alternatively, you can name the function On_BP_*_#, where * is the name of the process (with every letter capitalized and every special character changed to an underscore) and # is any decimal number. Then select the Cur Proc Script Function function from the list.

This allows you to write per-process breakpoint routines. So, for example, if you wanted to write a script routine that works only in Gamex86.exe, you could write a function named On_BP_GAMEX86_EXE_2, set the Callback Function to Cur Proc Script Function, and set the Parm to 2. Your function will only be called while debugging Gamex86.exe. This allows you to use the same Parm value but call a different function for each different process. If you intend to share your scripts, this method should always be used to ensure your breakpoint script functions do not clash with others’.

To apply the name of the process to your function, capitalize every letter and change all non-alphanumeric characters to underscores. Include the extension. For example, gamex86.exe becomes GAMEX86_EXE, and Super Fun Game!!.exe becomes SUPER_FUN_GAME___EXE.

The script function has one of the following prototypes:

VOID On_BP_#( LPVOID lpvAddress, LPPROC_INFO_MHS lpProcInfo );
VOID On_BP_*_#( LPVOID lpvAddress, LPPROC_INFO_MHS lpProcInfo );

This parameters are exactly the same as with DLL breakpoint-handler functions, except that the PROC_INFO_MHS structure is passed as a reference to DLL functions but as a pointer to script functions.

This is an example of a script function used as a breakpoint handler:

INT g_iCount = 0;
VOID On_BP_10( LPVOID lpvAddress, LPPROC_INFO_MHS lpProcInfo ) {
    if ( g_iCount++ % 2 == 0 ) {
        lpProcInfo->pcContext->Eax |= 0x40;
        lpProcInfo->bSetContext = TRUE;
        //PrintF( "%X", lpProcInfo->pcContext->Eax );
    }
    else {
        lpProcInfo->pcContext->Eax |= 0x80;
        lpProcInfo->bSetContext = TRUE;
    }
}

As shown in this snippet, when changing registers via lpProcInfo->pcContext, set lpProcInfo->bSetContext to TRUE rather than calling SetThreadContext() directly. When lpProcInfo->bSetContext is set to TRUE, MHS will call SetThreadContext() itself after the function returns. This is slightly faster than calling SetThreadContext() from within the script.

NOTE: If possible, the breakpoint will put a lock on the script system. This lock allows the breakpoint to maintain a ready-to-go script thread that can directly execute the script function. Without locking the script system, a script thread must be allocated and initialized, and then the function address must be found from a table for the script thread to execute.
The lock is made when the script function already exists within the compiled scripts when the breakpoint is created. This is normally the case.
If a lock is made, the script function can be called roughly 10 times faster than without a lock. This improves the performance of the breakpoint—and thusly the Debugger—substantially.
However when the script system is locked it can not be recompiled. In order to recompile the scripts, the breakpoint must lose its lock on the script system. This can be achieved by setting the breakpoint function to Do Nothing temporarily. After recompiling the scripts you can set the breakpoint function back to Script Function. You will not need to modify the breakpoint Parm values.
The lock has no other effects on the script system.

Conditions

Conditions allow you to decide under what circumstances the breakpoint will be “hit”. If the conditional statement evaluates to 0, the target process resumes without the breakpoint performing any actions. If the conditional statement evaluates to any other number, the breakpoint performs normal processing, calling its prolog, callback, and epilog functions.

The conditional statement may include all operators recognized by the Expression Evaluator.

The following lists the registers that may be used:

  • EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI
  • AX, CX, DX, BX, SP, BP, SI, DI
  • AL, CL, DL, BL, AH, CH, DH, BH
  • ES, CS, SS, DS, FS, GS
  • DR0, DR1, DR2, DR3, DR6, DR7
  • ST0, ST1, ST2, ST3, ST4, ST5, ST6, ST7

As well, keyword HC may be used to retreive the hit count of the breakpoint.

Any other non-recognized words are treated as module names, making the expression [game.exe+5334h] valid if the current process is game.exe.

Ensure that the Use Condition check is checked when creating or modifying the breakpoint. This toggle allows you to keep the condition but disable it at times.

Here are examples of valid expressions:

  • eax: Breaks if register EAX is not 0.
  • [EBP+Ch] == 32: Breaks if the value at address EBP+Ch (the 2nd arguement pass to the function) is equal to 32.
  • (HC >= 1 && HC < 100): Breaks after the breakpoint has been hit once but not after the breakpoint has been hit 100 times.

Miscellaneous

This section covers the miscellaneous issues regarding breakpoints.

  • Software executable breakpoints modify one byte each of the RAM of the target process. If this is a dire problem, use hardware executable breakpoints instead.
  • Software read/write breakpoints change page protections in the target process. If this is a dire problem, use hardware access/write breakpoints instead.
  • The Debugger does not have to be enabled for breakpoints to be set, but does have to be enabled for breakpoints to be applied. If the Debugger is not active, no RAM is modified and no page properties are changed.
  • Breakpoints are organized into layers. All breakpoints created by the user are loaded to the User Breakpoint Layer. Breakpoints created by the Auto-Hack feature are loaded to the Auto-Hack Layer. Only one breakpoint layer may be active at once. This means user breakpoints will not operate while the Auto-Hack feature is active. To enable user breakpoints again, stop the Auto-Hack session by opening the Helper window Auto-Hack tab and pressing Stop or Clear All.
  • The current version of MHS does not offer 100% backwards compability with MHPB/MHBPL breakpoint files. Specifically, hardware read breakpoints no longer exist. All other features are compatible.
  • While software read/write breakpoints are set, some features of MHS may not function 100% correctly. Specifically, the RAM Watcher and Hex Editor may flicker and some values in the main list may become “Unobtainable”. This behavior is normal; it is caused by the fact that software read/write breakpoints change the page properties in the target process to inaccessible. Inaccessible RAM appears red in the Hex Editor, N/A in the RAM Watcher, and Unobtainable in the main list. Flickering appears when the page of RAM is read or written, causing the page properties to be temporarily set back to normal and then set back to inaccessible.
  • If the target process is a full-screen game, do not set breakpoints that cause single-stepping. This is a common problem shared between all ring-3 debuggers. When the breakpoint is hit the game will be completely paused and will be unable to minimize (because minimizing would require it to execute code, which, being completely paused, will not happen). If a breakpoint, or anything else, pauses the game, you will be forced to reboot your computer.


Disassembler - Debugger - Breakpoints - Custom DLL Handler

Custom Breakpoint Functions

MHS allows you to write your own functions to handle breakpoint hits. You can write them in any language capable of producing a compatible DLL file. This section explains how to load existing breakpoint handlers from DLL files and how to write your own DLL to handle breakpoints.

Loading Existing Breakpoint Handlers from DLL Files

Under Breakpoint Functionality, select Custom Function… for the Prolog Function, Callback Function, and/or Epilog Function to display the Add Function dialog.

Here you can browse for a DLL file containing breakpoint handlers or select an existing one from the list. The functions in the selected DLL are shown in the drop-down combo box. Press OK to use the selected function.

If you browse for a DLL that is not in the list already, it is automatically loaded and added to the list. The DLL will remain in the list until removed manually using the Manage Breakpoint Handler Libraries dialog.

After selecting a breakpoint handler and pressing OK, the selected function will be used as the prolog, callback, or epilog function.
Writing Your Own Breakpoint Handler Functions

A compatible DLL is broken into two parts: optional functions for initialization and destruction, and the actual functions capable of being assigned to breakpoints to handle hits. Your library can optionally export these 10 functions.

  • StartUp
    • typedef VOID (CALLBACK * PFnBpStartUp)( HANDLE hProcess, DWORD dwProcessId );
    • Called when debugging starts.
    • This is always the first function called.
    • This function is used for backwards compatibility. Modern breakpoint handlers should use MHS_StartUp instead.
  • MHS_StartUp
    • This can be declared instead of StartUp. If this is declared, StartUp is not called.
  • CleanUp
    • typedef VOID (CALLBACK * PFnBpCleanUp)( HANDLE hProcess, DWORD dwProcessId );
    • Called when debugging ends.
    • This is always the last function called.
    • This function is used for backwards compatibility. Modern breakpoint handlers should use MHS_CleanUp instead.
  • MHS_CleanUp
    • This can be declared instead of CleanUp. If this is declared, CleanUp is not called.
  • ProcessAttach
    • typedef VOID (CALLBACK * PFnBpProcessAttach)( HANDLE hProcess, DWORD dwProcessID );
    • Called when attaching to a process.
    • This function is used for backwards compatibility. Modern breakpoint handlers should use MHS_ProcessAttach instead.
  • MHS_ProcessAttach
    • This can be declared instead of ProcessAttach. If this is declared, ProcessAttach is not called.
  • ProcessDetach
    • typedef VOID (CALLBACK * PFnBpOldProcessDetach)( HANDLE hProcess, DWORD dwProcessID );
    • Called when detaching from a process.
    • This function is used for backwards compatibility. Modern breakpoint handlers should use MHS_ProcessDetach instead.
  • MHS_ProcessDetach
    This can be declared instead of ProcessDetach. If this is declared, ProcessDetach is not called.
  • SetDisasmFunc
    • typedef VOID (CALLBACK * PFnBpSetDisasmFunc)( LPVOID lpvData );
    • Called immediately after MHS_StartUp or StartUp.
    • lpvData holds a pointer to a function that can be used to load the Disassembler and single-step through the process. Save this pointer if you plan to load the Disassembler from your breakpoint handler library.
    • To call the function to which lpvData points:
      • typedef VOID (CALLBACK * PFnDisasm)( LPVOID lpvAddress ); // Defined the way of calling the function.
      • PFnDisasm pfLoadDisasm = (PFnDisasm)lpvData; // Cast lpvData to that.
      • pfLoadDisasm( (LPVOID)lpvAddressToShow ); // Call the function with the address you wish to show.
    • See the FileWatcher plug-in for a real example of how to do this.
  • SetReportFunc
    • typedef VOID (CALLBACK * PFnBpSetReportFunc)( LPVOID lpvData );
    • Called immediately after MHS_StartUp or StartUp.
    • lpvData holds a pointer to a function that can be used to print a message that will appear in the Disassembler window.
    • To call the function to which lpvData points:
      • typedef VOID (CALLBACK * PFnReport)( CHAR * pcText ); // Defined the way of calling the function.
      • PFnReport pfReport = (PFnReport)lpvData; // Cast lpvData to that.
      • pfReport( (CHAR *)pcTextToPrint ); // Call the function with the text you wish to print.
    • See the FileWatcher plug-in for a real example of how to do this.

The above functions are optional and only called when exported by the DLL. These functions may not be used as breakpoint handlers.

Functions to be used as breakpoint handlers must have one of the following prototypes:

typedef VOID CALLBACK FuncName( LPVOID lpvAddress, CONST PROC_INFO_MHS &piInfo );
typedef VOID CALLBACK FuncName( LPVOID lpvAddress, CONST PROCESS_INFORMATION &piInfo );

PROC_INFO_MHS has the following definition:

typedef struct tagPROC_INFO_MHS {
    HANDLE               hProcess;           // Handle to the process.
    HANDLE               hThread;            // Handle to the current thread.
    DWORD                dwProcessId;        // Process ID.
    DWORD                dwThreadId;         // Thread ID.

    LPVOID               lpvReseved1;        // Reserved; used by the system.

    union {
        DWORD            dwUserData;         // DWORD form of user data.
        CHAR *           pcStringPointer;    // String form of user data.
        LPVOID           lpvMiscData;        // Other forms of user data.
    } u;
    LPVOID               lpvReadWriteAddress;// The address read/written in a read/write operation.
                                             //  NULL otherwise.

    CONTEXT *            pcContext;          // Context to the current thread, so that
                                             //  GetThreadContext() and SetThreadContext()
                                             //  are not needed inside breakpoint handlers.
    BOOL                 bSetContext;        // Tells the debugger to set the thread context after
                                             //  a breakpoint handler has been called.

    LPVOID               lpvReseved2;        // Reserved; used by the system.
} PROC_INFO_MHS, * LPPROC_INFO_MHS;

lpvAddress is the address of the instruction that caused the break. lpvReadWriteAddress contains the address that was read, written, or accessed to cause the break, if applicable. This is NULL for executable breakpoints.

Use piInfo.pcContext to access the current thread’s context and modify its registers. Using this pointer is faster than calling GetThreadContext() and SetThreadContext(). After modifying the thread context as desired, set piInfo.bSetContext to TRUE. This causes MHS to set the thread context itself after the breakpoint handler returns. Note that piInfo.pcContext is NULL when the breakpoint handler is assigned to the Epilog Function.

piInfo.u.dwUserData contains the value supplied in the Parm field of the respective breakpoint function of the Add Breakpoint dialog. This value is up to the user and can be used by the breakpoint handler in any way it desires.


Disassembler - Debugger - Auto-Hack

Auto-Hack lists all the addresses of code that read, write, or access a given address. The Auto-Hack feature can be found in the Disassembler’s Helper window. The Debugger must be active for the Auto-Hack feature to function.

The Auto-Hack window is divided into 3 parts: target addresses, code addresses, and information.

Target Addresses

These are the addresses whose read/write code you want to find—that is, you want to find the code that reads/writes these addresses. You can have as many addresses in this list as you please.

You can add addresses manually by pressing the Add button. The most common way to add addresses to this list, however, is by right-clicking addresses from the main list and selecting Find What Reads/Writes/Accesses This Address.

To remove a target address, select it and press the Delete button.

Select an address from this list to view its code addresses.

Code Addresses

This lists the addresses of instructions that read, wrote, or accessed one of the target addresses. This pane is empty if no address is selected from the target addresses. This pane shows the address of the instructions, the bytes that make up the instructions, the instructions themselves, and the number of times the instructions read, wrote, or accessed the respective target address.

To view an item in the Disassembler, select it and press the Go To Dis... button. If New Tab is checked the item will be opened in a new tab.

To NOP an item (change the code to perform no action), select it and press NOP.

To get more information about one of the items in this pane, select it.

Information

When an item is selected from the Code Addresses list, more detailed information about it is shown here. This pane shows the registers at the time of the read, write, or access, and explains the instruction.

If hardware breakpoints are used for the Auto-Hack feature (see Disassembler - Options), the registers shown here are the registers after the instruction was executed. If it is important to know the registers before the instruction was executed, use software breakpoints. For more information regarding the differences between software breakpoints and hardware breakpoints, see Disassembler - Debugger - Breakpoints.

The registers shown are from the first time the instruction was executed.

Miscellaneous

The Auto-Hack feature uses breakpoints to carry out its operations. As noted on the Disassembler - Debugger - Breakpoints page, breakpoints are organized into layers. While the Auto-Hack feature is active, user breakpoints will not be hit.

To enable user breakpoints, stop the Auto-Hack feature by pressing Stop or Clear All. The Stop button temporarily stops the Auto-Hack feature, leaving the Target Addresses in the list. Auto-Hack can be resumed by pressing Start. The Clear All button stops the Auto-Hack feature and clears all Target Addresses. Similarly, if you manually delete all Target Addresses, Auto-Hack will automatically shut down and user breakpoints will become active again.


Code Filter

The Code Filter allows to easily find code related to certain operations in the target application or game by logging the execution of functions into categories defined by the user and then filtering those logs out of future logs, thereby allowing the user to procedurally cause code to be executed without unrelated code being logged. The Code Filter dialog can be opened via the Tools/Code Filter menu item in either the main window or the Disassembler.

Usage

The Code Filter makes it easy to classify code and to determine when and why it is being executed. The first step is to define which modules you want to log. By default, the main module of the target process is set, however you can define more or fewer by pressing the Add/Remove button in the lower-left corner.

With the modules defined, the next step is to create a code filter where code will be logged. Right-click an item in the Data Sources tree. You may add groups as you wish, but at least one code filter must be added before logging can begin. Groups help organize filters. The following table illustrates the meanings of the icons.

Icon | Meaning
 | This item is a folder (group) used for organizing sets of related code filters.
 | This item is a filter capable of containing a list of logged code. The addresses in this filter are not omitted from following searches.
 | This item is a filter capable of containing a list of logged code. The addresses in this filter are omitted from following searches (excludes results).

A red check indicates the current filter where logged code will go.

The next step is to set the preferred options in the Control panel. Use Restore Breakpoints to allow a single address to be added multiple times to the current list. Use Log Contexts to store context information for addresses added to the list.

Finally, to begin logging, press Go.

After Logging

Press Stop to stop logging. The current filter will be shown in the Data Exploration listview. Selecting an item shows context information (if logged) in the Context panel. When multiple items are selected, all similar context fields are shown in full while all context fields that are different between any of the selected items are shown as double hyphens.

To show the contents of another filter, right-click it an select the View Results menu item from the pop-up menu.

The entire filter set can be saved via the File/Save or File/Save As... menu items. Filters can be reloaded via the Load Filter button, by selecting the File/Open, or by pressing Ctrl-O.

Using Data Exploration

Multiple items can be selected at a time. When the contexts values of all selected items match they are shown in the Context panel. The lists can be sorted by any of the columns by clicking the respective listview header.

The following table explains each of the pop-up menu options.

Menu Item | Meaning
Cut | Copies and removes the selected items from the current list.
Copy | Copies the selected items to the clipboard. Items can be pasted back into another filter or as text, for example in Notepad or on forums.
Paste | Pastes copied items into the current filter. Pasted items go to the end of the list.
Go To | Views the selected item(s) in the Disassembler.

Using Data Sources

The Base item always exists and can not be renamed or removed. The following table explains each of the pop-up menu options.

Menu Item | Meaning
Add Group | Adds a group to the filter tree. Groups are simply folders capable of containing filters and/or other groups.
Add Filter | Adds a filter to the filter tree. Filters contain logged code.
Rename | Renames the right-clicked object.
Remove | Removes the right-clicked object.
Set as Current Target | Sets the right-clicked filter as the target of the next logging session. New code will be added to it.
Exclude Results | Indicates that the right-clicked filter will knock the addresses it contains out of future searches.
View Results | Shows the logged addresses in a filter in the Data Exploration panel.
Paste | Pastes copied addresses into the right-clicked filter.


Auto-Assembler

The Auto-Assembler allows you to write ASM scripts that are composed of more than just raw ASM. For example, auto-assemble scripts allow automatic allocation of memory and modification of page protections, as well as full label support. The Injection Manager, on the other hand, is designed to be simpler to use. To open the Auto-Assembler, select the Tools/Auto-Assembler menu item or press Ctrl-U.

Commands

The following table explains each command.

Command Description
LABEL( IDENTIFIER )

Denotes an identifier (IDENTIFIER) as a label.
Labels can be used in command operands and to denote the address of code.
For example, with the command Label( MyLabel ) anywhere in the script, it is valid to write:

MyLabel : ; Indicates the starting address of the next line of code.
mov eax, [gamex86.dll+0024FE8C]
mov ecx, MyLabel ; Moves the value of MyLabel into ECX.
mov ecx, DWORD PTR DS: [MyLabel+C] ; Moves the value at the address of MyLabel (plus 12 bytes) into ECX.
jmp MyLabel ; Goes to MyLabel.

If MyLabel becomes address 0x1030015, the final code might become:
01030015: MOV EAX, DWORD PTR DS:[124FE8C]
0103001B: MOV ECX, 1030015
01030021: MOV ECX, DWORD PTR DS:[1030021]
01030027: JMP 01030015

When a label is declared with this command, it has no initial address. Its actual address is determined by its position within the code when first used in a label statement (INDENTIFIER :) within the code. Once given an actual address, using it in a label statement causes the label to indicate the address of the next line of code.

This command can appear anywhere in the script.
FULLACCESS( ADDRESS, SIZE )

Modifies the protection of addresses in the range from ADDRESS to (ADDRESS + SIZE - 1) inclusively to provide full access. If code can not be modified, this is used to enable it to be modified.
Any valid expression may be used for SIZE and ADDRESS. The address is treated as a hexadecimal number by default, so the 0x prefix and h postfix are optional.

This command can appear anywhere in the script.
ALLOC( IDENTIFIER, SIZE )

Allocates a minimum of SIZE bytes and creates a new label with the address of the allocated bytes. The label can be used the same as with labels declared with LABEL, however it is given an initial value, and therefor when it appears in a label statement (IDENTIFIER :) it indicates that the next line of code begins at that address.
Any valid expression may be used for SIZE.

This command can appear anywhere in the script.
DEALLOC( IDENTIFIER )

Indicates that the specified IDENTIFIER will be deallocated when MHS detaches from the target pocess.

IDENTIFIER must be a label, typically declared with ALLOC.

This command can appear anywhere in the script.
INCLUDE( FILEPATH )

Causes the Auto-Assembler to parse the specified file. Files may only be included to a recursion level of 1.

This command must appear at the location where the file’s script is to be inserted into the current script.

GLOBALALLOC( IDENITIFIER, SIZE)

Allocates IDENTIFIER exactly once, no matter how many times it is called on the same identifier. If IDENTIFIER already exists as a global, its current value is returned, otherwise SIZE bytes are allocated and the address of the allocation is returned and the indentifier is registered as a global so that subsequent attempts to globally allocate it will return the same address.

If the global already exists, its reference count is increased. These are only deallocated when the reference count is 0 and the reference count can only be decreased by GLOBALFREE and GLOBALDEALLOCATE.

When a symbol is registered as a global it is accessible by other Auto-Assemble scripts.

GLOBALFREE( IDENTIFIER )

Decreases the reference count on a global. If the count reaches 0 it is then freed from the target process and removed from the global list.

As with DEALLOC, this only deallocates memory when MHS detaches from the target process.

GLOBALDEALLOC( IDENTIFIER )

Same as GLOBALFREE.

REGISTERSYMBOL( IDENTIFIER )

Registers an already-allocated (via ALLOC) symbol (IDENTIFIER) as a global that can be accessed from other Auto-Assemble scripts.

UNREGISTERSYMBOL( IDENTIFIER )

Removes a previously registered global from the global list making it inaccessible via future Auto-Assemble scripts unless registered again later. If the symbol being undergistered was created via GLOBALALLOC then this behaves the same way as GLOBALFREE.

Writing Code

Code is written in standard ASM format. Each statement must be on one line. Comments are created with either ; or // and last until the end of the line. Comments can be extended to the next line by placing a \ at the end of the current line inside the comment. All standard 80x86, protected, FPU, MMX and 3DNow! instructions (including Athlon extensions to the MMX command set) are recognized. 16-bit addressing modes are not supported.

Labels are used to indicate the addresses of instructions and end with a colon. Labels can be pure numeric literals (for example 01005224 :), numeric expressions (for example gamex86.dll+5224h), or identifiers created with the LABEL, GLOBALALLOC, or ALLOC commands (for example MyLabel :). Labels created with LABEL do not have addresses assigned to them. Instead, they are used to obtain the address of the next instruction (or data). Therefore, to specify an address for code, a numeric label or a label created with ALLOC must be used. If the first label in the code was created with LABEL, the code will be compiled at address 0x00000000 and is guaranteed to fail injection.

DB, DW, DD, and DQ can be used to indicate data to apply in-place in the code. The data to be used must follow the command, and multiple values may be used per command. For example, db "Hello" 0 and db Ah 0xA 10 "hello\0" are valid. Each value can be any valid numeric, a string literal ("Cafebabe"), a Unicode string literal (L"Cafebabe"), or a label.
DB is used to create data composed of bytes. DW creates data composed of words (2 bytes). DD creates data composed of double-words (4 bytes), and DQ creates data composed of quad-words (8 bytes).

You can create floats by using DB (4-byte floats) and DQ (8-byte floats) and supplying floating-point numbers.
For example, dd 1.0f creates a 4-byte floating-point numeric equal to 1. Use periods to create floating-point numbers.

The following example shows how to place a DWORD into the middle of the code and then to access it.
Label( MyVal )
Label( AfterMyVal )
gamex86.dll+0x2EFC0 :
XOR EAX, EAX
JMP AfterMyVal
MyVal :
DD 0
AfterMyVal :
MOV EAX, [MyVal] ; Load our value into EAX.

This would create final code similar to the following:
0102EFC0: XOR EAX, EAX
0102EFC2: JMP 0102EFCB
0102EFC7: 00 00 00 00
0102EFCB: MOV EAX, DWORD PTR DS:[102EFC7]

Strings and Unicode strings use escape sequences to support miscellaneous characters. Therefore all of the following are valid and equal:
db "Hello" 0
db "Hello\0"
db 'H' "ello" 0h

Strings and Unicode strings are always evaluated to byte and word arrays respectively, regardless of in which command they appear.

Injecting

Press the Next button to preview the current code before injection.

Here you can examine the finalized code and copy the text from any of the edit controls to paste into other applications.

You can inject the code into the target process by pressing Inject. If you would like to make more modifications, or you only wanted to copy some of the text, press Back to return to the Auto-Assembler dialog.

After injecting, the Auto-Assembler dialog is shown again, allowing you to continue editing from the previous code base.

Opening and Saving

The File/Open menu item allows you to open existing Auto-Assembler scripts. Supported formats are .LSAA (L. Spiro Auto-Assemble) and .CEA (Cheat Engine Auto-Assemble). MHS’s Auto-Assembler provides approximately 80% compatibility with .CEA files and the compatibility will increase with future releases.

The File/Save and File/Save As menu items allow saving the current Auto-Assembler script.


Injection Manager

The Injection Manager allows you to manage injections you have created or imported. MHS uses its own ASM parser. Certain improvements over the OllyDbg parser include speed, stability, and automatic hex-format detection on pure-hex numerics. For example, MOV EAX, [ESI+C] will error in OllyDbg’s parser because “C” is recognized as an identifier (because it begins with an alpha character). This is no problem in the MHS parser, which realizes that it should be treated as a hex value rather than as an identifier.

Terminology

The following table explains the important terminology used in code injections.

Term | Meaning
Flow of Control | The order in which the target process executes instructions. Also called flow of execution.
Original Code | The original code inside the target process that has not been altered.
Control Address

The address where flow of control leaves the target process’ original code and goes to the users’ custom code. This address will be overwritten with a JMP instruction, causing execution to go to the custom code written by the user (at the user’s code cave). This is sometimes called the Main Address.

MHS adds this JMP to the code cave automatically.
Injection

ASM-type code written by the user which has been injected into the executable space of the target process. Once injected, the code will be executed by the target process, and allows the user to have control over the flow of execution.

Injections are commonly used to save pointers to dynamic locations at a static location that can be accessed at any time.
Code Cave

An empty area in the target process that isn’t used by the target process. Injections are placed into these.

MHS can create or find a code cave for you. Or you can specify one manually.
Overwritten

A JMP is written at the control address which causes the previous instructions there to be overwritten. These instructions must then be moved into the code cave where the user’s code is so they can still be executed.

MHS will move the overwritten bytes to your code cave for you, either at the start or end of your code cave.
Return

The JMP from the code cave back to the original code. This is required to resume normal execution by the target process.

The JMP back to the original code must go to the next valid instruction after the JMP in the control address. For example, if the JMP at the control address overwrites 2 instructions, which combined are 7 bytes, and the JMP itself only uses 5 bytes, then there are 2 meaningless bytes after the JMP. The return JMP should skip these two bytes and jump to the location 7 bytes after the control address, because that is where control would have been if the 2 instructions did not get overwritten.

MHS will add the return JMP back to the original code for you.

Creating an Injection

Injections can be created in the Disassembler. Open the Disassembler using the Tool/Disassembler menu item, then right-click in the disassembled address where you want your injection to be placed and select Inject Code. You will see the following dialog.

Here you can create an injection.

The preview window allows you to see the result of the injection before you inject, to allow you to ensure it does what you want it to do.

Near the bottom there are two Poke lines which can be copied and pasted into Trainer Maker Kit. The first is the code for the code cave and the second is the code for the control address to jump to the code cave.

Injections are automatically saved and you can organize your injections in the Injection Manager.

The following chart explains each option.

Option | Meaning
Address | The address where control leaves the target-process code and goes to your injected code. See Control Address above.
Auto-find Code Cave | Allows MHS to find or create a code cave for you.
Code Cave | Allows you to define a specific code cave rather than allowing MHS to find or create a code cave for you.
Allocate Code Cave | Allows MHS to create a code cave for you.
ASM | Add the lines of ASM you want to inject. Lines are not case-sensitive and you may add as many lines as you please. Add new lines by hitting Ctrl-Enter.
Place Overwritten At End | If selected, the overwritten instructions are placed at the end of the code cave. Otherwise they are placed at the beginning.
Always Inject | If checked, the Injection will be automatically injected when the module is detected in the target process. This happens when MHS opens a t process, either by attaching to it normally or by loading it in debug mode.
Inject into This Process Only | Adds a restriction on the above rule. If set to Always Inject, it will abort the injection if the target process is not the same. That is, you could inject into USER32.DLL, which is shared by many processes. This option will prevent the injection from being injected into every USER32.DLL in every process you load into MHS. Instead, it will only be injected into USER32.DLL if the target process is the same as it was when you wrote the injection.
Export

Allows you to save the injection to a file to share with others. The injection does not need to be injected to export, but it does need to be a valid injection.

When you export an injection, you can pack it into an existing injection file. When asked to save the injection, select an existing .inje file. You will be prompted to either add to the file or to save over it.

Managing Your Injections

To open the Injection Manager, select the Tools/Injection Manager menu item.

All injections are shown here. To modify an injection it must not be applied. Press Unapply to unapply an injection for modification.

To remove an injection, select it and press Remove. Removing injections does not cause them to be unapplied; you must unapply manually if you want the injection to be removed from the target process.

Injections are always fully verified before being injected, so there are never undesirable results from automatic injection. For an automatic injection to be successful, the original code at the control address must be exactly the same as it was when the injection was created and the bytes at the code cave must also match how they were when the injection was created, unless the injection is set to create a code cave.

Errors

The following chart explains the meanings of the various error messages related to injections.

Error | Meaning
Invalid code-cave address. | The address for the code cave is not valid.
Invalid target address. | The control address is not valid.
Invalid target address—unable to NOP overwritten instructions. | NOP’ing the overwritten bytes at the control address failed. This is only possible if the control address is within 16 bytes of an unwritable chunk boundary.
Invalid target address—unable to create the JMP to the code cave. | Writing the JMP at the control address failed. This is only possible if the control address is within 16 bytes of an unwritable chunk boundary.
Not set for injection into this process. | Not fatal. It simply means the user opted not to have this injection be automatically injected into every process with the target module.
Unable to access initial JMP point ([Address]). | Verification for automatic injection failed because the memory at the control address could not be read.
Initial JMP point ([Address]) code has changed. | Verification for automatic injection has detected that the code at the control address is no longer the same as it was when the injection was created.
Unable to access initial code cave location ([Address]). | Verification for automatic injection failed because the memory at the code cave address could not be read.
Initial code in the code cave ([Address]) has changed. | Verification for automatic injection has detected that the bytes at the code cave address are no longer the same as they were when the injection was created.
Unable to allocate a code cave. | Allocation for a code cave failed. Applies only to injections set to have code caves created.
Unable to recompile the code at the allocated location. | For any reason, the injection could not be recompiled at the code cave location. The most common cause for this is specifying SHORT jumps—if the code cave is created at a new location later, the jump may need to be LONG.


DLL Injector

The DLL Injector allows you to inject a DLL and then to call any functions inside the injected DLL(s). The Inject DLL dialog can be opened from the main window by selecting the Tools/DLL Injector menu item or pressing Ctrl-J, or from the Disassembler window by selecting the Tools/DLL Injector menu item or pressing Ctrl-J.

Inject DLL

Enter the DLL name or path, or press Browse to browse for a DLL to inject. This simply puts the path into the edit box without actually injecting.

If you would like the DLL to be unloaded when MHS detaches from the target process, check the Uninject When MHS Deteches check. If MHS closes, or if another process is opened, the DLL will be unloaded from the target process first.

Press Inject to inject the DLL described in the edit box. This adds it to the list where you can see its address in the target process and whether or not it will be uninjected when MHS detaches. If the DLL has already been injected it is not injected again, but rather its Uninject When MHS Deteches status is updated. If the DLL already exists in the target process but not because of MHS, its reference count can not be tracked and so it can not be guaranteed to be unloaded when MHS detaches. Therefore, if the Uninject When MHS Deteches is checked MHS will prompt you to allow injecting without the ability to uninject later.

Call Function

If no DLL is selected from the list, this is left blank. Select a DLL from the list to enable this area.

The functions in the selected DLL are listed in the combobox. You may select any function from this list to call. There is no restriction on the calling convention, number of parameters, or return type.

In the Parameters edit, enter the parameters to be passed to the function. Separate each with a comma. Each individual parameter can be any valid expression, so, for example, “122 * 32, gamex86.dll + 5334h, ([gamex86.dll+0x1FE80] & 0xFF) || ([gamex86.dll+0x1FE78] & 0xFF)” is a valid parameter list (if the target process has a gamex86.dll) with 3 parameters. See Expression Evaluator for more information on valid expressions.

Each parameter evaluates to either long, unsigned long, __int64, unsigned __int64, or double. A parameter can be forced to evaluate to a different type using a standard C cast. For example, ([gamex86.dll+0x1FE80] / 3.33f) normally evaluates to a double type because a floating-point value was used in the expression. You can cast this back to a DWORD with (DWORD)([gamex86.dll+0x1FE80] / 3.33f). Casting is only performed after the entire expression has been evaluated.

If an incorrect number of parameters are passed or the parameters are not in a format the target function expects, the game’s internal state may be modified undesirably and lead to undesired behavior and even crashes.

__cdecl or __stdcall should be selected depending on the calling convention of the target function. If you are unsure which to use, check with the person who wrote the DLL you are injecting (which may be you). __cdecl functions require the caller to realign the stack while __stdcall functions align their own stacks. Most functions use __stdcall. In practice, however, a wrong calling type often causes no problems due to the methods used by MHS to call the function. Also, if there are no parameters, the calling type can be ignored.

Press Call Function to call the selected function from the selected DLL with the current parameters and calling type. The return from the function is shown after calling it. If the function returns float, double, or void, the return value will be garbage. Also, if the function returns an __int64, the return shown will be truncated at 32 bits.

Uninjecting

Press Uninject Selected DLL to uninject the selected DLL.

Other

MHS injects a DLL into the target process the first time a function is called. This is assumed not to be a problem because you would have already injected a DLL in order to call a function. Some games detect DLL injection, so be aware of this while using this dialog.

MHS calls CreateRemoteThread() when a DLL is injected and when a remote function is called. Some games detect this, so be aware of this while using this dialog. In the future, there will be alternative (and faster) ways of calling remote functions.


Memory Allocator

The Memory Allocator allows you to allocate memory within the target process. You can access the Memory Allocator from either the main window or the Disassembler window by selecting the Tools/Memory Allocator menu item or pressing Ctrl-M.

Add Allocation

The Add Allocations box provides you with the options for adding allocations.

The following table explains the options available for adding allocations.

Option | Meaning
Size | The size of the allocation. The actual allocation will always be this number rounded up to the nearest page (typically 0x1000 bytes). So an allocation of 32 bytes actually yields 0x1000 bytes starting at the allocated address. If the size is a multiple of the page size, the actual size specified is used.
Execute | Indicates that the allocated memory will have page properties that allow it to contain executable code. Always se this if the allocation is to be used for injected code.
Read | Indicates that the allocated memory will be readable. This must be set if any data is to be stored in the allocated space.
Write | Indicates that the allocated memory will be writable. This can not be set unless Read is also set. If an attemp is made to set this without having Read set, a warning dialog will appear which will allow you to add the Read property or remove the Write property.
Deallocate When MHS Detaches | This indicates that the allocation will be cleaned up automatically by MHS when MHS detaches from the target process. If this is not checked, the allocation will remain even if MHS detaches or closes.

If none of the Execute, Read, or Write options are selected, the allocation will be of an inaccessible type (PAGE_NOACCESS).

Allocations

This listview shows all allocations that have been made and their properties.

The following table explains the properties.

Property | Meaning
Address | The address of the allocation
Size | The requested size of the allocation.
Flags | Indicates the readable, writable, and executable properties of the allocation. If this is blank, the allocation allows no access (PAGE_NOACCESS property).
Auto-Deallocate | Indicates whether the allocation will be automatically deallocated when MHS detaches from the target process. Detaching occurs when MHS closes, when another process is opened, or when the target process closes.

Modify Selected Allocation

This box allows you to modify all selected allocations. Here you can update their properties or remove them. Removing them causes them to be deallocated from the target process and the memory will no longer be usable, so if you have reserved allocations for code, ensure that the code is no longer being executed before removing the allocation.

If only one allocation is selected the checks will only have 2 states: checked or unchecked. If more are selected the checks then have 3 states: checked, unchecked, and indeterminate.

If a check is set to indeterminate, that property will not be changed on the selected allocations.

Press the Modify Selected button to apply the changes to the selected allocations.

Other

If MHS detaches from the target process, all allocations set to auto-deallocate are deallocated. If MHS attaches to the target process again, all automatically deallocated allocations are reallocated. Allocations that were no removed when detaching are not added again when reattaching. If another process is loaded, the automatically deallocated allocations are not automatically reallocated.

Each allocation remembers the process name and will not be allocated into another process unless specifically asked to do so.


Preprocessor

MHS comes with a fully featured 100% conformant preprocessor. This feature is not necessarily intended to assist in working with the target process. This feature is simply an extra that can help some people in any case when they may need a real preprocessor for any miscellaneous work they are doing. The Preprocessor is primarily used with the scripts, but it is provided here as a stand-alone tool to lend a hand when needed. Specifically, the Preprocessor may be helpful in adding macros to Java® projects, or for preprocessing header files for use with custom C/C++ parsers.

To access the Preprocessor, select the Tools/Preprocessor menu item.

Include Directories

These are the directories that will be searched when #include <…> directives are reached. The directories are scanned in the order in which they are listed. Use the Up and Down buttons to change the order of include directories.

Environment variables can be used in the paths. Press the Environment Variables button to add more if you need.

Predefined Macros

These are simple macros to be predefined before preprocessing begins. Macros must be valid identifiers and can not be function-like or variadic. If no value is specified, the macro is simply defined as #define MACRO, otherwise it is defined as #define MACRO VALUE.

You can allow the standard Windows® macros to be automatically included by checking the Auto-Include Windows Macros check.

The following lists the macros that are defined with this is checked:

Macro | Value
_CPPLIB_VER | 405
_CPPRTTI |
_CRT_SECURE_NO_DEPRECATE |
_INTEGRAL_MAX_BITS | 64
_M_IX86 | 600
_M_IX86_FP | 0
_MSC_EXTENSIONS | 1
_MSC_VER | 1400
_MT |
_NATIVE_WCHAR_T_DEFINED |
_WCHAR_T_DEFINED |
_WIN32 |
_WINDOWS |
_Wp64 |
WIN32 |

Preprocess a File

This is where you can actually preprocess a file with the options above. An input and output file are required. If Retain Whitespace/Comments is checked, all whitespace and comments are included in the output file. Otherwise, all comments, blank lines, and preceding whitespace is stripped in the output file. If Emit #line Directives is checked, #line directives will be included in the output file in the format #line <line> "<file>".

Press the Go! button to begin preprocessing a file.

Support

This preprocessor is a Standard-conformant C++ preprocessor and accepts several Microsoft® extensions such as #pragma once.

Function-like macros are supported, as well as variadic macros (macros with a variable number of parameters). All macros are correctly expanded as per the Standard.

All standard preprocessor directives are supported.

The preprocessor is based off Wave.


Programs

The Programs page of the Options dialog allows adding custom programs on the Tools menu. Select the Tools/Options... menu item and select Programs from the list to view this page.

The upper area of the dialog contains a list of all custom programs available. Click the New button to add a new program to the list, or select an item and click Delete to remove a program from the list. The Up and Down buttons will change the order of items in the list.

After selecting a program from the list, the program’s details will be shown in the Program Options box. The Name field indicates the caption that will appear on the Tools menu, and may contain an ‘&’ to indicate the default character. The Program field should contain the file name of the program to be run. Click the ‘…’ button next to the Program field to use a file dialog box to select a file. The arguments for the program can be specified in the Arguments field. Environment Variables are allowed in both the Program and Arguements fields . See Environment Variables for more information.

A working directory can be specified for the program by entering a directory into the Working Dir field. Click the ‘…’ button next to the field to use a directory browser to select the directory. Environment Variables are allowed here. See Environment Variables for more information.

The Visible check indicates whether or not the selected item will be visible in the Tools menu.

By default, the following custom programs are installed:

  • Windows Calculator…: Displays the standard Windows calculator.
  • Windows Notepad…: Runs the standard Windows Notepad.


Introduction to Scripts

MHS allows you to write scripts to perform a large array of options, which will continue to be expanded throughout each of the next releases. Script syntax is very similar to C, but with a few modifications from C++ and Java®. Because scripts are implicitly for advanced users, this section assumes you have an understanding of coding terminology and practice; this section is not meant to teach you how to code. Because the syntax is almost exactly like C, programmers who are already familiar with C will be able to start writing scripts right away.

Scripts are compiled into bytecode like Java®, and execute very quickly. Unlike Java®, however, they allow full access to RAM, as C and C++ do. This means you can work with pointers with the same flexibility as you can in C/C++.

TO BE ADDED: Scripts inside MHS are broken down into tool-specific sets. This means there are scripts just for the Hex Editor, just for the system. just for the Debugger, etc. (note: this has yet to be implemented and at this time there are only scripts for the system, however the documentation for scripting will assume they have been implemented. Do not be confused when the documentation refers to the scripts for the Hex Editor—it is planned feature that will be added in an upcoming release).

The biggest feature, however is the new extern implementation. The extern implementation allows you to define variables and functions (soon) that are declared inside the target process or files. When working with the system scripts, extern will declare variables in the target process. While working with the Hex Editor scripts, variables will be declared in either the open file or the open process, depending on what the current Hex Editor window is displaying. Once an extern variable is declared, it can be used exactly the same way as normal variables in scripts can, including the use of all variable operators, such as & (address of), * (indirection), . (member access), -> (pointer member access), and [] (array access).

Scripts are event-driven, roughly the same way as mIRC scripts are. This means MHS will issue system events that can be handled by scripts, if the correct script functions have been written. Script functions can also be associated with hotkeys, so that pressing the hotkey of your desire will execute the script function of your desire.

Scripts are stored as .lss (L. Spiro Script) files. For information on how to create them, modify them, and add them to a script set, see Using the Code Editor.


Using the Code Editor

The Code Editor is used to edit and compile the source files for all scripts used by MHS. Any text editor is sufficient for editing the source files, as the .lss file format is plain text with no special formatting, however only the Code Editor can be used to compile the scripts.

Layout

Number | Name | Description
 | Editing Area | This is were you edit the code for the open script. You may edit any script file, regardless of whether it is part of any script set or not.
 | Functions | This dockable window displays all functions available for the current script set. It organizes them into groups and displays their names and parameters and provides an explanation for each. As with all dockable windows, hold Ctrl to drag it while not allowing it to dock.
 | Scripts | This dockable window displays the scripts in the current script set. As with all dockable windows, hold Ctrl to drag it while not allowing it to dock.
 | Output Area | This is where compilation errors or message generated by the script go. Using PrintF(), Warning(), and Error() send their texts here.
 | Script Tabs | Each open script has a tab. Edit a script by selecting its tab.
 | Compile Button | Use this to compile the current script set. Compiling a script set implies that all running threads in the script will be halted. Scripts are automatically compiled at the launch of the program, so this is not required to begin using scripts.

Script Sets

Scripts are broken into sets. For example, scripts related to the MHS system, scripts related to the Hex Editor, etc. Currently the only supported script set is System, which are the scripts that work with MHS generically.

To change between current script sets, use the Script Set menu item. Currently the only script set is System. When you select a new script set, the Functions dockable window will list the functions for that script set, and the Scripts dockable window will display the script files that are in that script set.

To add a script file to a script set, press Ctrl-D or select the File/Add to Scripts menu item. The file will be added to the current script set, which means it will be compiled with the other scripts in the set either the next time MHS is run or the next time you press Compile.

To remove a script file from the current script set, press Ctrl-R or select the File/Remove from Scripts menu item.

Creating/Modifying Files

To create a new script file, press Ctrl-N or select the File/New menu item. New files are not added to the current script set automatically.

To open an existing script file, whether part of any script set or not, press Ctrl-O or select the File/Open menu item.

To save an open script file, select its tab and press Ctrl-S or select the File/Save menu item. If the file has not been saved already, you will be prompted to supply a file name.

To save a script file under a new name, select its tab and press Ctrl-Shift-A or select the File/Save As menu item.

To save all open script files, select the File/Save All menu item.

To close an open script file, select its tab and press press Ctrl-F4 or select the File/Close menu item. If the file has not been saved since its last modification, you will be prompted to save.

To close all open script files, press Ctrl-Shift-F4 or select the File/Close All menu item. All unsaved files will prompted you to save.

Edit Options

The edit options are standard and are in no way unique to this application.

Menu Item | Shortcut | Operation
Undo | Ctrl-Z | Undo the last operation in the current script file. There is no limit on the number of operations that can be undone.
Redo | Ctrl-Y | Redo the last operation in the current script file.
Cut | Ctrl-X | Cut the selected text from the current script file.
Copy | Ctrl-C | Copy the selected text from the current script file.
Paste | Ctrl-V | Paste text over the selected area in the current script file. If no selection, the paste operation is performed at the caret position and the data is inserted.
Delete | Del | Delete the selected text in the current script file. If no selection, the character before the caret is deleted.
Select All | Ctrl-A | Select all text in the current script file.

Find

Press Ctrl-F or select the Search/Find menu item to load the Find dialog. the following chart explains each option.

Option | Description
Find What | This is the text to find.
Match Whole Word Only | Indicates that only whole words will be matched in the search. Whole words are words that are surrounded on both sides by characters other than alpha-numeric characters and underscores.
Match Case | Indicates that the values found in the script file must have the same case as the value in Find what.
Regular Expression | Indicates that Find what is a regular-expression-formatted string. See Wildcards and Regular Expressions.
Wrap Around | Indicates that the search will wrap at the end of the file and come back to the starting point before the search is complete.
Up | Indicates the search will proceed upwards.
Down | Indicates the search will proceed downwards.
Mark All | Instead of finding the next occurrence, this will place a bookmark on all occurrences found.

Press F3 or select the Search/Find Next... menu item to repeat the search from the current position. Repeating a search uses the previous search parameters and always performs a Find Next operation rather than a Mark All operation, even if Mark All was the last operation performed. The search is also always performed downwards.

Press Ctrl-F3 or select the Search/Find Previous... menu item to repeat the search, but upwards instead of downwards.

Replace

Press Ctrl-H or select the Search/Replace menu item to load the Replace dialog. The following chart explains each option.

Option | Description
Find What | This is the text to find.
Replace With | If replacing, all matches are replaced with this text value.
Match Whole Word Only | Indicates that only whole words will be matched in the search. Whole words are words that are surrounded on both sides by characters other than alpha-numeric characters and underscores.
Match Case | Indicates that the values found in the script file must have the same case as the value in Find what.
Regular Expression | Indicates that Find what is a regular-expression-formatted string. See Wildcards and Regular Expressions.
Wrap Around | Indicates that the search will wrap at the end of the file and come back to the starting point before the search is complete.
Find Next | Finds the next occurrence without replacing it.
Replace | Finds the next occurrence and replaces it.
Replace All | Finds all occurrences and replaces them. The number of replacements made is displayed in the Replacements label.
Replace in Selection | Finds all occurrences completely within the selected area and replaces them. The number of replacements made is displayed in the Replacements label.

Go To...

Press Ctrl-G or select the Search/Go To... menu item to load the Go To dialog.

You can go to any position in the current script file, either by line number or character position.

This is mostly used after compiling a script with errors. For the time being, the error report tells you the position of the error and you must manually go there using the Go To dialog. To quickly go to any error, press Ctrl-G and immediately type the error line. By default, when the dialog is loaded, the Line field is automatically highlighted and ready to be edited.

Bookmarks

Bookmarks are used to indicate areas of interest in your code. They appear as white ragged-edged circles next to the line numbers. Bookmarks allow easy navigation of your code through the keyboard shortcuts (F2 and Ctrl-F2) used to quickly go to the next and previous bookmarks.

To add a bookmark, press Ctrl-B or select the Search/Toggle Bookmark menu item. If no bookmark is on the current line of the current file, one will be added, otherwise the existing one will be removed.

To go to the next bookmark after the caret position, press F2 or select the Search/Next Bookmark menu item.

To go to the previous bookmark from the caret position, press Ctrl-F2 or select the Search/Previous Bookmark menu item.

To clear all bookmarks, press Ctrl-Shift-F2 or select the Search/Clear All Bookmarks menu item.

Dockable Windows

If any of the dockable windows are closed, they can be reopened by using the Window menu item.

All dockable windows in MHS can have their docking disabled by holding Ctrl while dragging them.

You can quickly open a script file in a script set by double-clicking it in the Scripts dockable window.

Code Folding

To fold a segment of code, click on the grey line anywhere in the range of the folding - indicator, as shown in the diagram to the left.

Folded code is designated by a line under the folded code segment and an unfolding + in the grey area to the left.

To unfold again, click the unfolding + indicator.

Options

The Code Editor options can be accessed in the Options dialog (Tools/Options menu item). Here you can enable or disable showing of lines and code folding, select the font and text size, and select styles and colors for each of the type of text elements.

Option | Description
Show Lines | Determines if line numbers are shown. Does not change code windows that are already open.
Code Folding | Determines if code folding is enabled. Does not change code windows that are already open.
Font | Allows you to select a font from the list. Fixed-width fonts are listed first.
Size | Indicates the size of the text.
Styles | Allows you to select the bold, italic, and color styles for each of the text elements.
The IDE control is Scintilla.


Script Basics

MHS incorporates a full programming language, using it as a scripting language allowing many useful features to be automated, and furthermore allowing much easier examination of the target process during RAM. In future versions, it will even be possible to export your scripts as stand-alone .exe files capable of running entirely free of MHS.

The script syntax is almost identical to C, and includes structures, unions, enumerations, and typedefs.

View the following topics for more information on the syntax used when writing scripts:

  • Expressions
  • Declaring Script Variables
  • Data Types, Typedefs, Structs, Unions, and Enums
  • Arrays
  • Control Statements
  • Functions
  • Externals
  • Limitations

A large number of functions are available while working in any script set. See Script Function Reference for a complete list.

To view the predefined data, see Predefined Structures, Unions, Typedefs, Enums, and Constants.

To see how to handle events and make functional scripts, see Script Events.

For a walk-through explaining how to make basic scripts from scratch, see Script Walkthroughs.


Expressions

Expressions

Expressions in scripts can contain any of the standard C operators:

  • + (addition)
  • - (subtraction)
  • * (multiplication)
  • / (division)
  • ~ (binary invert [one’s compliment])
  • ^ (binary xor)
  • & (binary and)
  • | (binary or)
  • % (modulus)
  • ++ (increment [prefix and postfix])
  • -- (decrement [prefix and postfix])
  • ?: (ternary)
  • << (shift left)
  • >> (shift right)

Parentheses “(” and “)” can be used to group expressions. For example:

(13 + 358) * (12 ^ 3)

is a valid expression. The following comparison operators can be used:

  • < (less than)
  • > (greater than)
  • <= (less than or equal)
  • >= (greater than or equal)
  • == (equal)
  • != (not equal)
  • ! (not)

For example,

(64 > 32)

would return the value 1. Any of the assignment operators +=, -=, *=, /=, &=, ^=, %=, |=, <<=, or >>= can also be used.

Numbers

Numbers may be entered in 3 formats: octal, hexadecimal, and decimal. See Introduction to Number Systems.

  • Decimal: 64
  • Octal: 0100 (0 at the beginning of the number)
  • Hexadecimal: 0x40

Characters

As with C, characters are treated internally as numeric constants, and as such can be used in the same types of mathematical operations as any numbers.

Characters are single quotes (') that enclose the desired character value. For example, 'A' represents the character A. Escape sequences are the same as in C:

  • '\a': Bell (alert)
  • '\b': Backspace
  • '\f': Form feed
  • '\n': New line
  • '\r': Carriage return
  • '\t': Horizontal tab
  • '\v': Vertical tab
  • '\'': Single quotation mark
  • '\"': Double quotation mark
  • '\\': Backslash
  • '\?': Literal question mark
  • '\ooo': ASCII character in octal notation
  • '\xhh': ASCII character in hexadecimal notation

Character quotes can surround entire strings (for example, 'L. Spiro'), however only the first character will be used as the character constant ('L' in this case).

Octal sequences are interpreted up to the first non-octal character, and then are cast down to either type char or type short (wchar_t) in the case of Unicode strings. This is in contrast with C, which would have the programmer specify two octal characters to form a single Unicode character (for example, L'\032\040').

Hexadecimal sequences are interpreted up to the first non-hexadecimal character, and then are cast down to either type char or type short (wchar_t) in the case of Unicode strings. This is in contrast with C, which would have the programmer specify two hexadecimal characters to form a single Unicode character (for example, L'\x32\x40').

Create a Unicode character by placing an L in front of it, for example, L'A'.

Strings

Strings are exactly the same as in C with the exception of how octal and hexadecimal escape sequences are parsed. As mentioned above, octal and hexadecimal escape sequences are parsed until the first invalid character and then cast down to either char or short (wchar_t).


Declaring Script Variables

Basic Declarations

A variable can be declared using the syntax “<data type> <variable name>;” Use an “=” to assign a value to the variable upon initialization. For example:

  • int I;
  • short J = 3;
  • float fValue = 3.1415963f;

<data type> can be any built-in data type or a custom structure, union, enumeration, or typedef. See Data Types, Typedefs, Structs, Unions, and Enums for information on what is valid in a <data type>. Unlike in C, you can not use commas to create multiple variables at once.

Variables declared outside of functions are globals. Globals can be declared in any order, and used from anywhere in the script, which is in contrast with C and C++, where globals must be declared before they are used.

Variables declared inside functions are locals. Unlike in C, locals can be declared anywhere inside the function, however, like C, they must be declared before they can be used.

Like in C and C++, you can declare structures at the same time that you declare the variable. See Data Types, Typedefs, Structs, Unions, and Enums for more information.

Constants

Constants are values that do not change. They follow the same rules as above, however they can not be modified, and during compilation they are always simplified into their numeric constant values for optimization.

To declare a constant value, prefix its declaration with the keyword const. For example:

  • const float PI = 3.1415963f;

Because PI will be simplified during compilation, typing “fValue *= PI;” would be exactly the same as typing “fValue *= 3.1415963f;”.

Function parameters can be declared as constants, but this has absolutely no meaning to the compiler at all; it is just to inform the user that the value is not modified inside the function (even though the compiler does not enforce this at all).


Data Types, Typedefs, Structs, Unions, and Enums

Basic Data Types

Because the scripting language is intended to work closely with real C and C++ and is used for hacking and mapping the target process RAM, it supports all typical data types. The following chart shows each type.

Type | Aliases | Description
char | int8, __int8, CHAR, TCHAR | 8-Bit Signed Integer
byte | unsigned int8, unsigned __int8, BYTE | 8-Bit Unsigned Integer
short | int16, __int16, wchar_t, SHORT, WCHAR | 16-Bit Signed Integer
unsigned short | unsigned int16, unsigned __int16, WORD | 16-Bit Unsigned Integer
int | int32, __int32, long, INT, LONG | 32-Bit Signed Integer
unsigned int | unsigned int32, unsigned __int32, unsigned long, UINT, ULONG, DWORD | 32-Bit Unsigned Integer
int64 | __int64, INT64 | 64-Bit Signed Integer
unsigned int64 | unsigned __int64, UINT64, QWORD | 64-Bit Unsigned Integer
float | FLOAT | 32-Bit Floating-Point Number
double | DOUBLE | 64-Bit Floating-Point Number

Typedefs

Aliases for built-in data types, structures, unions, and existing typedefs can be created by using the typedef keyword. The syntax for creating new types is “typedef <data type> <new type name>”. For example,

typedef unsigned int myInt;

would generate a new data type myInt for unsigned integers. <data type> can be any data type, structure, union, or typedef. Typedefs can not be used with arrays, however, but they can be used with pointer types.

Typedefs must be declared before they can be used, unlike the other data types.

Each time the same typedef is declared, it overwrites its previous meaning without throwing an error or warning.

Structs

Structures and unions work the same way as in C; they are complicated data types created by combining other data types, including other structures and unions. Use the struct keyword to define structures. For example:

struct NiftyPuff {
int iA;
int iB;
int iC;
};

You can declare a variable instance of the structure at the same time as you define the structure, as you can in C. For example,

struct NiftyPuff {
int iA;
int iB;
int iC;
} npPuffster;

would define a structure named NiftyPuff and create a variable npPuffster of type NiftyPuff. Unlike in C, you can not use commas to create multiple variables at once.

Structures are always global, even when nested inside each other, however variables declared at the same time as the structures have the same locality as they normally would have (global or local).

Normally structures can be declared anywhere and used from anywhere. The only exceptions are when the structure members require typedef information, as typedefs must be declared before they can be used, or if any members are typed as structures that have not been declared or forward-declared yet.

You can forward-declare a structure the same way as in C, for example,

struct NiftyPuff;

would forward-declare Niftypuff so that it can be used as a member of other structures without having been defined.

The declaration rules for the structure members follow the same rules as other variable declarations, except for locality (they are neither global nor local, but are structure members), which means any member can be declared as any type, including other structures and unions, and those structures and unions can be declared at the same time as the member of that type is declared. For example:

struct NiftyPuff {
int iA;
int iB;
int iC;
struct NestedStruct {
float fD;
double dE;
} nsF;
} npPuffster;

Here we have our npPuffster variable of type NiftyPuff, but NiftyPuff now contains a member nsF, which is of type NestedStruct, which was declared inside NiftyPuff at the same time nsF was declared. Remember that all structures and unions are global, so NestedStruct can be used without having to specify that it is somehow part of NiftyPuff.

A structure can not be a member of itself unless done so through a pointer.

Unions

Unions are exactly the same as structs, but with each member starting at offset 0, and the size of the union is just large enough to hold the largest data type in the union. Use the union keyword instead of the struct keyword.

union NiftyPuff {
int iA;
int iB;
int iC;
struct NestedStruct {
float fD;
double dE;
} nsF;
} npPuffster;

Enums

Enumerations work in the same way as they do in C, except that variables declared as an enumerated type always resolve to type int instead.

An enum can be created using the C syntax “enum [<type name>] { <constant name> [ = value ], ... } [<variable name>];”.

As in C, <type name> and <variable name> are optional.

enum CARDS {
CARDS_NONE = -1,
CARDS_HEARTS,
CARDS_CLUBS,
CARDS_DIAMONDS,
CARDS_SPADES
};

As in C, you can define values for each enumerated constant. Any undefined values are one number higher than the previous constant in the enumeration, with the first constant starting at 0, if its value is not otherwise specified. In our example, CLUB_HEARTS has value 0, which is one higher than CARDS_NONE, whose value is -1. If CARDS_NONE was not defined as -1, it would have been 0, and CLUB_HEARTS would then be 1.

Enumerations are always global, and do not need to be declared in any specific order.

Enumerations are always of type int.


Arrays

Arrays

Arrays of data types can be declared using the syntax “<data type> <variable name> [ <constant expression> ]”. <data type> can be any built-in data type or a custom structure, union, enumeration, or typedef. See Data Types, Typedefs, Structs, Unions, and Enums for information on what is valid in a <data type>.

int myArray[32];

The array size must be a constant number and must be larger than 0.

The array size can be a float or double type, but it will be automatically cast to an int, which is in contrast with C, which would throw an error.

The individual elements of the array can be accessed using the “[ ]” operator. For example:

for ( int i = 0; i < 32; i++ ) myArray[i] = i;


Control Statements

if Statements

Statements may be grouped using “{” and “}” and may contain the regular C if statement, or the if-else statement in the form “if ( <condition> ) <statement> [ else <statement> ]”. For example:

if ( x < 5 ) x = 0;

or

if ( y > x ) max = y;
else { max = x; y = 0; }

for Statements

The standard C for statement can be used in the form “for ( <initialization expression>; <expression>; <expression> ) <statement>” and work the same way as in C++. For example:

for ( int i = 0, x = 0; i < 15; i++ ) {
x += i;
}

while, do-while Statements

The while and do-while statements can also be used in the form “while ( <condition> ) <statement>” or “do <statement> while ( <condition> )” and work the same way as in C. For example:

while ( myVar < 15 ) {
x *= myVar;
myVar += 2;
}

or

do {
x *= myVar;
myVar += 2;
} while ( myVar < 23 );

switch Statements

A switch statement can be used to compare a variable with a number of values and perform different operations depending on the result. switch statements are of the form “switch ( <variable> ) { case <expression>: <statement>; [break;] ... default : <statement>; }” and works the same way as in C.

For example:

switch ( value ) {
case 2 : { result = 1; break; }
case 4 : { result = 2; break; }
case 8 : { result = 3; break; }
case 16 : { result = 4; break; }
default : { result = -1; }
}

break and continue

break or continue can be used using the syntax “break;” or “continue;”. Use break to exit out of the current for, switch, while, or do block and transfer program control to the first statement after the block. Use continue to jump to the end brace of any for or while loop and continue execution of the loop. These work the same way as they do in C.


Functions

Functions

Each script set comes with a large number of built-in functions, including most of the standard-library functions, though with capital letters to maintain consistent styling. Functions are called using the typical C syntax “<function name> ( <argument list> )”. For example,

char * pcDefinitionOfAwesome = "L. Spiro";
int iLength = StrLen( pcDefinitionOfAwesome );

would set iLength to 8.

Some functions have a variable number of arguments, as denoted by a ... parameter definition. For example:

PrintF( "%u of your base are belong to %s.", i, pcDefinitionOfAwesome );

These follow the same rules as in standard C/C++. Custom script functions, however, can not be defined this way. See Script Function Reference for a complete list of functions.

Custom Functions

Custom functions can be defined with the regular C syntax “<return type> <function name> ( <argument_list> ) { <statements> }”. The return type can be void or any of the supported built-in data types. For example:

void OutputInt( int d ) {
PrintF( "%d\n", d );
}
OutputInt( 5 ); // Call the custom function, which will then print 5.

Arguments are usually passed by value, but can be passed by reference using the & (address of) operator before the argument name. Array arguments can be indicated using the array operator [] after the argument name. Array arguments are always passed by reference, just as they are in C/C++.

Full recursion is supported on custom functions.

Synchronized Functions

Synchronized functions are functions that can only be executed by one thread at a time. If a script thread is inside a synchronized function and another script thread calls that, or any other synchronized function, the second script thread will be forced to wait until the first thread leaves the function. This allows the user to implement thread-safe code. To create a synchronized function, prefix the function declaration with the keyword synchronized. For example:

synchronized void OutputInt( int d ) {
PrintF( "%d\n", d );
}

Note that normal threading issues and threats apply. Specifically, deadlocks can occur if two threads are waiting on each other to leave synchronized functions.

Only one synchronized function can be called at a time in a script set. This means that if thread A is inside a synchronized function, and thread B wants to enter a synchronized function, thread B must wait even if it is not calling the same synchronized function as thread A.


Externals

Variables

An external variable is a variable declared in the target process, or in files when used with the Hex Editor. After being declared, they are used the same way as normal variables. External variables are declared using the form “extern <data type> <variable name> = { <string>, <expression> };”. For example:

extern int iValue = { "gamex86.dll", 0x1344C };

This would declare an external variable named iValue at the address 0x1344C bytes after the base address of gamex86.dll. <data type> can be any built-in data type or a custom structure, union, enumeration, or typedef. See Data Types, Typedefs, Structs, Unions, and Enums for information on what is valid in a <data type>. <string> must be a constant literal string. <expression> must resolve to an integer value.

You can use an empty string to indicate that the <expression> is the actual address of the variable in the target process or file. For example:

extern int iValue = { "", 0x0041344C };

This would declare iValue as an int in the target process (or file) at address 0x0041344C.

<data type> follows the same rules as with any other variable declaration, and as such you can define structures and unions at the same time you define the external variable. For example,

extern struct NiftyPuff {
int iA;
int iB[32];
float * pfC;
} e_npPuffster = { "gamex86.dll", 0x1344C };

would declare an external variable e_npPuffster as type Niftypuff at address [gamex86.dll+0x1344C].

For information on how to use the [module+offset] format, refer to the About Pointers section.

Using the & (address-of) operator on external variables returns the address of the variable as an unsigned long. Be sure to remember that the value returned is an unsigned long and not actually a pointer type. For example:

unsigned long ulThis = &e_npPuffster;

Examples of Usage

After declaring the above external variables, we can use them in the same ways as we can any other variables. Here are some examples:

  • iValue++;
  • int iCopy = iValue;
  • e_npPuffster.iA = iValue;
  • e_npPuffster.iB[iValue] = 3;
  • (*e_npPuffster.pfC) = 3.1415963f;
  • if ( e_npPuffster.iA > iValue ) { e_npPuffster.iA = iValue; }

Functions

External functions are not yet supported. External functions will, in the future, allow the user to call functions that exist inside the target process. Like external variables, external functions will be usable in the same manner as normal functions. Internally, however, the parameters passed in the script will be sent to the target process and the return value (if any) will be taken from the target process.


Limitations

The script was designed to be very similar to C, however there are a few differences as outlined on this page.

  • Comma-Separated Declarations: In C/C++, it is possible to declare multiple variables at once using a comma between them. This is currently not supported.
  • Multi-Dimensional Arrays: Multi-dimensional arrays are not supported and never will be. You can simulate them, however, by creating a structure whose only member is an array of the desired type, then create an array of that structure.
  • Enumeration Types: Declaring a variable as an enumerated type automatically resolves to type int instead, and is therefore in no way actually associated with the specified enumeration. Declaring variables as enumerated types is purely for visual candy.
  • Constants in Function Parameters: Function parameters declared as const have no special properties associated with being constant. The compiler literally strips this flag away, allowing parameters to be used in exactly the same way as they would without having been declared const. Declaring function parameters as const is for visual candy.
  • Passing Structures/Unions to Functions by Value: Structures and unions can be passed to functions as pointers, but not by value.
  • Functions Returning Structures/Unions: Functions can not return structures and unions by value, however they can return pointers to them.
  • External Functions: External functions are not yet supported, however there are plans to support them in the future (note that these external functions are not the same as C/C++ external functions—see Externals for details).
  • External Pointer Variables as Function Arguments: External variables declared as pointers can not yet be passed to functions, though this is planned for the future.


Script Preprocessor

Directives

  • #define: Allows to define macros. Function-like macros are supported, as well as variadic macros.
  • #elif: Allows conditional inclusion of sections of code.
  • #else: Allows conditional inclusion of sections of code.
  • #endif: Ends a conditional-inclusion section.
  • #error: Allows to throw an error in the preprocessor.
  • #if: Allows conditional inclusion of sections of code.
  • #ifdef: Allows conditional inclusion of sections of code.
  • #ifndef: Allows conditional inclusion of sections of code.
  • #include: Allows to include a given file.
  • #undef: Undefines a macro.

Operators

  • Stringizing Operator (# or #@): Causes the corresponding actual argument to be enclosed in double quotation marks.
  • Token-Pasting Operator (##): Allows tokens used as actual arguments to be concatenated to form other tokens.
  • defined Operator: Simplifies the writing of compound expressions in certain macro directives.

Including Files

Files to be included must be in either the MHS root directory, the Scripts\ subdirectory, or the Includes\ subdirectory.

Variadic Macros

Variadic macros are function-like macros that contain a variable number of arguments.

To use variadic macros, the ellipsis may be specified as the final formal argument in a macro definition, and the replacement identifier __VA_ARGS__ may be used in the definition to insert the extra arguments. __VA_ARGS__ is replaced by all of the arguments that match the ellipsis, including commas between them.

For example:

#ifdef _DEBUG
#define DBG_PRINT( STRING, ... ) PrintF( STRING, __VA_ARGS__ )
#else
#define DBG_PRINT( STRING, ... )
#endif

This would create a macro that prints a message only if the _DEBUG macro is defined (you have to define this yourself; it is not defined by the system).

Predefined Macros

A few macros are predefined as follows.

  • __DATE__: The compilation date of the current source file. The date is a string literal of the form Mmm dd yyyy.
  • __FILE__: The name of the current source file. __FILE__ expands to a string surrounded by double quotation marks.
  • __LINE__: The line number in the current source file. The line number is a decimal integer constant. It can be altered with a #line directive.
  • __STDC__: Indicates full conformance with the ANSI C standard. Defined as the integer constant 1.
  • __TIME__: The most recent compilation time of the current source file. The time is a string literal of the form hh:mm:ss.


Predefined Enums

enum {
    LS_NONE                       = 0 /* 0 */,
    LS_DATATYPE                   = 1 /* 1 */,
    LS_GROUP                      = 2 /* 2 */,
    LS_STRING                     = 3 /* 3 */,
    LS_POINTER                    = 4 /* 4 */,
    LS_SCRIPT                     = 5 /* 5 */,
    LS_EXP                        = 6 /* 6 */,

    ST_EXACT                      = 0 /* 0 */,
    ST_NOTEQUALTO                 = 1 /* 1 */,
    ST_RANGE                      = 2 /* 2 */,
    ST_LOWERTHAN                  = 3 /* 3 */,
    ST_GREATERTHAN                = 4 /* 4 */,
    ST_UNKNOWN                    = 5 /* 5 */,

    SST_EXACT                     = 0 /* 0 */,
    SST_NOTEQUALTO                = 1 /* 1 */,
    SST_INCREASED                 = 2 /* 2 */,
    SST_DECREASED                 = 3 /* 3 */,
    SST_RANGE                     = 4 /* 4 */,
    SST_SAME                      = 5 /* 5 */,
    SST_DIF                       = 6 /* 6 */,
    SST_CHANGEDBY                 = 7 /* 7 */,
    SST_SAMEASORIG                = 8 /* 8 */,

    MT_NONE                       = -1 /* FFFFFFFF */,
    MT_INT                        = 0 /* 0 */,
    MT_SHORT                      = 1 /* 1 */,
    MT_LONG                       = 2 /* 2 */,
    MT_USHORT                     = 3 /* 3 */,
    MT_ULONG                      = 4 /* 4 */,
    MT_FLOAT                      = 5 /* 5 */,
    MT_DOUBLE                     = 6 /* 6 */,
    MT_STRING                     = 7 /* 7 */,
    MT_BYTE                       = 8 /* 8 */,
    MT_CHAR                       = 9 /* 9 */,
    MT_POINTER                    = 10 /* A */,
    MT_INT64                      = 11 /* B */,
    MT_UINT64                     = 12 /* C */,
};

enum {
    TH32CS_SNAPHEAPLIST           = 1 /* 1 */,
    TH32CS_SNAPPROCESS            = 2 /* 2 */,
    TH32CS_SNAPTHREAD             = 4 /* 4 */,
    TH32CS_SNAPMODULE             = 8 /* 8 */,
    TH32CS_SNAPMODULE32           = 16 /* 10 */,
    TH32CS_SNAPALL                = 15 /* F */,
    TH32CS_INHERIT                = -2147483648 /* 80000000 */,
};

enum {
    PAGE_NOACCESS                 = 1 /* 1 */,
    PAGE_READONLY                 = 2 /* 2 */,
    PAGE_READWRITE                = 4 /* 4 */,
    PAGE_WRITECOPY                = 8 /* 8 */,
    PAGE_EXECUTE                  = 16 /* 10 */,
    PAGE_EXECUTE_READ             = 32 /* 20 */,
    PAGE_EXECUTE_READWRITE        = 64 /* 40 */,
    PAGE_EXECUTE_WRITECOPY        = 128 /* 80 */,
    PAGE_GUARD                    = 256 /* 100 */,
    PAGE_NOCACHE                  = 512 /* 200 */,
    PAGE_WRITECOMBINE             = 1024 /* 400 */,

    MEM_COMMIT                    = 4096 /* 1000 */,
    MEM_RESERVE                   = 8192 /* 2000 */,
    MEM_DECOMMIT                  = 16384 /* 4000 */,
    MEM_RELEASE                   = 32768 /* 8000 */,
    MEM_FREE                      = 65536 /* 10000 */,
    MEM_PRIVATE                   = 131072 /* 20000 */,
    MEM_MAPPED                    = 262144 /* 40000 */,
    MEM_RESET                     = 524288 /* 80000 */,
    MEM_TOP_DOWN                  = 1048576 /* 100000 */,
    MEM_WRITE_WATCH               = 2097152 /* 200000 */,
    MEM_PHYSICAL                  = 4194304 /* 400000 */,
    MEM_LARGE_PAGES               = 536870912 /* 20000000 */,
    MEM_4MB_PAGES                 = -2147483648 /* 80000000 */,

    SEC_FILE                      = 8388608 /* 800000 */,
    SEC_IMAGE                     = 16777216 /* 1000000 */,
    SEC_RESERVE                   = 67108864 /* 4000000 */,
    SEC_COMMIT                    = 134217728 /* 8000000 */,
    SEC_NOCACHE                   = 268435456 /* 10000000 */,

    MEM_IMAGE                     = 16777216 /* 1000000 */,

    WRITE_WATCH_FLAG_RESET        = 1 /* 1 */,
};

enum {
    HF32_DEFAULT                  = 1 /* 1 */,
};

enum {
    MBS_OK                        = 1 /* 1 */,
    MBS_CANCEL                    = 2 /* 2 */,
    MBS_YES                       = 4 /* 4 */,
    MBS_NO                        = 8 /* 8 */,
};

enum {
    UINT_MAX                      = -1 /* FFFFFFFF */,

    WM_NULL                       = 0 /* 0 */,
    WM_CREATE                     = 1 /* 1 */,
    WM_DESTROY                    = 2 /* 2 */,
    WM_MOVE                       = 3 /* 3 */,
    WM_SIZE                       = 5 /* 5 */,
    WM_ACTIVATE                   = 6 /* 6 */,

    WA_INACTIVE                   = 0 /* 0 */,
    WA_ACTIVE                     = 1 /* 1 */,
    WA_CLICKACTIVE                = 2 /* 2 */,

    WM_SETFOCUS                   = 7 /* 7 */,
    WM_KILLFOCUS                  = 8 /* 8 */,
    WM_ENABLE                     = 10 /* A */,
    WM_SETREDRAW                  = 11 /* B */,
    WM_SETTEXT                    = 12 /* C */,
    WM_GETTEXT                    = 13 /* D */,
    WM_GETTEXTLENGTH              = 14 /* E */,
    WM_PAINT                      = 15 /* F */,
    WM_CLOSE                      = 16 /* 10 */,
    WM_QUERYENDSESSION            = 17 /* 11 */,
    WM_QUERYOPEN                  = 19 /* 13 */,
    WM_ENDSESSION                 = 22 /* 16 */,
    WM_QUIT                       = 18 /* 12 */,
    WM_ERASEBKGND                 = 20 /* 14 */,
    WM_SYSCOLORCHANGE             = 21 /* 15 */,
    WM_SHOWWINDOW                 = 24 /* 18 */,
    WM_WININICHANGE               = 26 /* 1A */,
    WM_SETTINGCHANGE              = 26 /* 1A */,
    WM_DEVMODECHANGE              = 27 /* 1B */,
    WM_ACTIVATEAPP                = 28 /* 1C */,
    WM_FONTCHANGE                 = 29 /* 1D */,
    WM_TIMECHANGE                 = 30 /* 1E */,
    WM_CANCELMODE                 = 31 /* 1F */,
    WM_SETCURSOR                  = 32 /* 20 */,
    WM_MOUSEACTIVATE              = 33 /* 21 */,
    WM_CHILDACTIVATE              = 34 /* 22 */,
    WM_QUEUESYNC                  = 35 /* 23 */,
    WM_GETMINMAXINFO              = 36 /* 24 */,
    WM_PAINTICON                  = 38 /* 26 */,
    WM_ICONERASEBKGND             = 39 /* 27 */,
    WM_NEXTDLGCTL                 = 40 /* 28 */,
    WM_SPOOLERSTATUS              = 42 /* 2A */,
    WM_DRAWITEM                   = 43 /* 2B */,
    WM_MEASUREITEM                = 44 /* 2C */,
    WM_DELETEITEM                 = 45 /* 2D */,
    WM_VKEYTOITEM                 = 46 /* 2E */,
    WM_CHARTOITEM                 = 47 /* 2F */,
    WM_SETFONT                    = 48 /* 30 */,
    WM_GETFONT                    = 49 /* 31 */,
    WM_SETHOTKEY                  = 50 /* 32 */,
    WM_GETHOTKEY                  = 51 /* 33 */,
    WM_QUERYDRAGICON              = 55 /* 37 */,
    WM_COMPAREITEM                = 57 /* 39 */,
    WM_GETOBJECT                  = 61 /* 3D */,
    WM_COMPACTING                 = 65 /* 41 */,
    WM_COMMNOTIFY                 = 68 /* 44 */,
    WM_WINDOWPOSCHANGING          = 70 /* 46 */,
    WM_WINDOWPOSCHANGED           = 71 /* 47 */,
    WM_POWER                      = 72 /* 48 */,

    PWR_OK                        = 1 /* 1 */,
    PWR_FAIL                      = -1 /* FFFFFFFF */,
    PWR_SUSPENDREQUEST            = 1 /* 1 */,
    PWR_SUSPENDRESUME             = 2 /* 2 */,
    PWR_CRITICALRESUME            = 3 /* 3 */,

    WM_COPYDATA                   = 74 /* 4A */,
    WM_CANCELJOURNAL              = 75 /* 4B */,
    WM_NOTIFY                     = 78 /* 4E */,
    WM_INPUTLANGCHANGEREQUEST     = 80 /* 50 */,
    WM_INPUTLANGCHANGE            = 81 /* 51 */,
    WM_TCARD                      = 82 /* 52 */,
    WM_HELP                       = 83 /* 53 */,
    WM_USERCHANGED                = 84 /* 54 */,
    WM_NOTIFYFORMAT               = 85 /* 55 */,

    NFR_ANSI                      = 1 /* 1 */,
    NFR_UNICODE                   = 2 /* 2 */,

    NF_QUERY                      = 3 /* 3 */,
    NF_REQUERY                    = 4 /* 4 */,

    WM_CONTEXTMENU                = 123 /* 7B */,
    WM_STYLECHANGING              = 124 /* 7C */,
    WM_STYLECHANGED               = 125 /* 7D */,
    WM_DISPLAYCHANGE              = 126 /* 7E */,
    WM_GETICON                    = 127 /* 7F */,
    WM_SETICON                    = 128 /* 80 */,
    WM_NCCREATE                   = 129 /* 81 */,
    WM_NCDESTROY                  = 130 /* 82 */,
    WM_NCCALCSIZE                 = 131 /* 83 */,
    WM_NCHITTEST                  = 132 /* 84 */,
    WM_NCPAINT                    = 133 /* 85 */,
    WM_NCACTIVATE                 = 134 /* 86 */,
    WM_GETDLGCODE                 = 135 /* 87 */,
    WM_SYNCPAINT                  = 136 /* 88 */,
    WM_NCMOUSEMOVE                = 160 /* A0 */,
    WM_NCLBUTTONDOWN              = 161 /* A1 */,
    WM_NCLBUTTONUP                = 162 /* A2 */,
    WM_NCLBUTTONDBLCLK            = 163 /* A3 */,
    WM_NCRBUTTONDOWN              = 164 /* A4 */,
    WM_NCRBUTTONUP                = 165 /* A5 */,
    WM_NCRBUTTONDBLCLK            = 166 /* A6 */,
    WM_NCMBUTTONDOWN              = 167 /* A7 */,
    WM_NCMBUTTONUP                = 168 /* A8 */,
    WM_NCMBUTTONDBLCLK            = 169 /* A9 */,
    WM_NCXBUTTONDOWN              = 171 /* AB */,
    WM_NCXBUTTONUP                = 172 /* AC */,
    WM_NCXBUTTONDBLCLK            = 173 /* AD */,
    WM_INPUT                      = 255 /* FF */,
    WM_KEYFIRST                   = 256 /* 100 */,
    WM_KEYDOWN                    = 256 /* 100 */,
    WM_KEYUP                      = 257 /* 101 */,
    WM_CHAR                       = 258 /* 102 */,
    WM_DEADCHAR                   = 259 /* 103 */,
    WM_SYSKEYDOWN                 = 260 /* 104 */,
    WM_SYSKEYUP                   = 261 /* 105 */,
    WM_SYSCHAR                    = 262 /* 106 */,
    WM_SYSDEADCHAR                = 263 /* 107 */,
    WM_UNICHAR                    = 265 /* 109 */,
    WM_KEYLAST                    = 265 /* 109 */,

    UNICODE_NOCHAR                = 65535 /* FFFF */,

    WM_KEYLAST                    = 264 /* 108 */,
    WM_IME_STARTCOMPOSITION       = 269 /* 10D */,
    WM_IME_ENDCOMPOSITION         = 270 /* 10E */,
    WM_IME_COMPOSITION            = 271 /* 10F */,
    WM_IME_KEYLAST                = 271 /* 10F */,
    WM_INITDIALOG                 = 272 /* 110 */,
    WM_COMMAND                    = 273 /* 111 */,
    WM_SYSCOMMAND                 = 274 /* 112 */,
    WM_TIMER                      = 275 /* 113 */,
    WM_HSCROLL                    = 276 /* 114 */,
    WM_VSCROLL                    = 277 /* 115 */,
    WM_INITMENU                   = 278 /* 116 */,
    WM_INITMENUPOPUP              = 279 /* 117 */,
    WM_MENUSELECT                 = 287 /* 11F */,
    WM_MENUCHAR                   = 288 /* 120 */,
    WM_ENTERIDLE                  = 289 /* 121 */,
    WM_MENURBUTTONUP              = 290 /* 122 */,
    WM_MENUDRAG                   = 291 /* 123 */,
    WM_MENUGETOBJECT              = 292 /* 124 */,
    WM_UNINITMENUPOPUP            = 293 /* 125 */,
    WM_MENUCOMMAND                = 294 /* 126 */,
    WM_CHANGEUISTATE              = 295 /* 127 */,
    WM_UPDATEUISTATE              = 296 /* 128 */,
    WM_QUERYUISTATE               = 297 /* 129 */,

    UIS_SET                       = 1 /* 1 */,
    UIS_CLEAR                     = 2 /* 2 */,
    UIS_INITIALIZE                = 3 /* 3 */,
    UISF_HIDEFOCUS                = 1 /* 1 */,
    UISF_HIDEACCEL                = 2 /* 2 */,
    UISF_ACTIVE                   = 4 /* 4 */,

    WM_CTLCOLORMSGBOX             = 306 /* 132 */,
    WM_CTLCOLOREDIT               = 307 /* 133 */,
    WM_CTLCOLORLISTBOX            = 308 /* 134 */,
    WM_CTLCOLORBTN                = 309 /* 135 */,
    WM_CTLCOLORDLG                = 310 /* 136 */,
    WM_CTLCOLORSCROLLBAR          = 311 /* 137 */,
    WM_CTLCOLORSTATIC             = 312 /* 138 */,

    MN_GETHMENU                   = 481 /* 1E1 */,

    WM_MOUSEFIRST                 = 512 /* 200 */,
    WM_MOUSEMOVE                  = 512 /* 200 */,
    WM_LBUTTONDOWN                = 513 /* 201 */,
    WM_LBUTTONUP                  = 514 /* 202 */,
    WM_LBUTTONDBLCLK              = 515 /* 203 */,
    WM_RBUTTONDOWN                = 516 /* 204 */,
    WM_RBUTTONUP                  = 517 /* 205 */,
    WM_RBUTTONDBLCLK              = 518 /* 206 */,
    WM_MBUTTONDOWN                = 519 /* 207 */,
    WM_MBUTTONUP                  = 520 /* 208 */,
    WM_MBUTTONDBLCLK              = 521 /* 209 */,
    WM_MOUSEWHEEL                 = 522 /* 20A */,
    WM_XBUTTONDOWN                = 523 /* 20B */,
    WM_XBUTTONUP                  = 524 /* 20C */,
    WM_XBUTTONDBLCLK              = 525 /* 20D */,
    WM_MOUSELAST                  = 525 /* 20D */,
    WM_MOUSELAST                  = 522 /* 20A */,
    WM_MOUSELAST                  = 521 /* 209 */,

    WHEEL_DELTA                   = 120 /* 78 */,
    WHEEL_PAGESCROLL              = -1 /* FFFFFFFF */,

    XBUTTON1                      = 1 /* 1 */,
    XBUTTON2                      = 2 /* 2 */,

    WM_PARENTNOTIFY               = 528 /* 210 */,
    WM_ENTERMENULOOP              = 529 /* 211 */,
    WM_EXITMENULOOP               = 530 /* 212 */,
    WM_NEXTMENU                   = 531 /* 213 */,
    WM_SIZING                     = 532 /* 214 */,
    WM_CAPTURECHANGED             = 533 /* 215 */,
    WM_MOVING                     = 534 /* 216 */,
    WM_POWERBROADCAST             = 536 /* 218 */,

    PBT_APMQUERYSUSPEND           = 0 /* 0 */,
    PBT_APMQUERYSTANDBY           = 1 /* 1 */,
    PBT_APMQUERYSUSPENDFAILED     = 2 /* 2 */,
    PBT_APMQUERYSTANDBYFAILED     = 3 /* 3 */,
    PBT_APMSUSPEND                = 4 /* 4 */,
    PBT_APMSTANDBY                = 5 /* 5 */,
    PBT_APMRESUMECRITICAL         = 6 /* 6 */,
    PBT_APMRESUMESUSPEND          = 7 /* 7 */,
    PBT_APMRESUMESTANDBY          = 8 /* 8 */,
    PBTF_APMRESUMEFROMFAILURE     = 1 /* 1 */,
    PBT_APMBATTERYLOW             = 9 /* 9 */,
    PBT_APMPOWERSTATUSCHANGE      = 10 /* A */,
    PBT_APMOEMEVENT               = 11 /* B */,
    PBT_APMRESUMEAUTOMATIC        = 18 /* 12 */,

    WM_DEVICECHANGE               = 537 /* 219 */,
    WM_MDICREATE                  = 544 /* 220 */,
    WM_MDIDESTROY                 = 545 /* 221 */,
    WM_MDIACTIVATE                = 546 /* 222 */,
    WM_MDIRESTORE                 = 547 /* 223 */,
    WM_MDINEXT                    = 548 /* 224 */,
    WM_MDIMAXIMIZE                = 549 /* 225 */,
    WM_MDITILE                    = 550 /* 226 */,
    WM_MDICASCADE                 = 551 /* 227 */,
    WM_MDIICONARRANGE             = 552 /* 228 */,
    WM_MDIGETACTIVE               = 553 /* 229 */,
    WM_MDISETMENU                 = 560 /* 230 */,
    WM_ENTERSIZEMOVE              = 561 /* 231 */,
    WM_EXITSIZEMOVE               = 562 /* 232 */,
    WM_DROPFILES                  = 563 /* 233 */,
    WM_MDIREFRESHMENU             = 564 /* 234 */,
    WM_IME_SETCONTEXT             = 641 /* 281 */,
    WM_IME_NOTIFY                 = 642 /* 282 */,
    WM_IME_CONTROL                = 643 /* 283 */,
    WM_IME_COMPOSITIONFULL        = 644 /* 284 */,
    WM_IME_SELECT                 = 645 /* 285 */,
    WM_IME_CHAR                   = 646 /* 286 */,
    WM_IME_REQUEST                = 648 /* 288 */,
    WM_IME_KEYDOWN                = 656 /* 290 */,
    WM_IME_KEYUP                  = 657 /* 291 */,
    WM_MOUSEHOVER                 = 673 /* 2A1 */,
    WM_MOUSELEAVE                 = 675 /* 2A3 */,
    WM_NCMOUSEHOVER               = 672 /* 2A0 */,
    WM_NCMOUSELEAVE               = 674 /* 2A2 */,
    WM_WTSSESSION_CHANGE          = 689 /* 2B1 */,
    WM_TABLET_FIRST               = 704 /* 2C0 */,
    WM_TABLET_LAST                = 735 /* 2DF */,
    WM_CUT                        = 768 /* 300 */,
    WM_COPY                       = 769 /* 301 */,
    WM_PASTE                      = 770 /* 302 */,
    WM_CLEAR                      = 771 /* 303 */,
    WM_UNDO                       = 772 /* 304 */,
    WM_RENDERFORMAT               = 773 /* 305 */,
    WM_RENDERALLFORMATS           = 774 /* 306 */,
    WM_DESTROYCLIPBOARD           = 775 /* 307 */,
    WM_DRAWCLIPBOARD              = 776 /* 308 */,
    WM_PAINTCLIPBOARD             = 777 /* 309 */,
    WM_VSCROLLCLIPBOARD           = 778 /* 30A */,
    WM_SIZECLIPBOARD              = 779 /* 30B */,
    WM_ASKCBFORMATNAME            = 780 /* 30C */,
    WM_CHANGECBCHAIN              = 781 /* 30D */,
    WM_HSCROLLCLIPBOARD           = 782 /* 30E */,
    WM_QUERYNEWPALETTE            = 783 /* 30F */,
    WM_PALETTEISCHANGING          = 784 /* 310 */,
    WM_PALETTECHANGED             = 785 /* 311 */,
    WM_HOTKEY                     = 786 /* 312 */,
    WM_PRINT                      = 791 /* 317 */,
    WM_PRINTCLIENT                = 792 /* 318 */,
    WM_APPCOMMAND                 = 793 /* 319 */,
    WM_THEMECHANGED               = 794 /* 31A */,
    WM_HANDHELDFIRST              = 856 /* 358 */,
    WM_HANDHELDLAST               = 863 /* 35F */,
    WM_AFXFIRST                   = 864 /* 360 */,
    WM_AFXLAST                    = 895 /* 37F */,
    WM_PENWINFIRST                = 896 /* 380 */,
    WM_PENWINLAST                 = 911 /* 38F */,
    WM_APP                        = 32768 /* 8000 */,
    WM_USER                       = 1024 /* 400 */,

    MK_LBUTTON                    = 1 /* 1 */,
    MK_RBUTTON                    = 2 /* 2 */,
    MK_SHIFT                      = 4 /* 4 */,
    MK_CONTROL                    = 8 /* 8 */,
    MK_MBUTTON                    = 16 /* 10 */,
    MK_XBUTTON1                   = 32 /* 20 */,
    MK_XBUTTON2                   = 64 /* 40 */,

    MNS_NOCHECK                   = -2147483648 /* 80000000 */,
    MNS_MODELESS                  = 1073741824 /* 40000000 */,
    MNS_DRAGDROP                  = 536870912 /* 20000000 */,
    MNS_AUTODISMISS               = 268435456 /* 10000000 */,
    MNS_NOTIFYBYPOS               = 134217728 /* 8000000 */,
    MNS_CHECKORBMP                = 67108864 /* 4000000 */,

    MIM_MAXHEIGHT                 = 1 /* 1 */,
    MIM_BACKGROUND                = 2 /* 2 */,
    MIM_HELPID                    = 4 /* 4 */,
    MIM_MENUDATA                  = 8 /* 8 */,
    MIM_STYLE                     = 16 /* 10 */,
    MIM_APPLYTOSUBMENUS           = -2147483648 /* 80000000 */,

    MF_INSERT                     = 0 /* 0 */,
    MF_CHANGE                     = 128 /* 80 */,
    MF_APPEND                     = 256 /* 100 */,
    MF_DELETE                     = 512 /* 200 */,
    MF_REMOVE                     = 4096 /* 1000 */,
    MF_BYCOMMAND                  = 0 /* 0 */,
    MF_BYPOSITION                 = 1024 /* 400 */,
    MF_SEPARATOR                  = 2048 /* 800 */,
    MF_ENABLED                    = 0 /* 0 */,
    MF_GRAYED                     = 1 /* 1 */,
    MF_DISABLED                   = 2 /* 2 */,
    MF_UNCHECKED                  = 0 /* 0 */,
    MF_CHECKED                    = 8 /* 8 */,
    MF_USECHECKBITMAPS            = 512 /* 200 */,
    MF_STRING                     = 0 /* 0 */,
    MF_BITMAP                     = 4 /* 4 */,
    MF_OWNERDRAW                  = 256 /* 100 */,
    MF_POPUP                      = 16 /* 10 */,
    MF_MENUBARBREAK               = 32 /* 20 */,
    MF_MENUBREAK                  = 64 /* 40 */,
    MF_UNHILITE                   = 0 /* 0 */,
    MF_HILITE                     = 128 /* 80 */,
    MF_DEFAULT                    = 4096 /* 1000 */,
    MF_SYSMENU                    = 8192 /* 2000 */,
    MF_HELP                       = 16384 /* 4000 */,
    MF_RIGHTJUSTIFY               = 16384 /* 4000 */,
    MF_MOUSESELECT                = 32768 /* 8000 */,
    MF_END                        = 128 /* 80 */,

    MFT_STRING                    = 0 /* 0 */,
    MFT_BITMAP                    = 4 /* 4 */,
    MFT_MENUBARBREAK              = 32 /* 20 */,
    MFT_MENUBREAK                 = 64 /* 40 */,
    MFT_OWNERDRAW                 = 256 /* 100 */,
    MFT_RADIOCHECK                = 512 /* 200 */,
    MFT_SEPARATOR                 = 2048 /* 800 */,
    MFT_RIGHTORDER                = 8192 /* 2000 */,
    MFT_RIGHTJUSTIFY              = 16384 /* 4000 */,

    MFS_GRAYED                    = 3 /* 3 */,
    MFS_DISABLED                  = 3 /* 3 */,
    MFS_CHECKED                   = 8 /* 8 */,
    MFS_HILITE                    = 128 /* 80 */,
    MFS_ENABLED                   = 0 /* 0 */,
    MFS_UNCHECKED                 = 0 /* 0 */,
    MFS_UNHILITE                  = 0 /* 0 */,
    MFS_DEFAULT                   = 4096 /* 1000 */,

    WS_OVERLAPPED                 = 0 /* 0 */,
    WS_POPUP                      = -2147483648 /* 80000000 */,
    WS_CHILD                      = 1073741824 /* 40000000 */,
    WS_MINIMIZE                   = 536870912 /* 20000000 */,
    WS_VISIBLE                    = 268435456 /* 10000000 */,
    WS_DISABLED                   = 134217728 /* 8000000 */,
    WS_CLIPSIBLINGS               = 67108864 /* 4000000 */,
    WS_CLIPCHILDREN               = 33554432 /* 2000000 */,
    WS_MAXIMIZE                   = 16777216 /* 1000000 */,
    WS_CAPTION                    = 12582912 /* C00000 */,
    WS_BORDER                     = 8388608 /* 800000 */,
    WS_DLGFRAME                   = 4194304 /* 400000 */,
    WS_VSCROLL                    = 2097152 /* 200000 */,
    WS_HSCROLL                    = 1048576 /* 100000 */,
    WS_SYSMENU                    = 524288 /* 80000 */,
    WS_THICKFRAME                 = 262144 /* 40000 */,
    WS_GROUP                      = 131072 /* 20000 */,
    WS_TABSTOP                    = 65536 /* 10000 */,
    WS_MINIMIZEBOX                = 131072 /* 20000 */,
    WS_MAXIMIZEBOX                = 65536 /* 10000 */,
    WS_TILED                      = 0 /* 0 */,
    WS_ICONIC                     = 536870912 /* 20000000 */,
    WS_SIZEBOX                    = 262144 /* 40000 */,
    WS_OVERLAPPEDWINDOW           = 13565952 /* CF0000 */,
    WS_TILEDWINDOW                = 13565952 /* CF0000 */,
    WS_POPUPWINDOW                = -2138570752 /* 80880000 */,
    WS_CHILDWINDOW                = 1073741824 /* 40000000 */,
    WS_EX_DLGMODALFRAME           = 1 /* 1 */,
    WS_EX_NOPARENTNOTIFY          = 4 /* 4 */,
    WS_EX_TOPMOST                 = 8 /* 8 */,
    WS_EX_ACCEPTFILES             = 16 /* 10 */,
    WS_EX_TRANSPARENT             = 32 /* 20 */,
    WS_EX_MDICHILD                = 64 /* 40 */,
    WS_EX_TOOLWINDOW              = 128 /* 80 */,
    WS_EX_WINDOWEDGE              = 256 /* 100 */,
    WS_EX_CLIENTEDGE              = 512 /* 200 */,
    WS_EX_CONTEXTHELP             = 1024 /* 400 */,
    WS_EX_RIGHT                   = 4096 /* 1000 */,
    WS_EX_LEFT                    = 0 /* 0 */,
    WS_EX_RTLREADING              = 8192 /* 2000 */,
    WS_EX_LTRREADING              = 0 /* 0 */,
    WS_EX_LEFTSCROLLBAR           = 16384 /* 4000 */,
    WS_EX_RIGHTSCROLLBAR          = 0 /* 0 */,
    WS_EX_CONTROLPARENT           = 65536 /* 10000 */,
    WS_EX_STATICEDGE              = 131072 /* 20000 */,
    WS_EX_APPWINDOW               = 262144 /* 40000 */,
    WS_EX_OVERLAPPEDWINDOW        = 768 /* 300 */,
    WS_EX_PALETTEWINDOW           = 392 /* 188 */,
    WS_EX_LAYERED                 = 524288 /* 80000 */,
    WS_EX_NOINHERITLAYOUT         = 1048576 /* 100000 */,
    WS_EX_LAYOUTRTL               = 4194304 /* 400000 */,
    WS_EX_COMPOSITED              = 33554432 /* 2000000 */,
    WS_EX_NOACTIVATE              = 134217728 /* 8000000 */,

    LB_CTLCODE                    = 0 /* 0 */,
    LB_OKAY                       = 0 /* 0 */,
    LB_ERR                        = -1 /* FFFFFFFF */,
    LB_ERRSPACE                   = -2 /* FFFFFFFE */,

    LBN_ERRSPACE                  = -2 /* FFFFFFFE */,
    LBN_SELCHANGE                 = 1 /* 1 */,
    LBN_DBLCLK                    = 2 /* 2 */,
    LBN_SELCANCEL                 = 3 /* 3 */,
    LBN_SETFOCUS                  = 4 /* 4 */,
    LBN_KILLFOCUS                 = 5 /* 5 */,

    LB_ADDSTRING                  = 384 /* 180 */,
    LB_INSERTSTRING               = 385 /* 181 */,
    LB_DELETESTRING               = 386 /* 182 */,
    LB_SELITEMRANGEEX             = 387 /* 183 */,
    LB_RESETCONTENT               = 388 /* 184 */,
    LB_SETSEL                     = 389 /* 185 */,
    LB_SETCURSEL                  = 390 /* 186 */,
    LB_GETSEL                     = 391 /* 187 */,
    LB_GETCURSEL                  = 392 /* 188 */,
    LB_GETTEXT                    = 393 /* 189 */,
    LB_GETTEXTLEN                 = 394 /* 18A */,
    LB_GETCOUNT                   = 395 /* 18B */,
    LB_SELECTSTRING               = 396 /* 18C */,
    LB_DIR                        = 397 /* 18D */,
    LB_GETTOPINDEX                = 398 /* 18E */,
    LB_FINDSTRING                 = 399 /* 18F */,
    LB_GETSELCOUNT                = 400 /* 190 */,
    LB_GETSELITEMS                = 401 /* 191 */,
    LB_SETTABSTOPS                = 402 /* 192 */,
    LB_GETHORIZONTALEXTENT        = 403 /* 193 */,
    LB_SETHORIZONTALEXTENT        = 404 /* 194 */,
    LB_SETCOLUMNWIDTH             = 405 /* 195 */,
    LB_ADDFILE                    = 406 /* 196 */,
    LB_SETTOPINDEX                = 407 /* 197 */,
    LB_GETITEMRECT                = 408 /* 198 */,
    LB_GETITEMDATA                = 409 /* 199 */,
    LB_SETITEMDATA                = 410 /* 19A */,
    LB_SELITEMRANGE               = 411 /* 19B */,
    LB_SETANCHORINDEX             = 412 /* 19C */,
    LB_GETANCHORINDEX             = 413 /* 19D */,
    LB_SETCARETINDEX              = 414 /* 19E */,
    LB_GETCARETINDEX              = 415 /* 19F */,
    LB_SETITEMHEIGHT              = 416 /* 1A0 */,
    LB_GETITEMHEIGHT              = 417 /* 1A1 */,
    LB_FINDSTRINGEXACT            = 418 /* 1A2 */,
    LB_SETLOCALE                  = 421 /* 1A5 */,
    LB_GETLOCALE                  = 422 /* 1A6 */,
    LB_SETCOUNT                   = 423 /* 1A7 */,
    LB_INITSTORAGE                = 424 /* 1A8 */,
    LB_ITEMFROMPOINT              = 425 /* 1A9 */,
    LB_MULTIPLEADDSTRING          = 433 /* 1B1 */,
    LB_GETLISTBOXINFO             = 434 /* 1B2 */,
    LB_MSGMAX                     = 435 /* 1B3 */,
    LB_MSGMAX                     = 433 /* 1B1 */,
    LB_MSGMAX                     = 432 /* 1B0 */,
    LB_MSGMAX                     = 424 /* 1A8 */,

    LBS_NOTIFY                    = 1 /* 1 */,
    LBS_SORT                      = 2 /* 2 */,
    LBS_NOREDRAW                  = 4 /* 4 */,
    LBS_MULTIPLESEL               = 8 /* 8 */,
    LBS_OWNERDRAWFIXED            = 16 /* 10 */,
    LBS_OWNERDRAWVARIABLE         = 32 /* 20 */,
    LBS_HASSTRINGS                = 64 /* 40 */,
    LBS_USETABSTOPS               = 128 /* 80 */,
    LBS_NOINTEGRALHEIGHT          = 256 /* 100 */,
    LBS_MULTICOLUMN               = 512 /* 200 */,
    LBS_WANTKEYBOARDINPUT         = 1024 /* 400 */,
    LBS_EXTENDEDSEL               = 2048 /* 800 */,
    LBS_DISABLENOSCROLL           = 4096 /* 1000 */,
    LBS_NODATA                    = 8192 /* 2000 */,
    LBS_NOSEL                     = 16384 /* 4000 */,
    LBS_COMBOBOX                  = 32768 /* 8000 */,
    LBS_STANDARD                  = 10485763 /* A00003 */,

    CB_OKAY                       = 0 /* 0 */,
    CB_ERR                        = -1 /* FFFFFFFF */,
    CB_ERRSPACE                   = -2 /* FFFFFFFE */,

    CBN_ERRSPACE                  = -1 /* FFFFFFFF */,
    CBN_SELCHANGE                 = 1 /* 1 */,
    CBN_DBLCLK                    = 2 /* 2 */,
    CBN_SETFOCUS                  = 3 /* 3 */,
    CBN_KILLFOCUS                 = 4 /* 4 */,
    CBN_EDITCHANGE                = 5 /* 5 */,
    CBN_EDITUPDATE                = 6 /* 6 */,
    CBN_DROPDOWN                  = 7 /* 7 */,
    CBN_CLOSEUP                   = 8 /* 8 */,
    CBN_SELENDOK                  = 9 /* 9 */,
    CBN_SELENDCANCEL              = 10 /* A */,

    CBS_SIMPLE                    = 1 /* 1 */,
    CBS_DROPDOWN                  = 2 /* 2 */,
    CBS_DROPDOWNLIST              = 3 /* 3 */,
    CBS_OWNERDRAWFIXED            = 16 /* 10 */,
    CBS_OWNERDRAWVARIABLE         = 32 /* 20 */,
    CBS_AUTOHSCROLL               = 64 /* 40 */,
    CBS_OEMCONVERT                = 128 /* 80 */,
    CBS_SORT                      = 256 /* 100 */,
    CBS_HASSTRINGS                = 512 /* 200 */,
    CBS_NOINTEGRALHEIGHT          = 1024 /* 400 */,
    CBS_DISABLENOSCROLL           = 2048 /* 800 */,
    CBS_UPPERCASE                 = 8192 /* 2000 */,
    CBS_LOWERCASE                 = 16384 /* 4000 */,

    CB_GETEDITSEL                 = 320 /* 140 */,
    CB_LIMITTEXT                  = 321 /* 141 */,
    CB_SETEDITSEL                 = 322 /* 142 */,
    CB_ADDSTRING                  = 323 /* 143 */,
    CB_DELETESTRING               = 324 /* 144 */,
    CB_DIR                        = 325 /* 145 */,
    CB_GETCOUNT                   = 326 /* 146 */,
    CB_GETCURSEL                  = 327 /* 147 */,
    CB_GETLBTEXT                  = 328 /* 148 */,
    CB_GETLBTEXTLEN               = 329 /* 149 */,
    CB_INSERTSTRING               = 330 /* 14A */,
    CB_RESETCONTENT               = 331 /* 14B */,
    CB_FINDSTRING                 = 332 /* 14C */,
    CB_SELECTSTRING               = 333 /* 14D */,
    CB_SETCURSEL                  = 334 /* 14E */,
    CB_SHOWDROPDOWN               = 335 /* 14F */,
    CB_GETITEMDATA                = 336 /* 150 */,
    CB_SETITEMDATA                = 337 /* 151 */,
    CB_GETDROPPEDCONTROLRECT      = 338 /* 152 */,
    CB_SETITEMHEIGHT              = 339 /* 153 */,
    CB_GETITEMHEIGHT              = 340 /* 154 */,
    CB_SETEXTENDEDUI              = 341 /* 155 */,
    CB_GETEXTENDEDUI              = 342 /* 156 */,
    CB_GETDROPPEDSTATE            = 343 /* 157 */,
    CB_FINDSTRINGEXACT            = 344 /* 158 */,
    CB_SETLOCALE                  = 345 /* 159 */,
    CB_GETLOCALE                  = 346 /* 15A */,
    CB_GETTOPINDEX                = 347 /* 15B */,
    CB_SETTOPINDEX                = 348 /* 15C */,
    CB_GETHORIZONTALEXTENT        = 349 /* 15D */,
    CB_SETHORIZONTALEXTENT        = 350 /* 15E */,
    CB_GETDROPPEDWIDTH            = 351 /* 15F */,
    CB_SETDROPPEDWIDTH            = 352 /* 160 */,
    CB_INITSTORAGE                = 353 /* 161 */,
    CB_MULTIPLEADDSTRING          = 355 /* 163 */,
    CB_GETCOMBOBOXINFO            = 356 /* 164 */,
    CB_MSGMAX                     = 357 /* 165 */,

    BN_CLICKED                    = 0 /* 0 */,
    BN_PAINT                      = 1 /* 1 */,
    BN_HILITE                     = 2 /* 2 */,
    BN_UNHILITE                   = 3 /* 3 */,
    BN_DISABLE                    = 4 /* 4 */,
    BN_DOUBLECLICKED              = 5 /* 5 */,
    BN_PUSHED                     = 2 /* 2 */,
    BN_UNPUSHED                   = 3 /* 3 */,
    BN_DBLCLK                     = 5 /* 5 */,
    BN_SETFOCUS                   = 6 /* 6 */,
    BN_KILLFOCUS                  = 7 /* 7 */,

    BM_GETCHECK                   = 240 /* F0 */,
    BM_SETCHECK                   = 241 /* F1 */,
    BM_GETSTATE                   = 242 /* F2 */,
    BM_SETSTATE                   = 243 /* F3 */,
    BM_SETSTYLE                   = 244 /* F4 */,
    BM_CLICK                      = 245 /* F5 */,
    BM_GETIMAGE                   = 246 /* F6 */,
    BM_SETIMAGE                   = 247 /* F7 */,

    BST_UNCHECKED                 = 0 /* 0 */,
    BST_CHECKED                   = 1 /* 1 */,
    BST_INDETERMINATE             = 2 /* 2 */,
    BST_PUSHED                    = 4 /* 4 */,
    BST_FOCUS                     = 8 /* 8 */,

    SC_SIZE                       = 61440 /* F000 */,
    SC_MOVE                       = 61456 /* F010 */,
    SC_MINIMIZE                   = 61472 /* F020 */,
    SC_MAXIMIZE                   = 61488 /* F030 */,
    SC_NEXTWINDOW                 = 61504 /* F040 */,
    SC_PREVWINDOW                 = 61520 /* F050 */,
    SC_CLOSE                      = 61536 /* F060 */,
    SC_VSCROLL                    = 61552 /* F070 */,
    SC_HSCROLL                    = 61568 /* F080 */,
    SC_MOUSEMENU                  = 61584 /* F090 */,
    SC_KEYMENU                    = 61696 /* F100 */,
    SC_ARRANGE                    = 61712 /* F110 */,
    SC_RESTORE                    = 61728 /* F120 */,
    SC_TASKLIST                   = 61744 /* F130 */,
    SC_SCREENSAVE                 = 61760 /* F140 */,
    SC_HOTKEY                     = 61776 /* F150 */,
    SC_DEFAULT                    = 61792 /* F160 */,
    SC_MONITORPOWER               = 61808 /* F170 */,
    SC_CONTEXTHELP                = 61824 /* F180 */,
    SC_SEPARATOR                  = 61455 /* F00F */,
    SC_ICON                       = 61472 /* F020 */,
    SC_ZOOM                       = 61488 /* F030 */,

    HOTKEYF_SHIFT                 = 1 /* 1 */,
    HOTKEYF_CONTROL               = 2 /* 2 */,
    HOTKEYF_ALT                   = 4 /* 4 */,
    HOTKEYF_EXT                   = 8 /* 8 */,

    SIZE_RESTORED                 = 0 /* 0 */,
    SIZE_MINIMIZED                = 1 /* 1 */,
    SIZE_MAXIMIZED                = 2 /* 2 */,
    SIZE_MAXSHOW                  = 3 /* 3 */,
    SIZE_MAXHIDE                  = 4 /* 4 */,

    WMSZ_LEFT                     = 1 /* 1 */,
    WMSZ_RIGHT                    = 2 /* 2 */,
    WMSZ_TOP                      = 3 /* 3 */,
    WMSZ_TOPLEFT                  = 4 /* 4 */,
    WMSZ_TOPRIGHT                 = 5 /* 5 */,
    WMSZ_BOTTOM                   = 6 /* 6 */,
    WMSZ_BOTTOMLEFT               = 7 /* 7 */,
    WMSZ_BOTTOMRIGHT              = 8 /* 8 */,

    SWP_NOSIZE                    = 1 /* 1 */,
    SWP_NOMOVE                    = 2 /* 2 */,
    SWP_NOZORDER                  = 4 /* 4 */,
    SWP_NOREDRAW                  = 8 /* 8 */,
    SWP_NOACTIVATE                = 16 /* 10 */,
    SWP_FRAMECHANGED              = 32 /* 20 */,
    SWP_SHOWWINDOW                = 64 /* 40 */,
    SWP_HIDEWINDOW                = 128 /* 80 */,
    SWP_NOCOPYBITS                = 256 /* 100 */,
    SWP_NOOWNERZORDER             = 512 /* 200 */,
    SWP_NOSENDCHANGING            = 1024 /* 400 */,
    SWP_DRAWFRAME                 = 32 /* 20 */,
    SWP_NOREPOSITION              = 512 /* 200 */,
    SWP_DEFERERASE                = 8192 /* 2000 */,
    SWP_ASYNCWINDOWPOS            = 16384 /* 4000 */,

    KEYEVENTF_EXTENDEDKEY         = 1 /* 1 */,
    KEYEVENTF_KEYUP               = 2 /* 2 */,
    KEYEVENTF_UNICODE             = 4 /* 4 */,
    KEYEVENTF_SCANCODE            = 8 /* 8 */,

    MOUSEEVENTF_MOVE              = 1 /* 1 */,
    MOUSEEVENTF_LEFTDOWN          = 2 /* 2 */,
    MOUSEEVENTF_LEFTUP            = 4 /* 4 */,
    MOUSEEVENTF_RIGHTDOWN         = 8 /* 8 */,
    MOUSEEVENTF_RIGHTUP           = 16 /* 10 */,
    MOUSEEVENTF_MIDDLEDOWN        = 32 /* 20 */,
    MOUSEEVENTF_MIDDLEUP          = 64 /* 40 */,
    MOUSEEVENTF_XDOWN             = 128 /* 80 */,
    MOUSEEVENTF_XUP               = 256 /* 100 */,
    MOUSEEVENTF_WHEEL             = 2048 /* 800 */,
    MOUSEEVENTF_VIRTUALDESK       = 16384 /* 4000 */,
    MOUSEEVENTF_ABSOLUTE          = 32768 /* 8000 */,

    INPUT_MOUSE                   = 0 /* 0 */,
    INPUT_KEYBOARD                = 1 /* 1 */,
    INPUT_HARDWARE                = 2 /* 2 */,

    SW_HIDE                       = 0 /* 0 */,
    SW_SHOWNORMAL                 = 1 /* 1 */,
    SW_NORMAL                     = 1 /* 1 */,
    SW_SHOWMINIMIZED              = 2 /* 2 */,
    SW_SHOWMAXIMIZED              = 3 /* 3 */,
    SW_MAXIMIZE                   = 3 /* 3 */,
    SW_SHOWNOACTIVATE             = 4 /* 4 */,
    SW_SHOW                       = 5 /* 5 */,
    SW_MINIMIZE                   = 6 /* 6 */,
    SW_SHOWMINNOACTIVE            = 7 /* 7 */,
    SW_SHOWNA                     = 8 /* 8 */,
    SW_RESTORE                    = 9 /* 9 */,
    SW_SHOWDEFAULT                = 10 /* A */,
    SW_FORCEMINIMIZE              = 11 /* B */,
    SW_MAX                        = 11 /* B */,

    HIDE_WINDOW                   = 0 /* 0 */,

    SHOW_OPENWINDOW               = 1 /* 1 */,
    SHOW_ICONWINDOW               = 2 /* 2 */,
    SHOW_FULLSCREEN               = 3 /* 3 */,
    SHOW_OPENNOACTIVATE           = 4 /* 4 */,

    SW_PARENTCLOSING              = 1 /* 1 */,
    SW_OTHERZOOM                  = 2 /* 2 */,
    SW_PARENTOPENING              = 3 /* 3 */,
    SW_OTHERUNZOOM                = 4 /* 4 */,

    NM_FIRST                      = 0 /* 0 */,
    NM_LAST                       = -99 /* FFFFFF9D */,

    LVN_FIRST                     = -100 /* FFFFFF9C */,
    LVN_LAST                      = -199 /* FFFFFF39 */,

    HDN_FIRST                     = -300 /* FFFFFED4 */,
    HDN_LAST                      = -399 /* FFFFFE71 */,

    TVN_FIRST                     = -400 /* FFFFFE70 */,
    TVN_LAST                      = -499 /* FFFFFE0D */,

    TTN_FIRST                     = -520 /* FFFFFDF8 */,
    TTN_LAST                      = -549 /* FFFFFDDB */,

    TCN_FIRST                     = -550 /* FFFFFDDA */,
    TCN_LAST                      = -580 /* FFFFFDBC */,

    CDN_FIRST                     = -601 /* FFFFFDA7 */,
    CDN_LAST                      = -699 /* FFFFFD45 */,

    TBN_FIRST                     = -700 /* FFFFFD44 */,
    TBN_LAST                      = -720 /* FFFFFD30 */,

    UDN_FIRST                     = -721 /* FFFFFD2F */,
    UDN_LAST                      = -740 /* FFFFFD1C */,

    MCN_FIRST                     = -750 /* FFFFFD12 */,
    MCN_LAST                      = -759 /* FFFFFD09 */,

    DTN_FIRST                     = -760 /* FFFFFD08 */,
    DTN_LAST                      = -799 /* FFFFFCE1 */,

    CBEN_FIRST                    = -800 /* FFFFFCE0 */,
    CBEN_LAST                     = -830 /* FFFFFCC2 */,

    RBN_FIRST                     = -831 /* FFFFFCC1 */,
    RBN_LAST                      = -859 /* FFFFFCA5 */,

    IPN_FIRST                     = -860 /* FFFFFCA4 */,
    IPN_LAST                      = -879 /* FFFFFC91 */,

    SBN_FIRST                     = -880 /* FFFFFC90 */,
    SBN_LAST                      = -899 /* FFFFFC7D */,

    PGN_FIRST                     = -900 /* FFFFFC7C */,
    PGN_LAST                      = -950 /* FFFFFC4A */,

    WMN_FIRST                     = -1000 /* FFFFFC18 */,
    WMN_LAST                      = -1200 /* FFFFFB50 */,

    BCN_FIRST                     = -1250 /* FFFFFB1E */,
    BCN_LAST                      = -1350 /* FFFFFABA */,

    MSGF_COMMCTRL_BEGINDRAG       = 16896 /* 4200 */,
    MSGF_COMMCTRL_SIZEHEADER      = 16897 /* 4201 */,
    MSGF_COMMCTRL_DRAGSELECT      = 16898 /* 4202 */,
    MSGF_COMMCTRL_TOOLBARCUST     = 16899 /* 4203 */,

    UD_MAXVAL                     = 32767 /* 7FFF */,
    UD_MINVAL                     = -32767 /* FFFF8001 */,

    UDS_WRAP                      = 1 /* 1 */,
    UDS_SETBUDDYINT               = 2 /* 2 */,
    UDS_ALIGNRIGHT                = 4 /* 4 */,
    UDS_ALIGNLEFT                 = 8 /* 8 */,
    UDS_AUTOBUDDY                 = 16 /* 10 */,
    UDS_ARROWKEYS                 = 32 /* 20 */,
    UDS_HORZ                      = 64 /* 40 */,
    UDS_NOTHOUSANDS               = 128 /* 80 */,
    UDS_HOTTRACK                  = 256 /* 100 */,

    UDM_SETRANGE                  = 1125 /* 465 */,
    UDM_GETRANGE                  = 1126 /* 466 */,
    UDM_SETPOS                    = 1127 /* 467 */,
    UDM_GETPOS                    = 1128 /* 468 */,
    UDM_SETBUDDY                  = 1129 /* 469 */,
    UDM_GETBUDDY                  = 1130 /* 46A */,
    UDM_SETACCEL                  = 1131 /* 46B */,
    UDM_GETACCEL                  = 1132 /* 46C */,
    UDM_SETBASE                   = 1133 /* 46D */,
    UDM_GETBASE                   = 1134 /* 46E */,
    UDM_SETRANGE32                = 1135 /* 46F */,
    UDM_GETRANGE32                = 1136 /* 470 */,
    UDM_SETPOS32                  = 1137 /* 471 */,
    UDM_GETPOS32                  = 1138 /* 472 */,

    UDN_DELTAPOS                  = -722 /* FFFFFD2E */,
};

enum {
    SM_CXSCREEN                   = 0 /* 0 */,
    SM_CYSCREEN                   = 1 /* 1 */,
    SM_CXVSCROLL                  = 2 /* 2 */,
    SM_CYHSCROLL                  = 3 /* 3 */,
    SM_CYCAPTION                  = 4 /* 4 */,
    SM_CXBORDER                   = 5 /* 5 */,
    SM_CYBORDER                   = 6 /* 6 */,
    SM_CXDLGFRAME                 = 7 /* 7 */,
    SM_CYDLGFRAME                 = 8 /* 8 */,
    SM_CYVTHUMB                   = 9 /* 9 */,
    SM_CXHTHUMB                   = 10 /* A */,
    SM_CXICON                     = 11 /* B */,
    SM_CYICON                     = 12 /* C */,
    SM_CXCURSOR                   = 13 /* D */,
    SM_CYCURSOR                   = 14 /* E */,
    SM_CYMENU                     = 15 /* F */,
    SM_CXFULLSCREEN               = 16 /* 10 */,
    SM_CYFULLSCREEN               = 17 /* 11 */,
    SM_CYKANJIWINDOW              = 18 /* 12 */,
    SM_MOUSEPRESENT               = 19 /* 13 */,
    SM_CYVSCROLL                  = 20 /* 14 */,
    SM_CXHSCROLL                  = 21 /* 15 */,
    SM_DEBUG                      = 22 /* 16 */,
    SM_SWAPBUTTON                 = 23 /* 17 */,
    SM_RESERVED1                  = 24 /* 18 */,
    SM_RESERVED2                  = 25 /* 19 */,
    SM_RESERVED3                  = 26 /* 1A */,
    SM_RESERVED4                  = 27 /* 1B */,
    SM_CXMIN                      = 28 /* 1C */,
    SM_CYMIN                      = 29 /* 1D */,
    SM_CXSIZE                     = 30 /* 1E */,
    SM_CYSIZE                     = 31 /* 1F */,
    SM_CXFRAME                    = 32 /* 20 */,
    SM_CYFRAME                    = 33 /* 21 */,
    SM_CXMINTRACK                 = 34 /* 22 */,
    SM_CYMINTRACK                 = 35 /* 23 */,
    SM_CXDOUBLECLK                = 36 /* 24 */,
    SM_CYDOUBLECLK                = 37 /* 25 */,
    SM_CXICONSPACING              = 38 /* 26 */,
    SM_CYICONSPACING              = 39 /* 27 */,
    SM_MENUDROPALIGNMENT          = 40 /* 28 */,
    SM_PENWINDOWS                 = 41 /* 29 */,
    SM_DBCSENABLED                = 42 /* 2A */,
    SM_CMOUSEBUTTONS              = 43 /* 2B */,
    SM_CXFIXEDFRAME               = 7 /* 7 */,
    SM_CYFIXEDFRAME               = 8 /* 8 */,
    SM_CXSIZEFRAME                = 32 /* 20 */,
    SM_CYSIZEFRAME                = 33 /* 21 */,
    SM_SECURE                     = 44 /* 2C */,
    SM_CXEDGE                     = 45 /* 2D */,
    SM_CYEDGE                     = 46 /* 2E */,
    SM_CXMINSPACING               = 47 /* 2F */,
    SM_CYMINSPACING               = 48 /* 30 */,
    SM_CXSMICON                   = 49 /* 31 */,
    SM_CYSMICON                   = 50 /* 32 */,
    SM_CYSMCAPTION                = 51 /* 33 */,
    SM_CXSMSIZE                   = 52 /* 34 */,
    SM_CYSMSIZE                   = 53 /* 35 */,
    SM_CXMENUSIZE                 = 54 /* 36 */,
    SM_CYMENUSIZE                 = 55 /* 37 */,
    SM_ARRANGE                    = 56 /* 38 */,
    SM_CXMINIMIZED                = 57 /* 39 */,
    SM_CYMINIMIZED                = 58 /* 3A */,
    SM_CXMAXTRACK                 = 59 /* 3B */,
    SM_CYMAXTRACK                 = 60 /* 3C */,
    SM_CXMAXIMIZED                = 61 /* 3D */,
    SM_CYMAXIMIZED                = 62 /* 3E */,
    SM_NETWORK                    = 63 /* 3F */,
    SM_CLEANBOOT                  = 67 /* 43 */,
    SM_CXDRAG                     = 68 /* 44 */,
    SM_CYDRAG                     = 69 /* 45 */,
    SM_SHOWSOUNDS                 = 70 /* 46 */,
    SM_CXMENUCHECK                = 71 /* 47 */,
    SM_CYMENUCHECK                = 72 /* 48 */,
    SM_SLOWMACHINE                = 73 /* 49 */,
    SM_MIDEASTENABLED             = 74 /* 4A */,
    SM_MOUSEWHEELPRESENT          = 75 /* 4B */,
    SM_XVIRTUALSCREEN             = 76 /* 4C */,
    SM_YVIRTUALSCREEN             = 77 /* 4D */,
    SM_CXVIRTUALSCREEN            = 78 /* 4E */,
    SM_CYVIRTUALSCREEN            = 79 /* 4F */,
    SM_CMONITORS                  = 80 /* 50 */,
    SM_SAMEDISPLAYFORMAT          = 81 /* 51 */,
    SM_IMMENABLED                 = 82 /* 52 */,
    SM_CXFOCUSBORDER              = 83 /* 53 */,
    SM_CYFOCUSBORDER              = 84 /* 54 */,
    SM_TABLETPC                   = 86 /* 56 */,
    SM_MEDIACENTER                = 87 /* 57 */,
    SM_CMETRICS                   = 76 /* 4C */,
    SM_CMETRICS                   = 83 /* 53 */,
    SM_CMETRICS                   = 88 /* 58 */,
    SM_REMOTESESSION              = 4096 /* 1000 */,
    SM_SHUTTINGDOWN               = 8192 /* 2000 */,
    SM_REMOTECONTROL              = 8193 /* 2001 */,
};

enum {
    MOD_ALT                       = 1 /* 1 */,
    MOD_CONTROL                   = 2 /* 2 */,
    MOD_SHIFT                     = 4 /* 4 */,
    MOD_WIN                       = 8 /* 8 */,

    VK_LBUTTON                    = 1 /* 1 */,
    VK_RBUTTON                    = 2 /* 2 */,
    VK_CANCEL                     = 3 /* 3 */,
    VK_MBUTTON                    = 4 /* 4 */,
    VK_XBUTTON1                   = 5 /* 5 */,
    VK_XBUTTON2                   = 6 /* 6 */,
    VK_BACK                       = 8 /* 8 */,
    VK_TAB                        = 9 /* 9 */,
    VK_CLEAR                      = 12 /* C */,
    VK_RETURN                     = 13 /* D */,
    VK_SHIFT                      = 16 /* 10 */,
    VK_CONTROL                    = 17 /* 11 */,
    VK_MENU                       = 18 /* 12 */,
    VK_PAUSE                      = 19 /* 13 */,
    VK_CAPITAL                    = 20 /* 14 */,
    VK_KANA                       = 21 /* 15 */,
    VK_HANGEUL                    = 21 /* 15 */,
    VK_HANGUL                     = 21 /* 15 */,
    VK_JUNJA                      = 23 /* 17 */,
    VK_FINAL                      = 24 /* 18 */,
    VK_HANJA                      = 25 /* 19 */,
    VK_KANJI                      = 25 /* 19 */,
    VK_ESCAPE                     = 27 /* 1B */,
    VK_CONVERT                    = 28 /* 1C */,
    VK_NONCONVERT                 = 29 /* 1D */,
    VK_ACCEPT                     = 30 /* 1E */,
    VK_MODECHANGE                 = 31 /* 1F */,
    VK_SPACE                      = 32 /* 20 */,
    VK_PRIOR                      = 33 /* 21 */,
    VK_NEXT                       = 34 /* 22 */,
    VK_END                        = 35 /* 23 */,
    VK_HOME                       = 36 /* 24 */,
    VK_LEFT                       = 37 /* 25 */,
    VK_UP                         = 38 /* 26 */,
    VK_RIGHT                      = 39 /* 27 */,
    VK_DOWN                       = 40 /* 28 */,
    VK_SELECT                     = 41 /* 29 */,
    VK_PRINT                      = 42 /* 2A */,
    VK_EXECUTE                    = 43 /* 2B */,
    VK_SNAPSHOT                   = 44 /* 2C */,
    VK_INSERT                     = 45 /* 2D */,
    VK_DELETE                     = 46 /* 2E */,
    VK_HELP                       = 47 /* 2F */,
    VK_LWIN                       = 91 /* 5B */,
    VK_RWIN                       = 92 /* 5C */,
    VK_APPS                       = 93 /* 5D */,
    VK_SLEEP                      = 95 /* 5F */,
    VK_NUMPAD0                    = 96 /* 60 */,
    VK_NUMPAD1                    = 97 /* 61 */,
    VK_NUMPAD2                    = 98 /* 62 */,
    VK_NUMPAD3                    = 99 /* 63 */,
    VK_NUMPAD4                    = 100 /* 64 */,
    VK_NUMPAD5                    = 101 /* 65 */,
    VK_NUMPAD6                    = 102 /* 66 */,
    VK_NUMPAD7                    = 103 /* 67 */,
    VK_NUMPAD8                    = 104 /* 68 */,
    VK_NUMPAD9                    = 105 /* 69 */,
    VK_MULTIPLY                   = 106 /* 6A */,
    VK_ADD                        = 107 /* 6B */,
    VK_SEPARATOR                  = 108 /* 6C */,
    VK_SUBTRACT                   = 109 /* 6D */,
    VK_DECIMAL                    = 110 /* 6E */,
    VK_DIVIDE                     = 111 /* 6F */,
    VK_F1                         = 112 /* 70 */,
    VK_F2                         = 113 /* 71 */,
    VK_F3                         = 114 /* 72 */,
    VK_F4                         = 115 /* 73 */,
    VK_F5                         = 116 /* 74 */,
    VK_F6                         = 117 /* 75 */,
    VK_F7                         = 118 /* 76 */,
    VK_F8                         = 119 /* 77 */,
    VK_F9                         = 120 /* 78 */,
    VK_F10                        = 121 /* 79 */,
    VK_F11                        = 122 /* 7A */,
    VK_F12                        = 123 /* 7B */,
    VK_F13                        = 124 /* 7C */,
    VK_F14                        = 125 /* 7D */,
    VK_F15                        = 126 /* 7E */,
    VK_F16                        = 127 /* 7F */,
    VK_F17                        = 128 /* 80 */,
    VK_F18                        = 129 /* 81 */,
    VK_F19                        = 130 /* 82 */,
    VK_F20                        = 131 /* 83 */,
    VK_F21                        = 132 /* 84 */,
    VK_F22                        = 133 /* 85 */,
    VK_F23                        = 134 /* 86 */,
    VK_F24                        = 135 /* 87 */,
    VK_NUMLOCK                    = 144 /* 90 */,
    VK_SCROLL                     = 145 /* 91 */,
    VK_OEM_NEC_EQUAL              = 146 /* 92 */,
    VK_OEM_FJ_JISHO               = 146 /* 92 */,
    VK_OEM_FJ_MASSHOU             = 147 /* 93 */,
    VK_OEM_FJ_TOUROKU             = 148 /* 94 */,
    VK_OEM_FJ_LOYA                = 149 /* 95 */,
    VK_OEM_FJ_ROYA                = 150 /* 96 */,
    VK_LSHIFT                     = 160 /* A0 */,
    VK_RSHIFT                     = 161 /* A1 */,
    VK_LCONTROL                   = 162 /* A2 */,
    VK_RCONTROL                   = 163 /* A3 */,
    VK_LMENU                      = 164 /* A4 */,
    VK_RMENU                      = 165 /* A5 */,
    VK_BROWSER_BACK               = 166 /* A6 */,
    VK_BROWSER_FORWARD            = 167 /* A7 */,
    VK_BROWSER_REFRESH            = 168 /* A8 */,
    VK_BROWSER_STOP               = 169 /* A9 */,
    VK_BROWSER_SEARCH             = 170 /* AA */,
    VK_BROWSER_FAVORITES          = 171 /* AB */,
    VK_BROWSER_HOME               = 172 /* AC */,
    VK_VOLUME_MUTE                = 173 /* AD */,
    VK_VOLUME_DOWN                = 174 /* AE */,
    VK_VOLUME_UP                  = 175 /* AF */,
    VK_MEDIA_NEXT_TRACK           = 176 /* B0 */,
    VK_MEDIA_PREV_TRACK           = 177 /* B1 */,
    VK_MEDIA_STOP                 = 178 /* B2 */,
    VK_MEDIA_PLAY_PAUSE           = 179 /* B3 */,
    VK_LAUNCH_MAIL                = 180 /* B4 */,
    VK_LAUNCH_MEDIA_SELECT        = 181 /* B5 */,
    VK_LAUNCH_APP1                = 182 /* B6 */,
    VK_LAUNCH_APP2                = 183 /* B7 */,
    VK_OEM_1                      = 186 /* BA */,
    VK_OEM_PLUS                   = 187 /* BB */,
    VK_OEM_COMMA                  = 188 /* BC */,
    VK_OEM_MINUS                  = 189 /* BD */,
    VK_OEM_PERIOD                 = 190 /* BE */,
    VK_OEM_2                      = 191 /* BF */,
    VK_OEM_3                      = 192 /* C0 */,
    VK_OEM_4                      = 219 /* DB */,
    VK_OEM_5                      = 220 /* DC */,
    VK_OEM_6                      = 221 /* DD */,
    VK_OEM_7                      = 222 /* DE */,
    VK_OEM_8                      = 223 /* DF */,
    VK_OEM_AX                     = 225 /* E1 */,
    VK_OEM_102                    = 226 /* E2 */,
    VK_ICO_HELP                   = 227 /* E3 */,
    VK_ICO_00                     = 228 /* E4 */,
    VK_PROCESSKEY                 = 229 /* E5 */,
    VK_ICO_CLEAR                  = 230 /* E6 */,
    VK_PACKET                     = 231 /* E7 */,
    VK_OEM_RESET                  = 233 /* E9 */,
    VK_OEM_JUMP                   = 234 /* EA */,
    VK_OEM_PA1                    = 235 /* EB */,
    VK_OEM_PA2                    = 236 /* EC */,
    VK_OEM_PA3                    = 237 /* ED */,
    VK_OEM_WSCTRL                 = 238 /* EE */,
    VK_OEM_CUSEL                  = 239 /* EF */,
    VK_OEM_ATTN                   = 240 /* F0 */,
    VK_OEM_FINISH                 = 241 /* F1 */,
    VK_OEM_COPY                   = 242 /* F2 */,
    VK_OEM_AUTO                   = 243 /* F3 */,
    VK_OEM_ENLW                   = 244 /* F4 */,
    VK_OEM_BACKTAB                = 245 /* F5 */,
    VK_ATTN                       = 246 /* F6 */,
    VK_CRSEL                      = 247 /* F7 */,
    VK_EXSEL                      = 248 /* F8 */,
    VK_EREOF                      = 249 /* F9 */,
    VK_PLAY                       = 250 /* FA */,
    VK_ZOOM                       = 251 /* FB */,
    VK_NONAME                     = 252 /* FC */,
    VK_PA1                        = 253 /* FD */,
    VK_OEM_CLEAR                  = 254 /* FE */,
};

enum {
    DT_TOP                        = 0 /* 0 */,
    DT_LEFT                       = 0 /* 0 */,
    DT_CENTER                     = 1 /* 1 */,
    DT_RIGHT                      = 2 /* 2 */,
    DT_VCENTER                    = 4 /* 4 */,
    DT_BOTTOM                     = 8 /* 8 */,
    DT_WORDBREAK                  = 16 /* 10 */,
    DT_SINGLELINE                 = 32 /* 20 */,
    DT_EXPANDTABS                 = 64 /* 40 */,
    DT_TABSTOP                    = 128 /* 80 */,
    DT_NOCLIP                     = 256 /* 100 */,
    DT_EXTERNALLEADING            = 512 /* 200 */,
    DT_CALCRECT                   = 1024 /* 400 */,
    DT_NOPREFIX                   = 2048 /* 800 */,
    DT_INTERNAL                   = 4096 /* 1000 */,
    DT_EDITCONTROL                = 8192 /* 2000 */,
    DT_PATH_ELLIPSIS              = 16384 /* 4000 */,
    DT_END_ELLIPSIS               = 32768 /* 8000 */,
    DT_MODIFYSTRING               = 65536 /* 10000 */,
    DT_RTLREADING                 = 131072 /* 20000 */,
    DT_WORD_ELLIPSIS              = 262144 /* 40000 */,
    DT_NOFULLWIDTHCHARBREAK       = 524288 /* 80000 */,
    DT_HIDEPREFIX                 = 1048576 /* 100000 */,
    DT_PREFIXONLY                 = 2097152 /* 200000 */,
};

enum {
    DC_ACTIVE                     = 1 /* 1 */,
    DC_SMALLCAP                   = 2 /* 2 */,
    DC_ICON                       = 4 /* 4 */,
    DC_TEXT                       = 8 /* 8 */,
    DC_INBUTTON                   = 16 /* 10 */,
    DC_GRADIENT                   = 32 /* 20 */,
    DC_BUTTONS                    = 4096 /* 1000 */,
};

enum {
    STATUS_WAIT_0                 = 0 /* 0 */,
    STATUS_ABANDONED_WAIT_0       = 128 /* 80 */,
    STATUS_USER_APC               = 192 /* C0 */,
    STATUS_TIMEOUT                = 258 /* 102 */,
    STATUS_PENDING                = 259 /* 103 */,

    WAIT_FAILED                   = -1 /* FFFFFFFF */,
    WAIT_OBJECT_0                 = 0 /* 0 */,
    WAIT_ABANDONED                = 128 /* 80 */,
    WAIT_ABANDONED_0              = 128 /* 80 */,
    WAIT_IO_COMPLETION            = 192 /* C0 */,
    WAIT_TIMEOUT                  = 258 /* 102 */,

    ERROR_FILE_NOT_FOUND          = 2 /* 2 */,
    ERROR_PATH_NOT_FOUND          = 3 /* 3 */,
    ERROR_BAD_FORMAT              = 11 /* B */,

    SE_ERR_FNF                    = 2 /* 2 */,
    SE_ERR_PNF                    = 3 /* 3 */,
    SE_ERR_ACCESSDENIED           = 5 /* 5 */,
    SE_ERR_OOM                    = 8 /* 8 */,
    SE_ERR_DLLNOTFOUND            = 32 /* 20 */,
    SE_ERR_SHARE                  = 26 /* 1A */,
    SE_ERR_ASSOCINCOMPLETE        = 27 /* 1B */,
    SE_ERR_DDETIMEOUT             = 28 /* 1C */,
    SE_ERR_DDEFAIL                = 29 /* 1D */,
    SE_ERR_DDEBUSY                = 30 /* 1E */,
    SE_ERR_NOASSOC                = 31 /* 1F */,
};

enum {
    FILE_ATTRIBUTE_READONLY       = 1 /* 1 */,
    FILE_ATTRIBUTE_HIDDEN         = 2 /* 2 */,
    FILE_ATTRIBUTE_SYSTEM         = 4 /* 4 */,
    FILE_ATTRIBUTE_DIRECTORY      = 16 /* 10 */,
    FILE_ATTRIBUTE_ARCHIVE        = 32 /* 20 */,
    FILE_ATTRIBUTE_DEVICE         = 64 /* 40 */,
    FILE_ATTRIBUTE_NORMAL         = 128 /* 80 */,
    FILE_ATTRIBUTE_TEMPORARY      = 256 /* 100 */,
    FILE_ATTRIBUTE_SPARSE_FILE    = 512 /* 200 */,
    FILE_ATTRIBUTE_REPARSE_POINT  = 1024 /* 400 */,
    FILE_ATTRIBUTE_COMPRESSED     = 2048 /* 800 */,
    FILE_ATTRIBUTE_OFFLINE        = 4096 /* 1000 */,
    FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 8192 /* 2000 */,
    FILE_ATTRIBUTE_ENCRYPTED      = 16384 /* 4000 */,
};

enum {
    MAXLONGLONG                   = -1 /* FFFFFFFF */,

    MINCHAR                       = 128 /* 80 */,

    MAXCHAR                       = 127 /* 7F */,

    MINSHORT                      = 32768 /* 8000 */,

    MAXSHORT                      = 32767 /* 7FFF */,

    MINLONG                       = -2147483648 /* 80000000 */,

    MAXLONG                       = 2147483647 /* 7FFFFFFF */,
    MAXBYTE                       = 255 /* FF */,
    MAXWORD                       = 65535 /* FFFF */,
    MAXDWORD                      = -1 /* FFFFFFFF */,

    THREAD_BASE_PRIORITY_LOWRT    = 15 /* F */,
    THREAD_BASE_PRIORITY_MAX      = 2 /* 2 */,
    THREAD_BASE_PRIORITY_MIN      = -2 /* FFFFFFFE */,
    THREAD_BASE_PRIORITY_IDLE     = -15 /* FFFFFFF1 */,
    THREAD_PRIORITY_LOWEST        = -2 /* FFFFFFFE */,
    THREAD_PRIORITY_BELOW_NORMAL  = -1 /* FFFFFFFF */,
    THREAD_PRIORITY_NORMAL        = 0 /* 0 */,
    THREAD_PRIORITY_HIGHEST       = 2 /* 2 */,
    THREAD_PRIORITY_ABOVE_NORMAL  = 1 /* 1 */,
    THREAD_PRIORITY_ERROR_RETURN  = 2147483647 /* 7FFFFFFF */,
    THREAD_PRIORITY_TIME_CRITICAL = 15 /* F */,
    THREAD_PRIORITY_IDLE          = -15 /* FFFFFFF1 */,

    DELETE                        = 65536 /* 10000 */,

    READ_CONTROL                  = 131072 /* 20000 */,

    WRITE_DAC                     = 262144 /* 40000 */,
    WRITE_OWNER                   = 524288 /* 80000 */,

    SYNCHRONIZE                   = 1048576 /* 100000 */,

    STANDARD_RIGHTS_REQUIRED      = 983040 /* F0000 */,
    STANDARD_RIGHTS_READ          = 131072 /* 20000 */,
    STANDARD_RIGHTS_WRITE         = 131072 /* 20000 */,
    STANDARD_RIGHTS_EXECUTE       = 131072 /* 20000 */,
    STANDARD_RIGHTS_ALL           = 2031616 /* 1F0000 */,

    SPECIFIC_RIGHTS_ALL           = 65535 /* FFFF */,

    ACCESS_SYSTEM_SECURITY        = 16777216 /* 1000000 */,

    MAXIMUM_ALLOWED               = 33554432 /* 2000000 */,

    THREAD_TERMINATE              = 1 /* 1 */,
    THREAD_SUSPEND_RESUME         = 2 /* 2 */,
    THREAD_GET_CONTEXT            = 8 /* 8 */,
    THREAD_SET_CONTEXT            = 16 /* 10 */,
    THREAD_SET_INFORMATION        = 32 /* 20 */,
    THREAD_QUERY_INFORMATION      = 64 /* 40 */,
    THREAD_SET_THREAD_TOKEN       = 128 /* 80 */,
    THREAD_IMPERSONATE            = 256 /* 100 */,
    THREAD_DIRECT_IMPERSONATION   = 512 /* 200 */,
    THREAD_ALL_ACCESS             = 2032639 /* 1F03FF */,

    PROCESS_TERMINATE             = 1 /* 1 */,
    PROCESS_CREATE_THREAD         = 2 /* 2 */,
    PROCESS_SET_SESSIONID         = 4 /* 4 */,
    PROCESS_VM_OPERATION          = 8 /* 8 */,
    PROCESS_VM_READ               = 16 /* 10 */,
    PROCESS_VM_WRITE              = 32 /* 20 */,
    PROCESS_DUP_HANDLE            = 64 /* 40 */,
    PROCESS_CREATE_PROCESS        = 128 /* 80 */,
    PROCESS_SET_QUOTA             = 256 /* 100 */,
    PROCESS_SET_INFORMATION       = 512 /* 200 */,
    PROCESS_QUERY_INFORMATION     = 1024 /* 400 */,
    PROCESS_SUSPEND_RESUME        = 2048 /* 800 */,
    PROCESS_ALL_ACCESS            = 2035711 /* 1F0FFF */,
};

enum {
    DONT_RESOLVE_DLL_REFERENCES   = 1 /* 1 */,

    LOAD_LIBRARY_AS_DATAFILE      = 2 /* 2 */,
    LOAD_WITH_ALTERED_SEARCH_PATH = 8 /* 8 */,
    LOAD_IGNORE_CODE_AUTHZ_LEVEL  = 16 /* 10 */,
    LOAD_LIBRARY_AS_IMAGE_RESOURCE = 32 /* 20 */,
    LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE = 64 /* 40 */,
};

enum {
    EXCEPTION_READ_FAULT          = 0 /* 0 */,
    EXCEPTION_WRITE_FAULT         = 1 /* 1 */,
    EXCEPTION_EXECUTE_FAULT       = 8 /* 8 */,

    SIZE_OF_80387_REGISTERS       = 80 /* 50 */,

    CONTEXT_i386                  = 65536 /* 10000 */,
    CONTEXT_i486                  = 65536 /* 10000 */,
    CONTEXT_CONTROL               = 65537 /* 10001 */,
    CONTEXT_INTEGER               = 65538 /* 10002 */,
    CONTEXT_SEGMENTS              = 65540 /* 10004 */,
    CONTEXT_FLOATING_POINT        = 65544 /* 10008 */,
    CONTEXT_DEBUG_REGISTERS       = 65552 /* 10010 */,
    CONTEXT_EXTENDED_REGISTERS    = 65568 /* 10020 */,
    CONTEXT_FULL                  = 65543 /* 10007 */,
    CONTEXT_ALL                   = 65599 /* 1003F */,

    MAXIMUM_SUPPORTED_EXTENSION   = 512 /* 200 */,
};

enum {
    MHS_EAX                       = 0 /* 0 */,
    MHS_ECX                       = 1 /* 1 */,
    MHS_EDX                       = 2 /* 2 */,
    MHS_EBX                       = 3 /* 3 */,
    MHS_ESP                       = 4 /* 4 */,
    MHS_EBP                       = 5 /* 5 */,
    MHS_ESI                       = 6 /* 6 */,
    MHS_EDI                       = 7 /* 7 */,
    MHS_UNDEF                     = -1 /* FFFFFFFF */,
    MHS_ES                        = 0 /* 0 */,
    MHS_CS                        = 1 /* 1 */,
    MHS_SS                        = 2 /* 2 */,
    MHS_DS                        = 3 /* 3 */,
    MHS_FS                        = 4 /* 4 */,
    MHS_GS                        = 5 /* 5 */,
    MHS_CR0                       = 0 /* 0 */,
    MHS_CR1                       = 1 /* 1 */,
    MHS_CR2                       = 2 /* 2 */,
    MHS_CR3                       = 3 /* 3 */,
    MHS_CR4                       = 4 /* 4 */,
    MHS_CR5                       = 5 /* 5 */,
    MHS_CR6                       = 6 /* 6 */,
    MHS_CR7                       = 7 /* 7 */,
    MHS_DR0                       = 0 /* 0 */,
    MHS_DR1                       = 1 /* 1 */,
    MHS_DR2                       = 2 /* 2 */,
    MHS_DR3                       = 3 /* 3 */,
    MHS_DR4                       = 4 /* 4 */,
    MHS_DR5                       = 5 /* 5 */,
    MHS_DR6                       = 6 /* 6 */,
    MHS_DR7                       = 7 /* 7 */,
    MHS_ST0                       = 0 /* 0 */,
    MHS_ST1                       = 1 /* 1 */,
    MHS_ST2                       = 2 /* 2 */,
    MHS_ST3                       = 3 /* 3 */,
    MHS_ST4                       = 4 /* 4 */,
    MHS_ST5                       = 5 /* 5 */,
    MHS_ST6                       = 6 /* 6 */,
    MHS_ST7                       = 7 /* 7 */,
};

enum {
    CD_TYPEMASK                   = 240 /* F0 */,
    CD_CMD                        = 0 /* 0 */,
    CD_PSH                        = 16 /* 10 */,
    CD_POP                        = 32 /* 20 */,
    CD_MMX                        = 48 /* 30 */,
    CD_FLT                        = 64 /* 40 */,
    CD_JMP                        = 80 /* 50 */,
    CD_JMC                        = 96 /* 60 */,
    CD_CAL                        = 112 /* 70 */,
    CD_RET                        = 128 /* 80 */,
    CD_FLG                        = 144 /* 90 */,
    CD_RTF                        = 160 /* A0 */,
    CD_REP                        = 176 /* B0 */,
    CD_PRI                        = 192 /* C0 */,
    CD_DAT                        = 208 /* D0 */,
    CD_NOW                        = 224 /* E0 */,
    CD_BAD                        = 240 /* F0 */,
    CD_RARE                       = 8 /* 8 */,
    CD_SIZEMASK                   = 7 /* 7 */,
    CD_EXPL                       = 1 /* 1 */,
    CD_DANGER95                   = 1 /* 1 */,
    CD_DANGER                     = 3 /* 3 */,
    CD_DANGERLOCK                 = 7 /* 7 */,
};

enum {
    MDEC_TYPEMASK                 = 31 /* 1F */,

    MDCE_UNKNOWN                  = 0 /* 0 */,
    MDCE_BYTE                     = 1 /* 1 */,
    MDCE_WORD                     = 2 /* 2 */,
    MDCE_NEXTDATA                 = 3 /* 3 */,
    MDCE_DWORD                    = 4 /* 4 */,
    MDCE_FLOAT4                   = 5 /* 5 */,
    MDCE_FWORD                    = 6 /* 6 */,
    MDCE_FLOAT8                   = 7 /* 7 */,
    MDCE_QWORD                    = 8 /* 8 */,
    MDCE_FLOAT10                  = 9 /* 9 */,
    MDCE_TBYTE                    = 10 /* A */,
    MDCE_STRING                   = 11 /* B */,
    MDCE_UNICODE                  = 12 /* C */,
    MDCE_3DNOW                    = 13 /* D */,
    MDCE_BYTESW                   = 17 /* 11 */,
    MDCE_NEXTCODE                 = 19 /* 13 */,
    MDCE_COMMAND                  = 29 /* 1D */,
    MDCE_JMPDEST                  = 30 /* 1E */,
    MDCE_CALLDEST                 = 31 /* 1F */,
    MDCE_PROCMASK                 = 96 /* 60 */,
    MDCE_PPROC                    = 32 /* 20 */,
    MDCE_PBODY                    = 64 /* 40 */,
    MDCE_PEND                     = 96 /* 60 */,
    MDCE_CHECKED                  = 128 /* 80 */,
};

enum {
    MDCER_TYPEMASK                = 63 /* 3F */,
    MDCER_BYTE                    = 33 /* 21 */,
    MDCER_WORD                    = 34 /* 22 */,
    MDCER_DWORD                   = 36 /* 24 */,
    MDCER_QWORD                   = 40 /* 28 */,
    MDCER_FLOAT10                 = 41 /* 29 */,
    MDCER_SEG                     = 42 /* 2A */,
    MDCER_3DNOW                   = 45 /* 2D */,
    MDCER_ISREG                   = 32 /* 20 */,
};

enum {
    DIS_SIZE                      = 0 /* 0 */,
    DIS_DATA                      = 1 /* 1 */,
    DIS_FILE                      = 2 /* 2 */,
    DIS_CODE                      = 3 /* 3 */,
};

enum {
    DISW_FARADDR                  = 1 /* 1 */,
    DISW_SEGMENT                  = 2 /* 2 */,
    DISW_PRIV                     = 4 /* 4 */,
    DISW_IO                       = 8 /* 8 */,
    DISW_SHIFT                    = 16 /* 10 */,
    DISW_PREFIX                   = 32 /* 20 */,
    DISW_LOCK                     = 64 /* 40 */,
    DISW_STACK                    = 128 /* 80 */,
    DISW_DANGER95                 = 4096 /* 1000 */,
    DISW_DANGEROUS                = 12288 /* 3000 */,
};

enum {
    DERR_NONE                     = 0 /* 0 */,
    DERR_BADCMD                   = 1 /* 1 */,
    DERR_CROSS                    = 2 /* 2 */,
    DERR_BADSEG                   = 3 /* 3 */,
    DERR_MEM                      = 4 /* 4 */,
    DERR_REG                      = 5 /* 5 */,
    DERR_OTHER                    = 6 /* 6 */,
};

enum {
    DIS_DATA_TYPE_NONE            = 0 /* 0 */,
    DIS_DATA_TYPE_MEMADDR         = 1 /* 1 */,
};

enum {
    ARG_NNN                       = 0 /* 0 */,
    ARG_REG                       = 1 /* 1 */,
    ARG_RCM                       = 2 /* 2 */,
    ARG_RG4                       = 3 /* 3 */,
    ARG_RAC                       = 4 /* 4 */,
    ARG_RAX                       = 5 /* 5 */,
    ARG_RDX                       = 6 /* 6 */,
    ARG_RCL                       = 7 /* 7 */,
    ARG_RS0                       = 8 /* 8 */,
    ARG_RST                       = 9 /* 9 */,
    ARG_RMX                       = 10 /* A */,
    ARG_R3D                       = 11 /* B */,
    ARG_MRG                       = 12 /* C */,
    ARG_MR1                       = 13 /* D */,
    ARG_MR2                       = 14 /* E */,
    ARG_MR4                       = 15 /* F */,
    ARG_RR4                       = 16 /* 10 */,
    ARG_MR8                       = 17 /* 11 */,
    ARG_RR8                       = 18 /* 12 */,
    ARG_MRD                       = 19 /* 13 */,
    ARG_RRD                       = 20 /* 14 */,
    ARG_MRJ                       = 21 /* 15 */,
    ARG_MMA                       = 22 /* 16 */,
    ARG_MML                       = 23 /* 17 */,
    ARG_MMS                       = 24 /* 18 */,
    ARG_MM6                       = 25 /* 19 */,
    ARG_MMB                       = 26 /* 1A */,
    ARG_MD2                       = 27 /* 1B */,
    ARG_MB2                       = 28 /* 1C */,
    ARG_MD4                       = 29 /* 1D */,
    ARG_MD8                       = 30 /* 1E */,
    ARG_MDA                       = 31 /* 1F */,
    ARG_MF4                       = 32 /* 20 */,
    ARG_MF8                       = 33 /* 21 */,
    ARG_MFA                       = 34 /* 22 */,
    ARG_MFE                       = 35 /* 23 */,
    ARG_MFS                       = 36 /* 24 */,
    ARG_MFX                       = 37 /* 25 */,
    ARG_MSO                       = 38 /* 26 */,
    ARG_MDE                       = 39 /* 27 */,
    ARG_MXL                       = 40 /* 28 */,
    ARG_IMM                       = 41 /* 29 */,
    ARG_IMU                       = 42 /* 2A */,
    ARG_VXD                       = 43 /* 2B */,
    ARG_IMX                       = 44 /* 2C */,
    ARG_C01                       = 45 /* 2D */,
    ARG_IMS                       = 46 /* 2E */,
    ARG_IM1                       = 47 /* 2F */,
    ARG_IM2                       = 48 /* 30 */,
    ARG_IMA                       = 49 /* 31 */,
    ARG_JOB                       = 50 /* 32 */,
    ARG_JOW                       = 51 /* 33 */,
    ARG_JMF                       = 52 /* 34 */,
    ARG_SGM                       = 53 /* 35 */,
    ARG_SCM                       = 54 /* 36 */,
    ARG_CRX                       = 55 /* 37 */,
    ARG_DRX                       = 56 /* 38 */,
};

enum {
    DOPT_IDEAL                    = 1 /* 1 */,
    DOPT_LOWER                    = 2 /* 2 */,
    DOPT_TAB_ARGS                 = 4 /* 4 */,
    DOPT_EXTRASPACE               = 8 /* 8 */,
    DOPT_PUTDEFSEG                = 16 /* 10 */,
    DOPT_SHOWMEMSIZE              = 32 /* 20 */,
    DOPT_NEAR                     = 64 /* 40 */,
    DOPT_SHRTSTRCMD               = 128 /* 80 */,
    DOPT_SIZESENS                 = 256 /* 100 */,
    DOPT_SYMBOLIC                 = 1024 /* 400 */,
    DOPT_FARCALLS                 = 2048 /* 800 */,
    DOPT_VXD                      = 4096 /* 1000 */,
    DOPT_PRIVILAGED               = 8192 /* 2000 */,
    DOPT_IOCOMMAND                = 16384 /* 4000 */,
    DOPT_BADSHIFT                 = 32768 /* 8000 */,
    DOPT_EXTRAPREFIX              = 65536 /* 10000 */,
    DOPT_LOCKEDBUS                = 131072 /* 20000 */,
    DOPT_STACKALIGN               = 262144 /* 40000 */,
    DOPT_SEGMOD                   = 524288 /* 80000 */,
    DOPT_WINDOWSNT                = 1048576 /* 100000 */,
};

enum {
    HT_JMP                        = 0 /* 0 */,
    HT_JMPTABLE                   = 1 /* 1 */,
    HT_OTHER                      = 2 /* 2 */,
};

enum {
    DS_HOOKFOUND                  = 0 /* 0 */,
    DS_HOOKNOTFOUND               = 1 /* 1 */,
    DS_FAILURE                    = 2 /* 2 */,
};

enum {
    NTOSKRNL                      = 0 /* 0 */,

    NTKRNLPA                      = 1 /* 1 */,
    NTKRNLMP                      = 2 /* 2 */,

    UNKNOWN                       = 3 /* 3 */,
};

enum {
    WSADESCRIPTION_LEN            = 256 /* 100 */,
    WSASYS_STATUS_LEN             = 128 /* 80 */,
    WSABASEERR                    = 10000 /* 2710 */,
    WSAEINTR                      = 10004 /* 2714 */,
    WSAEBADF                      = 10009 /* 2719 */,
    WSAEACCES                     = 10013 /* 271D */,
    WSAEFAULT                     = 10014 /* 271E */,
    WSAEINVAL                     = 10022 /* 2726 */,
    WSAEMFILE                     = 10024 /* 2728 */,
    WSAEWOULDBLOCK                = 10035 /* 2733 */,
    WSAEINPROGRESS                = 10036 /* 2734 */,
    WSAEALREADY                   = 10037 /* 2735 */,
    WSAENOTSOCK                   = 10038 /* 2736 */,
    WSAEDESTADDRREQ               = 10039 /* 2737 */,
    WSAEMSGSIZE                   = 10040 /* 2738 */,
    WSAEPROTOTYPE                 = 10041 /* 2739 */,
    WSAENOPROTOOPT                = 10042 /* 273A */,
    WSAEPROTONOSUPPORT            = 10043 /* 273B */,
    WSAESOCKTNOSUPPORT            = 10044 /* 273C */,
    WSAEOPNOTSUPP                 = 10045 /* 273D */,
    WSAEPFNOSUPPORT               = 10046 /* 273E */,
    WSAEAFNOSUPPORT               = 10047 /* 273F */,
    WSAEADDRINUSE                 = 10048 /* 2740 */,
    WSAEADDRNOTAVAIL              = 10049 /* 2741 */,
    WSAENETDOWN                   = 10050 /* 2742 */,
    WSAENETUNREACH                = 10051 /* 2743 */,
    WSAENETRESET                  = 10052 /* 2744 */,
    WSAECONNABORTED               = 10053 /* 2745 */,
    WSAECONNRESET                 = 10054 /* 2746 */,
    WSAENOBUFS                    = 10055 /* 2747 */,
    WSAEISCONN                    = 10056 /* 2748 */,
    WSAENOTCONN                   = 10057 /* 2749 */,
    WSAESHUTDOWN                  = 10058 /* 274A */,
    WSAETOOMANYREFS               = 10059 /* 274B */,
    WSAETIMEDOUT                  = 10060 /* 274C */,
    WSAECONNREFUSED               = 10061 /* 274D */,
    WSAELOOP                      = 10062 /* 274E */,
    WSAENAMETOOLONG               = 10063 /* 274F */,
    WSAEHOSTDOWN                  = 10064 /* 2750 */,
    WSAEHOSTUNREACH               = 10065 /* 2751 */,
    WSAENOTEMPTY                  = 10066 /* 2752 */,
    WSAEPROCLIM                   = 10067 /* 2753 */,
    WSAEUSERS                     = 10068 /* 2754 */,
    WSAEDQUOT                     = 10069 /* 2755 */,
    WSAESTALE                     = 10070 /* 2756 */,
    WSAEREMOTE                    = 10071 /* 2757 */,
    WSAEDISCON                    = 10101 /* 2775 */,
    WSASYSNOTREADY                = 10091 /* 276B */,
    WSAVERNOTSUPPORTED            = 10092 /* 276C */,
    WSANOTINITIALISED             = 10093 /* 276D */,
    WSAHOST_NOT_FOUND             = 11001 /* 2AF9 */,
    WSATRY_AGAIN                  = 11002 /* 2AFA */,
    WSANO_RECOVERY                = 11003 /* 2AFB */,
    WSANO_DATA                    = 11004 /* 2AFC */,

    SOCKET_ERROR                  = -1 /* FFFFFFFF */,
    SOCK_STREAM                   = 1 /* 1 */,
    SOCK_DGRAM                    = 2 /* 2 */,
    SOCK_RAW                      = 3 /* 3 */,
    SOCK_RDM                      = 4 /* 4 */,
    SOCK_SEQPACKET                = 5 /* 5 */,

    AF_UNSPEC                     = 0 /* 0 */,
    AF_UNIX                       = 1 /* 1 */,
    AF_INET                       = 2 /* 2 */,
    AF_IMPLINK                    = 3 /* 3 */,
    AF_PUP                        = 4 /* 4 */,
    AF_CHAOS                      = 5 /* 5 */,
    AF_IPX                        = 6 /* 6 */,
    AF_NS                         = 6 /* 6 */,
    AF_ISO                        = 7 /* 7 */,
    AF_OSI                        = 7 /* 7 */,
    AF_ECMA                       = 8 /* 8 */,
    AF_DATAKIT                    = 9 /* 9 */,
    AF_CCITT                      = 10 /* A */,
    AF_SNA                        = 11 /* B */,
    AF_DECnet                     = 12 /* C */,
    AF_DLI                        = 13 /* D */,
    AF_LAT                        = 14 /* E */,
    AF_HYLINK                     = 15 /* F */,
    AF_APPLETALK                  = 16 /* 10 */,
    AF_NETBIOS                    = 17 /* 11 */,
    AF_VOICEVIEW                  = 18 /* 12 */,
    AF_FIREFOX                    = 19 /* 13 */,
    AF_UNKNOWN1                   = 20 /* 14 */,
    AF_BAN                        = 21 /* 15 */,
    AF_MAX                        = 22 /* 16 */,

    INADDR_ANY                    = 0 /* 0 */,
    INADDR_LOOPBACK               = 2130706433 /* 7F000001 */,
    INADDR_BROADCAST              = -1 /* FFFFFFFF */,
    INADDR_NONE                   = -1 /* FFFFFFFF */,

    INVALID_SOCKET                = -1 /* FFFFFFFF */,

    SOMAXCONN                     = 5 /* 5 */,

    SOL_SOCKET                    = 65535 /* FFFF */,

    MSG_OOB                       = 1 /* 1 */,
    MSG_PEEK                      = 2 /* 2 */,
    MSG_DONTROUTE                 = 4 /* 4 */,
    MSG_MAXIOVLEN                 = 16 /* 10 */,
    MSG_PARTIAL                   = 32768 /* 8000 */,

    MAXGETHOSTSTRUCT              = 1024 /* 400 */,

    FD_SETSIZE                    = 64 /* 40 */,

    IPPROTO_IP                    = 0 /* 0 */,
    IPPROTO_ICMP                  = 1 /* 1 */,
    IPPROTO_IGMP                  = 2 /* 2 */,
    IPPROTO_GGP                   = 3 /* 3 */,
    IPPROTO_TCP                   = 6 /* 6 */,
    IPPROTO_PUP                   = 12 /* C */,
    IPPROTO_UDP                   = 17 /* 11 */,
    IPPROTO_IDP                   = 22 /* 16 */,
    IPPROTO_ND                    = 77 /* 4D */,
    IPPROTO_RAW                   = 255 /* FF */,
    IPPROTO_MAX                   = 256 /* 100 */,
    IPPORT_ECHO                   = 7 /* 7 */,
    IPPORT_DISCARD                = 9 /* 9 */,
    IPPORT_SYSTAT                 = 11 /* B */,
    IPPORT_DAYTIME                = 13 /* D */,
    IPPORT_NETSTAT                = 15 /* F */,
    IPPORT_FTP                    = 21 /* 15 */,
    IPPORT_TELNET                 = 23 /* 17 */,
    IPPORT_SMTP                   = 25 /* 19 */,
    IPPORT_TIMESERVER             = 37 /* 25 */,
    IPPORT_NAMESERVER             = 42 /* 2A */,
    IPPORT_WHOIS                  = 43 /* 2B */,
    IPPORT_MTP                    = 57 /* 39 */,
    IPPORT_TFTP                   = 69 /* 45 */,
    IPPORT_RJE                    = 77 /* 4D */,
    IPPORT_FINGER                 = 79 /* 4F */,
    IPPORT_TTYLINK                = 87 /* 57 */,
    IPPORT_SUPDUP                 = 95 /* 5F */,
    IPPORT_EXECSERVER             = 512 /* 200 */,
    IPPORT_LOGINSERVER            = 513 /* 201 */,
    IPPORT_CMDSERVER              = 514 /* 202 */,
    IPPORT_EFSSERVER              = 520 /* 208 */,
    IPPORT_BIFFUDP                = 512 /* 200 */,
    IPPORT_WHOSERVER              = 513 /* 201 */,
    IPPORT_ROUTESERVER            = 520 /* 208 */,
    IPPORT_RESERVED               = 1024 /* 400 */,

    IMPLINK_IP                    = 155 /* 9B */,
    IMPLINK_LOWEXPER              = 156 /* 9C */,
    IMPLINK_HIGHEXPER             = 158 /* 9E */,

    SO_DEBUG                      = 1 /* 1 */,
    SO_ACCEPTCONN                 = 2 /* 2 */,
    SO_REUSEADDR                  = 4 /* 4 */,
    SO_KEEPALIVE                  = 8 /* 8 */,
    SO_DONTROUTE                  = 16 /* 10 */,
    SO_BROADCAST                  = 32 /* 20 */,
    SO_USELOOPBACK                = 64 /* 40 */,
    SO_LINGER                     = 128 /* 80 */,
    SO_OOBINLINE                  = 256 /* 100 */,
    SO_DONTLINGER                 = -129 /* FFFFFF7F */,
    SO_SNDBUF                     = 4097 /* 1001 */,
    SO_RCVBUF                     = 4098 /* 1002 */,
    SO_SNDLOWAT                   = 4099 /* 1003 */,
    SO_RCVLOWAT                   = 4100 /* 1004 */,
    SO_SNDTIMEO                   = 4101 /* 1005 */,
    SO_RCVTIMEO                   = 4102 /* 1006 */,
    SO_ERROR                      = 4103 /* 1007 */,
    SO_TYPE                       = 4104 /* 1008 */,
    SO_CONNDATA                   = 28672 /* 7000 */,
    SO_CONNOPT                    = 28673 /* 7001 */,
    SO_DISCDATA                   = 28674 /* 7002 */,
    SO_DISCOPT                    = 28675 /* 7003 */,
    SO_CONNDATALEN                = 28676 /* 7004 */,
    SO_CONNOPTLEN                 = 28677 /* 7005 */,
    SO_DISCDATALEN                = 28678 /* 7006 */,
    SO_DISCOPTLEN                 = 28679 /* 7007 */,
    SO_OPENTYPE                   = 28680 /* 7008 */,
    SO_SYNCHRONOUS_ALERT          = 16 /* 10 */,
    SO_SYNCHRONOUS_NONALERT       = 32 /* 20 */,
    SO_MAXDG                      = 28681 /* 7009 */,
    SO_MAXPATHDG                  = 28682 /* 700A */,
    SO_UPDATE_ACCEPT_CONTEXT      = 28683 /* 700B */,
    SO_CONNECT_TIME               = 28684 /* 700C */,

    TCP_NODELAY                   = 1 /* 1 */,
    TCP_BSDURGENT                 = 28672 /* 7000 */,

    IOCPARM_MASK                  = 127 /* 7F */,
    IOC_VOID                      = 536870912 /* 20000000 */,
    IOC_OUT                       = 1073741824 /* 40000000 */,
    IOC_IN                        = -2147483648 /* 80000000 */,
    IOC_INOUT                     = -1073741824 /* C0000000 */,

    FIONREAD                      = 1074030207 /* 4004667F */,
    FIONBIO                       = -2147195266 /* 8004667E */,
    FIOASYNC                      = -2147195267 /* 8004667D */,

    SIOCSHIWAT                    = -2147192064 /* 80047300 */,
    SIOCGHIWAT                    = 1074033409 /* 40047301 */,
    SIOCSLOWAT                    = -2147192062 /* 80047302 */,
    SIOCGLOWAT                    = 1074033411 /* 40047303 */,
    SIOCATMARK                    = 1074033415 /* 40047307 */,
};

enum {
    ZLIB_VERNUM                   = 4656 /* 1230 */,

    Z_NO_FLUSH                    = 0 /* 0 */,

    Z_PARTIAL_FLUSH               = 1 /* 1 */,

    Z_SYNC_FLUSH                  = 2 /* 2 */,

    Z_FULL_FLUSH                  = 3 /* 3 */,
    Z_FINISH                      = 4 /* 4 */,

    Z_BLOCK                       = 5 /* 5 */,

    Z_OK                          = 0 /* 0 */,

    Z_STREAM_END                  = 1 /* 1 */,

    Z_NEED_DICT                   = 2 /* 2 */,

    Z_ERRNO                       = -1 /* FFFFFFFF */,

    Z_STREAM_ERROR                = -2 /* FFFFFFFE */,

    Z_DATA_ERROR                  = -3 /* FFFFFFFD */,

    Z_MEM_ERROR                   = -4 /* FFFFFFFC */,

    Z_BUF_ERROR                   = -5 /* FFFFFFFB */,

    Z_VERSION_ERROR               = -6 /* FFFFFFFA */,

    Z_NO_COMPRESSION              = 0 /* 0 */,

    Z_BEST_SPEED                  = 1 /* 1 */,
    Z_BEST_COMPRESSION            = 9 /* 9 */,

    Z_DEFAULT_COMPRESSION         = -1 /* FFFFFFFF */,

    Z_FILTERED                    = 1 /* 1 */,

    Z_HUFFMAN_ONLY                = 2 /* 2 */,

    Z_RLE                         = 3 /* 3 */,

    Z_FIXED                       = 4 /* 4 */,

    Z_DEFAULT_STRATEGY            = 0 /* 0 */,

    Z_BINARY                      = 0 /* 0 */,

    Z_TEXT                        = 1 /* 1 */,

    Z_ASCII                       = 1 /* 1 */,

    Z_UNKNOWN                     = 2 /* 2 */,

    Z_DEFLATED                    = 8 /* 8 */,

    Z_NULL                        = 0 /* 0 */,
};

enum {
    HK_FUNC_NOTHING               = 0 /* 0 */,
    HK_FUNC_POKE                  = 1 /* 1 */,
    HK_FUNC_BEEP                  = 2 /* 2 */,
    HK_FUNC_PAUSE                 = 3 /* 3 */,
    HK_FUNC_RESUME                = 4 /* 4 */,
    HK_FUNC_SCRIPTFUNCTION        = 5 /* 5 */,
    HK_FUNC_CURPROCSCRIPTFUNCTION = 6 /* 6 */,
    HK_FUNC_LOADCONVERTER         = 7 /* 7 */,
    HK_FUNC_LOADHEXEDITOR         = 8 /* 8 */,
    HK_FUNC_LOADDISASSEMBLER      = 9 /* 9 */,
    HK_FUNC_LOADSCRIPTEDITOR      = 10 /* A */,
    HK_FUNC_SUBSAMEASBEFORE       = 11 /* B */,
    HK_FUNC_SUBDIFFROMBEFORE      = 12 /* C */,
    HK_FUNC_SUBINCREASED          = 13 /* D */,
    HK_FUNC_SUBDECREASED          = 14 /* E */,
    HK_FUNC_SUBSAMEASORIGINAL     = 15 /* F */,
    HK_FUNC_LOCKSTORED            = 16 /* 10 */,
    HK_FUNC_UNLOCKSTORED          = 17 /* 11 */,
    HK_FUNC_SETPROCESSSPEED       = 18 /* 12 */,
    HK_FUNC_TOTAL                 = 19 /* 13 */,
};

enum {
    SDT_NTACCEPTCONNECTPORT       = 0 /* 0 */,
    SDT_NTACCESSCHECK             = 1 /* 1 */,
    SDT_NTACCESSCHECKANDAUDITALARM = 2 /* 2 */,
    SDT_NTACCESSCHECKBYTYPE       = 3 /* 3 */,
    SDT_NTACCESSCHECKBYTYPEANDAUDITALARM = 4 /* 4 */,
    SDT_NTACCESSCHECKBYTYPERESULTLIST = 5 /* 5 */,
    SDT_NTACCESSCHECKBYTYPERESULTLISTANDAUDITALARM = 6 /* 6 */,
    SDT_NTACCESSCHECKBYTYPERESULTLISTANDAUDITALARMBYHANDLE = 7 /* 7 */,
    SDT_NTADDATOM                 = 8 /* 8 */,
    SDT_NTADDBOOTENTRY            = 9 /* 9 */,
    SDT_NTADDDRIVERENTRY          = 10 /* A */,
    SDT_NTADJUSTGROUPSTOKEN       = 11 /* B */,
    SDT_NTADJUSTPRIVILEGESTOKEN   = 12 /* C */,
    SDT_NTALERTRESUMETHREAD       = 13 /* D */,
    SDT_NTALERTTHREAD             = 14 /* E */,
    SDT_NTALLOCATELOCALLYUNIQUEID = 15 /* F */,
    SDT_NTALLOCATEUSERPHYSICALPAGES = 16 /* 10 */,
    SDT_NTALLOCATEUUIDS           = 17 /* 11 */,
    SDT_NTALLOCATEVIRTUALMEMORY   = 18 /* 12 */,
    SDT_NTAPPHELPCACHECONTROL     = 19 /* 13 */,
    SDT_NTAREMAPPEDFILESTHESAME   = 20 /* 14 */,
    SDT_NTASSIGNPROCESSTOJOBOBJECT = 21 /* 15 */,
    SDT_NTCALLBACKRETURN          = 22 /* 16 */,
    SDT_NTCANCELDEVICEWAKEUPREQUEST = 23 /* 17 */,
    SDT_NTCANCELIOFILE            = 24 /* 18 */,
    SDT_NTCANCELTIMER             = 25 /* 19 */,
    SDT_NTCLEAREVENT              = 26 /* 1A */,
    SDT_NTCLOSE                   = 27 /* 1B */,
    SDT_NTCLOSEOBJECTAUDITALARM   = 28 /* 1C */,
    SDT_NTCOMPACTKEYS             = 29 /* 1D */,
    SDT_NTCOMPARETOKENS           = 30 /* 1E */,
    SDT_NTCOMPLETECONNECTPORT     = 31 /* 1F */,
    SDT_NTCOMPRESSKEY             = 32 /* 20 */,
    SDT_NTCONNECTPORT             = 33 /* 21 */,
    SDT_NTCONTINUE                = 34 /* 22 */,
    SDT_NTCREATECHANNEL           = 35 /* 23 */,
    SDT_NTCREATEDEBUGOBJECT       = 36 /* 24 */,
    SDT_NTCREATEDIRECTORYOBJECT   = 37 /* 25 */,
    SDT_NTCREATEEVENT             = 38 /* 26 */,
    SDT_NTCREATEEVENTPAIR         = 39 /* 27 */,
    SDT_NTCREATEFILE              = 40 /* 28 */,
    SDT_NTCREATEIOCOMPLETION      = 41 /* 29 */,
    SDT_NTCREATEJOBOBJECT         = 42 /* 2A */,
    SDT_NTCREATEJOBSET            = 43 /* 2B */,
    SDT_NTCREATEKEY               = 44 /* 2C */,
    SDT_NTCREATEKEYEDEVENT        = 45 /* 2D */,
    SDT_NTCREATEMAILSLOTFILE      = 46 /* 2E */,
    SDT_NTCREATEMUTANT            = 47 /* 2F */,
    SDT_NTCREATENAMEDPIPEFILE     = 48 /* 30 */,
    SDT_NTCREATEPAGINGFILE        = 49 /* 31 */,
    SDT_NTCREATEPORT              = 50 /* 32 */,
    SDT_NTCREATEPROCESS           = 51 /* 33 */,
    SDT_NTCREATEPROCESSEX         = 52 /* 34 */,
    SDT_NTCREATEPROFILE           = 53 /* 35 */,
    SDT_NTCREATESECTION           = 54 /* 36 */,
    SDT_NTCREATESEMAPHORE         = 55 /* 37 */,
    SDT_NTCREATESYMBOLICLINKOBJECT = 56 /* 38 */,
    SDT_NTCREATETHREAD            = 57 /* 39 */,
    SDT_NTCREATETIMER             = 58 /* 3A */,
    SDT_NTCREATETOKEN             = 59 /* 3B */,
    SDT_NTCREATEWAITABLEPORT      = 60 /* 3C */,
    SDT_NTDEBUGACTIVEPROCESS      = 61 /* 3D */,
    SDT_NTDEBUGCONTINUE           = 62 /* 3E */,
    SDT_NTDELAYEXECUTION          = 63 /* 3F */,
    SDT_NTDELETEATOM              = 64 /* 40 */,
    SDT_NTDELETEBOOTENTRY         = 65 /* 41 */,
    SDT_NTDELETEDRIVERENTRY       = 66 /* 42 */,
    SDT_NTDELETEFILE              = 67 /* 43 */,
    SDT_NTDELETEKEY               = 68 /* 44 */,
    SDT_NTDELETEOBJECTAUDITALARM  = 69 /* 45 */,
    SDT_NTDELETEVALUEKEY          = 70 /* 46 */,
    SDT_NTDEVICEIOCONTROLFILE     = 71 /* 47 */,
    SDT_NTDISPLAYSTRING           = 72 /* 48 */,
    SDT_NTDUPLICATEOBJECT         = 73 /* 49 */,
    SDT_NTDUPLICATETOKEN          = 74 /* 4A */,
    SDT_NTENUMERATEBOOTENTRIES    = 75 /* 4B */,
    SDT_NTENUMERATEKEY            = 76 /* 4C */,
    SDT_NTENUMERATESYSTEMENVIRONMENTVALUESEX = 77 /* 4D */,
    SDT_NTENUMERATEVALUEKEY       = 78 /* 4E */,
    SDT_NTEXTENDSECTION           = 79 /* 4F */,
    SDT_NTFILTERTOKEN             = 80 /* 50 */,
    SDT_NTFINDATOM                = 81 /* 51 */,
    SDT_NTFLUSHBUFFERSFILE        = 82 /* 52 */,
    SDT_NTFLUSHINSTRUCTIONCACHE   = 83 /* 53 */,
    SDT_NTFLUSHKEY                = 84 /* 54 */,
    SDT_NTFLUSHVIRTUALMEMORY      = 85 /* 55 */,
    SDT_NTFLUSHWRITEBUFFER        = 86 /* 56 */,
    SDT_NTFREEUSERPHYSICALPAGES   = 87 /* 57 */,
    SDT_NTFREEVIRTUALMEMORY       = 88 /* 58 */,
    SDT_NTFSCONTROLFILE           = 89 /* 59 */,
    SDT_NTGETCONTEXTTHREAD        = 90 /* 5A */,
    SDT_NTGETDEVICEPOWERSTATE     = 91 /* 5B */,
    SDT_NTGETPLUGPLAYEVENT        = 92 /* 5C */,
    SDT_NTGETTICKCOUNT            = 93 /* 5D */,
    SDT_NTGETWRITEWATCH           = 94 /* 5E */,
    SDT_NTIMPERSONATEANONYMOUSTOKEN = 95 /* 5F */,
    SDT_NTIMPERSONATECLIENTOFPORT = 96 /* 60 */,
    SDT_NTIMPERSONATETHREAD       = 97 /* 61 */,
    SDT_NTINITIALIZEREGISTRY      = 98 /* 62 */,
    SDT_NTINITIATEPOWERACTION     = 99 /* 63 */,
    SDT_NTISPROCESSINJOB          = 100 /* 64 */,
    SDT_NTISSYSTEMRESUMEAUTOMATIC = 101 /* 65 */,
    SDT_NTLISTENCHANNEL           = 102 /* 66 */,
    SDT_NTLISTENPORT              = 103 /* 67 */,
    SDT_NTLOADDRIVER              = 104 /* 68 */,
    SDT_NTLOADKEY                 = 105 /* 69 */,
    SDT_NTLOADKEY2                = 106 /* 6A */,
    SDT_NTLOCKFILE                = 107 /* 6B */,
    SDT_NTLOCKPRODUCTACTIVATIONKEYS = 108 /* 6C */,
    SDT_NTLOCKREGISTRYKEY         = 109 /* 6D */,
    SDT_NTLOCKVIRTUALMEMORY       = 110 /* 6E */,
    SDT_NTMAKEPERMANENTOBJECT     = 111 /* 6F */,
    SDT_NTMAKETEMPORARYOBJECT     = 112 /* 70 */,
    SDT_NTMAPUSERPHYSICALPAGES    = 113 /* 71 */,
    SDT_NTMAPUSERPHYSICALPAGESSCATTER = 114 /* 72 */,
    SDT_NTMAPVIEWOFSECTION        = 115 /* 73 */,
    SDT_NTMODIFYBOOTENTRY         = 116 /* 74 */,
    SDT_NTNOTIFYCHANGEDIRECTORYFILE = 117 /* 75 */,
    SDT_NTNOTIFYCHANGEKEY         = 118 /* 76 */,
    SDT_NTNOTIFYCHANGEMULTIPLEKEYS = 119 /* 77 */,
    SDT_NTOPENCHANNEL             = 120 /* 78 */,
    SDT_NTOPENDIRECTORYOBJECT     = 121 /* 79 */,
    SDT_NTOPENEVENT               = 122 /* 7A */,
    SDT_NTOPENEVENTPAIR           = 123 /* 7B */,
    SDT_NTOPENFILE                = 124 /* 7C */,
    SDT_NTOPENIOCOMPLETION        = 125 /* 7D */,
    SDT_NTOPENJOBOBJECT           = 126 /* 7E */,
    SDT_NTOPENKEY                 = 127 /* 7F */,
    SDT_NTOPENKEYEDEVENT          = 128 /* 80 */,
    SDT_NTOPENMUTANT              = 129 /* 81 */,
    SDT_NTOPENOBJECTAUDITALARM    = 130 /* 82 */,
    SDT_NTOPENPROCESS             = 131 /* 83 */,
    SDT_NTOPENPROCESSTOKEN        = 132 /* 84 */,
    SDT_NTOPENPROCESSTOKENEX      = 133 /* 85 */,
    SDT_NTOPENSECTION             = 134 /* 86 */,
    SDT_NTOPENSEMAPHORE           = 135 /* 87 */,
    SDT_NTOPENSYMBOLICLINKOBJECT  = 136 /* 88 */,
    SDT_NTOPENTHREAD              = 137 /* 89 */,
    SDT_NTOPENTHREADTOKEN         = 138 /* 8A */,
    SDT_NTOPENTHREADTOKENEX       = 139 /* 8B */,
    SDT_NTOPENTIMER               = 140 /* 8C */,
    SDT_NTPLUGPLAYCONTROL         = 141 /* 8D */,
    SDT_NTPOWERINFORMATION        = 142 /* 8E */,
    SDT_NTPRIVILEGECHECK          = 143 /* 8F */,
    SDT_NTPRIVILEGEOBJECTAUDITALARM = 144 /* 90 */,
    SDT_NTPRIVILEGEDSERVICEAUDITALARM = 145 /* 91 */,
    SDT_NTPROTECTVIRTUALMEMORY    = 146 /* 92 */,
    SDT_NTPULSEEVENT              = 147 /* 93 */,
    SDT_NTQUERYATTRIBUTESFILE     = 148 /* 94 */,
    SDT_NTQUERYBOOTENTRYORDER     = 149 /* 95 */,
    SDT_NTQUERYBOOTOPTIONS        = 150 /* 96 */,
    SDT_NTQUERYDEBUGFILTERSTATE   = 151 /* 97 */,
    SDT_NTQUERYDEFAULTLOCALE      = 152 /* 98 */,
    SDT_NTQUERYDEFAULTUILANGUAGE  = 153 /* 99 */,
    SDT_NTQUERYDIRECTORYFILE      = 154 /* 9A */,
    SDT_NTQUERYDIRECTORYOBJECT    = 155 /* 9B */,
    SDT_NTQUERYEAFILE             = 156 /* 9C */,
    SDT_NTQUERYEVENT              = 157 /* 9D */,
    SDT_NTQUERYFULLATTRIBUTESFILE = 158 /* 9E */,
    SDT_NTQUERYINFORMATIONATOM    = 159 /* 9F */,
    SDT_NTQUERYINFORMATIONFILE    = 160 /* A0 */,
    SDT_NTQUERYINFORMATIONJOBOBJECT = 161 /* A1 */,
    SDT_NTQUERYINFORMATIONPORT    = 162 /* A2 */,
    SDT_NTQUERYINFORMATIONPROCESS = 163 /* A3 */,
    SDT_NTQUERYINFORMATIONTHREAD  = 164 /* A4 */,
    SDT_NTQUERYINFORMATIONTOKEN   = 165 /* A5 */,
    SDT_NTQUERYINSTALLUILANGUAGE  = 166 /* A6 */,
    SDT_NTQUERYINTERVALPROFILE    = 167 /* A7 */,
    SDT_NTQUERYIOCOMPLETION       = 168 /* A8 */,
    SDT_NTQUERYKEY                = 169 /* A9 */,
    SDT_NTQUERYMULTIPLEVALUEKEY   = 170 /* AA */,
    SDT_NTQUERYMUTANT             = 171 /* AB */,
    SDT_NTQUERYOBJECT             = 172 /* AC */,
    SDT_NTQUERYOPENSUBKEYS        = 173 /* AD */,
    SDT_NTQUERYPERFORMANCECOUNTER = 174 /* AE */,
    SDT_NTQUERYPORTINFORMATIONPROCESS = 175 /* AF */,
    SDT_NTQUERYQUOTAINFORMATIONFILE = 176 /* B0 */,
    SDT_NTQUERYSECTION            = 177 /* B1 */,
    SDT_NTQUERYSECURITYOBJECT     = 178 /* B2 */,
    SDT_NTQUERYSEMAPHORE          = 179 /* B3 */,
    SDT_NTQUERYSYMBOLICLINKOBJECT = 180 /* B4 */,
    SDT_NTQUERYSYSTEMENVIRONMENTVALUE = 181 /* B5 */,
    SDT_NTQUERYSYSTEMENVIRONMENTVALUEEX = 182 /* B6 */,
    SDT_NTQUERYSYSTEMINFORMATION  = 183 /* B7 */,
    SDT_NTQUERYSYSTEMTIME         = 184 /* B8 */,
    SDT_NTQUERYTIMER              = 185 /* B9 */,
    SDT_NTQUERYTIMERRESOLUTION    = 186 /* BA */,
    SDT_NTQUERYVALUEKEY           = 187 /* BB */,
    SDT_NTQUERYVIRTUALMEMORY      = 188 /* BC */,
    SDT_NTQUERYVOLUMEINFORMATIONFILE = 189 /* BD */,
    SDT_NTQUEUEAPCTHREAD          = 190 /* BE */,
    SDT_NTRAISEEXCEPTION          = 191 /* BF */,
    SDT_NTRAISEHARDERROR          = 192 /* C0 */,
    SDT_NTREADFILE                = 193 /* C1 */,
    SDT_NTREADFILESCATTER         = 194 /* C2 */,
    SDT_NTREADREQUESTDATA         = 195 /* C3 */,
    SDT_NTREADVIRTUALMEMORY       = 196 /* C4 */,
    SDT_NTREGISTERTHREADTERMINATEPORT = 197 /* C5 */,
    SDT_NTRELEASEKEYEDEVENT       = 198 /* C6 */,
    SDT_NTRELEASEMUTANT           = 199 /* C7 */,
    SDT_NTRELEASESEMAPHORE        = 200 /* C8 */,
    SDT_NTREMOVEIOCOMPLETION      = 201 /* C9 */,
    SDT_NTREMOVEPROCESSDEBUG      = 202 /* CA */,
    SDT_NTRENAMEKEY               = 203 /* CB */,
    SDT_NTREPLACEKEY              = 204 /* CC */,
    SDT_NTREPLYPORT               = 205 /* CD */,
    SDT_NTREPLYWAITRECEIVEPORT    = 206 /* CE */,
    SDT_NTREPLYWAITRECEIVEPORTEX  = 207 /* CF */,
    SDT_NTREPLYWAITREPLYPORT      = 208 /* D0 */,
    SDT_NTREPLYWAITSENDCHANNEL    = 209 /* D1 */,
    SDT_NTREQUESTDEVICEWAKEUP     = 210 /* D2 */,
    SDT_NTREQUESTPORT             = 211 /* D3 */,
    SDT_NTREQUESTWAITREPLYPORT    = 212 /* D4 */,
    SDT_NTREQUESTWAKEUPLATENCY    = 213 /* D5 */,
    SDT_NTRESETEVENT              = 214 /* D6 */,
    SDT_NTRESETWRITEWATCH         = 215 /* D7 */,
    SDT_NTRESTOREKEY              = 216 /* D8 */,
    SDT_NTRESUMEPROCESS           = 217 /* D9 */,
    SDT_NTRESUMETHREAD            = 218 /* DA */,
    SDT_NTSAVEKEY                 = 219 /* DB */,
    SDT_NTSAVEKEYEX               = 220 /* DC */,
    SDT_NTSAVEMERGEDKEYS          = 221 /* DD */,
    SDT_NTSECURECONNECTPORT       = 222 /* DE */,
    SDT_NTSENDWAITREPLYCHANNEL    = 223 /* DF */,
    SDT_NTSETBOOTENTRYORDER       = 224 /* E0 */,
    SDT_NTSETBOOTOPTIONS          = 225 /* E1 */,
    SDT_NTSETCONTEXTCHANNEL       = 226 /* E2 */,
    SDT_NTSETCONTEXTTHREAD        = 227 /* E3 */,
    SDT_NTSETDEBUGFILTERSTATE     = 228 /* E4 */,
    SDT_NTSETDEFAULTHARDERRORPORT = 229 /* E5 */,
    SDT_NTSETDEFAULTLOCALE        = 230 /* E6 */,
    SDT_NTSETDEFAULTUILANGUAGE    = 231 /* E7 */,
    SDT_NTSETEAFILE               = 232 /* E8 */,
    SDT_NTSETEVENT                = 233 /* E9 */,
    SDT_NTSETEVENTBOOSTPRIORITY   = 234 /* EA */,
    SDT_NTSETHIGHEVENTPAIR        = 235 /* EB */,
    SDT_NTSETHIGHWAITLOWEVENTPAIR = 236 /* EC */,
    SDT_NTSETINFORMATIONDEBUGOBJECT = 237 /* ED */,
    SDT_NTSETINFORMATIONFILE      = 238 /* EE */,
    SDT_NTSETINFORMATIONJOBOBJECT = 239 /* EF */,
    SDT_NTSETINFORMATIONKEY       = 240 /* F0 */,
    SDT_NTSETINFORMATIONOBJECT    = 241 /* F1 */,
    SDT_NTSETINFORMATIONPROCESS   = 242 /* F2 */,
    SDT_NTSETINFORMATIONTHREAD    = 243 /* F3 */,
    SDT_NTSETINFORMATIONTOKEN     = 244 /* F4 */,
    SDT_NTSETINTERVALPROFILE      = 245 /* F5 */,
    SDT_NTSETIOCOMPLETION         = 246 /* F6 */,
    SDT_NTSETLDTENTRIES           = 247 /* F7 */,
    SDT_NTSETLOWEVENTPAIR         = 248 /* F8 */,
    SDT_NTSETLOWWAITHIGHEVENTPAIR = 249 /* F9 */,
    SDT_NTSETQUOTAINFORMATIONFILE = 250 /* FA */,
    SDT_NTSETSECURITYOBJECT       = 251 /* FB */,
    SDT_NTSETSYSTEMENVIRONMENTVALUE = 252 /* FC */,
    SDT_NTSETSYSTEMENVIRONMENTVALUEEX = 253 /* FD */,
    SDT_NTSETSYSTEMINFORMATION    = 254 /* FE */,
    SDT_NTSETSYSTEMPOWERSTATE     = 255 /* FF */,
    SDT_NTSETSYSTEMTIME           = 256 /* 100 */,
    SDT_NTSETTHREADEXECUTIONSTATE = 257 /* 101 */,
    SDT_NTSETTIMER                = 258 /* 102 */,
    SDT_NTSETTIMERRESOLUTION      = 259 /* 103 */,
    SDT_NTSETUUIDSEED             = 260 /* 104 */,
    SDT_NTSETVALUEKEY             = 261 /* 105 */,
    SDT_NTSETVOLUMEINFORMATIONFILE = 262 /* 106 */,
    SDT_NTSHUTDOWNSYSTEM          = 263 /* 107 */,
    SDT_NTSIGNALANDWAITFORSINGLEOBJECT = 264 /* 108 */,
    SDT_NTSTARTPROFILE            = 265 /* 109 */,
    SDT_NTSTOPPROFILE             = 266 /* 10A */,
    SDT_NTSUSPENDPROCESS          = 267 /* 10B */,
    SDT_NTSUSPENDTHREAD           = 268 /* 10C */,
    SDT_NTSYSTEMDEBUGCONTROL      = 269 /* 10D */,
    SDT_NTTERMINATEJOBOBJECT      = 270 /* 10E */,
    SDT_NTTERMINATEPROCESS        = 271 /* 10F */,
    SDT_NTTERMINATETHREAD         = 272 /* 110 */,
    SDT_NTTESTALERT               = 273 /* 111 */,
    SDT_NTTRACEEVENT              = 274 /* 112 */,
    SDT_NTTRANSLATEFILEPATH       = 275 /* 113 */,
    SDT_NTUNLOADDRIVER            = 276 /* 114 */,
    SDT_NTUNLOADKEY               = 277 /* 115 */,
    SDT_NTUNLOADKEYEX             = 278 /* 116 */,
    SDT_NTUNLOCKFILE              = 279 /* 117 */,
    SDT_NTUNLOCKVIRTUALMEMORY     = 280 /* 118 */,
    SDT_NTUNMAPVIEWOFSECTION      = 281 /* 119 */,
    SDT_NTVDMCONTROL              = 282 /* 11A */,
    SDT_NTWAITFORDEBUGEVENT       = 283 /* 11B */,
    SDT_NTWAITFORKEYEDEVENT       = 284 /* 11C */,
    SDT_NTWAITFORMULTIPLEOBJECTS  = 285 /* 11D */,
    SDT_NTWAITFORSINGLEOBJECT     = 286 /* 11E */,
    SDT_NTWAITHIGHEVENTPAIR       = 287 /* 11F */,
    SDT_NTWAITLOWEVENTPAIR        = 288 /* 120 */,
    SDT_NTWRITEFILE               = 289 /* 121 */,
    SDT_NTWRITEFILEGATHER         = 290 /* 122 */,
    SDT_NTWRITEREQUESTDATA        = 291 /* 123 */,
    SDT_NTWRITEVIRTUALMEMORY      = 292 /* 124 */,
    SDT_NTYIELDEXECUTION          = 293 /* 125 */,
    SDT_TOTAL                     = 294 /* 126 */,
};

enum {
    MAL_DEFAULT                   = 0 /* 0 */,
    MAL_AUTOASSEMBLE              = 1 /* 1 */,
    MAL_SCRIPT                    = 2 /* 2 */,

    MAA_DEFAULT                   = 3 /* 3 */,
    MAA_COMPLEX                   = 4 /* 4 */,
    MAA_SCRIPT                    = 5 /* 5 */,

    SV_LOCK_INVALID               = -1 /* FFFFFFFF */,
    SV_LOCK_STATIC                = 0 /* 0 */,
    SV_LOCK_RANGE                 = 1 /* 1 */,
    SV_LOCK_MINBARRIER            = 2 /* 2 */,
    SV_LOCK_MAXBARRIER            = 3 /* 3 */,
};

enum {
    SYS_FUNCS_DO_NOTHING          = 0 /* 0 */,
    SYS_FUNCS_BEEP                = 1 /* 1 */,
    SYS_FUNCS_BREAK               = 2 /* 2 */,
    SYS_FUNCS_SETCF               = 3 /* 3 */,
    SYS_FUNCS_SETPF               = 4 /* 4 */,
    SYS_FUNCS_SETAF               = 5 /* 5 */,
    SYS_FUNCS_SETZF               = 6 /* 6 */,
    SYS_FUNCS_SETSF               = 7 /* 7 */,
    SYS_FUNCS_SETOF               = 8 /* 8 */,
    SYS_FUNCS_SCRIPT_FUNC         = 9 /* 9 */,
    SYS_FUNCS_CUR_PROC_SCRIPT_FUNC = 10 /* A */,
};

enum {
    SPBT_EXECUTE                  = 0 /* 0 */,
    SPBT_READ                     = 1 /* 1 */,
    SPBT_WRITE                    = 2 /* 2 */,
};

enum {
    RCE_NOTHING                   = 0 /* 0 */,
    RCE_STDCALL                   = 1 /* 1 */,
    RCE_FLOAT                     = 2 /* 2 */,
    RCE_DOUBLE                    = 4 /* 4 */,
};


Predefined Structs & Unions

struct _MEMORY_BASIC_INFORMATION {
    void * BaseAddress;                                         // Offset: 0, Size: 4.
    void * AllocationBase;                                      // Offset: 4, Size: 4.
    unsigned int AllocationProtect;                             // Offset: 8, Size: 4.
    unsigned int RegionSize;                                    // Offset: 12, Size: 4.
    unsigned int State;                                         // Offset: 16, Size: 4.
    unsigned int Protect;                                       // Offset: 20, Size: 4.
    unsigned int Type;                                          // Offset: 24, Size: 4.
}; // Size: 28.

struct tagMODULEENTRY32 {
    unsigned int dwSize;                                        // Offset: 0, Size: 4.
    unsigned int th32ModuleID;                                  // Offset: 4, Size: 4.
    unsigned int th32ProcessID;                                 // Offset: 8, Size: 4.
    unsigned int GlblcntUsage;                                  // Offset: 12, Size: 4.
    unsigned int ProccntUsage;                                  // Offset: 16, Size: 4.
    unsigned char * modBaseAddr;                                // Offset: 20, Size: 4.
    unsigned int modBaseSize;                                   // Offset: 24, Size: 4.
    void * hModule;                                             // Offset: 28, Size: 4.
    char szModule[256];                                         // Offset: 32, Size: 256.
    char szExePath[260];                                        // Offset: 288, Size: 260.
}; // Size: 548.

struct tagHEAPLIST32 {
    unsigned int dwSize;                                        // Offset: 0, Size: 4.
    unsigned int th32ProcessID;                                 // Offset: 4, Size: 4.
    unsigned int th32HeapID;                                    // Offset: 8, Size: 4.
    unsigned int dwFlags;                                       // Offset: 12, Size: 4.
}; // Size: 16.

struct tagTHREADENTRY32 {
    unsigned int dwSize;                                        // Offset: 0, Size: 4.
    unsigned int cntUsage;                                      // Offset: 4, Size: 4.
    unsigned int th32ThreadID;                                  // Offset: 8, Size: 4.
    unsigned int th32OwnerProcessID;                            // Offset: 12, Size: 4.
    int tpBasePri;                                              // Offset: 16, Size: 4.
    int tpDeltaPri;                                             // Offset: 20, Size: 4.
    unsigned int dwFlags;                                       // Offset: 24, Size: 4.
}; // Size: 28.

struct tagPROCESSENTRY32 {
    unsigned int dwSize;                                        // Offset: 0, Size: 4.
    unsigned int cntUsage;                                      // Offset: 4, Size: 4.
    unsigned int th32ProcessID;                                 // Offset: 8, Size: 4.
    unsigned int th32DefaultHeapID;                             // Offset: 12, Size: 4.
    unsigned int th32ModuleID;                                  // Offset: 16, Size: 4.
    unsigned int cntThreads;                                    // Offset: 20, Size: 4.
    unsigned int th32ParentProcessID;                           // Offset: 24, Size: 4.
    int pcPriClassBase;                                         // Offset: 28, Size: 4.
    unsigned int dwFlags;                                       // Offset: 32, Size: 4.
    char szExeFile[260];                                        // Offset: 36, Size: 260.
}; // Size: 296.

struct tagHEAPENTRY32 {
    unsigned int dwSize;                                        // Offset: 0, Size: 4.
    void * hHandle;                                             // Offset: 4, Size: 4.
    unsigned int dwAddress;                                     // Offset: 8, Size: 4.
    unsigned int dwBlockSize;                                   // Offset: 12, Size: 4.
    unsigned int dwFlags;                                       // Offset: 16, Size: 4.
    unsigned int dwLockCount;                                   // Offset: 20, Size: 4.
    unsigned int dwResvd;                                       // Offset: 24, Size: 4.
    unsigned int th32ProcessID;                                 // Offset: 28, Size: 4.
    unsigned int th32HeapID;                                    // Offset: 32, Size: 4.
}; // Size: 36.

struct _FLOATING_SAVE_AREA {
    unsigned int ControlWord;                                   // Offset: 0, Size: 4.
    unsigned int StatusWord;                                    // Offset: 4, Size: 4.
    unsigned int TagWord;                                       // Offset: 8, Size: 4.
    unsigned int ErrorOffset;                                   // Offset: 12, Size: 4.
    unsigned int ErrorSelector;                                 // Offset: 16, Size: 4.
    unsigned int DataOffset;                                    // Offset: 20, Size: 4.
    unsigned int DataSelector;                                  // Offset: 24, Size: 4.
    unsigned char RegisterArea[80];                             // Offset: 28, Size: 80.
    unsigned int Cr0NpxState;                                   // Offset: 108, Size: 4.
}; // Size: 112.

struct _CONTEXT {
    unsigned int ContextFlags;                                  // Offset: 0, Size: 4.
    unsigned int Dr0;                                           // Offset: 4, Size: 4.
    unsigned int Dr1;                                           // Offset: 8, Size: 4.
    unsigned int Dr2;                                           // Offset: 12, Size: 4.
    unsigned int Dr3;                                           // Offset: 16, Size: 4.
    unsigned int Dr6;                                           // Offset: 20, Size: 4.
    unsigned int Dr7;                                           // Offset: 24, Size: 4.
    _FLOATING_SAVE_AREA FloatSave;                              // Offset: 28, Size: 112.
    unsigned int SegGs;                                         // Offset: 140, Size: 4.
    unsigned int SegFs;                                         // Offset: 144, Size: 4.
    unsigned int SegEs;                                         // Offset: 148, Size: 4.
    unsigned int SegDs;                                         // Offset: 152, Size: 4.
    unsigned int Edi;                                           // Offset: 156, Size: 4.
    unsigned int Esi;                                           // Offset: 160, Size: 4.
    unsigned int Ebx;                                           // Offset: 164, Size: 4.
    unsigned int Edx;                                           // Offset: 168, Size: 4.
    unsigned int Ecx;                                           // Offset: 172, Size: 4.
    unsigned int Eax;                                           // Offset: 176, Size: 4.
    unsigned int Ebp;                                           // Offset: 180, Size: 4.
    unsigned int Eip;                                           // Offset: 184, Size: 4.
    unsigned int SegCs;                                         // Offset: 188, Size: 4.
    unsigned int EFlags;                                        // Offset: 192, Size: 4.
    unsigned int Esp;                                           // Offset: 196, Size: 4.
    unsigned int SegSs;                                         // Offset: 200, Size: 4.
    unsigned char ExtendedRegisters[512];                       // Offset: 204, Size: 512.
}; // Size: 716.

struct _RECT {
    int left;                                                   // Offset: 0, Size: 4.
    int top;                                                    // Offset: 4, Size: 4.
    int right;                                                  // Offset: 8, Size: 4.
    int bottom;                                                 // Offset: 12, Size: 4.
}; // Size: 16.

struct tagPOINT {
    int x;                                                      // Offset: 0, Size: 4.
    int y;                                                      // Offset: 4, Size: 4.
}; // Size: 8.

struct tagCREATESTRUCT {
    void * lpCreateParams;                                      // Offset: 0, Size: 4.
    void * hInstance;                                           // Offset: 4, Size: 4.
    void * hMenu;                                               // Offset: 8, Size: 4.
    void * hwndParent;                                          // Offset: 12, Size: 4.
    int cy;                                                     // Offset: 16, Size: 4.
    int cx;                                                     // Offset: 20, Size: 4.
    int y;                                                      // Offset: 24, Size: 4.
    int x;                                                      // Offset: 28, Size: 4.
    int style;                                                  // Offset: 32, Size: 4.
    const char * lpszName;                                      // Offset: 36, Size: 4.
    const char * lpszClass;                                     // Offset: 40, Size: 4.
    unsigned int dwExStyle;                                     // Offset: 44, Size: 4.
}; // Size: 48.

struct tagPAINTSTRUCT {
    void * hdc;                                                 // Offset: 0, Size: 4.
    int fErase;                                                 // Offset: 4, Size: 4.
    _RECT rcPaint;                                              // Offset: 8, Size: 16.
    int fRestore;                                               // Offset: 24, Size: 4.
    int fIncUpdate;                                             // Offset: 28, Size: 4.
    unsigned char rgbReserved[32];                              // Offset: 32, Size: 32.
}; // Size: 64.

struct tagMENUINFO {
    unsigned int cbSize;                                        // Offset: 0, Size: 4.
    unsigned int fMask;                                         // Offset: 4, Size: 4.
    unsigned int dwStyle;                                       // Offset: 8, Size: 4.
    unsigned int cyMax;                                         // Offset: 12, Size: 4.
    void * hbrBack;                                             // Offset: 16, Size: 4.
    unsigned int dwContextHelpID;                               // Offset: 20, Size: 4.
    unsigned int dwMenuData;                                    // Offset: 24, Size: 4.
}; // Size: 28.

struct tagNMHDR {
    void * hwndFrom;                                            // Offset: 0, Size: 4.
    unsigned int idFrom;                                        // Offset: 4, Size: 4.
    unsigned int code;                                          // Offset: 8, Size: 4.
}; // Size: 12.

struct _UDACCEL {
    unsigned int nSec;                                          // Offset: 0, Size: 4.
    unsigned int nInc;                                          // Offset: 4, Size: 4.
}; // Size: 8.

struct _NM_UPDOWN {
    tagNMHDR hdr;                                               // Offset: 0, Size: 12.
    int iPos;                                                   // Offset: 12, Size: 4.
    int iDelta;                                                 // Offset: 16, Size: 4.
}; // Size: 20.

struct tagMOUSEINPUT {
    int dx;                                                     // Offset: 0, Size: 4.
    int dy;                                                     // Offset: 4, Size: 4.
    unsigned int mouseData;                                     // Offset: 8, Size: 4.
    unsigned int dwFlags;                                       // Offset: 12, Size: 4.
    unsigned int time;                                          // Offset: 16, Size: 4.
    unsigned int dwExtraInfo;                                   // Offset: 20, Size: 4.
}; // Size: 24.

struct tagKEYBDINPUT {
    unsigned short wVk;                                         // Offset: 0, Size: 2.
    unsigned short wScan;                                       // Offset: 2, Size: 2.
    unsigned int dwFlags;                                       // Offset: 4, Size: 4.
    unsigned int time;                                          // Offset: 8, Size: 4.
    unsigned int dwExtraInfo;                                   // Offset: 12, Size: 4.
}; // Size: 16.

struct tagHARDWAREINPUT {
    unsigned int uMsg;                                          // Offset: 0, Size: 4.
    unsigned short wParamL;                                     // Offset: 4, Size: 2.
    unsigned short wParamH;                                     // Offset: 6, Size: 2.
}; // Size: 8.

union INPUTTYPE {
    tagMOUSEINPUT mi;                                           // Offset: 0, Size: 24.
    tagKEYBDINPUT ki;                                           // Offset: 0, Size: 16.
    tagHARDWAREINPUT hi;                                        // Offset: 0, Size: 8.
}; // Size: 24.

struct tagINPUT {
    unsigned int type;                                          // Offset: 0, Size: 4.
    INPUTTYPE iType;                                            // Offset: 4, Size: 24.
}; // Size: 28.

struct _FILETIME {
    unsigned int dwLowDateTime;                                 // Offset: 0, Size: 4.
    unsigned int dwHighDateTime;                                // Offset: 4, Size: 4.
}; // Size: 8.

struct _WIN32_FIND_DATA {
    unsigned int dwFileAttributes;                              // Offset: 0, Size: 4.
    _FILETIME ftCreationTime;                                   // Offset: 4, Size: 8.
    _FILETIME ftLastAccessTime;                                 // Offset: 12, Size: 8.
    _FILETIME ftLastWriteTime;                                  // Offset: 20, Size: 8.
    unsigned int nFileSizeHigh;                                 // Offset: 28, Size: 4.
    unsigned int nFileSizeLow;                                  // Offset: 32, Size: 4.
    unsigned int dwReserved0;                                   // Offset: 36, Size: 4.
    unsigned int dwReserved1;                                   // Offset: 40, Size: 4.
    char cFileName[260];                                        // Offset: 44, Size: 260.
    char cAlternateFileName[14];                                // Offset: 304, Size: 14.
}; // Size: 318.

union MHS_ALLTYPES {
    signed char Char;                                           // Offset: 0, Size: 1.
    unsigned char Byte;                                         // Offset: 0, Size: 1.
    signed short Short;                                         // Offset: 0, Size: 2.
    unsigned short UShort;                                      // Offset: 0, Size: 2.
    signed int Int;                                             // Offset: 0, Size: 4.
    unsigned int UInt;                                          // Offset: 0, Size: 4.
    signed __int64 Int64;                                       // Offset: 0, Size: 8.
    unsigned __int64 UInt64;                                    // Offset: 0, Size: 8.
    float Float;                                                // Offset: 0, Size: 4.
    double Double;                                              // Offset: 0, Size: 8.
}; // Size: 8.

struct DATATYPEPARMS {
    unsigned int dwDataType;                                    // Offset: 0, Size: 4.
    unsigned int dwSubType;                                     // Offset: 4, Size: 4.
    int bEnableSame;                                            // Offset: 8, Size: 4.
    MHS_ALLTYPES atValue;                                       // Offset: 12, Size: 8.
    MHS_ALLTYPES atTo;                                          // Offset: 20, Size: 8.
}; // Size: 28.

struct POINTERPARMS {
    unsigned int dwSubType;                                     // Offset: 0, Size: 4.
    int bEnableSame;                                            // Offset: 4, Size: 4.
    int bStatic;                                                // Offset: 8, Size: 4.
    void * lpvSaveOffset;                                       // Offset: 12, Size: 4.
    MHS_ALLTYPES atValue;                                       // Offset: 16, Size: 8.
    MHS_ALLTYPES atTo;                                          // Offset: 24, Size: 8.
}; // Size: 32.

struct STRINGPARMS {
    const char * pcValue;                                       // Offset: 0, Size: 4.
    unsigned int dwLength;                                      // Offset: 4, Size: 4.
    int bUnicode;                                               // Offset: 8, Size: 4.
    int bOffsets;                                               // Offset: 12, Size: 4.
    unsigned char bOffFrom;                                     // Offset: 16, Size: 1.
    unsigned char bOffTo;                                       // Offset: 17, Size: 1.
}; // Size: 18.

struct SCRIPTPARMS {
    int iDataSize;                                              // Offset: 0, Size: 4.
    int iAlign;                                                 // Offset: 4, Size: 4.
    const char * pcCallback;                                    // Offset: 8, Size: 4.
    const char * pcDecoder;                                     // Offset: 12, Size: 4.
    const char * pcSetup;                                       // Offset: 16, Size: 4.
}; // Size: 20.

union TP {
    DATATYPEPARMS dtParms;                                      // Offset: 0, Size: 28.
    POINTERPARMS pParms;                                        // Offset: 0, Size: 32.
    STRINGPARMS sParms;                                         // Offset: 0, Size: 18.
    SCRIPTPARMS scParms;                                        // Offset: 0, Size: 20.
}; // Size: 32.

struct MHS_API_SEARCH_PARMS {
    unsigned int dwType;                                        // Offset: 0, Size: 4.
    void * lpvStart;                                            // Offset: 4, Size: 4.
    void * lpvEnd;                                              // Offset: 8, Size: 4.
    int bAligned;                                               // Offset: 12, Size: 4.
    TP TypeParms;                                               // Offset: 16, Size: 32.
}; // Size: 48.

struct MHS_API_SUB_SEARCH_PARMS {
    unsigned int dwSearchType;                                  // Offset: 0, Size: 4.
    MHS_ALLTYPES atValue;                                       // Offset: 4, Size: 8.
    MHS_ALLTYPES atTo;                                          // Offset: 12, Size: 8.
    const char * pcCallback;                                    // Offset: 20, Size: 4.
}; // Size: 24.

union PINFO_UNION {
    unsigned int dwUserData;                                    // Offset: 0, Size: 4.
    char * pcStringPointer;                                     // Offset: 0, Size: 4.
    void * lpvMiscData;                                         // Offset: 0, Size: 4.
}; // Size: 4.

struct tagPROC_INFO_MHS {
    void * hProcess;                                            // Offset: 0, Size: 4.
    void * hThread;                                             // Offset: 4, Size: 4.
    unsigned int dwProcessId;                                   // Offset: 8, Size: 4.
    unsigned int dwThreadId;                                    // Offset: 12, Size: 4.
    void * lpvReseved;                                          // Offset: 16, Size: 4.
    PINFO_UNION u;                                              // Offset: 20, Size: 4.
    void * lpvReadWriteAddress;                                 // Offset: 24, Size: 4.
    _CONTEXT * pcContext;                                       // Offset: 28, Size: 4.
    int bSetContext;                                            // Offset: 32, Size: 4.
}; // Size: 36.

struct WSAData {
    unsigned short wVersion;                                    // Offset: 0, Size: 2.
    unsigned short wHighVersion;                                // Offset: 2, Size: 2.
    char szDescription[257];                                    // Offset: 4, Size: 257.
    char szSystemStatus[129];                                   // Offset: 261, Size: 129.
    unsigned short iMaxSockets;                                 // Offset: 390, Size: 2.
    unsigned short iMaxUdpDg;                                   // Offset: 392, Size: 2.
    char * lpVendorInfo;                                        // Offset: 394, Size: 4.
}; // Size: 398.

struct z_stream_s {
    unsigned char * next_in;                                    // Offset: 0, Size: 4.
    unsigned int avail_in;                                      // Offset: 4, Size: 4.
    unsigned int total_in;                                      // Offset: 8, Size: 4.
    unsigned char * next_out;                                   // Offset: 12, Size: 4.
    unsigned int avail_out;                                     // Offset: 16, Size: 4.
    unsigned int total_out;                                     // Offset: 20, Size: 4.
    char * msg;                                                 // Offset: 24, Size: 4.
    void * state;                                               // Offset: 28, Size: 4.
    void * zalloc;                                              // Offset: 32, Size: 4.
    void * zfree;                                               // Offset: 36, Size: 4.
    void * opaque;                                              // Offset: 40, Size: 4.
    int data_type;                                              // Offset: 44, Size: 4.
    unsigned int adler;                                         // Offset: 48, Size: 4.
    unsigned int reserved;                                      // Offset: 52, Size: 4.
}; // Size: 56.

union EVAL_UNION {
    unsigned __int64 ui64Int64;                                 // Offset: 0, Size: 8.
    __int64 i64Int64;                                           // Offset: 0, Size: 8.
    double dDouble;                                             // Offset: 0, Size: 8.
}; // Size: 8.

struct EVAL_RET_TYPE {
    int iType;                                                  // Offset: 0, Size: 4.
    EVAL_UNION u;                                               // Offset: 4, Size: 8.
}; // Size: 12.

struct SCRIPT_ADD_BP {
    char * pcName;                                              // Offset: 0, Size: 4.
    int bHardware;                                              // Offset: 4, Size: 4.
    int iType;                                                  // Offset: 8, Size: 4.
    int bSet;                                                   // Offset: 12, Size: 4.
    unsigned __int64 aAddress;                                  // Offset: 16, Size: 8.
    unsigned int dwRange;                                       // Offset: 24, Size: 4.
    int iProlog;                                                // Offset: 28, Size: 4.
    int iCallback;                                              // Offset: 32, Size: 4.
    int iEpilog;                                                // Offset: 36, Size: 4.
    unsigned int dwNewParms[3];                                 // Offset: 40, Size: 12.
    int bUseCondition;                                          // Offset: 52, Size: 4.
    char * pcCondition;                                         // Offset: 56, Size: 4.
}; // Size: 60.

struct MODULEWITHADDRESS {
    unsigned __int64 aBaseAddress;                              // Offset: 0, Size: 8.
    char szModule[260];                                         // Offset: 8, Size: 260.
    char szExePath[260];                                        // Offset: 268, Size: 260.
}; // Size: 528.

struct HOOK_INFO {
    unsigned __int64 aAddress;                                  // Offset: 0, Size: 8.
    unsigned int dwSize;                                        // Offset: 8, Size: 4.
    int htType;                                                 // Offset: 12, Size: 4.
    char szApiName[32];                                         // Offset: 16, Size: 32.
    unsigned int dwApiOffset;                                   // Offset: 48, Size: 4.
    char szHookedModule[260];                                   // Offset: 52, Size: 260.
    unsigned __int64 aJmpAddress;                               // Offset: 312, Size: 8.
    char szHookerModule[260];                                   // Offset: 320, Size: 260.
    unsigned __int64 aHookerAddress;                            // Offset: 580, Size: 8.
    unsigned char * pbHookBytes;                                // Offset: 588, Size: 4.
    unsigned char * pbRealBytes;                                // Offset: 592, Size: 4.
}; // Size: 596.

union REMOTE_CALL_RETURN {
    unsigned char bByte;                                        // Offset: 0, Size: 1.
    char cChar;                                                 // Offset: 0, Size: 1.
    unsigned short wWord;                                       // Offset: 0, Size: 2.
    short sShort;                                               // Offset: 0, Size: 2.
    unsigned int dwDWord;                                       // Offset: 0, Size: 4.
    int lLong;                                                  // Offset: 0, Size: 4.
    unsigned __int64 ui64Int64;                                 // Offset: 0, Size: 8.
    __int64 i64Int64;                                           // Offset: 0, Size: 8.
    float fFloat;                                               // Offset: 0, Size: 4.
    double dDouble;                                             // Offset: 0, Size: 8.
    unsigned int uiPtr;                                         // Offset: 0, Size: 4.
}; // Size: 8.

struct LOCKPARMSINT {
    __int64 i64LockLow;                                         // Offset: 0, Size: 8.
    __int64 i64LockHigh;                                        // Offset: 8, Size: 8.
}; // Size: 16.

struct LOCKPARMSDOUBLE {
    double dLockLow;                                            // Offset: 0, Size: 8.
    double dLockHigh;                                           // Offset: 8, Size: 8.
}; // Size: 16.

union LOCKPARMS {
    LOCKPARMSINT irLock;                                        // Offset: 0, Size: 16.
    LOCKPARMSDOUBLE drLock;                                     // Offset: 0, Size: 16.
    __int64 i64Limit;                                           // Offset: 0, Size: 8.
    double dLimit;                                              // Offset: 0, Size: 8.
}; // Size: 16.

struct ADDADDRESS {
    unsigned __int64 aAddress;                                  // Offset: 0, Size: 8.
    int iType;                                                  // Offset: 8, Size: 4.
    int iLockType;                                              // Offset: 12, Size: 4.
    LOCKPARMS uLockParms;                                       // Offset: 16, Size: 16.
    unsigned int dwLockValueSize;                               // Offset: 32, Size: 4.
    unsigned char * pbLockedValue;                              // Offset: 36, Size: 4.
    char * pcDescr;                                             // Offset: 40, Size: 4.
    int iLockMethod;                                            // Offset: 44, Size: 4.
    char * pcAutoAssemble;                                      // Offset: 48, Size: 4.
    char * pcScript;                                            // Offset: 52, Size: 4.
    int iAddressType;                                           // Offset: 56, Size: 4.
    int bUseScriptForBaseAddress;                               // Offset: 60, Size: 4.
    int bUseScriptForFinalAddress;                              // Offset: 64, Size: 4.
    char * pcComplexAddress;                                    // Offset: 68, Size: 4.
    int iHotKeyKey;                                             // Offset: 72, Size: 4.
}; // Size: 76.

struct BROWSEINFO {
    void * hwndOwner;                                           // Offset: 0, Size: 4.
    void * pidlRoot;                                            // Offset: 4, Size: 4.
    char * pszDisplayName;                                      // Offset: 8, Size: 4.
    const char * lpszTitle;                                     // Offset: 12, Size: 4.
    unsigned int ulFlags;                                       // Offset: 16, Size: 4.
    void * lpfn;                                                // Offset: 20, Size: 4.
    unsigned int lParam;                                        // Offset: 24, Size: 4.
    int iImage;                                                 // Offset: 28, Size: 4.
}; // Size: 32.

struct VECTOR3 {
    float x;                                                    // Offset: 0, Size: 4.
    float y;                                                    // Offset: 4, Size: 4.
    float z;                                                    // Offset: 8, Size: 4.
}; // Size: 12.

struct MATRIX {
    float a[16];                                                // Offset: 0, Size: 64.
}; // Size: 64.

struct FLOAT_POINT {
    float fX;                                                   // Offset: 0, Size: 4.
    float fY;                                                   // Offset: 4, Size: 4.
}; // Size: 8.

struct ARMOR_DATA {
    unsigned char bUnknown1[60];                                // Offset: 0, Size: 60.
    char * pcArmorName;                                         // Offset: 60, Size: 4.
    unsigned char bUnknown2[24];                                // Offset: 64, Size: 24.
    float fMaxEnergy;                                           // Offset: 88, Size: 4.
    unsigned char bUnknown3[1164];                              // Offset: 92, Size: 1164.
    float fUnknown1;                                            // Offset: 1256, Size: 4.
    float fUnknown2;                                            // Offset: 1260, Size: 4.
    float fUnknown3;                                            // Offset: 1264, Size: 4.
    float fGroundForce;                                         // Offset: 1268, Size: 4.
    float fGroundTraction;                                      // Offset: 1272, Size: 4.
    float fJetForward;                                          // Offset: 1276, Size: 4.
    float fUnknown4;                                            // Offset: 1280, Size: 4.
    float fUnknown5;                                            // Offset: 1284, Size: 4.
    float fJetSideForce;                                        // Offset: 1288, Size: 4.
    float fJumpImpulse;                                         // Offset: 1292, Size: 4.
    float fUnknown6;                                            // Offset: 1296, Size: 4.
    float fMass;                                                // Offset: 1300, Size: 4.
    float fUnknown7;                                            // Offset: 1304, Size: 4.
    float fUnknown8;                                            // Offset: 1308, Size: 4.
    float fJetForce;                                            // Offset: 1312, Size: 4.
}; // Size: 1316.

struct WINDOWPOS {
    void * hwnd;                                                // Offset: 0, Size: 4.
    void * hwndInsertAfter;                                     // Offset: 4, Size: 4.
    int x;                                                      // Offset: 8, Size: 4.
    int y;                                                      // Offset: 12, Size: 4.
    int cx;                                                     // Offset: 16, Size: 4.
    int cy;                                                     // Offset: 20, Size: 4.
    unsigned int flags;                                         // Offset: 24, Size: 4.
}; // Size: 28.

struct s_S_un0 {
    unsigned char s_b1;                                         // Offset: 0, Size: 1.
    unsigned char s_b2;                                         // Offset: 1, Size: 1.
    unsigned char s_b3;                                         // Offset: 2, Size: 1.
    unsigned char s_b4;                                         // Offset: 3, Size: 1.
}; // Size: 4.

struct s_S_un1 {
    unsigned short s_w1;                                        // Offset: 0, Size: 2.
    unsigned short s_w2;                                        // Offset: 2, Size: 2.
}; // Size: 4.

union u_S_un {
    s_S_un0 S_un_b;                                             // Offset: 0, Size: 4.
    s_S_un1 S_un_w;                                             // Offset: 0, Size: 4.
    unsigned int S_addr;                                        // Offset: 0, Size: 4.
}; // Size: 4.

struct in_addr {
    u_S_un S_un;                                                // Offset: 0, Size: 4.
}; // Size: 4.

struct sockaddr_in {
    short sin_family;                                           // Offset: 0, Size: 2.
    unsigned short sin_port;                                    // Offset: 2, Size: 2.
    in_addr sin_addr;                                           // Offset: 4, Size: 4.
    char sin_zero[8];                                           // Offset: 8, Size: 8.
}; // Size: 16.

struct hostent {
    char * h_name;                                              // Offset: 0, Size: 4.
    char ** h_aliases;                                          // Offset: 4, Size: 4.
    short h_addrtype;                                           // Offset: 8, Size: 2.
    short h_length;                                             // Offset: 10, Size: 2.
    char ** h_addr_list;                                        // Offset: 12, Size: 4.
}; // Size: 16.

struct servent {
    char * s_name;                                              // Offset: 0, Size: 4.
    char ** s_aliases;                                          // Offset: 4, Size: 4.
    short s_port;                                               // Offset: 8, Size: 2.
    char * s_proto;                                             // Offset: 10, Size: 4.
}; // Size: 14.

struct protoent {
    char * p_name;                                              // Offset: 0, Size: 4.
    char ** p_aliases;                                          // Offset: 4, Size: 4.
    short p_proto;                                              // Offset: 8, Size: 2.
}; // Size: 10.

struct timeval {
    int tv_sec;                                                 // Offset: 0, Size: 4.
    int tv_usec;                                                // Offset: 4, Size: 4.
}; // Size: 8.

struct fd_set {
    unsigned int fd_count;                                      // Offset: 0, Size: 4.
    unsigned int fd_array[64];                                  // Offset: 4, Size: 256.
}; // Size: 260.

struct linger {
    unsigned short l_onoff;                                     // Offset: 0, Size: 2.
    unsigned short l_linger;                                    // Offset: 2, Size: 2.
}; // Size: 4.

struct DIS_CMD {
    unsigned int dwMask;                                        // Offset: 0, Size: 4.
    unsigned int dwCode;                                        // Offset: 4, Size: 4.
    unsigned char ucLength;                                     // Offset: 8, Size: 1.
    unsigned char ucBits;                                       // Offset: 9, Size: 1.
    unsigned char ucArg1;                                       // Offset: 10, Size: 1.
    unsigned char ucArg2;                                       // Offset: 11, Size: 1.
    unsigned char ucArg3;                                       // Offset: 12, Size: 1.
    unsigned char ucType;                                       // Offset: 13, Size: 1.
    unsigned char ucDiffDecOpSize;                              // Offset: 14, Size: 1.
    unsigned char ucDepOdAddSize;                               // Offset: 15, Size: 1.
    char * ptcName;                                             // Offset: 16, Size: 4.
    void * lpvReserved;                                         // Offset: 20, Size: 4.
}; // Size: 24.

struct CODESTRING {
    unsigned int dwReserved;                                    // Offset: 0, Size: 4.
    unsigned int dwLength;                                      // Offset: 4, Size: 4.
    char * pcText;                                              // Offset: 8, Size: 4.
}; // Size: 12.

struct DIS_OP_DATA {
    int iDataType;                                              // Offset: 0, Size: 4.
    unsigned short wMemBegin;                                   // Offset: 4, Size: 2.
    unsigned short wMemEnd;                                     // Offset: 6, Size: 2.
    int bIsMemory;                                              // Offset: 8, Size: 4.
    int bIsRegister;                                            // Offset: 12, Size: 4.
    unsigned char bSize;                                        // Offset: 16, Size: 1.
    unsigned char bRegisterCount;                               // Offset: 17, Size: 1.
}; // Size: 18.

struct DIS_DISASM {
    unsigned int aEIP;                                          // Offset: 0, Size: 4.
    CODESTRING strDump;                                         // Offset: 4, Size: 12.
    CODESTRING strResult;                                       // Offset: 16, Size: 12.
    CODESTRING strComment;                                      // Offset: 28, Size: 12.
    int iCmdType;                                               // Offset: 40, Size: 4.
    int iMemType;                                               // Offset: 44, Size: 4.
    int iPrefixes;                                              // Offset: 48, Size: 4.
    int iIndexed;                                               // Offset: 52, Size: 4.
    unsigned int dwJmpConst;                                    // Offset: 56, Size: 4.
    unsigned int dwTable;                                       // Offset: 60, Size: 4.
    unsigned int dwAddrConst;                                   // Offset: 64, Size: 4.
    unsigned int dwImmConst;                                    // Offset: 68, Size: 4.
    int iArg1;                                                  // Offset: 72, Size: 4.
    int iArg2;                                                  // Offset: 76, Size: 4.
    int iArg3;                                                  // Offset: 80, Size: 4.
    int iZeroConst;                                             // Offset: 84, Size: 4.
    int iFixUpOffset;                                           // Offset: 88, Size: 4.
    int iFixUpSize;                                             // Offset: 92, Size: 4.
    int iError;                                                 // Offset: 96, Size: 4.
    int iWarnings;                                              // Offset: 100, Size: 4.
    DIS_CMD * lpcCmdData;                                       // Offset: 104, Size: 4.
    DIS_OP_DATA dodData[3];                                     // Offset: 108, Size: 54.
}; // Size: 162.


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 function “ScriptFunction”.

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.


Script Walkthroughs

This section provides walkthroughs on how to create scripts for each script set from the ground up.

General

To begin creating any script, you must first create the script file and add it to the script set of your choice. Press Alt-T/R to load the Code Editor. Go to the Script Set menu item and select the script set of your choice. We will be using System. Press Ctrl-N to create a new script file. Press Ctrl-D to add the script file to the current script set (System in our case). Because the file has not been saved, it will prompt you to save it before adding it to the set. Select any file name you want; for the walkthrough we will use Script Walkthroughs.lss.

You should have something very similar to this.

You are now ready to create an actual script.

System Scripts

To write scripts for the MHS system, you must handle the correct events. Events are similar to mIRC script events, such as ON TEXT.

First, create a script file by following the steps outlined in General, if you have not already. Our examples used the Script Walkthroughs.lss file created there.

On_StartUp

First we will handle the On_StartUp event. Just add the following shell code:

void On_StartUp( HWND hWnd ) {
}

You can fill the function with any contents you desire. Here we add a simple Warning( "Starting: %.8X", hWnd ); to show a message box. Press Ctrl-S to save the file.

You should have something similar to this.

When MHS finishes its loading routine after starting, this function will now be called, and a message box will be shown.

Remember that Script Walkthroughs.lss must be added to the System script set. You may save and compile the file, but because this function is only called once when MHS starts, you will not be able to see the results of this function until you restart MHS.


Snippets

This section provides useful code snippets that not only perform useful functions, but also demonstrate how to use the language.

INT GetFileSize( const CHAR * pcFile )
INT GetFileSize( const CHAR * pcFile ) {
    FILE * pfFile = FOpen( pcFile, "rb" );
    if ( pfFile == NULL ) { return 0; }

    FSeek( pfFile, 0, SEEK_END );
    INT iReturn = FTell( pfFile );
    FClose( pfFile );
    return iReturn;
}
Uses the standard functions to get the size of a file. Works exactly the same way as in C.

DWORD GetProcessByName( const CHAR * pcName )

BOOL OpenProcessByName( const CHAR * pcName )

DWORD GetProcessByName( const CHAR * pcName ) {
    // Exactly the same as the Win32 API.
    PROCESSENTRY32 pe32;
    ZeroMemory( &pe32, sizeof( PROCESSENTRY32 ) );
    pe32.dwSize = sizeof( PROCESSENTRY32 );

    HANDLE hSnap = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
    if ( hSnap == INVALID_HANDLE_VALUE ) {          // If the snap fails, error.
        return -1;
    }
    else {
        DWORD dwID = -1;                            // Our default value.
        if ( Process32First( hSnap, &pe32 ) ) {
            do {
                                                    // String compare, case insensitive.
                if ( StrICmp( pe32.szExeFile, pcName ) == 0 ) {
                    dwID = pe32.th32ProcessID;      // We have a match!  Break immediately.
                    break;
                }
            } while ( Process32Next( hSnap, &pe32 ) );
        }
        // As with the Win32 API, we must close our handles.
        // Technically we do not need to do this because the
        //  script will do it for us when this script thread exits,
        //  but that is bad coding practice.
        CloseHandle( hSnap );

        return dwID;
    }
}
This function scans the process list and finds the process specified by pcName. This works exactly the same way as it does in the Win32 API, with StrICmp() being used instead of stricmp().
BOOL OpenProcessByName( const CHAR * pcName ) {
    return LoadProcess( GetProcessByName( pcName ) );
}

After (or before) defining GetProcessByName(), we can easily go one step further and define a function that causes MHS to load a process by its name. Our function returns whatever LoadProcess() returns, which will be 0 if GetProcessByName() returns -1.

Note that functions can be declared in any order, so you can write OpenProcessByName() before writing GetProcessByName().


Feature Examples

These examples demonstrate various important features in the script.

External Variables and Minesweeper
VOID ShowMineSweeperBoard() {
    // Easily work with the RAM of winmine.exe by declaring variables as “extern”.
    extern struct BOARDSIZE {
        INT iWidth;
        INT iHeight;
    }               g_bwSize            = { "winmine.exe", 0x5334 };    // The width and height of the board.
    extern BYTE     g_pbBoard[32*32]    = { "winmine.exe", 0x5361 };    // The board array itself.
                                                                        //  Note that 32*32 here is meaningless,
                                                                        //  except to show us how large the array
                                                                        //  is in the target process (it does not
                                                                        //  actually declare the array in the target
                                                                        //  process; it already exists).

    CHAR * pcRow = Malloc( g_bwSize.iWidth + 1 );                       // Allocate a string long enough to handle
                                                                        //  the longest row print-out.
                                                                        //  No cast is required; it is done for us.

    PrintF( "%d×%d", g_bwSize.iWidth, g_bwSize.iHeight );               // Print the width and height of the board.
    for ( INT J = 0; J < g_bwSize.iHeight; J++ ) {
        for ( INT I = 0; I < g_bwSize.iWidth; I++ ) {
            pcRow[I] = (g_pbBoard[(32*J)+I] & 0x80) ? '1' : '0';        // Set the current character to a 1 or a 0
                                                                        //  depending on whether there is a mine
                                                                        //  here or not.  1 indicates a mine.
        }
        pcRow[g_bwSize.iWidth] = '\0';                                  // Terminate with NULL.
        PrintF( pcRow );                                                // Print the line.
    }
    Free( pcRow );                                                      // Just as in C, you must free your resources.
}

This example demonstrates using external variables to easily work with the RAM of the target process. All variable operators work on external variables the same way as they do normal variables. The difference is that the value of the variable is held in WinMine.exe (in this case) instead of locally in our own application.

Calling this function would yield output similar to that in the image on the left.

Following the output, we can solve the board easily.

Hotkey Handler
// Handle hotkeys by creating a On_HK_* function and placing a number instead of the
//  asterisk.
VOID On_HK_23( DWORD dw1, DWORD dw2 ) {
/**Parm 1--^ Parm 2--^  Parm 3--^       */

    Clear();        // Clear the output window.

    // dw1 and dw2 are parameters you define in the Options dialog when you set the hotkey
    //  there.
    PrintF( "%u×%u=%u", dw1, dw2, dw1 * dw2 );
}

To create a function to be called at the press of a hotkey, first define the function itself as shown above. The name of the function must be On_HK_* where the asterisk can be any positive decimal number. This number must match that of the Parm 1 when you create the hotkey itself in the Options dialog.

Remember to recompile!

To open the hotkey manager, select the Tools/Options menu item.

First press the New button to create a new hotkey.

Next select the ScriptFunction function.

Now select the key you wish to use to activate the script function. Key F3 is used in our example, with no modifiers.

Our function is named On_HK_23, so set Parm 1 to 23.

In our example function, we just print the product of two numbers, which are defined by Parm 2 and Parm 3. Enter any two numbers you want to see multiplied.

Hit OK.

If you remembered to recompile the script, all you need to do now is press F3 and view the following print:

34×48=1632

Generally you will use hotkeys to activate most of the script functions you have written. For example:

VOID On_HK_23( DWORD dw1, DWORD dw2 ) {
    ShowMineSweeperBoard();
}

Now pressing F3 would instead call our example ShowMineSweeperBoard() function, which will in turn print the Minesweeper board for us (if available).

Creating/Using Dialogs
VOID ShowDialog() {
    // Create a dialog.
    HWND hDialog = CreateDialog(
        NULL,               // No owner.
        "L. Spiro",         // Window title.
        90, 89, 115, 150,   // Position and size.
        "" );               // No function for handling messages this time.

    if ( hDialog == NULL ) { return; }

    // Enumerate the control ID’s. 0 is not a valid control ID, so start at 1.
    enum {
        CTRL_BUTTON = 1,
        CTRL_CHECK,
        CTRL_EDIT,
        CTRL_COMBO,
    };

    // Add a button to it.
    if ( !AddButton( hDialog,
        20, 20, 75, 23,
        CTRL_BUTTON, "Close",
        "ButtonProc" ) ) { return; }

    // Add a check to it.
    if ( !AddCheck( hDialog,
        20, 50, 75, 16,
        CTRL_CHECK, "Click Me",
        "CheckProc" ) ) { return; }

    // Add an edit to it.
    if ( !AddEdit( hDialog,
        20, 70, 75, 20,
        CTRL_EDIT,
        "EditProc" ) ) { return; }

    // Add a drop-list combo box to it.
    if ( !AddDropCombo( hDialog,
        20, 95, 75, 21,
        CTRL_COMBO,
        "ComboProc" ) ) { return; }

    // Drop-list combo boxes are useless without
    //  things to select!
    if ( !AddComboItem( GetDlgItem( hDialog, CTRL_COMBO ),
        "Hi", 3 ) ) { return; }
    if ( !AddComboItem( GetDlgItem( hDialog, CTRL_COMBO ),
        "Hello", 4 ) ) { return; }
    if ( !AddComboItem( GetDlgItem( hDialog, CTRL_COMBO ),
        "Greetings", 5 ) ) { return; }
}

// Our function to handle the Close button.
VOID ButtonProc( HWND hOwner, HWND hButton ) {
    DestroyWindow( hOwner );    // This function was assigned to the Close
                                //  button, so destroy the window.
}

// Our function to handle the check click.
VOID CheckProc( HWND hOwner, HWND hCheck, BOOL bChecked ) {
    if ( bChecked ) {       // bChecked indicates the state after the button press.
        PrintF( "Check!" );
    }
}

// Our function for handling edit-box changes.
VOID EditProc( HWND hOwner, HWND hEdit ) {
    // Get the whole line of text and print it.
    INT iLength = GetWindowTextLength( hEdit ) + 1;
    CHAR * pcText = Malloc( iLength );
    GetWindowText( hEdit, pcText, iLength );
    PrintF( "Text changed to “%s”.", pcText );
    Free( pcText );         // Free your resources!
}

// Our function for handling combo-box selection changes.
VOID ComboProc( HWND hOwner, HWND hCombo, const _TCHAR * ptcText, DWORD dwData ) {
    // Print the text that was selected in the combo box.
    PrintF( "%s: %u", ptcText, dwData );
    // For fun, set the edit box to this text.
    SetWindowText( GetDlgItem( hOwner, CTRL_EDIT ), ptcText );

    // That was really fun!
}

This demonstrates how easy it is to create dialogs and handle messages created by their controls.

Call ShowDialog() from a hotkey and you will get a dialog such as the one on the left.

Try pressing the different buttons and see what happens. Then modify this example code to suite your own needs and make your own dialog interfaces. Creating dialog interfaces is the first step in making a trainer.

Nearly all of the functions available for working with dialogs are exactly the same as they are in the Win32 API, so not many of them need to be covered here.

Creating Threads
VOID MainThread() {
    Clear();

    // Create a random parameter.
    DWORD dwParm = Random( 90 );
    // Create the thread with this parameter.  We write “SecondThread” ourselves.
    HANDLE hThread = CreateThread( "SecondThread", dwParm );

    // It may have failed!
    if ( !hThread ) {
        PrintF( "Creating the second thread failed." );
        return;
    }

    // It succeeded!
    PrintF( "Second thread created with parameter %u.", dwParm );

    // The second thread is active while this one is active.
    // Let’s demonstrate this by holding this thread in a loop for one whole second.
    //  During this second, the other thread should also get its run, printing a
    //  line of text somewhere in the middle of all the text printed in our loop.
    DWORD dwTime = Time();
    DWORD dwCounter = 0;
    while ( Time() - dwTime < 1000 ) {
        if ( dwCounter++ % 100000 == 0 ) {
            PrintF( "\tIn loop, counter is %u.  Time left: %u.",
                dwCounter, 1000 - (Time() - dwTime) );
        }
    }
    PrintF( "Done." );

    // If CreateThread() is successful, the valid handle must be closed!
    CloseHandle( hThread );
}

// And this is the function called by CreateThread() above.
// dwParm is the parameter passed in CreateThread() and can be whatever we want it to
//  be—a number, a pointer, whatever.  If more parameters are needed than just one,
//  organize them into a structure and pass a pointer to the structure to
//  CreateThread() (the pointer will be cast to a DWORD, then you can cast it back
//  inside your second thread).
DWORD SecondThread( DWORD dwParm ) {
    // All we need to do for this example is print the parameter passed to us.
    PrintF( "From Second Thread: %u.", dwParm );
}

This demonstrates how to call your script functions in new threads.

By examining the output:

Second thread created with parameter 71.
    In loop, counter is 1.  Time left: 1000.
    In loop, counter is 100001.  Time left: 941.
From Second Thread: 71.
    In loop, counter is 200001.  Time left: 897.
    In loop, counter is 300001.  Time left: 853.
    In loop, counter is 400001.  Time left: 808.
    In loop, counter is 500001.  Time left: 763.
    In loop, counter is 600001.  Time left: 716.
    In loop, counter is 700001.  Time left: 671.
    In loop, counter is 800001.  Time left: 624.
    In loop, counter is 900001.  Time left: 576.
    In loop, counter is 1000001.  Time left: 529.
    In loop, counter is 1100001.  Time left: 480.
    In loop, counter is 1200001.  Time left: 431.
    In loop, counter is 1300001.  Time left: 382.
    In loop, counter is 1400001.  Time left: 274.
    In loop, counter is 1500001.  Time left: 221.
    In loop, counter is 1600001.  Time left: 169.
    In loop, counter is 1700001.  Time left: 117.
    In loop, counter is 1800001.  Time left: 67.
    In loop, counter is 1900001.  Time left: 13.
Done.

we can see that the second thread was able to execute at the same time as the first thread, printing its information while the first thread was still in its loop.

Thread safety can be enforced by creating synchronized functions. In a single script set, only one synchronized function can be executed at a time. If any thread is inside a synchronized function and another thread tries to enter any synchronized function, the second thread must wait for the first thread to leave its current synchronized function. This does not mean that if a thread enters a synchronized function, all other threads must stop and wait for it to leave that function. Waiting only occurs when another thread wants to enter a synchronized function. This allows you to ensure that certain data can only be accessed by one thread at a time, avoiding thread races.

See Functions.

While a thread is running the script set can not be stopped and recompiled. Care must be taken to ensure all threads are stopped at the appropriate times. If a thread is created at start-up and allowed to run until shut-down, the only way to compile the scripts after changes would be to shut down and restart MHS. Typically threads should check a global variable, and when the variable is FALSE the thread would exit. A hotkey would be used to set the variable to FALSE. This allows you to end a thread with a hotkey.

A thread is exited when the function called on that thread returns.


Search Examples

These examples demonstrate how to perform automated searches with the scripts.

bool FloatSearch( float fFrom, float fTo )
// Search for either an exact float or a range of floats.
// If fFrom and fTo are the same, the search is for an
//  exact value.
bool FloatSearch( float fFrom, float fTo ) {
    // The structure holds all the information we will
    //  pass to the function.
    MHS_API_SEARCH_PARMS pSearch;

    // Firstly, we are doing a data-type search.
    pSearch.dwType = LS_DATATYPE;

    // Cover the full user-mode range.
    pSearch.lpvStart = (LPVOID)0;
    pSearch.lpvEnd = (LPVOID)0x80000000;

    // Values are nearly always aligned.
    pSearch.bAligned = true;

    // We are doing searches for floats, so use MT_FLOAT.
    // Valid types are:
    //  MT_CHAR
    //  MT_BYTE
    //  MT_SHORT
    //  MT_USHORT
    //  MT_LONG
    //  MT_ULONG
    //  MT_INT
    //  MT_INT64
    //  MT_UINT64
    //  MT_FLOAT
    //  MT_DOUBLE
    pSearch.TypeParms.dtParms.dwDataType = MT_FLOAT;

    // This is the evaluation method used in the search.
    //  We are doing an exact-value search if the two floats are equal
    //  and a range otherwise.
    // Valid values are:
    //  ST_EXACT
    //  ST_RANGE
    //  ST_LOWERTHAN
    //  ST_GREATERTHAN
    //  ST_UNKNOWN
    pSearch.TypeParms.dtParms.dwSubType = (fFrom == fTo) ? ST_EXACT : ST_RANGE;

    // No need for a Same as Original sub search.
    pSearch.TypeParms.dtParms.bEnableSame = false;

    // Now the actual values for which we are scanning.
    pSearch.TypeParms.dtParms.atValue.Float = fFrom;
    // This value is ignored if we use ST_EXACT, so just set it
    //  anyway.  Only ST_RANGE uses this value.
    pSearch.TypeParms.dtParms.atTo.Float = fTo;

    // Now search.
    return RAMSearch( &pSearch );
}

This demonstrates a standard search for float-type values. In our function, we search for an exact value if the two floats are the same, or a range of floats specified by the two values if not.

bool FindPointer( LPVOID lpvLowestItemInStruct )
// Search for a pointer.
// This function does a bit of logical processing
//  for us.  It accepts the lowest known address in
//  a structure and creates a suitable search range.
bool FindPointer( LPVOID lpvLowestItemInStruct ) {
    // The structure holds all the information we will
    //  pass to the function.
    MHS_API_SEARCH_PARMS pSearch;

    // Firstly, we are doing a pointer search.
    pSearch.dwType = LS_POINTER;

    // Cover the full user-mode range.
    pSearch.lpvStart = (LPVOID)0;
    pSearch.lpvEnd = (LPVOID)0x80000000;

    // pSearch.bAligned is not used in Pointer Searches.

    // Now to the pointer-specific data.

    // We will search for a range of pointers.
    pSearch.TypeParms.pParms.dwSubType = ST_RANGE;

    // We don't need the Same as Original sub search.
    pSearch.TypeParms.pParms.bEnableSame = false;

    // We are only interested in static pointers.
    pSearch.TypeParms.pParms.bStatic = true;

    // Save the offset from the value passed to us.
    // This should always be done in Pointer Searches.
    //  This allows us to see how far away pointers are
    //  from this addres in the address list.
    pSearch.TypeParms.pParms.lpvSaveOffset = lpvLowestItemInStruct;

    // Fill the range of the search.
    // A good range is (target address - 0x5000).
    // Set the low value to this.
    pSearch.TypeParms.pParms.atValue.UInt = (DWORD)lpvLowestItemInStruct - 0x5000;

    // Set the high value to the actual value specified.
    pSearch.TypeParms.pParms.atTo.UInt = (DWORD)lpvLowestItemInStruct;

    // Now search.
    return RAMSearch( &pSearch );
}

This allows us to easily perform the most common Pointer Search. Pointer Searches are almost always done using the range evaluation, with the top of the range being the address of the lowest item in the structure you know, and the Save Offset From (pSearch.TypeParms.pParms.lpvSaveOffset) being the same value. The bottom of the range is usually 0x5000 lower, but can be more in some cases, especially in large games.

Now all you have to specify is an address inside the structure for which you want to find pointers.

bool FindFF7String( const CHAR szString[] )
// Search for a string.
// This function is specific to Final Fantasy VII,
//  which uses a proprietary format for storing its
//  strings.  Each character is 32 bytes lower and
//  they terminate with 0xFF.
bool FindFF7String( const CHAR szString[] ) {
    // If we aren't running Final Fantasy VII, we won't
    //  find any strings this way.
    if ( StrICmp( GetCurProcessName(), "FF7.exe" ) != 0 ) {
        Error( "Open FF7.exe for this to work (currently hacking %s).",
            GetCurProcessName() );
        return false;
    }

    // The structure holds all the information we will
    //  pass to the function.
    MHS_API_SEARCH_PARMS pSearch;

    // Firstly, we are doing a string search.
    pSearch.dwType = LS_STRING;

    // Cover the full user-mode range.
    pSearch.lpvStart = (LPVOID)0;
    pSearch.lpvEnd = (LPVOID)0x80000000;

    // Do not align the search.
    pSearch.bAligned = false;

    // Now to the data specific to the string search.

    // First we have to give it a pointer to the actual string
    //  to find.
    pSearch.TypeParms.sParms.pcValue = szString;

    // Set the length of the string.  Note that this
    //  is what determines how long the string is rather
    //  than a NULL character.  This means strings can
    //  have the NULL character inside them.  The search
    //  is actually binary.
    pSearch.TypeParms.sParms.dwLength = StrLen( szString );

    // The data in the target process is not Unicode,
    //  so do not perform a Unicode search.
    pSearch.TypeParms.sParms.bUnicode = false;

    // We will use offsets.
    pSearch.TypeParms.sParms.bOffsets = true;

    // Final Fantasy VII stores strings as offset
    //  -32, so set our offsets accordingly.
    pSearch.TypeParms.sParms.bOffFrom = -32;
    pSearch.TypeParms.sParms.bOffTo = -32;

    // Now search.
    return RAMSearch( &pSearch );
}

This function demonstrates string searching and using string offsets. Final Fantasy VII stores strings in offset form, where each character is -32 of what we see.

So “Cloud” becomes “#LOUD”, where each character has been shifted down 32 bytes.

FindFF7String() allows you to find these strings easily by typing them as you see them. The scanner itself will convert them to offsets used by Final Fantasy VII and scan for those. You should get six cases of “Cloud”.

bool SetResultsTo0()
// Scan through the search results and do something with them.
bool SetResultsTo0() {
    // In this function, we assume the last scan was
    //  initialized by FloatSearch() from above, but
    //  this function works on any scan as long as
    //  the parameters are adjusted to match.

    Clear();

    // Store the total number of returns to a local
    //  variable for speed.
    DWORD dwTotal = GetScanTotal();

    // Before reading any of the values we need to lock the scanner.
    // This MUST be matched with a call to UnlockScanForRead() if successful.
    if ( !LockScanForRead() ) { return false; }
    // Now scan through them.
    for ( DWORD I = 0; I < dwTotal; I++ ) {
        // This is what will hold the address of each return.
        LPVOID lpvAddress;
        // And this will hold the actual value.
        //  We use float type because that is the type
        //  of each value when we call FloatSearch().
        // If you use another type of search, just change
        //  this type to the appropriate one.
        float fValue;

        // Now get the value/address at this index.
        if ( GetScanValue( I, &lpvAddress, &fValue ) ) {
            // Print the value for demonstration.
            PrintF( "%.3u: Address: %.8X.  Value: %f",
                I, lpvAddress, fValue );
            // Remember that fValue here is the value at the
            //  time of the search.

            // Let's set them to 0.
            // Use extern.
            extern float e_fValue = { "", (DWORD)lpvAddress };
            // e_fValue now stores the CURRENT value of the float
            //  here.  Remember that fValue is the value at the
            //  time of the search, so changing the actual value
            //  will not do anything to the values next time we
            //  call GetScanValue().

            // Let's save its current value for printing later.
            float fCur = e_fValue;
            // And set the real value to 0.0f;
            e_fValue = 0.0f;

            // Print the changes we have made:
            PrintF( "Before: %f.  After: %f.",
                fCur, e_fValue );

            // Remember that some areas in RAM are read-only, so
            //  the value may not change at all.
            // Also remember that randomly changing values in the target
            //  process can cause strange behavior and even crashes.

        }
    }
    // Remember to call UnlockScanForRead() or the searcher may not
    //  work properly afterwards.
    return UnlockScanForRead();
}

This demonstrates scanning through the results after a search and performing some type of operation on them.

Typically you may want to do something more advanced than just setting them to 0, and you would usually want to perform additional logic before changing them. However, in any case, you would want to use extern variables as shown here to make any types of modifications simple.

The following would set a scan to a hotkey function and search for floats between the values passed by the hotkey (order does not matter, and they are cast to float autmatically). It would then set all found results to 0.0f.

void On_HK_1( DWORD dw1, DWORD dw2 ) {
    FloatSearch( dw1, dw2 );
    SetResultsTo0();
}

bool DemonstrateSubSearch()
// Perform a sub search to filter invalid
//  results from the list.
bool DemonstrateSubSearch() {
    // We will first perform an Unknown search
    //  for unisgned longs, giving us every unsigned
    //  long in the game.
    // We will then wait 1 second and search again for
    //  all values that have changed.
    // This gives us a small list of values that are constantly
    //  changing.

    // The structure holds all the information we will
    //  pass to the function.
    MHS_API_SEARCH_PARMS pSearch;

    // Firstly, we are doing a data-type search.
    pSearch.dwType = LS_DATATYPE;

    // Cover the standard range.
    pSearch.lpvStart = (LPVOID)0x00400000;
    pSearch.lpvEnd = (LPVOID)0x80000000;

    // Align the search.
    pSearch.bAligned = true;

    // Search for all unsigned longs.
    pSearch.TypeParms.dtParms.dwDataType = MT_ULONG;

    // We want to find all of them, so use ST_UNKNOWN.
    pSearch.TypeParms.dtParms.dwSubType = ST_UNKNOWN;

    // No need for a Same as Original sub search.
    pSearch.TypeParms.dtParms.bEnableSame = false;

    // No need to fill the atValue and atTo members.

    // Now search.
    if ( !RAMSearch( &pSearch ) ) { return false; }

    // Wait one second.
    Sleep( 1000 );

    // Now search again for values that have changed.
    MHS_API_SUB_SEARCH_PARMS sspParms;

    // The search evaluation type is SST_DIF, for
    //  values that are different from what they were
    //  in the search above.
    // Valid values are:
    //  SST_EXACT
    //  SST_INCREASED
    //  SST_DECREASED
    //  SST_RANGE (not valid after Pointer Searches)
    //  SST_SAME
    //  SST_DIF
    //  SST_CHANGEDBY
    //  SST_SAMEASORIG (valid only if enabled from the initial search)
    sspParms.dwSearchType = SST_DIF;

    // Since we are not doing SST_EXACT, SST_RANGE, or SST_CHANGEDBY,
    //  we do not need to fill out sspParms.atValue or sspParms.atTo.
    return RAMSubSearch( &sspParms );
}

This function demonstrates performing a Data-Type Search followed by a Different Sub Search. We use Sleep( 1000 ) to wait and give the values time to change.

The initial search applies most of the searching options, such as type (data type, pointer, or string), data type (unsigned long in this case), etc. By the time we are ready to perform a sub search, there are only 3 parameters we must provide at most, and in this case only one.

Since we are using SST_DIF, we only need to supply sspParms.dwSearchType.

Script Searching (Basic)
INT MultiRangeSearch( LPVOID lpvAddress, LPVOID lpvBuffer, INT iSize ) {
    // There is a Range search already, but there are many times when
    //  we want to add values that fall into multiple sets of ranges
    //  instead of just one.

    // The searching routine will pass us the address, buffered value,
    //  and the size of the buffer, and we can then check it in any way
    //  we please, giving us ultimate control over the search.

    // If the buffer size is fixed, we return any non-zero number to indicate
    //  that it should be added to the list.
    // If the buffer size is variable, the return of this function indicates
    //  its size, and again 0 indicates it should not be added to the list.

    // In this basic example, we simply add DWORD values depending on
    //  whether they fall into one of multiple ranges we specify.
    DWORD dwThis = *(DWORD *)lpvBuffer;
    // Check our first range: lower-case letters.
    if ( dwThis >= 'a' && dwThis <= 'z' ) { return 1; }  // Returning any number
                                                        //  besides 0 causes the
                                                        //  value to be added to
                                                        //  the list.
    // Check our next range: upper-case letters.
    if ( dwThis >= 'A' && dwThis <= 'Z' ) { return 1; }

    // Return 0 to indicate it should not be added to the list.
    return 0;
}

This function is a Callback Function used to check items during an initial search. Items to be checked are completely proprietary; it is up to the script to make sense of what the item is. In this case, it is a DWORD.

To work with any other type of data rather than a DWORD, use the following formula:

X xThis = *(X *)lpvBuffer;

Where the X is the type of data.

To work with structures rather than basic data types, use this formula instead:

X * xThis = lpvBuffer;

From here, use -> to access the members of xThis.

For this search to run correctly, set the data size (fixed) to 4. The alignment should also be 4.

INT MultiRangeSubSearch( LPVOID lpvAddress, LPVOID lpvCur, DWORD dwCurSize, LPVOID lpvOld, DWORD dwOldSize ) {
    // This time find DWORD values that are the same as they were during the
    //  last search.
    DWORD dwCur = *(DWORD *)lpvCur; // Value as of now.
    DWORD dwOld = *(DWORD *)lpvOld; // Value at the time of the last search.
    return dwCur == dwOld;  // If they are not the same, this causes them to be
                            //  removed from the list.
}

This sub-search handler is intended to be used after MultiRangeSearch(), and therefore we know that both the current and the old values are DWORD values. We use the same formula as above to turn them into DWORD values.

The criteria for this sub search is that the value now must be the same as it was during the last search. Items that do not meet this criteria are removed.

VOID MultiRangeDecoder( LPVOID lpvAddress, LPVOID lpvBuffer, DWORD dwLength, CHAR * pcReturn, INT iMaxLength ) {
    // The search is taylored towards DWORD values that are between
    //  a and z and A and Z, as characters.

    // Sounds confusing, but take it one step at a time.
    // The search is for DWORD values.
    DWORD dwValue = *(DWORD *)lpvBuffer;

    // These DWORD values are treated as characters, either upper-case
    //  or lower-case.
    CHAR cChar = dwValue;

    // Now print the result, including the whole DWORD value too.
    // Always use SNPrintF() here to be sure you do not write past the
    //  end of the supplied text buffer!
    SNPrintF( pcReturn, iMaxLength, "%c (%.8X)", cChar, dwValue );
}

After a search, the found values must be displayed in the Found Address list. The Found Address list will call our script function and allow us to print each of the results into a text string for display. If we do not supply a Result-Display Function in the Script Search dialog (see Script Search), the data will be decoded as a hex string by default.

This function is optional, but it gives us a chance to display more information about the returns in the last search that may help us analyze the data.

MultiRangeDecoder() simply prints the value as a character and a DWORD (in hex form). Remember that in MultiRangeSearch() we found DWORD values that are also letters.

VOID MultiRangeSearchSetup( INT * piDataSize, INT * piAlign, CHAR ** ppcCallback, CHAR ** ppcDecoder ) {
    // This callback function allows us to keep all the information
    //  for a search type in a single location, together.
    // The searcher only needs to know 4 things to perform the scan,
    //  one of which is optional too.

    // It needs to know the size of the data we are seeking.  Set this
    //  to 0 to indicate variable size.  Our multi-range search scans for
    //  DWORDs, so we set it to sizeof( DWORD ) here.
    (*piDataSize) = sizeof( DWORD );

    // It needs to know how many bytes to skip between each address it
    //  checks.  This is the alignment.
    (*piAlign) = 4;

    // It needs to know our script-defined function that will be used to
    //  check the items and decide which should be added to the list.
    (*ppcCallback) = "MultiRangeSearch";

    // Lastly, we have the option of using a custom display function to
    //  represent the items in the Found Addresses list.
    // This is optional, and can be set to "" if not used.
    (*ppcDecoder) = "MultiRangeDecoder";
}

If we use Programmatic in the Script Search dialog and supply this function in the Setup Function field, this function will give MHS the information it needs to correctly execute our multiple-range letter search.

It sets the data size to sizeof( DWORD ) (4) because we are looking for DWORD values.

It sets the alignment to 4 for speed. We don’t need to check every address since DWORD values are usually only on every 4th address anyway.

It supplies our MultiRangeSearch function as the callback and our MultiRangeDecoder as the decoder for printing the Found Address list.


WinSocks Examples

These examples are helpful for creating network connections to perform actions across networks or over the Internet.

Create a Server
VOID ServerTest() {
    // WSAStartup() is called by the system at startup and is not available in
    //  the scripts.
    // The system also automatically calls WSACleanup(), so this function is
    //  also not available to the scripts.
    // It should always be assumed that the system has already successfully
    //  initialized the WinSocks DLL file.

    SOCKET sServer;
    sServer = socket( AF_INET, SOCK_STREAM, 0 );
    if ( sServer == INVALID_SOCKET ) {
        PrintF( "Call to socket failed." );
        return;
    }

    sockaddr_in saLocal;
    // Populate the sockaddr_in structure.
    saLocal.sin_family              = AF_INET;                  // Address family.
    saLocal.sin_addr.S_un.S_addr    = INADDR_ANY;               // Wildcard IP address.
    saLocal.sin_port                = htons( (USHORT)20248 );   // Port to use.

    // bind() links the socket we just created with the sockaddr_in structure.
    //  It connects the socket with the saLocal address and a specified port.
    // If it returns non-zero, quit, as this indicates an error.
    if ( bind( sServer, &saLocal, sizeof( sockaddr_in ) ) != 0 ) {
        PrintF( "Call to bind failed." );
        // Never leave sockets open.
        closesocket( sServer );
        return;
    }

    // listen() instructs the socket to listen for incoming connections from
    //  clients. The second arguement is the backlog.
    if( listen( sServer, 2 ) != 0 ) {
        closesocket( sServer );
        return;
    }

    // There will also be a sClient socket.
    // We will set the sServer to listen for a sClient connection for 10 seconds,
    //  then exit.
    SOCKET      sClient;
    sockaddr_in saFrom;
    INT         iFromLen = sizeof( sockaddr_in );

    // This loop continuously calls accept() until it finally gets a connection, or
    //  until 10 seconds pass.  This means the incomming connection has 10 seconds
    //  to be made.
    // If a connection is made, the client receives a report of its IP address and
    //  then the connection is closed.
    PrintF( "Creating a server." );
    DWORD dwTime = ProcTime();
    while ( ProcTime() - dwTime < 10 * 1000 ) {
        CHAR szTemp[512];

        // accept() will accept an incoming client connection.
        sClient = accept( sServer,
            &saFrom, &iFromLen );

        if ( sClient != INVALID_SOCKET ) {
            SPrintF( szTemp, "Your IP is %s.\r\n", inet_ntoa( saFrom.sin_addr.S_un.S_addr ) );

            // Just send this string back to the client.
            send( sClient, szTemp, StrLen( szTemp ), 0 );

            // On our end print a message that lets us know it happened.
            PrintF( "Connection from %s.", inet_ntoa( saFrom.sin_addr.S_un.S_addr ) );

            // And close the client socket.
            closesocket( sClient );

            // Leave the loop after a successful connection.
            break;
        }
    }

    // Close the saServer socket.
    closesocket( sServer );
}

This function creates a server on port 20248 and listens for connections for 10 seconds. If a connection is made it reports the IP of the client and closes.

To connect to the server while it is listening for 10 seconds, go to Start/Run and type “telnet 127.0.0.1 20248”. This allows you to make a connection on your own machine. If you run this sample code on another machine, enter the IP of that machine instead of 127.0.0.1.

Always close your sockets with closesocket(). All open sockets are closed when the script is destroyed, for example when recompiling or closing Memory Hacking Software.

Download a File
BOOL DownloadFile( const CHAR * pcServer, const CHAR * pcFile, const CHAR * pcSaveTo ) {
    // First we have to create a socket that will be used for the connection.
    SOCKET sCon;
    sCon = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
    if ( sCon == INVALID_SOCKET ) {
        PrintF( "Failed to create a socket." );
        return FALSE;
    }

    // Now we have to resolve the host name.  If we were supplied an IP instead of
    //  a www.*.* address, we use gethostbyname(), otherwise gethostbyaddr().
    UINT uiAddr;
    hostent * pHp;
    if ( inet_addr( pcServer ) == INADDR_NONE ) {
        pHp = gethostbyname( pcServer );
    }
    else {
        uiAddr = inet_addr( pcServer );
        pHp = gethostbyaddr( (CHAR *)&uiAddr, sizeof( hostent ), AF_INET );
    }
    if ( pHp == NULL ) {                        // Of course it may have failed.
        closesocket( sCon );
        PrintF( "Failed to resolve the host name." );
        return FALSE;
    }

    // Now we have to make the connection using the socket from above
    //  and the server information.
    sockaddr_in saServer;
    saServer.sin_addr.S_un.S_addr   = *((DWORD *)pHp->h_addr_list[0]);
    saServer.sin_family             = AF_INET;
    saServer.sin_port               = htons( 80 );
    if ( connect( sCon, &saServer, sizeof( sockaddr_in ) ) ) {
        closesocket( sCon );
        PrintF( "Failed to create a connection." );
        return FALSE;
    }

    // The connection has been made and we are ready to do.  Create the
    //  actual file where we are to store the download.
    FILE * fSaveTo = FOpen( pcSaveTo, "wb" );
    if ( !fSaveTo ) {
        closesocket( sCon );
        PrintF( "Failed to open the file to where the download will be saved." );
        return FALSE;
    }

    // Create a GET command to send to the server.  This tells it to start sending
    //  the file.
    INT iPathLen = StrLen( pcFile );
    CHAR * pcBuffer = Malloc( (iPathLen + 16) * sizeof( CHAR ) );

    if ( !pcBuffer ) {
        FClose( fSaveTo );
        closesocket( sCon );
        PrintF( "Failed to create a text buffer for the command to get the file!" );
        return FALSE;
    }
    SPrintF( pcBuffer, "GET %s\r\n\r\n", pcFile );      // Create the GET command.
    send( sCon, pcBuffer, StrLen( pcBuffer ), 0 );      // Send the command to the server.

    // Now our downloading loop.  We just use recv() and store the data in chunks into
    //  the file.
    INT iTotal = 0;
    INT iThis = 0;
    BYTE bTemp[512];
    while ( (iThis = recv( sCon, bTemp, 512, 0 )) ) {
        FWrite( bTemp, iThis, 1, fSaveTo );
        iTotal += iThis;
    }

    // Free everything.  We have a socket, open file, and an allocated text buffer.
    Free( pcBuffer );
    FClose( fSaveTo );
    closesocket( sCon );

    // Success!
    PrintF( "Downloaded %u bytes to %s.", iTotal, pcSaveTo );

    // Return the size we downloaded.  This might be 0 if the file size was 0.
    return iTotal;
}

This function downloads a file to a specific location on your hard drive.

pcServer is a www address or an IP. Remember to omit the http:// form this string.

pcFile is the path on the server of the file you wish to download.

Here are examples of calling this function:

DownloadFile( "images.google.com", "/intl/en_ALL/images/images_hp.gif",
    "C:\\images_hp.gif" );

DownloadFile( "www.google.com", "/intl/en_ALL/images/logo.gif",
    "C:\\logo.gif" );


Miscellaneous Examples

These examples are not necessarily important for everyday coding but can be very helpful to have when the time comes. They also serve to demonstrate some of the capabilities within the language.

Demonstrate QSort()
const INT TOTAL_SORTS = 10;     // The total number of items in the array.
VOID DemonstrateQSort() {
    // QSort() can sort any type of array, including those of structures
    //  and unions.  It works the same way as the standard C-library
    //  qsort() but instead of a function pointer, this QSort() accepts
    //  the actual name of the function used to compare the items.

    Clear();                    // Clear the output text.

    // First let’s create an arbitrary structure with items to be sorted.
    struct SORTME {
        CHAR    szName[5];      // Our structure has a name
        DWORD   dwValue;        //  and a value.
    }       sSortStruct[TOTAL_SORTS];

    // We now have TOTAL_SORTS sSortStruct objects.
    // Run through them and fill in their data.

    for ( DWORD I = 0; I < TOTAL_SORTS; I++ ) {
        // Create a 4-letter name.
        for ( INT J = 0; J < 4; J++ ) {
            sSortStruct[I].szName[J] = 'A' + Random( 26 );
        }
        // Cap it with a NULL.
        sSortStruct[I].szName[4] = '\0';

        // Supply a random value.
        sSortStruct[I].dwValue = Random( 20 );

        // Print the structure information.
        PrintF( "Item %u: %u, %s",
            I, sSortStruct[I].dwValue, sSortStruct[I].szName );
    }

    // Let’s see how quickly we can sort this list!
    DWORD dwTime = Time();

    // First sort by name.  We will write a function called SortByName which
    //  is used by QSort() to compare how two items relate to each other.
    if ( !QSort( sSortStruct,       // The address of our list.
            TOTAL_SORTS,            // The total number of items in the list.
            sizeof( SORTME ),       // The size of each item in the list.
            "SortByName"            // The script function used to compare
                                    //  items in the list.  We have to write this.
            ) ) {
        PrintF( "QSort() failed: Invalid comparison function." );
        return;
    }

    // How long did it take?
    PrintF( "%u milliseconds to sort an array of %d items.",
        Time() - dwTime,
        TOTAL_SORTS );

    // Print the list to make sure it is sorted.
    for ( DWORD I = 0; I < TOTAL_SORTS; I++ ) {
        PrintF( "Item %u: %u, %s",
            I, sSortStruct[I].dwValue, sSortStruct[I].szName );
    }

    // Let’s sort by dwValue now.
    dwTime = Time();
    if ( !QSort( sSortStruct,       // The address of our list.
            TOTAL_SORTS,            // The total number of items in the list.
            sizeof( SORTME ),       // The size of each item in the list.
            "SortByValue"           // The script function used to compare
                                    //  items in the list.  We have to write this.
            ) ) {
        PrintF( "QSort() failed: Invalid comparison function." );
        return;
    }

    // How long did it take?
    PrintF( "%u milliseconds to resort an array of %d items.",
        Time() - dwTime,
        TOTAL_SORTS );

    // Print the list to make sure it is sorted again.
    for ( DWORD I = 0; I < TOTAL_SORTS; I++ ) {
        PrintF( "Item %u: %u, %s",
            I, sSortStruct[I].dwValue, sSortStruct[I].szName );
    }
}

// This is our function used to compare two objects.  It must be in the form
//  [name]( void * pvLeft, void * pvRight )
// Note that, of course, the parameter names can be changed,
//  and typedefs can be used (we use LPVOID).
INT SortByName( LPVOID lpvLeft, LPVOID lpvRight ) {
    // We supplied "SortByName" in the first QSort(), and this is the function it will call
    //  to compare two items.
    SORTME * psLeft = lpvLeft;      // Remember, the list we created above was of type
                                    //  SORT, thus the parameters above are of type SORT *.
    SORTME * psRight = lpvRight;    // Automatic typecasting.

    // Now compare each object by name.
    // Return an integer based on which item should be above the other in the sorted
    //  list.
    // 1 = Left goes higher in the list than right.
    // 0 = They are the same.
    // -1 = Left is below right in the sorted list.

    // Luckily, we already have a function that will do this exact type of comparison
    //  for us: StrCmp().
    return StrCmp( psLeft->szName, psRight->szName );
}

// This function compares each object by their data values.
// The function needs two LPVOID parameters as mentioned above,
//  however casting is done for us, so this time let’s take a
//  shortcut and declare them as SORT * types instead.
INT SortByValue( SORTME * psLeft, SORTME * psRight ) {
    // Same return scheme as above, but we don’t have a function
    //  to do it for us.

    // Do it manually.  If they are the same, return 0.
    if ( psLeft->dwValue == psRight->dwValue ) { return 0; }

    // They aren’t the same so return 1 if left is greater and -1
    //  if left is lower.
    return psLeft->dwValue > psRight->dwValue ? 1 : -1;
}

These functions seem long but most of it is commentation.

DemonstrateQSort() fills an arbitrary array of items with random names and random data values, then sorts once by name and once by value. SortByName() and SortByValue() are callback functions that QSort() uses to compare two items in the array to decide how to place them (one before the other or keep them the same). Because these functions are defined by the user, you can sort any type of array using any custom data type you can create. BSearch() works the same way—that is, you must create the callback functions (SortByName() and SortByValue() here) which accept a void * to the left and right elements to be compared.

Aside from how to use QSort() (and BSearch()), there are several key things to note in this example:

  1. Automatic casting: We don’t need to cast the void * (LPVOID) types to SORT * types. It is done for us implicitly.
  2. The SORT structure is global. Structures and unions can be declared anywhere, but are always global, and can be used anywhere, even in code above DemonstrateQSort().

This is an example of the output from calling this function:

Item 0: 3, WYBG
Item 1: 3, NCNF
Item 2: 1, GHOH
Item 3: 3, HZAU
Item 4: 12, YFWX
Item 5: 19, NOVX
Item 6: 9, LYYF
Item 7: 15, EDKD
Item 8: 15, ZRJH
Item 9: 6, IOYD
0 milliseconds to sort an array of 10 items.
Item 0: 15, EDKD
Item 1: 1, GHOH
Item 2: 3, HZAU
Item 3: 6, IOYD
Item 4: 9, LYYF
Item 5: 3, NCNF
Item 6: 19, NOVX
Item 7: 3, WYBG
Item 8: 12, YFWX
Item 9: 15, ZRJH
0 milliseconds to resort an array of 10 items.
Item 0: 1, GHOH
Item 1: 3, HZAU
Item 2: 3, WYBG
Item 3: 3, NCNF
Item 4: 6, IOYD
Item 5: 9, LYYF
Item 6: 12, YFWX
Item 7: 15, EDKD
Item 8: 15, ZRJH
Item 9: 19, NOVX

Search Directories
// This function will search the specified directory for the specified
//  wildcard-string file name.  iSubDirSearchDepth indicates the number of
//  subdirectories deep the search will go.  For example, passing 0 will search
//  only the directory passed in pcPath, while passing 1 will search pcPath
//  and all its folders, but none of those folders’ folders.
// Passing -1 will search all subdirectories of all subdirectories.

// This function fails if you do not put a slash/backslash at the end of pcPath.
INT SearchDir( const CHAR * pcPath, const CHAR * pcFile, INT iSubDirSearchDepth ) {
    // To search directories we use two searches.
    //  One for the files the user wants and one to go into directories (if
    //  iSubDirSearchDepth is not 0).
    // For the first search, create the final path based on what the user wants
    //  to find.
    CHAR szFinal[MAX_PATH];
    SNPrintF( szFinal, MAX_PATH, "%s%s", pcPath, pcFile );

    // The structure to accept the return from Find*File().
    WIN32_FIND_DATA wfdFind;

    // Now the actual search handle used to find the files the user wants.
    // This search will not go into subdirectories because not all subdirectories
    //  will be matched by the wildcard string in pcFile.
    // In other words, if the user wants to find all files named “*.mp3”, this
    //  search will not find all subdirectories unless they all end with “.mp3”,
    //  which is highly unlikely.

    // So the search is broken into two parts.
    // First we create a scan string to match only what the user wants and we ignore
    //  folders that are returned during this scan.
    // Then we create a second search which will simply replace pcFile with “*” and
    //  will allow us to find all subdirectories.
    // We call this function recursively on all subdirectories, ignore the files
    //  returned by that scan.

    // Keep track of how many we find.
    INT iFound = 0;

    // Get the handle for the search for the files the user wants.
    HANDLE hFind = FindFirstFile( szFinal, &wfdFind );

    // If the handle is not invalid, continue.
    if ( hFind != INVALID_HANDLE_VALUE ) {
        while ( hFind ) {
            // “.” and “..” can be returned, so ignore them.
            if ( StrCmp( wfdFind.cFileName, "." ) != 0 && StrCmp( wfdFind.cFileName, ".." ) != 0 ) {
                // If it is a directory, ignore it for now.  We will get to it in our second
                //  search.  Check that the FILE_ATTRIBUTE_DIRECTORY flag is NOT set.
                if ( (wfdFind.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0 ) {
                    // If we come here it is a file, not a directory.
                    // Increase the found count and print the file name.
                    iFound++;

                    // Optionally print the file we found.
                    // This will be very slow if many files are found!

                    // Otherwise, replace this with your own code for operating on
                    //  wfdFind.cFileName.

                    CHAR szFile[MAX_PATH];
                    SNPrintF( szFile, MAX_PATH, "%s%s", pcPath, wfdFind.cFileName );
                    PrintF( "%s", szFile );
                }
            }

            // Get the next file.
            if ( !FindNextFile( hFind, &wfdFind ) ) {
                // No next file?  Close the handle and leave the loop.
                FindClose( hFind );
                hFind = NULL;
                break;
            }
        }
    }   // hFind != INVALID_HANDLE_VALUE

    // If we are not to search subdirectories, we are done.
    if ( iSubDirSearchDepth == 0 ) { return iFound; }

    // If we come here, it means we are supposed to search subdirectories too.
    // The only way to get all subdirectories is to remake the scan string using
    //  “*”.
    SNPrintF( szFinal, MAX_PATH, "%s*", pcPath );
    hFind = FindFirstFile( szFinal, &wfdFind );
    if ( hFind != INVALID_HANDLE_VALUE ) {
        while ( hFind ) {
            if ( StrCmp( wfdFind.cFileName, "." ) != 0 && StrCmp( wfdFind.cFileName, ".." ) != 0 ) {
                // This time we only care about directories
                if ( wfdFind.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) {
                    // Recompile the string and pass it right back to this function in a
                    //  recursive call.
                    CHAR szDir[MAX_PATH];
                    SNPrintF( szDir, MAX_PATH, "%s%s\\", pcPath, wfdFind.cFileName );
                    //PrintF( "%s", szDir );

                    // Call ourself and add the return.
                    // Pass iSubDirSearchDepth - 1 since we are going one folder deeper.
                    iFound += SearchDir( szDir, pcFile, iSubDirSearchDepth - 1 );
                }
            }

            // Get the next file.
            if ( !FindNextFile( hFind, &wfdFind ) ) {
                FindClose( hFind );
                hFind = NULL;
                break;
            }
        }
    }   // hFind != INVALID_HANDLE_VALUE

    // We are done, and iFound contains the total matching files.
    return iFound;
}

This function seems long, and it is. This function will search the directories on your hard drive for whatever you would like to find. This demonstrates recursion as well.

An example call to this function may look like this:

INT iFound = SearchDir( "C:\\", "*.dll", 1 );

This would search all directories in C: up to one folder deep for DLL files, list the returns in the window, and return the number of files found. Specify -1 to search all folders at any depth.

Be careful not to modify the current file while its handle is still valid. For example, when a file is found, it is not safe to delete that file immediately. Instead continue to the next file in the search before deleting.

Minesweeper Bot
VOID SolveMinesweeperBoard() {
    // “extern” only works if we are attached to the process.
    if ( StrICmp( GetCurProcessName(), "winmine.exe" ) != 0 ) {
        PrintF( "You must be attached to Minesweeper to solve the board!" );
        return;
    }

    HWND hWnd = FindWindow( "Minesweeper" );
    if ( !hWnd ) { return; }

    // If the user is holding certain keys it will cause problems when we try to send clicks
    //  to the Minesweeper board.  Release problematic keys.  Even if the user is still holding
    //  them, this forces them to be “released”.
    KeyboardEvent( VK_SHIFT, KEYEVENTF_KEYUP );
    KeyboardEvent( VK_CONTROL, KEYEVENTF_KEYUP );
    KeyboardEvent( VK_MENU, KEYEVENTF_KEYUP );

    // Put the Minesweeper board in front if possible.
    ShowWindowAsync( hWnd, SW_RESTORE );
    while ( GetForegroundWindow() != hWnd ) { SetForegroundWindow( hWnd ); Sleep( 10 ); }

    // Get its position.
    INT iX;
    INT iY;
    GetWindowPos( hWnd, &iX, &iY );
    PrintF( "Minesweeper board located at %d %d.", iX, iY );
    if ( iX < 0 || iY < 0 ) {
        PrintF( "Minesweeper board is out of range!" );
        return;
    }

    extern struct BOARDSIZE {
        INT iWidth;
        INT iHeight;
    }               e_bwSize            = { "winmine.exe", 0x5334 };    // The width and height of the board.
    extern BYTE     e_pbBoard[32*32]    = { "winmine.exe", 0x5361 };    // The board array itself.
                                                                        //  Note that 32*32 here is meaningless,
                                                                        //  except to show us how large the
                                                                        //  array is in the target process (it
                                                                        //  does not actually declare the array
                                                                        //  in the target process; it already
                                                                        //  exists).

    POINT pClick;
    // To calculate the final click positions we need the screen
    //  resolution.
    INT iScreenW = GetPrimScreenWidth();
    INT iScreenH = GetPrimScreenHeight();

    // System settings change the location of mines on the board, so
    //  get the settings needed to know where to click.
    INT iBorderHeight = GetSystemMetrics( SM_CYEDGE );      // Window border (height).
    INT iCaptionHeight = GetSystemMetrics( SM_CYCAPTION );  // Title bar height.
    INT iMenuHeight = GetSystemMetrics( SM_CYMENU );        // Menu height.
    INT iFinalYOffset = iBorderHeight + iCaptionHeight + iMenuHeight;
    INT iFinalXOffset = GetSystemMetrics( SM_CXEDGE );      // Window border (width).

    // Save the externs to locals.  This makes it faster to scan the
    //  board.
    INT iBoardWidth = e_bwSize.iWidth;
    INT iBoardHeight = e_bwSize.iHeight;

    // Run through the board and click on each square that has no mine.
    for ( INT J = 0; J < iBoardHeight; J++ ) {
        for ( INT I = 0; I < iBoardWidth; I++ ) {
            if ( (e_pbBoard[(32*J)+I] & 0x80) == 0 ) {
                GetClickPos( iFinalXOffset, iFinalYOffset, iX, iY, &pClick, I, J );
                // Translate the click position into the actual screen coordinates (Windows®
                //  forces us to do this).
                pClick.x = pClick.x * 65535.0f / iScreenW;
                pClick.y = pClick.y * 65535.0f / iScreenH;

                // Move there.
                MouseEvent( MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE,
                    pClick.x, pClick.y,
                    0 );

                // Click there (down and up).
                MouseEvent( MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_LEFTDOWN,
                    0, 0,                           // X and Y positions aren’t used.
                    0 );
                MouseEvent( MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_LEFTUP,
                    0, 0,
                    0 );

                // Optionally sleep for a bit in case you like to see the action unfold.
                //Sleep( 1 );
            }
        }
    }

}

// Constants that tell us the dimensions of the board.
const INT iSquareSize   = 16;                       // Size of the actual squares we click.
const INT iXOff         = 14;                       // Offset to the left-most square from the border.
const INT iYOff         = 56;                       // Offset to the top-most square from the bottom of the menu.
const INT iFinalX       = iXOff + (iSquareSize / 2);// The final click offset (X).
const INT iFinalY       = iYOff + (iSquareSize / 2);// The final click offset (Y).
VOID GetClickPos( INT iScreenOffX, INT iScreenOffY, INT iPosX, INT iPosY, POINT * ppRet, INT iX, INT iY ) {
    // The window is at (iPosX, iPosY).  From there, go to the corner of the board
    //  and then offset based on which square we are hitting.
    // iScreenOffX and iScreenOffY are derived by using system settings to get the
    //  border, caption, and menu widths/heights.
    ppRet->x = iPosX + iX * iSquareSize + iFinalX + iScreenOffX;
    ppRet->y = iPosY + iY * iSquareSize + iFinalY + iScreenOffY;
}

This will create a bot that will play Minesweeper for you, quickly hitting all of the correct squares and skipping squares with mines.

First we must check that we are actually attached to Minesweeper because extern will not work otherwise. Then we check for the Minesweeper window, which should never fail if we are actually attached to it.

When we press the hotkey to activate this script, the keys we press will still be held when the script begins executing. That means it is possible that we are holding Ctrl, Alt, or Shift while the script is trying to send mouse clicks. These buttons can cause problems, so we use KeyboardEvent() to force these buttons to be released.

We then force Minesweeper to be in the front so our clicks actually go to the board and not some other window.

Next we create the extern variables we need to access the board. Then we get a few system parameters that are required to calculate where the board actually is given the top-left corner of the board. Menu heights and border widths will cause the actual board to be in different locations on different machines. The values we obtain here allow this script to work on all machines, regardless of their settings.

Finally, we scan the board and click when we need to click. This can solve any board of any complexity.

Hook Detection
// Some programs try to interfere with the normal behavior of MHS, typically
//  by installing hooks.  These hooks often cause system instability as well
//  and interfere with other programs when placed in the kernel.
// MHS comes with a library for detecting hooks, and being script-based means
//  the hooks can be handled in any way the user pleases.

// This function recursively lists hooks given a starting address and the number
//  of calls deep to search.  Note that hook detection is prone to error, always,
//  so some false positives will be thrown.  Particularly, jump tables are always
//  treated as hooks.  Most of these returns are false positives (jump tables are
//  used when functions are imported from exteral DLL files), but it is required
//  to return them since there is no way to distinguish between them and real
//  jump-table hooks (modifying the jump table to jump to the attacker’s code
//  instead of to the imported DLL function.
VOID ListHooks( MHS_ADDRESS aAddress, INT iCallsDeep, BOOL bResetList ) {
    // The first call to this function should always pass TRUE for
    //  bResetList.  This function keeps a list of checked functions
    //  to avoid rechecking them millions of times.  Passing TRUE
    //  resets the list, which is desirable when we start a new scan.
    if ( bResetList ) { ResetHooks(); }
    // Call our custom function to see if we already checked this
    //  address.
    DWORD dwIndex;
    if ( HasHook( aAddress, &dwIndex ) ) { return; }    // Checked it.
    // Add it to the list to never be checked again.
    AddHook( aAddress );

    // Create a FUNC_HOOK_INFO object which stores all the information
    //  for all hooks found in a function.
    LPFUNC_HOOK_INFO lpfhiInfo = CreateFuncHookInfo();
    if ( !lpfhiInfo ) { return; }

    // Get all the hook information from a function.
    switch ( GetFuncHookInfo( aAddress, lpfhiInfo ) ) {
        case DS_HOOKFOUND : {
            // Print a header.  Makes it easier to read the recursive
            //  information.
            PrintF( "=== %.8I64X (%d) ===", aAddress, iCallsDeep );
            PrintF( "%u hook(s) found in %.8I64X!", GetFuncHookTotalHooks( lpfhiInfo ), aAddress );

            // For each hook found, print its information.
            for ( DWORD I = 0; I < GetFuncHookTotalHooks( lpfhiInfo ); I++ ) {
                PrintHook( aAddress, GetFuncHookHook( lpfhiInfo, I ) );
            }
            break;
        }
        // We don’t handle this case because it really clutters the screen.
        /*case DS_HOOKNOTFOUND : {
            PrintF( "No hooks in %.8I64X!", aAddress );
            break;
        }*/
        case DS_FAILURE : {
            // This usually only happens when a jump-table hook is found that leads to
            //  an unrecognized module (kernel-mode only).
            // The scanner has no choice but to follow the code into the unknown module
            //  and from there is is unable to obtain the appropriate information for
            //  detecting hooks.
            // Although this reports to be an internal error, it is usually an indication
            //  of a malicious jump-table hook and if this is encountered it should be
            //  studied in more detail.
            PrintF( "Internal error on address %.8I64X!", aAddress );
        }
    }
    // If we are to scan at least one more call-depth deeper, do it.
    if ( iCallsDeep ) {
        for ( DWORD I = 0; I < GetFuncHookTotalCalls( lpfhiInfo ); I++ ) {
            // Don’t reset the function list!
            ListHooks( GetFuncHookCall( lpfhiInfo, I ), iCallsDeep - 1, FALSE );
        }
    }

    // We are done with the hook information.  Do not leave it hanging around.
    DestroyFuncHookInfo( lpfhiInfo );

    // If this was a base scan, we also don’t need the function list.
    if ( bResetList ) { ResetHooks(); }
}

// A function for printing a single hook.
VOID PrintHook( MHS_ADDRESS aOriginalAddress, LPHOOK_INFO lphiInfo ) {
    // If the hook type is not recognized it is usually an error in parsing
    //  the function and it can cause a lot of clutter.  Just skip it.
    if ( lphiInfo->htType == HT_OTHER ) { return; }

    // If it is an API function, show its name.
    if ( lphiInfo->szApiName[0] ) {
        PrintF( "\tAddress: %s+%d (%.8I64X)",
            lphiInfo->szApiName, lphiInfo->dwApiOffset, lphiInfo->aAddress );
    }
    // Otherwise just the address.
    else {
        PrintF( "\tAddress: %.8I64X+%d (%.8I64X)",
            aOriginalAddress, lphiInfo->dwApiOffset, lphiInfo->aAddress );
    }

    // Size of the hook.
    PrintF( "\tSize: %u", lphiInfo->dwSize );

    // If it is a jump-table hook, lphiInfo->pbHookBytes and lphiInfo->pbRealBytes
    //  will be NULL.  Nothing to print in that case (and trying to do so will
    //  cause a crash).
    if ( lphiInfo->htType != HT_JMPTABLE ) {
        CHAR * pcHex = Malloc( lphiInfo->dwSize * 3 + 1 );
        PrintF( "\tCurrent Bytes: %s", CreateHexString( lphiInfo->pbHookBytes, lphiInfo->dwSize, pcHex ) );
        PrintF( "\tReal Bytes: %s", CreateHexString( lphiInfo->pbRealBytes, lphiInfo->dwSize, pcHex ) );
        Free( pcHex );
    }

    // The type of hook.
    switch ( lphiInfo->htType ) {
        case HT_JMP : {
            // A JMP to another module (inline hook).
            // For example, 84A790EF: JMP BF8043E9 ; Jumps to another module.
            PrintF( "\tType:  JMP (to %.8I64X)", lphiInfo->aJmpAddress );
            break;
        }
        case HT_JMPTABLE : {
            // A JMP that uses a DWORD address pointer, and the address points
            //  to a different module.
            // For example, 84A790EF: JMP [8474AE30] ; The value at address 8474AE30 is BF8043E9.
            PrintF( "\tType:  JMP Table (to %.8I64X)", lphiInfo->aJmpAddress );
            break;
        }
        default : {
            // Something else.  We don’t actually come here.
            PrintF( "\tType:  Other" );
        }
    }
    // The address of the module that placed the hook.
    if ( lphiInfo->aHookerAddress ) {
        if ( lphiInfo->szHookerModule[0] ) {
            PrintF( "\tHooker: %s (%.8I64X)", lphiInfo->szHookerModule, lphiInfo->aHookerAddress );
        }
        else {
            PrintF( "\tHooker: (%.8I64X)", lphiInfo->aHookerAddress );
        }
    }
    // And the module that was hooked.
    PrintF( "\tHooked Module: %s", lphiInfo->szHookedModule );
}

// A helper for creating strings of hex text.
CHAR * CreateHexString( PBYTE pbArray, DWORD dwSize, CHAR * pcRet ) {
    INT iPos = 0;
    for ( DWORD I = 0; I < dwSize; I++ ) {
        iPos += SPrintF( &pcRet[iPos], "%.2X ", pbArray[I] );
    }
    return pcRet;
}

// Keeping track of the functions we have visited requires 3 variables.
MHS_ADDRESS *   g_paProcessedHooks = 0;
DWORD           g_dwTotalHooks = 0;
DWORD           g_dwAllocHooks = 0;
// And notice that these functions are synchronized for thread safety.
// There are helper functions and will not receive extra commentation.
synchronized VOID AddHook( MHS_ADDRESS aHook ) {
    DWORD dwIndex;
    if ( HasHook( aHook, &dwIndex ) ) { return; }
    if ( g_dwTotalHooks == g_dwAllocHooks ) {
        MHS_ADDRESS * paNew = ReAlloc( g_paProcessedHooks, sizeof( MHS_ADDRESS ) * (g_dwAllocHooks + 0x10) );
        if ( !paNew ) { return; }
        g_paProcessedHooks = paNew;
        g_dwAllocHooks += 0x10;
    }
    MemMove( &g_paProcessedHooks[dwIndex+1], &g_paProcessedHooks[dwIndex], sizeof( MHS_ADDRESS ) * (g_dwTotalHooks - dwIndex) );
    g_paProcessedHooks[dwIndex] = aHook;
    g_dwTotalHooks++;
}
synchronized BOOL HasHook( MHS_ADDRESS aHook, DWORD * pdwIndex ) {
    for ( DWORD I = 0; I < g_dwTotalHooks; I++ ) {
        if ( g_paProcessedHooks[I] == aHook ) { (*pdwIndex) = I; return TRUE; }
        if ( aHook > g_paProcessedHooks[I] ) { (*pdwIndex) = I; return FALSE; }
    }
    (*pdwIndex) = g_dwTotalHooks;
    return FALSE;
}
synchronized VOID ResetHooks() {
    Free( g_paProcessedHooks );
    g_paProcessedHooks = NULL;
    g_dwTotalHooks = 0;
    g_dwAllocHooks = 0;
}

// Finally, a function that shows some common hooks and provides a very easy
//  way to add and remove addresses to be checked.
VOID ShowHooks() {
    struct SHOW_US {
        BOOL    bKernel;
        CHAR *  pcDll;
        CHAR *  pcFunc;
        INT     iDepth;
    } suList[] = {
        { FALSE, "Kernel32.dll",    "ReadProcessMemory",        4 },
        { FALSE, "Kernel32.dll",    "WriteProcessMemory",       4 },
        { FALSE, "Kernel32.dll",    "VirtualProtectEx",         4 },

        { TRUE, "",                 "NtOpenProcess",            4 },
        { TRUE, "",                 "KeStackAttachProcess",     4 },
        { TRUE, "",                 "KeAttachProcess",          4 },
        { TRUE, "",                 "NtProtectVirtualMemory",   4 },

        // Add more addresses here following the same methods as above.
    };

    // Run the list and print hooks.
    for ( DWORD I = 0; I < sizeof( suList ) / sizeof( suList[0] ); I++ ) {
        PrintF( "==== Showing %s ====", suList[I].pcFunc );
        if ( suList[I].bKernel ) {
            ListHooks( GetKernelProcAddress( suList[I].pcFunc ), suList[I].iDepth, TRUE );
        }
        else {
            ListHooks( GetProcAddress( suList[I].pcDll, suList[I].pcFunc ), suList[I].iDepth, TRUE );
        }
    }
}

This illustrates how to use the hook-detecting functions in MHS. Call ShowHooks() from any Hotkey to see hooks on the API that are most commonly hooked.

You can easily modify suList at the bottom to add more functions to be checked.


Script Function Reference

I/O
FClose(FILE * fFile)
FEof(FILE * fFile)
FError(FILE * fFile)
FFlush(FILE * fFile)
FGetC(FILE * fFile)
FGetS(char * pcString, int iMax, FILE * fFile)
FGetWC(FILE * fFile)
FGetWS(wchar_t * pwString, int iMax, FILE * fFile)
FOpen(const char * pcFileName, const char * pcMode)
FPrintF(FILE * fFile, const char * pcFormat, ...)
FPutC(char cChar, FILE * fFile)
FPutS(const char * pcString, FILE * fFile)
FPutWC(wchar_t wChar, FILE * fFile)
FPutWS(const wchar_t * pwString, FILE * fFile)
FRead(void * pvBuffer, unsigned int uiSize, unsigned int uiCount, FILE * fFile)
FScanF(FILE * fFile, const char * pcFormat, ...)
FSeek(FILE * fFile, long lOffset, int iOrigin)
FTell(FILE * fFile)
FWPrintF(FILE * fFile, const wchar_t * pwFormat, ...)
FWScanF(FILE * fFile, const wchar_t * pwFormat, ...)
FWrite(const void * pvBuffer, unsigned int uiSize, unsigned int uiCount, FILE * fFile)
WFOpen(const wchar_t * pwFileName, const wchar_t * pwMode)

Math
ACos(double dVal)
ASin(double dVal)
ATan(double dVal)
ATan2(double dY, double dX)
Abs(double dVal)
Abs64(int64 i64Val)
Ceil(double dVal)
Cos(double dVal)
CosH(double dVal)
Exp(double dVal)
Floor(double dVal)
Log(double dVal)
Max(double dLeft, double dRight)
Min(double dLeft, double dRight)
Mod(double dLeft, double dRight)
Pow(double dLeft, double dRight)
Random(int iMax)
Sin(double dVal)
SinH(double dVal)
Sqrt(double dVal)
Tan(double dVal)
TanH(double dVal)

Memory
Free(void * pvMemory)
Malloc(unsigned long ulCount)
MemCmp(void * pvBuf1, void * pvBuf2, unsigned long ulCount)
MemCpy(void * pvDest, const void * pvSource, unsigned long ulCount)
MemMove(void * pvDest, const void * pvSource, unsigned long ulCount)
MemSet(void * pvDest, char cValue, unsigned long ulCount)
ReAlloc(void * pvOrig, unsigned long ulCount)
WMemCmp(short * pwBuf1, short * pwBuf2, unsigned long ulCount)
WMemCpy(short * pwDest, const short * pwSource, unsigned long ulCount)
WMemMove(short * pwDest, const short * pwSource, unsigned long ulCount)
WMemSet(short * pwDest, short wValue, unsigned long ulCount)

MHSAntiAntiCheat
CreateFuncHookInfo()
CreateHookInfo()
DestroyFuncHookInfo(LPFUNC_HOOK_INFO lpfhiInfo)
DestroyHookInfo(LPHOOK_INFO lphiInfo)
GetCurrentSdtFuncAddress(DWORD dwFuncId)
GetCurrentSdtTableAddress()
GetFuncHookCall(LPFUNC_HOOK_INFO lpfhiInfo, DWORD dwIndex)
GetFuncHookHook(LPFUNC_HOOK_INFO lpfhiInfo, DWORD dwIndex)
GetFuncHookInfo(MHS_ADDRESS aAddress, LPFUNC_HOOK_INFO lpfhiReturn)
GetFuncHookTotalCalls(LPFUNC_HOOK_INFO lpfhiInfo)
GetFuncHookTotalHooks(LPFUNC_HOOK_INFO lpfhiInfo)
GetHookInfo(MHS_ADDRESS aAddress, LPHOOK_INFO lphiReturn)
GetKernelProcAddress(const CHAR * pcFunc)
GetNtOsKrnlName(CHAR * pcRet)
GetNtOsKrnlPath(CHAR * pcRet)
GetNtOsKrnlRange(MHS_ADDRESS * paStart, MHS_ADDRESS * paEnd)
GetProcAddress(const CHAR * pcModule, const CHAR * pcFunc)
GetSdtFuncAddress(DWORD dwFuncId)
GetSdtFuncIndex(DWORD dwFuncId)
GetSdtFuncName(DWORD dwFuncId, CHAR * pcRet)
GetSdtTableAddress()
GetSdtTotalFuncs()

MHSAssembly
AutoAssemble(const CHAR * pcString)
CreateDisObj()
DestroyDisObj(DIS_DISASM * lpdDestroyMe)
Disasm(BYTE * pbSource, DWORD dwSourceSize, D_ADDRESS aEip, DIS_DISASM * lpddDis, INT iMode, DWORD dwOpts)

MHSDebugging
AddBreakpoint(LPSCRIPT_ADD_BP lpbpBreakpoint, PDWORD pdwId)
AttachDebugger()
DebugFile(LPSTR lpstrFile, LPSTR lpstrParms, BOOL bPause)
DetachDebugger()
EnterSingleStep()
FindModuleByAddress(MHS_ADDRESS aAddress, PMODULEENTRY32 pme32Return)
FindModuleByName(const CHAR * pcName, PMODULEENTRY32 pme32Return)
FindModuleByRange(MHS_ADDRESS aAddress, PMODULEENTRY32 pme32Return)
GetRemoteFuncAddress(const CHAR * pcModule, const CHAR * pcFunction)
InjectDll(const CHAR * pcPath, BOOL bAutoUninject)
IsDebugging()
IsStepping()
RemoveBreakpoint(DWORD dwId)
StepOnce()
StepOver()
StopStepping()

MHSSearch
AddAddress(LPADDADDRESS lpaAddress)
GetLastEvaluationType()
GetLastScanDataType()
GetLastScanType()
GetScanTotal()
GetScanValue(DWORD dwIndex, LPVOID * lppvAddress, LPVOID lpvBuffer)
HaveScanned()
LockScanForRead()
RAMSearch(LPMHS_API_SEARCH_PARMS lpsParms)
RAMSubSearch(LPMHS_API_SUB_SEARCH_PARMS lpsParms)
SaOSubScanEnabled()
SetSearchRange(MHS_ADDRESS aStart, MHS_ADDRESS aEnd)
UnlockScanForRead()

MHSSys
CallLocalFunction(LOCAL_CALL_RETURN * prcrRet, LPVOID lpvFuncAddress, DWORD dwExtra, INT iParms, ...)
CastExpression(LPEVAL_RET_TYPE lpertExp, INT iType)
Clear()
Error(const CHAR * pcFormat, ...)
EvalExp(const CHAR * pcExpression, LPEVAL_RET_TYPE lpertRet, BOOL bTreatAsHex)
GetCrc(const CHAR * pcPath)
GetCurProcessHandle()
GetCurProcessID()
GetCurProcessName()
GetCurProcessPEproc()
GetCurProcessPath(_TCHAR * ptcReturn)
GetProcessNameByPEproc(LPVOID lpEprocAddress, CHAR * pcBuffer)
InputFloat(const _TCHAR * ptcTitle, const _TCHAR * ptcCaption, const _TCHAR * ptcDefault, DOUBLE * pdReturn)
InputFloat(const _TCHAR * ptcTitle, const _TCHAR * ptcCaption, const _TCHAR * ptcDefault, FLOAT * pfReturn)
InputNumber(const _TCHAR * ptcTitle, const _TCHAR * ptcCaption, const _TCHAR * ptcDefault, INT * piReturn)
InputString(const _TCHAR * ptcTitle, const _TCHAR * ptcCaption, const _TCHAR * ptcDefault, _TCHAR * ptcReturn, INT iMaxLen)
LoadProcess(DWORD dwID)
LoadProcess(DWORD dwID, BOOL bRestricted)
MessageBox(DWORD dwMask, const CHAR * pcTitle, CHAR * pcFormat, ...)
ReadLocalMemory(LPVOID lpBaseAddress, LPVOID lpBuffer, SIZE_T nSize, SIZE_T * lpNumberOfBytesRead)
RegisterHotkey(INT iKey, INT iMod, INT iFunc, DWORD dwParms[3], BOOL bPoll)
UnregisterHotkey(INT iId)
Warning(const CHAR * pcFormat, ...)
WriteLocalMemory(LPVOID lpBaseAddress, LPVOID lpBuffer, SIZE_T nSize, SIZE_T * lpNumberOfBytesWritten)

Searching & Sorting
BSearch(LPCVOID lpvKey, LPCVOID lpvBase, SIZE_T nNum, SIZE_T nWidth, const TCHAR * ptcCompareFunc)
QSort(LPCVOID lpvBase, SIZE_T nNum, SIZE_T nWidth, const TCHAR * ptcCompareFunc)
RegExMatch(const CHAR * pcRegEx, const CHAR * pcCheck, INT * piLength)

String
AtoF(const char * pcString1)
AtoI(const char * pcString1)
AtoI64(const char * pcString1)
PrintF(const char * pcFormat, ...)
SNPrintF(char * pcBuffer, unsigned long ulCount, const char * pcFormat, ...)
SNWPrintF(short * pwBuffer, unsigned long ulCount, const short * pwFormat, ...)
SPrintF(char * pcBuffer, const char * pcFormat, ...)
SScanF(const char * pcBuffer, const char * pcFormat, ...)
SWPrintF(short * pwBuffer, const short * pwFormat, ...)
SWScanF(const short * pwBuffer, const short * pwFormat, ...)
StrCat(char * pcDest, const char * pcSource)
StrChr(const char * pcString, char cChar)
StrCmp(const char * pcString1, const char * pcString2)
StrCpy(char * pcDest, const char * pcSource)
StrDup(const char * pcString)
StrICmp(const char * pcString1, const char * pcString2)
StrLen(const char * pcString)
StrNCat(char * pcDest, const char * pcSource, unsigned long ulCount)
StrNCmp(const char * pcString1, const char * pcString2, unsigned long ulCount)
StrNCpy(char * pcDest, const char * pcSource, unsigned long ulCount)
StrNICmp(const char * pcString1, const char * pcString2, unsigned long ulCount)
StrRChr(const char * pcString, char cChar)
StrSpn(const char * pcString, const char * pcCharSet)
StrStr(const char * pcString, const char * pcFind)
ToLower(char cChar)
ToUpper(char cChar)
ToWLower(short wChar)
ToWUpper(short wChar)
WcsCat(short * pwDest, const short * pwSource)
WcsChr(const short * pwString, short wChar)
WcsCmp(const short * pwcString1, const short * pwcString2)
WcsCpy(short * pwcDest, const short * pwcSource)
WcsDup(const short * pwcString)
WcsICmp(const short * pwcString1, const short * pwcString2)
WcsLen(const short * pwcString)
WcsNCat(short * pwDest, const short * pwSource, unsigned long ulCount)
WcsNCmp(const short * pwcString1, const short * pwcString2, unsigned long ulCount)
WcsNCpy(short * pwcDest, const short * pwcSource, unsigned long ulCount)
WcsNICmp(const short * pwcString1, const short * pwcString2, unsigned long ulCount)
WcsRChr(const short * pwString, short wChar)
WcsSpn(const short * pwString, const short * pwCharSet)
WcsStr(const short * pwString, const short * pwFind)
WtoF(const short * pwcString1)
WtoI(const short * pwcString1)
WtoI64(const short * pwcString1)

System
KeyboardEvent(BYTE bVk, DWORD dwFlags)
MouseEvent(DWORD dwFlags, DWORD dx, DWORD dy, DWORD dwData)
ProcTime()
SendInput(UINT uiInputs, LPINPUT pInputs)
Sleep(DWORD dwMilliseconds)
Time()
Type(TCHAR * ptcChar)
Type(TCHAR tcChar)
VkKeyScan(TCHAR tcChar)

Win32Debugging
CheckRemoteDebuggerPresent(HANDLE hProcess, PBOOL pbDebuggerPresent)
IsDebuggerPresent()
ReadProcessMemory(HANDLE hProcess, LPCVOID lpBaseAddress, LPVOID lpBuffer, SIZE_T nSize, SIZE_T* lpNumberOfBytesRead)
WriteProcessMemory(HANDLE hProcess, LPVOID lpBaseAddress, LPCVOID lpBuffer, SIZE_T nSize, SIZE_T* lpNumberOfBytesWritten)

Win32Dialogs
AddButton(HWND hWnd, INT iX, INT iY, INT iWidth, INT iHeight, INT iID, const _TCHAR * ptcText, const _TCHAR * ptcActionHandler)
AddCheck(HWND hWnd, INT iX, INT iY, INT iWidth, INT iHeight, INT iID, const _TCHAR * ptcText, const _TCHAR * ptcActionHandler)
AddComboItem(HWND hWnd, const _TCHAR * ptcText, DWORD dwData)
AddDropCombo(HWND hWnd, INT iX, INT iY, INT iWidth, INT iHeight, INT iID, const _TCHAR * ptcSelectionHandler)
AddEdit(HWND hWnd, INT iX, INT iY, INT iWidth, INT iHeight, INT iID, const _TCHAR * ptcActionHandler)
AddLabel(HWND hWnd, INT iX, INT iY, INT iWidth, INT iHeight, INT iID, const _TCHAR * ptcText)
AddStatic(HWND hWnd, INT iX, INT iY, INT iWidth, INT iHeight, INT iID, const _TCHAR * ptcText)
AddUpDown(HWND hWnd, INT iX, INT iY, INT iWidth, INT iHeight, INT iID, INT iLow, INT iHigh, INT iPos, const _TCHAR * ptcActionHandler)
AddUpDown(HWND hWnd, INT iX, INT iY, INT iWidth, INT iHeight, INT iID, INT iPos, const _TCHAR * ptcActionHandler)
AddUpDown(HWND hWnd, INT iX, INT iY, INT iWidth, INT iHeight, INT iID, const _TCHAR * ptcActionHandler)
BeginPaint(HWND hWnd, LPPAINTSTRUCT lpPaint)
CaptureScreen(const LPRECT lprRegion, const _TCHAR * ptcFile)
CheckDlgButton(HWND hWnd, INT iID, UINT uiCheck)
ClipCursor(const RECT * lpRect)
CloseWindow(HWND hWnd)
CreateDialog(HWND hOwner, const CHAR * pcTitle, INT iX, INT iY, INT iWidth, INT iHeight, const TCHAR * ptcHandler)
CreateWindow(const CHAR * pcTitle, INT iX, INT iY, INT iWidth, INT iHeight, const TCHAR * ptcHandler)
DestroyWindow(HWND hWnd)
DrawBitmap(HDC hDC, const CHAR * pcFile, INT iX, INT iY, BOOL bTrans)
DrawCaption(HDC hDC, LPCTSTR lpString, int nCount, LPRECT lpRect)
DrawLine(HDC hDC, COLORREF crColor, INT iX1, INT iY1, INT iX2, INT iY2)
DrawLine(HDC hDC, COLORREF crColor, LPPOINT lpStart, LPPOINT lpEnd)
DrawLine(HDC hDC, INT iX1, INT iY1, INT iX2, INT iY2)
DrawLine(HDC hDC, LPPOINT lpStart, LPPOINT lpEnd)
DrawRect(HDC hDC, COLORREF crOutter, COLORREF crInner, INT iLeft, INT iTop, INT iRight, INT iBottom)
DrawRect(HDC hDC, COLORREF crOutter, COLORREF crInner, LPCRECT lpcRect)
DrawRect(HDC hDC, INT iLeft, INT iTop, INT iRight, INT iBottom)
DrawRect(HDC hDC, LPCRECT lpcRect)
DrawText(HDC hDC, LPCTSTR lpString, int nCount, LPRECT lpRect, UINT uFormat)
EndPaint(HWND hWnd, const PAINTSTRUCT * lpPaint)
EnumChildWindows(HWND hWndParent, LPCSTR lpEnumFunc, LPARAM lParam)
EnumWindows(LPCSTR lpEnumFunc, LPARAM lParam)
FindWindow(LPCTSTR lpClassName, LPCTSTR lpWindowName)
FindWindow(LPCTSTR lpWindowName)
FindWindowEx(HWND hWnd, HWND hwndChildAfter, LPCTSTR lpClassName, LPCTSTR lpWindowName)
GetActiveWindow()
GetBkColor(HDC hDC)
GetClientHeight(HWND hWnd)
GetClientRect(HWND hWnd, LPRECT lpRect)
GetClientWidth(HWND hWnd)
GetCursorPos(LPPOINT lpPoint)
GetDlgItem(HWND hWnd, INT iID)
GetFocus()
GetForegroundWindow()
GetPixel(HDC hDC, INT nXPos, INT nYPos)
GetTextColor(HDC hDC)
GetWindowDC(HWND hWnd)
GetWindowHeight(HWND hWnd)
GetWindowPos(HWND hWnd, INT * piX, INT * piY)
GetWindowRect(HWND hWnd, LPRECT lpRect)
GetWindowSize(HWND hWnd, INT * piWidth, INT * piHeight)
GetWindowText(HWND hWnd, LPTSTR lpString, int nMaxCount)
GetWindowTextLength(HWND hWnd)
GetWindowWidth(HWND hWnd)
HIWORD(DWORD dwParam)
IsDlgButtonChecked(HWND hWnd, INT iID)
IsWindow(HWND hWnd)
IsWindowEnabled(HWND hWnd)
IsWindowVisible(HWND hWnd)
LOWORD(DWORD dwParam)
MAKELONG(WORD wLow, WORD wHigh)
MAKEWORD(BYTE bLow, BYTE bHigh)
PostMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
RGB(DWORD dwR, DWORD dwG, DWORD dwB)
ReleaseDC(HWND hWnd, HDC hDC)
SendDlgItemMessage(HWND hDlg, INT nIDDlgItem, UINT Msg, WPARAM wParam, LPARAM lParam)
SendMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
SetActiveWindow(HWND hWnd)
SetBkColor(HDC hDC, COLORREF crColor)
SetCursorPos(INT iX, INT iY)
SetFocus(HWND hWnd)
SetForegroundWindow(HWND hWnd)
SetPixel(HDC hDC, INT nXPos, INT nYPos, COLORREF crColor)
SetTextColor(HDC hDC, COLORREF crColor)
SetWindowPos(HWND hWnd, INT iX, INT iY)
SetWindowSize(HWND hWnd, INT iWidth, INT iHeight)
SetWindowText(HWND hWnd, LPCTSTR lpString)
ShowWindow(HWND hWnd, INT nCmdShow)
ShowWindowAsync(HWND hWnd, INT nCmdShow)

Win32Files
CopyFile(LPCTSTR lpExistingFileName, LPCTSTR lpNewFileName, BOOL bFailIfExists)
CreateDirectory(LPCTSTR lpPathName)
DeleteFile(LPCTSTR lpFileName)
FindClose(HANDLE hObject)
FindFirstFile(LPCTSTR lpFileName, LPWIN32_FIND_DATA lpFindFileData)
FindNextFile(HANDLE hFindFile, LPWIN32_FIND_DATA lpFindFileData)
MoveFile(LPCTSTR lpExistingFileName, LPCTSTR lpNewFileName)
PathCanonicalize(LPTSTR lpszDst, LPCTSTR lpszSrc)
PathCombine(LPTSTR lpszDest, LPCTSTR lpszDir, LPCTSTR lpszFile)
PathIsURL(LPCTSTR pszPath)
PathQuoteSpaces(LPTSTR pszPath)
PathRelativePathTo(LPTSTR pszPath, LPCTSTR pszFrom, DWORD dwAttrFrom, LPCTSTR pszTo, DWORD dwAttrTo)
PathRemoveArgs(LPTSTR pszPath)
PathRemoveBackslash(LPTSTR pszPath)
PathRemoveBlanks(LPTSTR lpszString)
PathRemoveExtension(LPTSTR pszPath)
PathRemoveFileSpec(LPTSTR pszPath)
PathRenameExtension(LPTSTR pszPath, LPCTSTR pszExt)
RemoveDirectory(LPCTSTR lpPathName)

Win32Memory
VirtualAllocEx(HANDLE hProcess, LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect)
VirtualFreeEx(HANDLE hProcess, LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType)
VirtualProtectEx(HANDLE hProcess, LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect)
VirtualQueryEx(HANDLE hProcess, LPVOID lpAddress, PMEMORY_BASIC_INFORMATION lpBuffer, SIZE_T dwLength)
ZeroMemory(PVOID Destination, SIZE_T Length)

Win32PerfMon
CreateToolhelp32Snapshot(DWORD dwFlags, DWORD th32ProcessID)
Heap32First(LPHEAPENTRY32 lphe, DWORD th32ProcessID, ULONG_PTR th32HeapID)
Heap32ListFirst(HANDLE hSnapshot, LPHEAPLIST32 lphl)
Heap32ListNext(HANDLE hSnapshot, LPHEAPLIST32 lphl)
Heap32Next(LPHEAPENTRY32 lphe)
Module32First(HANDLE hSnapshot, LPMODULEENTRY32 lpme)
Module32Next(HANDLE hSnapshot, LPMODULEENTRY32 lpme)
Process32First(HANDLE hSnapshot, LPPROCESSENTRY32 lppe)
Process32Next(HANDLE hSnapshot, LPPROCESSENTRY32 lppe)
Thread32First(HANDLE hSnapshot, LPTHREADENTRY32 lpte)
Thread32Next(HANDLE hSnapshot, LPTHREADENTRY32 lpte)

Win32Sys
BrowseForFolder(LPBROWSEINFO lpbiInfo)
CloseHandle(HANDLE hObject)
Execute(HWND hWnd, LPCTSTR lpOperation, LPCTSTR lpFile, LPCTSTR lpParameters, LPCTSTR lpDirectory, INT nShowCmd)
GetPathFromIDList(PCIDLIST_ABSOLUTE pidlaList, LPTSTR pszPath)
GetPrimScreenHeight()
GetPrimScreenWidth()
GetSystemMetrics(INT nIndex)

Win32Threading
CallRemoteFunction(REMOTE_CALL_RETURN * prcrRet, LPVOID lpvFuncAddress, DWORD dwExtra, INT iParms, ...)
CallRemoteFunction(REMOTE_CALL_RETURN * prcrRet, LPVOID lpvFuncAddress, DWORD dwExtra, UINT_PTR * puiRegisters, INT iParms, ...)
CreateRemoteThread(HANDLE hProcess, LPVOID lpStartAddress, LPVOID lpParameter, LPDWORD lpThreadId)
CreateRemoteThreadAndGetReturn(BOOL * pbSuccess, HANDLE hProcess, LPVOID lpStartAddress, LPVOID lpParameter)
CreateThread(const _TCHAR * ptcFunc, DWORD dwParm)
FreeLibrary(HMODULE hModule)
GetCurrentThread()
GetCurrentThreadId()
GetEproc(DWORD dwId)
GetExitCodeThread(HANDLE hThread, LPDWORD lpExitCode)
GetModuleHandle(LPCTSTR lpModuleName)
GetThreadContext(HANDLE hThread, LPCONTEXT lpContext)
GetThreadPriority(HANDLE hThread)
LoadLibrary(LPCTSTR lpFileName)
LoadLibraryEx(LPCTSTR lpFileName, DWORD dwFlags)
OpenProcess(DWORD dwDesiredAccess, DWORD dwProcessId)
OpenThread(DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwThreadId)
ResumeThread(HANDLE hThread)
SetThreadContext(HANDLE hThread, const LPCONTEXT lpContext)
SetThreadPriority(HANDLE hThread, int nPriority)
SuspendThread(HANDLE hThread)
TerminateThread(HANDLE hThread, DWORD dwExitCode)
WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds)

WinSocks
WSAGetLastError()
accept(SOCKET sSocket, const sockaddr_in * psaAddr, INT * iAddrlen)
bind(SOCKET sSocket, const sockaddr_in * psaName, INT iNamelen)
closesocket(SOCKET sSocket)
connect(SOCKET sSocket, const sockaddr_in * saName, INT iNameLen)
gethostbyaddr(const CHAR * pcAddr, INT iLen, INT iType)
gethostbyname(const CHAR * pcName)
gethostname(CHAR * pcName, INT iNameLen)
getpeername(SOCKET sSocket, sockaddr_in * psaName, INT * piNameLen)
getprotobyname(const CHAR * pcName)
getprotobynumber(INT iNumber)
getservbyname(const CHAR * pcName, const CHAR * pcProto)
getservbyport(INT iPort, const CHAR * pcProto)
getsockname(SOCKET sSocket, sockaddr_in * psaName, INT * piNameLen)
getsockopt(SOCKET sSocket, INT iLevel, INT iOptName, const CHAR * pcOptVal, INT * piOptLen)
htonl(ULONG ulHostLong)
htons(USHORT usHostShort)
inet_addr(const CHAR * pcAddr)
inet_ntoa(ULONG iaAddr)
ioctlsocket(SOCKET sSocket, LONG lCmd, ULONG * pulArgp)
listen(SOCKET sSocket, INT iBacklog)
recv(SOCKET sSocket, CHAR * pcBuf, INT iLen, INT iFlags)
recvfrom(SOCKET sSocket, CHAR * pcBuf, INT iLen, INT iFlags, sockaddr_in * pasFrom, INT * piFromLen)
select(INT iNfds, fd_set * pfsReadFds, fd_set * pfsWriteFds, fd_set * pfsExceptFds, timeval * ptTimeout)
send(SOCKET sSocket, const CHAR * pcBuf, INT iLen, INT iFlags)
sendto(SOCKET sSocket, CHAR * pcBuf, INT iLen, INT iFlags, sockaddr_in * psaTo, INT iToLen)
setsockopt(SOCKET sSocket, INT iLevel, INT iOptName, const CHAR * pcOptVal, INT iOptLen)
shutdown(SOCKET sSocket, INT iHow)
socket(INT iAf, INT iType, INT iProtocol)

ZLib
GzClose(gzFile zFile)
GzEof(gzFile zFile)
GzError(gzFile zFile, INT * piError)
GzFlush(gzFile zFile, INT iFlush)
GzGetC(gzFile zFile)
GzGetS(gzFile zFile, const CHAR * pcBuffer, DWORD dwMaxLen)
GzOpen(const CHAR * pcPath, const CHAR * pcMode)
GzPrintF(gzFile zFile, const CHAR * pcFormat, ...)
GzPutC(gzFile zFile, INT iChar)
GzPutS(gzFile zFile, const CHAR * pcString)
GzRead(gzFile zFile, LPVOID lpvBuffer, DWORD dwLength)
GzRewind(gzFile zFile)
GzSeek(gzFile zFile, LONG lOffset, INT iMethod)
GzSetParams(gzFile zFile, INT iLevel, INT iStrategy)
GzTell(gzFile zFile)
GzWrite(gzFile zFile, LPVOID lpvBuffer, DWORD dwLength)
compress(PBYTE pbDest, PDWORD pdwDestLen, PBYTE pbSrc, DWORD dwSrcLen)
compress2(PBYTE pbDest, PDWORD pdwDestLen, PBYTE pbSrc, DWORD dwSrcLen, INT iLevel)
deflate(z_streamp pzsStream, INT iFlush)
deflateEnd(z_streamp pzsStream)
deflateInit(z_streamp pzsStream, INT iLevel)
deflateToFile(const PBYTE pbData, DWORD dwDataLength, const _TCHAR * ptcDest, INT iLevel)
inflate(z_streamp pzsStream, INT iFlush)
inflateEnd(z_streamp pzsStream)
inflateFromFile(const _TCHAR * ptcSrc, PBYTE * ppbDest, PDWORD pdwDataLength)
inflateInit(z_streamp pzsStream)
uncompress(PBYTE pbDest, PDWORD pdwDestLen, PBYTE pbSrc, DWORD dwSrcLen)


I/O

int FClose(
FILE * fFile)

Returns 0 if the stream is successfully closed. All streams created by a thread are closed automatically when the thread exits.

int FEof(
FILE * fFile)

Returns a nonzero value after the first read operation that attempts to read past the end of the file. It returns 0 if the current position is not end of file. There is no error return.

int FError(
FILE * fFile)

If no error has occurred on fFile, this function returns 0. Otherwise, it returns a nonzero value.

int FFlush(
FILE * fFile)

Returns 0 if the buffer was successfully flushed. The value 0 is also returned in cases in which the specified stream has no buffer or is open for reading only. A return value of EOF indicates an error.

int FGetC(
FILE * fFile)

Returns the character read as an int or returns EOF to indicate an error or end of file.

char * FGetS(
char * pcString,
int iMax,
FILE * fFile)

Reads a string from the input stream argument and stores it in pcString. This function reads characters from the current stream position to and including the first newline character, to the end of the stream, or until the number of characters read is equal to iMax – 1, whichever comes first. The result stored in string is appended with a NULL character. The newline character, if read, is included in the string. NULL is returned to indicate an error.

int FGetWC(
FILE * fFile)

Returns the wide character read as an int or returns WEOF to indicate an error or end of file.

wchar_t * FGetWS(
wchar_t * pwString,
int iMax,
FILE * fFile)

Reads a string from the input stream argument and stores it in pwString. This function reads characters from the current stream position to and including the first newline character, to the end of the stream, or until the number of characters read is equal to iMax – 1, whichever comes first. The result stored in string is appended with a NULL character. The newline character, if read, is included in the string. NULL is returned to indicate an error. Whether the characters are read as wide characters or multibyte characters depends on whether the stream was opened in text mode or binary mode, respectively.

FILE * FOpen(
const char * pcFileName,
const char * pcMode)

Returns a pointer to the opened file. A NULL pointer value indicates an error.

The character string pcMode specifies the type of access requested for the file, as follows:
“r”: Opens for reading. If the file does not exist or cannot be found, the function fails.
“w”: Opens an empty file for writing. If the given file exists, its contents are destroyed.
“a”: Opens for writing at the end of the file (appending) without removing the EOF marker before writing new data to the file; creates the file first if it doesn’t exist.
“r+”: Opens for both reading and writing. (The file must exist.)
“w+”: Opens an empty file for both reading and writing. If the given file exists, its contents are destroyed.
“a+”: Opens for reading and appending; the appending operation includes the removal of the EOF marker before new data is written to the file and the EOF marker is restored after writing is complete; creates the file first if it doesn’t exist.

int FPrintF(
FILE * fFile,
const char * pcFormat,
...)

Formats and prints a series of characters and values to the output stream. Each function argument (if any) is converted and output according to the corresponding format specification in pcFormat. For this function, the pcFormat argument has the same syntax and use that it has in PrintF(). Returns the number of bytes written.

int FPutC(
char cChar,
FILE * fFile)

Writes character cChar to the stream. Returns the character written or EOF to indicate an error.

int FPutS(
const char * pcString,
FILE * fFile)

Copies pcString to the output stream at the current position, but does not copy the terminating NULL character. Returns EOF to indicate an error.

int FPutWC(
wchar_t wChar,
FILE * fFile)

Writes wide character wChar to the stream. Returns the wide character written or WEOF to indicate an error.

int FPutWS(
const wchar_t * pwString,
FILE * fFile)

Copies pwString to the output stream at the current position, but does not copy the terminating NULL character. Whether the characters are copied as wide characters or multibyte characters depends on whether the stream was opened in text mode or binary mode, respectively. Returns WEOF to indicate an error.

int FRead(
void * pvBuffer,
unsigned int uiSize,
unsigned int uiCount,
FILE * fFile)

Returns the number of full items actually read, which may be less than uiCount if an error occurs or if the end of the file is encountered before reaching uiCount. Use the FEof() or FError() function to distinguish a read error from an end-of-file condition. If size or count is 0, this function returns 0 and the buffer contents are unchanged.

int FScanF(
FILE * fFile,
const char * pcFormat,
...)

Reads data from the current position of stream into the locations given by the optional argument (...). Each argument must be a pointer to a variable of a type that corresponds to a type specifier in pcFormat. pcFormat controls the interpretation of the input fields and has the same form and function as the format argument for SScanF(); see SScanF() for a description of pcFormat. If copying takes place between strings that overlap, the behavior is undefined.

int FSeek(
FILE * fFile,
long lOffset,
int iOrigin)

Moves the file pointer associated with stream to a new location that is lOffset bytes from iOrigin. The next operation on the stream takes place at the new location. The argument iOrigin must be one of the following constants:
SEEK_CUR: Current position of file pointer.
SEEK_END: End of file.
SEEK_SET: Beginning of file.

int FTell(
FILE * fFile)

Returns the current file position.

int FWPrintF(
FILE * fFile,
const wchar_t * pwFormat,
...)

Formats and prints a series of wide characters and values to the output stream. Each function argument (if any) is converted and output according to the corresponding format specification in pwFormat. For this function, the pwFormat argument has the same syntax and use that it has in PrintF(). Returns the number of wide characters written.

int FWScanF(
FILE * fFile,
const wchar_t * pwFormat,
...)

Reads data from the current position of stream into the locations given by the optional argument (...). Each argument must be a pointer to a variable of a type that corresponds to a type specifier in pwFormat. pwFormat controls the interpretation of the input fields and has the same form and function as the format argument for SWScanF(); see SWScanF() for a description of pwFormat. If copying takes place between strings that overlap, the behavior is undefined.

int FWrite(
const void * pvBuffer,
unsigned int uiSize,
unsigned int uiCount,
FILE * fFile)

Returns the number of full items actually written, which may be less than uiCount if an error occurs. Also, if an error occurs, the file-position indicator cannot be determined.

FILE * WFOpen(
const wchar_t * pwFileName,
const wchar_t * pwMode)

Returns a pointer to the opened file. A NULL pointer value indicates an error.

The character string pwMode specifies the type of access requested for the file, as follows:
“r”: Opens for reading. If the file does not exist or cannot be found, the function fails.
“w”: Opens an empty file for writing. If the given file exists, its contents are destroyed.
“a”: Opens for writing at the end of the file (appending) without removing the EOF marker before writing new data to the file; creates the file first if it doesn’t exist.
“r+”: Opens for both reading and writing. (The file must exist.)
“w+”: Opens an empty file for both reading and writing. If the given file exists, its contents are destroyed.
“a+”: Opens for reading and appending; the appending operation includes the removal of the EOF marker before new data is written to the file and the EOF marker is restored after writing is complete; creates the file first if it doesn’t exist.


Math

double ACos(
double dVal)

Returns the arccosine of dVal.

double ASin(
double dVal)

Returns the arcsine of dVal.

double ATan(
double dVal)

Returns the arctangent of dVal.

double ATan2(
double dY,
double dX)

Returns the arctangent of dY/dX.

double Abs(
double dVal)

Returns the absolute value of its parameter (double).

int64 Abs64(
int64 i64Val)

Returns the absolute value of its parameter (64-bit integer).

double Ceil(
double dVal)

Returns a double value representing the smallest integer that is greater than or equal to dVal.

double Cos(
double dVal)

Returns the cosine of dVal.

double CosH(
double dVal)

Returns the hyperbolic cosine of dVal.

double Exp(
double dVal)

Returns the exponential of dVal.

double Floor(
double dVal)

Returns a floating-point value representing the largest integer that is less than or equal to dVal.

double Log(
double dVal)

Returns the logarithm of dVal.

double Max(
double dLeft,
double dRight)

Returns the larger number between dLeft and dRight.

double Min(
double dLeft,
double dRight)

Returns the smaller number between dLeft and dRight.

double Mod(
double dLeft,
double dRight)

Calculates the floating-point remainder dRem of dLeft / dRight such that dLeft = iInt * dRight + dRem, where iInt is an integer, dRem has the same sign as dLeft, and the absolute value of dRem is less than the absolute value of dRight.

double Pow(
double dLeft,
double dRight)

Calculates dLeft raised to the power of dRight.

int Random(
int iMax)

Returns a random integer between 0 and (iMax - 1).

double Sin(
double dVal)

Returns the sine of dVal.

double SinH(
double dVal)

Returns the hyperbolic sine of dVal.

double Sqrt(
double dVal)

Returns the square-root of dVal.

double Tan(
double dVal)

Returns the tangent of dVal.

double TanH(
double dVal)

Returns the hyperbolic tangent of dVal.


Memory

void Free(
void * pvMemory)

Accepts a pointer to memory previously allocated with Malloc() and deallocates it. The memory is then free to be allocated again at a later time. If NULL is passed, the function returns without further processing. Allocated memory should always be freed by the script when the script is done with it, except in the case where the script returns a pointer to allocated memory to the system.

void * Malloc(
unsigned long ulCount)

Allocate ulCount bytes of memory. If the requested memory can not be allocated, NULL is returned. Memory allocated with this function should be freed with Free() when the memory is done being used, however failure to do so will not result in a memory leak. If the system calls a script function and that function returns a pointer to memory allocated with this function, that memory should not be released by the script or the system will be unable to access the value at that location. The memory will be released by the system after the script has executed and the system has used the data returned by the script.

int MemCmp(
void * pvBuf1,
void * pvBuf2,
unsigned long ulCount)

Compares the first ulCount bytes of pvBuf1 and pvBuf2 and returns a value indicating their relationship.

void * MemCpy(
void * pvDest,
const void * pvSource,
unsigned long ulCount)

Copies ulCount bytes from pvSource to pvDest.

void * MemMove(
void * pvDest,
const void * pvSource,
unsigned long ulCount)

Copies ulCount bytes from pvSource to pvDest. If some regions of the source area and the destination overlap, the function ensures that the original source bytes in the overlapping region are copied before being overwritten.

void * MemSet(
void * pvDest,
char cValue,
unsigned long ulCount)

Sets the first ulCount characters of pvDest to the character cValue.

void * ReAlloc(
void * pvOrig,
unsigned long ulCount)

Accepts a pointer to memory previously allocated with Malloc() and the new size of the allocation. Returns a pointer to the new allocation or NULL on failure. If the return pointer is different from the original pointer, the memory previously stored at pvOrig is copied to the new location. If pvOrig is NULL, the function behaves exactly like Malloc() and allocates the requested number of bytes.

int WMemCmp(
short * pwBuf1,
short * pwBuf2,
unsigned long ulCount)

Compares the first ulCount 16-bit Unicode characters of pwBuf1 and pwBuf2 and returns a value indicating their relationship.

short * WMemCpy(
short * pwDest,
const short * pwSource,
unsigned long ulCount)

Copies ulCount 16-bit Unicode characters from pwSource to pwDest.

short * WMemMove(
short * pwDest,
const short * pwSource,
unsigned long ulCount)

Copies ulCount Unicode characters from pwSource to pwDest. If some regions of the source area and the destination overlap, the function ensures that the original source bytes in the overlapping region are copied before being overwritten.

short * WMemSet(
short * pwDest,
short wValue,
unsigned long ulCount)

Sets the first ulCount Unicode characters of pwDest to the character wValue.


MHSAntiAntiCheat

LPFUNC_HOOK_INFO CreateFuncHookInfo()

Creates a structure that can be used to store hook information from calls to GetFuncHookInfo. This must be destroyed with DestroyFuncHookInfo

LPHOOK_INFO CreateHookInfo()

Creates a structure that can be used to store hook information from calls to GetHookInfo. This must be destroyed with DestroyHookInfo

VOID DestroyFuncHookInfo(
LPFUNC_HOOK_INFO lpfhiInfo)

Destroys a FUNC_HOOK_INFO structure previously created by CreateFuncHookInfo.

VOID DestroyHookInfo(
LPHOOK_INFO lphiInfo)

Destroys a HOOK_INFO structure previously created by CreateHookInfo.

MHS_ADDRESS GetCurrentSdtFuncAddress(
DWORD dwFuncId)

Returns the address of an SDT function inside the System Service Descriptor Table (SSDT). dwFuncId must be one of the SDT_* enumeration values. This is the current address of the function, which may be different from the value returned by GetSdtFuncAddress if the System Service Decriptor Table (SSDT) is hooked. If the specified function ID is not available on the current operating system this returns MHS_NULL.

MHS_ADDRESS GetCurrentSdtTableAddress()

Returns the current address of the System Service Descriptor Table (SSDT), which may be different from the value returned by GetSdtTableAddress if there are hooks in place.

MHS_ADDRESS GetFuncHookCall(
LPFUNC_HOOK_INFO lpfhiInfo,
DWORD dwIndex)

Gets a function call held in the FUNC_HOOK_INFO structure to which lpfhiInfo points. Returns MHS_NULL if the index is out of bounds.

LPHOOK_INFO GetFuncHookHook(
LPFUNC_HOOK_INFO lpfhiInfo,
DWORD dwIndex)

Gets a pointer to a hook held in the FUNC_HOOK_INFO structure to which lpfhiInfo points. Returns NULL if the index is out of bounds.

INT GetFuncHookInfo(
MHS_ADDRESS aAddress,
LPFUNC_HOOK_INFO lpfhiReturn)

Scans the entire function starting at the specified address for hooks. Returns DS_HOOKFOUND if any hooks are found. If no hook is found, it returns DS_HOOKNOTFOUND. If there is an internal error, DS_FAILURE is returned.
lpfhiReturn is filled with any hooks that are found and all calls the function makes, allowing you to scan all of the functions it calls.
You can use this to scan recursively any number of calls deep to find hooks on functions called by the specified function. If the same FUNC_HOOK_INFO is passed to multiple calls to GetFuncHookInfo, the new data from GetFuncHookInfo will be appended to the existing data already in the structure. This may be desireable in some cases but it is usually recommended to use a FUNC_HOOK_INFO structure in only one call to GetFuncHookInfo.
lpfhiReturn must have been previously created by a call to CreateFuncHookInfo.

DWORD GetFuncHookTotalCalls(
LPFUNC_HOOK_INFO lpfhiInfo)

Gets the total function calls held in the FUNC_HOOK_INFO structure to which lpfhiInfo points.

DWORD GetFuncHookTotalHooks(
LPFUNC_HOOK_INFO lpfhiInfo)

Gets the total number of hooks held in the FUNC_HOOK_INFO structure to which lpfhiInfo points.

INT GetHookInfo(
MHS_ADDRESS aAddress,
LPHOOK_INFO lphiReturn)

If a hook at the specified address if found, this function fills the HOOK_INFO structure to which lphiReturn points with information regarding the hook and returns DS_HOOKFOUND. If no hook is found, it returns DS_HOOKNOTFOUND. If there is an internal error, DS_FAILURE is returned.
lphiReturn must point to a HOOK_INFO structure created by CreateHookInfo. The same structure can be used in multiple calls to this function.
This can be used to get hook information from kernel RAM.

MHS_ADDRESS GetKernelProcAddress(
const CHAR * pcFunc)

Gets the address of the specified function from the loaded kernel module or Hal.dll.

CHAR * GetNtOsKrnlName(
CHAR * pcRet)

Fills pcRet with the name of the loaded kernel module and returns pcRet, allowing it to be used as a parameter to another function. pcRet will be an empty string if the name of the kernel module can not be obtained.

CHAR * GetNtOsKrnlPath(
CHAR * pcRet)

Fills pcRet with the path of the loaded kernel module and returns pcRet, allowing it to be used as a parameter to another function. pcRet will be an empty string if the path to the kernel module can not be obtained.

DWORD GetNtOsKrnlRange(
MHS_ADDRESS * paStart,
MHS_ADDRESS * paEnd)

Fills the MHS_ADDRESS to which paStart and paEnd point with the starting and ending addresses of the loaded kernel executable inclusively and returns the size of the loaded module. paStart and paEnd can be NULL.

MHS_ADDRESS GetProcAddress(
const CHAR * pcModule,
const CHAR * pcFunc)

Gets the actual address of a function in a loaded module. pcModule can be either a module name or a path to a module. If the function can not be found, MHS_NULL is returned.

Because exports can be forwarded to other modules, the return address is not guaranteed to be within the range of the module specified. This function works only on user-mode addresses. Use GetKernelProcAddress to get the addresses of kernel functions.

MHS_ADDRESS GetSdtFuncAddress(
DWORD dwFuncId)

Returns the address of an SDT function inside the System Service Descriptor Table (SSDT). dwFuncId must be one of the SDT_* enumeration values. This is the real address of the function. If the specified function ID is not available on the current operating system this returns MHS_NULL.

DWORD GetSdtFuncIndex(
DWORD dwFuncId)

Returns the index of an SDT function inside the System Service Descriptor Table (SSDT). dwFuncId must be one of the SDT_* enumeration values.

CHAR * GetSdtFuncName(
DWORD dwFuncId,
CHAR * pcRet)

Returns the name of an SDT function inside the System Service Descriptor Table (SSDT). dwFuncId must be one of the SDT_* enumeration values. pcRet will be filled with the name of the function and must be at least 128 characters long.

MHS_ADDRESS GetSdtTableAddress()

Returns the address of the System Service Descriptor Table (SSDT).

DWORD GetSdtTotalFuncs()

Returns the number of SDT functions on the system. This varies per operating system. This should not be confused total number of function ID’s, which includes all functions across all operating systems.


MHSAssembly

BOOL AutoAssemble(
const CHAR * pcString)

Auto-assembles and injects the supplied string. The string is the actual auto-assemble text to compile. Returns TRUE if the text is fully auto-assembled and injected into the target process. If the function returns FALSE, the target process remains unmodified.

DIS_DISASM * CreateDisObj()

Creates a disassembly object that can be used in a call to Disasm. DIS_DISASM objects must be created with this function and destroyed with DestroyDisObj. Never create a DIS_DISASM object manually.

VOID DestroyDisObj(
DIS_DISASM * lpdDestroyMe)

Destroys a DIS_DISASM created with CreateDisObj.

DWORD Disasm(
BYTE * pbSource,
DWORD dwSourceSize,
D_ADDRESS aEip,
DIS_DISASM * lpddDis,
INT iMode,
DWORD dwOpts)

Disassembles the given buffer and returns the size of the found command.
pbSource points to a buffer containing the actual bytes to disassemble.

dwSourceSize indicates the length of pbSource.

aEip indicates the EIP (starting address) of the buffer to which pbSource points.

lpddDis holds the return information. This object must be created with a call to CreateDisObj. It contains the following information:
lpddDis->aEIP indicates the address of the disassembled data.
lpddDis->strDump provides a string representation of the bytes that create the command. Use lpddDis->strDump.dwLength to get the length of the string and lpddDis->strDump.pcText to get the actual text. This is not returned if iMode is DIS_SIZE or DIS_DATA.
lpddDis->strResult provides a string representation of the disassembled command. Use lpddDis->strResult.dwLength to get the length of the string and lpddDis->strResult.pcText to get the actual text. This is not returned if iMode is DIS_SIZE or DIS_DATA.
lpddDis->strComment provides extra comments associated with the command. Use lpddDis->strComment.dwLength to get the length of the string and lpddDis->strComment.pcText to get the actual text. This is not returned if iMode is DIS_SIZE or DIS_DATA.
lpddDis->iCmdType returns the type of command. Use this with the CD_* enumerations. To get the actual type of the command, use (lpddDis->iCmdType & CD_TYPEMASK). For example, to determine if the current command is a jump of any kind, use ((lpddDis->iCmdType & CD_TYPEMASK) == CD_JMP) || (lpddDis->iCmdType & CD_TYPEMASK) == CD_JMC).
lpddDis->iMemType provides information regarding the memory type of an address. Use this with the MDEC_* enumerations. Similary with iCmdType, use MDEC_TYPEMASK to get the type mask.
lpddDis->iPrefixes returns the number of prefixes on the command.
lpddDis->iIndexed returns the number of registers the command operand address contains.
lpddDis->dwJmpConst returns the constant of a JMP command, if any. If lpddDis->iIndexed is 0, lpddDis->dwJmpConst is the actual target address of the JMP, otherwise the operand is using it in an equation such as 01005334+EAX.
lpddDis->dwTable returns a possible switch-table address
lpddDis->dwAddrConst returns the constant of an address-related operand. If lpddDis->iIndexed is 0, this is the actual target address referenced by the operand.
lpddDis->dwImmConst returns the immediate constant of an operand. An immediate constant is a hard-coded numeric value.
lpddDis->iArg1, lpddDis->iArg2, and lpddDis->iArg3 specify the types of arguments, if applicable. These will be one of the ARG_* enumerated values.
lpddDis->iZeroConst indicates if zero-constants appear in the command.
lpddDis->iFixUpOffset indicates the fix-up offset if applicable.
lpddDis->iFixUpSize indicates the size of the fix-up.
lpddDis->iError indicates any errors during processing. The error will be one of the DERR_* enumerated values.
lpddDis->iWarnings indicates any warnings during processing. This will be one of the DISW_* enumerated values.
lpddDis->lpcCmdData points to the DIS_CMD structure containing the data related to the found command.

iMode indicates the level of detail to use when disassembling. This can be one of the following:
DIS_SIZE: Gets the size of the command only. The data in lpddDis should not be considered valid.
DIS_DATA: Gets the size and fills in the lpddDis structure, except for strings.
DIS_FILE: All of the above but also fills in the strings.
DIS_CODE: Same as DIS_FILE. Symbolic names are not resolved.

dwOpts indicates the options for decoding. This can be any combination of the following values:
DOPT_IDEAL: Use IDEAL decoding.
DOPT_LOWER: Use lower-case output.
DOPT_TAB_ARGS: Tab after the command before the args.
DOPT_EXTRASPACE: Put an extra space between arguments.
DOPT_PUTDEFSEG: Show the default segments.
DOPT_SHOWMEMSIZE: Show memory sizes.
DOPT_NEAR: Show NEAR modifiers.
DOPT_SHRTSTRCMD: Use the short form of string commands.
DOPT_FARCALLS: FAR calls and returns do not issue warnings.
DOPT_VXD: VxD calls do not issue warnings.
DOPT_PRIVILAGED: privilaged commands do not issue warnings.
DOPT_IOCOMMAND: I/O commands do not issue warnings.
DOPT_BADSHIFT: Bad shifts do not issue warnings.
DOPT_EXTRAPREFIX: Extra prefixes do not issue warnings.
DOPT_LOCKEDBUS: LOCK prefixes do not issue warnings.
DOPT_STACKALIGN: Commands that misalign the stack do not issue warnings.
DOPT_SEGMOD: Segment modifications do not issue warnings.
DOPT_WINDOWSNT: Check for dangers while assuming Windows® NT.


MHSDebugging

BOOL AddBreakpoint(
LPSCRIPT_ADD_BP lpbpBreakpoint,
PDWORD pdwId)

Adds a user breakpoint. This fails when called from a breakpoint-handler (such as On_BP_0()). pdwId will be filled with the ID of the breakpoint if the function is successful. The ID of the breakpoint is required to remove the breakpoint later. This parameter can be NULL.
lpbpBreakpoint->iType can be SPBT_EXECUTE, SPBT_READ, or SPBT_WRITE.
lpbpBreakpoint->iProlog, lpbpBreakpoint->iCallback, and lpbpBreakpoint->iEpilog can be one of:
SYS_FUNCS_DO_NOTHING, SYS_FUNCS_BEEP, SYS_FUNCS_BREAK, SYS_FUNCS_SETCF, SYS_FUNCS_SETPF, SYS_FUNCS_SETAF, SYS_FUNCS_SETZF, SYS_FUNCS_SETSF, SYS_FUNCS_SETOF, SYS_FUNCS_SCRIPT_FUNC, or SYS_FUNCS_CUR_PROC_SCRIPT_FUNC.
lpbpBreakpoint->dwNewParms[0] is the parameter for the iProlog function, lpbpBreakpoint->dwNewParms[1] is the parameter for the iCallback function, and lpbpBreakpoint->dwNewParms[2] is the parameter for the iEpilog function.
lpbpBreakpoint->pcName points to the optional name for the breakpoint. This can be NULL.
lpbpBreakpoint->bHardware determines whether the new breakpoint should be hardware or software. Only 4 hardware breakpoints can be set. Any number of software breakpoints can be set but each modifies the RAM of the target process.
lpbpBreakpoint->bSet determines whether the breakpoint is active or not.
lpbpBreakpoint->aAddress indicates the address of the breakpoint. If another breakpoint is already on the supplied address, AddBreakpoint fails.
lpbpBreakpoint->dwRange indicates the total number of addresses to cover. This is valid only for software read/write breakpoints. This indicates that X number of addresses from aAddress can cause breaks when read/written. lpbpBreakpoint->bUseCondition indicates whether the condition is used. lpbpBreakpoint->pcCondition points to a string representing the condition for the breakpoint. This can be NULL.

BOOL AttachDebugger()

Attaches the Debugger to the current process. Returns TRUE if the attach was successful or if the Debugger was already attached. This must not be called from a breakpoint handler.

BOOL DebugFile(
LPSTR lpstrFile,
LPSTR lpstrParms,
BOOL bPause)

Opens an executable file for debugging. This must not be called while opening or closing a process or from a breakpoint handler.

VOID DetachDebugger()

Detaches the Debugger from the current process. This must only be called from a hotkey.

VOID EnterSingleStep()

Enters the Debugger into single-stepping mode. The Debugger will wait for human input before continuing. This must be called from within a breakpoint handler.

BOOL FindModuleByAddress(
MHS_ADDRESS aAddress,
PMODULEENTRY32 pme32Return)

Searches the target process’s module list for a module beginning at the specified address and, if found, fills the supplied MODULEENTRY32 structure with its data. pme32Return can be NULL. This function uses a binary search and is extremely fast, but not always up-to-date unless the Debugger is active.

BOOL FindModuleByName(
const CHAR * pcName,
PMODULEENTRY32 pme32Return)

Searches the target process’s module list for a module matching the specified name and, if found, fills the supplied MODULEENTRY32 structure with its data. pme32Return can be NULL. This function uses a binary search and is extremely fast, but not always up-to-date unless the Debugger is active.

BOOL FindModuleByRange(
MHS_ADDRESS aAddress,
PMODULEENTRY32 pme32Return)

Searches the target process’s module list for a module covering the specified address and, if found, fills the supplied MODULEENTRY32 structure with its data. pme32Return can be NULL. This function uses a binary search and is extremely fast, but not always up-to-date unless the Debugger is active.

MHS_ADDRESS GetRemoteFuncAddress(
const CHAR * pcModule,
const CHAR * pcFunction)

Returns the address of the specified function within the target process. If there is no target process or the module or function does not exist, the return value is MHS_NULL. The module name is case-insensitive.

BOOL InjectDll(
const CHAR * pcPath,
BOOL bAutoUninject)

Injects the specified DLL into the target process. If bAutoUninject is TRUE the DLL will be uninjected when Memory Hacking Software detaches from the target process.

BOOL IsDebugging()

Returns TRUE if the Debugger is currently active.

BOOL IsStepping()

Returns TRUE if the Debugger is in single-stepping mode.

BOOL RemoveBreakpoint(
DWORD dwId)

Removes a breakpoint by its ID. This fails when called from within a breakpoint handler.

VOID StepOnce()

Causes the Debugger to single-step one instruction if it is in single-stepping mode. The breakpoint handler that was originally used to put the thread into single-stepping mode is not triggered as a result of this call, however if single-stepping results in a breakpoint being hit, that breakpoint’s handlers will be called. This should not be called from a breakpoint handler.

VOID StepOver()

Causes the Debugger to step over a function CALL if it is in single-stepping mode. If the next instruction is not a CALL, this behaves the same as StepOnce. The breakpoint handler that was originally used to put the thread into single-stepping mode is not triggered as a result of this call, however if stepping over results in a breakpoint being hit, that breakpoint’s handlers will be called. This should not be called from a breakpoint handler.

VOID StopStepping()

Causes the Debugger to leave single-stepping mode, if already in it.


MHSSearch

LPSTOREDADDRESS AddAddress(
LPADDADDRESS lpaAddress)

Adds an address to the Stored Address List.
lpaAddress->aAddress: The address of the item. This is not needed if lpaAddress->iAddressType is not MAA_DEFAULT.
lpaAddress->iType: MT_* type of data. For example, MT_ULONG.
lpaAddress->iLockType: Type of lock. Must be one of the SV_LOCK_* enumerated values. For example, SV_LOCK_STATIC.
If lpaAddress->iLockType is SV_LOCK_STATIC, lpaAddress->pbLockedValue and lpaAddress->dwLockValueSize must contain the value to which to lock the stored address and its size, respectively.
If lpaAddress->iLockType is SV_LOCK_RANGE, lpaAddress->uLockParms.irLock or lpaAddress->uLockParms.drLock must be filled with the high and low values. If the type of the item is MT_DOUBLE or MT_FLOAT, drLock must be filled, otherwise irLock must be filled.
If lpaAddress->iLockType is SV_LOCK_MINBARRIER, lpaAddress->uLockParms.i64Limit should specify the minimum possible value if the type of the item is not MT_FLOAT or MT_DOUBLE, otherwise lpaAddress->uLockParms.dLimit should be used.
If lpaAddress->iLockType is SV_LOCK_MAXBARRIER, lpaAddress->uLockParms.i64Limit should specify the maximum possible value if the type of the item is not MT_FLOAT or MT_DOUBLE, otherwise lpaAddress->uLockParms.dLimit should be used.
lpaAddress->pcDescr: Pointer to a string indicating the description of the item.
lpaAddress->iLockMethod: Indicates the method of locking. This can be MAL_DEFAULT, MAL_AUTOASSEMBLE, or MAL_SCRIPT. If MAL_AUTOASSEMBLE is specified then an Auto-Assembler script must also be given via lpaAddress->pcAutoAssemble. Otherwise, if MAL_SCRIPT is specified, a script must be given via lpaAddress->pcScript.
lpaAddress->iAddressType: Indicate the method used to determine the address of the item. This can be MAA_DEFAULT, MAA_COMPLEX, or MAA_SCRIPT. If MAA_COMPLEX is specified, lpaAddress->pcComplexAddress must point to a valid Complex Address in string format. If MAA_SCRIPT is specified, lpaAddress->pcScript must point to a valid script in string format.
lpaAddress->bUseScriptForBaseAddress: Indicates that the script supplied in lpaAddress->pcScript will be used to determine the base address of the item. Only valid if lpaAddress->iAddressType is MAA_SCRIPT.
lpaAddress->bUseScriptForFinalAddress: Indicates that the script supplied in lpaAddress->pcScript will be used to determine the final address of the item. Only valid if lpaAddress->iAddressType is MAA_SCRIPT.
lpaAddress->iHotKeyKey: The Hotkey Key used for unlocking and locking the stored address via Hotkeys.

DWORD GetLastEvaluationType()

Returns one of the ST_* enumerations depicting the evaluation type of the last search.

DWORD GetLastScanDataType()

Returns one of the MT_* enumerations depicting the data type of the last search or sub search. If the last search was a Group Search or Template Search, this returns MT_NONE. If the last search was a String Search, this returns MT_STRING.

DWORD GetLastScanType()

Returns one of the LS_* enumerations depicting the type of the last search or sub search.

DWORD GetScanTotal()

Gets the total number of results returned in a scan.

BOOL GetScanValue(
DWORD dwIndex,
LPVOID * lppvAddress,
LPVOID lpvBuffer)

Gets a value from the last scan by index. dwIndex is the index of the value to get. lppvAddress is filled with the address of the returned result and lpvBuffer is filled with the actual value of the return. If the supplied index is invalid, it returns false.

BOOL HaveScanned()

Determines whether a scan has been made.

BOOL LockScanForRead()

Locks the scanner for reading. This should be called before reading values with GetScanValue. If it is not, GetScanValue may sometimes fail. This must be matched with a call to UnlockScanForRead. Returns TRUE if the scanner was locked, false otherwise.

BOOL RAMSearch(
LPMHS_API_SEARCH_PARMS lpsParms)

Scans the process RAM using the type of search specified by lpsParms->dwType (LS_DATATYPE, LS_POINTER, or LS_STRING) using the search-specific options.
LS_DATATYPE:
lpsParms->TypeParms.dtParms.dwDataType can be one of MT_CHAR, MT_BYTE, MT_SHORT, MT_USHORT, MT_LONG, MT_ULONG, MT_FLOAT, or MT_DOUBLE, and this determines how lpsParms->TypeParms.dtParms.atValue and lpsParms->TypeParms.dtParms.atTo are decoded.
lpsParms->TypeParms.dtParms.dwSubType can be one of ST_EXACT, ST_NOTEQUALTO, ST_RANGE, ST_LOWERTHAN, ST_GREATERTHAN, or ST_UNKNOWN. If ST_RANGE, the lpsParms->TypeParms.dtParms.atTo value is used.
LS_POINTER:
lpsParms->TypeParms.pParms.dwSubType can be one of ST_EXACT, ST_NOTEQUALTO, ST_RANGE, ST_LOWERTHAN, ST_GREATERTHAN, or ST_UNKNOWN. If ST_RANGE, the lpsParms->TypeParms.pParms.atTo value is used.
Set lpsParms->TypeParms.pParms.lpvSaveOffset to NULL if you do not wish to save offsets from any addresses, otherwise set it to the address from which you wish to save offsets.
LS_STRING:
lpsParms->TypeParms.sParms.pcValue indicates the byte array to find (not necessarily terminated with a NULL character).
lpsParms->TypeParms.sParms.dwLength indicates the length of the byte array to find.

BOOL RAMSubSearch(
LPMHS_API_SUB_SEARCH_PARMS lpsParms)

Perform a sub search.

BOOL SaOSubScanEnabled()

Determines whether the Same as Original sub search is enabled.

VOID SetSearchRange(
MHS_ADDRESS aStart,
MHS_ADDRESS aEnd)

Set the search range.

BOOL UnlockScanForRead()

Unlocks the scanner after having been previously locked with LockScanForRead.


MHSSys

VOID CallLocalFunction(
LOCAL_CALL_RETURN * prcrRet,
LPVOID lpvFuncAddress,
DWORD dwExtra,
INT iParms,
...)

Calls a local non-script function. The function will usually be inside a loaded DLL, and the DLL will usually, but not necessarily, have been loaded by a previous call to the LoadLibrary script function.
prcrRet will be filled with the return of the function call. prcrRet can be NULL if the return is not interesting or if the function has no return. dwExtra can be a combination of RCE_NOTHING, RCE_STDCALL, RCE_FLOAT, and RCE_DOUBLE. RCE_FLOAT and RCE_DOUBLE can not be used together.
iParms is the number of parameters to pass to the function. Note that any 64-bit parameters must be counted twice. For example, if there is only one parameter, and it is a UINT64 type, iParms will be 2 rather than 1.
This function has no return value. If the function call fails for any reason, Memory Hacking Software will crash. Use LoadLibrary to load your own custom DLL’s and GetProcAddress to get the addresses of functions inside them.

BOOL CastExpression(
LPEVAL_RET_TYPE lpertExp,
INT iType)

Casts the supplied expression to the supplied type. The expression must be a valid expression returned from a successful call to EvalExp. The expression will be cast to the type specified by iType. iType must be one of the MT_* type enumerations. The same expression can be cast again later.

VOID Clear()

Clears the output window.

VOID Error(
const CHAR * pcFormat,
...)

Show a message box with an error specified by pcFormat. The format-control string matches that of PrintF().

BOOL EvalExp(
const CHAR * pcExpression,
LPEVAL_RET_TYPE lpertRet,
BOOL bTreatAsHex)

Parses a text expression and returns the result in lpertRet.
lpertRet->iType determines the type of the returned result.
MT_DOUBLE indicates the return is stored in lpertRet->u.dDouble.
MT_UINT64 indicates the return is stored in lpertRet->u.ui64Int64.
MT_INT64 indicates the return is stored in lpertRet->u.i64Int64.

DWORD GetCrc(
const CHAR * pcPath)

Returns the CRC of the specified file.

HANDLE GetCurProcessHandle()

Returns the current process handle.

DWORD GetCurProcessID()

Returns the current process ID.

_TCHAR * GetCurProcessName()

Returns a pointer to a string containing the name of the current process.

LPVOID GetCurProcessPEproc()

Returns the address of the current process’ EPROCESS structure.

_TCHAR * GetCurProcessPath(
_TCHAR * ptcReturn)

Returns a pointer to a string containing the path of the current process. The return value is the same as ptcReturn on success, or NULL otherwise.

BOOL GetProcessNameByPEproc(
LPVOID lpEprocAddress,
CHAR * pcBuffer)

Copies the process name associated with the given EPROCESS pointer to the given CHAR buffer. The buffer must be at least 16 characters long. The buffer is always padded with a terminating NULL.

BOOL InputFloat(
const _TCHAR * ptcTitle,
const _TCHAR * ptcCaption,
const _TCHAR * ptcDefault,
DOUBLE * pdReturn)

Displays a dialog box that prompts the user for input in the form of a floating-point number. If the user presses Cancel, FALSE is returned. Otherwise, pdReturn is filled with the floating-point value the user typed into the dialog.

BOOL InputFloat(
const _TCHAR * ptcTitle,
const _TCHAR * ptcCaption,
const _TCHAR * ptcDefault,
FLOAT * pfReturn)

Displays a dialog box that prompts the user for input in the form of a floating-point number. If the user presses Cancel, FALSE is returned. Otherwise, pfReturn is filled with the floating-point value the user typed into the dialog.

BOOL InputNumber(
const _TCHAR * ptcTitle,
const _TCHAR * ptcCaption,
const _TCHAR * ptcDefault,
INT * piReturn)

Displays a dialog box that prompts the user for input in the form of an integer number. If the user presses Cancel, FALSE is returned. Otherwise, piReturn is filled with the integer value the user typed into the dialog.

BOOL InputString(
const _TCHAR * ptcTitle,
const _TCHAR * ptcCaption,
const _TCHAR * ptcDefault,
_TCHAR * ptcReturn,
INT iMaxLen)

Displays a dialog box that prompts the user for a string. If the user presses Cancel, FALSE is returned. Otherwise, ptcReturn is filled with the string the user typed into the dialog. iMaxLen indicates the length of the buffer specified by ptcReturn.

BOOL LoadProcess(
DWORD dwID)

Attach Memory Hacking Software to the process specified by dwID.

BOOL LoadProcess(
DWORD dwID,
BOOL bRestricted)

Attach Memory Hacking Software to the process specified by dwID.

INT MessageBox(
DWORD dwMask,
const CHAR * pcTitle,
CHAR * pcFormat,
...)

Show a message box with the text specified by pcFormat. The format-control string matches that of PrintF().
The following values are valid for dwMask:
MBS_OK: Dialog has an OK button.
MBS_YES: Dialog has a Yes button.
MBS_NO: Dialog has a No button.
MBS_CANCEL: Dialog has a Cancel button.

You can combine these flags using bitwise OR (|), however not every combination is valid.
The return value is one of MBS_OK, MBS_YES, MBS_NO, and MBS_CANCEL, depending on which button was pressed. If an invalid value is supplied for dwMask, the function returns MBS_CANCEL without displaying a message box.

BOOL ReadLocalMemory(
LPVOID lpBaseAddress,
LPVOID lpBuffer,
SIZE_T nSize,
SIZE_T * lpNumberOfBytesRead)

Safely reads local memory inside MHS. If the kernel driver is successfully loaded, this can be used to read kernel RAM as well.

INT RegisterHotkey(
INT iKey,
INT iMod,
INT iFunc,
DWORD dwParms[3],
BOOL bPoll)

Registers a hotkey with the system and returns its ID for use with UnregisterHotkey. The return is -1 on error. iKey is the virtual key code of the key to press (VK_*). iMod is the key modifier, for example the Alt key or Shift (MOD_*). iFunc is the ID of the function to be called when the specified keys are pressed, and dwParms are the parameters to pass to that function. bPoll determines whether the polling method is used or the standard Windows hotkey system is used.
iFunc can be any of the following:
HK_FUNC_NOTHING: No operation.
HK_FUNC_POKE: Writes a value into an address. dwParm[0] specifies the address, dwParm[1] specifies the value, and dwParm[2] specifies the size (up to 4 bytes).
HK_FUNC_BEEP: Beeps for dwParm[0] milliseconds.
HK_FUNC_PAUSE: Pauses the target process.
HK_FUNC_RESUME: Resumes the target process.
HK_FUNC_SCRIPTFUNCTION: Calls a script function specified by dwParm[0].
HK_FUNC_CURPROCSCRIPTFUNCTION: Calls a script function specific to the current process, specified by dwParm[0].
HK_FUNC_LOADCONVERTER: Loads the Converter. This is not available in trainers.
HK_FUNC_LOADHEXEDITOR: Loads the Hex Editor. This is not available in trainers.
HK_FUNC_LOADDISASSEMBLER: Loads the Disassembler. This is not available in trainers.
HK_FUNC_LOADSCRIPTEDITOR: Loads the Script Editor. This is not available in trainers.
HK_FUNC_SUBSAMEASBEFORE: Performs a Same as Before Sub Search. This is not available in trainers.
HK_FUNC_SUBDIFFROMBEFORE: Performs a Different From Before Sub Search. This is not available in trainers.
HK_FUNC_SUBINCREASED: Performs an Increase Sub Search. This is not available in trainers.
HK_FUNC_SUBDECREASED: Performs a Decreased Sub Search. This is not available in trainers.
HK_FUNC_SUBSAMEASORIGINAL: Performs a Same as Original Sub Search. This is not available in trainers.
HK_FUNC_LOCKSTORED: Locks Stored Values whose Hotkey Key matches dwParm[0]. If dwParm[1] is not 0, the Stored Values will be toggled on or off, instead of always being set to on. This is not available in trainers.
HK_FUNC_UNLOCKSTORED: Unlocks Stored Values whose Hotkey Key matches dwParm[0]. This is not available in trainers.
HK_FUNC_SETPROCESSSPEED: Set the speed of the process. dwParm[0] is the denominator, dwParm[1] is the numerator, and dwParm[2] indicates whether the Hotkey toggles the process speed. If this is 0, the speed specified here will always be applied to the process. If it is not 0, the process speed will be toggled between the supplied speed and normal speed. This is not available in trainers.

BOOL UnregisterHotkey(
INT iId)

Unregisters the specified hotkey by its ID.

VOID Warning(
const CHAR * pcFormat,
...)

Show a message box with a warning specified by pcFormat. The format-control string matches that of PrintF().

BOOL WriteLocalMemory(
LPVOID lpBaseAddress,
LPVOID lpBuffer,
SIZE_T nSize,
SIZE_T * lpNumberOfBytesWritten)

Safely writes local memory inside MHS. If the kernel driver is successfully loaded, this can be used to write to kernel RAM as well.


Searching & Sorting

LPVOID BSearch(
LPCVOID lpvKey,
LPCVOID lpvBase,
SIZE_T nNum,
SIZE_T nWidth,
const TCHAR * ptcCompareFunc)

Returns a pointer to an occurrence of lpvKey in the array pointed to by lpvBase. If lpvKey is not found, the function returns NULL. If the array is not in ascending sort order or contains duplicate records with identical keys, the result is unpredictable.
The BSearch() function performs a binary search of a sorted array of nNum elements, each of nWidth bytes in size. The lpvBase value is a pointer to the base of the array to be searched, and lpvKey is the value being sought. The ptcCompareFunc parameter is a string indicating a user-supplied script routine that compares two array elements and returns a value specifying their relationship. BSearch() calls the ptcCompareFunc routine one or more times during the search, passing pointers to two array elements on each call. The compare routine compares the elements, then returns one of the following values:
Return < 0: lpvElem1 less than lpvElem2
Return == 0: lpvElem1 equivalent to lpvElem2
Return > 0: lpvElem1 greater than lpvElem2

BOOL QSort(
LPCVOID lpvBase,
SIZE_T nNum,
SIZE_T nWidth,
const TCHAR * ptcCompareFunc)

Implements a quick-sort algorithm to sort an array of nNum elements, each of nWidth bytes. The argument lpvBase is a pointer to the base of the array to be sorted.
QSort() overwrites this array with the sorted elements. The argument ptcCompareFunc is a string indicating a user-supplied script routine that compares two array elements and returns a value specifying their relationship. QSort() calls the ptcCompareFunc routine one or more times during the sort, passing pointers to two array elements on each call:
pcCompareFunc( (LPVOID) lpvElem1, (LPVOID) lpvElem2 );

The routine must compare the elements, then return one of the following values:
Return < 0: lpvElem1 less than lpvElem2
Return == 0: lpvElem1 equivalent to lpvElem2
Return > 0: lpvElem1 greater than lpvElem2

The array is sorted in increasing order, as defined by the comparison function. To sort an array in decreasing order, reverse the sense of “greater than” and “less than” in the comparison function.
Returns FALSE if the sort fails due to an invalid ptcCompareFunc parameter.

BOOL RegExMatch(
const CHAR * pcRegEx,
const CHAR * pcCheck,
INT * piLength)

Determines if pcCheck is a match for the regular-expression string pcRegEx. If a match is found, piLength will be set to the match length.

Example of usage:
RegExMatch("[0-9+]","780 Miles", &iRet )
The function will return TRUE and iRet will be set to 3.


String

double AtoF(
const char * pcString1)

Returns the double value produced by interpreting the input characters as a number.

int AtoI(
const char * pcString1)

Returns the int value produced by interpreting the input characters as a number.

int64 AtoI64(
const char * pcString1)

Returns the 64-bit integer value produced by interpreting the input characters as a number.

int PrintF(
const char * pcFormat,
...)

Formats and prints a series of characters and values to the standard output stream. Format specifications are exactly the same as in the standard-library “printf” function.

int SNPrintF(
char * pcBuffer,
unsigned long ulCount,
const char * pcFormat,
...)

Formats and stores ulCount or fewer characters and values (including a terminating NULL character that is always appended unless ulCount is zero or the formatted string length is greater than or equal to ulCount characters) in pcBuffer. Each argument (if any) is converted and output according to the corresponding format specification in pcFormat. The format consists of ordinary characters and has the same form and function as the standard-library “_snprintf” function.
Returns the number of bytes stored in pcBuffer, not counting the terminating NULL character.

int SNWPrintF(
short * pwBuffer,
unsigned long ulCount,
const short * pwFormat,
...)

Formats and stores ulCount or fewer Unicode characters and values (including a terminating NULL character that is always appended unless ulCount is zero or the formatted string length is greater than or equal to ulCount characters) in pwBuffer. Each argument (if any) is converted and output according to the corresponding format specification in pwFormat. The format consists of ordinary characters and has the same form and function as the standard-library “_snwprintf” function.
Returns the number of Unicode characters stored in pwBuffer, not counting the terminating NULL character.

int SPrintF(
char * pcBuffer,
const char * pcFormat,
...)

Formats and stores a series of characters and values in pcBuffer. Each argument (if any) is converted and output according to the corresponding format specification in pcFormat. The format consists of ordinary characters and has the same form and function as the standard-library “sprintf” function.
Returns the number of bytes stored in pcBuffer, not counting the terminating NULL character.

int SScanF(
const char * pcBuffer,
const char * pcFormat,
...)

Reads data from pcBuffer into the location given by each argument. Every argument must be a pointer to a variable with a type that corresponds to a type specifier in pcFormat.
The pcFormat argument controls the interpretation of the input fields and has the same form and function as the pcFormat argument for the standard-library “sscanf” function.
If copying takes place between strings that overlap, the behavior is undefined.

Returns the number of fields successfully converted and assigned; the return value does not include fields that were read but not assigned. A return value of 0 indicates that no fields were assigned.

int SWPrintF(
short * pwBuffer,
const short * pwFormat,
...)

Formats and stores a series of Unicode characters and values in pwBuffer. Each argument (if any) is converted and output according to the corresponding format specification in pwFormat. The format consists of ordinary characters and has the same form and function as the standard-library “swprintf” function.
Returns the number of Unicode characters stored in pwBuffer, not counting the terminating NULL character.

int SWScanF(
const short * pwBuffer,
const short * pwFormat,
...)

Reads data from pwBuffer into the location given by each argument. Every argument must be a pointer to a variable with a type that corresponds to a type specifier in pwFormat.
The pwFormat argument controls the interpretation of the input fields and has the same form and function as the pwFormat argument for the standard-library “swscanf” function.
If copying takes place between strings that overlap, the behavior is undefined.

Returns the number of fields successfully converted and assigned; the return value does not include fields that were read but not assigned. A return value of 0 indicates that no fields were assigned.

char * StrCat(
char * pcDest,
const char * pcSource)

Appends pcSource to pcDest. Returns the destination string.

char * StrChr(
const char * pcString,
char cChar)

Returns a pointer to the first occurrence of cChar in pcString, or NULL if cChar is not found.

int StrCmp(
const char * pcString1,
const char * pcString2)

Compares the two strings and returns the lexicographic relation of pcString1 to pcString2 (pcString1 less than pcString2, pcString1 equal to pcString2, or pcString1 greater than pcString2).

char * StrCpy(
char * pcDest,
const char * pcSource)

Copies pcSource, including the terminating NULL character, to the location specified by pcDest. The behavior of StrCpy() is undefined if the source and destination strings overlap. Returns the destination string.

char * StrDup(
const char * pcString)

Returns a pointer to the storage location for the copied string or NULL if storage cannot be allocated. The copied string is allocated with Malloc and should be freed with Free when it is no longer needed.

int StrICmp(
const char * pcString1,
const char * pcString2)

Lexicographically compares lowercase versions of pcString1 and pcString2 and returns a value indicating their relationship (pcString1 less than pcString2, pcString1 equal to pcString2, or pcString1 greater than pcString2).

unsigned long StrLen(
const char * pcString)

Returns the number of characters in pcString, excluding the terminal NULL.

char * StrNCat(
char * pcDest,
const char * pcSource,
unsigned long ulCount)

Appends, at most, ulCount characters from pcSource to pcDest. Returns the destination string.

int StrNCmp(
const char * pcString1,
const char * pcString2,
unsigned long ulCount)

Lexicographically compares, at most, the first ulCount characters in pcString1 and pcString2 and returns a value indicating the relationship between the substrings.

char * StrNCpy(
char * pcDest,
const char * pcSource,
unsigned long ulCount)

Copies the initial ulCount characters of pcSource to pcDest and returns pcDest. If ulCount is less than or equal to the length of pcSource, a NULL character is not appended automatically to the copied string. If ulCount is greater than the length of pcSource, the destination string is padded with NULL characters up to length ulCount. The behavior of StrNCpy() is undefined if the source and destination strings overlap.

int StrNICmp(
const char * pcString1,
const char * pcString2,
unsigned long ulCount)

Lexicographically compares lowercase versions of pcString1 and pcString2 and returns a value indicating their relationship (pcString1 less than pcString2, pcString1 equal to pcString2, or pcString1 greater than pcString2).

char * StrRChr(
const char * pcString,
char cChar)

Returns a pointer to the last occurrence of cChar in pcString, or NULL if cChar is not found.

unsigned int StrSpn(
const char * pcString,
const char * pcCharSet)

Returns the index of the first character in pcString that does not belong to the set of characters in pcCharSet. The search does not include terminating NULL characters.

char * StrStr(
const char * pcString,
const char * pcFind)

Returns a pointer to the first occurrence of pcFind in pcString, or NULL if pcFind does not appear in string. If pcFind points to a string of zero length, the function returns string.

char ToLower(
char cChar)

Converts a given uppercase letter to a lowercase letter if possible and appropriate.

char ToUpper(
char cChar)

Converts a given lowercase letter to an uppercase letter if possible and appropriate.

short ToWLower(
short wChar)

Converts a given uppercase letter to a lowercase letter if possible and appropriate.

short ToWUpper(
short wChar)

Converts a given lowercase letter to an uppercase letter if possible and appropriate.

short * WcsCat(
short * pwDest,
const short * pwSource)

Appends pwSource to pwDest. Returns the destination string.

short * WcsChr(
const short * pwString,
short wChar)

Returns a pointer to the first occurrence of wChar in pwString, or NULL if wChar is not found.

int WcsCmp(
const short * pwcString1,
const short * pwcString2)

Compares the two strings and returns the lexicographic relation of pwcString1 to pwcString2 (pwcString1 less than pwcString2, pwcString1 equal to pwcString2, or pwcString1 greater than pwcString2).

short * WcsCpy(
short * pwcDest,
const short * pwcSource)

Copies pwcSource, including the terminating NULL character, to the location specified by pwcDest. The behavior of WcsCpy() is undefined if the source and destination strings overlap. Returns the destination string.

short * WcsDup(
const short * pwcString)

Returns a pointer to the storage location for the copied Unicode string or NULL if storage cannot be allocated. The copied Unicode string is allocated with Malloc and should be freed with Free when it is no longer needed.

int WcsICmp(
const short * pwcString1,
const short * pwcString2)

Lexicographically compares lowercase versions of pwcString1 and pwcString2 and returns a value indicating their relationship (pwcString1 less than pwcString2, pwcString1 equal to pwcString2, or pwcString1 greater than pwcString2).

unsigned long WcsLen(
const short * pwcString)

Returns the number of 16-bit characters in pwcString, excluding the terminal NULL.

short * WcsNCat(
short * pwDest,
const short * pwSource,
unsigned long ulCount)

Appends, at most, ulCount Unicode characters from pwSource to pwDest. Returns the destination string.

int WcsNCmp(
const short * pwcString1,
const short * pwcString2,
unsigned long ulCount)

Lexicographically compares, at most, the first ulCount Unicode characters in pwString1 and pwString2 and returns a value indicating the relationship between the substrings.

short * WcsNCpy(
short * pwcDest,
const short * pwcSource,
unsigned long ulCount)

Copies the initial ulCount Unicode characters of pwSource to pwDest and returns pwDest. If ulCount is less than or equal to the length of pwSource, a NULL character is not appended automatically to the copied string. If ulCount is greater than the length of pwSource, the destination string is padded with NULL Unicode characters up to length ulCount. The behavior of WcsNCpy() is undefined if the source and destination strings overlap.

int WcsNICmp(
const short * pwcString1,
const short * pwcString2,
unsigned long ulCount)

Lexicographically compares lowercase versions of pwcString1 and pwcString2 and returns a value indicating their relationship (pwcString1 less than pwcString2, pwcString1 equal to pwcString2, or pwcString1 greater than pwcString2).

short * WcsRChr(
const short * pwString,
short wChar)

Returns a pointer to the last occurrence of wChar in pwString, or NULL if wChar is not found.

unsigned int WcsSpn(
const short * pwString,
const short * pwCharSet)

Returns the index of the first Unicode character in pwString that does not belong to the set of Unicode characters in pwCharSet. The search does not include terminating NULL characters.

short * WcsStr(
const short * pwString,
const short * pwFind)

Returns a pointer to the first occurrence of pwFind in pwString, or NULL if pwFind does not appear in string. If pwFind points to a string of zero length, the function returns string.

double WtoF(
const short * pwcString1)

Returns the double value produced by interpreting the input Unicode characters as a number.

int WtoI(
const short * pwcString1)

Returns the int value produced by interpreting the input Unicode characters as a number.

int64 WtoI64(
const short * pwcString1)

Returns the 64-bit integer value produced by interpreting the input Unicode characters as a number.


System

VOID KeyboardEvent(
BYTE bVk,
DWORD dwFlags)

Synthesizes a keystroke. The system can use such a synthesized keystroke to generate a WM_KEYUP or WM_KEYDOWN message.
dwFlags can be 0 or the following:
KEYEVENTF_KEYUP: If specified, the key is being released. If not specified, the key is being depressed.

VOID MouseEvent(
DWORD dwFlags,
DWORD dx,
DWORD dy,
DWORD dwData)

Synthesizes mouse motion and button clicks.
dwFlags can be certain combinations of the following values:
MOUSEEVENTF_ABSOLUTE: Specifies that the dx and dy parameters contain normalized absolute coordinates. If not set, those parameters contain relative data: the change in position since the last reported position. This flag can be set, or not set, regardless of what kind of mouse or mouse-like device, if any, is connected to the system.
MOUSEEVENTF_MOVE: Specifies that movement occurred.
MOUSEEVENTF_LEFTDOWN: Specifies that the left button is down.
MOUSEEVENTF_LEFTUP: Specifies that the left button is up.
MOUSEEVENTF_RIGHTDOWN: Specifies that the right button is down.
MOUSEEVENTF_RIGHTUP: Specifies that the right button is up.
MOUSEEVENTF_MIDDLEDOWN: Specifies that the middle button is down.
MOUSEEVENTF_MIDDLEUP: Specifies that the middle button is up.
MOUSEEVENTF_WHEEL: Specifies that the wheel has been moved, if the mouse has a wheel. The amount of movement is specified in dwData.
MOUSEEVENTF_XDOWN: Specifies that an X button was pressed.
MOUSEEVENTF_XUP: Specifies that an X button was released.

unsigned long ProcTime()

Gets the number of milliseconds that have elapsed since the application was started.

UINT SendInput(
UINT uiInputs,
LPINPUT pInputs)

Synthesizes keystrokes, mouse motions, and button clicks. Returns the number of events that it successfully inserted into the keyboard or mouse input stream. If the function returns zero, the input was already blocked by another thread.

VOID Sleep(
DWORD dwMilliseconds)

Causes a thread to relinquish the remainder of its time slice and become unrunnable for at least the specified number of milliseconds, after which the thread is ready to run. In particular, if you specify zero milliseconds, the thread will relinquish the remainder of its time slice but remain ready. Note that a ready thread is not guaranteed to run immediately. Consequently, the thread may not run until some time after the specified interval elapses.

unsigned long Time()

Gets the number of milliseconds that have elapsed since Windows was started.

BOOL Type(
TCHAR * ptcChar)

Calls Type on each character in the string to emulate the typing of the whole string by the current keyboard.

BOOL Type(
TCHAR tcChar)

Uses KeyboardEvent to send the correct virtual keycode(s) to emulate typing of the specified character by the current keyboard. Multiple KeyboardEvent calls may be made if typing the specified character requires holding Shift, Alt, and/or Control.

SHORT VkKeyScan(
TCHAR tcChar)

Translates a character to the corresponding virtual-key code and shift state for the current keyboard.


Win32Debugging

BOOL CheckRemoteDebuggerPresent(
HANDLE hProcess,
PBOOL pbDebuggerPresent)

Determines whether the specified process is being debugged. This function always returns FALSE on machines that do not support it.

BOOL IsDebuggerPresent()

Determines whether Memory Hacking Software is being debugged.

BOOL ReadProcessMemory(
HANDLE hProcess,
LPCVOID lpBaseAddress,
LPVOID lpBuffer,
SIZE_T nSize,
SIZE_T* lpNumberOfBytesRead)

Reads data from an area of memory in a specified process.

BOOL WriteProcessMemory(
HANDLE hProcess,
LPVOID lpBaseAddress,
LPCVOID lpBuffer,
SIZE_T nSize,
SIZE_T* lpNumberOfBytesWritten)

Writes data to an area of memory in a specified process.


Win32Dialogs

BOOL AddButton(
HWND hWnd,
INT iX,
INT iY,
INT iWidth,
INT iHeight,
INT iID,
const _TCHAR * ptcText,
const _TCHAR * ptcActionHandler)

Adds a button to the specified window. When pressed, the script function ptcActionHandler will be called.
ptcActionHandler must be in format:
VOID [function]( HWND hOwner, HWND hButton )

Standard dimensions are 75×23.

BOOL AddCheck(
HWND hWnd,
INT iX,
INT iY,
INT iWidth,
INT iHeight,
INT iID,
const _TCHAR * ptcText,
const _TCHAR * ptcActionHandler)

Adds a check button to the specified window. When pressed, the script function ptcActionHandler will be called, along with a parameter indicating whether the button is checked or unchecked as a result of the press. You can also use IsDlgButtonChecked() to obtain the checked state of the button at any given time.
ptcActionHandler must be in format:
VOID [function]( HWND hOwner, HWND hCheck, BOOL bChecked )

Standard height is 16.

BOOL AddComboItem(
HWND hWnd,
const _TCHAR * ptcText,
DWORD dwData)

Adds a string with related data to the combo box specified by hWnd.

BOOL AddDropCombo(
HWND hWnd,
INT iX,
INT iY,
INT iWidth,
INT iHeight,
INT iID,
const _TCHAR * ptcSelectionHandler)

Adds a drop-list combo box to the specified window. A drop-list combo box has specific items the user must select, and the user can not enter text into it. ptcSelectionHandler defines the script function that will be called when an item from the combo box is selected.
Use SetWindowText() to set the initial selection after using AddComboItem() to fill it with items the user can select.
ptcActionHandler must be in format:
VOID [function]( HWND hOwner, HWND hCombo, const _TCHAR * ptcText, DWORD dwData )
Standard height is 21.

BOOL AddEdit(
HWND hWnd,
INT iX,
INT iY,
INT iWidth,
INT iHeight,
INT iID,
const _TCHAR * ptcActionHandler)

Adds an edit box to the specified window. When the text in the edit box changes, the script function ptcActionHandler will be called.
ptcActionHandler must be in format:
VOID [function]( HWND hOwner, HWND hEdit )
Standard height is 20.

BOOL AddLabel(
HWND hWnd,
INT iX,
INT iY,
INT iWidth,
INT iHeight,
INT iID,
const _TCHAR * ptcText)

Adds a label to the specified window. Same as AddStatic().
Standard height is 13.

BOOL AddStatic(
HWND hWnd,
INT iX,
INT iY,
INT iWidth,
INT iHeight,
INT iID,
const _TCHAR * ptcText)

Adds a label to the specified window. Same as AddLabel().
Standard height is 13.

BOOL AddUpDown(
HWND hWnd,
INT iX,
INT iY,
INT iWidth,
INT iHeight,
INT iID,
INT iLow,
INT iHigh,
INT iPos,
const _TCHAR * ptcActionHandler)

Adds an Up/Down control to the specified window. An Up/Down control allows users to press up/down buttons to increment and decrement a value or selection inside a combo box. ptcActionHandler defines the script function that will be called when the user presses an arrow in the Up/Down control. Inside this function, return WIN_ALLOW to allow the change or WIN_DISALLOW to prevent the change. This function allows you to specify the range of the control and a starting position. ptcActionHandler must be in format:
BOOL [function]( HWND hOwner, HWND hUpDown, INT iPrevNum, INT iDelta )
The function should return TRUE if the change should be allowed.

BOOL AddUpDown(
HWND hWnd,
INT iX,
INT iY,
INT iWidth,
INT iHeight,
INT iID,
INT iPos,
const _TCHAR * ptcActionHandler)

Adds an Up/Down control to the specified window. An Up/Down control allows users to press up/down buttons to increment and decrement a value or selection inside a combo box. ptcActionHandler defines the script function that will be called when the user presses an arrow in the Up/Down control. Inside this function, return WIN_ALLOW to allow the change or WIN_DISALLOW to prevent the change. This function allows you to specify a starting position. ptcActionHandler must be in format:
BOOL [function]( HWND hOwner, HWND hUpDown, INT iPrevNum, INT iDelta )
The function should return TRUE if the change should be allowed.

BOOL AddUpDown(
HWND hWnd,
INT iX,
INT iY,
INT iWidth,
INT iHeight,
INT iID,
const _TCHAR * ptcActionHandler)

Adds an Up/Down control to the specified window. An Up/Down control allows users to press up/down buttons to increment and decrement a value or selection inside a combo box. ptcActionHandler defines the script function that will be called when the user presses an arrow in the Up/Down control. Inside this function, return WIN_ALLOW to allow the change or WIN_DISALLOW to prevent the change. ptcActionHandler must be in format:
BOOL [function]( HWND hOwner, HWND hUpDown, INT iPrevNum, INT iDelta )
The function should return TRUE if the change should be allowed.

HDC BeginPaint(
HWND hWnd,
LPPAINTSTRUCT lpPaint)

Prepares the specified window for painting and fills a PAINTSTRUCT structure with information about the painting. Returns the HDC for the specified window, or NULL for error.
Call EndPaint() when you are done painting the window.

BOOL CaptureScreen(
const LPRECT lprRegion,
const _TCHAR * ptcFile)

Captures an area of the screen and saves the image to a BMP file specified by ptcFile. lprRegion is a pointer to a RECT structure containing the region to capture. If lprRegion is NULL, the whole screen is captured.

BOOL CheckDlgButton(
HWND hWnd,
INT iID,
UINT uiCheck)

Changes the check state of a check-button control.
uiCheck can be one of the following values:
BST_CHECKED: Sets the button state to checked.
BST_INDETERMINATE: Sets the button state to grayed, indicating an indeterminate state.
BST_UNCHECKED: Sets the button state to cleared

BOOL ClipCursor(
const RECT * lpRect)

Confines the cursor to a rectangular area on the screen. If a subsequent cursor position (set by the SetCursorPos function or the mouse) lies outside the rectangle, the system automatically adjusts the position to keep the cursor inside the rectangular area.

BOOL CloseWindow(
HWND hWnd)

Minimizes (but does not destroy) the specified window. To destroy a window, the script must use the DestroyWindow() function.

HWND CreateDialog(
HWND hOwner,
const CHAR * pcTitle,
INT iX,
INT iY,
INT iWidth,
INT iHeight,
const TCHAR * ptcHandler)

Creates a dialog window at the specified position with the specified width, height, and name. ptcHandler identifies the script-defined function for handling the window’s messages.

HWND CreateWindow(
const CHAR * pcTitle,
INT iX,
INT iY,
INT iWidth,
INT iHeight,
const TCHAR * ptcHandler)

Creates a window at the specified position with the specified width, height, and name. ptcHandler identifies the script-defined function for handling the window’s messages.

BOOL DestroyWindow(
HWND hWnd)

Destroys the specified window. The function sends WM_DESTROY and WM_NCDESTROY messages to the window.
This function can only be used to destroy windows created via scripts.

BOOL DrawBitmap(
HDC hDC,
const CHAR * pcFile,
INT iX,
INT iY,
BOOL bTrans)

Draws a rectangle using the current brush (fill) and pen (outline).

BOOL DrawCaption(
HDC hDC,
LPCTSTR lpString,
int nCount,
LPRECT lpRect)

Draws a window caption. uiFlags can be 0 or any of the following flags combined with the logical OR (|) operator:
DC_ACTIVE: The function uses the colors that denote an active caption.
DC_BUTTONS: If set, the function draws the buttons in the caption bar (to minimize, restore, or close an application).
DC_GRADIENT: Uses the system settings to apply a gradient to the caption.
DC_ICON: The function draws the icon when drawing the caption text.
DC_INBUTTON: The function draws the caption as a button.
DC_SMALLCAP: The function draws a small caption, using the current small caption font.
DC_TEXT: The function draws the caption text when drawing the caption.

BOOL DrawLine(
HDC hDC,
COLORREF crColor,
INT iX1,
INT iY1,
INT iX2,
INT iY2)

Draws a line from the starting point up to, but not including, the end point, using the specified color

BOOL DrawLine(
HDC hDC,
COLORREF crColor,
LPPOINT lpStart,
LPPOINT lpEnd)

Draws a line from the starting point up to, but not including, the end point, using the specified color

BOOL DrawLine(
HDC hDC,
INT iX1,
INT iY1,
INT iX2,
INT iY2)

Draws a line from the starting point up to, but not including, the end point. The line is drawn by using the current pen and, if the pen is a geometric pen, the current brush.

BOOL DrawLine(
HDC hDC,
LPPOINT lpStart,
LPPOINT lpEnd)

Draws a line from the starting point up to, but not including, the end point. The line is drawn by using the current pen and, if the pen is a geometric pen, the current brush.

BOOL DrawRect(
HDC hDC,
COLORREF crOutter,
COLORREF crInner,
INT iLeft,
INT iTop,
INT iRight,
INT iBottom)

Draws a rectangle using the specified colors.

BOOL DrawRect(
HDC hDC,
COLORREF crOutter,
COLORREF crInner,
LPCRECT lpcRect)

Draws a rectangle using the specified colors.

BOOL DrawRect(
HDC hDC,
INT iLeft,
INT iTop,
INT iRight,
INT iBottom)

Draws a rectangle using the current brush (fill) and pen (outline).

BOOL DrawRect(
HDC hDC,
LPCRECT lpcRect)

Draws a rectangle using the current brush (fill) and pen (outline).

int DrawText(
HDC hDC,
LPCTSTR lpString,
int nCount,
LPRECT lpRect,
UINT uFormat)

Draws formatted text in the specified rectangle. It formats the text according to the specified method (expanding tabs, justifying characters, breaking lines, and so forth). uFormat can be one or more of the following values:
DT_BOTTOM: Justifies the text to the bottom of the rectangle. This value is used only with the DT_SINGLELINE value.
DT_CALCRECT: Determines the width and height of the rectangle. If there are multiple lines of text, DrawText uses the width of the rectangle pointed to by the lpRect parameter and extends the base of the rectangle to bound the last line of text. If the largest word is wider than the rectangle, the width is expanded. If the text is less than the width of the rectangle, the width is reduced. If there is only one line of text, DrawText modifies the right side of the rectangle so that it bounds the last character in the line. In either case, DrawText returns the height of the formatted text but does not draw the text.
DT_CENTER: Centers text horizontally in the rectangle.
DT_EDITCONTROL: Duplicates the text-displaying characteristics of a multiline edit control. Specifically, the average character width is calculated in the same manner as for an edit control, and the function does not display a partially visible last line.
DT_END_ELLIPSIS: For displayed text, if the end of a string does not fit in the rectangle, it is truncated and ellipses are added. If a word that is not at the end of the string goes beyond the limits of the rectangle, it is truncated without ellipses.
The string is not modified unless the DT_MODIFYSTRING flag is specified.
Compare with DT_PATH_ELLIPSIS and DT_WORD_ELLIPSIS.
DT_EXPANDTABS: Expands tab characters. The default number of characters per tab is eight. The DT_WORD_ELLIPSIS, DT_PATH_ELLIPSIS, and DT_END_ELLIPSIS values cannot be used with the DT_EXPANDTABS value.
DT_EXTERNALLEADING: Includes the font external leading in line height. Normally, external leading is not included in the height of a line of text.
DT_HIDEPREFIX: Ignores the ampersand (&) prefix character in the text. The letter that follows will not be underlined, but other mnemonic-prefix characters are still processed. Compare with DT_NOPREFIX and DT_PREFIXONLY.
DT_INTERNAL: Uses the system font to calculate text metrics.
DT_LEFT: Aligns text to the left.
DT_MODIFYSTRING: Modifies the specified string to match the displayed text. This value has no effect unless DT_END_ELLIPSIS or DT_PATH_ELLIPSIS is specified.
DT_NOCLIP: Draws without clipping. DrawText is somewhat faster when DT_NOCLIP is used.
DT_NOFULLWIDTHCHARBREAK: Prevents a line break at a DBCS (double-wide character string), so that the line breaking rule is equivalent to SBCS strings. For example, this can be used in Korean windows, for more readability of icon labels. This value has no effect unless DT_WORDBREAK is specified.
DT_NOPREFIX: Turns off processing of prefix characters. Normally, DrawText interprets the mnemonic-prefix character & as a directive to underscore the character that follows, and the mnemonic-prefix characters && as a directive to print a single &. By specifying DT_NOPREFIX, this processing is turned off.
DT_PATH_ELLIPSIS: For displayed text, replaces characters in the middle of the string with ellipses so that the result fits in the specified rectangle. If the string contains backslash characters, DT_PATH_ELLIPSIS preserves as much as possible of the text after the last backslash.
The string is not modified unless the DT_MODIFYSTRING flag is specified.
Compare with DT_END_ELLIPSIS and DT_WORD_ELLIPSIS.
DT_RIGHT: Aligns text to the right.
DT_RTLREADING: Layout in right-to-left reading order for bi-directional text when the font selected into the hdc is a Hebrew or Arabic font. The default reading order for all text is left-to-right.
DT_SINGLELINE: Displays text on a single line only. Carriage returns and line feeds do not break the line.
DT_TABSTOP: Sets tab stops. Bits 15-8 (high-order byte of the low-order word) of the uFormat parameter specify the number of characters for each tab. The default number of characters per tab is eight. The DT_CALCRECT, DT_EXTERNALLEADING, DT_INTERNAL, DT_NOCLIP, and DT_NOPREFIX values cannot be used with the DT_TABSTOP value.
DT_TOP: Justifies the text to the top of the rectangle.
DT_VCENTER: Centers text vertically. This value is used only with the DT_SINGLELINE value.
DT_WORDBREAK: Breaks words. Lines are automatically broken between words if a word would extend past the edge of the rectangle specified by the lpRect parameter. A carriage return-line feed sequence also breaks the line.
If this is not specified, output is on one line.
DT_WORD_ELLIPSIS: Truncates any word that does not fit in the rectangle and adds ellipses.
Compare with DT_END_ELLIPSIS and DT_PATH_ELLIPSIS.

BOOL EndPaint(
HWND hWnd,
const PAINTSTRUCT * lpPaint)

Marks the end of painting in the specified window. This function is required for each call to the BeginPaint() function, but only after painting is complete.

BOOL EnumChildWindows(
HWND hWndParent,
LPCSTR lpEnumFunc,
LPARAM lParam)

Enumerates the child windows that belong to the specified parent window by passing the handle to each child window, in turn, to an application-defined callback function. EnumChildWindows continues until the last child window is enumerated or the callback function returns FALSE. lpEnumFunc must be in the format BOOL Func( HWND hwnd, LPARAM lParam ).

BOOL EnumWindows(
LPCSTR lpEnumFunc,
LPARAM lParam)

Enumerates all top-level windows on the screen by passing the handle to each window, in turn, to an application-defined callback function. EnumWindows continues until the last top-level window is enumerated or the callback function returns FALSE. lpEnumFunc must be in the format BOOL Func( HWND hwnd, LPARAM lParam ).

BOOL FindWindow(
LPCTSTR lpClassName,
LPCTSTR lpWindowName)

Retrieves a handle to the top-level window whose class name and window name match the specified strings. This function does not search child windows. This function does not perform a case-sensitive search.
To search child windows, beginning with a specified child window, use the FindWindowEx() function.

BOOL FindWindow(
LPCTSTR lpWindowName)

Retrieves a handle to the top-level window whose window name matches the specified string. This function does not search child windows. This function does not perform a case-sensitive search.
To search child windows, beginning with a specified child window, use the FindWindowEx() function.

BOOL FindWindowEx(
HWND hWnd,
HWND hwndChildAfter,
LPCTSTR lpClassName,
LPCTSTR lpWindowName)

Retrieves a handle to a window whose class name and window name match the specified strings. The function searches child windows, beginning with the one following the specified child window. This function does not perform a case-sensitive search.

HWND GetActiveWindow()

Retrieves the window handle to the active window attached to the calling thread’s message queue.

COLORREF GetBkColor(
HDC hDC)

Returns the current background color for the specified device context. If hDC is invalid, it returns CLR_INVALID.

BOOL GetClientHeight(
HWND hWnd)

Gets the height of the specified window’s client area.

BOOL GetClientRect(
HWND hWnd,
LPRECT lpRect)

Retrieves the coordinates of a window’s client area. The client coordinates specify the upper-left and lower-right corners of the client area. Because client coordinates are relative to the upper-left corner of a window’s client area, the coordinates of the upper-left corner are (0,0).

BOOL GetClientWidth(
HWND hWnd)

Gets the width of the specified window’s client area.

BOOL GetCursorPos(
LPPOINT lpPoint)

Retrieves the cursor’s position, in screen coordinates.

HWND GetDlgItem(
HWND hWnd,
INT iID)

Retrieves a handle to a control in the specified dialog box.

HWND GetFocus()

Retrieves the handle to the window that has the keyboard focus, if the window is attached to the calling thread’s message queue.

HWND GetForegroundWindow()

Returns a handle to the foreground window (the window with which the user is currently working). The system assigns a slightly higher priority to the thread that creates the foreground window than it does to other threads.

COLORREF GetPixel(
HDC hDC,
INT nXPos,
INT nYPos)

Retrieves the red, green, blue (RGB) color value of the pixel at the specified coordinates.

COLORREF GetTextColor(
HDC hDC)

Retrieves the current text color for the specified device context. If hDC is invalid, it returns CLR_INVALID.

HDC GetWindowDC(
HWND hWnd)

Retrieves the device context (DC) for the entire window, including title bar, menus, and scroll bars. A window device context permits painting anywhere in a window, because the origin of the device context is the upper-left corner of the window instead of the client area.
Call ReleaseDC() to release the device context when you are done with it.

BOOL GetWindowHeight(
HWND hWnd)

Gets the height of the specified window.

BOOL GetWindowPos(
HWND hWnd,
INT * piX,
INT * piY)

Gets the position of the specified window.

BOOL GetWindowRect(
HWND hWnd,
LPRECT lpRect)

Retrieves the dimensions of the bounding rectangle of the specified window. The dimensions are given in screen coordinates that are relative to the upper-left corner of the screen.

BOOL GetWindowSize(
HWND hWnd,
INT * piWidth,
INT * piHeight)

Gets the width and height of the specified window.

BOOL GetWindowText(
HWND hWnd,
LPTSTR lpString,
int nMaxCount)

Copies the text of the specified window’s title bar (if it has one) into a buffer. If the specified window is a control, the text of the control is copied. However, GetWindowText() cannot retrieve the text of a control in another application.

INT GetWindowTextLength(
HWND hWnd)

Retrieves the length, in characters, of the specified window’s title bar text (if the window has a title bar). If the specified window is a control, the function retrieves the length of the text within the control. However, GetWindowTextLength() cannot retrieve the length of the text of an edit control in another application.

BOOL GetWindowWidth(
HWND hWnd)

Gets the width of the specified window.

BOOL HIWORD(
DWORD dwParam)

Gets the upper 16 bits from the specified value.

BOOL IsDlgButtonChecked(
HWND hWnd,
INT iID)

Whether a check-button control has a check mark next to it or whether a three-state button control is grayed, checked, or neither.

BOOL IsWindow(
HWND hWnd)

Determines whether the specified window handle identifies an existing window.

BOOL IsWindowEnabled(
HWND hWnd)

Determines whether the specified window is enabled for mouse and keyboard input.

BOOL IsWindowVisible(
HWND hWnd)

Retrieves the visibility state of the specified window.

BOOL LOWORD(
DWORD dwParam)

Gets the lower 16 bits from the specified value.

DWORD MAKELONG(
WORD wLow,
WORD wHigh)

Creates a DWORD value by concatenating the specified values.

WORD MAKEWORD(
BYTE bLow,
BYTE bHigh)

Creates a WORD value by concatenating the specified values.

BOOL PostMessage(
HWND hWnd,
UINT Msg,
WPARAM wParam,
LPARAM lParam)

Places (posts) a message in the message queue associated with the thread that created the specified window and returns without waiting for the thread to process the message.

COLORREF RGB(
DWORD dwR,
DWORD dwG,
DWORD dwB)

Converts the specified red, green, and blue values to a COLORREF type.

INT ReleaseDC(
HWND hWnd,
HDC hDC)

Releases a device context (DC), freeing it for use by other applications.

LRESULT SendDlgItemMessage(
HWND hDlg,
INT nIDDlgItem,
UINT Msg,
WPARAM wParam,
LPARAM lParam)

Sends a message to the specified control in a dialog box. The SendDlgItemMessage() function does not return until the message has been processed.

BOOL SendMessage(
HWND hWnd,
UINT Msg,
WPARAM wParam,
LPARAM lParam)

Sends the specified message to a window or windows. It calls the window procedure for the specified window and does not return until the window procedure has processed the message.

HWND SetActiveWindow(
HWND hWnd)

Activates a window. The window must be attached to the calling thread’s message queue.

COLORREF SetBkColor(
HDC hDC,
COLORREF crColor)

Sets the current background color to the specified color value, or to the nearest physical color if the device cannot represent the specified color value. The return value specifies the previous background color as a COLORREF value.

BOOL SetCursorPos(
INT iX,
INT iY)

Moves the cursor to the specified screen coordinates. If the new coordinates are not within the screen rectangle set by the most recent ClipCursor function call, the system automatically adjusts the coordinates so that the cursor stays within the rectangle.

HWND SetFocus(
HWND hWnd)

Sets the keyboard focus to the specified window. The window must be attached to the calling thread’s message queue.

BOOL SetForegroundWindow(
HWND hWnd)

Puts the thread that created the specified window into the foreground and activates the window. Keyboard input is directed to the window, and various visual cues are changed for the user. The system assigns a slightly higher priority to the thread that created the foreground window than it does to other threads.

COLORREF SetPixel(
HDC hDC,
INT nXPos,
INT nYPos,
COLORREF crColor)

Sets the pixel at the specified coordinates to the specified color.

COLORREF SetTextColor(
HDC hDC,
COLORREF crColor)

Sets the text color for the specified device context to the specified color. The return value specifies the previous text color as a COLORREF value.

BOOL SetWindowPos(
HWND hWnd,
INT iX,
INT iY)

Sets the position of the specified window.

BOOL SetWindowSize(
HWND hWnd,
INT iWidth,
INT iHeight)

Sets the width and height of the specified window.

BOOL SetWindowText(
HWND hWnd,
LPCTSTR lpString)

Changes the text of the specified window’s title bar (if it has one). If the specified window is a control, the text of the control is changed. However, SetWindowText() cannot change the text of a control in another application.

BOOL ShowWindow(
HWND hWnd,
INT nCmdShow)

Sets the specified window’s show state.
nCmdShow can be one of the following:
SW_FORCEMINIMIZE: Minimizes a window, even if the thread that owns the window is not responding. This flag should only be used when minimizing windows from a different thread.
SW_HIDE: Hides the window and activates another window.
SW_MAXIMIZE: Maximizes the specified window.
SW_MINIMIZE: Minimizes the specified window and activates the next top-level window in the Z order.
SW_RESTORE: Activates and displays the window. If the window is minimized or maximized, the system restores it to its original size and position. An application should specify this flag when restoring a minimized window.
SW_SHOW: Activates the window and displays it in its current size and position.
SW_SHOWDEFAULT: Sets the show state based on the SW_ value specified in the STARTUPINFO structure passed to the CreateProcess function by the program that started the application.
SW_SHOWMAXIMIZED: Activates the window and displays it as a maximized window.
SW_SHOWMINIMIZED: Activates the window and displays it as a minimized window.
SW_SHOWMINNOACTIVE: Displays the window as a minimized window. This value is similar to SW_SHOWMINIMIZED, except the window is not activated.
SW_SHOWNA: Displays the window in its current size and position. This value is similar to SW_SHOW, except the window is not activated.
SW_SHOWNOACTIVATE: Displays a window in its most recent size and position. This value is similar to SW_SHOWNORMAL, except the window is not actived.
SW_SHOWNORMAL: Activates and displays a window. If the window is minimized or maximized, the system restores it to its original size and position. An application should specify this flag when displaying the window for the first time.

BOOL ShowWindowAsync(
HWND hWnd,
INT nCmdShow)

Sets the show state of a window created by a different thread. nCmdShow is the same as in ShowWindow().


Win32Files

BOOL CopyFile(
LPCTSTR lpExistingFileName,
LPCTSTR lpNewFileName,
BOOL bFailIfExists)

Copies an existing file to a new file.

BOOL CreateDirectory(
LPCTSTR lpPathName)

Creates a new directory.

BOOL DeleteFile(
LPCTSTR lpFileName)

Deletes an existing file.

BOOL FindClose(
HANDLE hObject)

Closes a file search handle opened by the FindFirstFile function.

HANDLE FindFirstFile(
LPCTSTR lpFileName,
LPWIN32_FIND_DATA lpFindFileData)

Searches a directory for a file or subdirectory with a name that matches a specific name.

BOOL FindNextFile(
HANDLE hFindFile,
LPWIN32_FIND_DATA lpFindFileData)

Continues a file search from a previous call to the FindFirstFile function.

BOOL MoveFile(
LPCTSTR lpExistingFileName,
LPCTSTR lpNewFileName)

Moves an existing file or a directory, including its children.

BOOL PathCanonicalize(
LPTSTR lpszDst,
LPCTSTR lpszSrc)

Canonicalizes a path.

LPTSTR PathCombine(
LPTSTR lpszDest,
LPCTSTR lpszDir,
LPCTSTR lpszFile)

Concatenates two strings that represent properly formed paths into one path, as well as any relative path pieces. The directory path should be in the form of A:, B:, …, Z:. The file path should be in a correct form that represents the file part of the path. The file path must not be null, and if it ends with a backslash, the backslash will be maintained.

BOOL PathIsURL(
LPCTSTR pszPath)

Tests a given string to determine if it conforms to a valid URL format.

VOID PathQuoteSpaces(
LPTSTR pszPath)

Searches a path for spaces. If spaces are found, the entire path is enclosed in quotation marks.

BOOL PathRelativePathTo(
LPTSTR pszPath,
LPCTSTR pszFrom,
DWORD dwAttrFrom,
LPCTSTR pszTo,
DWORD dwAttrTo)

This function takes a pair of paths and generates a relative path from one to the other. The paths do not have to be fully-qualified, but they must have a common prefix, or the function will fail and return FALSE.
For example, let the starting point, pszFrom, be"C:\FolderA\FolderB\FolderC", and the ending point, pszTo, be"C:\FolderA\FolderD\FolderE". PathRelativePathTo will return the relative path from pszFrom to pszTo as:"..\..\FolderD\FolderE". You will get the same result if you set pszFrom to "\FolderA\FolderB\FolderC"and pszTo to"\FolderA\FolderD\FolderE". On the other hand, "C:\FolderA\FolderB"and "A:\FolderA\FolderD"do not share a common prefix, and the function will fail.

VOID PathRemoveArgs(
LPTSTR pszPath)

Removes any arguments from a given path.

LPTSTR PathRemoveBackslash(
LPTSTR pszPath)

Removes the trailing backslash from a given path. Returns the address of the NULL that replaced the backslash, or the address of the last character if it’s not a backslash.

VOID PathRemoveBlanks(
LPTSTR lpszString)

Removes all leading and trailing spaces from a string.

VOID PathRemoveExtension(
LPTSTR pszPath)

Removes the file extension from a path, if there is one.

BOOL PathRemoveFileSpec(
LPTSTR pszPath)

Removes the trailing file name and backslash from a path, if it has them.

BOOL PathRenameExtension(
LPTSTR pszPath,
LPCTSTR pszExt)

Replaces the extension of a file name with a new extension. If the file name does not contain an extension, the extension will be attached to the end of the string.

BOOL RemoveDirectory(
LPCTSTR lpPathName)

Deletes an existing empty directory.


Win32Memory

LPVOID VirtualAllocEx(
HANDLE hProcess,
LPVOID lpAddress,
SIZE_T dwSize,
DWORD flAllocationType,
DWORD flProtect)

Reserves or commits a region of memory within the virtual address space of a specified process. The function initializes the memory it allocates to zero, unless MEM_RESET is used.

BOOL VirtualFreeEx(
HANDLE hProcess,
LPVOID lpAddress,
SIZE_T dwSize,
DWORD dwFreeType)

Releases, decommits, or releases and decommits a region of memory within the virtual address space of a specified process.

BOOL VirtualProtectEx(
HANDLE hProcess,
LPVOID lpAddress,
SIZE_T dwSize,
DWORD flNewProtect,
PDWORD lpflOldProtect)

Changes the protection on a region of committed pages in the virtual address space of a specified process.

DWORD VirtualQueryEx(
HANDLE hProcess,
LPVOID lpAddress,
PMEMORY_BASIC_INFORMATION lpBuffer,
SIZE_T dwLength)

Provides information about a range of pages within the virtual address space of a specified process.

VOID ZeroMemory(
PVOID Destination,
SIZE_T Length)

Fills a block of memory with zeros.


Win32PerfMon

HANDLE CreateToolhelp32Snapshot(
DWORD dwFlags,
DWORD th32ProcessID)

Takes a snapshot of the specified processes in the system, as well as the heaps, modules, and threads used by these processes. Returns INVALID_HANDLE_VALUE on failure. The handle is closed automatically when the script object is destroyed.

BOOL Heap32First(
LPHEAPENTRY32 lphe,
DWORD th32ProcessID,
ULONG_PTR th32HeapID)

Retrieves information about the first block of a heap that has been allocated by a process.

BOOL Heap32ListFirst(
HANDLE hSnapshot,
LPHEAPLIST32 lphl)

Retrieves information about the first heap that has been allocated by a specified process.

BOOL Heap32ListNext(
HANDLE hSnapshot,
LPHEAPLIST32 lphl)

Retrieves information about the next heap that has been allocated by a process.

BOOL Heap32Next(
LPHEAPENTRY32 lphe)

Retrieves information about the next block of a heap that has been allocated by a process.

BOOL Module32First(
HANDLE hSnapshot,
LPMODULEENTRY32 lpme)

Retrieves information about the first module associated with a process.

BOOL Module32Next(
HANDLE hSnapshot,
LPMODULEENTRY32 lpme)

Retrieves information about the next module associated with a process or thread.

BOOL Process32First(
HANDLE hSnapshot,
LPPROCESSENTRY32 lppe)

Retrieves information about the first process encountered in a system snapshot.

BOOL Process32Next(
HANDLE hSnapshot,
LPPROCESSENTRY32 lppe)

Retrieves information about the next process recorded in a system snapshot.

BOOL Thread32First(
HANDLE hSnapshot,
LPTHREADENTRY32 lpte)

Retrieves information about the first thread of any process encountered in a system snapshot.

BOOL Thread32Next(
HANDLE hSnapshot,
LPTHREADENTRY32 lpte)

Retrieves information about the next thread of any process encountered in the system memory snapshot.


Win32Sys

PIDLIST_ABSOLUTE BrowseForFolder(
LPBROWSEINFO lpbiInfo)

Displays a dialog box that enables the user to select a Shell folder. Returns a pointer to an item identifier list (PIDL) specifying the location of the selected folder relative to the root of the namespace. If the user chooses the Cancel button in the dialog box, the return value is NULL.

BOOL CloseHandle(
HANDLE hObject)

Closes an open object handle.

INT Execute(
HWND hWnd,
LPCTSTR lpOperation,
LPCTSTR lpFile,
LPCTSTR lpParameters,
LPCTSTR lpDirectory,
INT nShowCmd)

Performs an operation on a specified file. Returns a value greater than 32 if successful, or an error value that is less than or equal to 32 otherwise. The following table lists the error values:
0: The operating system is out of memory or resources.
ERROR_FILE_NOT_FOUND: The specified file was not found.
ERROR_PATH_NOT_FOUND: The specified path was not found.
ERROR_BAD_FORMAT: The .exe file is invalid (non-Microsoft Win32 .exe or error in .exe image).
SE_ERR_ACCESSDENIED: The operating system denied access to the specified file.
SE_ERR_ASSOCINCOMPLETE: The file name association is incomplete or invalid.
SE_ERR_DDEBUSY: The Dynamic Data Exchange (DDE) transaction could not be completed because other DDE transactions were being processed.
SE_ERR_DDEFAIL: The DDE transaction failed.
SE_ERR_DDETIMEOUT: The DDE transaction could not be completed because the request timed out.
SE_ERR_DLLNOTFOUND: The specified DLL was not found.
SE_ERR_FNF: The specified file was not found.
SE_ERR_NOASSOC: There is no application associated with the given file name extension. This error will also be returned if you attempt to print a file that is not printable.
SE_ERR_OOM: There was not enough memory to complete the operation.
SE_ERR_PNF: The specified path was not found.
SE_ERR_SHARE: A sharing violation occurred.

BOOL GetPathFromIDList(
PCIDLIST_ABSOLUTE pidlaList,
LPTSTR pszPath)

Converts an item identifier list to a file system path.

INT GetPrimScreenHeight()

Gets the height (in pixels) of the primary display device.

INT GetPrimScreenWidth()

Gets the width (in pixels) of the primary display device.

INT GetSystemMetrics(
INT nIndex)

Retrieves various system metrics (widths and heights of display elements) and system configuration settings. All dimensions retrieved by GetSystemMetrics are in pixels.


Win32Threading

BOOL CallRemoteFunction(
REMOTE_CALL_RETURN * prcrRet,
LPVOID lpvFuncAddress,
DWORD dwExtra,
INT iParms,
...)

Calls a function in the target process. Returns TRUE if the call succeeds, FALSE otherwise. The function must exist inside the currently opened process. dwExtra can be a combination of RCE_NOTHING, RCE_STDCALL, RCE_FLOAT, and RCE_DOUBLE. RCE_FLOAT and RCE_DOUBLE can not be used together. The return value is stored in prcrRet, which must not be NULL.
iParms is the number of parameters to pass to the function. Note that any 64-bit parameters must be counted twice. For example, if there is only one parameter, and it is a UINT64 type, iParms will be 2 rather than 1.

BOOL CallRemoteFunction(
REMOTE_CALL_RETURN * prcrRet,
LPVOID lpvFuncAddress,
DWORD dwExtra,
UINT_PTR * puiRegisters,
INT iParms,
...)

Calls a function in the target process. Returns TRUE if the call succeeds, FALSE otherwise. The function must exist inside the currently opened process. dwExtra can be a combination of RCE_NOTHING, RCE_STDCALL, RCE_FLOAT, and RCE_DOUBLE. RCE_FLOAT and RCE_DOUBLE can not be used together. The return value is stored in prcrRet, which must not be NULL.
puiRegisters is an array of 8 UINT_PTR values that indicate the values to which to set each register before calling the function. Use the MHS_E* enumerated values as indices to set the values in your array. For example, uiRegsArray[MHS_ECX] = 0x90;.
iParms is the number of parameters to pass to the function. Note that any 64-bit parameters must be counted twice. For example, if there is only one parameter, and it is a UINT64 type, iParms will be 2 rather than 1.

HANDLE CreateRemoteThread(
HANDLE hProcess,
LPVOID lpStartAddress,
LPVOID lpParameter,
LPDWORD lpThreadId)

Creates a thread that runs in the virtual address space of another process. If the target process is being debugged and the returned HANDLE is to be used in WaitForSingleObject, this function must be called on a separate script thread. In all other cases, this can be called on any thread. lpThreadId can be NULL. If the function fails, the return value is NULL.

DWORD CreateRemoteThreadAndGetReturn(
BOOL * pbSuccess,
HANDLE hProcess,
LPVOID lpStartAddress,
LPVOID lpParameter)

Creates a thread that runs in the virtual address space of another process and returns the return value of that thread (which will be the return value of the function called in the remote process). This is safe to call on any thread, regardless of whether or not the target process is being debugged. lpThreadId can be NULL. If the function fails, the BOOL to which pbSuccess points is set to FALSE. This function can only call __stdcall functions that accept one parameter, and the return is stored as a DWORD. If the target function has any other return type, the DWORD returned by this function can be explicitly cast to that type, unless the type is 64 bits in size.

HANDLE CreateThread(
const _TCHAR * ptcFunc,
DWORD dwParm)

Calls the script function specified by ptcFunc on a new thread and returns a HANDLE to that thread. The target function must be in the format DWORD [function]( DWORD dwParm ). dwParm is passed to the script function specified by ptcFunc.
When using multithreaded scripts, care should be taken to avoid standard thread-related deadlocks caused by multiple threads waiting on each other for exclusive access to resources, specifically “synchronized” functions.

BOOL FreeLibrary(
HMODULE hModule)

Decrements the reference count of the loaded dynamic-link library (DLL). When the reference count reaches zero, the module is unmapped from the address space of Memory Hacking Software and the handle is no longer valid.

HANDLE GetCurrentThread()

Retrieves a pseudo handle for the current thread.

DWORD GetCurrentThreadId()

Retrieves the thread identifier of the calling thread.

LPVOID GetEproc(
DWORD dwId)

Returns a pointer to the specified process’ EPROCESS structure or NULL if the process ID is invalid.

BOOL GetExitCodeThread(
HANDLE hThread,
LPDWORD lpExitCode)

Retrieves the termination status of the specified thread. The termination status is the value returned by the function called on the specified thread.

HMODULE GetModuleHandle(
LPCTSTR lpModuleName)

Retrieves a module handle for the specified module.

BOOL GetThreadContext(
HANDLE hThread,
LPCONTEXT lpContext)

Retrieves the context of the specified thread. The thread must be suspended. The function allows a selective context to be retrieved based on the value of the ContextFlags member of the CONTEXT structure. The thread handle identified by the hThread parameter is typically being debugged, but the function can also operate when it is not being debugged.

INT GetThreadPriority(
HANDLE hThread)

Retrieves the priority value for the specified thread. This value, together with the priority class of the thread’s process, determines the thread’s base-priority level.

HMODULE LoadLibrary(
LPCTSTR lpFileName)

Maps the specified executable module into the address space of Memory Hacking Software. Libraries loaded with this function are automatically unloaded when scripts are recompiled or Memory Hacking Software closes. The library can be manually unloaded with FreeLibrary.

HMODULE LoadLibraryEx(
LPCTSTR lpFileName,
DWORD dwFlags)

Maps the specified executable module into the address space of Memory Hacking Software. The executable module can be a .dll or an .exe file. The specified module may cause other modules to be mapped into the address space. Libraries loaded with this function are automatically unloaded when scripts are recompiled or Memory Hacking Software closes. The library can be manually unloaded with FreeLibrary.

HANDLE OpenProcess(
DWORD dwDesiredAccess,
DWORD dwProcessId)

Opens an existing process object. If the function succeeds, the return value is an open handle to the specified process. If the function fails, the return value is NULL. The returned HANDLE must be closed with CloseHandle when no longer needed.
dwDesiredAccess can be any combination of the following:
PROCESS_ALL_ACCESS: All possible access rights for a process object.
PROCESS_CREATE_PROCESS: Required to create a process.
PROCESS_CREATE_THREAD: Required to create a thread.
PROCESS_DUP_HANDLE: Required to duplicate a handle.
PROCESS_QUERY_INFORMATION: Required to retrieve certain information about a process, such as its token, exit code, and priority class.
PROCESS_SET_QUOTA: Required to set memory limits.
PROCESS_SET_INFORMATION: Required to set certain information about a process, such as its priority class.
PROCESS_TERMINATE: Required to terminate a process.
PROCESS_VM_OPERATION: Required to perform an operation on the address space of a process (see VirtualProtectEx and WriteProcessMemory).
PROCESS_VM_READ: Required to read memory in a process using ReadProcessMemory.
PROCESS_VM_WRITE: Required to write to memory in a process using WriteProcessMemory.
SYNCHRONIZE: Required to wait for the process to terminate using the wait functions (specifically WaitForSingleObject).

HANDLE OpenThread(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
DWORD dwThreadId)

Opens an existing thread object. If the function succeeds, the return value is an open handle to the specified thread. If the function fails, the return value is NULL.
dwDesiredAccess can be one or more of the following values:
SYNCHRONIZE: Enables the use of the thread handle in any of the wait functions such as WaitForSingleObject.
THREAD_ALL_ACCESS: All possible access rights for a thread object.
THREAD_DIRECT_IMPERSONATION: Required for a server thread that impersonates a client.
THREAD_GET_CONTEXT: Required to read the context of a thread using GetThreadContext.
THREAD_IMPERSONATE: Required to use a thread’s security information directly without calling it by using a communication mechanism that provides impersonation services.
THREAD_QUERY_INFORMATION: Required to read certain information from the thread object, such as the exit code (see GetExitCodeThread).
THREAD_SET_CONTEXT: Required to write the context of a thread using SetThreadContext.
THREAD_SET_INFORMATION: Required to set certain information in the thread object.
THREAD_SET_THREAD_TOKEN: Required to set the impersonation token for a thread.
THREAD_SUSPEND_RESUME: Required to suspend or resume a thread (see SuspendThread and ResumeThread).
THREAD_TERMINATE: Required to terminate a thread.

DWORD ResumeThread(
HANDLE hThread)

Decrements a thread’s suspend count. When the suspend count is decremented to zero, the execution of the thread is resumed.

BOOL SetThreadContext(
HANDLE hThread,
const LPCONTEXT lpContext)

Sets the context for the specified thread. The thread must be suspended. The function allows the selective context to be set based on the value of the ContextFlags member of the context structure. The thread handle identified by the hThread parameter is typically being debugged, but the function can also operate even when it is not being debugged.

BOOL SetThreadPriority(
HANDLE hThread,
int nPriority)

Sets the priority value for the specified thread.
nPriority can be one of the following values:
THREAD_PRIORITY_TIME_CRITICAL: Indicates 3 points above normal priority.
THREAD_PRIORITY_HIGHEST: Indicates 2 points above normal priority.
THREAD_PRIORITY_ABOVE_NORMAL: Indicates 1 point above normal priority.
THREAD_PRIORITY_NORMAL: Indicates normal priority.
THREAD_PRIORITY_BELOW_NORMAL: Indicates 1 point below normal priority.
THREAD_PRIORITY_LOWEST: Indicates 2 points below normal priority.
THREAD_PRIORITY_ABOVE_IDLE: Indicates 3 points below normal priority.
THREAD_PRIORITY_IDLE: Indicates 4 points below normal priority.

DWORD SuspendThread(
HANDLE hThread)

Suspends the specified thread. If the function succeeds, execution of the specified thread is suspended and the thread’s suspend count is incremented. Suspending a thread causes the thread to stop executing user-mode (application) code. Returns -1 on failure, or the thread’s suspend count otherwise. Each call of SuspendThread must be matched with a call to ResumeThread.

BOOL TerminateThread(
HANDLE hThread,
DWORD dwExitCode)

TerminateThread is used to cause a thread to exit. When this occurs, the target thread has no chance to execute any user-mode code and its initial stack is not deallocated. DLLs attached to the thread are not notified that the thread is terminating.

TerminateThread is a dangerous function that should only be used in the most extreme cases. You should call TerminateThread only if you know exactly what the target thread is doing, and you control all of the code that the target thread could possibly be running at the time of the termination.

DWORD WaitForSingleObject(
HANDLE hHandle,
DWORD dwMilliseconds)

Returns when the specified object is in the signaled state or the time-out interval elapses. Primarily used to wait for threads (created by CreateThread) to end.
The WaitForSingleObject function can wait for the following objects:
Threads (see CreateThread and OpenThread)
Processes (see OpenProcess)

If the function succeeds, the return value indicates the event that caused the function to return. It can be one of the following values:
WAIT_ABANDONED (0x00000080): The specified object is a mutex object that was not released by the thread that owned the mutex object before the owning thread terminated. Ownership of the mutex object is granted to the calling thread, and the mutex is set to nonsignaled.
WAIT_OBJECT_0 (0x00000000): The state of the specified object is signaled.
WAIT_TIMEOUT (0x00000102): The time-out interval elapsed, and the object’s state is nonsignaled.


WinSocks

INT WSAGetLastError()

Returns the error status for the last operation that failed.

SOCKET accept(
SOCKET sSocket,
const sockaddr_in * psaAddr,
INT * iAddrlen)

Permits an incoming connection attempt on a socket.

INT bind(
SOCKET sSocket,
const sockaddr_in * psaName,
INT iNamelen)

Associates a local address with a socket.

INT closesocket(
SOCKET sSocket)

Closes an existing socket.

INT connect(
SOCKET sSocket,
const sockaddr_in * saName,
INT iNameLen)

Retrieves the host information corresponding to a network address.

hostent * gethostbyaddr(
const CHAR * pcAddr,
INT iLen,
INT iType)

Retrieves the host information corresponding to a network address.

hostent * gethostbyname(
const CHAR * pcName)

Retrieves host information corresponding to a host name from a host database.

INT gethostname(
CHAR * pcName,
INT iNameLen)

Retrieves the standard host name for the local computer.

INT getpeername(
SOCKET sSocket,
sockaddr_in * psaName,
INT * piNameLen)

Retrieves the name of the peer to which a socket is connected.

protoent * getprotobyname(
const CHAR * pcName)

Retrieves the protocol information corresponding to a protocol name.

protoent * getprotobynumber(
INT iNumber)

Retrieves protocol information corresponding to a protocol number.

servent * getservbyname(
const CHAR * pcName,
const CHAR * pcProto)

Retrieves service information corresponding to a service name and protocol.

servent * getservbyport(
INT iPort,
const CHAR * pcProto)

Retrieves service information corresponding to a port and protocol.

INT getsockname(
SOCKET sSocket,
sockaddr_in * psaName,
INT * piNameLen)

Retrieves the local name for a socket.

INT getsockopt(
SOCKET sSocket,
INT iLevel,
INT iOptName,
const CHAR * pcOptVal,
INT * piOptLen)

Retrieves a socket option.

ULONG htonl(
ULONG ulHostLong)

Converts a ULONG from host to TCP/IP network byte order (which is big endian).

USHORT htons(
USHORT usHostShort)

Converts a USHORT from host to TCP/IP network byte order (which is big-endian).

ULONG inet_addr(
const CHAR * pcAddr)

Converts a string containing an (Ipv4) Internet Protocol dotted address into a proper address for the IN_ADDR structure.

CHAR * inet_ntoa(
ULONG iaAddr)

Converts an (Ipv4) Internet network address into a string in Internet standard dotted format. If no error occurs, inet_ntoa returns a character pointer to a static buffer containing the text address in standard "."notation. Otherwise, it returns NULL.

INT ioctlsocket(
SOCKET sSocket,
LONG lCmd,
ULONG * pulArgp)

Controls the I/O mode of a socket.

INT listen(
SOCKET sSocket,
INT iBacklog)

Places a socket in a state in which it is listening for an incoming connection.

INT recv(
SOCKET sSocket,
CHAR * pcBuf,
INT iLen,
INT iFlags)

Retrieves data from a connected or bound socket.

INT recvfrom(
SOCKET sSocket,
CHAR * pcBuf,
INT iLen,
INT iFlags,
sockaddr_in * pasFrom,
INT * piFromLen)

Retrieves a datagram and stores the source address.

INT select(
INT iNfds,
fd_set * pfsReadFds,
fd_set * pfsWriteFds,
fd_set * pfsExceptFds,
timeval * ptTimeout)

Determines the status of one or more sockets, waiting if necessary, to perform synchronous I/O.

INT send(
SOCKET sSocket,
const CHAR * pcBuf,
INT iLen,
INT iFlags)

Sends data on a connected socket.

INT sendto(
SOCKET sSocket,
CHAR * pcBuf,
INT iLen,
INT iFlags,
sockaddr_in * psaTo,
INT iToLen)

Sends data to a specific destination.

INT setsockopt(
SOCKET sSocket,
INT iLevel,
INT iOptName,
const CHAR * pcOptVal,
INT iOptLen)

Sets a socket option.
If iLevel = SOL_SOCKET, iOptName should be on of the SO_* enumerated values (note that not all values are accepted).
If iLevel = IPPROTO_TCP1, iOptName should be one of the TCP_* enumerated values.

INT shutdown(
SOCKET sSocket,
INT iHow)

Disables sends or receives on a socket.

SOCKET socket(
INT iAf,
INT iType,
INT iProtocol)

Creates a socket that is bound to a specific service provider.
iType can be one of the following values:
SOCK_STREAM: Provides sequenced, reliable, two-way, connection-based byte streams with an OOB data transmission mechanism. Uses TCP for the Internet address family.
SOCK_DGRAM: Supports datagrams, which are connectionless, unreliable buffers of a fixed (typically small) maximum length. Uses UDP for the Internet address family.
SOCK_RAW: Provides access to internal network protocols and interfaces. Available only to individuals with root user authority, a raw socket allows an application direct access to lower-level communication protocols. Raw sockets are intended for advanced users who wish to take advantage of some protocol feature that is not directly accessible through a normal interface, or who wish to build new protocols atop existing low-level protocols.
SOCK_RDM: Similar to SOCK_DGRAM, except that the reliability of the message is guaranteed.
SOCK_SEQPACKET: Provides sequenced, reliable, and unduplicated flow of information.


ZLib

INT GzClose(
gzFile zFile)

Flushes all pending output if necessary, closes the compressed file and deallocates all the (de)compression state. The return value is the zlib error number (see GzError).

INT GzEof(
gzFile zFile)

Returns 1 when end-of-file has previously been detected reading the given input stream, otherwise zero.

CHAR * GzError(
gzFile zFile,
INT * piError)

Returns the error message for the last error which occurred on the given compressed file. piError is set to the zlib error number. If an error occurred in the file system and not in the compression library, errnum is set to Z_ERRNO.

INT GzFlush(
gzFile zFile,
INT iFlush)

Flushes all pending output into the compressed file. The return value is the zlib error number (see GzError). GzFlush returns Z_OK if the flush parameter is Z_FINISH and all output could be flushed.

INT GzGetC(
gzFile zFile)

Reads one byte from the compressed file. GzGetC returns this byte or -1 in case of end-of-file or error.

CHAR * GzGetS(
gzFile zFile,
const CHAR * pcBuffer,
DWORD dwMaxLen)

Reads bytes from the compressed file until (dwMaxLen - 1) characters are read, or a newline character is read and transferred to pcBuffer, or an end-of-file condition is encountered. The string is then terminated with a NULL character.

gzFile GzOpen(
const CHAR * pcPath,
const CHAR * pcMode)

Opens a gzip (.gz) file for reading or writing. The mode parameter is as in FOpen (“rb” or “wb”) but can also include a compression level (“wb9”) or a strategy: “f” for filtered data as in “wb6f”, “h” for Huffman only compression as in “wb1h”.

INT GzPrintF(
gzFile zFile,
const CHAR * pcFormat,
...)

Converts, formats, and writes the arguments to the compressed file under control of the format string, just as FPrintF does. GzPrintF returns the number of uncompressed bytes actually written (0 in case of error).

INT GzPutC(
gzFile zFile,
INT iChar)

Writes cChar, converted to an unsigned char, into the compressed file. GzPutC returns the value that was written, or -1 in case of error.

INT GzPutS(
gzFile zFile,
const CHAR * pcString)

Writes the given NULL-terminated string to the compressed file, excluding the terminating NULL character.

INT GzRead(
gzFile zFile,
LPVOID lpvBuffer,
DWORD dwLength)

Reads the given number of uncompressed bytes from the compressed file. If the input file was not in gzip format, GzRead copies the given number of bytes into the buffer.

INT GzRewind(
gzFile zFile)

Rewinds the given file. This function is supported only for reading.

LONG GzSeek(
gzFile zFile,
LONG lOffset,
INT iMethod)

Sets the starting position for the next GzRead or GzWrite on the given compressed file. iOffset represents a number of bytes in the uncompressed data stream. iMethod is defined as in FSeek; the value SEEK_END is not supported.

INT GzSetParams(
gzFile zFile,
INT iLevel,
INT iStrategy)

Dynamically update the compression level or strategy.

LONG GzTell(
gzFile zFile)

Returns the starting position for the next GzRead or GzWrite on the given compressed file. This position represents a number of bytes in the uncompressed data stream.

INT GzWrite(
gzFile zFile,
LPVOID lpvBuffer,
DWORD dwLength)

Writes the given number of uncompressed bytes into the compressed file. GzWrite returns the number of uncompressed bytes actually written (0 in case of error).

INT compress(
PBYTE pbDest,
PDWORD pdwDestLen,
PBYTE pbSrc,
DWORD dwSrcLen)

Compresses the source buffer into the destination buffer. Upon entry, pdwDestLen is the total size of the destination buffer, which must be at least 0.1% larger than dwSrcLen plus 12 bytes.
Upon exit, pdwDestLen is the actual size of the compressed buffer.

INT compress2(
PBYTE pbDest,
PDWORD pdwDestLen,
PBYTE pbSrc,
DWORD dwSrcLen,
INT iLevel)

Compresses the source buffer into the destination buffer given a level of compression. Upon entry, pdwDestLen is the total size of the destination buffer, which must be at least 0.1% larger than dwSrcLen plus 12 bytes.
Upon exit, pdwDestLen is the actual size of the compressed buffer.

INT deflate(
z_streamp pzsStream,
INT iFlush)

Compresses as much data as possible and stops when the input buffer becomes empty or the output buffer becomes full. It may introduce some output latency (reading input without producing any output) except when forced to flush. Z_NO_FLUSH is the typical value for iFlush.

INT deflateEnd(
z_streamp pzsStream)

Frees all dynamically allocated data structures for this stream. This function discards any unprocessed input and does not flush any pending output.

INT deflateInit(
z_streamp pzsStream,
INT iLevel)

Initializes the internal stream state for compression. The fields zalloc, zfree, and opaque are automatically set to Z_NULL and default allocations are always used. iLevel must be Z_DEFAULT_COMPRESSION, or between 0 and 9: 1 gives best speed, 9 gives best compression, 0 gives no compression at all (the input data is simply copied a block at a time).

INT deflateToFile(
const PBYTE pbData,
DWORD dwDataLength,
const _TCHAR * ptcDest,
INT iLevel)

Compresses the data in pbData and stores the result into the file indicated by ptcDest. Returns Z_OK if successful, or another Z_* error code otherwise.

INT inflate(
z_streamp pzsStream,
INT iFlush)

Decompresses as much data as possible and stops when the input buffer becomes empty or the output buffer becomes full. It may introduce some output latency (reading input without producing any output) except when forced to flush. Z_NO_FLUSH is the typical value for iFlush.

INT inflateEnd(
z_streamp pzsStream)

Frees all dynamically allocated data structures for this stream. This function discards any unprocessed input and does not flush any pending output.

INT inflateFromFile(
const _TCHAR * ptcSrc,
PBYTE * ppbDest,
PDWORD pdwDataLength)

Decompresses a file and stores the data in the memory buffer to which ppbDest points. The size of the buffer is returned in pdwDataLength. When the buffer that receives the decompressed data is no longer needed, it must be freed with a call to Free().

INT inflateInit(
z_streamp pzsStream)

Initializes the internal stream state for decompression. The fields next_in, avail_in, zalloc, zfree, and opaque are automatically initialized.

INT uncompress(
PBYTE pbDest,
PDWORD pdwDestLen,
PBYTE pbSrc,
DWORD dwSrcLen)

Decompresses the source buffer into the destination buffer. Upon entry, pdwDestLen is the total size of the destination buffer, which must be large enough to hold the entire uncompressed data. (The size of the uncompressed data must have been saved previously by the compressor and transmitted to the decompressor by some mechanism outside the scope of this compression library.)
Upon exit, pdwDestLen is the actual size of the compressed buffer.


Data Types

All programs, great and small, take advantage of built-in data types. Data types are specialized types of numbers that have meaning to the compiler of a program. Examples of data types are int, long, short, and double. These data types are used by programs to perform the mathematical operations they need to perform in order to run. Each data type has special properties: sign, size, range, and precision.

When a program is compiled, information regarding data types is removed, and instead the compiler simply writes the instructions into the final result that will automatically use the correct operations to perform the correct math with the correct types of data.

The following chart provides a comprehensive list of common data types used in games.

Data Type | Alias | Sign | Size | Range | Precision | Extra
1 bit |  |  | 1 bit | 0 to 1 | Whole Numbers | The smallest of small types, single bits can only be 0 or 1. All data types are created by putting multiple bits together. A bit in itself is not actually a data type.
2 bits |  |  | 2 bits | 0 to 3 | Whole Numbers | As a form of compression, games may store index-based tile information in bits. 2 bits can only represent numbers from 0 to 3, so they aren’t common.
4 bits |  |  | 4 bits | 0 to 15 | Whole Numbers | Dividing a byte in half allows games to store two numbers in a single byte, both ranging from 0 to 15.
char |  | Signed | 1 byte (8 bits) | -128 to 127 | Whole Numbers | The smallest data type, chars and bytes are often used to contain data in lists, such as strings.
byte | unsigned char | Unsigned | 1 byte (8 bits) | 0 to 255 | Whole Numbers
short |  | Signed | 2 bytes (16 bits) | -32,768 to 32,767 | Whole Numbers | Long ago these were the standard types on Windows® platforms. Old games still use these, and some other scanners may call these words. Modern programs use these to store Unicode strings.
unsigned short | word | Unsigned | 2 bytes (16 bits) | 0 to 65,535 | Whole Numbers
long |  | Signed | 4 bytes (32 bits) | -2,147,483,648 to 2,147,483,647 | Whole Numbers | On 32-bit Windows® platforms, these are the standard type. This type is most common in all modern games. As a relic of the past, these are sometimes called dwords, or double words.
unsigned long | dword | Unsigned | 4 bytes (32 bits) | 0 to 4,294,967,295 | Whole Numbers
int |  | Signed | Variable, but 4 bytes (32 bits) in MHS | -2,147,483,648 to 2,147,483,647 | Whole Numbers | This type changes on a per-language basis, though the most common form is 4 bytes. MHS, therefore, treats this as 4 bytes, though only supports the signed version because int is exactly the same as long.
unsigned int |  | Unsigned | Variable, but 4 bytes (32 bits) in MHS | 0 to 4,294,967,295 | Whole Numbers
64-bit integer | __int64 | Signed | 8 bytes (64 bits) | -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 | Whole Numbers | 64-bit Windows® provides more support for these than 32-bit Windows®, however they are used in 32-bit Windows® as well. These are very rare and may never be encountered in game hacking. These are sometimes called qwords, or quadruple words.
unsigned 64-bit integer | unsigned __int64, qword | Unsigned | 8 bytes (64 bits) | 0 to 18,446,744,073,709,551,615 | Whole Numbers
float |  |  | 4 bytes (32 bits) | (+/-)3.4E+38 | Floating-point Numbers | Floating-point types are used in all 3-D games, though float is much more common because its accuracy is sufficient, yet it is smaller and faster than doubles.
double |  |  | 8 bytes (64 bits) | (+/-)1.7E+308 | Floating-point Numbers


Introduction to Number Systems

MHS makes use of 4 numbering systems in various areas. The 4 numbering systems are decimal, hexadecimal, octal, and binary. Each number system has a base, which is used to convert it from a set of digits to a numeric value. For example, the digits 316 can be converted to a base-10 number using the formula (3×10²)+(1×10¹)+6, which equals 316. The 3, 1, and 6 come from the digits, and the 10 comes from the base.

The following chart explains the 4 numbering systems used.

System | Description
Decimal—Base 10 | The digits may be from 0 to 9. For example, in decimal, 150 would be (1×10²)+(5×10¹)+0.
Hexadecimal—Base 16 | All decimal digits are used, plus A, B, C, D, E, and F to represent numbers 10 through 15, respectively. For example, in hexadecimal, digits A1F would be (10×16²)+(1×16¹)+15, which equals 2,591. This is commonly called Hex.
Octal—Base 8 | Only digits 0 to 7 are used. For example, in octal, digits 524 would be (5×8²)+(2×8¹)+4, which equals 340.
Binary—Base 2 | Only digits 0 and 1 are used. For example, in binary, digits 1010 would be (1×2³)+(0×2²)+(1×2¹)+0, which equals 10.

Bits and Bytes

A digit of a binary number is called a bit. 8 bits make a byte, which is the smallest data type. Since a byte has 8 bits, its range is from 0 to 255, inclusive. See Data Types for more information.

Entering Numbers

Some edit boxes in MHS allow both decimal and hexadecimal input. In cases where both types of numbers are allowed, you must prefix a number with 0x to indicate it is hexadecimal.

In some cases, only hexadecimal numbers are allowed, and in those cases it is not required (and sometimes forbidden) to use the 0x prefix.


About Pointers

Pointers are 4-byte (this is machine-dependant) integral values like any other. Literally, they are nothing but unsigned longs (or dwords). Pointers are not magic, and have no special properties that make them confusing. Why are so many people confused about pointers? Because they have been led to believe they are more than they are—they have been given a misunderstanding.

Pointers themselves are not magic; you can not normally look at a chunk of RAM and determine what are pointers and what are not. The only thing that separates pointers from any other unsigned long is how they are used. For the sake of clarity, pointers are always displayed in hexadecimal format. See Introduction to Number Systems.

On the Data Types page, you can see that each data type has specific properties and specific uses. Since pointers are unsigned longs, they have the same range and sign as unsigned longs. Unsigned longs are used to perform mathematical operations on numbers with a large range. Chars are usually used to hold text strings. Shorts are also used for math, but with a smaller range, and floats are used for math, but on a much more precise scale. Doubles are used for even more precise math. Now it becomes clear that data types are actually defined by how they are used rather than their properties—the usage of a data type is defined first, and then it is given properties to facilitate that use.

So What About Pointers?

Now that it is clear that the only true separation between data types is the way they are intended to be used, we can discuss pointers, which are nothing more than another data type with an intended usage. Pointers, however, are not intended to be used for mathematical operations. Pointers are used to indicate the locations of objects (structures, classes, functions, data types, anything) in the RAM space of your game.

That means to understand pointers, you need to understand the RAM in your game.

We view RAM from low to high, with the low addresses on top and the higher ones on bottom. Humans are silly creatures.

This diagram illustrates how RAM generally appears in a game, with the red lines indicating the modules loaded by the game (these are different in every game). A module is any .DLL or .EXE file in this RAM space—the most familiar module is the one at 0x00400000, where your game is almost always loaded. Kernel32.dll, USER32.dll, and SHELL32.dll are other common modules.

EXTRA

The accessible range (as shown) is from 0x00000000 to 0x7FFFE000, which covers two gigabytes. But wait. What if you don’t have two gigabytes of RAM? And, wait a minute. How can both of your games be loaded at 0x004000000 at the same time? Most of the processes on your computer are running at either 0x00400000 or 0x01000000, so that must mean they each have their own 2 gigabytes of RAM, right? But then, that means every time you load a process, you suddenly gain 2 gigabytes of RAM, right?

In my left hand is a red pill. If you take it I will show you the truth. I lost my right hand in the war, so I’m afraid you’re stuck with the red pill.

Every address you see here is a lie. You always see 0x00400000 as the base address of your game, but that is not its real address. Windows® hides its real address, and uses a virtual mapping system to give each process its own “set of RAM”. These addresses are virtual. But luckily for us, we don’t need to know this at all! All that is important to know is that when working in any given process, we are working within a virtual space with addresses that have meaning only to that process. For the sake of simplicity we always treat them as literal real addresses.

It feels as if we are looking down on an ocean, from so high that all the tiny modules are just thin lines. The cluster of lines at the end are the system modules which generally have no use during hacking. The modules of interest are usually at the very base of this image, from 0x00400000 onward. In this diagram, address 0x00400000 is actually the second line! Address 0x00400000 seems as if it is deep inside the RAM of the game, but in fact it is hardly even a spec at the beginning of the whole. Every pixel in this diagram covers 0x00400000 addresses. Now you can see just how small 0x00400000 really is.

To actually see the modules, we are going to need to zoom in a bit. In this diagram, we have zoomed into the range 0x00300000 to 0x00440000. This entire range is a single line in the above diagram, but it covers the area just before and just after the address where games are typically loaded (0x00400000), and contains several modules.

This diagram illustrates where the modules have been loaded and graphically depicts their sizes. Any value you find inside the space of a module is static. In the diagram, logger.dll is 0x0004C000 bytes in size, ending at 0x0037C000. That means all addresses from 0x00330000 to 0x0037C000 are static, and they belong to logger.dll.

It is important to be very clear on what a static address is. In our above example, address 0x00340000 would be static. Some people define static addresses as addresses that never move. But quite clearly that would mean every address is static. Address 0x00340000 is always address 0x00340000, address 0x00300000 is always 0x00300000, 3 is always 3, etc. So it isn’t the address that never moves, since addresses never move anyway. It’s the data that never moves. The data for my player may be at address 0x00340000. Address 0x00340000 will always be there, but the data for my player may later move off to address 0x0234C000.

So a static address really means (almost), “An address containing data that never moves.” Thus, all the integers, floats, chars, bytes, structures, classes, etc., from 0x00330000 to 0x0037C000 in logger.dll, will always be at the same addresses. Well, almost, anyway. If you read the EXTRA section above you will remember that there is no such thing as static data.

Windows® reserves the right to load modules to different locations as it desires. That means logger.dll may not be at 0x00330000 next time we start the game. But the addresses from 0x00330000 to 0x0037C000 are static, right? That’s what we just established, right? Well, they are static, but the definition of “static” needs to be modified just a bit.

The Real Definition of Static Addresses

The final, real, true, undisputable definition of static addresses is, “Addresses that move predictably in relation to the module that owns them.” 99.999% of the time your game will always be loaded to 0x00400000, and thus 99.999% of the time, it is accurate to think of static addresses as being data that never moves. But it’s still a dirty lie that has millions of people fooled.

Using the diagram above, we defined addresses from 0x00330000 to 0x0037C000 as being static. Static addresses are relative to the module that owns them, and in this case that means logger.dll. Thus if logger.dll decides to relocate to 0x05000000, it is no problem, because all the values from 0x00330000 to 0x0037C000 have now moved to addresses from 0x05000000 to 0x0504C000.

Static addresses are noted in the format [module+offset]. For example, In logger.dll, we may have discovered a valuable integer at address 0x003304C0. Since logger.dll may move, we don’t just use this address directly. Instead, we use [logger.dll+0x4C0]. Therefore, when logger.dll decides to packs its things and move on to 0x05000000, [logger.dll+0x4C0] gives us 0x050004C0, and we still have our valuable integer.

The Other Kind of Address

Between modules there are many pockets of empty space, as shown in both diagrams above. Not all, but many of these addresses are available to the game to use as it pleases. This is the free store, where the game can use undefined amounts of RAM for undefined purposes. Games never know exactly how much RAM they are going to need at any given moment. Literally, this is up to the user to decide. For example, the user decides which map to play in a first-person shooter. Each map contains different data, and thus will consume different amounts of RAM.

When the game needs more memory, it asks Windows® to give it some. Windows® will create an area of usable RAM and give the game a pointer to it. Ah! Now we are starting to see the so-called magic behind pointers. Windows® will create an area of usable RAM at any location where there is enough unused RAM available. The only way the game is ever going to know where that new RAM is located is by the pointer that Windows® gives back to the game. Because the location where Windows® will find enough free RAM for the game changes, it is called dynamic. When the game requests new RAM, it is called allocation. Thus the process is called dynamic memory allocation, or DMA.

How a Pointer is Used

Remember, a pointer is just another data type, and data types are defined by how they are used. During dynamic memory allocation, Windows® will find an arbitrary address that can not be predicted, thus pointers are used to tell the game where the requested memory has been allocated. A pointer is an unsigned long that, instead of being used for math, is used to indicate the locations of various data the game needs to run. The game knows where the pointer itself is, and then by using that pointer it can determine where other things are—and so can we.

All data in all games have addresses and values. An integer can have an address of 0x00443C0C and have a value of 100 (0x64), and this value is used for mathematical purposes. Likewise, a pointer can have an address of 0x0042104C and a value of 0x004AE804, but this value isn’t used for math. This value is yet another address.

In these diagrams, we have zoomed into the range from 0x00400000 to 0x00500000. Our pointer is at address 0x0042104C, which we note as [lspiro.exe+0x2104C].

In the diagram to the left, the value of this pointer is 0x004AE804. 0x004AE804 is itself another address. That means if we go to address 0x004AE804, we will find more data that the game is using. It can be any kind of data, but let’s assume it is the data for our player.

The largest problem new hackers face is that after they die, they notice that the data for their player is no longer at the same address. This is a result of dynamic memory allocation, mentioned above. The game has decided it no longer needs the data at address 0x004AE804, and has told Windows® to get rid of it, and then to give it a new address where it can store the new data for your player as you respawn.

In the diagram to the right, the pointer at [lspiro.exe+0x2104C] is now pointing to 0x004BD808. The data for our player is now at this location, and we know that because the value of the pointer at [lspiro.exe+0x2104C] is 0x004BD808.

Notice that the pointer itself did not move—only its value changed to indicate a new address for our player data. The game relies on this pointer staying in the same location, or else it has no way to know where our player data is in RAM. When the game was compiled, address [lspiro.exe+0x2104C] was designated by the compiler as being the location the game can always use to find its player data.

This is why it is important that all the data inside a module moves exactly with the module itself. During compilation, the compiler writes instructions that simply “understand” the way each address (from the module base) is to be used, and if the data in the module does not stay perfectly aligned, the game simply can not run.

So How is This Useful for Hacking?

Surely you jest. If the game knows how to locate the data for our player, we know how to locate the data for our player. And that means always. Every time we die, every time the game is restarted, etc.

The only thing important to note that is that if the value of a pointer is 0x00000000, it does not mean there is actually an object at address 0x00000000. Value 0x00000000 is reserved to tell the game that the pointer itself is invalid, and points to nothing.

It is also worthy of noting that the values of pointers will be divisible by 4 in 99% of all cases. Windows® always gives the game pointers that are aligned on 4-byte boundaries because the architecture of x86 processors allows them to work faster on data that is aligned this way. If data is not aligned by every 4 bytes, the game may suffer from slowdown. Thus the only way pointers can point to addresses that are not divisible by 4 is if the programmers specifically go out of their ways to change the pointers. This is sometimes done as an optimization technique when performing some string-based parsing, but it is never done on objects that are frequently and randomly accessed throughout the execution of the game. Incidentally, these objects are the very objects we are interested in hacking, and thus it is always safe to assume that the data you are trying to find will have its base along a 4-byte boundary.

Structures, and the Pointers that Love Them

In our above examples, the pointer is pointing to our player data. Our player has a weapon, health, magic, experience, a level, gold, and a position in the 3-D game space.

Games organize sets of related data into structures and classes. Because your player has a lot of related data, it will always be organized a structure or class. This is actually done simply so that the programmers themselves (the ones making the games you hack) can work in an organized fashion, making complicated sets of data easier to manage.

These images depict how our player structure might look in RAM. The left image shows our player data at address 0x004EA804 and the right at address 0x004BD808.

Notice that the positions of each data element (officially called members) inside the structures are the same distance from each other, no matter where the structure itself is located in RAM. That is, our GOLD is always 0x10 bytes after the start of the structure. Structures and classes share something in common with modules: when they move in RAM, the data that is related to them also moves exactly with them. For this reason, we note their members the same way as we note addresses in modules: [structbase+offset]. In this case, HEALTH is offset 0x4, MP is offset 0x8, LEVEL is offset 0xA, etc.

We know quite a bit now. Let’s assume we wanted to know our player’s health at any given moment.

  1. Get the base address of lspiro.exe. That is 0x00400000 in this case.
  2. Go to [lspiro.exe+0x2104C]. This means 0x00400000 + 0x2104C. The result is 0x0042104C.
  3. Get the value at 0x0042104C. Let’s assume it is 0x004AE804.
  4. Go to the address depicted by the above value. That means we are now at address 0x004AE804. This is the structbase.
  5. Go to [structbase+offset]. Our structbase is 0x004AE804, and the offset of our health is 0x4. 0x004AE804 + 0x4 = 0x004AE808.
  6. 0x004AE808 is the address where our health is, as shown in the above diagram. And getting the value here gives us 9,999.

Pointers to Pointers

We have covered a static pointer ([lspiro.exe+0x2104C]) that points to dynamic data. But pointers are not limited to being static themselves. In the above player structure, the very first member (WPN PTR *) is a pointer to the player’s weapon data. Luckily, this has no special meaning what-so-ever. We already discussed everything there is to know about pointers, and, just because this particular pointer is moving around with our player structure, it does not mean it follows any special set of rules apart from other pointers. The value of this pointer is 0x028F3B0C, and like every other pointer we turn this value into an address. Now we know that at address 0x028F3B0C there is another set of data that is used to give details regarding the weapon our player is using.

Pointers can point to pointers that point to pointers that point to pointers that point to pointers. It doesn’t matter how many layers of pointers there are, because you now know everything there is to know about pointers, and they all work the same.

And So the Magic Behind Pointers Is…

Copyright © 2006 Shawn (L. Spiro) Wilcoxen


Wildcards and Regular Expressions

Wildcards and regular expressions provide useful ways to search for text, allowing parameters inside the search string itself.

Terminology

To explain these strings, a few terms must be defined.

Term | Meaning
Control String | The string from the file or RAM that will be tested for a match.
Test String | The string entered by the user that will be used in the search to find all matches. The wildcard an regular-expression strings are test strings.
Identifier | Used in regular expressions, these are simply whole words that are composed only of alpha characters A to Z and a to z.

Wildcards

Wildcard strings are similar to normal strings but with two characters that have special meaning.

  • ? — This causes any single character from the control string to be accepted. For example, the wildcard string B?b will cause matches with Bob, Brb, and Bbb, but not Bbab, because only one character is used to match with the ?.
  • * — Matches any number of characters until the next character in the wildcard string is found in the control string. For example, the wildcard string B*b will cause matches with Bob, Brb, Blab, Blaster-nab, etc. If the * character is used at the end of the wildcard string, the number of characters matched depends on the settings for wildcard match lengths.

Regular Expressions

Regular expressions are a widely recognized way of describing string patterns. MHS uses a method of parsing regular expressions called deterministic finite automation. This means the regular expressions are not parsed on-the-fly as they are with the Windows® API, but instead are compiled to an internal form which makes scanning exceptionally fast. To make the explanation of regular expressions more intuitive, first the elements of regular expressions will be explained, followed by various examples that use the explained elements.

Elements of regular expressions are sets of characters with special meaning. Regular-expressions strings are strings that are made of regular-expression elements. The following chart explains the most basic elements of regular expressions.

Element | Description | Example
Individual Characters

Individual characters can be elements of a regular expression.

Some individual characters have special meaning to the regular-expression parser, so to use these characters without using their special meanings, put a \ in front of them.

The characters with special meanings are:

  • *
  • +
  • ?
  • .
  • |
  • [
  • ]
  • (
  • )
  • -
  • $
  • ^
  • (space)

To use the space as an individual character, use “\ ”.

To use non-printable characters, either use the correct escape sequence from C (\n, \t, \r, or \v), or use \xhh where h is a hexadecimal digit.

  • “H” — In the string, “Here I am!”, the H at the beginning of the string would be matched.
  • “you” — 3 characters. In the string, “Where are you going?”, the you would be matched.
  • “\$50\.00” — $ and . are special characters, so to use them as individual characters we have placed a \ in front of them. This translates into $50.00 during the scan, which would find a match in the string, “You want to donate $50.00 to L. Spiro for his software, don’t you?”
  • To find the word “L. Spiro”, we would use the regular expession “L\.\ Spiro”. The period and space both have a \ in front of them.
  • We could also find “L. Spiro” by using the regular expression “L\.\x20Spiro”, using the \x20 to specify a space.
Character Sets in Brackets

Characters inside brackets are used to indicate that any of the characters in the brackets can cause a match at that location in the test string.

Using the hyphen, you can indicate a range of characters that can be valid matches.

Using the ^ special character, you can indicate that the inverse set of characters is matched, instead of the characters in the brackets.

  • “[_A9]” — Indicates that either an _, an A, or a 9 will cause a match.
  • “[A-Z]” — Indicates that any character from A to Z (inclusive and case-sensitive) will cause a match.
  • “[_A-Za-z]” — Indicates that an _, any character from A to Z, or any character from a to z will cause a match.
  • “[^A-Za-z]” — Any character except characters from A to Z and from a to z will cause a match. The ^ special character must be used immediately after the [.
  • “[\^A-Z]” — The \ causes the ^ to be treated as an individual character rather than a character with special meaning. So the match will occur on ^ and on any character from A to Z.
Dot (.) Matches any character. Same meaning as ? in wildcard strings.
  • “B.b” — Matches Bbb, Bob, etc.
Identifiers Prefixed With $ | Indicates a regular-expression macro defined by the user or the system. | MHS allows you to define macros for regular expressions so that you do not have to type the entire regular expression each time you want to use it.
Parentheses Like in math, parentheses cause their contents to become the most basic level.
  • “(ui)” — Two individual characters encloses in parentheses. Same as just typing “ui” in this case.
  • Any of the examples above could be enclosed in parentheses.
  • These are usually used with operators below to indicate the operator works on the entire set of elements inside the parentheses.

Regular-expression operators are used to indicate an action to be used for matching.

Operator Description Example
* Repetition 0 or more times.
  • “[0-9]*” — Matches 9, 90, 7897938796, and even an empty string.
+ Repetition 1 or more times.
  • “[0-9]+” — Matches 9, 90, and 7897938796, but not an empty string.
? At most one occurrence, or 0 or 1 repetitions.
  • “Number[0-9]?” — Matches Number0, Number8, and Number, but not Number78, etc.
\i Ignore case.
  • “(L\.\ Spiro)\i” — Matches L. Spiro in a case-insensitive way.

All of the above can be combined to form full regular expression strings.

  • [_A-Za-z][_A-Za-z0-9]* Matches a valid C/C++/Java identifier. The first character can be any alpha character or an underscore, and can be followed by 0 or more alpha-numeric characters or underscores.
  • 0[0-7]* Matches an octal number.
  • 0[xX][0-9A-Fa-f]+ Matches a hexadecimal number, including the 0x (or 0X) prefix.

Finally, on the highest level, regular expressions can be separated by a |. The expressions on the left and right sides of the | are alternatives—either of them can cause a match in the control string.

  • (0[0-7]*)|(0[xX][0-9A-Fa-f]+) Matches either an octal number or a hexadecimal number.
  • ((L\.\ Spiro)\i)|((I\.\ Omega)\i) Matches either L. Spiro or I. Omega, both in case-insensitive ways.

Macros

Macros are used to make life easier by shortening what is required to type. We can associate the regular expression “[_A-Za-z][_A-Za-z0-9]*” with the macro “$Identifier”, and from then forth we only need to type “$Identifier” instead of “[_A-Za-z][_A-Za-z0-9]*”. Macros can be used to make long regular expressions easier to understand. Consider the following macros.

  • $Int = [0-9]+
  • $Frac = \.[0-9]+
  • $Exp = ([Ee](\+|-)?[0-9]+)

Now we can make a regular expression for a floating-point number by using the following:

  • ($Int? $Frac $Exp? | $Int \. $Exp? | $Int $Exp)[fFlL]?

Simple, huh?

Well just start small and work up as you understand more and more of how regular expressions work.

The regular-expression parser was written by Martin Holzherr.


Tile Map Tutorial

This page illustrates step-by-step how to hack some types of tile maps out of tile-based games. The game we will be using is Super Mario World® running on ZSNES, though the same tricks apply to most tile-based games on most emulators.

Step 1: Load the Game

A wise starting place would be to actually attach to the emulator of your choice. The choice of the tutorial is ZSNES. Go to File/Open Process and select your emulator from the list.

Naturally, you must also load the game of your choice into the emulator. No instructions are provided for doing that.

Step 2: Finding a Good Tile Map

We will use the USD Search to find the tile map. The USD pattern filters junk more easily if it is longer and more unique. The USD pattern is based off the tile pattern in the game, so we need to find a tile pattern in the game that is unique and long.

The tile map to the left is long, but not very unique. We can expect to find many numbers in the game that repeat. The pattern to the right, however, is much more unique.

Here we are assuming the tile map runs from left to right, but it may happen that the tiles actually run from the top to the bottom. Either way will work the same, however. But when you compose a USD string, you will follow the tiles downwards instead of to the right, as we will in our example.

Step 3: Create the USD String

Certainly the easiest of the steps. Go to Search/Group Search in MHS. Select USD Search in the Evaluation Type. Most tile maps are stored as single bytes. Set Treat Elements in List as to 1 Byte. Also select a suitable range to reduce the number of junk returns we get. Opening the Hex Editor (and selecting the Info tab) we can see that ZSNES.exe starts at 0x00400000 and ends at 0x008E2000. Set our range accordingly. Note that this is not the correct way to calculate ranges on larger games that allocate the game heap dynamically. However, in ZSNES and other low-end emulators, the ROMs are stored in a static heap declared within the .EXE module itself, so we can snap our search range to that.

When the dialog matches the picture, you are ready to create the USD pattern to match the tile pattern. Be sure not to select Search Every 4 Bytes, as the pattern we are trying to find could be on any byte.

To create the USD string, look at the tile pattern in the game. Notice how the tiles relate to each other in terms of Unknown, Same, and Different. The first tile is always Unknown; we don’t know its value and we don’t know how it relates to the tile in front of it because it is the first tile already. If the following tile is the same, it is Same. If the following tile is different, it is Different. Look at the game.

The tiles are lettered based off how they relate to the previous tile. Obviously the background is on another layer and is not counted.

The first tile is always Unknown. The next tile is Different. So is the next. But the next tile is the Same, followed by a Different.

In this example, the string we get is UDDSDDSSSSSDDDD. Quite a good pattern! Enter this into Values to Find and press OK.

Step 4: Identify the Target

We got 18 returns! A low number that makes it possible to eliminate junk by hand. If you got a large number of results, you will need to try to find a better tile-map pattern and search again. If you got no returns or no good returns, the tile map might be stored differently from what you expect. Maybe 4 bits. Maybe top-to-bottom. Try various searches until you get something good.

We can easily eliminate most of them instantly. Look at the first return: 64 248 0 0 248- STOP. Is the 5th tile the same as the second tile? No, so this is not the tile map.

We know that the first tile is the same as all the ones in the middle that repeat. For example, in the second return, the middle is filled with 171’s. The first value, however, is 199, so this is not the match. We can eliminate all the ones that begin with 199 this way.

We are already half way down the list. When you have eliminated all that you can you will find that there is only 1 on this list that could be our match: 006BA589, 4th from the bottom.

First, add this address to the main list by double-clicking it. Feel free to name it. Now right-click it in either list and select View in Hex Editor.

We need to test to ensure that this is really our target. Once the Hex Editor is open you should see more of the data.

The data looks normal. Let’s mess it up to see if it changes our game.

There are many ways to do this. One method is to select some of the data and simply hit Del to set the selected bytes to 0. Let’s look at something a bit fancier because it has many uses, once you are familiar with it.

We searched for 15 tiles, so select 15 tiles from the start (addresses 006BA589 to 006BA597). Select the Tools/Operations/Assign menu item. Enter the following:

Treat Data As: Byte (because our tiles are 1 byte in size).
Operand: 0
Decimal
Operand Step: 1
Skip Bytes: 0
Press OK.

The values have been changed from 00 to 0E (15). This is a great feature useful in many situations.

Now that we changed the data, we need to go back to the game and check the results.

Going back to Super Mario World® we see no changes to the ground. Did we miss our target? Wait! The Super Nintendo Entertainment System®, as well as other low-end game machines, buffers layers into VRAM (graphics RAM) for quick drawing. Even if we change the tile map, we won’t see anything change until we cause the VRAM to be flushed.

How do we do this? Luckily it is extremely easy. The VRAM banks on old machines are capable of holding only 4 screens (2 in each direction) at a time. So simply by moving away from this area by at least one full screen we can cause the VRAM to flush this area.

Coming back we see the changes we made.

We are inside the tile map. But not at the beginning. It is useful to know this is the tile-map location, but where does the tile map actually begin?

The start of a tile map can be difficult to find. First, estimate how far into the tile map you are. In our example, we aren’t far. Scroll back in the Hex Editor looking for odd changes in the data patterns. If you scroll over a long range and feel you aren’t that far inside the tile map, you may have missed the start.

Finding the start is most often just a matter of good eyes and spotting sudden changes in the pattern. If you know how the pattern should be near the start, it can help to spot that too.

You can also walk backwards towards the start of the tile map and change data as you go, just as we did above. Scroll back, change some data, then look for your changes to see how far back you’ve gone.

Be creative; there is no single way to find the start.


Snippets and Examples

Each sub topic in this section covers a set of related snippets and examples that can help greatly with using the scripts.