Class: Nitro::CodeBin

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

Overview

Various utility methods tying Nitro to the Unarm module

Direct Known Subclasses

ArmBin, OverlayBin

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#functionsObject (readonly)

Returns the value of attribute functions.



591
592
593
# File 'lib/ncpp/utils.rb', line 591

def functions
  @functions
end

Instance Method Details

#boundsObject



175
176
177
# File 'lib/nitro/nitro.rb', line 175

def bounds
  start_addr..end_addr
end

#disasm_function(addr) ⇒ Object Also known as: disasm_func

TODO: rewrite in Rust?



625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
# File 'lib/ncpp/utils.rb', line 625

def disasm_function(addr)
  is_thumb = addr & 1 != 0
  addr -= 1 if is_thumb

  instructions = [] # [Unarm::Ins]
  labels = {}       # key: addr, val: [xrefs]
  pool = {}         # key: addr, val: Unarm::Data

  send(:"each_#{is_thumb ? 'thumb' : 'arm'}_ins", addr..) do |ins|
    next if pool.keys.include? ins.addr
    raise "Illegal instruction found at #{ins.addr.to_hex}; this is likely not a function." if ins.illegal?

    instructions << ins

    if target = ins.target_addr # if target addr not nil
      pool[target] ||= Unarm::Data.new(read_word(target), addr: target, loc: get_loc)
    end

    if ins.opcode == :b && (label_addr = ins.branch_dest)
      labels[label_addr] = [] if labels[label_addr].nil?
      labels[label_addr] << ins.addr # add xref

      break if ins.unconditional? && ins.addr > labels.keys.max

    elsif ins.function_end? && (labels.empty? || ins.addr > labels.keys.max)
      break
    end

    if instructions.length > 2500 # TODO: is this a good threshold?
      raise "Function at #{addr.to_hex} is growing exceptionally large; it is likely not a function."
    end
  end

  if !instance_variable_defined? :@functions
    instance_variable_set(:@functions, {})
  end

  @functions[addr] = {
    thumb?: is_thumb,
    instructions: instructions,
    labels: labels,
    literal_pool: pool,
  }
end

#each_arm_instruction(range = bounds) ⇒ Object Also known as: each_arm_ins, each_ins



609
610
611
612
613
# File 'lib/ncpp/utils.rb', line 609

def each_arm_instruction(range = bounds)
  each_word(range) do |word, addr|
    yield Unarm::ArmIns.disasm(word, addr, get_loc)
  end
end

#each_byte(range = bounds) ⇒ Object



206
207
208
209
210
# File 'lib/nitro/nitro.rb', line 206

def each_byte(range = bounds)
  read(range,1).each do |byte, addr|
    yield byte, addr
  end
end

#each_char(range = bounds) ⇒ Object



212
213
214
215
216
# File 'lib/nitro/nitro.rb', line 212

def each_char(range = bounds)
  each_byte(range) do |char, addr|
    yield char.chr, addr
  end
end

#each_dword(range = bounds) ⇒ Object



194
195
196
197
198
# File 'lib/nitro/nitro.rb', line 194

def each_dword(range = bounds)
  read(range,8).each do |dword, addr|
    yield dword, addr
  end
end

#each_hword(range = bounds) ⇒ Object



200
201
202
203
204
# File 'lib/nitro/nitro.rb', line 200

def each_hword(range = bounds)
  read(range,2).each do |hword, addr|
    yield hword, addr
  end
end

#each_thumb_instruction(range = bounds) ⇒ Object Also known as: each_thumb_ins



617
618
619
620
621
# File 'lib/ncpp/utils.rb', line 617

def each_thumb_instruction(range = bounds)
  each_hword(range) do |hword, addr|
    yield Unarm::ThumbIns.disasm(hword, addr, get_loc)
  end
end

#each_word(range = bounds) ⇒ Object



188
189
190
191
192
# File 'lib/nitro/nitro.rb', line 188

def each_word(range = bounds)
  read(range).each do |word, addr|
    yield word, addr
  end
end

#end_addressObject Also known as: end_addr



170
171
172
# File 'lib/nitro/nitro.rb', line 170

def end_address
  start_address + size
end

#find_hex(hex_str) ⇒ Object



719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
# File 'lib/ncpp/utils.rb', line 719

def find_hex(hex_str)
  target_bytes = [hex_str].pack('H*').unpack('C*')
  target_len = target_bytes.length
  found = 0
  found_addr = nil

  each_byte do |byte, addr|
    if byte == target_bytes[found]
      found += 1
      found_addr = addr
      break if found == target_len
    else
      found = 0
    end
  end

  raise 'Could not find hex byte string in binary.' if found != target_len

  found_addr - (target_len - 1) * 4
end

#get_function(addr) ⇒ Object

TODO: rewrite in Rust?



672
673
674
675
676
677
678
679
# File 'lib/ncpp/utils.rb', line 672

def get_function(addr)
  func = @functions.nil? ? nil : @functions[addr]
  if func.nil?
    disasm_func(addr)
    func = @functions[addr]
  end
  func
end

#get_locationObject Also known as: get_loc



593
594
595
# File 'lib/ncpp/utils.rb', line 593

def get_location
  respond_to?(:id) ? "ov#{id}" : Unarm.cpu.to_s
end

#get_section_ptr(addr, sect_size) ⇒ Object Also known as: get_sect_ptr



223
224
225
226
227
228
# File 'lib/nitro/nitro.rb', line 223

def get_section_ptr(addr, sect_size)
  ptr = FFI::MemoryPointer.new(:pointer, 1)
  ptr.write_pointer(codeBin_getSectPtr(@ptr, addr, sect_size))
  raise "Could not read #{sect_size} bytes from address #{addr.to_hex}" if ptr.read_pointer == FFI::Pointer::NULL
  ptr.read_pointer
end

#read(range = bounds, step = 4) ⇒ Object

Raises:

  • (ArgumentError)


179
180
181
182
183
184
185
186
# File 'lib/nitro/nitro.rb', line 179

def read(range = bounds, step = 4)
  raise ArgumentError, 'step must be 1, 2, 4, or 8 (bytes)' unless [1,2,4,8].include? step
  raise ArgumentError, 'range must be a Range' unless range.is_a? Range

  clamped = Range.new(range.begin || start_addr, [range.end || end_addr, end_addr].min)

  clamped.step(step).map { |addr| [send(:"read#{step * 8}", addr), addr] }
end

#read16(addr) ⇒ Object Also known as: read_hword



151
152
153
# File 'lib/nitro/nitro.rb', line 151

def read16(addr)
  codeBin_read16(@ptr, addr)
end

#read32(addr) ⇒ Object Also known as: read_word



146
147
148
# File 'lib/nitro/nitro.rb', line 146

def read32(addr)
  codeBin_read32(@ptr, addr)
end

#read64(addr) ⇒ Object Also known as: read_dword



141
142
143
# File 'lib/nitro/nitro.rb', line 141

def read64(addr)
  codeBin_read64(@ptr, addr)
end

#read8(addr) ⇒ Object Also known as: read_byte



156
157
158
# File 'lib/nitro/nitro.rb', line 156

def read8(addr)
  codeBin_read8(@ptr, addr)
end

#read_arm_instruction(addr) ⇒ Object Also known as: read_arm_ins, read_ins



598
599
600
# File 'lib/ncpp/utils.rb', line 598

def read_arm_instruction(addr)
    Unarm::ArmIns.disasm(read32(addr), addr, get_loc)
end

#read_cstring(addr) ⇒ Object Also known as: read_cstr



218
219
220
# File 'lib/nitro/nitro.rb', line 218

def read_cstring(addr)
  codeBin_readCString(@ptr, addr)
end

#read_thumb_instruction(addr) ⇒ Object Also known as: read_thumb_ins



604
605
606
# File 'lib/ncpp/utils.rb', line 604

def read_thumb_instruction(addr)
    Unarm::ThumbIns.disasm(read16(addr), addr, get_loc)
end

#reloc_function(addr) ⇒ Object Also known as: reloc_func



681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
# File 'lib/ncpp/utils.rb', line 681

def reloc_function(addr)
  if !instance_variable_defined? :@functions
    instance_variable_set(:@functions, {})
  end

  func = get_function(addr)

  out = ""
  labels = func[:labels].keys
  func[:instructions].each do |ins|
    if label = labels.index(ins.addr)
      out << "#{label+1}:\n"
    end

    if target = ins.target_addr
      out << "#{ins.str[..ins.str.index(',')]} =#{(func[:literal_pool][target]).value}"

    elsif ins.opcode == :b && (label = labels.index(ins.branch_dest))
      out << ins.str[..ins.str.index('#')-1] << "#{label+1}f"

    elsif (branch = ins.branch_dest) && ins.str.include?('#') &&
          (dest = ins.str[ins.str.index('#')+1..].hex) &&
          dest != branch && (dest += ins.addr) &&
          (dest < func[:instructions][0].addr || dest > func[:instructions][-1].addr)

      out << ins.str[..ins.str.index('#')] << dest.to_hex

    else
      out << ins.str
    end

    out << "\n"
  end

  out
end

#sizeObject



161
162
163
# File 'lib/nitro/nitro.rb', line 161

def size
  codeBin_getSize(@ptr)
end

#start_addressObject Also known as: start_addr



165
166
167
# File 'lib/nitro/nitro.rb', line 165

def start_address
  codeBin_getStartAddress(@ptr)
end