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,402 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'metasm'
|
4
|
+
require 'turborex/windows/constants.rb'
|
5
|
+
require 'turborex/windows/tinysdk'
|
6
|
+
require 'turborex/windows/process'
|
7
|
+
require 'win32/api' if ::OS.windows?
|
8
|
+
require 'turborex/windows/utils'
|
9
|
+
require 'turborex/windows/alpc'
|
10
|
+
# require 'turborex/windows/com'
|
11
|
+
|
12
|
+
module TurboRex
|
13
|
+
class Windows < Metasm::WinOS
|
14
|
+
class Win32API < Metasm::WinAPI
|
15
|
+
cp.lexer.warn_redefinition = false
|
16
|
+
cp.lexer.include_search_path += TurboRex::Utils.get_all_subdir(TurboRex.root + '/resources/headers/')
|
17
|
+
#dbghelp_path = ENV.fetch('DBGHELP_PATH') { File.join(TurboRex.root, '/resources/bin/x86/dbghelp.dll') }
|
18
|
+
|
19
|
+
if TurboRex::Windows::Utils.process_arch_x64?
|
20
|
+
cp.llp64
|
21
|
+
cp.lexer.define('_WIN64')
|
22
|
+
#dbghelp_path = ENV.fetch('DBGHELP64_PATH') { File.join(TurboRex.root, '/resources/bin/x64/dbghelp.dll') }
|
23
|
+
end
|
24
|
+
|
25
|
+
parse_c(File.read(TurboRex.root + '/resources/headers/tinysdk/tinysdk.h'))
|
26
|
+
|
27
|
+
new_api_c <<-EOS, 'Psapi.dll'
|
28
|
+
BOOL EnumProcesses(
|
29
|
+
DWORD *lpidProcess,
|
30
|
+
DWORD cb,
|
31
|
+
LPDWORD lpcbNeeded
|
32
|
+
);
|
33
|
+
EOS
|
34
|
+
|
35
|
+
new_api_c <<-EOS, 'Kernel32.dll'
|
36
|
+
HLOCAL
|
37
|
+
WINAPI
|
38
|
+
LocalFree(
|
39
|
+
HLOCAL hMem
|
40
|
+
);
|
41
|
+
|
42
|
+
LPVOID GlobalLock(
|
43
|
+
HGLOBAL hMem
|
44
|
+
);
|
45
|
+
|
46
|
+
BOOL GlobalUnlock(
|
47
|
+
HGLOBAL hMem
|
48
|
+
);
|
49
|
+
|
50
|
+
DWORD GetLastError();
|
51
|
+
|
52
|
+
BOOL CloseHandle(
|
53
|
+
HANDLE hObject
|
54
|
+
);
|
55
|
+
|
56
|
+
HANDLE CreateFileMappingA(
|
57
|
+
HANDLE hFile,
|
58
|
+
LPVOID lpFileMappingAttributes,
|
59
|
+
DWORD flProtect,
|
60
|
+
DWORD dwMaximumSizeHigh,
|
61
|
+
DWORD dwMaximumSizeLow,
|
62
|
+
LPCSTR lpName
|
63
|
+
);
|
64
|
+
|
65
|
+
HANDLE OpenFileMappingA(
|
66
|
+
DWORD dwDesiredAccess,
|
67
|
+
BOOL bInheritHandle,
|
68
|
+
LPCSTR lpName
|
69
|
+
);
|
70
|
+
|
71
|
+
LPVOID MapViewOfFile(
|
72
|
+
HANDLE hFileMappingObject,
|
73
|
+
DWORD dwDesiredAccess,
|
74
|
+
DWORD dwFileOffsetHigh,
|
75
|
+
DWORD dwFileOffsetLow,
|
76
|
+
SIZE_T dwNumberOfBytesToMap
|
77
|
+
);
|
78
|
+
|
79
|
+
BOOL UnmapViewOfFile(
|
80
|
+
LPCVOID lpBaseAddress
|
81
|
+
);
|
82
|
+
EOS
|
83
|
+
|
84
|
+
new_api_c <<-EOS, 'ole32.dll'
|
85
|
+
HRESULT CoInitialize(
|
86
|
+
LPVOID pvReserved
|
87
|
+
);
|
88
|
+
|
89
|
+
HRESULT CoInitializeEx(
|
90
|
+
LPVOID pvReserved,
|
91
|
+
DWORD dwCoInit
|
92
|
+
);
|
93
|
+
|
94
|
+
HRESULT CoCreateInstance(
|
95
|
+
REFCLSID rclsid,
|
96
|
+
LPUNKNOWN pUnkOuter,
|
97
|
+
DWORD dwClsContext,
|
98
|
+
REFIID riid,
|
99
|
+
LPVOID *ppv
|
100
|
+
);
|
101
|
+
|
102
|
+
HRESULT CoGetClassObject(
|
103
|
+
REFCLSID rclsid,
|
104
|
+
DWORD dwClsContext,
|
105
|
+
LPVOID pvReserved,
|
106
|
+
REFIID riid,
|
107
|
+
LPVOID *ppv
|
108
|
+
);
|
109
|
+
|
110
|
+
HRESULT CoGetPSClsid(
|
111
|
+
REFIID riid,
|
112
|
+
CLSID *pClsid
|
113
|
+
);
|
114
|
+
|
115
|
+
HRESULT CoMarshalInterface(
|
116
|
+
LPSTREAM pStm,
|
117
|
+
REFIID riid,
|
118
|
+
LPUNKNOWN pUnk,
|
119
|
+
DWORD dwDestContext,
|
120
|
+
LPVOID pvDestContext,
|
121
|
+
DWORD mshlflags
|
122
|
+
);
|
123
|
+
|
124
|
+
HRESULT CoUnmarshalInterface(
|
125
|
+
LPSTREAM pStm,
|
126
|
+
REFIID riid,
|
127
|
+
LPVOID *ppv
|
128
|
+
);
|
129
|
+
|
130
|
+
HRESULT CreateStreamOnHGlobal(
|
131
|
+
HGLOBAL hGlobal,
|
132
|
+
BOOL fDeleteOnRelease,
|
133
|
+
LPSTREAM *ppstm
|
134
|
+
);
|
135
|
+
|
136
|
+
HRESULT GetHGlobalFromStream(
|
137
|
+
LPSTREAM pstm,
|
138
|
+
HGLOBAL *phglobal
|
139
|
+
);
|
140
|
+
|
141
|
+
HRESULT CoGetMarshalSizeMax(
|
142
|
+
ULONG *pulSize,
|
143
|
+
REFIID riid,
|
144
|
+
LPUNKNOWN pUnk,
|
145
|
+
DWORD dwDestContext,
|
146
|
+
LPVOID pvDestContext,
|
147
|
+
DWORD mshlflags
|
148
|
+
);
|
149
|
+
|
150
|
+
HRESULT CLSIDFromString(
|
151
|
+
LPCOLESTR lpsz,
|
152
|
+
LPCLSID pclsid
|
153
|
+
);
|
154
|
+
|
155
|
+
HRESULT StgCreateDocfile(
|
156
|
+
const WCHAR *pwcsName,
|
157
|
+
DWORD grfMode,
|
158
|
+
DWORD reserved,
|
159
|
+
IStorage **ppstgOpen
|
160
|
+
);
|
161
|
+
EOS
|
162
|
+
|
163
|
+
new_api_c <<-EOS, 'OleAut32.dll'
|
164
|
+
BSTR SysAllocString(
|
165
|
+
const OLECHAR *psz
|
166
|
+
);
|
167
|
+
|
168
|
+
void SysFreeString(
|
169
|
+
BSTR bstrString
|
170
|
+
);
|
171
|
+
EOS
|
172
|
+
|
173
|
+
new_api_c <<-EOS, 'dbghelp.dll'
|
174
|
+
#define IMAGEAPI __stdcall
|
175
|
+
#define SYMOPT_CASE_INSENSITIVE 0x00000001
|
176
|
+
#define SYMOPT_UNDNAME 0x00000002
|
177
|
+
#define SYMOPT_DEFERRED_LOADS 0x00000004
|
178
|
+
#define SYMOPT_NO_CPP 0x00000008
|
179
|
+
#define SYMOPT_LOAD_LINES 0x00000010
|
180
|
+
#define SYMOPT_OMAP_FIND_NEAREST 0x00000020
|
181
|
+
#define SYMOPT_LOAD_ANYTHING 0x00000040
|
182
|
+
#define SYMOPT_IGNORE_CVREC 0x00000080
|
183
|
+
#define SYMOPT_NO_UNQUALIFIED_LOADS 0x00000100
|
184
|
+
#define SYMOPT_FAIL_CRITICAL_ERRORS 0x00000200
|
185
|
+
#define SYMOPT_EXACT_SYMBOLS 0x00000400
|
186
|
+
#define SYMOPT_ALLOW_ABSOLUTE_SYMBOLS 0x00000800
|
187
|
+
#define SYMOPT_IGNORE_NT_SYMPATH 0x00001000
|
188
|
+
#define SYMOPT_INCLUDE_32BIT_MODULES 0x00002000
|
189
|
+
#define SYMOPT_PUBLICS_ONLY 0x00004000
|
190
|
+
#define SYMOPT_NO_PUBLICS 0x00008000
|
191
|
+
#define SYMOPT_AUTO_PUBLICS 0x00010000
|
192
|
+
#define SYMOPT_NO_IMAGE_SEARCH 0x00020000
|
193
|
+
#define SYMOPT_SECURE 0x00040000
|
194
|
+
#define SYMOPT_NO_PROMPTS 0x00080000
|
195
|
+
#define SYMOPT_DEBUG 0x80000000
|
196
|
+
|
197
|
+
typedef int BOOL;
|
198
|
+
typedef char CHAR;
|
199
|
+
typedef unsigned long DWORD;
|
200
|
+
typedef unsigned __int64 DWORD64;
|
201
|
+
typedef void *HANDLE;
|
202
|
+
typedef unsigned __int64 *PDWORD64;
|
203
|
+
typedef void *PVOID;
|
204
|
+
typedef unsigned long ULONG;
|
205
|
+
typedef unsigned __int64 ULONG64;
|
206
|
+
typedef const CHAR *PCSTR;
|
207
|
+
typedef CHAR *PSTR;
|
208
|
+
typedef struct _SYMBOL_INFO *PSYMBOL_INFO;
|
209
|
+
typedef __stdcall BOOL (*PSYM_ENUMERATESYMBOLS_CALLBACK)(PSYMBOL_INFO pSymInfo, ULONG SymbolSize, PVOID UserContext);
|
210
|
+
typedef enum
|
211
|
+
{
|
212
|
+
SymNone = 0,
|
213
|
+
SymCoff,
|
214
|
+
SymCv,
|
215
|
+
SymPdb,
|
216
|
+
SymExport,
|
217
|
+
SymDeferred,
|
218
|
+
SymSym,
|
219
|
+
SymDia,
|
220
|
+
SymVirtual,
|
221
|
+
NumSymTypes
|
222
|
+
} SYM_TYPE;
|
223
|
+
|
224
|
+
typedef struct _SYMBOL_INFO {
|
225
|
+
ULONG SizeOfStruct;
|
226
|
+
ULONG TypeIndex;
|
227
|
+
ULONG64 Reserved[2];
|
228
|
+
ULONG info;
|
229
|
+
ULONG Size;
|
230
|
+
ULONG64 ModBase;
|
231
|
+
ULONG Flags;
|
232
|
+
ULONG64 Value;
|
233
|
+
ULONG64 Address;
|
234
|
+
ULONG Register;
|
235
|
+
ULONG Scope;
|
236
|
+
ULONG Tag;
|
237
|
+
ULONG NameLen;
|
238
|
+
ULONG MaxNameLen;
|
239
|
+
CHAR Name[1];
|
240
|
+
} SYMBOL_INFO, *PSYMBOL_INFO;
|
241
|
+
|
242
|
+
typedef struct _MODLOAD_DATA {
|
243
|
+
DWORD ssize;
|
244
|
+
DWORD ssig;
|
245
|
+
PVOID data;
|
246
|
+
DWORD size;
|
247
|
+
DWORD flags;
|
248
|
+
} MODLOAD_DATA, *PMODLOAD_DATA;
|
249
|
+
|
250
|
+
|
251
|
+
__stdcall DWORD SymGetOptions(void);
|
252
|
+
__stdcall DWORD SymSetOptions(DWORD SymOptions __attribute__((in)));
|
253
|
+
__stdcall BOOL SymInitialize(HANDLE hProcess __attribute__((in)), PSTR UserSearchPath __attribute__((in)), BOOL fInvadeProcess __attribute__((in)));
|
254
|
+
__stdcall DWORD64 SymLoadModule64(HANDLE hProcess __attribute__((in)), HANDLE hFile __attribute__((in)), PSTR ImageName __attribute__((in)), PSTR ModuleName __attribute__((in)), DWORD64 BaseOfDll __attribute__((in)), DWORD SizeOfDll __attribute__((in)));
|
255
|
+
__stdcall BOOL SymSetSearchPath(HANDLE hProcess __attribute__((in)), PSTR SearchPathA __attribute__((in)));
|
256
|
+
__stdcall BOOL SymFromAddr(HANDLE hProcess __attribute__((in)), DWORD64 Address __attribute__((in)), PDWORD64 Displacement __attribute__((out)), PSYMBOL_INFO Symbol __attribute__((in)) __attribute__((out)));
|
257
|
+
__stdcall BOOL SymEnumSymbols(HANDLE hProcess __attribute__((in)), ULONG64 BaseOfDll __attribute__((in)), PCSTR Mask __attribute__((in)), PSYM_ENUMERATESYMBOLS_CALLBACK EnumSymbolsCallback __attribute__((in)), PVOID UserContext __attribute__((in)));
|
258
|
+
|
259
|
+
DWORD64 IMAGEAPI SymLoadModuleEx(
|
260
|
+
HANDLE hProcess,
|
261
|
+
HANDLE hFile,
|
262
|
+
PCSTR ImageName,
|
263
|
+
PCSTR ModuleName,
|
264
|
+
DWORD64 BaseOfDll,
|
265
|
+
DWORD DllSize,
|
266
|
+
PMODLOAD_DATA Data,
|
267
|
+
DWORD Flags
|
268
|
+
);
|
269
|
+
|
270
|
+
BOOL IMAGEAPI SymFromName(
|
271
|
+
HANDLE hProcess,
|
272
|
+
PCSTR Name,
|
273
|
+
PSYMBOL_INFO Symbol
|
274
|
+
);
|
275
|
+
|
276
|
+
BOOL
|
277
|
+
IMAGEAPI
|
278
|
+
SymGetModuleInfo64(
|
279
|
+
HANDLE hProcess,
|
280
|
+
DWORD64 qwAddr,
|
281
|
+
PIMAGEHLP_MODULE64 ModuleInfo
|
282
|
+
);
|
283
|
+
|
284
|
+
BOOL
|
285
|
+
IMAGEAPI
|
286
|
+
SymGetModuleInfoW64(
|
287
|
+
HANDLE hProcess,
|
288
|
+
DWORD64 qwAddr,
|
289
|
+
PIMAGEHLP_MODULEW64 ModuleInfo
|
290
|
+
);
|
291
|
+
|
292
|
+
#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
|
293
|
+
#define SymGetModuleInfo SymGetModuleInfo64
|
294
|
+
#define SymGetModuleInfoW SymGetModuleInfoW64
|
295
|
+
#else
|
296
|
+
BOOL
|
297
|
+
IMAGEAPI
|
298
|
+
SymGetModuleInfo(
|
299
|
+
HANDLE hProcess,
|
300
|
+
DWORD dwAddr,
|
301
|
+
PIMAGEHLP_MODULE ModuleInfo
|
302
|
+
);
|
303
|
+
|
304
|
+
BOOL
|
305
|
+
IMAGEAPI
|
306
|
+
SymGetModuleInfoW(
|
307
|
+
HANDLE hProcess,
|
308
|
+
DWORD dwAddr,
|
309
|
+
PIMAGEHLP_MODULEW ModuleInfo
|
310
|
+
);
|
311
|
+
#endif
|
312
|
+
EOS
|
313
|
+
|
314
|
+
new_api_c <<-EOS, 'Advapi32.dll'
|
315
|
+
DWORD GetSecurityDescriptorLength(
|
316
|
+
PSECURITY_DESCRIPTOR pSecurityDescriptor
|
317
|
+
);
|
318
|
+
|
319
|
+
BOOL GetSecurityDescriptorDacl(
|
320
|
+
PSECURITY_DESCRIPTOR pSecurityDescriptor,
|
321
|
+
LPBOOL lpbDaclPresent,
|
322
|
+
PACL *pDacl,
|
323
|
+
LPBOOL lpbDaclDefaulted
|
324
|
+
);
|
325
|
+
|
326
|
+
BOOL GetSecurityDescriptorControl(
|
327
|
+
PSECURITY_DESCRIPTOR pSecurityDescriptor,
|
328
|
+
PSECURITY_DESCRIPTOR_CONTROL pControl,
|
329
|
+
LPDWORD lpdwRevision
|
330
|
+
);
|
331
|
+
|
332
|
+
BOOL GetSecurityDescriptorOwner(
|
333
|
+
PSECURITY_DESCRIPTOR pSecurityDescriptor,
|
334
|
+
PSID *pOwner,
|
335
|
+
LPBOOL lpbOwnerDefaulted
|
336
|
+
);
|
337
|
+
|
338
|
+
BOOL GetSecurityDescriptorGroup(
|
339
|
+
PSECURITY_DESCRIPTOR pSecurityDescriptor,
|
340
|
+
PSID *pGroup,
|
341
|
+
LPBOOL lpbGroupDefaulted
|
342
|
+
);
|
343
|
+
|
344
|
+
BOOL ConvertSidToStringSidA(
|
345
|
+
PSID Sid,
|
346
|
+
LPSTR *StringSid
|
347
|
+
);
|
348
|
+
|
349
|
+
BOOL ConvertStringSidToSidW(
|
350
|
+
LPCWSTR StringSid,
|
351
|
+
PSID *Sid
|
352
|
+
);
|
353
|
+
|
354
|
+
BOOL GetAclInformation(
|
355
|
+
PACL pAcl,
|
356
|
+
LPVOID pAclInformation,
|
357
|
+
DWORD nAclInformationLength,
|
358
|
+
ACL_INFORMATION_CLASS dwAclInformationClass
|
359
|
+
);
|
360
|
+
|
361
|
+
BOOL GetAce(
|
362
|
+
PACL pAcl,
|
363
|
+
DWORD dwAceIndex,
|
364
|
+
LPVOID *pAce
|
365
|
+
);
|
366
|
+
EOS
|
367
|
+
end
|
368
|
+
|
369
|
+
class Thread < Metasm::WinOS
|
370
|
+
end
|
371
|
+
|
372
|
+
class Token
|
373
|
+
end
|
374
|
+
|
375
|
+
def self.open_process(pid, mask = Metasm::WinAPI::PROCESS_QUERY_INFORMATION)
|
376
|
+
if handle = Metasm::WinAPI.openprocess(mask, 0, pid)
|
377
|
+
return open_process_handle(handle)
|
378
|
+
end
|
379
|
+
|
380
|
+
nil
|
381
|
+
end
|
382
|
+
|
383
|
+
def self.open_process_handle(handle)
|
384
|
+
pid = begin
|
385
|
+
WinAPI.getprocessid(handle)
|
386
|
+
rescue StandardError
|
387
|
+
0
|
388
|
+
end
|
389
|
+
TurboRex::Windows::Process.new(pid, handle)
|
390
|
+
end
|
391
|
+
|
392
|
+
def self.list_all_process_pid
|
393
|
+
lpidProcess = Win32API.alloc_c_ary('DWORD', 1024)
|
394
|
+
cb = 1024
|
395
|
+
lpcbNeeded = 0
|
396
|
+
|
397
|
+
Win32API.enumprocesses(lpidProcess, cb, lpcbNeeded)
|
398
|
+
|
399
|
+
lpidProcess
|
400
|
+
end
|
401
|
+
end
|
402
|
+
end
|
@@ -0,0 +1,844 @@
|
|
1
|
+
warn "\033[33m[-]Warning: This module doesn't currently work on non-Windows os.\033[0m" unless OS.windows?
|
2
|
+
|
3
|
+
module TurboRex
|
4
|
+
class Windows < Metasm::WinOS
|
5
|
+
module ALPC
|
6
|
+
include ::TurboRex::Windows::Constants
|
7
|
+
|
8
|
+
PORMSG_PAD = 0x100
|
9
|
+
|
10
|
+
ALPC_MSGFLG_REPLY_MESSAGE = 0x1
|
11
|
+
ALPC_MSGFLG_LPC_MODE = 0x2
|
12
|
+
ALPC_MSGFLG_RELEASE_MESSAGE = 0x10000
|
13
|
+
ALPC_MSGFLG_SYNC_REQUEST = 0x20000
|
14
|
+
ALPC_MSGFLG_WAIT_USER_MODE = 0x100000
|
15
|
+
ALPC_MSGFLG_WAIT_ALERTABLE = 0x200000
|
16
|
+
ALPC_MSGFLG_WOW64_CALL = 0x80000000
|
17
|
+
|
18
|
+
ALPC_MESSAGE_SECURITY_ATTRIBUTE = 0x80000000
|
19
|
+
ALPC_MESSAGE_VIEW_ATTRIBUTE = 0x40000000
|
20
|
+
ALPC_MESSAGE_CONTEXT_ATTRIBUTE = 0x20000000
|
21
|
+
ALPC_MESSAGE_HANDLE_ATTRIBUTE = 0x10000000
|
22
|
+
ALPC_MESSAGE_TOKEN_ATTRIBUTE = 0x8000000
|
23
|
+
ALPC_MESSAGE_DIRECT_ATTRIBUTE = 0x4000000
|
24
|
+
ALPC_MESSAGE_WORK_ON_BEHALF_ATTRIBUTE = 0x2000000
|
25
|
+
|
26
|
+
ALPC_PORFLG_ALLOW_LPC_REQUESTS = 0x20000
|
27
|
+
|
28
|
+
def self.const_missing(name)
|
29
|
+
super(name) unless APIProxy.initialized?
|
30
|
+
const = APIProxy.cp.numeric_constants.assoc(name.to_s)
|
31
|
+
super(name) if const.nil?
|
32
|
+
|
33
|
+
const[1]
|
34
|
+
end
|
35
|
+
|
36
|
+
class APIProxy < Metasm::WinAPI
|
37
|
+
def self.init(cpu = Metasm::Ia32)
|
38
|
+
if @initialized
|
39
|
+
return true
|
40
|
+
end
|
41
|
+
|
42
|
+
opts = {}
|
43
|
+
opts[:cpu] = cpu
|
44
|
+
opts[:include_path] = [TurboRex.root + "/resources/headers/alpc"]
|
45
|
+
opts[:visual_studio] = true
|
46
|
+
opts[:data_model] = 'llp64' if cpu == Metasm::X86_64
|
47
|
+
opts[:predefined] = true
|
48
|
+
|
49
|
+
@np = TurboRex::CStruct::NativeParser.new(nil, opts)
|
50
|
+
@cp = @np.parser
|
51
|
+
@cp.parse("#define NT_VERSION #{TurboRex::Windows.version.join}")
|
52
|
+
@cp.parse_file TurboRex.root + '/resources/headers/alpc/ntlpcapi.h'
|
53
|
+
new_api_c('ntdll.dll')
|
54
|
+
|
55
|
+
@initialized = true
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.reload(cpu = Metasm::Ia32)
|
59
|
+
@initialized = false
|
60
|
+
init(cpu)
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.initialized?
|
64
|
+
@initialized
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.new_api_c(fromlib = nil)
|
68
|
+
cp.toplevel.symbol.dup.each_value { |v|
|
69
|
+
next if not v.kind_of? Metasm::C::Variable # enums
|
70
|
+
cp.toplevel.symbol.delete v.name
|
71
|
+
lib = fromlib || lib_from_sym(v.name)
|
72
|
+
addr = sym_addr(lib, v.name)
|
73
|
+
if addr == 0 or addr == -1 or addr == 0xffff_ffff or addr == 0xffffffff_ffffffff
|
74
|
+
api_not_found(lib, v)
|
75
|
+
next
|
76
|
+
end
|
77
|
+
|
78
|
+
rbname = c_func_name_to_rb(v.name)
|
79
|
+
if not v.type.kind_of? Metasm::C::Function
|
80
|
+
class << self;
|
81
|
+
self;
|
82
|
+
end.send(:define_method, rbname) { addr }
|
83
|
+
next
|
84
|
+
end
|
85
|
+
|
86
|
+
next if v.initializer
|
87
|
+
|
88
|
+
|
89
|
+
new_caller_for(v, rbname, addr)
|
90
|
+
}
|
91
|
+
|
92
|
+
|
93
|
+
cexist = constants.inject({}) { |h, c| h.update c.to_s => true }
|
94
|
+
cp.toplevel.symbol.each { |k, v|
|
95
|
+
if v.kind_of? ::Integer
|
96
|
+
n = c_const_name_to_rb(k)
|
97
|
+
const_set(n, v) if v.kind_of? Integer and not cexist[n]
|
98
|
+
end
|
99
|
+
}
|
100
|
+
|
101
|
+
cp.lexer.definition.each_key { |k|
|
102
|
+
n = c_const_name_to_rb(k)
|
103
|
+
if not cexist[n] and Object.const_defined?(n) and v = @cp.macro_numeric(n)
|
104
|
+
const_set(n, v)
|
105
|
+
end
|
106
|
+
}
|
107
|
+
end
|
108
|
+
|
109
|
+
def self.np
|
110
|
+
@np
|
111
|
+
end
|
112
|
+
|
113
|
+
def self.alloc_c_type(typename, init_value = 0)
|
114
|
+
alloc_c_ary(typename, [init_value])
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
class Transport
|
119
|
+
def initialize(opts = {})
|
120
|
+
@conn_handle = nil
|
121
|
+
@communication_handle = []
|
122
|
+
end
|
123
|
+
|
124
|
+
def listen(conn_handle, opts = {}, &block)
|
125
|
+
@conn_handle = conn_handle
|
126
|
+
#port_message = APIProxy.alloc_c_struct('PORT_MESSAGE')
|
127
|
+
port_message = APIProxy.alloc_c_ary('BYTE', 0x1000)
|
128
|
+
message_attr = MessageAttribute.new.struct
|
129
|
+
buf_len = APIProxy.alloc_c_type('SIZE_T')
|
130
|
+
buf_len[0] = port_message.sizeof
|
131
|
+
retry_count = 0
|
132
|
+
|
133
|
+
while true
|
134
|
+
begin
|
135
|
+
# call NtAlpcSendWaitReceivePort will cause interpreter blocks, until you kill it.
|
136
|
+
ntstatus = APIProxy.ntalpcsendwaitreceiveport(@conn_handle,
|
137
|
+
opts[:flag] || 0,
|
138
|
+
0,
|
139
|
+
0,
|
140
|
+
port_message,
|
141
|
+
buf_len,
|
142
|
+
message_attr,
|
143
|
+
0)
|
144
|
+
yield(port_message, buf_len, message_attr, TinySDK.format_hex_ntstatus(ntstatus, hex_str: true)) if block_given?
|
145
|
+
unless TinySDK.nt_success? ntstatus
|
146
|
+
unless buf_len[0] == port_message.sizeof
|
147
|
+
port_message = APIProxy.alloc_c_ary('BYTE', buf_len[0])
|
148
|
+
raise TurboRex::Exception::ALPC::BufferTooSmall
|
149
|
+
else
|
150
|
+
raise
|
151
|
+
end
|
152
|
+
end
|
153
|
+
rescue => e
|
154
|
+
if e.is_a? TurboRex::Exception::ALPC::BufferTooSmall
|
155
|
+
raise TurboRex::Exception::ALPC::TooManyRetries if retry_count >= 2
|
156
|
+
retry_count += 1
|
157
|
+
retry
|
158
|
+
else
|
159
|
+
raise TurboRex::Exception::UnknownError
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
kport_message = PortMessage.new(raw_message: port_message)
|
164
|
+
break if (kport_message.type & 0xFFF) == TurboRex::Windows::ALPC::LPC_CONNECTION_REQUEST
|
165
|
+
end
|
166
|
+
|
167
|
+
return kport_message
|
168
|
+
end
|
169
|
+
|
170
|
+
def send(handle, port_message, message_attr, opts = {})
|
171
|
+
flag = opts[:flag] || 0
|
172
|
+
timeout = opts[:timeout] || 0
|
173
|
+
#buf_len = opts.fetch(:buf_len) { APIProxy.alloc_c_type('SIZE_T') }
|
174
|
+
#buf_len = port_message.sizeof
|
175
|
+
|
176
|
+
ntstatus = APIProxy.ntalpcsendwaitreceiveport(handle, flag, port_message, message_attr, 0, 0, 0, timeout)
|
177
|
+
unless TinySDK.nt_success?(ntstatus)
|
178
|
+
raise TurboRex::Exception::NotNTSuccess.new TinySDK.format_hex_ntstatus(ntstatus, hex_str: true)
|
179
|
+
end
|
180
|
+
|
181
|
+
TinySDK.format_hex_ntstatus ntstatus
|
182
|
+
end
|
183
|
+
|
184
|
+
|
185
|
+
def recv(handle, opts = {})
|
186
|
+
port_message = opts.fetch(:port_message) { APIProxy.alloc_c_ary('BYTE', 0x1000) }
|
187
|
+
buf_len = opts.fetch(:buf_len) { APIProxy.alloc_c_type('SIZE_T') }
|
188
|
+
buf_len[0] = port_message.sizeof
|
189
|
+
#message_attr = opts.fetch(:message_attr) { APIProxy.alloc_c_struct('ALPC_MESSAGE_ATTRIBUTES') }
|
190
|
+
message_attr = MessageAttribute.new.struct
|
191
|
+
flag = opts[:flag] || 0
|
192
|
+
retry_count = opts[:retry_count] || 0
|
193
|
+
timeout = opts[:timeout] || 0
|
194
|
+
|
195
|
+
begin
|
196
|
+
ntstatus = APIProxy.ntalpcsendwaitreceiveport(handle, flag, 0, 0, port_message, buf_len, message_attr, timeout)
|
197
|
+
unless TinySDK.nt_success? ntstatus
|
198
|
+
unless buf_len[0] == port_message.sizeof
|
199
|
+
port_message = APIProxy.alloc_c_ary('BYTE', buf_len[0])
|
200
|
+
raise TurboRex::Exception::ALPC::BufferTooSmall
|
201
|
+
else
|
202
|
+
raise
|
203
|
+
end
|
204
|
+
end
|
205
|
+
rescue => e
|
206
|
+
if e.is_a? TurboRex::Exception::ALPC::BufferTooSmall
|
207
|
+
raise TurboRex::Exception::ALPC::TooManyRetries if retry_count >= 2
|
208
|
+
retry_count += 1
|
209
|
+
retry
|
210
|
+
else
|
211
|
+
raise TurboRex::Exception::NotNTSuccess.new(TinySDK.format_hex_ntstatus(ntstatus, hex_str: true))
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
PortMessage.new(raw_message: port_message)
|
216
|
+
end
|
217
|
+
|
218
|
+
def send_recv(handle, send_message, send_message_attr, recv_message_attr, opts = {})
|
219
|
+
port_message = opts.fetch(:port_message) { APIProxy.alloc_c_ary('BYTE', 0x1000) }
|
220
|
+
buf_len = opts.fetch(:buf_len) { APIProxy.alloc_c_type('SIZE_T') }
|
221
|
+
buf_len[0] = port_message.sizeof
|
222
|
+
message_attr = recv_message_attr
|
223
|
+
flag = opts[:flag] || TurboRex::Windows::ALPC::ALPC_MSGFLG_SYNC_REQUEST
|
224
|
+
retry_count = opts[:retry_count] || 0
|
225
|
+
timeout = opts[:timeout] || 0
|
226
|
+
|
227
|
+
begin
|
228
|
+
ntstatus = APIProxy.ntalpcsendwaitreceiveport(handle,
|
229
|
+
flag,
|
230
|
+
send_message,
|
231
|
+
send_message_attr,
|
232
|
+
port_message,
|
233
|
+
buf_len,
|
234
|
+
recv_message_attr,
|
235
|
+
timeout)
|
236
|
+
unless TinySDK.nt_success? ntstatus
|
237
|
+
unless buf_len[0] == port_message.sizeof
|
238
|
+
port_message = APIProxy.alloc_c_ary('BYTE', buf_len[0])
|
239
|
+
raise TurboRex::Exception::ALPC::BufferTooSmall
|
240
|
+
else
|
241
|
+
raise
|
242
|
+
end
|
243
|
+
end
|
244
|
+
rescue => e
|
245
|
+
if e.is_a? TurboRex::Exception::ALPC::BufferTooSmall
|
246
|
+
raise TurboRex::Exception::ALPC::TooManyRetries if retry_count >= 2
|
247
|
+
retry_count += 1
|
248
|
+
retry
|
249
|
+
else
|
250
|
+
raise TurboRex::Exception::NotNTSuccess.new(TinySDK.format_hex_ntstatus(ntstatus, hex_str: true))
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
PortMessage.new(raw_message: port_message)
|
255
|
+
end
|
256
|
+
|
257
|
+
def connect(opts = {}, &block)
|
258
|
+
unless wport_name = TurboRex::Windows::Utils.multibyte_to_widechar(opts[:port_name])
|
259
|
+
raise "Unable to convert characters to utf-16le encoding."
|
260
|
+
end
|
261
|
+
|
262
|
+
dest_str = APIProxy.alloc_c_ptr('UNICODE_STRING')
|
263
|
+
APIProxy.rtlinitunicodestring(dest_str, wport_name)
|
264
|
+
|
265
|
+
handle = APIProxy.alloc_c_type('HANDLE')
|
266
|
+
alpc_port_attr = APIProxy.alloc_c_struct('ALPC_PORT_ATTRIBUTES')
|
267
|
+
alpc_port_attr.Flags = TurboRex::Windows::ALPC::ALPC_PORFLG_ALLOW_LPC_REQUESTS
|
268
|
+
alpc_port_attr.MaxMessageLength = 0x1000
|
269
|
+
alpc_port_attr.MemoryBandwidth = 0
|
270
|
+
alpc_port_attr.MaxPoolUsage = 0xFFFFFFFF
|
271
|
+
alpc_port_attr.MaxSectionSize = 0xFFFFFFFF
|
272
|
+
alpc_port_attr.MaxViewSize = 0xFFFFFFFF
|
273
|
+
alpc_port_attr.MaxTotalSectionSize = 0xFFFFFFFF
|
274
|
+
alpc_port_attr.DupObjectTypes = 0xFFFFFFFF
|
275
|
+
|
276
|
+
alpc_port_attr.SecurityQos.Length = alpc_port_attr.SecurityQos.sizeof
|
277
|
+
alpc_port_attr.SecurityQos.ImpersonationLevel = opts[:impersonation_level] ||
|
278
|
+
TurboRex::Windows::Constants::SecurityIdentification
|
279
|
+
|
280
|
+
# timeout
|
281
|
+
#large_integer = APIProxy.alloc_c_struct('LARGE_INTEGER')
|
282
|
+
#large_integer.HighPart
|
283
|
+
#large_integer.LowPart
|
284
|
+
|
285
|
+
kport_message = PortMessage.new(payload: opts[:payload], alloc_size: (opts[:alloc_size]||3800))
|
286
|
+
obj_attr = opts[:obj_attr] || 0
|
287
|
+
flags = opts[:flags] || TurboRex::Windows::ALPC::ALPC_MSGFLG_SYNC_REQUEST # Don't use the ALPC_MSGFLG_SYNC_REQUEST flag when specific attributes
|
288
|
+
timeout = opts[:timeout] || 0
|
289
|
+
retry_count = opts[:retry_count] || 0
|
290
|
+
|
291
|
+
buf_len = nil
|
292
|
+
if message_size = kport_message.message_size
|
293
|
+
buf_len = APIProxy.alloc_c_type('SIZE_T')
|
294
|
+
buf_len[0] = message_size
|
295
|
+
end
|
296
|
+
|
297
|
+
out_msg_attr = 0
|
298
|
+
in_msg_attr = 0
|
299
|
+
|
300
|
+
if opts[:client_obj_attr] # perform to call NtAlpcConnectPortEx
|
301
|
+
raise NotImplementedError
|
302
|
+
else
|
303
|
+
ntstatus = APIProxy.ntalpcconnectport(handle, dest_str, obj_attr, alpc_port_attr, flags, 0, kport_message.message,
|
304
|
+
buf_len, out_msg_attr, in_msg_attr, timeout)
|
305
|
+
kport_message.refresh_message
|
306
|
+
formatted_status = TinySDK.format_hex_ntstatus(ntstatus)
|
307
|
+
if formatted_status == 0xC0000041
|
308
|
+
puts "[-] The server refused the connection.(STATUS_PORT_CONNECTION_REFUSED)"
|
309
|
+
retry_count.times do |i|
|
310
|
+
puts "[*] Retrying..."
|
311
|
+
ntstatus = APIProxy.ntalpcconnectport(handle, dest_str, obj_attr, alpc_port_attr, flags, 0, kport_message.message,
|
312
|
+
buf_len, out_msg_attr, in_msg_attr, timeout)
|
313
|
+
break unless TinySDK.format_hex_ntstatus(ntstatus) == 0xC0000041
|
314
|
+
end
|
315
|
+
elsif !TinySDK.nt_success?(ntstatus)
|
316
|
+
raise TurboRex::Exception::NotNTSuccess.new("0x#{formatted_status.to_s(16).upcase}")
|
317
|
+
end
|
318
|
+
end
|
319
|
+
|
320
|
+
@communication_handle << handle[0]
|
321
|
+
return handle[0], kport_message
|
322
|
+
end
|
323
|
+
|
324
|
+
def accept(opts = {})
|
325
|
+
communication_handle = APIProxy.alloc_c_type('HANDLE')
|
326
|
+
alpc_port_attr = APIProxy.alloc_c_struct('ALPC_PORT_ATTRIBUTES')
|
327
|
+
alpc_port_attr.Flags = 0
|
328
|
+
alpc_port_attr.MaxMessageLength = 0x1000
|
329
|
+
alpc_port_attr.MemoryBandwidth = 0
|
330
|
+
alpc_port_attr.MaxPoolUsage = 0xFFFFFFFF
|
331
|
+
alpc_port_attr.MaxSectionSize = 0xFFFFFFFF
|
332
|
+
alpc_port_attr.MaxViewSize = 0xFFFFFFFF
|
333
|
+
alpc_port_attr.MaxTotalSectionSize = 0xFFFFFFFF
|
334
|
+
alpc_port_attr.DupObjectTypes = 0xFFFFFFFF
|
335
|
+
|
336
|
+
port_context = opts[:port_context] || 0
|
337
|
+
flags = opts[:flags] || 0
|
338
|
+
uniq_process = opts[:uniq_process]
|
339
|
+
uniq_thread = opts[:uniq_thread]
|
340
|
+
message_id = opts[:message_id]
|
341
|
+
accept = 1
|
342
|
+
port_message = opts[:port_message]
|
343
|
+
|
344
|
+
if port_message.nil?
|
345
|
+
raise TurboRex::Exception::ALPC::ReplyMessageMismatch if uniq_process.nil? || uniq_thread.nil? || message_id.nil?
|
346
|
+
port_message = PortMessage.new(alloc_size: 1)
|
347
|
+
port_message.client_id = [uniq_process, uniq_thread]
|
348
|
+
port_message.message_id = message_id
|
349
|
+
end
|
350
|
+
|
351
|
+
accept = 0 if opts[:refuse]
|
352
|
+
|
353
|
+
ntstatus = APIProxy.ntalpcacceptconnectport(communication_handle,
|
354
|
+
@conn_handle,
|
355
|
+
flags,
|
356
|
+
0,
|
357
|
+
alpc_port_attr,
|
358
|
+
port_context,
|
359
|
+
port_message.message,
|
360
|
+
0,
|
361
|
+
accept)
|
362
|
+
|
363
|
+
unless opts[:refuse]
|
364
|
+
@communication_handle << communication_handle[0]
|
365
|
+
[communication_handle[0], TinySDK.format_hex_ntstatus(ntstatus)]
|
366
|
+
else
|
367
|
+
TinySDK.format_hex_ntstatus(ntstatus)
|
368
|
+
end
|
369
|
+
end
|
370
|
+
|
371
|
+
def refuse_connect(opts = {})
|
372
|
+
opts[:refuse] = true
|
373
|
+
accept(opts)
|
374
|
+
end
|
375
|
+
|
376
|
+
def close
|
377
|
+
APIProxy.ntalpcdisconnectport(@conn_handle, 0)
|
378
|
+
Metasm::WinAPI.closehandle @conn_handle
|
379
|
+
@conn_handle = nil
|
380
|
+
@communication_handle = []
|
381
|
+
end
|
382
|
+
end
|
383
|
+
|
384
|
+
class PortMessage
|
385
|
+
class ClientID
|
386
|
+
attr_accessor :unique_thread
|
387
|
+
attr_accessor :unique_process
|
388
|
+
|
389
|
+
def initialize(unique_process, unique_thread)
|
390
|
+
@unique_process = unique_process
|
391
|
+
@unique_thread = unique_thread
|
392
|
+
end
|
393
|
+
|
394
|
+
def to_struct
|
395
|
+
client_id = APIProxy.alloc_c_struct('CLIENT_ID')
|
396
|
+
client_id.UniqueProcess = @unique_process
|
397
|
+
client_id.UniqueThread = @unique_thread
|
398
|
+
client_id
|
399
|
+
end
|
400
|
+
end
|
401
|
+
|
402
|
+
attr_reader :message_size
|
403
|
+
attr_reader :buf_size
|
404
|
+
attr_reader :header_size
|
405
|
+
attr_reader :message
|
406
|
+
attr_reader :payload
|
407
|
+
attr_reader :payload_size
|
408
|
+
attr_accessor :total_length
|
409
|
+
attr_accessor :data_length
|
410
|
+
attr_reader :header
|
411
|
+
attr_accessor :attributes
|
412
|
+
|
413
|
+
# header data member
|
414
|
+
attr_reader :length
|
415
|
+
attr_reader :type
|
416
|
+
attr_reader :data_info_offset
|
417
|
+
attr_reader :zero_init
|
418
|
+
attr_reader :client_id
|
419
|
+
attr_reader :do_not_use_this_field
|
420
|
+
attr_reader :message_id
|
421
|
+
attr_reader :client_view_size
|
422
|
+
attr_reader :callback_id
|
423
|
+
|
424
|
+
def initialize(opts = {})
|
425
|
+
raw_message = opts[:raw_message]
|
426
|
+
payload = opts[:payload]
|
427
|
+
@payload = payload
|
428
|
+
@attributes = MessageAttribute.new.struct
|
429
|
+
|
430
|
+
if raw_message
|
431
|
+
perform_raw_message raw_message
|
432
|
+
elsif payload
|
433
|
+
port_message = opts[:port_message]
|
434
|
+
@header = (port_message ||= APIProxy.alloc_c_struct('PORT_MESSAGE'))
|
435
|
+
set_header
|
436
|
+
#@message_size = @header_size = port_message.sizeof
|
437
|
+
if payload.is_a? String
|
438
|
+
pure_set_msg payload, payload.bytesize
|
439
|
+
elsif payload.is_a? ::Metasm::C::AllocCStruct
|
440
|
+
pure_set_msg payload.str, payload.sizeof
|
441
|
+
else
|
442
|
+
raise TurboRex::Exception::ALPC::UnknownPayloadType
|
443
|
+
end
|
444
|
+
elsif opts[:alloc_size]
|
445
|
+
@header = APIProxy.alloc_c_struct('PORT_MESSAGE')
|
446
|
+
set_header
|
447
|
+
|
448
|
+
@payload = 0.chr * opts[:alloc_size].to_i
|
449
|
+
pure_set_msg @payload, @payload.bytesize
|
450
|
+
end
|
451
|
+
end
|
452
|
+
|
453
|
+
def payload=(payload)
|
454
|
+
@payload = payload
|
455
|
+
if payload.is_a? String
|
456
|
+
@payload_size = payload.bytesize
|
457
|
+
elsif payload.is_a? ::Metasm::C::AllocCStruct
|
458
|
+
@payload_size = payload.sizeof
|
459
|
+
end
|
460
|
+
|
461
|
+
if @payload_size > @buf_size
|
462
|
+
pure_set_msg payload, @payload_size
|
463
|
+
else
|
464
|
+
@message[@header_size, @payload_size] = payload
|
465
|
+
set_data_length @payload_size
|
466
|
+
end
|
467
|
+
end
|
468
|
+
|
469
|
+
def set_data_length(len)
|
470
|
+
@total_length = @header_size + len
|
471
|
+
@data_length = len
|
472
|
+
|
473
|
+
@header.u1.s1.TotalLength = @total_length
|
474
|
+
@header.u1.s1.DataLength = @data_length
|
475
|
+
end
|
476
|
+
|
477
|
+
def get_total_and_data_len
|
478
|
+
[@header.u1.s1.TotalLength, @header.u1.s1.DataLength]
|
479
|
+
end
|
480
|
+
|
481
|
+
def header=(header)
|
482
|
+
@header = header
|
483
|
+
|
484
|
+
set_header
|
485
|
+
set_data_length(@payload_size)
|
486
|
+
pure_set_msg @payload, @payload_size
|
487
|
+
end
|
488
|
+
|
489
|
+
def set_header
|
490
|
+
@total_length, @data_length = get_total_and_data_len
|
491
|
+
@length = @header.u1.Length
|
492
|
+
@type = @header.u2.s2.Type
|
493
|
+
@data_info_offset = @header.u2.s2.DataInfoOffset
|
494
|
+
@zero_init = @header.u2.ZeroInit
|
495
|
+
@client_id = @do_not_use_this_field = ClientID.new(@header.ClientId.UniqueProcess, @header.ClientId.UniqueThread)
|
496
|
+
@message_id = @header.MessageId
|
497
|
+
@client_view_size = @callback_id = @header.ClientViewSize
|
498
|
+
@header_size = @header.sizeof
|
499
|
+
end
|
500
|
+
|
501
|
+
def type=(type)
|
502
|
+
binding.pry
|
503
|
+
@type = @header.u2.s2.Type = type
|
504
|
+
@message[0, @header_size] = @header.str
|
505
|
+
end
|
506
|
+
|
507
|
+
def message_id=(id)
|
508
|
+
@message_id = @header.MessageId = id
|
509
|
+
@message[0, @header_size] = @header.str
|
510
|
+
end
|
511
|
+
|
512
|
+
def client_id=(client_id)
|
513
|
+
if client_id.is_a? ClientID
|
514
|
+
@client_id = client_id
|
515
|
+
elsif client_id.is_a? ::Metasm::C::AllocCStruct
|
516
|
+
@client_id = @do_not_use_this_field = ClientID.new(client_id.UniqueProcess, client_id.UniqueThread)
|
517
|
+
else
|
518
|
+
@client_id = @do_not_use_this_field = ClientID.new(client_id[0], client_id[1])
|
519
|
+
end
|
520
|
+
|
521
|
+
@header.ClientId.UniqueProcess = @client_id.unique_process
|
522
|
+
@header.ClientId.UniqueThread = @client_id.unique_thread
|
523
|
+
|
524
|
+
@message[0, @header_size] = @header.str
|
525
|
+
end
|
526
|
+
|
527
|
+
def callback_id=(callback_id)
|
528
|
+
@callback_id = @header.CallbackId = callback_id
|
529
|
+
@message[0, @header_size] = @header.str
|
530
|
+
end
|
531
|
+
|
532
|
+
def refresh_message
|
533
|
+
return unless @message
|
534
|
+
perform_raw_message @message
|
535
|
+
end
|
536
|
+
|
537
|
+
private
|
538
|
+
|
539
|
+
def perform_raw_message(raw_message)
|
540
|
+
raise "Invalid message class." unless raw_message.is_a?(::Metasm::C::AllocCStruct)
|
541
|
+
@message = raw_message
|
542
|
+
nport_message = APIProxy.np['PORT_MESSAGE']
|
543
|
+
@header = nport_message.from_str raw_message[0, nport_message.sizeof]
|
544
|
+
set_header
|
545
|
+
|
546
|
+
@message_size = @message.sizeof
|
547
|
+
@payload_size = @data_length
|
548
|
+
@payload = @message[@header_size, @payload_size]
|
549
|
+
@buf_size = @message.sizeof - @header_size
|
550
|
+
end
|
551
|
+
|
552
|
+
def pure_set_msg(payload, payload_size)
|
553
|
+
@message_size = @header_size = @header.sizeof
|
554
|
+
@payload_size = payload_size
|
555
|
+
@buf_size = @payload_size + PORMSG_PAD
|
556
|
+
@message_size += @buf_size
|
557
|
+
@message = APIProxy.alloc_c_ary('BYTE', @message_size)
|
558
|
+
set_data_length @payload_size
|
559
|
+
@message[0, @header_size] = @header.str
|
560
|
+
@message[@header_size, @payload_size] = payload
|
561
|
+
end
|
562
|
+
end
|
563
|
+
|
564
|
+
class MessageAttribute
|
565
|
+
attr_reader :struct
|
566
|
+
attr_reader :buf
|
567
|
+
attr_reader :attr
|
568
|
+
|
569
|
+
def initialize(attr = nil)
|
570
|
+
@attr = attr ||= (
|
571
|
+
TurboRex::Windows::ALPC::ALPC_MESSAGE_SECURITY_ATTRIBUTE |
|
572
|
+
TurboRex::Windows::ALPC::ALPC_MESSAGE_VIEW_ATTRIBUTE |
|
573
|
+
TurboRex::Windows::ALPC::ALPC_MESSAGE_CONTEXT_ATTRIBUTE |
|
574
|
+
TurboRex::Windows::ALPC::ALPC_MESSAGE_HANDLE_ATTRIBUTE |
|
575
|
+
TurboRex::Windows::ALPC::ALPC_MESSAGE_TOKEN_ATTRIBUTE |
|
576
|
+
TurboRex::Windows::ALPC::ALPC_MESSAGE_DIRECT_ATTRIBUTE |
|
577
|
+
TurboRex::Windows::ALPC::ALPC_MESSAGE_WORK_ON_BEHALF_ATTRIBUTE
|
578
|
+
)
|
579
|
+
msg_attr = APIProxy.alloc_c_struct('ALPC_MESSAGE_ATTRIBUTES')
|
580
|
+
reqired_buf_size = APIProxy.alloc_c_type('ULONG')
|
581
|
+
@buf = required_buf(attr)
|
582
|
+
ntstatus = APIProxy.alpcinitializemessageattribute(attr, @buf, @buf.sizeof, reqired_buf_size)
|
583
|
+
unless TinySDK.nt_success? ntstatus
|
584
|
+
formatted = TurboRex::Windows::TinySDK.format_hex_ntstatus ntstatus, hex_str: true
|
585
|
+
raise "Failed to call AlpcInitializeMessageAttribute: #{formatted}"
|
586
|
+
end
|
587
|
+
|
588
|
+
@struct = @buf
|
589
|
+
end
|
590
|
+
|
591
|
+
def required_buf(attr)
|
592
|
+
size = required_buf_size(attr)
|
593
|
+
APIProxy.alloc_c_ary('BYTE', size)
|
594
|
+
end
|
595
|
+
|
596
|
+
def required_buf_size(attr)
|
597
|
+
required_bud_size = APIProxy.alloc_c_type('ULONG')
|
598
|
+
ntstatus = APIProxy.alpcinitializemessageattribute(attr, 0, 0, required_bud_size)
|
599
|
+
required_bud_size.str.unpack('V')[0]
|
600
|
+
end
|
601
|
+
end
|
602
|
+
|
603
|
+
class Client
|
604
|
+
class ServerProxy
|
605
|
+
def initialize(communication_handle, transport, server_pid, server_tid)
|
606
|
+
@communication_handle = communication_handle
|
607
|
+
@transport = transport
|
608
|
+
@server_pid = server_pid
|
609
|
+
@server_tid = server_tid
|
610
|
+
end
|
611
|
+
|
612
|
+
def gets(opts = {})
|
613
|
+
@transport.recv(@communication_handle, opts)
|
614
|
+
end
|
615
|
+
|
616
|
+
def puts(message, opts = {})
|
617
|
+
if message.is_a? String
|
618
|
+
port_message = PortMessage.new(payload: message)
|
619
|
+
if opts[:last_header]
|
620
|
+
port_message.header = opts[:last_header]
|
621
|
+
end
|
622
|
+
elsif message.is_a? PortMessage
|
623
|
+
port_message = message
|
624
|
+
else
|
625
|
+
raise TurboRex::Exception::ALPC::UnknownPayloadType
|
626
|
+
end
|
627
|
+
|
628
|
+
message_attr = opts.delete(:message_attr) || MessageAttribute.new.struct
|
629
|
+
@transport.send(@communication_handle, port_message.message, message_attr, opts)
|
630
|
+
end
|
631
|
+
|
632
|
+
def send_recv(message, opts = {})
|
633
|
+
if message.is_a? String
|
634
|
+
port_message = PortMessage.new(payload: message)
|
635
|
+
elsif message.is_a? ::Metasm::C::AllocCStruct
|
636
|
+
port_message = message
|
637
|
+
else
|
638
|
+
raise TurboRex::Exception::ALPC::UnknownPayloadType
|
639
|
+
end
|
640
|
+
|
641
|
+
send_attr = port_message.attributes || opts[:attributes] || 0
|
642
|
+
recv_attr = opts[:recv_attr] || MessageAttribute.new.struct
|
643
|
+
@transport.send_recv(@communication_handle, port_message.message, send_attr, recv_attr)
|
644
|
+
end
|
645
|
+
|
646
|
+
def disconnect
|
647
|
+
@transport.close
|
648
|
+
end
|
649
|
+
|
650
|
+
alias_method :write, :puts
|
651
|
+
alias_method :read, :gets
|
652
|
+
end
|
653
|
+
|
654
|
+
def initialize(port_name, opts = {})
|
655
|
+
if TurboRex::Windows::Utils.is_wow64?
|
656
|
+
default_cpu = Metasm::Ia32
|
657
|
+
else
|
658
|
+
default_cpu = Metasm::X86_64
|
659
|
+
end
|
660
|
+
|
661
|
+
cpu = opts[:cpu] || default_cpu
|
662
|
+
APIProxy.init(cpu)
|
663
|
+
|
664
|
+
unless port_name.start_with? '\\'
|
665
|
+
port_name = '\\' + port_name
|
666
|
+
end
|
667
|
+
@port_name = port_name
|
668
|
+
|
669
|
+
@transport = Transport.new
|
670
|
+
end
|
671
|
+
|
672
|
+
def connect(opts = {}, &block)
|
673
|
+
opts[:port_name] = @port_name
|
674
|
+
@communication_handle, msg = @transport.connect(opts)
|
675
|
+
server_pid = msg.client_id&.unique_process
|
676
|
+
server_tid = msg.client_id&.unique_thread
|
677
|
+
@server = ServerProxy.new(@communication_handle, @transport, server_pid, server_tid)
|
678
|
+
yield(@server) if block_given?
|
679
|
+
[@server, msg]
|
680
|
+
end
|
681
|
+
end
|
682
|
+
|
683
|
+
class Server
|
684
|
+
include TurboRex::Windows::ALPC
|
685
|
+
|
686
|
+
attr_reader :port_name
|
687
|
+
attr_reader :obj_attr
|
688
|
+
|
689
|
+
class ClientStub
|
690
|
+
def initialize(communication_handle, conn_handle, transport, conn_message)
|
691
|
+
@communication_handle = communication_handle
|
692
|
+
@connection_handle = conn_handle
|
693
|
+
@conn_message = conn_message
|
694
|
+
@transport = transport
|
695
|
+
@client_id = conn_message.client_id
|
696
|
+
end
|
697
|
+
|
698
|
+
def gets(opts = {})
|
699
|
+
@transport.recv(@connection_handle, opts)
|
700
|
+
end
|
701
|
+
|
702
|
+
def puts(message, message_id = nil, opts = {})
|
703
|
+
if message.is_a? String
|
704
|
+
port_message = PortMessage.new(payload: message)
|
705
|
+
if opts[:last_header]
|
706
|
+
port_message.header = opts[:last_header]
|
707
|
+
elsif message_id
|
708
|
+
port_message.message_id = message_id
|
709
|
+
else
|
710
|
+
raise "Message ID must be specified when :last_header option is not specified."
|
711
|
+
end
|
712
|
+
elsif message.is_a? PortMessage
|
713
|
+
port_message = message
|
714
|
+
else
|
715
|
+
raise TurboRex::Exception::ALPC::UnknownPayloadType
|
716
|
+
end
|
717
|
+
|
718
|
+
message_attr = opts.delete(:message_attr) || port_message.attributes
|
719
|
+
@transport.send(@communication_handle, port_message.message, message_attr, opts)
|
720
|
+
end
|
721
|
+
|
722
|
+
# def impersonate(msg)
|
723
|
+
# ntstatus = APIProxy.ntalpcimpersonateclientofport(@communication_handle, msg.message, 0)
|
724
|
+
# TurboRex::Windows::TinySDK.format_hex_ntstatus ntstatus
|
725
|
+
# end
|
726
|
+
|
727
|
+
alias_method :write, :puts
|
728
|
+
alias_method :read, :gets
|
729
|
+
end
|
730
|
+
|
731
|
+
def initialize(port_name, opts = {})
|
732
|
+
if TurboRex::Windows::Utils.is_wow64?
|
733
|
+
default_cpu = Metasm::Ia32
|
734
|
+
else
|
735
|
+
default_cpu = Metasm::X86_64
|
736
|
+
end
|
737
|
+
|
738
|
+
cpu = opts[:cpu] || default_cpu
|
739
|
+
APIProxy.init(cpu)
|
740
|
+
|
741
|
+
@communication_port_handles = []
|
742
|
+
@clients = []
|
743
|
+
|
744
|
+
unless port_name.start_with? '\\'
|
745
|
+
port_name = '\\' + port_name
|
746
|
+
end
|
747
|
+
@port_name = port_name
|
748
|
+
|
749
|
+
if wport_name = TurboRex::Windows::Utils.multibyte_to_widechar(port_name)
|
750
|
+
dest_str = APIProxy.alloc_c_struct('UNICODE_STRING')
|
751
|
+
APIProxy.rtlinitunicodestring(dest_str, wport_name)
|
752
|
+
|
753
|
+
handle = APIProxy.alloc_c_type('HANDLE')
|
754
|
+
alpc_port_attr, obj_attr = make_attr(obj_name: dest_str)
|
755
|
+
ntstatus = APIProxy.ntalpccreateport(handle, obj_attr, alpc_port_attr)
|
756
|
+
|
757
|
+
unless TinySDK.nt_success? ntstatus
|
758
|
+
formatted = TurboRex::Windows::TinySDK.format_hex_ntstatus ntstatus, hex_str: true
|
759
|
+
raise "Unable to create alpc port: #{formatted}"
|
760
|
+
end
|
761
|
+
|
762
|
+
@conn_port_handle = handle[0]
|
763
|
+
@transport = Transport.new
|
764
|
+
else
|
765
|
+
raise "Unable to convert characters to utf-16le encoding."
|
766
|
+
end
|
767
|
+
end
|
768
|
+
|
769
|
+
def run(opts = {}, &block)
|
770
|
+
loop do
|
771
|
+
conn_message = @transport.listen(@conn_port_handle)
|
772
|
+
puts "[*] Receiving connection request"
|
773
|
+
|
774
|
+
if opts[:conn_req_cb]
|
775
|
+
unless (permit_conn = opts[:conn_req_cb].call(:connection_req, conn_message, self))
|
776
|
+
######################################################################################################################
|
777
|
+
## Requires following params(UniqueProcess, UniqueThread, MessageId), otherwise raise STATUS_REPLY_MESSAGE_MISMATCH ##
|
778
|
+
## uniq_process = conn_message.client_id.unique_process ##
|
779
|
+
## uniq_thread = conn_message.client_id.unique_thread ##
|
780
|
+
## message_id = conn_message.message_id ##
|
781
|
+
## ##
|
782
|
+
## Or we can pass a instance of PortMessage with the 'port_message' key ##
|
783
|
+
######################################################################################################################
|
784
|
+
@transport.refuse_connect(port_message: conn_message) and next
|
785
|
+
end
|
786
|
+
end
|
787
|
+
|
788
|
+
client = accept(conn_message)
|
789
|
+
if block_given?
|
790
|
+
yield(client)
|
791
|
+
end
|
792
|
+
end
|
793
|
+
end
|
794
|
+
|
795
|
+
def accept(conn_message, &block)
|
796
|
+
handle, ntstatus = @transport.accept(port_message: conn_message)
|
797
|
+
if TinySDK.nt_success?(ntstatus)
|
798
|
+
@communication_port_handles << handle
|
799
|
+
client_stub = ClientStub.new(handle, @conn_port_handle, @transport, conn_message)
|
800
|
+
@clients << client_stub
|
801
|
+
yield(client_stub) if block_given?
|
802
|
+
client_stub
|
803
|
+
else
|
804
|
+
puts "[-] Unable to accept connection. (0x#{ntstatus.to_s(16).upcase})"
|
805
|
+
raise TurboRex::Exception::ALPC::UnableToAcceptConnection
|
806
|
+
end
|
807
|
+
end
|
808
|
+
|
809
|
+
#def impersonate_client(client)
|
810
|
+
# client.impersonate
|
811
|
+
#end
|
812
|
+
|
813
|
+
private
|
814
|
+
|
815
|
+
def make_attr(opts = {})
|
816
|
+
unless @alpc_port_attr
|
817
|
+
alpc_port_attr = APIProxy.alloc_c_struct('ALPC_PORT_ATTRIBUTES')
|
818
|
+
alpc_port_attr.Flags = ALPC::ALPC_PORFLG_ALLOW_LPC_REQUESTS
|
819
|
+
alpc_port_attr.MaxMessageLength = 0x1000
|
820
|
+
alpc_port_attr.MemoryBandwidth = 0
|
821
|
+
alpc_port_attr.MaxPoolUsage = 0xFFFFFFFF
|
822
|
+
alpc_port_attr.MaxSectionSize = 0xFFFFFFFF
|
823
|
+
alpc_port_attr.MaxViewSize = 0xFFFFFFFF
|
824
|
+
alpc_port_attr.MaxTotalSectionSize = 0xFFFFFFFF
|
825
|
+
alpc_port_attr.DupObjectTypes = 0xFFFFFFFF
|
826
|
+
end
|
827
|
+
|
828
|
+
unless @obj_attr
|
829
|
+
obj_attr = APIProxy.alloc_c_struct('OBJECT_ATTRIBUTES')
|
830
|
+
obj_attr.Length = obj_attr.sizeof
|
831
|
+
obj_attr.ObjectName = opts[:obj_name]
|
832
|
+
@obj_attr = obj_attr
|
833
|
+
end
|
834
|
+
|
835
|
+
[alpc_port_attr, obj_attr]
|
836
|
+
end
|
837
|
+
|
838
|
+
def np
|
839
|
+
APIProxy.np
|
840
|
+
end
|
841
|
+
end
|
842
|
+
end
|
843
|
+
end
|
844
|
+
end
|