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