Class: Unicorn::Emulator

Inherits:
Object
  • Object
show all
Includes:
UnicornBind
Defined in:
lib/ncpp/utils.rb,
lib/unicorn/unicorn.rb

Overview

Various utility methods tying Unicorn to the Unarm and Nitro modules

Constant Summary

Constants included from UnicornBind

UnicornBind::REG_ID

Instance Method Summary collapse

Constructor Details

#initialize(arch: UC_ARCH_ARM, mode: UC_MODE_ARM946, regions: NDS_REGIONS, sections: [], registers: {}) ⇒ Emulator

Returns a new instance of Emulator.



106
107
108
109
110
111
112
113
114
115
116
# File 'lib/unicorn/unicorn.rb', line 106

def initialize(arch: UC_ARCH_ARM, mode: UC_MODE_ARM946, regions: NDS_REGIONS, sections: [], registers: {})

  FFI::MemoryPointer.new(:pointer, 1) do |ptr|
    safe_call(:uc_open, arch, mode, ptr)
    @engine = FFI::AutoPointer.new(ptr.read_pointer, method(:uc_close))
  end

  regions.each { add_region(it) } unless regions.empty?
  sections.each { add_sect(it) } unless sections.empty?
  registers.each {|r,v| write_register(r,v) } unless registers.empty?
end

Instance Method Details

#add_region(region) ⇒ Object



126
127
128
# File 'lib/unicorn/unicorn.rb', line 126

def add_region(region)
  safe_call(:uc_mem_map, @engine, region.addr, region.size, region.prot)
end

#add_section(sect) ⇒ Object Also known as: add_sect



130
131
132
# File 'lib/unicorn/unicorn.rb', line 130

def add_section(sect)
  safe_call(:uc_mem_write, @engine, sect.addr, sect.ptr, sect.size)
end

#call_func(addr, *args) ⇒ Object



772
773
774
775
776
777
778
779
# File 'lib/ncpp/utils.rb', line 772

def call_func(addr, *args)
  raise "Calling functions with more than 4 args isn't supported yet" if args.length > 4
  args.each_with_index do |arg,i|
    send(:"write_r#{i}", arg)
  end
  run(from: addr, to: read_lr, timeout_ms: 5000)
  read_r0
end

#load_arm9Object



747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
# File 'lib/ncpp/utils.rb', line 747

def load_arm9
  arm = $rom.arm9
  main_added = false
  arm.autoload_entries.sort_by {|e| e[:address] }.each do |e|
    if e[:address] > arm.end_addr # DTCM found

      add_region(Uc::Region.new(e[:address], 16*1024))
      next
    end
    if !main_added && e[:address] > arm.start_addr # Main section found

      size = e[:address] - arm.start_addr
      add_section(Uc::Sect.new(arm.start_addr, arm.get_sect_ptr(arm.start_addr,size), size))
      main_added = true
    end
    next if e[:address]+e[:size] <= e[:address]
    ptr = arm.get_sect_ptr(e[:address],e[:size])
    add_section(Uc::Sect.new(e[:address], ptr, e[:size]))
  end
end

#load_overlay(ov_id) ⇒ Object Also known as: load_ov



766
767
768
769
# File 'lib/ncpp/utils.rb', line 766

def load_overlay(ov_id)
  ov = $rom.get_overlay(ov_id)
  add_section(Uc::Sect.new(ov.start_addr, ov.get_sect_ptr(ov.start_addr, ov.size), ov.size))
end

#read_mem(addr, size) ⇒ Object



142
143
144
145
146
147
148
149
# File 'lib/unicorn/unicorn.rb', line 142

def read_mem(addr, size)
  byte_str = nil
  FFI::MemoryPointer.new(:uint8, size) do |ptr|
    safe_call(:uc_mem_read, @engine, addr, ptr, size)
    byte_str = ptr.read_array_of_uint8(size).pack('C*')
  end
  byte_str
end

#read_register(reg) ⇒ Object Also known as: read_reg



170
171
172
173
174
175
176
177
# File 'lib/unicorn/unicorn.rb', line 170

def read_register(reg)
  val = nil
  FFI::MemoryPointer.new(:int32, 1) do |pv|
    safe_call(:uc_reg_read, @engine, REG_ID[reg], pv)
    val = pv.read_int32
  end
  val
end

#read_registers(reg_names = REG_ID.keys) ⇒ Object Also known as: read_regs



180
181
182
183
184
# File 'lib/unicorn/unicorn.rb', line 180

def read_registers(reg_names = REG_ID.keys)
  regs_h = {}
  reg_names.each {|r| regs_h[r] = read_register(r) }
  regs_h
end

#run(from: nil, to: -1,, timeout_ms: 0, max_ins: 0) ⇒ Object



118
119
120
121
122
123
124
# File 'lib/unicorn/unicorn.rb', line 118

def run(from: nil, to: -1, timeout_ms: 0, max_ins: 0)
  raise "The 'from' parameter must be specified" if from.nil?
  if to == -1 && timeout_ms == 0 && max_ins == 0
    raise "The 'to' parameter must be specified if 'timeout_ms' or 'max_ins' are not"
  end
  safe_call(:uc_emu_start, @engine, from, to, timeout_ms, max_ins)
end

#write_mem(addr, byte_str) ⇒ Object



135
136
137
138
139
140
# File 'lib/unicorn/unicorn.rb', line 135

def write_mem(addr, byte_str)
  FFI::MemoryPointer.new(:uint8, byte_str.bytesize) do |ptr|
    ptr.write_array_of_uint8(byte_str.bytes)
    safe_call(:uc_mem_write, @engine, addr, ptr, byte_str.bytesize)
  end
end

#write_register(reg, val) ⇒ Object Also known as: write_reg



151
152
153
154
155
156
# File 'lib/unicorn/unicorn.rb', line 151

def write_register(reg, val)
  FFI::MemoryPointer.new(:int32, 1) do |pv|
    pv.write_int(val)
    safe_call(:uc_reg_write, @engine, REG_ID[reg], pv)
  end
end

#write_registers(regs_h) ⇒ Object Also known as: write_regs



159
160
161
# File 'lib/unicorn/unicorn.rb', line 159

def write_registers(regs_h)
  regs_h.each {|r,v| write_register(r,v) }
end