scan_changed
Identifies memory addresses where values have changed since the previous scan, enabling detection of dynamic game variables for reverse engineering and modification.
Instructions
Find addresses where value has changed since last scan.
Returns:
Number of changed addresses.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
No arguments | |||
Implementation Reference
- MCP tool handler implementing scan_changed: filters scan results to addresses where value changed since last scan using batched Frida Memory.readByteArray and comparison to stored last_values. Updates scan_state.results and last_values.@mcp.tool() def scan_changed() -> Dict[str, Any]: """ Find addresses where value has changed since last scan. Returns: Number of changed 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) addresses = [a for a in _session.scan_state.results if a in _session.scan_state.last_values] if not addresses: return {"success": True, "remaining": 0} expected_map = {} for addr in addresses: last_val = _session.scan_state.last_values[addr] if value_type == "string": expected_map[addr] = str(last_val).encode('utf-8').hex() else: expected_map[addr] = _pack_value(last_val, value_type).hex() batch_size = 1000 new_results = [] new_last_values = {} for batch_start in range(0, len(addresses), batch_size): batch = addresses[batch_start:batch_start + batch_size] addr_expected = ", ".join(f'["{hex(a)}", "{expected_map[a]}"]' for a in batch) script_code = f""" var pairs = [{addr_expected}]; var size = {value_size}; var changed = []; for (var i = 0; i < pairs.length; i++) {{ try {{ var data = Memory.readByteArray(ptr(pairs[i][0]), 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 !== pairs[i][1]) {{ changed.push({{address: pairs[i][0], hex: hex}}); }} }} catch (e) {{ }} }} send(JSON.stringify(changed)); """ 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 changed = json.loads(result_data[0]) for c in changed: addr = int(c['address'], 16) try: current_value = _unpack_value(bytes.fromhex(c['hex']), value_type) new_results.append(addr) new_last_values[addr] = current_value except: pass _session.scan_state.results = new_results _session.scan_state.last_values = new_last_values return {"success": True, "remaining": len(new_results)} except Exception as e: return {"error": f"Scan changed failed: {str(e)}"}
- Dataclass managing global scan state (results list, last_values dict, value_type, active flag) used by scan_changed and related tools.@dataclass 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 to unpack raw bytes from memory read into typed value (int/float/string) for scan_changed value comparisons.def _unpack_value(data: bytes, value_type: str) -> Any: """Unpack bytes to value 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.unpack(fmt, data)[0] elif value_type == "string": return data.split(b'\x00')[0].decode('utf-8', errors='replace') return struct.unpack("<i", data)[0]
- Helper returning byte size for value_type used in scan_changed for Memory.readByteArray size.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)
- src/frida_game_hacking_mcp/server.py:195-198 (registration)Tool name 'scan_changed' listed in memory_operations category within list_capabilities tool response."memory_operations": [ "read_memory", "write_memory", "scan_value", "scan_next", "scan_changed", "scan_unchanged", "scan_pattern", "get_scan_results", "clear_scan", "list_memory_regions"