class TurboRex::Windows::COM::OutOfProcFinder

Constants

MaxNameLen
SizeOfStruct

Attributes

client[R]
clsid[R]
handle[R]
pid[R]
process[R]

Public Class Methods

new(clsid, opts = {}) click to toggle source
# File lib/turborex/windows/com/com_finder.rb, line 78
def initialize(clsid, opts = {})
  pid = opts[:pid]
  context = opts[:context] || CLSCTX_ALL
  @clsid = clsid
  @client = Client.new(clsid)
  @iunknown = @client.create_instance(cls_context: context, interface: Interface::IUnknown.new)
  @pid = get_pid_by_std_objref(@iunknown) || pid

  process = TurboRex::Windows::Process.new(@pid)
  raise "Unable to open process #{pid}" unless process.handle
  unless process.addrsz == (sz = Metasm::WinOS::Process.new(nil, -1).addrsz)
    raise "The architecture of Ruby interpreter process(#{sz}-bit) is not same as target process"
  end

  @process = process
  @handle = @process.handle
  @ptr_len = @process.cpusz / 8
  @memory = @process.memory

  unless @process.load_symbol_table('combase.dll')
    raise "Unable to load combase.dll's symbol"
  end

  @combase_base_addr = (@process.modules.find { |m| m.path =~ Regexp.new('combase.dll', true) }).addr
end

Public Instance Methods

close() click to toggle source
# File lib/turborex/windows/com/com_finder.rb, line 226
def close
  # TODO: Unload symbols
  if @process
    @process.close_handle
    @handle = nil
  end
end
find_oid_buckets_addr() click to toggle source
# File lib/turborex/windows/com/com_finder.rb, line 198
def find_oid_buckets_addr
  buffer = INTERNAL_APIPROXY.alloc_c_ary('BYTE', 300)
  sym_info = INTERNAL_APIPROXY.alloc_c_struct('SYMBOL_INFO')
  sym_info.SizeOfStruct = sym_info.sizeof
  sym_info.MaxNameLen = 150
  buffer[0, sym_info.sizeof] = sym_info.str

  if INTERNAL_APIPROXY.symfromname(@handle, 'COIDTable::s_OIDBuckets', buffer) == 1
    sym_info.str = buffer[0, sym_info.sizeof]
    sym_info.Address
  end
end
locate_interface_methods(iid, relative = true) click to toggle source
# File lib/turborex/windows/com/com_finder.rb, line 108
def locate_interface_methods(iid, relative = true)
  # Let the object exporter create IPID entry for target interface
  tmp_interface = TurboRex::Windows::COM::Interface.define_interface(iid, {}, Interface::IUnknown)
  ppv = INTERNAL_APIPROXY.alloc_c_ptr('PVOID')
  raise "No such interface: #{iid}" unless @iunknown.QueryInterface(Utils.clsid_to_raw(iid), ppv).nil?
  tmp_interface.this = ppv[0]
  tmp_interface.marshal_to_string # For In-Proc server
  return nil unless buckets_addr = find_oid_buckets_addr

  headers = read_bucket_headers(buckets_addr)
  walk_buckets(headers) do |_cid_obj, ipid_entry|
    raw_iid = ipid_entry.str[ipid_entry.iid.stroff, ipid_entry.iid.sizeof]
    _iid = TurboRex::MSRPC::Utils.raw_to_guid_str(raw_iid)

    if _iid == iid
      methods_count = iface_vtbl_count(ipid_entry)
      return nil unless methods_count

      if ipid_entry.pv
        pvtbl = to_ptr(@memory.get_page(ipid_entry.pv, @ptr_len))
      end

      methods = []
      @memory.get_page(pvtbl, methods_count * @ptr_len).split('').each_slice(@ptr_len) { |m| methods << to_ptr(m.join) }
      # dasm = Metasm::Shellcode.decode(@memory, Metasm::X86_64.new).disassembler
      # dasm.disassemble_fast_deep(methods.last)

      first_method = methods.first
      _module = @process.modules.find { |m| first_method > m.addr && first_method < m.addr + m.size }
      if relative
        tmp_interface.Release
        return {
          module: _module.path,
          methods: methods.map.with_index { |method, i| { index: i, rva: method - _module.addr } }
        }

        # return methods.map.with_index do |method, i|
        #   _module = @process.modules.find { |m| method > m.addr && method < m.addr + m.size }
        #   _module ? {index: i, module: _module.path, rva: method - _module.addr} : nil
        # end
      else
        tmp_interface.Release
        return {
          module: _module.path,
          methods: methods.map.with_index { |method, i| { index: i, va: method } }
        }
      end
    end
  end

  # Should decrease reference count
  tmp_interface.Release
  nil
end
locate_multiple_interfaces(iids, relative = true) click to toggle source
# File lib/turborex/windows/com/com_finder.rb, line 104
def locate_multiple_interfaces(iids, relative = true)
  iids.map {|iid| locate_interface_methods(iid, relative)}.compact
end
read_bucket_headers(address) click to toggle source
# File lib/turborex/windows/com/com_finder.rb, line 211
def read_bucket_headers(address)
  headers = []

  TurboRex::Windows::Constants::MAX_BUCKETS_NUM.times do |i|
    header = INTERNAL_APIPROXY.alloc_c_struct('SHashChain')
    header_addr = address + i * header.sizeof
    header.str = @memory.get_page(header_addr, header.sizeof)
    next if header.pNext == header_addr

    headers << [header, header_addr]
  end

  headers
end
walk_buckets(buckets) { |cid_obj, ipid_entry| ... } click to toggle source
# File lib/turborex/windows/com/com_finder.rb, line 163
def walk_buckets(buckets, &block)
  buckets.each do |bucket, base_addr|
    pNext = bucket.pNext
    until pNext == base_addr
      obj_addr = pNext - bucket.sizeof - @ptr_len
      cid_obj = INTERNAL_APIPROXY.alloc_c_struct('CIDObject')
      cid_obj.str = @memory.get_page(obj_addr, cid_obj.sizeof)
      pNext = cid_obj._oidChain.pNext

      std_ident = INTERNAL_APIPROXY.alloc_c_struct('CStdIdentity')
      std_ident.str = @memory.get_page(cid_obj._pStdID, std_ident.sizeof)
      walk_ipid_entries(std_ident._pFirstIPID) do |ipid_entry|
        yield(cid_obj, ipid_entry) if block_given?
      end
    end
  end
end
walk_ipid_entries(pfirst_entry) { |ipid_entry| ... } click to toggle source
# File lib/turborex/windows/com/com_finder.rb, line 182
def walk_ipid_entries(pfirst_entry)
  ipid_entries = []
  pNext = pfirst_entry

  # TODO: stdid.cIPIDs
  until pNext.nil?
    ipid_entry = INTERNAL_APIPROXY.alloc_c_struct('tagIPIDEntry')
    ipid_entry.str = @memory.get_page(pNext, ipid_entry.sizeof)
    yield(ipid_entry) if block_given?
    ipid_entries << ipid_entry
    pNext = ipid_entry.pNextIPID
  end

  ipid_entries
end

Private Instance Methods

iface_vtbl_count(ipid_entry) click to toggle source
# File lib/turborex/windows/com/com_finder.rb, line 236
def iface_vtbl_count(ipid_entry)
  if ipid_entry.pStub # Standard interface stub
    pstub_vtbl = @memory.get_page(ipid_entry.pStub, @ptr_len)
    if_stub_vtbl = INTERNAL_APIPROXY.alloc_c_struct('CInterfaceStubVtbl')
    if_stub_vtbl.str = @memory.get_page(to_ptr(pstub_vtbl) - if_stub_vtbl.Vtbl.stroff, if_stub_vtbl.sizeof)
    if_stub_vtbl.header.DispatchTableCount
  else
    # Get ProxyFileInfo->pStubVtblList->header->DispatchTableCount
    raw_iid = ipid_entry.str[ipid_entry.iid.stroff, ipid_entry.iid.sizeof]
    iid = TurboRex::MSRPC::Utils.raw_to_guid_str(raw_iid)
    proxy_file_info = get_proxy_file_info(iid)
    raise unless proxy_file_info

    # pcif_stub_vtbl_list = to_ptr(@memory.get_page(proxy_file_info.pStubVtblList, @ptr_len))
    # if_stub_vtbl = TurboRex::Windows::COM::INTERNAL_APIPROXY.alloc_c_struct('CInterfaceStubVtbl')
    # if_stub_vtbl.str = @memory.get_page(pcif_stub_vtbl_list, if_stub_vtbl.sizeof)
    # return if_stub_vtbl.header.DispatchTableCount
    get_disptbl_count(proxy_file_info)
  end
end