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,266 @@
1
+ # frozen_string_literal: true
2
+
3
+ unless OS.windows?
4
+ warn "\033[33m[-]Warning: This module doesn't currently work on non-Windows os.\033[0m"
5
+ end
6
+
7
+ module TurboRex
8
+ class Windows < ::Metasm::WinOS
9
+ module COM
10
+ module WellKnownIID
11
+ IID_IUnknown = '00000000-0000-0000-C000-000000000046'
12
+ IID_IClassFactory = '00000001-0000-0000-C000-000000000046'
13
+ IID_IStream = '0000000c-0000-0000-C000-000000000046'
14
+ IID_IStorage = '0000000b-0000-0000-C000-000000000046'
15
+ IID_IPSFactoryBuffer = 'D5F569D0-593B-101A-B569-08002B2DBF7A'
16
+ IID_IRpcProxyBuffer = 'D5F56A34-593B-101A-B569-08002B2DBF7A'
17
+ IID_IRpcStubBuffer = 'D5F56AFC-593B-101A-B569-08002B2DBF7A'
18
+ end
19
+
20
+ CLSCTX_INPROC_SERVER = 0x1
21
+ CLSCTX_INPROC_HANDLER = 0x2
22
+ CLSCTX_LOCAL_SERVER = 0x4
23
+ CLSCTX_INPROC_SERVER16 = 0x8
24
+ CLSCTX_REMOTE_SERVER = 0x10
25
+ CLSCTX_INPROC_HANDLER16 = 0x20
26
+ CLSCTX_RESERVED1 = 0x40
27
+ CLSCTX_RESERVED2 = 0x80
28
+ CLSCTX_RESERVED3 = 0x100
29
+ CLSCTX_RESERVED4 = 0x200
30
+ CLSCTX_NO_CODE_DOWNLOAD = 0x400
31
+ CLSCTX_RESERVED5 = 0x800
32
+ CLSCTX_NO_CUSTOM_MARSHAL = 0x1000
33
+ CLSCTX_ENABLE_CODE_DOWNLOAD = 0x2000
34
+ CLSCTX_NO_FAILURE_LOG = 0x4000
35
+ CLSCTX_DISABLE_AAA = 0x8000
36
+ CLSCTX_ENABLE_AAA = 0x10000
37
+ CLSCTX_FROM_DEFAULT_CONTEXT = 0x20000
38
+ CLSCTX_ACTIVATE_X86_SERVER = 0x40000
39
+ CLSCTX_ACTIVATE_32_BIT_SERVER = CLSCTX_ACTIVATE_X86_SERVER
40
+ CLSCTX_ACTIVATE_64_BIT_SERVER = 0x80000
41
+ CLSCTX_ENABLE_CLOAKING = 0x100000
42
+ CLSCTX_APPCONTAINER = 0x400000
43
+ CLSCTX_ACTIVATE_AAA_AS_IU = 0x800000
44
+ CLSCTX_RESERVED6 = 0x1000000
45
+ CLSCTX_ACTIVATE_ARM32_SERVER = 0x2000000
46
+ CLSCTX_PS_DLL = 0x80000000
47
+
48
+ # WinVer >= NT4 && DCOM
49
+ if TurboRex::Windows.version.first >= 4
50
+ CLSCTX_ALL = CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER | CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER
51
+ CLSCTX_SERVER = CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER
52
+ end
53
+
54
+ # Mashal Flag
55
+ MSHLFLAGS_NORMAL = 0
56
+ MSHLFLAGS_TABLESTRONG = 1
57
+ MSHLFLAGS_TABLEWEAK = 2
58
+ MSHLFLAGS_NOPING = 4
59
+ MSHLFLAGS_RESERVED1 = 8
60
+ MSHLFLAGS_RESERVED2 = 16
61
+ MSHLFLAGS_RESERVED3 = 32
62
+ MSHLFLAGS_RESERVED4 = 64
63
+
64
+ # Mashal Context
65
+ MSHCTX_LOCAL = 0
66
+ MSHCTX_NOSHAREDMEM = 1
67
+ MSHCTX_DIFFERENTMACHINE = 2
68
+ MSHCTX_INPROC = 3
69
+ MSHCTX_CROSSCTX = 4
70
+ MSHCTX_RESERVED1 = 5
71
+
72
+ # Object Refenrence Flags
73
+ OBJREF_STANDARD = 1
74
+ OBJREF_HANDLER = 2
75
+ OBJREF_CUSTOM = 4
76
+ OBJREF_EXTENDED = 8
77
+
78
+ # STGM Constant
79
+ STGM_READ = 0x00000000
80
+ STGM_WRITE = 0x00000001
81
+ STGM_READWRITE = 0x00000002
82
+ STGM_SHARE_DENY_NONE = 0x00000040
83
+ STGM_SHARE_DENY_READ = 0x00000030
84
+ STGM_SHARE_DENY_WRITE = 0x00000020
85
+ STGM_SHARE_EXCLUSIVE = 0x00000010
86
+ STGM_PRIORITY = 0x00040000
87
+ STGM_CREATE = 0x00001000
88
+ STGM_CONVERT = 0x00020000
89
+ STGM_FAILIFTHERE = 0x00000000
90
+ STGM_DIRECT = 0x00000000
91
+ STGM_TRANSACTED = 0x00010000
92
+ STGM_NOSCRATCH = 0x00100000
93
+ STGM_NOSNAPSHOT = 0x00200000
94
+ STGM_SIMPLE = 0x08000000
95
+ STGM_DIRECT_SWMR = 0x00400000
96
+ STGM_DELETEONRELEASE = 0x04000000
97
+
98
+ INTERNAL_APIPROXY = TurboRex::Windows::Win32API.dup
99
+ INTERNAL_APIPROXY.parse_c <<-EOS
100
+ typedef struct SHashChain
101
+ {
102
+ struct SHashChain *pNext;
103
+ struct SHashChain *pPrev;
104
+ } SHashChain;
105
+
106
+ typedef struct _CIDObject {
107
+ void * pVtable;
108
+ SHashChain _pidChain;
109
+ SHashChain _oidChain;
110
+ unsigned int _dwState;
111
+ unsigned int _cRefs;
112
+ void *_pServer;
113
+ void *_pServerCtx;
114
+ GUID _oid;
115
+ unsigned int _aptID;
116
+ void *_pStdWrapper;
117
+ void *_pStdID;
118
+ unsigned int _cCalls;
119
+ unsigned int _cLocks;
120
+ SHashChain _oidUnpinReqChain;
121
+ unsigned int _dwOidUnpinReqState;
122
+ void *_pvObjectTrackCookie;
123
+ } CIDObject;
124
+
125
+ typedef struct _CStdWrapper
126
+ {
127
+ void * pVtable;
128
+ unsigned long _dwState;
129
+ unsigned int _cRefs;
130
+ unsigned int _cCalls;
131
+ unsigned int _cIFaces;
132
+ void *_pIFaceHead;
133
+ void *_pCtxEntryHead;
134
+ void *_pCtxFreeList;
135
+ void *_pServer;
136
+ CIDObject *_pID;
137
+ void *_pVtableAddress;
138
+ }CStdWrapper;
139
+
140
+ typedef struct _tagIPIDEntry
141
+ {
142
+ void *pNextIPID;
143
+ unsigned int dwFlags;
144
+ unsigned int cStrongRefs;
145
+ unsigned int cWeakRefs;
146
+ unsigned int cPrivateRefs;
147
+ void *pv;
148
+ void *pStub;
149
+ void *pOXIDEntry;
150
+ GUID ipid;
151
+ GUID iid;
152
+ void *pChnl;
153
+ void *pIRCEntry;
154
+ void *pInterfaceName;
155
+ void *pOIDFLink;
156
+ void *pOIDBLink;
157
+ } tagIPIDEntry;
158
+
159
+
160
+ typedef struct _CStdIdentity
161
+ {
162
+ void *pVtable;
163
+ void *pVtable2;
164
+ unsigned int _dwFlags;
165
+ int _cIPIDs;
166
+ tagIPIDEntry *_pFirstIPID;
167
+ void *_pStdId;
168
+ void *_pChnl;
169
+ GUID _clsidHandler;
170
+ int _cNestedCalls;
171
+ int _cTableRefs;
172
+ } CStdIdentity;
173
+
174
+
175
+ typedef struct _IRpcStubBufferVtbl
176
+ {
177
+ HRESULT (__fastcall *QueryInterface)(void *, const GUID *, void **);
178
+ unsigned int (__fastcall *AddRef)(void *);
179
+ unsigned int (__fastcall *Release)(void *);
180
+ HRESULT (__fastcall *Connect)(void *, void *);
181
+ void (__fastcall *Disconnect)(void *);
182
+ HRESULT (__fastcall *Invoke)(void *, void *, void *);
183
+ void *(__fastcall *IsIIDSupported)(void *, const GUID *);
184
+ unsigned int (__fastcall *CountRefs)(void *);
185
+ HRESULT (__fastcall *DebugServerQueryInterface)(void *, void **);
186
+ void (__fastcall *DebugServerRelease)(void *, void *);
187
+ } IRpcStubBufferVtbl;
188
+
189
+ typedef struct tagCStdStubBuffer
190
+ {
191
+ const struct IRpcStubBufferVtbl * lpVtbl;
192
+ LONG RefCount;
193
+ struct IUnknown * pvServerObject;
194
+
195
+ const struct ICallFactoryVtbl * pCallFactoryVtbl;
196
+ const IID * pAsyncIID;
197
+ struct IPSFactoryBuffer * pPSFactory;
198
+ const struct IReleaseMarshalBuffersVtbl * pRMBVtbl;
199
+ } CStdStubBuffer;
200
+
201
+ typedef struct tagCInterfaceStubHeader
202
+ {
203
+ const IID * piid;
204
+ const void * pServerInfo; // MIDL_SERVER_INFO
205
+ ULONG DispatchTableCount;
206
+ const void * pDispatchTable;
207
+ } CInterfaceStubHeader;
208
+
209
+ typedef struct tagCInterfaceProxyHeader
210
+ {
211
+ #ifdef USE_STUBLESS_PROXY
212
+ const void * pStublessProxyInfo;
213
+ #endif
214
+ const IID * piid;
215
+ } CInterfaceProxyHeader;
216
+
217
+ typedef struct tagCInterfaceProxyVtbl
218
+ {
219
+ CInterfaceProxyHeader header;
220
+ void *Vtbl[1];
221
+ } CInterfaceProxyVtbl;
222
+
223
+ typedef struct tagCInterfaceStubVtbl
224
+ {
225
+ CInterfaceStubHeader header;
226
+ IRpcStubBufferVtbl Vtbl;
227
+ } CInterfaceStubVtbl;
228
+
229
+ typedef struct tagCInterfaceStubVtbl * PCInterfaceStubVtblList;
230
+ typedef struct tagCInterfaceProxyVtbl * PCInterfaceProxyVtblList;
231
+ typedef const char * PCInterfaceName;
232
+ typedef int __stdcall IIDLookupRtn( const IID * pIID, int * pIndex );
233
+ typedef IIDLookupRtn * PIIDLookup;
234
+
235
+ typedef struct tagProxyFileInfo
236
+ {
237
+ const PCInterfaceProxyVtblList *pProxyVtblList;
238
+ const PCInterfaceStubVtblList *pStubVtblList;
239
+ const PCInterfaceName * pNamesArray;
240
+ const IID ** pDelegatedIIDs;
241
+ const PIIDLookup pIIDLookupRtn;
242
+ unsigned short TableSize;
243
+ unsigned short TableVersion;
244
+ const IID ** pAsyncIIDLookup;
245
+ LONG_PTR Filler2;
246
+ LONG_PTR Filler3;
247
+ LONG_PTR Filler4;
248
+ }ProxyFileInfo;
249
+
250
+ typedef struct tagCStdPSFactoryBuffer
251
+ {
252
+ const IPSFactoryBufferVtbl * lpVtbl;
253
+ LONG RefCount;
254
+ const ProxyFileInfo ** pProxyFileList;
255
+ LONG Filler1; //Reserved for future use.
256
+ } CStdPSFactoryBuffer;
257
+ EOS
258
+
259
+ require 'turborex/windows/com/interface.rb'
260
+ require 'turborex/windows/com/utils.rb'
261
+ require 'turborex/windows/com/client.rb'
262
+ require 'turborex/windows/com/com_registry.rb'
263
+ require 'turborex/windows/com/com_finder.rb'
264
+ end
265
+ end
266
+ end
@@ -0,0 +1,84 @@
1
+ module TurboRex
2
+ class Windows < Metasm::WinOS
3
+ module COM
4
+ class Client
5
+ include WellKnownIID
6
+
7
+ attr_reader :clsid
8
+ attr_reader :api_proxy
9
+ attr_reader :iunknown
10
+
11
+ def initialize(clsid, opts = {})
12
+ @clsid = clsid
13
+ @context = opts[:cls_context] || CLSCTX_ALL
14
+ @iunknown = Interface::IUnknown.new
15
+ @apartment = opts[:apartment] || 0
16
+ @api_proxy = Win32API.dup
17
+ @cp = @api_proxy.cp
18
+
19
+ Win32API.coinitializeex(0, @apartment)
20
+ end
21
+
22
+ # Binding to class implementation
23
+ def create_instance(opts={})
24
+ interface = opts[:interface] || @iunknown
25
+ iid = interface.iid
26
+ cls_context = opts[:cls_context] || @context
27
+ ppv = @api_proxy.alloc_c_ptr('PVOID')
28
+ pclsid = Utils.clsid_to_raw(@clsid)
29
+ piid = Utils.clsid_to_raw(iid)
30
+
31
+ hr = @api_proxy.cocreateinstance(pclsid, 0, cls_context, piid, ppv)
32
+ raise "Failed to call CoCreateInstance: #{TinySDK.format_hex_ntstatus(hr, hex_str: true)}" unless TinySDK.nt_success?(hr)
33
+ pthis = ppv[0]
34
+ interface.this = pthis
35
+ @iunknown = interface if interface.kind_of?(Interface::IUnknown)
36
+ interface
37
+ end
38
+
39
+ # Binding to class object(class factory)
40
+ def get_class_object(opts={})
41
+ interface = Interface::IClassFactory.new
42
+ iid = interface.iid
43
+ cls_context = opts[:cls_context] || @context
44
+ server_info = opts[:server_info]
45
+ ppv = @api_proxy.alloc_c_ptr('LPVOID')
46
+ pclsid = Utils.clsid_to_raw(@clsid)
47
+ piid = Utils.clsid_to_raw(iid)
48
+
49
+ hr = @api_proxy.cogetclassobject(pclsid, cls_context, server_info, piid, ppv)
50
+ raise "Failed to call CoGetClassObject: #{TinySDK.format_hex_ntstatus(hr, hex_str: true)}" unless TinySDK.nt_success?(hr)
51
+
52
+ pthis = ppv[0]
53
+ interface.this = pthis
54
+ interface
55
+ end
56
+
57
+ def query_interface(iid_or_iface)
58
+ interface = nil
59
+ if iid_or_iface.is_a?(Interface)
60
+ interface = iid_or_iface
61
+ iid = interface.iid
62
+ elsif iid_or_iface.is_a?(String)
63
+ iid = iid_or_iface
64
+ end
65
+
66
+ create_instance unless @iunknown.this
67
+ iid = Utils.clsid_to_raw(iid)
68
+ ppv = @api_proxy.alloc_c_ptr('PVOID')
69
+
70
+ if @iunknown.QueryInterface(iid, ppv).nil?
71
+ if interface
72
+ interface.this = ppv[0]
73
+ return interface
74
+ else
75
+ return ppv[0]
76
+ end
77
+ end
78
+
79
+ false
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,330 @@
1
+ # frozen_string_literal: true
2
+ require 'turborex/pefile/scanner'
3
+
4
+ module TurboRex
5
+ class Windows < Metasm::WinOS
6
+ module COM
7
+ class Finder
8
+ include TurboRex::Utils::DisassemblerHelper
9
+ include TurboRex::PEFile::Scanner
10
+
11
+ def initialize(clsid)
12
+ @clsid = clsid
13
+ end
14
+ end
15
+
16
+ class InProcFinder < Finder
17
+ attr_reader :clsid
18
+ attr_reader :server_path
19
+
20
+ include Utils
21
+
22
+ def initialize(clsid)
23
+ @clsid = clsid
24
+ @process = TurboRex::Windows::Process.new(nil, -1)
25
+ @memory = @process.memory
26
+ @ptr_len = @process.cpusz / 8
27
+ end
28
+
29
+ def locate_interface_methods(iid)
30
+ Win32::Registry::HKEY_CLASSES_ROOT.open("CLSID\\{#{@clsid}}") do |reg_clsid|
31
+ reg_clsid.open('InprocServer32') do |reg_inproc32|
32
+ @server_path = reg_inproc32.read_s_expand('')
33
+ end
34
+ end
35
+
36
+ class_factory = Utils.dll_get_class_object(@clsid, @server_path)
37
+ ppv = INTERNAL_APIPROXY.alloc_c_ptr('PVOID')
38
+ unless class_factory.CreateInstance(0, Utils.clsid_to_raw(iid), ppv)
39
+ # class_factory.Release
40
+ pvtbl = to_ptr(@memory.get_page(ppv[0], @ptr_len))
41
+ proxy_file_info = get_proxy_file_info(iid)
42
+ return false unless proxy_file_info
43
+
44
+ count = get_disptbl_count(proxy_file_info)
45
+
46
+ if count
47
+ methods = []
48
+ @memory.get_page(pvtbl, count * @ptr_len).split('').each_slice(@ptr_len) { |m| methods << to_ptr(m.join) }
49
+
50
+ first_method = methods.first
51
+ _module = @process.modules.find { |m| first_method > m.addr && first_method < m.addr + m.size }
52
+ if relative
53
+ return {
54
+ module: _module.path,
55
+ methods: methods.map.with_index { |method, i| { index: i, rva: method - _module.addr } }
56
+ }
57
+ else
58
+ return {
59
+ module: _module.path,
60
+ methods: methods.map.with_index { |method, i| { index: i, va: method } }
61
+ }
62
+ end
63
+
64
+ end
65
+ end
66
+ end
67
+ end
68
+
69
+ class OutOfProcFinder < Finder
70
+ attr_reader :clsid
71
+ attr_reader :client
72
+ attr_reader :process
73
+ attr_reader :pid
74
+ attr_reader :handle
75
+
76
+ include Utils
77
+
78
+ def initialize(clsid, opts = {})
79
+ pid = opts[:pid]
80
+ context = opts[:context] || CLSCTX_ALL
81
+ @clsid = clsid
82
+ @client = Client.new(clsid)
83
+ @iunknown = @client.create_instance(cls_context: context, interface: Interface::IUnknown.new)
84
+ @pid = get_pid_by_std_objref(@iunknown) || pid
85
+
86
+ process = TurboRex::Windows::Process.new(@pid)
87
+ raise "Unable to open process #{pid}" unless process.handle
88
+ unless process.addrsz == (sz = Metasm::WinOS::Process.new(nil, -1).addrsz)
89
+ raise "The architecture of Ruby interpreter process(#{sz}-bit) is not same as target process"
90
+ end
91
+
92
+ @process = process
93
+ @handle = @process.handle
94
+ @ptr_len = @process.cpusz / 8
95
+ @memory = @process.memory
96
+
97
+ unless @process.load_symbol_table('combase.dll')
98
+ raise "Unable to load combase.dll's symbol"
99
+ end
100
+
101
+ @combase_base_addr = (@process.modules.find { |m| m.path =~ Regexp.new('combase.dll', true) }).addr
102
+ end
103
+
104
+ def locate_multiple_interfaces(iids, relative = true)
105
+ iids.map {|iid| locate_interface_methods(iid, relative)}.compact
106
+ end
107
+
108
+ def locate_interface_methods(iid, relative = true)
109
+ # Let the object exporter create IPID entry for target interface
110
+ tmp_interface = TurboRex::Windows::COM::Interface.define_interface(iid, {}, Interface::IUnknown)
111
+ ppv = INTERNAL_APIPROXY.alloc_c_ptr('PVOID')
112
+ raise "No such interface: #{iid}" unless @iunknown.QueryInterface(Utils.clsid_to_raw(iid), ppv).nil?
113
+ tmp_interface.this = ppv[0]
114
+ tmp_interface.marshal_to_string # For In-Proc server
115
+ return nil unless buckets_addr = find_oid_buckets_addr
116
+
117
+ headers = read_bucket_headers(buckets_addr)
118
+ walk_buckets(headers) do |_cid_obj, ipid_entry|
119
+ raw_iid = ipid_entry.str[ipid_entry.iid.stroff, ipid_entry.iid.sizeof]
120
+ _iid = TurboRex::MSRPC::Utils.raw_to_guid_str(raw_iid)
121
+
122
+ if _iid == iid
123
+ methods_count = iface_vtbl_count(ipid_entry)
124
+ return nil unless methods_count
125
+
126
+ if ipid_entry.pv
127
+ pvtbl = to_ptr(@memory.get_page(ipid_entry.pv, @ptr_len))
128
+ end
129
+
130
+ methods = []
131
+ @memory.get_page(pvtbl, methods_count * @ptr_len).split('').each_slice(@ptr_len) { |m| methods << to_ptr(m.join) }
132
+ # dasm = Metasm::Shellcode.decode(@memory, Metasm::X86_64.new).disassembler
133
+ # dasm.disassemble_fast_deep(methods.last)
134
+
135
+ first_method = methods.first
136
+ _module = @process.modules.find { |m| first_method > m.addr && first_method < m.addr + m.size }
137
+ if relative
138
+ tmp_interface.Release
139
+ return {
140
+ module: _module.path,
141
+ methods: methods.map.with_index { |method, i| { index: i, rva: method - _module.addr } }
142
+ }
143
+
144
+ # return methods.map.with_index do |method, i|
145
+ # _module = @process.modules.find { |m| method > m.addr && method < m.addr + m.size }
146
+ # _module ? {index: i, module: _module.path, rva: method - _module.addr} : nil
147
+ # end
148
+ else
149
+ tmp_interface.Release
150
+ return {
151
+ module: _module.path,
152
+ methods: methods.map.with_index { |method, i| { index: i, va: method } }
153
+ }
154
+ end
155
+ end
156
+ end
157
+
158
+ # Should decrease reference count
159
+ tmp_interface.Release
160
+ nil
161
+ end
162
+
163
+ def walk_buckets(buckets, &block)
164
+ buckets.each do |bucket, base_addr|
165
+ pNext = bucket.pNext
166
+ until pNext == base_addr
167
+ obj_addr = pNext - bucket.sizeof - @ptr_len
168
+ cid_obj = INTERNAL_APIPROXY.alloc_c_struct('CIDObject')
169
+ cid_obj.str = @memory.get_page(obj_addr, cid_obj.sizeof)
170
+ pNext = cid_obj._oidChain.pNext
171
+
172
+ std_ident = INTERNAL_APIPROXY.alloc_c_struct('CStdIdentity')
173
+ std_ident.str = @memory.get_page(cid_obj._pStdID, std_ident.sizeof)
174
+ walk_ipid_entries(std_ident._pFirstIPID) do |ipid_entry|
175
+ yield(cid_obj, ipid_entry) if block_given?
176
+ end
177
+ end
178
+ end
179
+ end
180
+
181
+
182
+ def walk_ipid_entries(pfirst_entry)
183
+ ipid_entries = []
184
+ pNext = pfirst_entry
185
+
186
+ # TODO: stdid.cIPIDs
187
+ until pNext.nil?
188
+ ipid_entry = INTERNAL_APIPROXY.alloc_c_struct('tagIPIDEntry')
189
+ ipid_entry.str = @memory.get_page(pNext, ipid_entry.sizeof)
190
+ yield(ipid_entry) if block_given?
191
+ ipid_entries << ipid_entry
192
+ pNext = ipid_entry.pNextIPID
193
+ end
194
+
195
+ ipid_entries
196
+ end
197
+
198
+ def find_oid_buckets_addr
199
+ buffer = INTERNAL_APIPROXY.alloc_c_ary('BYTE', 300)
200
+ sym_info = INTERNAL_APIPROXY.alloc_c_struct('SYMBOL_INFO')
201
+ sym_info.SizeOfStruct = sym_info.sizeof
202
+ sym_info.MaxNameLen = 150
203
+ buffer[0, sym_info.sizeof] = sym_info.str
204
+
205
+ if INTERNAL_APIPROXY.symfromname(@handle, 'COIDTable::s_OIDBuckets', buffer) == 1
206
+ sym_info.str = buffer[0, sym_info.sizeof]
207
+ sym_info.Address
208
+ end
209
+ end
210
+
211
+ def read_bucket_headers(address)
212
+ headers = []
213
+
214
+ TurboRex::Windows::Constants::MAX_BUCKETS_NUM.times do |i|
215
+ header = INTERNAL_APIPROXY.alloc_c_struct('SHashChain')
216
+ header_addr = address + i * header.sizeof
217
+ header.str = @memory.get_page(header_addr, header.sizeof)
218
+ next if header.pNext == header_addr
219
+
220
+ headers << [header, header_addr]
221
+ end
222
+
223
+ headers
224
+ end
225
+
226
+ def close
227
+ # TODO: Unload symbols
228
+ if @process
229
+ @process.close_handle
230
+ @handle = nil
231
+ end
232
+ end
233
+
234
+ private
235
+
236
+ def iface_vtbl_count(ipid_entry)
237
+ if ipid_entry.pStub # Standard interface stub
238
+ pstub_vtbl = @memory.get_page(ipid_entry.pStub, @ptr_len)
239
+ if_stub_vtbl = INTERNAL_APIPROXY.alloc_c_struct('CInterfaceStubVtbl')
240
+ if_stub_vtbl.str = @memory.get_page(to_ptr(pstub_vtbl) - if_stub_vtbl.Vtbl.stroff, if_stub_vtbl.sizeof)
241
+ if_stub_vtbl.header.DispatchTableCount
242
+ else
243
+ # Get ProxyFileInfo->pStubVtblList->header->DispatchTableCount
244
+ raw_iid = ipid_entry.str[ipid_entry.iid.stroff, ipid_entry.iid.sizeof]
245
+ iid = TurboRex::MSRPC::Utils.raw_to_guid_str(raw_iid)
246
+ proxy_file_info = get_proxy_file_info(iid)
247
+ raise unless proxy_file_info
248
+
249
+ # pcif_stub_vtbl_list = to_ptr(@memory.get_page(proxy_file_info.pStubVtblList, @ptr_len))
250
+ # if_stub_vtbl = TurboRex::Windows::COM::INTERNAL_APIPROXY.alloc_c_struct('CInterfaceStubVtbl')
251
+ # if_stub_vtbl.str = @memory.get_page(pcif_stub_vtbl_list, if_stub_vtbl.sizeof)
252
+ # return if_stub_vtbl.header.DispatchTableCount
253
+ get_disptbl_count(proxy_file_info)
254
+ end
255
+ end
256
+ end
257
+
258
+ class ClientFinder
259
+ include TurboRex::Utils::COMApiBacktraceHelper
260
+
261
+ BACKTRACE_PROC = {
262
+ 'CoCreateInstance' => :bt_cocreateinstance
263
+ }
264
+
265
+ def initialize(fname_or_dasm)
266
+ if fname_or_dasm.is_a?(String)
267
+ pe = Metasm::PE.decode_file(fname_or_dasm)
268
+ @dasm = _disassemble_executable_sections(pe)
269
+ elsif fname_or_dasm.is_a?(::Metasm::Disassembler)
270
+ @dasm = dasm
271
+ end
272
+ end
273
+
274
+ def find_client_call(dis=nil)
275
+ res = []
276
+
277
+ BACKTRACE_PROC.each do |k, v|
278
+ @dasm.call_sites(::Metasm::Expression[k]).each do |c|
279
+ bt_result = v.to_proc.call(self, @dasm, c)
280
+ pv = bt_result[:pv]
281
+ clsid = bt_result[:rclsid]
282
+ iid = bt_result[:riid]
283
+ context = bt_result[:context]
284
+
285
+ unless pv == :unknown
286
+ func_start = @dasm.find_function_start(c)
287
+ func_end = @dasm.function_including(c).return_address
288
+ return unless func_end
289
+ func_end = func_end.first
290
+ dis ||= @dasm.decoded.values.select {|di| di.address >= func_start && di.address <= func_end}
291
+ dis.select {|di| di.opcode.props[:saveip]}.each do |di_call|
292
+ if (obj, vtbl, index = solve_cppobj_call(@dasm, di_call))
293
+ if obj.reduce_rec.to_s == pv.to_s
294
+ res << {
295
+ clsid: clsid,
296
+ iid: iid,
297
+ context: context,
298
+ method_index: index,
299
+ call_site: di_call.address
300
+ }
301
+ end
302
+ elsif fptr = solve_guard_icall(@dasm, di_call) # TODO: check Guard Flags to detect whether cfg is enabled
303
+ if fptr.is_a?(::Metasm::Indirection)
304
+ if fptr.pointer.op == :+ &&
305
+ fptr.pointer.rexpr.is_a?(Integer) &&
306
+ fptr.pointer.lexpr.is_a?(::Metasm::Indirection)
307
+
308
+ if fptr.pointer.lexpr.pointer.reduce_rec.to_s == pv.to_s
309
+ res << {
310
+ clsid: clsid,
311
+ iid: iid,
312
+ context: context,
313
+ method_index: fptr.pointer.rexpr / (@dasm.cpu.size / 8),
314
+ call_site: di_call.address
315
+ }
316
+ end
317
+ end
318
+ end
319
+ end
320
+ end
321
+ end
322
+ end
323
+ end
324
+
325
+ res
326
+ end
327
+ end
328
+ end
329
+ end
330
+ end