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.
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