class TurboRex::MSRPC::RPCFinder::MemoryFinder

Attributes

header[R]
process[R]

Public Class Methods

list_process_pid() click to toggle source
# File lib/turborex/msrpc/rpcfinder.rb, line 1193
def self.list_process_pid
  TurboRex::Windows.list_all_process_pid
end
new(pid, opts = {}) click to toggle source
# File lib/turborex/msrpc/rpcfinder.rb, line 1168
def initialize(pid, opts = {})
  raise 'Not work on non-Windows os.' unless ::OS.windows?

  if opts[:debug_priv]
    unless Metasm::WinOS.get_debug_privilege
      raise 'Unable to get SeDebugPrivilege.'
    end
  end

  @process = open_process(pid)
  @mem = @process.memory
  opts[:force_load] ||= {}

  unless load_headers(opts[:force_load])
    raise 'Unable to load RPC structure definitions.'
  end

  @header.prepare_visualstudio

  @gRpcServer = nil
  @rpc_interfaces = []
  @server_interfaces = []
  @endpoints = []
end

Public Instance Methods

close() click to toggle source
# File lib/turborex/msrpc/rpcfinder.rb, line 1197
def close
  @process.close
end
enum_rpc_interfaces(rpc_server_t) click to toggle source
# File lib/turborex/msrpc/rpcfinder.rb, line 1209
def enum_rpc_interfaces(rpc_server_t)
  num_entries = rpc_server_t.InterfaceDict.NumberOfEntries
  dictsize = num_entries * ptr_len

  rpc_interface_t = @header['RPC_INTERFACE_T'] # read data as RPC_INTERFACE_T
  begin
    data = @mem.get_page(rpc_server_t.InterfaceDict.pArray, dictsize)
    return false if data.nil?

    (0..dictsize).step(ptr_len) do |p|
      prpc_interface_t = if pe.ptr_32?
                           data[p, ptr_len].unpack('V')[0]
                         else
                           data[p, ptr_len].unpack('Q<')[0]
                         end

      interface_t = rpc_interface_t.from_str(@mem.get_page(prpc_interface_t, rpc_interface_t.size))
      get_rpc_interfaces_info(interface_t)
    end
  rescue StandardError
    false
  end
end
find_global_rpc_server() click to toggle source
# File lib/turborex/msrpc/rpcfinder.rb, line 1299
def find_global_rpc_server
  rpcrt4 = @process.modules.select { |m| m.path =~ /rpcrt4.dll/i }[0]

  pe = TurboRex::PEFile::PE.new_from_file(rpcrt4.path)
  data_section = pe.sections.select { |s| s.name == '.data' }[0]
  startaddr = rpcrt4.addr + data_section.vma
  endaddr = startaddr + data_section._section_header.v['Misc']
  ptr_len = pe.ptr_32? ? 4 : 8 && @header.llp64
  max_entries = @header.numeric_constants.assoc('MAX_SIMPLE_DICT_ENTRIES')[1]
  pe.close

  scan_marker(nil, startaddr..endaddr, ptr_len) do |data|
    pointer = if pe.ptr_32?
                data.unpack('V')[0]
              else
                data.unpack('Q<')[0]
              end

    rpc_server_t = @header['RPC_SERVER_T'] # read data as RPC_SERVER_T
    begin
      data = @mem.get_page(pointer, rpc_server_t.size)
    rescue StandardError
      next
    end
    rpc_server_t.from_str data

    num_entries = rpc_server_t.InterfaceDict.NumberOfEntries
    dictsize = num_entries * ptr_len
    next if num_entries > max_entries || num_entries <= 0

    rpc_interface_t = @header['RPC_INTERFACE_T'] # read data as RPC_INTERFACE_T
    begin
      data = @mem.get_page(rpc_server_t.InterfaceDict.pArray, dictsize)
      next if data.nil?

      (0..dictsize).step(ptr_len) do |p|
        prpc_interface_t = if pe.ptr_32?
                             data[p, ptr_len].unpack('V')[0]
                           else
                             data[p, ptr_len].unpack('Q<')[0]
                           end

        interface_t = rpc_interface_t.from_str(@mem.get_page(prpc_interface_t, rpc_interface_t.size))
        if interface_t.pRpcServer == pointer
          if interface_t.RpcServerInterface.TransferSyntax.to_string == TurboRex::MSRPC::RPCBase::DCE_TransferSyntax.to_s
            return rpc_server_t
          end
        end
      end
    rescue StandardError
      next
    end
  end
end
find_rpc_server() click to toggle source
# File lib/turborex/msrpc/rpcfinder.rb, line 1205
def find_rpc_server
  @gRpcServer = find_global_rpc_server
end
get_com_interface_name(interface_id) click to toggle source
# File lib/turborex/msrpc/rpcfinder.rb, line 1262
def get_com_interface_name(interface_id)
  require 'win32/registry'
  case @arch
  when 'x86'
    prefix = ''
  when 'x64'
    prefix = 'Wow6432Node\\'
  end
  begin
    Win32::Registry::HKEY_CLASSES_ROOT.open(prefix + "Interface\\{#{interface_id}}") do |reg|
      return reg.read('')[1] # default value
    end
  rescue StandardError
    false
  end
end
get_interface_type(rpc_interface) click to toggle source
# File lib/turborex/msrpc/rpcfinder.rb, line 1249
def get_interface_type(rpc_interface)
  if rpc_interface.Flags == @header.numeric_constants.assoc('RPC_IF_OLE')[1]
    return TurboRex::MSRPC::RPCBase::InterfaceType::OLE
  end

  uuid = TurboRex::MSRPC::Utils.raw_to_guid_str(rpc_interface.RpcServerInterface.InterfaceId.to_string)
  if get_com_interface_name(uuid)
    return TurboRex::MSRPC::RPCBase::InterfaceType::DCOM
  end

  TurboRex::MSRPC::RPCBase::InterfaceType::RPC
end
get_location(_addr) click to toggle source
# File lib/turborex/msrpc/rpcfinder.rb, line 1279
def get_location(_addr)
  raise NotImplementedError
end
get_rpc_interfaces_info(rpc_interface) click to toggle source
# File lib/turborex/msrpc/rpcfinder.rb, line 1233
def get_rpc_interfaces_info(rpc_interface)
  info = TurboRex::MSRPC::RPCFinder::MemoryFinder::RPC_Interface.new
  info.flags = rpc_interface.Flags
  info.interface_type = get_interface_type(rpc_interface)
  info.interface_id = TurboRex::MSRPC::Utils.raw_to_guid_str(rpc_interface.RpcServerInterface.InterfaceId.to_string)
  if info.interface_type == TurboRex::MSRPC::RPCBase::InterfaceType::DCOM
    info.name = get_com_interface_name(info.interface_id)
  end
  info.syntax = TurboRex::MSRPC::Utils.raw_to_guid_str(rpc_interface.RpcServerInterface.TransferSyntax.to_string)

  case info.interface_type
  when TurboRex::MSRPC::RPCBase::InterfaceType::RPC
    get_location(rpc_interface.RpcServerInterface.DispatchTable)
  end
end
process_handle() click to toggle source
# File lib/turborex/msrpc/rpcfinder.rb, line 1201
def process_handle
  @process.handle
end
scan_marker(marker, range, size = marker.size, step = 1) { |data, va| ... } click to toggle source
# File lib/turborex/msrpc/rpcfinder.rb, line 1283
def scan_marker(marker, range, size = marker.size, step = 1)
  mem = @mem
  res = []

  range.step(step) do |va|
    data = mem.get_page(va, size)
    yield(data, va) if block_given?

    unless data.nil?
      res << va if data == marker
    end
  end

  res
end

Private Instance Methods

force_load_file(opts) click to toggle source
# File lib/turborex/msrpc/rpcfinder.rb, line 1420
def force_load_file(opts)
  @header = TurboRex::CStruct::NativeParser.new(nil, opts)
end
load_headers(force_load = {}) click to toggle source
# File lib/turborex/msrpc/rpcfinder.rb, line 1370
def load_headers(force_load = {})
  headers_path = TurboRex.root + '/resources/headers/rpc'
  include_path = TurboRex::Utils.get_all_subdir(headers_path)
  version_hl = get_version('rpcrt4.dll')
  version = ((version_hl[0] << 32) + version_hl[1])
  opts = {}
  distance = 0
  approximation = nil

  opts[:include_path] = include_path
  if force_load[:file] && force_load[:cpu]
    return force_load_file(force_load)
  end

  if @process.addrsz == 32
    opts[:cpu] = Metasm::Ia32
    pattern = '/v*_x86/rpcinternals.h'
  elsif @process.addrsz == 64
    opts[:cpu] = Metasm::X86_64
    pattern = '/v*_x64/rpcinternals.h'
  end

  Dir.glob(headers_path + pattern).each do |f|
    opts[:file] = f
    native_parser = TurboRex::CStruct::NativeParser.new(nil, opts)
    initializer = native_parser.parser.toplevel.symbol['RPC_CORE_RUNTIME_VERSION'].initializer
    initializer.each do |i|
      if i.rexpr == version
        @header = native_parser
        return true
      else
        d = (version - i.rexpr).abs
        distance = d if distance == 0

        if d < distance
          approximation = [i.rexpr, native_parser]
          distance = d
        end
      end
    end
  end

  if force_load[:approximation]
    @header = approximation[1]
    @approximation = approximation
  end

  true
end
open_process(pid) click to toggle source
# File lib/turborex/msrpc/rpcfinder.rb, line 1356
def open_process(pid)
  p = TurboRex::Windows.open_process(pid, Metasm::WinAPI::PROCESS_VM_READ)
  raise "Unable to open process #{pid}" if p.nil?

  case p.addrsz
  when 32
    @arch = 'x86'
  when 64
    @arch = 'x64'
  end

  p
end