#!/usr/bin/env python # small universal game trainer proof of concept # - search byte location in memory by value # - modify byte location in memory # Copyright (C) 2010 Matthias -apoc- Hecker # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # http://bitbucket.org/haypo/python-ptrace/overview from ptrace.debugger.debugger import PtraceDebugger from ptrace.debugger.memory_mapping import readProcessMappings import sys import logging import os import pickle #logging.basicConfig(level=logging.DEBUG) if len(sys.argv) != 2: print "Usage: %s " % sys.argv[0] sys.exit() pid = int(sys.argv[1]) max_memory = 0x00FFFFFF locations_file = "pycheat_locations.tmp" open(locations_file, "w").close() # truncate file print "pycheat: universal game trainer v0.1" print "process id: %d" % pid print "highest address: %#x" % max_memory def write_locations(locations): global locations_file tmpfile = open(locations_file, "w") pickle.dump(locations, tmpfile) tmpfile.close() def read_locations(): global locations_file try: tmpfile = open(locations_file, "r") locations = pickle.load(tmpfile) return locations except: return None else: tmpfile.close() def search_memory_locations(pid, max_memory, search_value): child_pid = os.fork() if child_pid == 0: # search within forked process: locations = list() prev_locations = read_locations() dbg = PtraceDebugger() process = dbg.addProcess(pid, False) memory_mappings = readProcessMappings(process) print "\x1B[?25l", # deactivate cursor (^_^) for memory_mapping in memory_mappings: # only search in read/writable memory areas within range... if "rw" in memory_mapping.permissions and memory_mapping.end <= max_memory: for loc in range(memory_mapping.start, memory_mapping.end): value = process.readBytes(loc, 1) if value[0] == search_value: print "search memory area[0x%08X-0x%08X] address[0x%08X] value[0x%02X (%03d)] \r" % (memory_mapping.start, memory_mapping.end, loc, ord(value), ord(value)), if prev_locations and len(prev_locations) > 0 and not loc in prev_locations: continue # skip prev not found locations locations.append(loc) print "\x1B[?25h", # activate cursor dbg.quit() write_locations(locations) sys.exit() return child_pid # don't really need this def change_memory(pid, location, value): dbg = PtraceDebugger() process = dbg.addProcess(pid, False) process.writeBytes(location, value) dbg.quit() locations = None # loop until the correct (uniqe) location is found while not locations or len(locations) != 1: print if locations and len(locations) != 1: print "found %d occurrences, change value to:" % len(locations), else: print "searching for address by byte value:", search_value = "%c" % int(raw_input()) # this forks the process and search within memory... search_memory_locations(pid, max_memory, search_value) # wait until the forked process returns/quits os.wait() # for communication with forked process a pickle tmp file is used locations = read_locations() found_location = locations[0] print print "found 1 occurrence! correct address for this value is 0x%08X" % (found_location) print "change value in memory at 0x%08X to:" % (found_location), change_value = "%c" % int(raw_input()) change_memory(pid, found_location, change_value) print "done."