--[[ Do an AOB scan of executable read-only memory and create a symbol for one of the returned addresses specified by index (0-based index of results). Some games have a few very similar code sections that can't be narrowed to a single result with an aob scan. For example in Heat Signature there are two very similar sections of code, the first one adds 1.0 from a memory address and the second one add -1.0 (to decrease item count when using equipment). This finds the second one (0-based index 1) and registers the symbol INJECT_INVENTORY_USE: LuaCall(registerAobAddress("INJECT_INVENTORY_USE", "f2 0f 10 01 f2 0f 58 05 * * * * f2 0f 11 01 EB 27", 1)) The function also returns the address that was found, or nil if it wasn't found or the index was out of range for the returned list. --]] function registerAobAddress(symbol, aobstring, index, flags) -- returns a StringList that must be freed if flags == nil then flags = "+X-W" end local sl = AOBScan(aobstring, flags) local result = nil -- local sl = AOBScan(aobstring) if (sl ~= nil and index >= 0 and index < sl.Count) then result = sl[index] unregisterSymbol(symbol) registerSymbol(symbol, result, true) else print("AOB NOT FOUND") end object_destroy(sl) return result end -- sample for heat signature, returns two results and registers the symbol -- for the second result -- print(registerAobAddress("TEST_SYMBOL", "f2 0f 10 01 f2 0f 58 05 * * * * f2 0f 11 01 EB 27", 1)) --[[ Simplified memscan, always waits for it to finish and deletes the memscan object and FoundList for you, and returns a lua array with the results vartype: vtByte vtWord 2 bytes vtDword 4 bytes vtQword 8 bytes vtSingle float vtDouble vtString vtByteArray vtGrouped vtBinary vtAll --]] function luaMemscan(scanoption, vartype, roundingType, input1, input2, startAddress, stopAddress, protectionFlags, alignmentType, alignmentparam, isHexadecimalInput, isNotABinaryString, isunicodescan, iscasesensitive) local scan = createMemScan() local found = createFoundList(scan) local a, n = fsmNotAligned, 1 local p = protectionFlags or "*X*W" --scan.firstScan(soExactValue, vtByteArray, nil, "f8 ff ff ff", nil, 0x0, 0xffffffffffffffff, "*X*W", a, n, true, false, false, false) --scan.firstScan(scanoption, vartype, roundingType, input1, input2, startAddress, stopAddress, p, a, n, isHexadecimalInput, isNotABinaryString, isunicodescan, iscasesensitive) scan.firstScan(scanoption, vartype, nil, input1, input2, startAddress, stopAddress, p, a, n, isHexadecimalInput, false, false, false) scan.waitTillDone() found.initialize() local result = {} for i=1,found.getCount() do result[i] = found.getAddress(i-1) end found.destroy() scan.destroy() return result end -- types: vtByte, vtWord, vtDword, vtQword, vtSingle, vtDouble, vtString, vtByteArray, vtGrouped, vtBinary, vtAll function simpleModuleScan(moduleName, input, inputType, isHex) local h = false if isHex then h = true end local moduleAddress = getAddress(moduleName) if (moduleAddress == nil) then print("Could not find module address "..moduleName) return {} end --print("moduleAddress: "..moduleAddress.." is a "..type(moduleAddress)) local moduleSize = getModuleSize(moduleName) if (moduleSize == nil) then print("Could not find module size "..moduleName) return {} end --print("moduleSize: "..moduleSize.." is a "..type(moduleSize)) local result = luaMemscan(soExactValue, inputType, nil, input, nil, moduleAddress, moduleAddress + moduleSize, "*X*W", fsmNotAligned, 1, isHex, false, false, false) return result end function printArray(a) print("Array with "..string.format("%d", #a).." elements:") for i=1,#a do print(" "..string.format("%d", i)..": "..a[i]) end end --[[ Memscan version that --[[ Specifically for Heat Signature (and possibly other Game Maker Studio games), this will do a memory scan to find a string, then scan a module to find where it is, and finally do an aob scan and return the closest matching address to where it was found. For example there is a string 'gml_Script_EnemyIsAlertable' that is referenced here in code: // I want to inject here, just changing this to 'ret; nop' "Heat_Signature.exe"+3C88E1: 89 E5 - mov ebp,esp "Heat_Signature.exe"+3C88E3: 53 - push ebx "Heat_Signature.exe"+3C88E4: 57 - push edi "Heat_Signature.exe"+3C88E5: 56 - push esi "Heat_Signature.exe"+3C88E6: 81 E4 F8 FF FF FF - and esp,FFFFFFF8 "Heat_Signature.exe"+3C88EC: 83 EC 58 - sub esp,58 "Heat_Signature.exe"+3C88EF: 8B 75 18 - mov esi,[ebp+18] "Heat_Signature.exe"+3C88F2: 8B 7D 10 - mov edi,[ebp+10] "Heat_Signature.exe"+3C88F5: 8B 45 08 - mov eax,[ebp+08] "Heat_Signature.exe"+3C88F8: 8B 4D 0C - mov ecx,[ebp+0C] "Heat_Signature.exe"+3C88FB: 89 4C 24 50 - mov [esp+50],ecx "Heat_Signature.exe"+3C88FF: 89 44 24 4C - mov [esp+4C],eax // Heat_Signature.exe+BA0BC4 points to the string 'gml_Script_EnemyIsAlertable' "Heat_Signature.exe"+3C8903: C7 44 24 44 C4 0B 3C 01 - mov [esp+44],Heat_Signature.exe+BA0BC4 "Heat_Signature.exe"+3C890B: C7 44 24 48 00 00 00 00 - mov [esp+48],00000000 "Heat_Signature.exe"+3C8913: A1 F0 4D 7E 04 - mov eax,[Heat_Signature.exe+3FC4DF0] aobScanNearStringReference("EnemyIsAlertable', "Heat_Signature.exe", "gml_Script_EnemyIsAlertable", "89 E5 53 57 56 81 E4 F8 FF FF FF") This does this: 1) Scan "Heat_Signature.exe" for the string "gml_Script_EnemyIsAlertable" (013C0bC4) 2) Scan "Heat_Signature.exe" for that address (013C0BC4), finding it at Heat_Signature.exe+3c8907 3) Scan "Heat_Signature.exe" for the aob "89 E5 53 57 56 81 E4 F8 FF FF FF" 4) Registers the symbol "EnemyIsAlertable" as the address from the aob scan that is nearest to Heat_Signature.exe+3c8907 (Heat_Signature.exe+3C88E1) An auto-assemble script would call it like this when injecting: [ENABLE] LuaCall(aobScanNearStringReference("EnemyIsAlertable', "Heat_Signature.exe", "gml_Script_EnemyIsAlertable", "89 E5 53 57 56 81 E4 F8 FF FF FF")) assert(EnemyIsAlertable,89 E5) EnemyIsAlertable: ret nop [DISABLE] EnemyIsalertable: mov ebp,esp unregistersymbol(EnemyIsAlertable) --]] function aobScanNearStringReference(symbolName, moduleName, searchString, searchAob, preferredIndex) local aAddressOfString = simpleModuleScan(moduleName, searchString, vtString, false) if #aAddressOfString ~= 1 then print("Could not find string "..searchString.." or not just once ("..string.format("%d", #aAddressOfString)..")") return nil end local sAddressOfString = aAddressOfString[1] -- print("Found string "..searchString.." at "..sAddressOfString) local aAddressOfStringReference = simpleModuleScan(moduleName, sAddressOfString, vtDword, true) -- it's a hex address and a string if #aAddressOfStringReference < 1 then print("Could not find reference to string "..searchString) return nil end if preferredIndex == nil and #aAddressOfStringReference ~= 1 then print("Could not find just one reference to string "..searchString.." (try passing preferredIndex)") return nil end preferredIndex = preferredIndex or 1 local sAddressOfStringReference = aAddressOfStringReference[preferredIndex] -- print("Found string "..searchString.." referebce at "..sAddressOfStringReference) local addressOfStringReference = getAddress(sAddressOfStringReference) local aAOB = simpleModuleScan(moduleName, searchAob, vtByteArray, true) if #aAOB < 1 then print("Could not find AOB "..searchAob) return nil end local closest, distance = 0, 0x100000 for i=1,#aAOB do local d = math.abs(getAddress(aAOB[i]) - addressOfStringReference) if d < distance then distance = d closest = i end end if closest == 0 then print("Could not find AOB within a megabyte of the string reference...") return nil end local result = aAOB[closest] unregisterSymbol(symbolName) registerSymbol(symbolName, result, true) return result end -- sample call -- print(aobScanNearStringReference("EnemyIsAlertable", "Heat_Signature.exe", "gml_Script_EnemyIsAlertable", "55 89 E5 53 57 56 81 E4 F8 FF FF FF", 1))