#!/usr/bin/ruby # # opens a target process and scan its memory for a specific integer value # which varies over time # returns a list of matching offsets, may patch them # # http://metasm.cr0.org/ require 'metasm' target = ARGV.shift # pid/part of the exe filename valfmt = 'L' # unsigned long, see "ri pack" for other values # ask user for a value to search def get_val puts "new value?" Integer(gets) # 0x28 works end class String # returns all indexes of str (iterate #index) def indexes(str) i = -1 ret = [] ret << i while i = index(str, i+1) ret end end def scan_for_value(mem, fmt) offsets = nil # [42, 28] if a previous search found offsets (with the same process, may be randomized otherwise) begin 10.times { # nr of searches - increase if too many matches remain mem.invalidate searchval = [get_val].pack(fmt) if not offsets # 1st scan: takes time offsets = [] (0...0x1_0000_0000).step(0x1000).each { |pageoff| if $stdout.tty? and pageoff & 0xff_ffff == 0 # little feedback $stdout.print "%08x\r" % pageoff $stdout.flush end offsets.concat mem[pageoff, 0x1000].to_str.indexes(searchval).map { |v| pageoff+v } } $stdout.print " \r" if $stdout.tty? else # refine search offsets.delete_if { |off| mem[off, searchval.length] != searchval } end puts '[' + offsets.map { |o| '%08x' % o }.join(', ') + ']' } rescue Interrupt # ctrl-C puts "ok, ok!" end offsets end def patch_value(mem, fmt, offset, value) bin = [value].pack(fmt) mem[offset, bin.length] = bin end process = Metasm::OS.current.find_process(target) abort "cannot find target" if not process mem = process.memory offsets = scan_for_value(mem, valfmt) puts "value to replace with ?" newval = Integer(gets) offsets.each { |off| patch_value(mem, valfmt, off, newval) }