turborex 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +674 -0
  3. data/README.md +38 -0
  4. data/README.rdoc +19 -0
  5. data/examples/alpc_client.rb +15 -0
  6. data/examples/alpc_server.rb +14 -0
  7. data/examples/com_client.rb +19 -0
  8. data/examples/com_finder.rb +39 -0
  9. data/examples/create_instance.rb +15 -0
  10. data/examples/cstruct.rb +19 -0
  11. data/examples/find_com_client_calls.rb +16 -0
  12. data/examples/find_rpc_security_callback.rb +12 -0
  13. data/examples/rpc_finder.rb +117 -0
  14. data/examples/scan_exports.rb +5 -0
  15. data/examples/scan_imports.rb +5 -0
  16. data/examples/tinysdk.rb +17 -0
  17. data/lib/turborex.rb +21 -0
  18. data/lib/turborex/cstruct.rb +565 -0
  19. data/lib/turborex/cstruct/struct_helper.rb +7 -0
  20. data/lib/turborex/exception.rb +65 -0
  21. data/lib/turborex/fuzzer.rb +204 -0
  22. data/lib/turborex/fuzzer/containers.rb +115 -0
  23. data/lib/turborex/fuzzer/coverage.rb +67 -0
  24. data/lib/turborex/fuzzer/mutators.rb +25 -0
  25. data/lib/turborex/fuzzer/seed.rb +30 -0
  26. data/lib/turborex/monkey.rb +11 -0
  27. data/lib/turborex/msrpc.rb +14 -0
  28. data/lib/turborex/msrpc/decompiler.rb +244 -0
  29. data/lib/turborex/msrpc/midl.rb +747 -0
  30. data/lib/turborex/msrpc/ndrtype.rb +167 -0
  31. data/lib/turborex/msrpc/rpcbase.rb +777 -0
  32. data/lib/turborex/msrpc/rpcfinder.rb +1426 -0
  33. data/lib/turborex/msrpc/utils.rb +70 -0
  34. data/lib/turborex/pefile.rb +8 -0
  35. data/lib/turborex/pefile/pe.rb +61 -0
  36. data/lib/turborex/pefile/scanner.rb +82 -0
  37. data/lib/turborex/utils.rb +321 -0
  38. data/lib/turborex/windows.rb +402 -0
  39. data/lib/turborex/windows/alpc.rb +844 -0
  40. data/lib/turborex/windows/com.rb +266 -0
  41. data/lib/turborex/windows/com/client.rb +84 -0
  42. data/lib/turborex/windows/com/com_finder.rb +330 -0
  43. data/lib/turborex/windows/com/com_registry.rb +100 -0
  44. data/lib/turborex/windows/com/interface.rb +522 -0
  45. data/lib/turborex/windows/com/utils.rb +210 -0
  46. data/lib/turborex/windows/constants.rb +82 -0
  47. data/lib/turborex/windows/process.rb +56 -0
  48. data/lib/turborex/windows/security.rb +12 -0
  49. data/lib/turborex/windows/security/ace.rb +76 -0
  50. data/lib/turborex/windows/security/acl.rb +25 -0
  51. data/lib/turborex/windows/security/security_descriptor.rb +118 -0
  52. data/lib/turborex/windows/tinysdk.rb +89 -0
  53. data/lib/turborex/windows/utils.rb +138 -0
  54. data/resources/headers/alpc/ntdef.h +72 -0
  55. data/resources/headers/alpc/ntlpcapi.h +1014 -0
  56. data/resources/headers/rpc/common.h +162 -0
  57. data/resources/headers/rpc/guiddef.h +191 -0
  58. data/resources/headers/rpc/internal_ndrtypes.h +262 -0
  59. data/resources/headers/rpc/rpc.h +10 -0
  60. data/resources/headers/rpc/rpcdce.h +266 -0
  61. data/resources/headers/rpc/rpcdcep.h +187 -0
  62. data/resources/headers/rpc/rpcndr.h +39 -0
  63. data/resources/headers/rpc/v4_x64/rpcinternals.h +154 -0
  64. data/resources/headers/rpc/wintype.h +517 -0
  65. data/resources/headers/tinysdk/tinysdk.h +5 -0
  66. data/resources/headers/tinysdk/tinysdk/comdef.h +645 -0
  67. data/resources/headers/tinysdk/tinysdk/dbghelp.h +118 -0
  68. data/resources/headers/tinysdk/tinysdk/guiddef.h +194 -0
  69. data/resources/headers/tinysdk/tinysdk/memoryapi.h +12 -0
  70. data/resources/headers/tinysdk/tinysdk/poppack.h +12 -0
  71. data/resources/headers/tinysdk/tinysdk/pshpack4.h +13 -0
  72. data/resources/headers/tinysdk/tinysdk/winnt.h +1059 -0
  73. data/resources/headers/tinysdk/tinysdk/wintype.h +326 -0
  74. metadata +290 -0
@@ -0,0 +1,1426 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TurboRex
4
+ module MSRPC
5
+ module RPCFinder
6
+
7
+ # This module provides a set of helper functions
8
+ # that backtracking RPC runtime routines parameters to determine information
9
+ # such as address of security callback function and rpc flags.
10
+ module StaticRPCBacktracer
11
+ include TurboRex::Utils::DisassemblerHelper
12
+
13
+ # RpcServerRegisterIf2/RpcServerRegisterIf3/RpcServerRegisterIfEx
14
+ def bt_security_callback(dasm, trace_flags=false, uuid=nil)
15
+ res = []
16
+
17
+ case dasm.cpu.size
18
+ when 64
19
+ expr_if_handle = 'rcx'
20
+ expr_sec_cb = '[rsp+30h]'
21
+ expr_flag = 'r9' if trace_flags
22
+ mk_struct_proc = Proc.new {
23
+ TurboRex::MSRPC::RPCBase::RPC_SERVER_INTERFACE64.make(pack: 8, align: true)
24
+ }
25
+ when 32
26
+ expr_if_handle = '[esp]'
27
+ expr_sec_cb = '[esp+14h]'
28
+ expr_flag = '[esp+Ch]' if trace_flags
29
+ mk_struct_proc = Proc.new {
30
+ TurboRex::MSRPC::RPCBase::RPC_SERVER_INTERFACE32.make(pack: 4, align: true)
31
+ }
32
+ end
33
+
34
+ fn = ['RpcServerRegisterIf2', 'RpcServerRegisterIf3', 'RpcServerRegisterIfEx']
35
+ fn.each do |f|
36
+ callers = dasm.call_sites(Metasm::Expression[f])
37
+ callers.each do |addr|
38
+ server_if = mk_struct_proc.call
39
+ if_handle = backtrace(addr, dasm, expr_if_handle).first.first
40
+
41
+ next unless raw = dasm.read_raw_data(if_handle, server_if.slength)
42
+ server_if.from_s(raw)
43
+ interface_id = TurboRex::MSRPC::Utils.raw_to_guid_str(server_if['interfaceId'].to_s)
44
+
45
+ found, _ = backtrace(addr, dasm, expr_sec_cb)
46
+ found_flags, _ = backtrace(addr, dasm, expr_flag) if trace_flags
47
+ if dasm.get_section_at(found.first)
48
+ r = {interface_id: interface_id, callback: found.first}
49
+ r[:flags] = found_flags.first.to_i if trace_flags
50
+ res << r
51
+ end
52
+ end
53
+ end
54
+
55
+ res.uniq!
56
+
57
+ case uuid
58
+ when String
59
+ return res.select {|r| r[:interface_id] == uuid}
60
+ when Array
61
+ return res.select { |r| uuid.include?(r[:interface_id])}
62
+ else
63
+ return res
64
+ end
65
+ end
66
+ end
67
+
68
+ class Collection
69
+ attr_reader :server_interfaces
70
+ attr_reader :client_interfaces
71
+
72
+ def initialize
73
+ @server_interfaces = []
74
+ @client_interfaces = []
75
+ end
76
+
77
+ def push_server(i)
78
+ @server_interfaces << i
79
+ end
80
+
81
+ def push_client(i)
82
+ @client_interfaces << i
83
+ end
84
+
85
+ def draw_xrefs
86
+ @server_interfaces.each do |si|
87
+ ci = find_client_by_server(si)
88
+ next if ci.empty?
89
+
90
+ si.xrefs_to << ci
91
+ ci.xrefs_from << si
92
+
93
+ si.uniq!
94
+ ci.uniq!
95
+ end
96
+
97
+ true
98
+ end
99
+
100
+ def find_server_by_client(client)
101
+ @server_interfaces.select { |i| i.interface_id == client.interface_id }
102
+ end
103
+
104
+ def find_client_by_server(server)
105
+ @client_interfaces.select { |i| i.interface_id == server.interface_id }
106
+ end
107
+
108
+ def find_by_interface_id(id, filter = nil)
109
+ case filter
110
+ when nil
111
+ @server_interfaces.select { |i| i.interface_id == id } + \
112
+ @client_interfaces.select { |i| i.interface_id == id }
113
+ when :server
114
+ @server_interfaces.select { |i| i.interface_id == id }
115
+ when :client
116
+ @client_interfaces.select { |i| i.interface_id == id }
117
+ end
118
+ end
119
+
120
+ def find_by_routine(routine)
121
+ @server_interfaces.select { |i| i.routines.include?(routine) } + \
122
+ @client_interfaces.select { |i| i.routines.include?(routine) }
123
+ end
124
+
125
+ def find_by_midl_switches(*switches)
126
+ @server_interfaces.select { |i| i.midl_switches.has_one_of_switches?(switches) } + \
127
+ @client_interfaces.select { |i| i.midl_switches.has_one_of_switches?(switches) }
128
+ end
129
+ end
130
+
131
+ class ImageFinder
132
+ include TurboRex::Utils::DisassemblerHelper
133
+
134
+ class AutoFindConf
135
+ def initialize
136
+ @options = {
137
+ include_client: true,
138
+ find_client_routines: false
139
+ }
140
+ end
141
+
142
+ def build
143
+ @options
144
+ end
145
+
146
+ def exclude_client
147
+ @options[:include_client] = false
148
+ end
149
+
150
+ def only_client
151
+ @options[:only_client] = true
152
+ end
153
+
154
+ def find_client_routines
155
+ @options[:find_client_routines] = true if @options[:include_client]
156
+ end
157
+
158
+ alias only_server exclude_client
159
+ end
160
+
161
+ include Rex::PeParsey
162
+ include TurboRex::MSRPC::RPCBase
163
+ include TurboRex::PEFile
164
+ include TurboRex::PEFile::Scanner
165
+ include TurboRex::MSRPC::RPCFinder::StaticRPCBacktracer
166
+
167
+ class InterfaceModel
168
+ attr_accessor :dasm
169
+ attr_reader :struct
170
+ attr_accessor :protocol
171
+ attr_reader :interface_id
172
+ attr_reader :uuid
173
+ attr_reader :interface_ver
174
+ attr_reader :midl_switches
175
+ attr_reader :finder
176
+
177
+ attr_accessor :decompiler
178
+ attr_accessor :routines
179
+ attr_accessor :xrefs_to
180
+ attr_accessor :xrefs_from
181
+ attr_accessor :endpoints
182
+
183
+ attr_accessor :pproc_fs
184
+ attr_accessor :offset_table
185
+ attr_accessor :ptype_fs
186
+
187
+
188
+ def initialize(struct, finder, dasm = nil)
189
+ @struct = struct # RPC_SERVER_INTERFACE_Klass
190
+ @protocol = get_protocol
191
+ @interface_id = @uuid = TurboRex::MSRPC::Utils.raw_to_guid_str(struct.InterfaceId_Value)
192
+ @interface_ver = get_interface_ver
193
+ @transfer_syntax = TurboRex::MSRPC::Utils.raw_to_guid_str(struct.TransferSyntax_Value)
194
+ @transfer_syntax_ver = get_trans_syntax_ver
195
+ @midl_switches = RPCBase::MIDL_SWITCHES.new
196
+ @dasm = dasm || finder.dasm
197
+ @endpoints = []
198
+ @finder = finder
199
+ @routines = []
200
+ @xrefs_to = []
201
+ @xrefs_from = []
202
+ end
203
+
204
+ def server?
205
+ !@struct.dispatch_table_nullptr?
206
+ end
207
+
208
+ def client?
209
+ !server?
210
+ end
211
+
212
+ def set_fs
213
+ return false if client?
214
+ analysis_midl_switches
215
+
216
+ pe = finder.pe
217
+ @poffset_table = pe.vma_to_file_offset(self.InterpreterInfo.FormatStringOffset_Value)
218
+ @pproc_fs = pe.vma_to_file_offset(self.InterpreterInfo.ProcFormatString_Value)
219
+
220
+ if @midl_switches.has_switch?('Oi')
221
+ @ptype_fs = pe.vma_to_file_offset(self.InterpreterInfo.pStubDesc.pFormatTypes_Value)
222
+ mode = :Oi
223
+ elsif @midl_switches.has_switch?('all') && @midl_switches.arch_64?
224
+ return false # TODO: Implement
225
+ elsif @midl_switches.has_switch?('Oicf')
226
+ @ptype_fs = pe.vma_to_file_offset(self.InterpreterInfo.pStubDesc.pFormatTypes_Value)
227
+ end
228
+
229
+ true
230
+ end
231
+
232
+ def decompile
233
+ return false unless set_fs
234
+
235
+ @finder.ndr_decompiler.decompile(self)
236
+ end
237
+
238
+ def func_in_server_routines?(addr)
239
+ func_in_server_routines(addr).empty? ? false : true
240
+ end
241
+
242
+ def func_in_server_routines(addr)
243
+ return false unless server?
244
+
245
+ @finder.disassemble_executable_sections
246
+ res = []
247
+
248
+ routines = @routines
249
+ routines.each do |r|
250
+ res << r if @finder.has_path?(dasm, r.addr, addr)
251
+ end
252
+
253
+ res
254
+ end
255
+
256
+ def analysis_midl_switches
257
+ # Don't analyze the client interfaces. It is performed when parsing the client dispatch functions.
258
+ if server?
259
+ dispatch_funcs = @struct.DispatchTable.DispatchTable
260
+
261
+ # TODO: Refine the parse methods with format string
262
+ # Oi/Oicf/ndr64
263
+ unless dispatch_funcs.empty?
264
+ begin
265
+ @dasm.disassemble dispatch_funcs[0]
266
+ rescue StandardError
267
+ return false
268
+ end
269
+
270
+ label = @dasm.get_label_at dispatch_funcs[0]
271
+ case label
272
+ when /NdrServerCall2/i
273
+ @midl_switches << %w[Oif Oicf]
274
+ when /NdrServerCall/i
275
+ @midl_switches << %w[Oi Oic]
276
+ when /NdrServerCallNdr64/i
277
+ if @struct.ndr64?
278
+ @midl_switches << 'ndr64'
279
+ @midl_switches << %w[win64 amd64 ia64]
280
+ end
281
+ end
282
+ end
283
+
284
+ if (@struct.Flags & 0x6000000) == 0x6000000 # test /Oicf /protocol all /win64(amd64/ia64)
285
+ interpreter_info = @struct.InterpreterInfo
286
+ unless interpreter_info == 0
287
+ if interpreter_info.nCount >= 2 && interpreter_info.ProcString != 0
288
+ begin
289
+ interpreter_info.pSyntaxInfo.each do |syntax_info|
290
+ disp_funcs = syntax_info.DispatchTable.DispatchFunctions
291
+ @dasm.disassemble disp_funcs.first
292
+ label = @dasm.get_label_at disp_funcs.first
293
+
294
+ next unless label =~ /NdrServerCallAll/
295
+
296
+ @midl_switches << 'all'
297
+ @midl_switches << %w[win64 amd64 ia64]
298
+ @protocol = :all
299
+ end
300
+ rescue StandardError
301
+ # TODO: exception handle
302
+ end
303
+ end
304
+ end
305
+ end
306
+
307
+ # Os is contradictory with '/protocol ndr64/all'
308
+ unless @midl_switches.has_one_of_switches?(%w[Oi Oic Oif Oicf ndr64 all])
309
+ if @struct.interpreter_info_nullptr? && @struct.Flags == 0
310
+ @midl_switches << 'Os'
311
+ end
312
+ end
313
+ end
314
+ end
315
+
316
+ def method_missing(m, *args)
317
+ if m.to_s.start_with?('raw_')
318
+ camelcase_name = camelcase(m.to_s.sub('raw_', ''))
319
+ if @struct.respond_to? camelcase_name
320
+ @struct.public_send camelcase_name, *args
321
+ end
322
+ elsif @struct.respond_to? m
323
+ @struct.public_send m, *args
324
+ else
325
+ begin
326
+ @struct.public_send m, *args
327
+ rescue StandardError
328
+ super(m, *args)
329
+ end
330
+ end
331
+ end
332
+
333
+ private
334
+
335
+ def get_protocol
336
+ @protocol = if @struct.ndr64?
337
+ :ndr64
338
+ elsif @struct.dce?
339
+ :dce
340
+ else
341
+ :unknown
342
+ end
343
+ end
344
+
345
+ def get_interface_ver
346
+ major = @struct.InterfaceId.SyntaxVersion.MajorVersion
347
+ minor = @struct.InterfaceId.SyntaxVersion.MinorVersion
348
+ get_syntax_version(major, minor)
349
+ end
350
+
351
+ def get_trans_syntax_ver
352
+ major = @struct.TransferSyntax.SyntaxVersion.MajorVersion
353
+ minor = @struct.TransferSyntax.SyntaxVersion.MinorVersion
354
+ get_syntax_version(major, minor)
355
+ end
356
+
357
+ def get_syntax_version(_major, _minor)
358
+ major = _major.to_s.unpack('S')[0]
359
+ minor = _minor.to_s.unpack('S')[0]
360
+ "#{major}.#{minor}"
361
+ end
362
+
363
+ def camelcase(str)
364
+ str.split('_').collect(&:capitalize).join
365
+ end
366
+ end
367
+
368
+ attr_reader :pe
369
+ attr_reader :dasm
370
+ attr_reader :ndr_decompiler
371
+ attr_reader :server_interfaces
372
+ attr_reader :midl_server_infos
373
+ attr_reader :midl_syntax_infos
374
+ attr_reader :midl_stubless_proxy_infos
375
+ attr_reader :dispatch_funcs
376
+ attr_reader :server_routines
377
+
378
+ attr_reader :client_interfaces
379
+ attr_reader :client_routines
380
+
381
+ def initialize(pe, _opts = {})
382
+ open_file(pe)
383
+
384
+ @server_interfaces = []
385
+ @midl_server_infos = []
386
+ @midl_stub_descs = []
387
+ @midl_syntax_infos = []
388
+ @midl_stubless_proxy_infos = []
389
+ @server_routines = []
390
+ @dispatch_funcs = []
391
+ @client_interfaces = []
392
+ @dasm = new_dasm
393
+ @collection_proxy = _opts[:collection_proxy]
394
+
395
+ arch = @pe.ptr_32? ? 'x86' : 'x64'
396
+ @ndr_decompiler = TurboRex::MSRPC::Decompiler.new(arch: arch)
397
+ end
398
+
399
+ def self.glob_all(root)
400
+ Dir.glob(root + '/**/*')
401
+ end
402
+
403
+ def self.glob(path, suffixes = nil)
404
+ pattern = []
405
+ suffixes&.each { |suffix| pattern << File.join(path, '*') + suffix }
406
+
407
+ if block_given?
408
+ Dir.glob(pattern) do |filename|
409
+ yield(filename)
410
+ end
411
+ else
412
+ Dir.glob(pattern)
413
+ end
414
+ end
415
+
416
+ def open_file(filename)
417
+ begin
418
+ @pe = TurboRex::PEFile::PE.new_from_file(filename)
419
+ @pe.image_path = Pathname.new(filename)
420
+ rescue FileHeaderError
421
+ return false
422
+ end
423
+
424
+ pe
425
+ end
426
+
427
+ def close
428
+ unless @pe.nil?
429
+ @pe.close
430
+ @pe = nil
431
+ end
432
+
433
+ true
434
+ end
435
+
436
+ def auto_find(&block)
437
+ default = TurboRex::MSRPC::RPCFinder::ImageFinder::AutoFindConf.new
438
+ config = if block_given?
439
+ Docile.dsl_eval(default, &block).build
440
+ else
441
+ default.build
442
+ end
443
+
444
+ internal_auto_find(config)
445
+ end
446
+
447
+ def make_rpc_server_interface(pe)
448
+ if pe.ptr_32?
449
+ TurboRex::MSRPC::RPCBase::RPC_SERVER_INTERFACE32.make(pack: 4, align: true)
450
+ else
451
+ TurboRex::MSRPC::RPCBase::RPC_SERVER_INTERFACE64.make(pack: 8, align: true)
452
+ end
453
+ end
454
+
455
+ def make_midl_server_info(pe)
456
+ if pe.ptr_32?
457
+ TurboRex::MSRPC::RPCBase::MIDL_SERVER_INFO32.make
458
+ else
459
+ TurboRex::MSRPC::RPCBase::MIDL_SERVER_INFO64.make
460
+ end
461
+ end
462
+
463
+ def make_midl_stubless_proxy_info(pe)
464
+ if pe.ptr_32?
465
+ TurboRex::MSRPC::RPCBase::MIDL_STUBLESS_PROXY_INFO32.make(pack: 4, align: true)
466
+ else
467
+ TurboRex::MSRPC::RPCBase::MIDL_STUBLESS_PROXY_INFO64.make(pack: 8, align: true)
468
+ end
469
+ end
470
+
471
+ def make_midl_syntax_info(pe)
472
+ if pe.ptr_32?
473
+ TurboRex::MSRPC::RPCBase::MIDL_SYNTAX_INFO32.make(pack: 4, align: true)
474
+ else
475
+ TurboRex::MSRPC::RPCBase::MIDL_SYNTAX_INFO64.make(pack: 8, align: true)
476
+ end
477
+ end
478
+
479
+ def make_rpc_dispatch_table_t(pe)
480
+ if pe.ptr_32?
481
+ TurboRex::MSRPC::RPCBase::RPC_DISPATCH_TABLE_T.make(pack: 4, align: true)
482
+ else
483
+ TurboRex::MSRPC::RPCBase::RPC_DISPATCH_TABLE_T64.make(pack: 8, align: true)
484
+ end
485
+ end
486
+
487
+ def make_rpc_protseq_endpoint(pe)
488
+ if pe.ptr_32?
489
+ TurboRex::MSRPC::RPCBase::RPC_PROTSEQ_ENDPOINT32.make(pack: 4, align: true)
490
+ else
491
+ TurboRex::MSRPC::RPCBase::RPC_PROTSEQ_ENDPOINT64.make(pack: 8, align: true)
492
+ end
493
+ end
494
+
495
+ def make_midl_stub_desc(pe)
496
+ if pe.ptr_32?
497
+ TurboRex::MSRPC::RPCBase::MIDL_STUB_DESC.make(pack: 4, align: true)
498
+ else
499
+ TurboRex::MSRPC::RPCBase::MIDL_STUB_DESC64.make(pack: 8, align: true)
500
+ end
501
+ end
502
+
503
+ def get_midl_server_info(rpc_server_if)
504
+ reconstruct_midl_server_info(@pe, rpc_server_if)
505
+ end
506
+
507
+ def get_midl_stub_desc(midl_server_info)
508
+ reconstruct_midl_stub_desc(@pe, midl_server_info)
509
+ end
510
+
511
+ def get_stubless_pinfo_from_client_if(rpc_client_if)
512
+ reconstruct_stubless_pinfo(@pe, rpc_client_if)
513
+ end
514
+
515
+ def get_midl_syntax_info(midl_server_info)
516
+ reconstruct_midl_syntax_info(@pe, midl_server_info)
517
+ end
518
+
519
+ def get_dispatch_table(rpc_server_if)
520
+ reconstruct_disptbl_for_server_interface(@pe, rpc_server_if)
521
+ end
522
+
523
+ def get_offset_table(rpc_server_if)
524
+ reconstruct_offset_table(@pe, rpc_server_if)
525
+ end
526
+
527
+ def get_offset_table2(disptbl, midl_server_info)
528
+ reconstruct_offset_table2(@pe, disptbl, midl_server_info)
529
+ end
530
+
531
+ def get_endpoint_info(rpc_server_if)
532
+ reconstruct_endpoint_info(@pe, rpc_server_if)
533
+ end
534
+
535
+ def get_disp_functions(rpc_dispatch_table)
536
+ reconstruct_disp_functions(@pe, rpc_dispatch_table)
537
+ end
538
+
539
+ def get_rpc_server_routines(midl_server_info, count)
540
+ reconstruct_disptbl_for_midl_server_info(@pe, midl_server_info, count)
541
+ end
542
+
543
+ def get_routines_from_server_interface(rpc_server_interface)
544
+ if has_interpreter_info?(rpc_server_interface)
545
+ disptbl = get_dispatch_table(rpc_server_interface)
546
+ midl_server_info = get_midl_server_info(rpc_server_interface)
547
+ count = disptbl['dispatchTableCount'].value
548
+
549
+ get_rpc_server_routines(midl_server_info, count) if count > 0
550
+ end
551
+ end
552
+
553
+ def find_rpc_server_interface(opts = {})
554
+ rpc_server_interface = make_rpc_server_interface(@pe)
555
+ regexp = Regexp.new [rpc_server_interface.slength].pack('V')
556
+ res = []
557
+ opts[:only_data_section] ||= true
558
+
559
+ if opts[:only_data_section]
560
+ @pe.data_sections.each do |s|
561
+ TurboRex::PEFile::Scanner.scan_section(s, regexp).each do |r|
562
+ rpc_server_interface = make_rpc_server_interface(@pe)
563
+ next unless reconstruct_struct_from_pe(@pe, r[0], rpc_server_interface) > 0
564
+
565
+ if validate_rpc_server_interface(@pe, rpc_server_interface)
566
+ yield(rpc_server_interface, r[0]) if block_given?
567
+ res << rpc_server_interface
568
+ end
569
+ end
570
+ end
571
+ else
572
+ addr_info = TurboRex::PEFile::Scanner.scan_all_sections(@pe, regexp)
573
+ unless addr_info.empty?
574
+ addr_info.each do |addr|
575
+ rpc_server_interface = make_rpc_server_interface(@pe)
576
+ if reconstruct_struct_from_pe(@pe, addr[0], rpc_server_interface) > 0
577
+ yield(rpc_server_interface, addr[0]) if block_given?
578
+ res << rpc_server_interface
579
+ end
580
+ end
581
+ end
582
+ end
583
+
584
+ res
585
+ end
586
+
587
+ def find_client_disp_functions(va, dasm, expr, *funcs)
588
+ disassemble_executable_sections(dasm)
589
+ # addrtolabel(dasm)
590
+ dispatch_funcs = []
591
+
592
+ if expr.nil?
593
+ case dasm.cpu.size
594
+ when 64
595
+ expr = 'rcx'
596
+ when 32
597
+ expr = '[esp]'
598
+ end
599
+ end
600
+
601
+ funcs.each do |func|
602
+ callers = dasm.call_sites(Metasm::Expression[func])
603
+ callers.each do |addr|
604
+ found, log = backtrace(addr, dasm, expr)
605
+ next unless found.include?(va)
606
+
607
+ # Finding proc number
608
+ proc_num = nil
609
+ case func
610
+ when 'NdrClientCall' # Oi, conflict with 64-bit platform
611
+ expr_procnum = '[esp+4]'
612
+ switch = :Oi
613
+ when 'NdrClientCall2' # Oicf
614
+ expr_procnum = dasm.cpu.size == 64 ? 'rdx' : '[esp+4]'
615
+ switch = :Oicf
616
+ when 'NdrClientCall3'
617
+ expr_procnum = 'rdx'
618
+ when 'NdrClientCall4' # ?
619
+ expr_procnum = dasm.cpu.size == 64 ? 'rdx' : '[esp+4]'
620
+ end
621
+
622
+ _found, _log = backtrace(addr, dasm, expr_procnum)
623
+
624
+ unless _found.empty?
625
+ if func == 'NdrClientCall3'
626
+ proc_num = _found.first
627
+ else
628
+ _dasm = dasm.dup
629
+ _dasm.c_parser = @ndr_decompiler.parser
630
+ fs_header, header_len = @ndr_decompiler.parse_proc_fs_header_dasm(_dasm, _found[0])
631
+ proc_num = fs_header.oi_header.common.ProcNum
632
+ end
633
+ end
634
+
635
+ yield(addr, dasm) if block_given?
636
+ func_start = dasm.find_function_start(addr)
637
+ dispatch_funcs << {
638
+ dispatch_func: func_start,
639
+ backtrace: [func, va, log],
640
+ proc_num: proc_num
641
+ }
642
+ end
643
+ end
644
+
645
+ dispatch_funcs
646
+ end
647
+
648
+ def find_client_routines(client_if, client_if_addr, dasm = nil)
649
+ dasm = @dasm || (@dasm = new_dasm)
650
+ disp_funcs = []
651
+
652
+ if has_proxy_info?(client_if) # stubless proxy, no dispatch table and thunk table
653
+ if proxy_info = get_stubless_pinfo_from_client_if(client_if)
654
+ pi_obj = TurboRex::MSRPC::RPCBase::MIDL_STUBLESS_PROXY_INFO_Klass.new(proxy_info)
655
+ pinterpreter_info = client_if.InterpreterInfo_Value
656
+ @midl_stubless_proxy_infos << pi_obj
657
+ client_if.midl_switches << %w[all win64 amd64 ia64]
658
+ client_if.link_to pi_obj
659
+ disp_funcs = find_client_disp_functions(pinterpreter_info, dasm, nil, 'NdrClientCall3')
660
+ end
661
+ else
662
+ xrefs = scan_xrefs_immediate(client_if_addr, dasm)
663
+ xrefs.each do |xref|
664
+ midl_stub_desc = make_midl_stub_desc(@pe)
665
+ reconstruct_struct_from_pe(@pe, @pe.vma_to_rva(xref), midl_stub_desc)
666
+ next unless validate_midl_stub_desc(@pe, midl_stub_desc)
667
+
668
+ stub_desc_obj = MIDL_STUB_DESC_Klass.new(midl_stub_desc)
669
+ stub_desc_obj.link_to client_if
670
+ @midl_stub_descs << stub_desc_obj
671
+
672
+ disp_funcs = find_client_disp_functions(xref, dasm, nil, 'NdrClientCall', 'NdrClientCall2', 'NdrClientCall4')
673
+
674
+ # TODO: detect switches when using NdrClientCall4
675
+ next unless b = disp_funcs[0]&.fetch(:backtrace) { }
676
+
677
+ client_if.midl_switches << %w[Oi Oic] if b[0] == 'NdrClientCall'
678
+ if b[0] == 'NdrClientCall2'
679
+ client_if.midl_switches << %w[Oif Oicf]
680
+ end
681
+ end
682
+ end
683
+
684
+ disp_funcs.map do |m|
685
+ r = TurboRex::MSRPC::RPCBase::CLIENT_ROUTINE_Klass.new(m[:dispatch_func])
686
+ r.proc_num = m[:proc_num]
687
+ r
688
+ end.uniq(&:addr)
689
+ end
690
+
691
+ def validate_transfer_syntax(transfer_syntax)
692
+ transfer_syntax.to_s == DCE_TransferSyntax.to_s || transfer_syntax.to_s == NDR64_TransferSyntax.to_s
693
+ end
694
+
695
+ def validate_rpc_server_interface(pe, rpc_server_interface)
696
+ length = rpc_server_interface.slength
697
+ return false unless rpc_server_interface['length'].value == length
698
+ unless validate_transfer_syntax(rpc_server_interface['transferSyntax'])
699
+ return false
700
+ end
701
+
702
+ itpInfo = rpc_server_interface['interpreterInfo'].value # vma
703
+ if itpInfo == 0
704
+ # TODO: Inline stub check
705
+ else
706
+ section = pe._find_section_by_rva(pe.vma_to_rva(itpInfo))
707
+ return false if section.nil?
708
+ return false unless TurboRex::PEFile::Scanner.data_section?(section)
709
+ end
710
+
711
+ dispTable = rpc_server_interface['dispatchTable'].value
712
+ unless dispTable == 0
713
+ section = pe._find_section_by_rva(pe.vma_to_rva(dispTable))
714
+ return false if section.nil?
715
+ return false unless TurboRex::PEFile::Scanner.data_section?(section)
716
+ end
717
+
718
+ true
719
+ end
720
+
721
+ def validate_server_interface_from_pe(pe, address)
722
+ make_rpc_server_interface(pe)
723
+ reconstruct_struct_from_pe(pe, address, rpc_server_interface)
724
+ validate_rpc_server_interface(pe, rpc_server_interface)
725
+ end
726
+
727
+ def validate_midl_stub_desc(pe, struct)
728
+ pfnAllocate = struct['pfnAllocate'].value
729
+ pfnFree = struct['pfnFree'].value
730
+ phandle = struct['implicit_handle_info'].value
731
+ bounds_flag = struct['fCheckBounds'].value
732
+
733
+ # TODO: more check(version)
734
+ pointer_check = pe.valid_vma?(pfnAllocate) && pe.valid_vma?(pfnFree)
735
+ # && pe.valid_vma?(phandle)
736
+ # Rex library valid_vma? method make a mistake here.
737
+ bounds_flag_check = (bounds_flag == 1 || bounds_flag == 0)
738
+
739
+ pointer_check && bounds_flag
740
+ end
741
+
742
+ # The "strict_check" option will use the algorithm of NdrClientCall3
743
+ def validate_stubless_proxy_info(pe, stubless_proxy_info, strict_check = true)
744
+ pTransferSyntax = stubless_proxy_info['pTransferSyntax'].value
745
+ pSyntaxInfo = stubless_proxy_info['pSyntaxInfo'].value
746
+ nCount = stubless_proxy_info['nCount'].value
747
+ dce_transfer = DCE_TransferSyntax.to_s
748
+ ndr64_transfer = NDR64_TransferSyntax.to_s
749
+ len = make_midl_syntax_info(pe).slength
750
+
751
+ return false if pTransferSyntax == 0
752
+
753
+ transfer_syntax = pe._isource.read(pe.vma_to_file_offset(pTransferSyntax), DCE_TransferSyntax.slength)
754
+ unless transfer_syntax == dce_transfer || transfer_syntax == ndr64_transfer
755
+ return false
756
+ end
757
+
758
+ if strict_check
759
+ nCount.times do |i|
760
+ syntaxinfo_trans = pe._isource.read(pe.vma_to_file_offset(pSyntaxInfo + i * len), DCE_TransferSyntax.slength)
761
+ break if syntaxinfo_trans == transfer_syntax
762
+ return false if i + 1 == nCount
763
+ end
764
+ end
765
+
766
+ true
767
+ end
768
+
769
+ def reconstruct_struct_from_pe(pe, rva, cstruct)
770
+ length = cstruct.slength
771
+ data = pe._isource.read(pe.rva_to_file_offset(rva), length)
772
+ cstruct.from_s data
773
+ end
774
+
775
+ def reconstruct_offset_table(pe, server_if)
776
+ if server_if['interpreterInfo'].value != 0
777
+ server_info = reconstruct_midl_server_info(pe, server_if)
778
+
779
+ unless server_info.nil?
780
+ pdisptbl = pe.vma_to_rva(server_if['dispatchTable'].value)
781
+ disptbl = reconstruct_disptbl_from_addr(pe, pdisptbl)
782
+
783
+ reconstruct_offset_table2(pe, disptbl, server_info)
784
+ end
785
+ end
786
+ end
787
+
788
+ def reconstruct_offset_table2(pe, disptbl, midl_server_info)
789
+ poffset_table = pe.vma_to_file_offset(midl_server_info['fmtStringOffset'].value)
790
+ count = disptbl['dispatchTableCount'].value
791
+ pe._isource.read(poffset_table, count).unpack('C'*count)
792
+ end
793
+
794
+ def reconstruct_endpoint_info(pe, server_if)
795
+ endpoints = []
796
+
797
+ if (count = server_if['rpcProtseqEndpointCount'].value) > 0
798
+ rva = pe.vma_to_rva(server_if['rpcProtseqEndpoint'].value)
799
+
800
+ count.times do |i|
801
+ ep = make_rpc_protseq_endpoint(pe)
802
+ reconstruct_struct_from_pe(pe, rva+i*ep.slength, ep)
803
+ pprotseq = pe.vma_to_file_offset(ep['rpcProtocolSequence'].value)
804
+ pendpoint = pe.vma_to_file_offset(ep['endpoint'].value)
805
+
806
+ protseq = TurboRex::MSRPC::Utils.read_cstring(pe._isource, pprotseq)[0]
807
+ ep_name = TurboRex::MSRPC::Utils.read_cstring(pe._isource, pendpoint)[0]
808
+ endpoints << {protseq: protseq, ep_name: ep_name}
809
+ i+=ep.slength
810
+ end
811
+
812
+ end
813
+
814
+ endpoints
815
+ end
816
+
817
+ def reconstruct_stubless_pinfo(pe, client_if)
818
+ proxy_info = make_midl_stubless_proxy_info(pe)
819
+ pinterpreter_info = client_if.InterpreterInfo_Value
820
+ rva = pe.vma_to_rva(pinterpreter_info)
821
+ reconstruct_struct_from_pe(pe, rva, proxy_info)
822
+ proxy_info if validate_stubless_proxy_info(pe, proxy_info)
823
+ end
824
+
825
+ def reconstruct_midl_syntax_info(pe, midl_server_info)
826
+ pSyntaxInfo = midl_server_info['pSyntaxInfo'].value
827
+ count = midl_server_info['nCount'].value
828
+ return nil if count < 0
829
+
830
+ syntax_infos = []
831
+
832
+ if pe.ptr_32?
833
+ len = TurboRex::MSRPC::RPCBase::MIDL_SYNTAX_INFO32.make(pack: 4, align: true).slength
834
+ else
835
+ len = TurboRex::MSRPC::RPCBase::MIDL_SYNTAX_INFO64.make(pack: 8, align: true).slength
836
+ end
837
+
838
+ unless pSyntaxInfo == 0
839
+ count.times do |i|
840
+ rva = pe.vma_to_rva(pSyntaxInfo + i * len)
841
+ midl_syntax_info = make_midl_syntax_info(pe)
842
+ reconstruct_struct_from_pe(pe, rva, midl_syntax_info)
843
+
844
+ syntax_infos << midl_syntax_info
845
+ end
846
+ end
847
+
848
+ syntax_infos
849
+ end
850
+
851
+ def reconstruct_midl_server_info(pe, rpc_server_interface)
852
+ if validate_rpc_server_interface(pe, rpc_server_interface) && has_interpreter_info?(rpc_server_interface)
853
+ rva = pe.vma_to_rva(rpc_server_interface['interpreterInfo'].value)
854
+ midl_server_info = make_midl_server_info(pe)
855
+ reconstruct_struct_from_pe(pe, rva, midl_server_info)
856
+
857
+ midl_server_info
858
+ end
859
+ end
860
+
861
+ def reconstruct_midl_stub_desc(pe, midl_server_info)
862
+ unless midl_server_info['pStubDesc'].value == 0
863
+ rva = pe.vma_to_rva(midl_server_info['pStubDesc'].value)
864
+ midl_stub_desc = make_midl_stub_desc(@pe)
865
+ reconstruct_struct_from_pe(pe, rva, midl_stub_desc)
866
+ midl_stub_desc if validate_midl_stub_desc(pe, midl_stub_desc)
867
+ end
868
+ end
869
+
870
+ def reconstruct_disptbl_for_server_interface(pe, rpc_server_interface)
871
+ rva = pe.vma_to_rva(rpc_server_interface['dispatchTable'].value)
872
+ reconstruct_disptbl_from_addr(pe, rva)
873
+ end
874
+
875
+ def reconstruct_disptbl_from_addr(pe, addr)
876
+ rpc_dispatch_table = make_rpc_dispatch_table_t(pe)
877
+ reconstruct_struct_from_pe(pe, addr, rpc_dispatch_table)
878
+
879
+ rpc_dispatch_table
880
+ end
881
+
882
+ def reconstruct_disp_functions(pe, rpc_dispatch_table)
883
+ count = rpc_dispatch_table['dispatchTableCount'].value
884
+ pdispatch_table = pe.vma_to_rva(rpc_dispatch_table['dispatchTable'].value)
885
+ dispatch_funcs = []
886
+
887
+ if pe.ptr_32?
888
+ ptr_len = 4
889
+ format = 'V'
890
+ func_name = 'read_dword'
891
+ else
892
+ ptr_len = 8
893
+ format = 'Q<'
894
+ func_name = 'read_qword'
895
+ end
896
+
897
+ unless pdispatch_table == 0
898
+ count.times do |i|
899
+ code = "#{func_name}(pe._isource, pe.rva_to_file_offset(pdispatch_table + #{i * ptr_len})).unpack('#{format}')[0]"
900
+ begin
901
+ dispatch_funcs << eval(code)
902
+ rescue StandardError
903
+ next
904
+ end
905
+ end
906
+ end
907
+
908
+ dispatch_funcs
909
+ end
910
+
911
+ def reconstruct_disptbl_for_midl_server_info(pe, midl_server_info, count)
912
+ rva = pe.vma_to_rva(midl_server_info['dispatchTable'].value)
913
+ server_routines = []
914
+
915
+ if pe.ptr_32?
916
+ count.times do
917
+ server_routines << read_dword(pe._isource, pe.rva_to_file_offset(rva)).unpack('V')[0]
918
+ rva += 4
919
+ end
920
+ else
921
+ count.times do
922
+ server_routines << read_qword(pe._isource, pe.rva_to_file_offset(rva)).unpack('Q<')[0]
923
+ rva += 8
924
+ end
925
+ end
926
+
927
+ server_routines
928
+ end
929
+
930
+ def new_dasm
931
+ exe = Metasm::PE.decode_file @pe.image_path.to_s
932
+
933
+ exe.disassembler
934
+ end
935
+
936
+ def scan_xrefs_immediate(addr, dasm = nil)
937
+ dasm ||= (@dasm || new_dasm)
938
+ cpu_size = dasm.cpu.size
939
+ mask = (1 << cpu_size) - 1
940
+ format = (cpu_size == 64 ? 'q' : 'V')
941
+ res = []
942
+
943
+ dasm.sections.sort.each do |start_addr, encoded_data|
944
+ raw = encoded_data.data.to_str
945
+ (0..raw.length - cpu_size / 8).each do |offset|
946
+ data = raw[offset, cpu_size / 8].unpack(format).first
947
+ res << (start_addr + offset) if data == addr
948
+ end
949
+ end
950
+
951
+ res
952
+ end
953
+
954
+
955
+ # Xrefs in the same binary file
956
+ def draw_ifs_xrefs
957
+ @server_interfaces.each do |si|
958
+ @client_interfaces.each do |ci|
959
+ calls = []
960
+ ci.routines.each do |cr|
961
+ unless (res = si.func_in_server_routines(cr.addr)).empty?
962
+ calls << { caller: res, called: cr }
963
+ end
964
+ end
965
+
966
+ next if calls.empty?
967
+
968
+ si.xrefs_from << [ci, calls]
969
+ ci.xrefs_to << [si, calls]
970
+
971
+ si.xrefs_from.uniq!
972
+ si.xrefs_to.uniq!
973
+ end
974
+ end
975
+ end
976
+
977
+
978
+
979
+ def disassemble(addr, dasm = nil)
980
+ dasm ||= (@dasm || new_dasm)
981
+ res = dasm.disassemble addr
982
+ [dasm, res]
983
+ end
984
+
985
+ def disassemble_fast_deep(addr, dasm = nil)
986
+ dasm ||= (@dasm || new_dasm)
987
+ res = dasm.disassemble_fast_deep(addr)
988
+ [dasm, res]
989
+ end
990
+
991
+ def disassemble_fast(addr, dasm = nil)
992
+ dasm ||= (@dasm || new_dasm)
993
+ res = dasm.disassemble_fast(addr)
994
+ [dasm, res]
995
+ end
996
+
997
+ def decompile_func(addr)
998
+ dasm = disassemble_fast(addr)[0]
999
+ dasm.decompiler.decompile(addr) # Metasm::C::Parser
1000
+ end
1001
+
1002
+ def disassemble_executable_sections(dasm = nil)
1003
+ exe_sections = @pe.executable_sections
1004
+ unless exe_sections.empty?
1005
+ dasm ||= (@dasm || new_dasm)
1006
+ add_dasm_all_method(dasm)
1007
+
1008
+ exe_sections.each do |s|
1009
+ dasm.dasm_all(@pe.rva_to_vma(s.base_rva), s.raw_size)
1010
+ end
1011
+
1012
+ addrtolabel(dasm)
1013
+ dasm
1014
+ end
1015
+ end
1016
+
1017
+ private
1018
+
1019
+ def read_dword(isource, address)
1020
+ isource.read(address, 4)
1021
+ end
1022
+
1023
+ def read_qword(isource, address)
1024
+ isource.read(address, 8)
1025
+ end
1026
+
1027
+ def has_interpreter_info?(rpc_server_interface)
1028
+ # Don't assume when flag is set. Must check if InterpreterInfo is a null pointer.
1029
+ (rpc_server_interface['flags'].value & 0x4000000) == 0x4000000 && \
1030
+ rpc_server_interface['interpreterInfo'].value != 0
1031
+ end
1032
+
1033
+ def has_proxy_info?(rpc_server_interface)
1034
+ flags = (begin
1035
+ rpc_server_interface['flags'].value
1036
+ rescue StandardError
1037
+ rpc_server_interface.Flags
1038
+ end)
1039
+ interpreter_info = (begin
1040
+ rpc_server_interface['interpreterInfo'].value
1041
+ rescue StandardError
1042
+ rpc_server_interface.InterpreterInfo
1043
+ end)
1044
+ (flags & 0x2000000) == 0x2000000 && \
1045
+ interpreter_info != 0
1046
+ end
1047
+
1048
+ def internal_auto_find(config = {})
1049
+ @server_interfaces = []
1050
+ @dispatch_funcs = []
1051
+ @server_routines = []
1052
+ @client_routines = []
1053
+ @client_interfaces = []
1054
+ @midl_stub_descs = []
1055
+
1056
+ find_rpc_server_interface do |r, s_addr|
1057
+ next if r.nil?
1058
+ si_obj = TurboRex::MSRPC::RPCBase::RPC_SERVER_INTERFACE_Klass.new(r)
1059
+ interface = InterfaceModel.new si_obj, self
1060
+ interface.endpoints = get_endpoint_info(r)
1061
+
1062
+ if interface.dispatch_table_nullptr? # maybe client
1063
+ next unless config[:include_client]
1064
+
1065
+ @client_interfaces << interface
1066
+ @collection_proxy&.push_client(interface)
1067
+
1068
+ if config[:find_client_routines]
1069
+ cr = find_client_routines(interface, @pe.rva_to_vma(s_addr))
1070
+ @client_routines |= cr
1071
+ interface.routines = cr
1072
+ end
1073
+
1074
+ else # server
1075
+ next if config[:only_client]
1076
+
1077
+ @server_interfaces << interface
1078
+ @collection_proxy&.push_server(interface)
1079
+
1080
+ disp_table = get_dispatch_table(r)
1081
+ disp_func = get_disp_functions(disp_table)
1082
+ @dispatch_funcs |= disp_func
1083
+
1084
+ disp_table_obj = TurboRex::MSRPC::RPCBase::RPC_DISPATCH_TABLE_Klass.new(disp_table)
1085
+ disp_table_obj.link_to disp_func
1086
+ interface.link_to disp_table_obj
1087
+
1088
+ msi = get_midl_server_info(r)
1089
+ if msi.nil? # Inline stub(-Os mode)
1090
+ @dispatch_funcs |= disp_func
1091
+ else
1092
+ msi_obj = TurboRex::MSRPC::RPCBase::MIDL_SERVER_INFO_Klass.new(msi)
1093
+ @midl_server_infos << msi_obj
1094
+ interface.link_to msi_obj
1095
+
1096
+ # find all server routines
1097
+ routines = get_routines_from_server_interface(r)
1098
+ unless routines.nil?
1099
+ r_objs = routines.map { |r| TurboRex::MSRPC::RPCBase::SERVER_ROUTINE_Klass.new(r) }
1100
+ @server_routines |= r_objs
1101
+ interface.routines = r_objs
1102
+ msi_obj.link_to r_objs
1103
+ end
1104
+
1105
+ # reconstruct MIDL_SYNTAX_INFO
1106
+ midl_syntax_info = get_midl_syntax_info(msi)
1107
+ unless midl_syntax_info.nil? || midl_syntax_info.empty?
1108
+ objs = []
1109
+ midl_syntax_info.each do |m|
1110
+ syntax_info_obj = TurboRex::MSRPC::RPCBase::MIDL_SYNTAX_INFO_Klass.new(m)
1111
+ pdisp_tbl = syntax_info_obj.DispatchTable
1112
+ unless pdisp_tbl == 0
1113
+ disptbl = reconstruct_disptbl_from_addr(@pe, @pe.vma_to_rva(pdisp_tbl))
1114
+ disp_funcs = reconstruct_disp_functions(@pe, disptbl)
1115
+ disptbl_obj = RPC_DISPATCH_TABLE_Klass.new(disptbl)
1116
+ disptbl_obj.link_to disp_funcs
1117
+ syntax_info_obj.link_to disptbl_obj
1118
+ end
1119
+
1120
+ @midl_syntax_infos << syntax_info_obj
1121
+ objs << syntax_info_obj
1122
+ end
1123
+
1124
+ msi_obj.link_to objs
1125
+ end
1126
+
1127
+ # reconstruct MIDL_STUB_DESC
1128
+ midl_stub_desc = get_midl_stub_desc(msi)
1129
+ unless midl_stub_desc.nil?
1130
+ stub_desc_obj = TurboRex::MSRPC::RPCBase::MIDL_STUB_DESC_Klass.new(midl_stub_desc)
1131
+ msi_obj.link_to stub_desc_obj
1132
+ end
1133
+
1134
+ # reconstruct offset_table
1135
+ interface.offset_table = get_offset_table2(disp_table, msi)
1136
+ end
1137
+ end
1138
+
1139
+ interface.analysis_midl_switches if config[:analysis_switches]
1140
+ end
1141
+ end
1142
+ end
1143
+
1144
+ class MemoryFinder
1145
+ attr_reader :process
1146
+ attr_reader :header
1147
+
1148
+ include TurboRex::Windows::Utils
1149
+ include TurboRex::MSRPC::Utils
1150
+
1151
+ using ::RefineAllocCStruct
1152
+
1153
+ ## Abstract data model for some key rpc structures
1154
+ class RPC_Interface
1155
+ attr_accessor :flags
1156
+ attr_accessor :interface_type
1157
+ attr_accessor :interface_id
1158
+ attr_accessor :name
1159
+ attr_accessor :syntax
1160
+ end
1161
+
1162
+ class RPC_Endpoint
1163
+ end
1164
+
1165
+ class RPC_AuthInfo
1166
+ end
1167
+
1168
+ def initialize(pid, opts = {})
1169
+ raise 'Not work on non-Windows os.' unless ::OS.windows?
1170
+
1171
+ if opts[:debug_priv]
1172
+ unless Metasm::WinOS.get_debug_privilege
1173
+ raise 'Unable to get SeDebugPrivilege.'
1174
+ end
1175
+ end
1176
+
1177
+ @process = open_process(pid)
1178
+ @mem = @process.memory
1179
+ opts[:force_load] ||= {}
1180
+
1181
+ unless load_headers(opts[:force_load])
1182
+ raise 'Unable to load RPC structure definitions.'
1183
+ end
1184
+
1185
+ @header.prepare_visualstudio
1186
+
1187
+ @gRpcServer = nil
1188
+ @rpc_interfaces = []
1189
+ @server_interfaces = []
1190
+ @endpoints = []
1191
+ end
1192
+
1193
+ def self.list_process_pid
1194
+ TurboRex::Windows.list_all_process_pid
1195
+ end
1196
+
1197
+ def close
1198
+ @process.close
1199
+ end
1200
+
1201
+ def process_handle
1202
+ @process.handle
1203
+ end
1204
+
1205
+ def find_rpc_server
1206
+ @gRpcServer = find_global_rpc_server
1207
+ end
1208
+
1209
+ def enum_rpc_interfaces(rpc_server_t)
1210
+ num_entries = rpc_server_t.InterfaceDict.NumberOfEntries
1211
+ dictsize = num_entries * ptr_len
1212
+
1213
+ rpc_interface_t = @header['RPC_INTERFACE_T'] # read data as RPC_INTERFACE_T
1214
+ begin
1215
+ data = @mem.get_page(rpc_server_t.InterfaceDict.pArray, dictsize)
1216
+ return false if data.nil?
1217
+
1218
+ (0..dictsize).step(ptr_len) do |p|
1219
+ prpc_interface_t = if pe.ptr_32?
1220
+ data[p, ptr_len].unpack('V')[0]
1221
+ else
1222
+ data[p, ptr_len].unpack('Q<')[0]
1223
+ end
1224
+
1225
+ interface_t = rpc_interface_t.from_str(@mem.get_page(prpc_interface_t, rpc_interface_t.size))
1226
+ get_rpc_interfaces_info(interface_t)
1227
+ end
1228
+ rescue StandardError
1229
+ false
1230
+ end
1231
+ end
1232
+
1233
+ def get_rpc_interfaces_info(rpc_interface)
1234
+ info = TurboRex::MSRPC::RPCFinder::MemoryFinder::RPC_Interface.new
1235
+ info.flags = rpc_interface.Flags
1236
+ info.interface_type = get_interface_type(rpc_interface)
1237
+ info.interface_id = TurboRex::MSRPC::Utils.raw_to_guid_str(rpc_interface.RpcServerInterface.InterfaceId.to_string)
1238
+ if info.interface_type == TurboRex::MSRPC::RPCBase::InterfaceType::DCOM
1239
+ info.name = get_com_interface_name(info.interface_id)
1240
+ end
1241
+ info.syntax = TurboRex::MSRPC::Utils.raw_to_guid_str(rpc_interface.RpcServerInterface.TransferSyntax.to_string)
1242
+
1243
+ case info.interface_type
1244
+ when TurboRex::MSRPC::RPCBase::InterfaceType::RPC
1245
+ get_location(rpc_interface.RpcServerInterface.DispatchTable)
1246
+ end
1247
+ end
1248
+
1249
+ def get_interface_type(rpc_interface)
1250
+ if rpc_interface.Flags == @header.numeric_constants.assoc('RPC_IF_OLE')[1]
1251
+ return TurboRex::MSRPC::RPCBase::InterfaceType::OLE
1252
+ end
1253
+
1254
+ uuid = TurboRex::MSRPC::Utils.raw_to_guid_str(rpc_interface.RpcServerInterface.InterfaceId.to_string)
1255
+ if get_com_interface_name(uuid)
1256
+ return TurboRex::MSRPC::RPCBase::InterfaceType::DCOM
1257
+ end
1258
+
1259
+ TurboRex::MSRPC::RPCBase::InterfaceType::RPC
1260
+ end
1261
+
1262
+ def get_com_interface_name(interface_id)
1263
+ require 'win32/registry'
1264
+ case @arch
1265
+ when 'x86'
1266
+ prefix = ''
1267
+ when 'x64'
1268
+ prefix = 'Wow6432Node\\'
1269
+ end
1270
+ begin
1271
+ Win32::Registry::HKEY_CLASSES_ROOT.open(prefix + "Interface\\{#{interface_id}}") do |reg|
1272
+ return reg.read('')[1] # default value
1273
+ end
1274
+ rescue StandardError
1275
+ false
1276
+ end
1277
+ end
1278
+
1279
+ def get_location(_addr)
1280
+ raise NotImplementedError
1281
+ end
1282
+
1283
+ def scan_marker(marker, range, size = marker.size, step = 1)
1284
+ mem = @mem
1285
+ res = []
1286
+
1287
+ range.step(step) do |va|
1288
+ data = mem.get_page(va, size)
1289
+ yield(data, va) if block_given?
1290
+
1291
+ unless data.nil?
1292
+ res << va if data == marker
1293
+ end
1294
+ end
1295
+
1296
+ res
1297
+ end
1298
+
1299
+ def find_global_rpc_server
1300
+ rpcrt4 = @process.modules.select { |m| m.path =~ /rpcrt4.dll/i }[0]
1301
+
1302
+ pe = TurboRex::PEFile::PE.new_from_file(rpcrt4.path)
1303
+ data_section = pe.sections.select { |s| s.name == '.data' }[0]
1304
+ startaddr = rpcrt4.addr + data_section.vma
1305
+ endaddr = startaddr + data_section._section_header.v['Misc']
1306
+ ptr_len = pe.ptr_32? ? 4 : 8 && @header.llp64
1307
+ max_entries = @header.numeric_constants.assoc('MAX_SIMPLE_DICT_ENTRIES')[1]
1308
+ pe.close
1309
+
1310
+ scan_marker(nil, startaddr..endaddr, ptr_len) do |data|
1311
+ pointer = if pe.ptr_32?
1312
+ data.unpack('V')[0]
1313
+ else
1314
+ data.unpack('Q<')[0]
1315
+ end
1316
+
1317
+ rpc_server_t = @header['RPC_SERVER_T'] # read data as RPC_SERVER_T
1318
+ begin
1319
+ data = @mem.get_page(pointer, rpc_server_t.size)
1320
+ rescue StandardError
1321
+ next
1322
+ end
1323
+ rpc_server_t.from_str data
1324
+
1325
+ num_entries = rpc_server_t.InterfaceDict.NumberOfEntries
1326
+ dictsize = num_entries * ptr_len
1327
+ next if num_entries > max_entries || num_entries <= 0
1328
+
1329
+ rpc_interface_t = @header['RPC_INTERFACE_T'] # read data as RPC_INTERFACE_T
1330
+ begin
1331
+ data = @mem.get_page(rpc_server_t.InterfaceDict.pArray, dictsize)
1332
+ next if data.nil?
1333
+
1334
+ (0..dictsize).step(ptr_len) do |p|
1335
+ prpc_interface_t = if pe.ptr_32?
1336
+ data[p, ptr_len].unpack('V')[0]
1337
+ else
1338
+ data[p, ptr_len].unpack('Q<')[0]
1339
+ end
1340
+
1341
+ interface_t = rpc_interface_t.from_str(@mem.get_page(prpc_interface_t, rpc_interface_t.size))
1342
+ if interface_t.pRpcServer == pointer
1343
+ if interface_t.RpcServerInterface.TransferSyntax.to_string == TurboRex::MSRPC::RPCBase::DCE_TransferSyntax.to_s
1344
+ return rpc_server_t
1345
+ end
1346
+ end
1347
+ end
1348
+ rescue StandardError
1349
+ next
1350
+ end
1351
+ end
1352
+ end
1353
+
1354
+ private
1355
+
1356
+ def open_process(pid)
1357
+ p = TurboRex::Windows.open_process(pid, Metasm::WinAPI::PROCESS_VM_READ)
1358
+ raise "Unable to open process #{pid}" if p.nil?
1359
+
1360
+ case p.addrsz
1361
+ when 32
1362
+ @arch = 'x86'
1363
+ when 64
1364
+ @arch = 'x64'
1365
+ end
1366
+
1367
+ p
1368
+ end
1369
+
1370
+ def load_headers(force_load = {})
1371
+ headers_path = TurboRex.root + '/resources/headers/rpc'
1372
+ include_path = TurboRex::Utils.get_all_subdir(headers_path)
1373
+ version_hl = get_version('rpcrt4.dll')
1374
+ version = ((version_hl[0] << 32) + version_hl[1])
1375
+ opts = {}
1376
+ distance = 0
1377
+ approximation = nil
1378
+
1379
+ opts[:include_path] = include_path
1380
+ if force_load[:file] && force_load[:cpu]
1381
+ return force_load_file(force_load)
1382
+ end
1383
+
1384
+ if @process.addrsz == 32
1385
+ opts[:cpu] = Metasm::Ia32
1386
+ pattern = '/v*_x86/rpcinternals.h'
1387
+ elsif @process.addrsz == 64
1388
+ opts[:cpu] = Metasm::X86_64
1389
+ pattern = '/v*_x64/rpcinternals.h'
1390
+ end
1391
+
1392
+ Dir.glob(headers_path + pattern).each do |f|
1393
+ opts[:file] = f
1394
+ native_parser = TurboRex::CStruct::NativeParser.new(nil, opts)
1395
+ initializer = native_parser.parser.toplevel.symbol['RPC_CORE_RUNTIME_VERSION'].initializer
1396
+ initializer.each do |i|
1397
+ if i.rexpr == version
1398
+ @header = native_parser
1399
+ return true
1400
+ else
1401
+ d = (version - i.rexpr).abs
1402
+ distance = d if distance == 0
1403
+
1404
+ if d < distance
1405
+ approximation = [i.rexpr, native_parser]
1406
+ distance = d
1407
+ end
1408
+ end
1409
+ end
1410
+ end
1411
+
1412
+ if force_load[:approximation]
1413
+ @header = approximation[1]
1414
+ @approximation = approximation
1415
+ end
1416
+
1417
+ true
1418
+ end
1419
+
1420
+ def force_load_file(opts)
1421
+ @header = TurboRex::CStruct::NativeParser.new(nil, opts)
1422
+ end
1423
+ end
1424
+ end
1425
+ end
1426
+ end