1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
|
---
-- This module is designed to parse the <code>PERF_DATA_BLOCK</code> structure, which is
-- stored in the registry under HKEY_PERFORMANCE_DATA. By querying this structure, you can
-- get a whole lot of information about what's going on.
--
-- To use this from a script, see <code>get_performance_data</code>, it is the only
-- "public" function in this module.
--
-- My primary sources of information were:
-- * This 1996 journal by Matt Pietrek: <http://www.microsoft.com/msj/archive/S271.aspx>
-- * The followup article: <http://www.microsoft.com/msj/archive/S2A9.aspx>
-- * The WinPerf.h header file
--
-- And my primary inspiration was PsTools, specifically, <code>pstasklist.exe</code>.
--
--@author Ron Bowes <ron@skullsecurity.net>
--@copyright Same as Nmap--See https://nmap.org/book/man-legal.html
-----------------------------------------------------------------------
local msrpc = require "msrpc"
local msrpctypes = require "msrpctypes"
local stdnse = require "stdnse"
local string = require "string"
_ENV = stdnse.module("msrpcperformance", stdnse.seeall)
---Parses the title database, which is a series of null-terminated string pairs.
--
--@param data The data being processed.
--@param pos The position within <code>data</code>.
--@return (status, pos, result) The status (true if successful), the new position in <code>data</code> (or an error
-- message), and a table representing the datatype, if any.
local function parse_perf_title_database(data, pos)
local result = {}
local i = 1
repeat
local number, name
number, name, pos = string.unpack("<zz", data, pos)
if(number == nil) then
return false, "Couldn't parse the title database: end of string encountered early"
elseif(tonumber(number) == nil) then -- Not sure if this actually happens, but it doesn't hurt to check
stdnse.debug1("MSRPC: ERROR: Couldn't parse the title database: string found where number expected (%d: '%s')", i, number)
return false, "Couldn't parse the title database"
end
result[tonumber(number)] = name
i = i + 1
until pos >= #data
return true, pos, result
end
---Parses a PERF_DATA_BLOCK, which has the following definition (from "WinPerf.h" on Visual Studio 8):
--
--<code>
-- typedef struct _PERF_DATA_BLOCK {
-- WCHAR Signature[4]; // Signature: Unicode "PERF"
-- DWORD LittleEndian; // 0 = Big Endian, 1 = Little Endian
-- DWORD Version; // Version of these data structures
-- // starting at 1
-- DWORD Revision; // Revision of these data structures
-- // starting at 0 for each Version
-- DWORD TotalByteLength; // Total length of data block
-- DWORD HeaderLength; // Length of this structure
-- DWORD NumObjectTypes; // Number of types of objects
-- // being reported
-- LONG DefaultObject; // Object Title Index of default
-- // object to display when data from
-- // this system is retrieved (-1 =
-- // none, but this is not expected to
-- // be used)
-- SYSTEMTIME SystemTime; // Time at the system under
-- // measurement
-- LARGE_INTEGER PerfTime; // Performance counter value
-- // at the system under measurement
-- LARGE_INTEGER PerfFreq; // Performance counter frequency
-- // at the system under measurement
-- LARGE_INTEGER PerfTime100nSec; // Performance counter time in 100 nsec
-- // units at the system under measurement
-- DWORD SystemNameLength; // Length of the system name
-- DWORD SystemNameOffset; // Offset, from beginning of this
-- // structure, to name of system
-- // being measured
-- } PERF_DATA_BLOCK, *PPERF_DATA_BLOCK;
--</code>
--
--@param data The data being processed.
--@param pos The position within <code>data</code>.
--@return (status, pos, result) The status (true if successful), the new position in <code>data</code> (or an error
-- message), and a table representing the datatype, if any.
local function parse_perf_data_block(data, pos)
local result = {}
pos, result['Signature'] = msrpctypes.unicode_to_string(data, pos, 4, false)
if(result['Signature'] ~= "PERF") then
return false, "MSRPC: PERF_DATA_BLOCK signature is missing or incorrect"
end
pos, result['LittleEndian'] = msrpctypes.unmarshall_int32(data, pos)
if(result['LittleEndian'] ~= 1) then
return false, "MSRPC: PERF_DATA_BLOCK returned a non-understood endianness"
end
-- Parse the header
pos, result['Version'] = msrpctypes.unmarshall_int32(data, pos)
pos, result['Revision'] = msrpctypes.unmarshall_int32(data, pos)
pos, result['TotalByteLength'] = msrpctypes.unmarshall_int32(data, pos)
pos, result['HeaderLength'] = msrpctypes.unmarshall_int32(data, pos)
pos, result['NumObjectTypes'] = msrpctypes.unmarshall_int32(data, pos)
pos, result['DefaultObject'] = msrpctypes.unmarshall_int32(data, pos)
pos, result['SystemTime'] = msrpctypes.unmarshall_SYSTEMTIME(data, pos)
pos, result['PerfTime'] = msrpctypes.unmarshall_int64(data, pos)
pos, result['PerfFreq'] = msrpctypes.unmarshall_int64(data, pos)
pos, result['PerfTime100nSec'] = msrpctypes.unmarshall_int64(data, pos)
pos = pos + 4 -- This value doesn't seem to line up, so add 4
pos, result['SystemNameLength'] = msrpctypes.unmarshall_int32(data, pos)
pos, result['SystemNameOffset'] = msrpctypes.unmarshall_int32(data, pos)
-- Ensure that the system name is directly after the header. This technically shouldn't matter, but Microsoft's documentation
-- (in WinPref.h) says that the actual object comes "after the PERF_DATA_BLOCK", so it doesn't make sense that the SystemName
-- could be anywhere else.
if(pos ~= result['SystemNameOffset'] + 1) then
return false, "MSRPC: PERF_DATA_BLOCK has SystemName in the wrong location"
end
-- Read the system name from the next location (which happens to be identical to SystemNameOffset, on a proper system)
pos, result['SystemName'] = msrpctypes.unicode_to_string(data, pos, result['SystemNameLength'] / 2, true)
pos = pos + 4 -- Again, we end up not lined up so here we fix it
return true, pos, result
end
---Parse a PERF_OBJECT_TYPE structure. From Microsoft's documentation:
--
--<code>
-- //
-- // The _PERF_DATA_BLOCK structure is followed by NumObjectTypes of
-- // data sections, one for each type of object measured. Each object
-- // type section begins with a _PERF_OBJECT_TYPE structure.
-- //
-- typedef struct _PERF_OBJECT_TYPE {
-- DWORD TotalByteLength; // Length of this object definition
-- // including this structure, the
-- // counter definitions, and the
-- // instance definitions and the
-- // counter blocks for each instance:
-- // This is the offset from this
-- // structure to the next object, if
-- // any
-- DWORD DefinitionLength; // Length of object definition,
-- // which includes this structure
-- // and the counter definition
-- // structures for this object: this
-- // is the offset of the first
-- // instance or of the counters
-- // for this object if there is
-- // no instance
-- DWORD HeaderLength; // Length of this structure: this
-- // is the offset to the first
-- // counter definition for this
-- // object
-- DWORD ObjectNameTitleIndex;
-- // Index to name in Title Database
-- #ifdef _WIN64
-- DWORD ObjectNameTitle; // Should use this as an offset
-- #else
-- LPWSTR ObjectNameTitle; // Initially NULL, for use by
-- // analysis program to point to
-- // retrieved title string
-- #endif
-- DWORD ObjectHelpTitleIndex;
-- // Index to Help in Title Database
-- #ifdef _WIN64
-- DWORD ObjectHelpTitle; // Should use this as an offset
-- #else
-- LPWSTR ObjectHelpTitle; // Initially NULL, for use by
-- // analysis program to point to
-- // retrieved title string
-- #endif
-- DWORD DetailLevel; // Object level of detail (for
-- // controlling display complexity);
-- // will be min of detail levels
-- // for all this object's counters
-- DWORD NumCounters; // Number of counters in each
-- // counter block (one counter
-- // block per instance)
-- LONG DefaultCounter; // Default counter to display when
-- // this object is selected, index
-- // starting at 0 (-1 = none, but
-- // this is not expected to be used)
-- LONG NumInstances; // Number of object instances
-- // for which counters are being
-- // returned from the system under
-- // measurement. If the object defined
-- // will never have any instance data
-- // structures (PERF_INSTANCE_DEFINITION)
-- // then this value should be -1, if the
-- // object can have 0 or more instances,
-- // but has none present, then this
-- // should be 0, otherwise this field
-- // contains the number of instances of
-- // this counter.
-- DWORD CodePage; // 0 if instance strings are in
-- // UNICODE, else the Code Page of
-- // the instance names
-- LARGE_INTEGER PerfTime; // Sample Time in "Object" units
-- //
-- LARGE_INTEGER PerfFreq; // Frequency of "Object" units in
-- // counts per second.
-- } PERF_OBJECT_TYPE, *PPERF_OBJECT_TYPE;
--</code>
--
--@param data The data being processed.
--@param pos The position within <code>data</code>.
--@return (status, pos, result) The status (true if successful), the new position in <code>data</code> (or an error
-- message), and a table representing the datatype, if any.
local function parse_perf_object_type(data, pos)
local result = {}
pos, result['TotalByteLength'] = msrpctypes.unmarshall_int32(data, pos) -- Offset to the next object
pos, result['DefinitionLength'] = msrpctypes.unmarshall_int32(data, pos) -- Offset to the first instance (or counter, if no instances)
pos, result['HeaderLength'] = msrpctypes.unmarshall_int32(data, pos) -- Offset to the first counter definition
pos, result['ObjectNameTitleIndex'] = msrpctypes.unmarshall_int32(data, pos) -- Index in the Title Database
pos, result['ObjectNameTitle'] = msrpctypes.unmarshall_int32(data, pos) -- TODO: will this work with 64-bit?
pos, result['ObjectHelpTitleIndex'] = msrpctypes.unmarshall_int32(data, pos) -- Index in the Help Database
pos, result['ObjectHelpTitle'] = msrpctypes.unmarshall_int32(data, pos) -- TODO: will this workw ith 64-bit?
pos, result['DetailLevel'] = msrpctypes.unmarshall_int32(data, pos)
pos, result['NumCounters'] = msrpctypes.unmarshall_int32(data, pos) -- The number of counters in each counter block
pos, result['DefaultCounter'] = msrpctypes.unmarshall_int32(data, pos)
pos, result['NumInstances'] = msrpctypes.unmarshall_int32(data, pos) -- Numer of object instances for which counters are being returned
pos, result['CodePage'] = msrpctypes.unmarshall_int32(data, pos) -- 0 if strings are in UNICODE, otherwise the Code Page
-- if(result['CodePage'] ~= 0) then
-- return false, string.format("Unknown Code Page for data: %d\n", result['CodePage'])
-- end
pos, result['PerfTime'] = msrpctypes.unmarshall_int64(data, pos) -- Sample time in "Object" units
pos, result['PerfFreq'] = msrpctypes.unmarshall_int64(data, pos) -- Frequency of "Object" units in counts/second
return true, pos, result
end
---Parse a PERF_COUNTER_DEFINITION structure. From Microsoft's documentation:
--
--<code>
-- // There is one of the following for each of the
-- // PERF_OBJECT_TYPE.NumCounters. The Unicode names in this structure MUST
-- // come from a message file.
-- typedef struct _PERF_COUNTER_DEFINITION {
-- DWORD ByteLength; // Length in bytes of this structure
-- DWORD CounterNameTitleIndex;
-- // Index of Counter name into
-- // Title Database
-- #ifdef _WIN64
-- DWORD CounterNameTitle;
-- #else
-- LPWSTR CounterNameTitle; // Initially NULL, for use by
-- // analysis program to point to
-- // retrieved title string
-- #endif
-- DWORD CounterHelpTitleIndex;
-- // Index of Counter Help into
-- // Title Database
-- #ifdef _WIN64
-- DWORD CounterHelpTitle;
-- #else
-- LPWSTR CounterHelpTitle; // Initially NULL, for use by
-- // analysis program to point to
-- // retrieved title string
-- #endif
-- LONG DefaultScale; // Power of 10 by which to scale
-- // chart line if vertical axis is 100
-- // 0 ==> 1, 1 ==> 10, -1 ==>1/10, etc.
-- DWORD DetailLevel; // Counter level of detail (for
-- // controlling display complexity)
-- DWORD CounterType; // Type of counter
-- DWORD CounterSize; // Size of counter in bytes
-- DWORD CounterOffset; // Offset from the start of the
-- // PERF_COUNTER_BLOCK to the first
-- // byte of this counter
-- } PERF_COUNTER_DEFINITION, *PPERF_COUNTER_DEFINITION;
--</code>
--
--@param data The data being processed.
--@param pos The position within <code>data</code>.
--@return (status, pos, result) The status (true if successful), the new position in <code>data</code> (or an error
-- message), and a table representing the datatype, if any.
local function parse_perf_counter_definition(data, pos)
local result = {}
local initial_pos = pos
pos, result['ByteLength'] = msrpctypes.unmarshall_int32(data, pos)
pos, result['CounterNameTitleIndex'] = msrpctypes.unmarshall_int32(data, pos)
pos, result['CounterNameTitle'] = msrpctypes.unmarshall_int32(data, pos)
pos, result['CounterHelpTitleIndex'] = msrpctypes.unmarshall_int32(data, pos)
pos, result['CounterHelpTitle'] = msrpctypes.unmarshall_int32(data, pos)
pos, result['DefaultScale'] = msrpctypes.unmarshall_int32(data, pos)
pos, result['DetailLevel'] = msrpctypes.unmarshall_int32(data, pos)
pos, result['CounterType'] = msrpctypes.unmarshall_int32(data, pos)
pos, result['CounterSize'] = msrpctypes.unmarshall_int32(data, pos)
pos, result['CounterOffset'] = msrpctypes.unmarshall_int32(data, pos)
pos = initial_pos + result['ByteLength']
return true, pos, result
end
---Parse the actual counter value. This is a fairly simple function, it takes a counter
-- definition and pulls out data based on it.
--
-- Note: I don't think this is doing the 8-byte values right, I suspect that they're supposed
-- to be doubles.
--
--@param data The data being processed.
--@param pos The position within <code>data</code>.
--@param counter_definition The matching counter_definition.
--@return (status, pos, result) The status (true if successful), the new position in <code>data</code> (or an error
-- message), and a table representing the datatype, if any.
local function parse_perf_counter(data, pos, counter_definition)
local result
if(counter_definition['CounterSize'] == 4) then
pos, result = msrpctypes.unmarshall_int32(data, pos)
elseif(counter_definition['CounterSize'] == 8) then
pos, result = msrpctypes.unmarshall_int64(data, pos)
else
pos, result = msrpctypes.unmarshall_raw(data, pos, counter_definition['CounterSize'])
end
return true, pos, result
end
---Parse a PERF_INSTANCE_DEFINITION structure. From Microsoft's documentation:
--
--<code>
-- // If (PERF_DATA_BLOCK.NumInstances >= 0) then there will be
-- // PERF_DATA_BLOCK.NumInstances of a (PERF_INSTANCE_DEFINITION
-- // followed by a PERF_COUNTER_BLOCK followed by the counter data fields)
-- // for each instance.
-- //
-- // If (PERF_DATA_BLOCK.NumInstances < 0) then the counter definition
-- // structure above will be followed by only a PERF_COUNTER_BLOCK and the
-- // counter data for that COUNTER.
-- typedef struct _PERF_INSTANCE_DEFINITION {
-- DWORD ByteLength; // Length in bytes of this structure,
-- // including the subsequent name
-- DWORD ParentObjectTitleIndex;
-- // Title Index to name of "parent"
-- // object (e.g., if thread, then
-- // process is parent object type);
-- // if logical drive, the physical
-- // drive is parent object type
-- DWORD ParentObjectInstance;
-- // Index to instance of parent object
-- // type which is the parent of this
-- // instance.
-- LONG UniqueID; // A unique ID used instead of
-- // matching the name to identify
-- // this instance, -1 = none
-- DWORD NameOffset; // Offset from beginning of
-- // this struct to the Unicode name
-- // of this instance
-- DWORD NameLength; // Length in bytes of name; 0 = none
-- // this length includes the characters
-- // in the string plus the size of the
-- // terminating NULL char. It does not
-- // include any additional pad bytes to
-- // correct structure alignment
-- } PERF_INSTANCE_DEFINITION, *PPERF_INSTANCE_DEFINITION;
--</code>
--
--@param data The data being processed.
--@param pos The position within <code>data</code>.
--@return (status, pos, result) The status (true if successful), the new position in <code>data</code> (or an error
-- message), and a table representing the datatype, if any.
local function parse_perf_instance_definition(data, pos)
local result = {}
-- Remember where we started. I noticed that where the counter part starts can move around, so we have to
-- determine it by adding ByteLength to the initial position
local initial_pos = pos
pos, result['ByteLength'] = msrpctypes.unmarshall_int32(data, pos)
pos, result['ParentObjectTitleIndex'] = msrpctypes.unmarshall_int32(data, pos)
pos, result['ParentObjectInstance'] = msrpctypes.unmarshall_int32(data, pos)
pos, result['UniqueID'] = msrpctypes.unmarshall_int32(data, pos)
pos, result['NameOffset'] = msrpctypes.unmarshall_int32(data, pos)
pos, result['NameLength'] = msrpctypes.unmarshall_int32(data, pos)
pos, result['InstanceName'] = msrpctypes.unicode_to_string(data, pos, result['NameLength'] / 2, true)
pos = initial_pos + result['ByteLength']
return true, pos, result
end
---Parse a PERF_COUNTER_BLOCK structure. From Microsoft's documentation:
--
--<code>
-- typedef struct _PERF_COUNTER_BLOCK {
-- DWORD ByteLength; // Length in bytes of this structure,
-- // including the following counters
-- } PERF_COUNTER_BLOCK, *PPERF_COUNTER_BLOCK;
--
--</code>
--
--@param data The data being processed.
--@param pos The position within <code>data</code>.
--@return (status, pos, result) The status (true if successful), the new position in <code>data</code> (or an error
-- message), and a table representing the datatype, if any.
local function parse_perf_counter_block(data, pos)
local result = {}
pos, result['ByteLength'] = msrpctypes.unmarshall_int32(data, pos)
return true, pos, result
end
---Retrieve the parsed performance data from the given host for the requested
-- object values.
--
-- To get a list of possible object values, leave 'objects' blank and look at
-- <code>result['title_database']</code> -- it'll contain a list of indexes
-- that can be looked up. These indexes are passed as a string or as a series
-- of space-separated strings (eg, "230" for "Process" and "238" for "Process"
-- and "Processor").
--
--@param host The host object
--@param objects [optional] The space-separated list of object numbers to
-- retrieve. Default: only retrieve the database.
function get_performance_data(host, objects)
-- Create the SMB session
local status, smbstate = msrpc.start_smb(host, msrpc.WINREG_PATH)
if(status == false) then
return false, smbstate
end
-- Bind to WINREG service
local status, bind_result = msrpc.bind(smbstate, msrpc.WINREG_UUID, msrpc.WINREG_VERSION, nil)
if(status == false) then
msrpc.stop_smb(smbstate)
return false, bind_result
end
-- Open HKEY_PERFORMANCE_DATA
local status, openhkpd_result = msrpc.winreg_openhkpd(smbstate)
if(status == false) then
msrpc.stop_smb(smbstate)
return false, openhkpd_result
end
local status, queryvalue_result = msrpc.winreg_queryvalue(smbstate, openhkpd_result['handle'], "Counter 009")
if(status == false) then
msrpc.stop_smb(smbstate)
return false, queryvalue_result
end
-- Parse the title database
local pos = 1
local status
local result = {}
status, pos, result['title_database'] = parse_perf_title_database(queryvalue_result['value'], pos)
if(status == false) then
msrpc.stop_smb(smbstate)
return false, pos
end
result['title_database'][0] = "<null>"
if(objects ~= nil and #objects > 0) then
-- Query for the objects
local status, queryvalue_result = msrpc.winreg_queryvalue(smbstate, openhkpd_result['handle'], objects)
if(status == false) then
msrpc.stop_smb(smbstate)
return false, queryvalue_result
end
-- Parse the header
pos = 1
local status, data_block
status, pos, data_block = parse_perf_data_block(queryvalue_result['value'], pos)
if(status == false) then
msrpc.stop_smb(smbstate)
return false, pos
end
-- Move past the header
pos = 1 + data_block['HeaderLength']
-- Parse the data sections
for i = 1, data_block['NumObjectTypes'], 1 do
local object_start = pos
local counter_definitions = {}
local object_instances = {}
local counter_definitions = {}
-- Get the type of the object (this is basically the class definition -- info about the object instances)
local status, object_type
status, pos, object_type = parse_perf_object_type(queryvalue_result['value'], pos)
if(status == false) then
msrpc.stop_smb(smbstate)
return false, pos
end
-- Start setting up the result object
--stdnse.debug1("Index = %d\n", object_type['ObjectNameTitleIndex'])
local object_name = result['title_database'][object_type['ObjectNameTitleIndex']]
result[object_name] = {}
--stdnse.debug1("\n\nOBJECT: %s\n", object_name)
--stdnse.debug1(" Counters: %d\n", object_type['NumCounters'])
--stdnse.debug1(" Instances: %d\n", object_type['NumInstances'])
--stdnse.debug1("-----------------\n")
-- Bring the position to the beginning of the counter definitions
pos = object_start + object_type['HeaderLength']
-- Parse the counter definitions
for j = 1, object_type['NumCounters'], 1 do
status, pos, counter_definitions[j] = parse_perf_counter_definition(queryvalue_result['value'], pos)
if(status == false) then
msrpc.stop_smb(smbstate)
return false, pos
end
--stdnse.debug1(" Counter definition #%2d: [%d bytes] %s\n", j, counter_definitions[j]['CounterSize'], result['title_database'][counter_definitions[j]['CounterNameTitleIndex']])
end
-- Bring the position to the beginning of the instances (or counters)
pos = object_start + object_type['DefinitionLength']
-- Check if we have any instances (sometimes we don't -- if we don't, the value returned is a negative)
if (object_type['NumInstances'] & 0x80000000) == 0 then
-- Parse the object instances and counters
for j = 1, object_type['NumInstances'], 1 do
local instance_start = pos
-- Instance definition
local status
status, pos, object_instances[j] = parse_perf_instance_definition(queryvalue_result['value'], pos)
if(status == false) then
msrpc.stop_smb(smbstate)
return false, pos
end
-- Set up the instance array
local instance_name = object_instances[j]['InstanceName']
result[object_name][instance_name] = {}
-- Bring the pos to the start of the counter block
pos = instance_start + object_instances[j]['ByteLength']
--stdnse.debug1("\n INSTANCE: %s\n", instance_name)
--stdnse.debug1(" Length: %d\n", object_instances[j]['ByteLength'])
--stdnse.debug1(" NameOffset: %d\n", object_instances[j]['NameOffset'])
--stdnse.debug1(" NameLength: %d\n", object_instances[j]['NameLength'])
--stdnse.debug1(" --------------\n")
-- The counter block
local status, counter_block
status, pos, counter_block = parse_perf_counter_block(queryvalue_result['value'], pos)
if(status == false) then
msrpc.stop_smb(smbstate)
return false, pos
end
for k = 1, object_type['NumCounters'], 1 do
-- Each individual counter
local status, counter_result
status, pos, counter_result = parse_perf_counter(queryvalue_result['value'], pos, counter_definitions[k])
if(status == false) then
msrpc.stop_smb(smbstate)
return false, pos
end
local counter_name = result['title_database'][counter_definitions[k]['CounterNameTitleIndex']]
--stdnse.debug1(" %s: %s\n", counter_name, counter_result)
-- Save it in the result
result[object_name][instance_name][counter_name] = counter_result
end
-- Bring the pos to the end of the next section
pos = instance_start + object_instances[j]['ByteLength'] + counter_block['ByteLength']
end
else
for k = 1, object_type['NumCounters'], 1 do
-- Each individual counter
local status, counter_result
status, pos, counter_result = parse_perf_counter(queryvalue_result['value'], pos, counter_definitions[k])
if(status == false) then
msrpc.stop_smb(smbstate)
return false, pos
end
local counter_name = result['title_database'][counter_definitions[k]['CounterNameTitleIndex']]
--stdnse.debug1(" %s: %s\n", counter_name, counter_result)
-- Save it in the result
result[object_name][counter_name] = counter_result
end
end
end
-- Blank out the database
result['title_database'] = nil
end
msrpc.stop_smb(smbstate)
return true, result
end
return _ENV;
|