home *** CD-ROM | disk | FTP | other *** search
/ Hackers Magazine 57 / CdHackersMagazineNr57.iso / Software / Networking / nmap-5.00-setup.exe / nselib / msrpcperformance.lua < prev    next >
Text File  |  2009-07-06  |  29KB  |  612 lines

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