Class: TurboRex::Windows::COM::OutOfProcFinder

Inherits:
Finder
  • Object
show all
Includes:
Utils
Defined in:
lib/turborex/windows/com/com_finder.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Utils

clsid_to_raw, create_istorage, create_istream, dll_get_class_object, #get_disptbl_count, #get_pid_by_std_objref, #get_proxy_file_info, #internal_get_proxyfile, marshal_interface, marshal_interface_to_string, #to_ptr, unmarshal_interface, unmarshal_interface_from_string

Methods included from PEFile::Scanner

data_section?, #draw_xrefs_dg, #has_path?, scan_all_sections, scan_section

Methods included from Utils::DisassemblerHelper

#_disassemble_executable_sections, #add_dasm_all_method, #addrtolabel, #backtrace, #solve_cppobj_call, #solve_guard_icall

Constructor Details

#initialize(clsid, opts = {}) ⇒ OutOfProcFinder

Returns a new instance of OutOfProcFinder.



78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# 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

Instance Attribute Details

#clientObject (readonly)

Returns the value of attribute client.



71
72
73
# File 'lib/turborex/windows/com/com_finder.rb', line 71

def client
  @client
end

#clsidObject (readonly)

Returns the value of attribute clsid.



70
71
72
# File 'lib/turborex/windows/com/com_finder.rb', line 70

def clsid
  @clsid
end

#handleObject (readonly)

Returns the value of attribute handle.



74
75
76
# File 'lib/turborex/windows/com/com_finder.rb', line 74

def handle
  @handle
end

#pidObject (readonly)

Returns the value of attribute pid.



73
74
75
# File 'lib/turborex/windows/com/com_finder.rb', line 73

def pid
  @pid
end

#processObject (readonly)

Returns the value of attribute process.



72
73
74
# File 'lib/turborex/windows/com/com_finder.rb', line 72

def process
  @process
end

Instance Method Details

#closeObject



226
227
228
229
230
231
232
# 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_addrObject



198
199
200
201
202
203
204
205
206
207
208
209
# 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) ⇒ Object



108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# 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) ⇒ Object



104
105
106
# 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) ⇒ Object



211
212
213
214
215
216
217
218
219
220
221
222
223
224
# 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, &block) ⇒ Object



163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
# 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) ⇒ Object



182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
# 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