Class: MSRegHive

Inherits:
Object
  • Object
show all
Defined in:
lib/metadata/util/win32/ms-registry.rb

Constant Summary collapse

HBIN_SIZE =

Size of the HBIN data (as well as initiale REGF) segments

0x1000
REG_DATA_OFFSET =

All data offsets in the registry DO NOT include the first block (REGF) which is 0x1000 (same as HBIN) and the 4 byte ‘hbin’ signature

HBIN_SIZE + 0x4
REGISTRY_HEADER_REGF =

define registry structures

BinaryStruct.new([
  'a4',     :id,                    # ASCII "regf" = 0x66676572
  'i',      :updates1,              # update counter 1
  'i',      :updates2,              # update counter 2
  'Q',      :timestamp,           # last modified (WinNT format)
  'i',      :version_major,       # Version - Major Number
  'i',      :version_minor,       # Version - Minor Number
  'i',      :version_release,     # Version - Release Number
  'i',      :version_build,       # Version - Build Number
  'i',      :data_offset,         # Data offset
  'i',      :last_block,            # Offset of Last Block
  'i',      nil,                    # UNKNOWN for 4 =1
  'a64',    :name,                  # description - last 31 characters of Fully Qualified Hive Name (in Unicode)
  'a396',   nil,                    # UNKNOWN x396
  'i',      :checksum,              # checksum of all DWORDS (XORed) from 0x0000 to 0x01FB
])
SIZEOF_REGISTRY_HEADER_REGF =
REGISTRY_HEADER_REGF.size
REGISTRY_STRUCT_HBIN =
BinaryStruct.new([
  'a4',     :id,                    # ASCII "hbin" = 0x6E696268
  'i',      :offset_from_first,   # Offset from 1st hbin-Block
  'i',      :offset_to_next,        # Offset to the next hbin-Block
  'Q',      nil,                    # UNKNOWN for 8
  'Q',      :timestamp,           # last modified (WinNT format)
  'i',      :block_size,            # Block size (including the header!)
  'l',      :length,                # Negative if not used, positive otherwise. Always a multiple of 8
])
SIZEOF_REGISTRY_STRUCT_HBIN =
REGISTRY_STRUCT_HBIN.size
REGISTRY_STRUCT_NK =
BinaryStruct.new([
  'a2',     :id,                    # ASCII "nk" = 0x6B6E
  's',      :type,                  # REG_ROOT_KEY = 0x2C, REG_SUB_KEY = 0x20, REG_SYM_LINK = 0x10
  'Q',      :timestamp,
  'a4',     nil,                    # UNKNOWN
  'i',      :parent_offset,       # Offset of Owner/Parent key
  'V',      :num_subkeys,         # Number of Subkeys
  'a4',     nil,                    # UNKNOWN
  'i',      :subkeys_offset,
  'i',      :unknown_offset,
  'i',      :num_values,
  'i',      :values_offset,       # Points to a list of offsets of vk-records
  'i',      :sk_offset,
  'i',      :classname_offset,
  'a20',    nil,                    # UNKNOWN
  's',      :name_length,
  's',      :classname_length,
])
SIZEOF_REGISTRY_STRUCT_NK =
REGISTRY_STRUCT_NK.size
REGISTRY_STRUCT_LH =

# Subkey listing with hash of first 4 characters

BinaryStruct.new([
  'a2',     :id,                    # ASCII "lh" = 0x666E
  's',      :num_keys,              # number of keys
])
SIZEOF_REGISTRY_STRUCT_LH =
REGISTRY_STRUCT_LH.size
REGISTRY_STRUCT_VK =

# The vk-record consists information to a single value (value key).

BinaryStruct.new([
  'a2',     :id,                    # ASCII "vk" = 0x6B76
  's',      :name_length,
  'i',      :data_length,         # If top-bit set, offset contains the data
  'i',      :data_offset,
  'i',      :data_type,
  's',      :flag,                  # =1, has name, else no name (=Default).
  'a2',     nil,                    # UNKNOWN
])
SIZEOF_REGISTRY_STRUCT_VK =
REGISTRY_STRUCT_VK.size
REGISTRY_STRUCT_LH_HASH =

set STRUCT(REC-LH-HASH) {

BinaryStruct.new([ #   set STRUCT(REC-LH-HASH) {
  'i',      :offset_nk,           # offset of corresponding NK record
  'a4',     :keyname,             # Key Name
])
SIZEOF_REGISTRY_STRUCT_LH_HASH =
REGISTRY_STRUCT_LH_HASH.size
REGISTRY_STRUCT_VK_OFFSET =

set STRUCT(REC-LH-HASH) {

BinaryStruct.new([ #   set STRUCT(REC-LH-HASH) {
  'i',      :offset_vk,           # offset of corresponding NK record
])
SIZEOF_REGISTRY_STRUCT_VK_OFFSET =
REGISTRY_STRUCT_VK_OFFSET.size
REGISTRY_STRUCT_LF =

The lf-record is the counterpart to the RGKN-record (the hash-function)

BinaryStruct.new([
  'a2',     :id,                    # ASCII "lf" = 0x666C
  's',      :num_keys,              # number of keys
])
SIZEOF_REGISTRY_STRUCT_LF =
REGISTRY_STRUCT_LF.size
REGISTRY_STRUCT_LF_HASH =
BinaryStruct.new([
  'i',      :offset_nk,           # offset of corresponding NK record
  'a4',     :keyname,             # Key Name
])
SIZEOF_REGISTRY_STRUCT_LF_HASH =
REGISTRY_STRUCT_LF_HASH.size
REGISTRY_STRUCT_RI =

A list of offsets to LI/LH records

BinaryStruct.new([
  'a2',     :id,                    # ASCII "ri" = 0x6972
  's',      :num_keys,              # number of keys
])
SIZEOF_REGISTRY_STRUCT_RI =
REGISTRY_STRUCT_RI.size
REGISTRY_STRUCT_RI_OFFSET =

set STRUCT(REC-LH-HASH) {

BinaryStruct.new([ #   set STRUCT(REC-LH-HASH) {
  'i',      :offset_ri,           # offset of corresponding NK record
])
SIZEOF_REGISTRY_STRUCT_RI_OFFSET =
REGISTRY_STRUCT_RI_OFFSET.size
KEY_TYPES =

Return registry key type. Otherwise return the hex value of the integer

Hash.new { |_h, k| "%08X" % k }

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(path, hiveName, xmlNode, fs = "M:/", filter = nil) ⇒ MSRegHive

Returns a new instance of MSRegHive.



24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# File 'lib/metadata/util/win32/ms-registry.rb', line 24

def initialize(path, hiveName, xmlNode, fs = "M:/", filter = nil)
  @RegPath = path.gsub(/^"/, "").gsub(/"$/, "")
  @hiveName = hiveName
  @xmlNode = xmlNode
  @fs = fs if fs.kind_of?(MiqFS)
  @expandEnv = {'%SystemDrive%' => 'C:', "%SystemRoot%" => "\\Windows", "%ProgramFiles%" => "\\Program Files"}
  @fileLoadTime, @fileParseTime = nil, nil
  @ccsIdx = 1     # CurrentControlSet default index
  @ccsName = "controlset%03d" % @ccsIdx
  @stats = {:cache_hits => 0, :file_reads => 0, :bytes_read => 0}
  @hbin = {}

  # Load up filters
  @filter_value = {}
  @filter = init_filters(filter)

  # Collect DigitalProductKeys as we find them for processing later
  @digitalProductKeys = []
end

Instance Attribute Details

#digitalProductKeysObject (readonly)

Returns the value of attribute digitalProductKeys.



16
17
18
# File 'lib/metadata/util/win32/ms-registry.rb', line 16

def digitalProductKeys
  @digitalProductKeys
end

#fileLoadTimeObject (readonly)

Returns the value of attribute fileLoadTime.



16
17
18
# File 'lib/metadata/util/win32/ms-registry.rb', line 16

def fileLoadTime
  @fileLoadTime
end

#fileParseTimeObject (readonly)

Returns the value of attribute fileParseTime.



16
17
18
# File 'lib/metadata/util/win32/ms-registry.rb', line 16

def fileParseTime
  @fileParseTime
end

#xmlNodeObject (readonly)

Returns the value of attribute xmlNode.



16
17
18
# File 'lib/metadata/util/win32/ms-registry.rb', line 16

def xmlNode
  @xmlNode
end

Class Method Details

.isRegBinary(data) ⇒ Object



426
427
428
# File 'lib/metadata/util/win32/ms-registry.rb', line 426

def self.isRegBinary(data)
  data =~ /^[0-9a-fA-F]{2}(,[0-9a-fA-F]{2})*$/
end

.rawBinaryToRegBinary(data) ⇒ Object



435
436
437
# File 'lib/metadata/util/win32/ms-registry.rb', line 435

def self.rawBinaryToRegBinary(data)
  data.unpack("H*")[0].scan(/../).join(',')
end

.regBinaryToRawBinary(data) ⇒ Object

Raises:

  • (ArgumentError)


430
431
432
433
# File 'lib/metadata/util/win32/ms-registry.rb', line 430

def self.regBinaryToRawBinary(data)
  raise ArgumentError unless isRegBinary(data)
  [data.delete(',')].pack("H*")
end

.wtime2time(wtime) ⇒ Object



420
421
422
423
424
# File 'lib/metadata/util/win32/ms-registry.rb', line 420

def self.wtime2time(wtime)
  Time.at((wtime - 116444736000000000) / 10000000).getutc
rescue RangeError
  return nil
end

Instance Method Details

#checkFilters(subKey, fqName, level) ⇒ Object



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
# File 'lib/metadata/util/win32/ms-registry.rb', line 170

def checkFilters(subKey, fqName, level)
  return true if @filter.nil?  # If there are no filters get out
  match = false
  # allNil = true
  alevel = level - 1

  @filter.each do |f|
    #      $log.debug "Filer [#{f[level]}]"
    #      allNil = false unless f[level].nil?
    if f[:key][alevel].nil? && fqName.downcase.index(f[:key_path])
      match = true if f[:depth].to_i == 0

      filter_depth = f[:depth] - 1 + f[:key].length
      if filter_depth >= level
        # $log.fatal "REG FILTER 1 fqName:[#{fqName.downcase}] - f[path]:[#{f[:key].join('\\')}] - depth:[#{filter_depth}] -- level:[#{level}]"
        match = true
        break
      end
    end
    if match == false && !f[:key][alevel].nil? && f[:key][alevel] == subKey
      match = true
      break
    end
  end
  #    $log.debug "match [#{match}]  allNil [#{allNil}]"
  #    return true if allNil == true # There were no filters specified at this depth
  match
end

#closeObject



69
70
71
72
73
# File 'lib/metadata/util/win32/ms-registry.rb', line 69

def close
  # Force memory cleanup
  @hbin = nil
  GC.start
end

#create_filter_hash(filter) ⇒ Object



56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/metadata/util/win32/ms-registry.rb', line 56

def create_filter_hash(filter)
  if filter.kind_of?(Hash)
    nh = filter
    nh[:key] = nh[:key].downcase.split("/")
    nh[:value].each { |v| @filter_value[v.downcase] = true } if nh[:value].kind_of?(Array)
  else
    nh = {:key => filter.downcase.split("/")}
  end
  nh[:key_path] = nh[:key].join('\\')
  nh[:depth] = nh[:depth].to_i
  nh
end

#determine_current_control_setObject



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
# File 'lib/metadata/util/win32/ms-registry.rb', line 134

def determine_current_control_set
  $log.debug "Determining ControlControlSet index"
  save_filters = @filter
  @filter = [create_filter_hash('select')]
  # Start parsing the registry based on the data offset stored in the first record
  # ccsNode = MiqXml.newNode(nil, REXML)
  ccsNode = XmlHash::Document.new("ccs")
  parseRecord(@hiveHash[:data_offset], ccsNode, @hiveName, 0)
  # idx = ccsNode.find_first("//value[@name\"Current\"]")
  @ccsIdx = 0
  ccsNode.elements[1].each_element_with_attribute(:name, "Current") { |e| @ccsIdx = e.text }
  @ccsIdx = 1 if @ccsIdx == 0
  @ccsName = "controlset%03d" % @ccsIdx
  @filter = save_filters
  # Search through the filter list and change any "CurrentControlSet" values to the proper idx
  if @filter
    @filter.each do |a1|
      if a1[:key][0] == "currentcontrolset"
        a1[:key][0] = @ccsName
        a1[:key_path] = a1[:key].join('\\')
      end
    end
  end
  $log.debug "ControlControlSet index will be set to [#{@ccsIdx}]"
end

#getHashObject



439
440
441
# File 'lib/metadata/util/win32/ms-registry.rb', line 439

def getHash
  @hiveHash
end

#getRegBinary(vkHash) ⇒ Object



373
374
375
376
377
378
379
380
381
382
# File 'lib/metadata/util/win32/ms-registry.rb', line 373

def getRegBinary(vkHash)
  if (vkHash[:data_length] & 0x80000000) == 0
    res = self.class.rawBinaryToRegBinary(read_buffer(vkHash[:data_offset], vkHash[:data_length] - 1))
  else
    res = vkHash[:data_offset].to_s(16).rjust(8, '0')
    res = "#{res[6..7]},#{res[4..5]},#{res[2..3]},#{res[0..1]}"
  end

  res
end

#getRegMultiString(vkHash) ⇒ Object



331
332
333
334
335
336
337
338
339
340
341
# File 'lib/metadata/util/win32/ms-registry.rb', line 331

def getRegMultiString(vkHash)
  # $log.debug sprintf("data offset: (0x%X)  Length: [%d]", vkHash['data_offset']+REG_DATA_OFFSET, vkHash['data_length'])
  if vkHash[:data_offset] < 0
    # $log.warn "Invalid offset for multi-string data Key:[#{fqName}] Value:[#{vkHash[:data_name]}] Offset:[#{vkHash[:data_offset]}]"
    return
  end
  vkHash[:data] = read_buffer(vkHash[:data_offset], vkHash[:data_length] - 1)
  vkHash[:data].UnicodeToUtf8!.strip!
ensure
  vkHash[:data].tr!("\0", "\n") unless vkHash[:data].nil?
end

#getRegString(vkHash, key_type) ⇒ Object



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
# File 'lib/metadata/util/win32/ms-registry.rb', line 343

def getRegString(vkHash, key_type)
  # $log.debug sprintf("data offset: (0x%X)  Length: [%d]", vkHash['data_offset']+REG_DATA_OFFSET, vkHash['data_length'])
  if (vkHash[:data_length] & 0x80000000) == 0
    vkHash[:data] = read_buffer(vkHash[:data_offset], vkHash[:data_length] - 1)
    begin
      vkHash[:data].UnicodeToUtf8!
    rescue
      # Since we are getting Unicode strings out of the registry they should be even numbers lengths
      if vkHash[:data_length].remainder(2) == 1
        vkHash[:data] = read_buffer(vkHash[:data_offset], vkHash[:data_length] - 2)
        vkHash[:data].UnicodeToUtf8!
      else
        raise $!
      end
    end
  else
    vkHash[:data] = (vkHash[:data_offset] & 0xFF).chr
  end

  # Truncate string at the first null character
  if i = vkHash[:data].index("\0")
    vkHash[:data] = vkHash[:data][0...i]
  end

  # Resolve expand keys
  @expandEnv.each_pair { |k, v| vkHash[:data].gsub!(k, v) } if key_type == :REG_EXPAND_SZ

  vkHash[:data]
end

#init_filters(filter) ⇒ Object



44
45
46
47
48
49
50
51
52
53
54
# File 'lib/metadata/util/win32/ms-registry.rb', line 44

def init_filters(filter)
  if filter.nil?
    @filter_value = nil if @filter_value.empty?
    return nil
  end
  filters = filter.collect { |f| create_filter_hash(f) }
  filters.compact!
  filters = nil if filters.empty?
  @filter_value = nil if @filter_value.empty?
  filters
end

#load_environment_variablesObject



121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/metadata/util/win32/ms-registry.rb', line 121

def load_environment_variables
  $log.debug "Determining ControlControlSet index"
  save_filters = @filter
  @filter = [create_filter_hash("#{@ccsName}/Control/Session Manager/Environment".downcase.split("/"))]
  # Start parsing the registry based on the data offset stored in the first record
  ccsNode = MiqXml.newNode
  parseRecord(@hiveHash[:data_offset], ccsNode, @hiveName, 0)
  @filter = save_filters

  # ccsNode.write(STDOUT,0)
  # @expandEnv = {"%SystemRoot%"=>"\\Windows", "%ProgramFiles%"=>"\\Program Files"}
end

#load_sections(idx) ⇒ Object



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
# File 'lib/metadata/util/win32/ms-registry.rb', line 454

def load_sections(idx)
  if @hbin.key?(idx)
    @stats[:cache_hits] += 1 if DEBUG_FILE_READS
    # If the hash points to data return its index.  Otherwise the hash
    # will point to the index of the starting block of data
    return @hbin[idx].kind_of?(Integer) ? @hbin[idx] : idx
  else
    @hbin[idx] = read_hbin(idx)
    binHash = REGISTRY_STRUCT_HBIN.decode(@hbin[idx][0, SIZEOF_REGISTRY_STRUCT_HBIN])

    unless binHash[:id] == 'hbin'
      # If the block does not start with the header sign then back up and find it so
      # we can load the full hbin which spans several block
      while binHash[:id] != 'hbin'
        binHash = REGISTRY_STRUCT_HBIN.decode(read_hbin(idx -= 1)[0, SIZEOF_REGISTRY_STRUCT_HBIN])
      end
    end

    # Determine if the hbin is more than one block
    hbin_count = binHash[:offset_to_next] / HBIN_SIZE
    if hbin_count > 1
      @hbin[idx] = read_hbin(idx, hbin_count)
      # Set contiguous blocks with the index of the starting block
      (idx + 1).upto(idx + hbin_count - 1) { |i| @hbin[i] = idx }
    end
    return idx
  end
end

#parseHivesObject



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
# File 'lib/metadata/util/win32/ms-registry.rb', line 75

def parseHives
  startTime = Time.now

  # Reads in the registry file and does some basic validation
  validateRegFile(File.join(@RegPath, @hiveName))

  @fileLoadTime = Time.now - startTime
  $log.info "Registry Load/Validate time = #{@fileLoadTime} sec" if DEBUG_LOG_PERFORMANCE

  startTime = Time.now
  pre_process

  # Start parsing the registry based on the data offset stored in the first record
  if @hiveName == 'ntuser.dat'
    parseRecord(@hiveHash[:data_offset], @xmlNode, nil, 0)
  else
    parseRecord(@hiveHash[:data_offset], @xmlNode, @hiveName, 0)
  end

  post_process

  @fileParseTime = Time.now - startTime
  parseStats = "Registry Parsing time = #{@fileParseTime} sec.  registry segments loaded:[#{@hbin.length}]" # if DEBUG_LOG_PERFORMANCE
  parseStats += "  Stats:[#{@stats.inspect}]" if DEBUG_FILE_READS
  $log.info parseStats
end

#parseRecord(offset, xmlNode, fqName, level) ⇒ Object



160
161
162
163
164
165
166
167
168
# File 'lib/metadata/util/win32/ms-registry.rb', line 160

def parseRecord(offset, xmlNode, fqName, level)
  type = read_buffer(offset, 1).downcase
  $log.debug sprintf("TYPE = [%s] at offset [0x%08x]", type, offset + REG_DATA_OFFSET) if DEBUG_PRINT
  begin
    send("parseRecord#{type}", offset, xmlNode, fqName, level)
  rescue => err
    $log.warn sprintf("Unhandled type encountered [%s] at file offset [0x%08X].  Msg:[#{err}]", type, offset + REG_DATA_OFFSET) if DEBUG_UNHANDLED_DATA
  end
end

#parseRecordlf(offset, xmlNode, fqName, level) ⇒ Object



264
265
266
267
268
269
270
271
272
273
274
275
276
# File 'lib/metadata/util/win32/ms-registry.rb', line 264

def parseRecordlf(offset, xmlNode, fqName, level)
  # $log.debug "parseRecordLF at offset #{offset}"
  lfHash = REGISTRY_STRUCT_LF.decode(read_buffer(offset, SIZEOF_REGISTRY_STRUCT_LF))

  if lfHash[:num_keys] > 0
    key_offset = offset + SIZEOF_REGISTRY_STRUCT_LF
    lfHash[:num_keys].times do
      hash = REGISTRY_STRUCT_LF_HASH.decode(read_buffer(key_offset, SIZEOF_REGISTRY_STRUCT_LF_HASH))
      parseRecord hash[:offset_nk], xmlNode, fqName, level
      key_offset += SIZEOF_REGISTRY_STRUCT_LF_HASH
    end
  end
end

#parseRecordlh(offset, xmlNode, fqName, level) ⇒ Object



278
279
280
281
282
283
284
285
286
287
288
289
290
# File 'lib/metadata/util/win32/ms-registry.rb', line 278

def parseRecordlh(offset, xmlNode, fqName, level)
  # $log.debug "parseRecordLH at offset #{offset}"
  lhHash = REGISTRY_STRUCT_LH.decode(read_buffer(offset, SIZEOF_REGISTRY_STRUCT_LH))

  if lhHash[:num_keys] > 0
    key_offset = offset + SIZEOF_REGISTRY_STRUCT_LH
    lhHash[:num_keys].times do
      hash = REGISTRY_STRUCT_LH_HASH.decode(read_buffer(key_offset, SIZEOF_REGISTRY_STRUCT_LH_HASH))
      parseRecord hash[:offset_nk], xmlNode, fqName, level
      key_offset += SIZEOF_REGISTRY_STRUCT_LH_HASH
    end
  end
end

#parseRecordnk(offset, xmlNode, fqName, level) ⇒ Object



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
# File 'lib/metadata/util/win32/ms-registry.rb', line 199

def parseRecordnk(offset, xmlNode, fqName, level)
  nkHash = REGISTRY_STRUCT_NK.decode(read_buffer(offset, SIZEOF_REGISTRY_STRUCT_NK))
  # Convert the type from hex to text
  nkHash[:type_display] = typeToString(nkHash[:type])
  # Get the keyname which is just beyond the structure
  nkHash[:keyname] = read_buffer(offset + SIZEOF_REGISTRY_STRUCT_NK, nkHash[:name_length] - 1).chomp("\0")

  DumpHash.SortPrint(nkHash, :NK)

  # $log.debug "parseRecordNK [#{xmlNode}] [#{xmlNode.class}] [#{nkHash[:keyname]}] [#{nkHash[:type_display]}]"
  if nkHash[:type_display] == :SUB
    level += 1
    if fqName.nil?
      fqName = nkHash[:keyname].chomp
    else
      fqName += "\\#{nkHash[:keyname].chomp}"
    end
    # $log.debug "Fully Q Name: [#{level}]  [#{nkHash['keyname'].chomp}]  [#{fqName}]"
    # Check sub-directory filters
    # return unless checkFilters(nkHash['keyname'].chomp.downcase, level-1)
    cf = checkFilters(nkHash[:keyname].chomp.downcase, fqName, level)
    # $log.debug "Fully Q Name: [#{"%5s" % cf}] [#{fqName}]  [#{level}]"
    # $log.debug "Fully Q Name: [#{fqName}]  [#{level}]"
    return unless cf
    # xmlSubNode = xmlNode
    xmlSubNode = xmlNode.add_element(:key, :keyname => nkHash[:keyname].chomp, :fqname => fqName)
    # on_start_element(:key, {:keyname=>nkHash[:keyname].chomp, :fqname=>fqName})
  else
    xmlSubNode = xmlNode
  end

  # Process all values
  if nkHash[:num_values] > 0
    vkOffset = nkHash[:values_offset]
    nkHash[:num_values].times do
      vkHash = REGISTRY_STRUCT_VK_OFFSET.decode(read_buffer(vkOffset, SIZEOF_REGISTRY_STRUCT_VK_OFFSET))
      parseRecord(vkHash[:offset_vk], xmlSubNode, fqName, level)
      vkOffset += SIZEOF_REGISTRY_STRUCT_VK_OFFSET
    end
  end

  # Process all subkeys
  if nkHash[:num_subkeys] > 0
    parseRecord(nkHash[:subkeys_offset], xmlSubNode, fqName, level)
  end

  # on_end_element(:key)
end

#parseRecordri(offset, xmlNode, fqName, level) ⇒ Object



248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
# File 'lib/metadata/util/win32/ms-registry.rb', line 248

def parseRecordri(offset, xmlNode, fqName, level)
  #        $log.debug "parseRecordRI at offset #{offset}"
  riHash = REGISTRY_STRUCT_RI.decode(read_buffer(offset, SIZEOF_REGISTRY_STRUCT_RI))

  DumpHash.SortPrint(riHash, :RI)

  if riHash[:num_keys] > 0
    key_offset = offset + SIZEOF_REGISTRY_STRUCT_RI
    riHash[:num_keys].times do
      hash = REGISTRY_STRUCT_RI_OFFSET.decode(read_buffer(key_offset, SIZEOF_REGISTRY_STRUCT_RI_OFFSET))
      parseRecord hash[:offset_ri], xmlNode, fqName, level
      key_offset += SIZEOF_REGISTRY_STRUCT_RI_OFFSET
    end
  end
end

#parseRecordvk(offset, xmlNode, _fqName, _level) ⇒ Object



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
# File 'lib/metadata/util/win32/ms-registry.rb', line 292

def parseRecordvk(offset, xmlNode, _fqName, _level)
  # $log.debug "parseRecordVK at offset #{offset}"
  vkHash = REGISTRY_STRUCT_VK.decode(read_buffer(offset, SIZEOF_REGISTRY_STRUCT_VK))
  vkHash[:data_type_display] = KEY_TYPES[vkHash[:data_type]]
  if vkHash[:name_length] == 0
    vkHash[:data_name] = "(Default)"
  else
    vkHash[:data_name] = read_buffer(offset + SIZEOF_REGISTRY_STRUCT_VK, vkHash[:name_length] - 1)
  end

  # Check value filters here
  return if @filter_value && !@filter_value.key?(vkHash[:data_name].downcase)

  begin
    case vkHash[:data_type_display]
    when :REG_SZ, :REG_EXPAND_SZ then vkHash[:data] = getRegString(vkHash, vkHash[:data_type_display])
    when :REG_DWORD    then vkHash[:data] = vkHash[:data_offset]
    when :REG_NONE     then vkHash[:data] = "(zero-length binary value)"
    when :REG_BINARY   then vkHash[:data] = getRegBinary(vkHash)
    when :REG_QWORD    then vkHash[:data] = read_buffer(vkHash[:data_offset], 8).unpack("Q").join.to_i
    when :REG_MULTI_SZ then vkHash[:data] = getRegMultiString(vkHash)
    else
      # Ignore types: REG_RESOURCE_REQUIREMENTS_LIST and REG_RESOURCE_LIS
      if DEBUG_UNHANDLED_DATA
        $log.warn "Unhandled vk record type of [#{vkHash[:data_type]}] [#{vkHash[:data_type_display]}]" unless vkHash[:data_type] == 8 || vkHash[:data_type] == 10 || vkHash[:data_type] >= 12
      end
    end

  ensure
    DumpHash.SortPrint(vkHash, :VK)
    # xmlSubNode = xmlNode
    xmlSubNode = xmlNode.add_element(:value, :type => vkHash[:data_type_display], :name => vkHash[:data_name])
    xmlSubNode.text = vkHash[:data]

    # This is a performance hack right now since searching the whole xml doc for DigitalProductIds takes so long.
    @digitalProductKeys << xmlSubNode if vkHash[:data_name].downcase == "digitalproductid"
  end
end

#post_processObject



111
112
113
114
115
116
117
118
119
# File 'lib/metadata/util/win32/ms-registry.rb', line 111

def post_process
  if @hiveName == "system"
    ccsNode = MIQRexml.findRegElement("HKEY_LOCAL_MACHINE\\SYSTEM\\#{@ccsName}", @xmlNode.root)
    if ccsNode
      $log.debug "Changing [#{@ccsName}] to CurrentControlSet"
      ccsNode.add_attribute(:keyname, 'CurrentControlSet')
    end
  end
end

#pre_processObject



102
103
104
105
106
107
108
109
# File 'lib/metadata/util/win32/ms-registry.rb', line 102

def pre_process
  # Determine what System/ControlSet00? to use when CurrentControlSet is
  # referenced and update the filter list.
  determine_current_control_set if @hiveName == "system"

  # Load environment variables to be used to "expand string" (REG_EXPAND_SZ) resolution.
  # load_environment_variables
end

#read_buffer(start_offset, data_length) ⇒ Object



443
444
445
446
447
448
449
450
451
452
# File 'lib/metadata/util/win32/ms-registry.rb', line 443

def read_buffer(start_offset, data_length)
  # Adjust offset so it matches the length of the actual registry hive file.
  start_offset += REG_DATA_OFFSET

  # Find what hbin section this data is in.  Also loads data from file if it is not already in memory
  idx = load_sections(start_offset / HBIN_SIZE)

  # Subtract the section offset from the full offset to get the position inside the buffer
  @hbin[idx][start_offset - (idx * HBIN_SIZE), data_length + 1]
end

#read_hbin(idx, count = 1) ⇒ Object



483
484
485
486
487
488
489
490
491
492
# File 'lib/metadata/util/win32/ms-registry.rb', line 483

def read_hbin(idx, count = 1)
  startAddr = idx * HBIN_SIZE
  readCount = (HBIN_SIZE * count)
  if DEBUG_FILE_READS
    @stats[:file_reads] += 1
    @stats[:bytes_read] += readCount
  end
  @fileHnd.seek(startAddr, IO::SEEK_SET)
  @fileHnd.read(readCount)
end

#typeToString(type) ⇒ Object



410
411
412
413
414
415
416
417
418
# File 'lib/metadata/util/win32/ms-registry.rb', line 410

def typeToString(type)
  case
  when type == 44 then :ROOT
  when type == 32 then :SUB
  when type == 4128 then :SUB
  when type == 16 then :LINK
  else                 :UNKNOWN
  end
end

#validateRegFile(fileName) ⇒ Object



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
# File 'lib/metadata/util/win32/ms-registry.rb', line 384

def validateRegFile(fileName)
  t0 = Time.now
  # Do some basic file validation

  fileObj = @fs ? @fs : File
  raise "Registry file [#{fileName}] does not exist." if fileObj.send(@fs ? :fileExists? : :exist?, fileName) == false
  regSize = fileObj.send(@fs ? :fileSize : :size, fileName)
  raise "Registry file [#{fileName}] is empty." if regSize.zero?
  @fileHnd = fileObj.send(@fs ? :fileOpen : :open, fileName, 'rb')
  regf_buf = read_hbin(0)

  raise "Registry file [#{fileName}] does not contain valid marker." if regf_buf[0, 4] != "regf"
  $log.info  "Reading #{fileName} with size (#{regSize})" if DEBUG_PRINT

  # Read in Registry header
  head_string = regf_buf[0, SIZEOF_REGISTRY_HEADER_REGF]
  raise "Registry hive [#{fileName}] does not contain a valid header." unless head_string
  @hiveHash = REGISTRY_HEADER_REGF.decode(head_string)
  @hiveHash[:name].UnicodeToUtf8!.strip!

  # Dump sorted hash results
  DumpHash.SortPrint(@hiveHash, :REGF)

  $log.info "Registry hive [#{File.basename(@hiveHash[:name])}] successfully opened for reading in [#{Time.now - t0}] seconds.  Size:[#{regSize}]  Last registry update: [#{MSRegHive.wtime2time(@hiveHash[:timestamp])}]"
end