Lua:AOBScanUnique

From Cheat Engine
Revision as of 04:21, 21 October 2025 by Mr millchick (talk | contribs) (fixed formatting, added related links)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

function AOBScanUnique(AOBString, ProtectionFlags OPTIONAL, AlignmentType OPTIONAL, AlignmentParam HALFOPTIONAL)

Scans the entire process memory for a byte pattern and returns the address if exactly one unique match is found.

  • Returns: The address as an integer if a unique match is found, or nil if no match or multiple matches are found.
  • Note: Unlike AOBScan, this function returns a single integer address (or nil) instead of a StringList, so there's no need to call destroy().
  • Implementation: This function is equivalent to calling AOBScanModuleUnique("", AOBString, ...) with an empty module name.

Function Parameters[edit]

Parameter Type Description
AOBString string A hex byte pattern with space/comma/dash separators. Use ?? or * for full-byte wildcards. Supports nibble wildcards like 7? or ?A
ProtectionFlags string Optional. Memory protection filter. Default: "*X*W*C" (scan all memory regions)
AlignmentType integer Optional. Alignment mode: 0=None, 1=Divisible, 2=Last digits. Default: 0
AlignmentParam string Optional. Hex value for alignment checking. Default: "1"


Protection Flags[edit]

The ProtectionFlags parameter filters memory regions based on protection attributes:

Syntax: Prefix each flag with:

  • + = Region MUST have this flag set
  • - = Region MUST NOT have this flag set
  • * = Don't care (default)

Flags:

  • X = Executable
  • W = Writable
  • C = Copy On Write
  • D = Dirty (macOS only)

Examples:

 "+X"       All executable memory (recommended for code patterns)
 "+W-C"     Writable memory excluding copy-on-write
 "+X-W-C"   Readonly executable memory (code sections)
 ""         Uses default "*X*W*C" (scan all memory)


Alignment Parameters[edit]

AlignmentType values:

  • 0 = fsmNotAligned - No alignment check (default)
  • 1 = fsmAligned - Address must be divisible by AlignmentParam
  • 2 = fsmLastDigits - Address must end with specific hex digits

AlignmentParam:

  • Interpreted as a hexadecimal value
  • For AlignmentType=1: Alignment boundary (e.g., "10" = 16-byte aligned, "4" = 4-byte aligned)
  • For AlignmentType=2: Required ending hex digits (e.g., "00" = addresses ending in ...00)


Usage Examples[edit]

Basic unique scan:

local address = AOBScanUnique("48 8B 05 ?? ?? ?? 89")
if address then
  print(string.format("Found at: %X", address))
  -- Use address directly for reading/writing
  writeBytes(address, 0x90, 0x90, 0x90)
else
  print("Pattern not found or not unique")
end

Scan executable memory only (faster):

local address = AOBScanUnique("55 8B EC 83 EC", "+X")
if address then
  print("Function found at: " .. address)
end

With alignment checking:

local address = AOBScanUnique("FF 15 ?? ?? ?? ??", "+X", 1, "10")
-- Only matches on 16-byte aligned addresses

Register the result as a symbol:

local addr = AOBScanUnique("48 89 5C 24 ?? 57", "+X")
if addr then
  registerSymbol("MyFunction", addr)
  print("Registered MyFunction at " .. addr)
end

Conditional execution based on pattern:

local patchAddr = AOBScanUnique("74 ?? 8B 45", "+X")
if patchAddr then
  -- Apply patch only if pattern is unique
  writeBytes(patchAddr, 0xEB)  -- Change JE to JMP
  print("Patched at: " .. patchAddr)
else
  print("ERROR: Pattern not unique or not found!")
  return false
end

Find and calculate relative offset:

local callAddr = AOBScanUnique("E8 ?? ?? ?? ??", "+X")
if callAddr then
  local offset = readInteger(callAddr + 1)
  local target = callAddr + 5 + offset
  print(string.format("Call target: %X", target))
end


Return Value[edit]

  • Success (unique match): Returns the address as an integer
  • Failure: Returns nil in these cases:
    • Pattern not found anywhere in memory
    • Multiple matches found (not unique)
    • Empty AOBString parameter


Performance Tips[edit]

  • Use +X protection flag when scanning for code to dramatically reduce scan time
  • For module-specific scans, use AOBScanModuleUnique instead (much faster)
  • The scan stops after finding more than one match to ensure uniqueness
  • More specific patterns (fewer wildcards) are faster and more likely to be unique


When to Use AOBScanUnique[edit]

Use AOBScanUnique when:

  • You expect exactly one match in the entire process
  • You want automatic validation that the pattern is unique
  • You need a simple integer address for immediate use
  • You're looking for a specific global function or unique code sequence

Use AOBScan when:

  • You expect or want multiple matches
  • You need to analyze all occurrences of a pattern
  • The pattern might appear in multiple locations


Comparison with Related Functions[edit]

Function Return Type Scope Uniqueness
AOBScanUnique Integer or nil All memory Yes (returns nil if multiple matches)
AOBScanModuleUnique Integer or nil Single module Yes (returns nil if multiple matches)
AOBScan StringList or nil All memory No (returns all matches)


Common Pitfalls[edit]

Pattern too generic:

-- This will likely fail because "90 90" appears many times
local addr = AOBScanUnique("90 90")  -- Returns nil (multiple matches)

Solution - Make pattern more specific:

local addr = AOBScanUnique("90 90 90 C3 48 89 5C")  -- More unique context

Not checking for nil:

-- WRONG - will crash if pattern not found
local addr = AOBScanUnique("48 8B")
writeBytes(addr, 0x90)  -- ERROR if addr is nil!

Solution - Always check return value:

local addr = AOBScanUnique("48 8B")
if addr then
  writeBytes(addr, 0x90)
else
  print("Pattern not found or not unique")
end


See also[edit]

Related Functions[edit]