scan_next
Refine memory scan results by searching for a specific value to narrow down addresses in game hacking and reverse engineering processes.
Instructions
Narrow scan results with new value.
Args:
value: New value to search for
Returns:
Number of remaining addresses.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| value | Yes |
Implementation Reference
- The scan_next tool handler: narrows previous scan results by batch-reading memory from candidate addresses via Frida scripts and filtering those whose current value bytes match the packed new value.@mcp.tool() def scan_next(value: Union[int, float, str]) -> Dict[str, Any]: """ Narrow scan results with new value. Args: value: New value to search for Returns: Number of remaining addresses. """ global _session if not _session.is_attached(): return {"error": "Not attached. Use attach() first."} if not _session.scan_state.scan_active: return {"error": "No active scan. Use scan_value() first."} try: value_type = _session.scan_state.value_type value_size = _get_value_size(value_type) if value_type == "string": expected_hex = value.encode('utf-8').hex() else: expected_hex = _pack_value(value, value_type).hex() addresses = _session.scan_state.results if not addresses: return {"success": True, "value": value, "remaining": 0} batch_size = 1000 new_results = [] for batch_start in range(0, len(addresses), batch_size): batch = addresses[batch_start:batch_start + batch_size] addr_list = ", ".join(f'"{hex(a)}"' for a in batch) script_code = f""" var addresses = [{addr_list}]; var size = {value_size}; var expected = "{expected_hex}"; var matches = []; for (var i = 0; i < addresses.length; i++) {{ try {{ var data = Memory.readByteArray(ptr(addresses[i]), size); var hex = ''; var bytes = new Uint8Array(data); for (var j = 0; j < bytes.length; j++) {{ hex += ('0' + bytes[j].toString(16)).slice(-2); }} if (hex === expected) matches.push(addresses[i]); }} catch (e) {{ }} }} send(JSON.stringify(matches)); """ result_data = [] def on_message(message, data): if message['type'] == 'send': result_data.append(message['payload']) script = _session.session.create_script(script_code) script.on('message', on_message) script.load() script.unload() if result_data: import json matches = json.loads(result_data[0]) new_results.extend([int(a, 16) for a in matches]) _session.scan_state.results = new_results _session.scan_state.last_values = {addr: value for addr in new_results} return { "success": True, "value": value, "remaining": len(new_results), "message": f"Narrowed to {len(new_results)} addresses." } except Exception as e: return {"error": f"Scan next failed: {str(e)}"}
- Dataclass used by scan_next to maintain scan results list, last known values per address, value type, and active state.class ScanState: """Tracks memory scan state for Cheat Engine-style scanning.""" value_type: str = "" results: List[int] = field(default_factory=list) last_values: Dict[int, Any] = field(default_factory=dict) scan_active: bool = False
- Helper function used by scan_next to determine byte size of value_type for memory reads.def _get_value_size(value_type: str) -> int: """Get byte size for value type.""" sizes = { "int8": 1, "uint8": 1, "int16": 2, "uint16": 2, "int32": 4, "uint32": 4, "int64": 8, "uint64": 8, "float": 4, "double": 8 } return sizes.get(value_type, 4)
- Helper function used by scan_next to pack the new value into bytes/hex for comparison in Frida script.def _pack_value(value: Any, value_type: str) -> bytes: """Pack value to bytes based on type.""" formats = { "int8": "<b", "uint8": "<B", "int16": "<h", "uint16": "<H", "int32": "<i", "uint32": "<I", "int64": "<q", "uint64": "<Q", "float": "<f", "double": "<d" } fmt = formats.get(value_type) if fmt: return struct.pack(fmt, value) elif value_type == "string": return value.encode('utf-8') + b'\x00' return struct.pack("<i", int(value))