turborex 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|