1# Copyright (c) 2017 CORE Security Technologies
2# Copyright (c) 2017 @MrAnde7son
3#
4# This software is provided under under a slightly modified version
5# of the Apache Software License. See the accompanying LICENSE file
6# for more information.
7#
8# Author: Itamar (@MrAnde7son)
9#
10# Description:
11#   Initial [MS-EVEN6] Interface implementation
12#
13#   Best way to learn how to use these calls is to grab the protocol standard
14#   so you understand what the call does, and then read the test case located
15#   at https://github.com/CoreSecurity/impacket/tree/master/impacket/testcases/SMB_RPC
16#
17#   Some calls have helper functions, which makes it even easier to use.
18#   They are located at the end of this file.
19#   Helper functions start with "h"<name of the call>.
20#   There are test cases for them too.
21#
22from impacket import system_errors
23from impacket.dcerpc.v5.dtypes import WSTR, DWORD, LPWSTR, ULONG, LARGE_INTEGER, WORD, BYTE
24from impacket.dcerpc.v5.ndr import NDRCALL, NDRPOINTER, NDRUniConformantArray, NDRUniVaryingArray, NDRSTRUCT
25from impacket.dcerpc.v5.rpcrt import DCERPCException
26from impacket.uuid import uuidtup_to_bin
27
28MSRPC_UUID_EVEN6 = uuidtup_to_bin(('F6BEAFF7-1E19-4FBB-9F8F-B89E2018337C', '1.0'))
29
30class DCERPCSessionError(DCERPCException):
31    def __init__(self, error_string=None, error_code=None, packet=None):
32        DCERPCException.__init__(self, error_string, error_code, packet)
33
34    def __str__(self):
35        key = self.error_code
36        if system_errors.ERROR_MESSAGES.has_key(key):
37            error_msg_short = system_errors.ERROR_MESSAGES[key][0]
38            error_msg_verbose = system_errors.ERROR_MESSAGES[key][1]
39            return 'EVEN6 SessionError: code: 0x%x - %s - %s' % (self.error_code, error_msg_short, error_msg_verbose)
40        else:
41            return 'EVEN6 SessionError: unknown error code: 0x%x' % self.error_code
42
43################################################################################
44# CONSTANTS
45################################################################################
46
47# Evt Path Flags
48EvtQueryChannelName = 0x00000001
49EvtQueryFilePath = 0x00000002
50EvtReadOldestToNewest = 0x00000100
51EvtReadNewestToOldest = 0x00000200
52
53################################################################################
54# STRUCTURES
55################################################################################
56
57class CONTEXT_HANDLE_LOG_HANDLE(NDRSTRUCT):
58    align = 1
59    structure = (
60        ('Data', '20s=""'),
61    )
62
63class PCONTEXT_HANDLE_LOG_HANDLE(NDRPOINTER):
64    referent = (
65        ('Data', CONTEXT_HANDLE_LOG_HANDLE),
66    )
67
68class CONTEXT_HANDLE_LOG_QUERY(NDRSTRUCT):
69    align = 1
70    structure = (
71        ('Data', '20s=""'),
72    )
73
74class PCONTEXT_HANDLE_LOG_QUERY(NDRPOINTER):
75    referent = (
76        ('Data', CONTEXT_HANDLE_LOG_QUERY),
77    )
78
79class LPPCONTEXT_HANDLE_LOG_QUERY(NDRPOINTER):
80    referent = (
81        ('Data', PCONTEXT_HANDLE_LOG_QUERY),
82    )
83
84class CONTEXT_HANDLE_OPERATION_CONTROL(NDRSTRUCT):
85    align = 1
86    structure = (
87        ('Data', '20s=""'),
88    )
89
90class PCONTEXT_HANDLE_OPERATION_CONTROL(NDRPOINTER):
91    referent = (
92        ('Data', CONTEXT_HANDLE_OPERATION_CONTROL),
93    )
94
95# 2.2.11 EvtRpcQueryChannelInfo
96class EvtRpcQueryChannelInfo(NDRSTRUCT):
97    structure = (
98        ('Name', LPWSTR),
99        ('Status', DWORD),
100    )
101
102class EvtRpcQueryChannelInfoArray(NDRUniVaryingArray):
103    item = EvtRpcQueryChannelInfo
104
105class LPEvtRpcQueryChannelInfoArray(NDRPOINTER):
106    referent = (
107        ('Data', EvtRpcQueryChannelInfoArray)
108    )
109
110class RPC_INFO(NDRSTRUCT):
111    structure = (
112        ('Error', DWORD),
113        ('SubError', DWORD),
114        ('SubErrorParam', DWORD),
115    )
116
117class PRPC_INFO(NDRPOINTER):
118    referent = (
119        ('Data', RPC_INFO)
120    )
121
122class WSTR_ARRAY(NDRUniVaryingArray):
123    item = WSTR
124
125class DWORD_ARRAY(NDRUniVaryingArray):
126    item = DWORD
127
128class LPDWORD_ARRAY(NDRPOINTER):
129    referent = (
130        ('Data', DWORD_ARRAY)
131    )
132
133class BYTE_ARRAY(NDRUniVaryingArray):
134    item = 'c'
135
136class CBYTE_ARRAY(NDRUniVaryingArray):
137    item = BYTE
138
139class CDWORD_ARRAY(NDRUniConformantArray):
140    item = DWORD
141
142class LPBYTE_ARRAY(NDRPOINTER):
143    referent = (
144        ('Data', CBYTE_ARRAY)
145    )
146
147class ULONG_ARRAY(NDRUniVaryingArray):
148    item = ULONG
149
150# 2.3.1 EVENT_DESCRIPTOR
151class EVENT_DESCRIPTOR(NDRSTRUCT):
152    structure = (
153        ('Id', WORD),
154        ('Version', BYTE),
155        ('Channel', BYTE),
156        ('LevelSeverity', BYTE),
157        ('Opcode', BYTE),
158        ('Task', WORD),
159        ('Keyword', ULONG),
160    )
161
162class BOOKMARK(NDRSTRUCT):
163    structure = (
164        ('BookmarkSize', DWORD),
165        ('HeaderSize', '<L=0x18'),
166        ('ChannelSize', DWORD),
167        ('CurrentChannel', DWORD),
168        ('ReadDirection', DWORD),
169        ('RecordIdsOffset', DWORD),
170        ('LogRecordNumbers', ULONG_ARRAY),
171    )
172
173
174#2.2.17 RESULT_SET
175class RESULT_SET(NDRSTRUCT):
176    structure = (
177        ('TotalSize', DWORD),
178        ('HeaderSize', DWORD),
179        ('EventOffset', DWORD),
180        ('BookmarkOffset', DWORD),
181        ('BinXmlSize', DWORD),
182        ('EventData', BYTE_ARRAY),
183        #('NumberOfSubqueryIDs', '<L=0'),
184        #('SubqueryIDs', BYTE_ARRAY),
185        #('BookMarkData', BOOKMARK),
186    )
187
188################################################################################
189# RPC CALLS
190################################################################################
191
192class EvtRpcRegisterLogQuery(NDRCALL):
193    opnum = 5
194    structure = (
195        ('Path', LPWSTR),
196        ('Query', WSTR),
197        ('Flags', DWORD),
198    )
199
200class EvtRpcRegisterLogQueryResponse(NDRCALL):
201    structure = (
202        ('Handle', CONTEXT_HANDLE_LOG_QUERY),
203        ('OpControl', CONTEXT_HANDLE_OPERATION_CONTROL),
204        ('QueryChannelInfoSize', DWORD),
205        ('QueryChannelInfo', EvtRpcQueryChannelInfoArray),
206        ('Error', RPC_INFO),
207        )
208
209class EvtRpcQueryNext(NDRCALL):
210    opnum = 11
211    structure = (
212        ('LogQuery', CONTEXT_HANDLE_LOG_QUERY),
213        ('NumRequestedRecords', DWORD),
214        ('TimeOutEnd', DWORD),
215        ('Flags', DWORD),
216    )
217
218class EvtRpcQueryNextResponse(NDRCALL):
219    structure = (
220        ('NumActualRecords', DWORD),
221        ('EventDataIndices', DWORD_ARRAY),
222        ('EventDataSizes', DWORD_ARRAY),
223        ('ResultBufferSize', DWORD),
224        ('ResultBuffer', BYTE_ARRAY),
225        ('ErrorCode', ULONG),
226    )
227
228class EvtRpcQuerySeek(NDRCALL):
229    opnum = 12
230    structure = (
231        ('LogQuery', CONTEXT_HANDLE_LOG_QUERY),
232        ('Pos', LARGE_INTEGER),
233        ('BookmarkXML', LPWSTR),
234        ('Flags', DWORD),
235    )
236
237class EvtRpcQuerySeekResponse(NDRCALL):
238    structure = (
239        ('Error', RPC_INFO),
240    )
241
242class EvtRpcClose(NDRCALL):
243    opnum = 13
244    structure = (
245        ("Handle", CONTEXT_HANDLE_LOG_HANDLE),
246    )
247
248class EvtRpcCloseResponse(NDRCALL):
249    structure = (
250        ("Handle", PCONTEXT_HANDLE_LOG_HANDLE),
251        ('ErrorCode', ULONG),
252    )
253
254class EvtRpcOpenLogHandle(NDRCALL):
255    opnum = 17
256    structure = (
257        ('Channel', WSTR),
258        ('Flags', DWORD),
259    )
260
261class EvtRpcOpenLogHandleResponse(NDRCALL):
262    structure = (
263        ('Handle', PCONTEXT_HANDLE_LOG_HANDLE),
264        ('Error', RPC_INFO),
265    )
266
267class EvtRpcGetChannelList(NDRCALL):
268    opnum = 19
269    structure = (
270        ('Flags', DWORD),
271    )
272
273class EvtRpcGetChannelListResponse(NDRCALL):
274    structure = (
275        ('NumChannelPaths', DWORD),
276        ('ChannelPaths', WSTR_ARRAY),
277        ('ErrorCode', ULONG),
278    )
279
280################################################################################
281# OPNUMs and their corresponding structures
282################################################################################
283
284OPNUMS = {
285    5   : (EvtRpcRegisterLogQuery, EvtRpcRegisterLogQueryResponse),
286    11  : (EvtRpcQueryNext,  EvtRpcQueryNextResponse),
287    12  : (EvtRpcQuerySeek, EvtRpcQuerySeekResponse),
288    13  : (EvtRpcClose, EvtRpcCloseResponse),
289    17  : (EvtRpcOpenLogHandle, EvtRpcOpenLogHandle),
290    19  : (EvtRpcGetChannelList, EvtRpcGetChannelListResponse),
291}
292
293################################################################################
294# HELPER FUNCTIONS
295################################################################################
296
297def hEvtRpcRegisterLogQuery(dce, path, flags, query='*\x00'):
298    request = EvtRpcRegisterLogQuery()
299
300    request['Path'] = path
301    request['Query'] = query
302    request['Flags'] = flags
303    resp = dce.request(request)
304    return resp
305
306def hEvtRpcQueryNext(dce, handle, numRequestedRecords, timeOutEnd=1000):
307    request = EvtRpcQueryNext()
308
309    request['LogQuery'] = handle
310    request['NumRequestedRecords'] = numRequestedRecords
311    request['TimeOutEnd'] = timeOutEnd
312    request['Flags'] = 0
313    status = system_errors.ERROR_MORE_DATA
314    resp = dce.request(request)
315    while status == system_errors.ERROR_MORE_DATA:
316        try:
317            resp = dce.request(request)
318        except DCERPCException, e:
319            if str(e).find('ERROR_NO_MORE_ITEMS') < 0:
320                raise
321            elif str(e).find('ERROR_TIMEOUT') < 0:
322                raise
323            resp = e.get_packet()
324        return resp
325
326def hEvtRpcClose(dce, handle):
327    request = EvtRpcClose()
328    request['Handle'] = handle
329    resp = dce.request(request)
330    return resp
331
332def hEvtRpcOpenLogHandle(dce, channel, flags):
333    request = EvtRpcOpenLogHandle()
334
335    request['Channel'] = channel
336    request['Flags'] = flags
337    return dce.request(request)
338
339def hEvtRpcGetChannelList(dce):
340    request = EvtRpcGetChannelList()
341
342    request['Flags'] = 0
343    resp = dce.request(request)
344    return resp
345
346