turborex 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +674 -0
- data/README.md +38 -0
- data/README.rdoc +19 -0
- data/examples/alpc_client.rb +15 -0
- data/examples/alpc_server.rb +14 -0
- data/examples/com_client.rb +19 -0
- data/examples/com_finder.rb +39 -0
- data/examples/create_instance.rb +15 -0
- data/examples/cstruct.rb +19 -0
- data/examples/find_com_client_calls.rb +16 -0
- data/examples/find_rpc_security_callback.rb +12 -0
- data/examples/rpc_finder.rb +117 -0
- data/examples/scan_exports.rb +5 -0
- data/examples/scan_imports.rb +5 -0
- data/examples/tinysdk.rb +17 -0
- data/lib/turborex.rb +21 -0
- data/lib/turborex/cstruct.rb +565 -0
- data/lib/turborex/cstruct/struct_helper.rb +7 -0
- data/lib/turborex/exception.rb +65 -0
- data/lib/turborex/fuzzer.rb +204 -0
- data/lib/turborex/fuzzer/containers.rb +115 -0
- data/lib/turborex/fuzzer/coverage.rb +67 -0
- data/lib/turborex/fuzzer/mutators.rb +25 -0
- data/lib/turborex/fuzzer/seed.rb +30 -0
- data/lib/turborex/monkey.rb +11 -0
- data/lib/turborex/msrpc.rb +14 -0
- data/lib/turborex/msrpc/decompiler.rb +244 -0
- data/lib/turborex/msrpc/midl.rb +747 -0
- data/lib/turborex/msrpc/ndrtype.rb +167 -0
- data/lib/turborex/msrpc/rpcbase.rb +777 -0
- data/lib/turborex/msrpc/rpcfinder.rb +1426 -0
- data/lib/turborex/msrpc/utils.rb +70 -0
- data/lib/turborex/pefile.rb +8 -0
- data/lib/turborex/pefile/pe.rb +61 -0
- data/lib/turborex/pefile/scanner.rb +82 -0
- data/lib/turborex/utils.rb +321 -0
- data/lib/turborex/windows.rb +402 -0
- data/lib/turborex/windows/alpc.rb +844 -0
- data/lib/turborex/windows/com.rb +266 -0
- data/lib/turborex/windows/com/client.rb +84 -0
- data/lib/turborex/windows/com/com_finder.rb +330 -0
- data/lib/turborex/windows/com/com_registry.rb +100 -0
- data/lib/turborex/windows/com/interface.rb +522 -0
- data/lib/turborex/windows/com/utils.rb +210 -0
- data/lib/turborex/windows/constants.rb +82 -0
- data/lib/turborex/windows/process.rb +56 -0
- data/lib/turborex/windows/security.rb +12 -0
- data/lib/turborex/windows/security/ace.rb +76 -0
- data/lib/turborex/windows/security/acl.rb +25 -0
- data/lib/turborex/windows/security/security_descriptor.rb +118 -0
- data/lib/turborex/windows/tinysdk.rb +89 -0
- data/lib/turborex/windows/utils.rb +138 -0
- data/resources/headers/alpc/ntdef.h +72 -0
- data/resources/headers/alpc/ntlpcapi.h +1014 -0
- data/resources/headers/rpc/common.h +162 -0
- data/resources/headers/rpc/guiddef.h +191 -0
- data/resources/headers/rpc/internal_ndrtypes.h +262 -0
- data/resources/headers/rpc/rpc.h +10 -0
- data/resources/headers/rpc/rpcdce.h +266 -0
- data/resources/headers/rpc/rpcdcep.h +187 -0
- data/resources/headers/rpc/rpcndr.h +39 -0
- data/resources/headers/rpc/v4_x64/rpcinternals.h +154 -0
- data/resources/headers/rpc/wintype.h +517 -0
- data/resources/headers/tinysdk/tinysdk.h +5 -0
- data/resources/headers/tinysdk/tinysdk/comdef.h +645 -0
- data/resources/headers/tinysdk/tinysdk/dbghelp.h +118 -0
- data/resources/headers/tinysdk/tinysdk/guiddef.h +194 -0
- data/resources/headers/tinysdk/tinysdk/memoryapi.h +12 -0
- data/resources/headers/tinysdk/tinysdk/poppack.h +12 -0
- data/resources/headers/tinysdk/tinysdk/pshpack4.h +13 -0
- data/resources/headers/tinysdk/tinysdk/winnt.h +1059 -0
- data/resources/headers/tinysdk/tinysdk/wintype.h +326 -0
- 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
|