Skip to content
Sajid edited this page Sep 2, 2023 · 13 revisions

This is a small, quick article to help you get started with HMMCodes.

Table of Contents

Getting Started

To get started, create a file called ExtraCodes.hmm in the mods directory. This is recommended as using Codes.hmm will cause your codes to be wiped out every time you go and update codes in HMM.

HedgeModManager will also read codes from files in Mods/.hedgemm/work/Codes where the files have .hmm extension.

Creating a code

HMMCodes start with an identifier for whether the program is a code or a patch.
The identifiers are the following.

Code
Patch

The identifier must be followed by a string literal for the program name.

Examples

Code "Example Code"
// Program code here
Patch "Example Patch"
// Program code here

Code vs Patch

The difference between code and patch are the following.

  • Code

    • Codes are executed every time a frame is drawn.
  • Patch

    • Patches are executed before any application code is called. Note that any writings with a read memory returns zero instead.

Optional attributes

  • by: The by attribute is used by Hedge Mod Manager to display the author of the code.

    Example

    Code "Example Code" by "Sajid"
  • in: The in attribute is used by Hedge Mod Manager to display the category of the code.

    Example

    Code "Example Code" in "General" by "Hyper"
  • does: The does attribute is used by Hedge Mod Manager to display the description of the code.

    Single Line Example

    Code "Example Code" in "General" by "Hyper" does "A code to show examples with."

    Multi-line Example

    Code "Example Code" in "General" by "Hyper" does
    /*
    A code to show examples with, but also with multiple lines.
    
    Anything past the first line will be displayed fully in the
    about window when double-clicking a code.
    
    You can also resize the description box in the UI to fit the
    whole description, or double-click the splitter to resize it
    automatically to fit all the text.
    */

Memory

Reading Memory

The following methods are exposed for reading from a memory address.

char[] Read(System.IntPtr address, System.IntPtr count);

T Read<T>(long address) where T : unmanaged
T Read<T>(System.IntPtr address) where T : unmanaged

Example

// Read a floating point value from the specified memory location and output it to the console.
System.Console.WriteLine(Read<float>(memoryLocation));

Writing Memory

The following methods are exposed for writing to a memory address.

void Write<T>(long address, T data) where T : unmanaged
void Write<T>(long address, params T[] data) where T : unmanaged

void WriteProtected<T>(long address, T data) where T : unmanaged
void WriteProtected<T>(long address, params T[] data) where T : unmanaged

Examples

// Write a 64bit long with the value of 1337 at the specified memory location
Write(memoryLocation, 1337L); // Implicitly Write<long>(memoryLocation, 1337);

// Write a single byte with the value of 5 at the specified memory location.
Write<byte>(memoryLocation, 5); // Implicitly cast 5 to byte
Write(memoryLocation, (byte)5); // Explicit cast; Will write the same value

// Note: For writing to a protected region of memory use WriteProtected

// Write an array of bytes at the specified memory location
Write(memoryLocation, new byte[] { 1, 3, 3, 7 }); 
// The above is the same as writing
// Write<byte>(memoryLocation, new byte[] { 1, 3, 3, 7 });

Assembly Code Injection

HMMCodes provides functions for assembling x86 or x86-64 code at runtime and injecting them.

The following functions are used for hooking assembly code.

// instructions:         The instructions to assemble.
// address:              The address to insert the hook at
// behavior (optional):  The behavior of the hooked code (see HookBehavior enum below).
// parameter (optional): The execution mode of the hooked code (see HookParameter enum below).
void WriteAsmHook(string instructions, long address, HookBehavior behavior, HookParameter parameter);
void WriteAsmHook(long address, HookBehavior behavior, params string[] instructions);

// HookBehavior.Before:  Write a jump/call to the custom code before the original code is done.
// HookBehavior.After:   Write a jump/call to the custom code after the original code is done (default).
// HookBehavior.Replace: Write a jump/call over the original code, replacing it entirely.
public enum HookBehavior
{
    Before, After, Replace
}

// HookParameter.Jump: Jump to the custom code (default).
// HookParameter.Call: Call the custom code as its own function.
public enum HookParameter
{
    Jump, Call
}

A hook will replace an instruction at the starting address with a jmp/call instruction (depending on HookParameter) to the custom code in memory. Please be wary of the length of this instruction when using hooks to ensure the original code will still be valid once jumped back into.

  • x86 (32-bit) jumps are 5 bytes long.
  • x86-64 (64-bit) jumps are 14 bytes long.

Examples

// Write an asm hook at the specified address that replaces the original code
WriteAsmHook(@"mov eax, eax
              xor eax, eax", hookAddress, HookBehavior.Replace);

// string[] overload example
// The assembled code for this would be called after the original code is done executing.
WriteAsmHook(hookAddress, HookBehavior.After, "xor eax, eax", "mov eax, eax");

Signature Scanning

HMMCodes 1.2 adds support for scanning for code-style patterns.

The following functions are available.

// Pattern length must be equal to mask length.
IntPtr ScanSignature(byte[] pattern, string mask);
long ScanSignature(string pattern, string mask);

Example

// Write a nop where the pattern matches.
WriteNop(ScanSignature("\xAA\xBB\xCC\x00\xDD\xEE", "xxx?xx"), 1);