Class: Msf::Util::WindowsRegistryParser

Inherits:
Object
  • Object
show all
Defined in:
lib/msf/util/windows_registry_parser.rb

Defined Under Namespace

Classes: RegHash, RegHash2, RegHbin, RegHbinBlock, RegLf, RegLh, RegNk, RegRegf, RegRi, RegSk, RegVk

Constant Summary collapse

ROOT_KEY =

Constants

0x2c
REG_NONE =
0x00
REG_SZ =
0x01
REG_EXPAND_SZ =
0x02
REG_BINARY =
0x03
REG_DWORD =
0x04
REG_MULTISZ =
0x07
REG_QWORD =
0x0b
REGF_MAGIC =

REGF magic value: 'regf'

0x72656766
NK_MAGIC =

NK magic value: 'nk'

0x6E6B
VK_MAGIC =

VK magic value: 'vk'

0x766B
LF_MAGIC =

LF magic value: 'lf'

0X6C66
LH_MAGIC =

LH magic value: 'lh'

0X6C68
RI_MAGIC =

RI magic value: 'ri'

0X7269
SK_MAGIC =

SK magic value: 'sk'

0X7269
HBIN_MAGIC =

HBIN magic value: 'hbin'

0x6862696E

Instance Method Summary collapse

Constructor Details

#initialize(hive_data) ⇒ WindowsRegistryParser

Returns a new instance of WindowsRegistryParser.


176
177
178
179
180
# File 'lib/msf/util/windows_registry_parser.rb', line 176

def initialize(hive_data)
  @hive_data = hive_data.b
  @regf = RegRegf.read(hive_data)
  @root_key = find_root_key
end

Instance Method Details

#compare_hash(magic, hash_rec, key) ⇒ Object


263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
# File 'lib/msf/util/windows_registry_parser.rb', line 263

def compare_hash(magic, hash_rec, key)
  case magic
  when LF_MAGIC
    if hash_rec.key_name.gsub(/(^\x00*)|(\x00*$)/, '') == key[0,4]
      return hash_rec.offset_nk
    end
  when LH_MAGIC
    if hash_rec.key_name.unpack('<L').first == get_lh_hash(key)
      return hash_rec.offset_nk
    end
  when RI_MAGIC
    # Special case here, don't know exactly why, an RI pointing to a NK
    offset = hash_rec.offset_nk
    nk = get_block(offset)
    return offset if nk.key_name == key
  else
    raise ArgumentError, "Unknow magic: #{magic}"
  end
end

#enum_key(parent_key) ⇒ Object


322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
# File 'lib/msf/util/windows_registry_parser.rb', line 322

def enum_key(parent_key)
  unless parent_key&.data&.magic == NK_MAGIC
    raise ArgumentError, "enum_key: parent key must be a NK record"
  end
  block = get_block(parent_key.data.offset_sub_key_lf)
  records = []
  if block.data.magic == RI_MAGIC
    # ri points to lf/lh records, so we consolidate the hash records in the main records array
    block.data.hash_records.each do |hash_record|
      record = get_block(hash_record.offset_nk)
      records.concat(record.data.hash_records)
    end
  else
    records.concat(block.data.hash_records)
  end

  records.map do |reg_hash|
    nk = get_block(reg_hash.offset_nk)
    nk.data.key_name.to_s.b
  end
end

#enum_values(key) ⇒ Object


344
345
346
347
348
349
350
351
352
353
354
# File 'lib/msf/util/windows_registry_parser.rb', line 344

def enum_values(key)
  unless key&.data&.magic == NK_MAGIC
    raise ArgumentError, "enum_values: key must be a NK record"
  end
  res = []
  value_list = get_value_blocks(key.data.offset_value_list, key.data.num_values + 1)
  value_list.each do |value|
    res << (value.data.flag > 0 ? value.data.name : nil)
  end
  res
end

#find_key(key) ⇒ Object


214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
# File 'lib/msf/util/windows_registry_parser.rb', line 214

def find_key(key)
  # Let's strip '\' from the beginning, except for the case of
  # only asking for the root node
  key = key[1..-1] if key[0] == '\\' && key.size > 1

  parent_key = @root_key
  if key.size > 0 && key[0] != '\\'
    key.split('\\').each do |sub_key|
      res = find_sub_key(parent_key, sub_key)
      return nil unless res
      parent_key = res
    end
  end
  parent_key
end

#find_root_keyObject

Raises:

  • (StandardError)

182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
# File 'lib/msf/util/windows_registry_parser.rb', line 182

def find_root_key
  reg_hbin = nil
  # Split the data in 4096-bytes blocks
  @hive_data.unpack('a4096' * (@hive_data.size / 4096)).each do |data|
    next unless data[0,4] == 'hbin'
    reg_hbin = RegHbin.read(data)
    root_key = reg_hbin.reg_hbin_blocks.find do |block|
      block.data.respond_to?(:magic) && block.data.magic == NK_MAGIC && block.data.nk_type == ROOT_KEY
    end
    return root_key if root_key
  rescue IOError
    raise StandardError, 'Cannot parse the RegHbin structure'
  end
  raise StandardError, 'Cannot find the RootKey' unless reg_hbin
end

#find_sub_key(parent_key, sub_key) ⇒ Object


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
# File 'lib/msf/util/windows_registry_parser.rb', line 230

def find_sub_key(parent_key, sub_key)
  unless parent_key&.data&.magic == NK_MAGIC
    raise ArgumentError, "find_sub_key: parent key must be a NK record"
  end
  block = get_block(parent_key.data.offset_sub_key_lf)
  blocks = []
  if block.data.magic == RI_MAGIC
    # ri points to lf/lh records, so we consolidate them in the main blocks array
    block.data.hash_records.each do |hash_record|
      blocks << get_block(hash_record.offset_nk)
    end
  else
    blocks << block
  end

  # Let's search the hash records for the name
  blocks.each do |block|
    block.data.hash_records.each do |hash_record|
      res = compare_hash(block.data.magic, hash_record, sub_key)
      if res
        nk = get_block(res)
        return nk if nk.data.key_name == sub_key
      end
    end
  end

  nil
end

#get_block(offset) ⇒ Object


259
260
261
# File 'lib/msf/util/windows_registry_parser.rb', line 259

def get_block(offset)
  RegHbinBlock.read(@hive_data[4096+offset..-1])
end

#get_data(offset, count) ⇒ Object


318
319
320
# File 'lib/msf/util/windows_registry_parser.rb', line 318

def get_data(offset, count)
  @hive_data[4096+offset, count][4..-1]
end

#get_lh_hash(key) ⇒ Object

'lh' Subkey-List Hash Algorithm (from www.sentinelchicken.com/data/TheWindowsNTRegistryFileFormat.pdf (Appendix C))


284
285
286
287
288
289
290
291
# File 'lib/msf/util/windows_registry_parser.rb', line 284

def get_lh_hash(key)
  res = 0
  key.upcase.bytes do |byte|
    res *= 37
    res += byte.ord
  end
  return res % 0x100000000
end

#get_value(reg_key, reg_value = nil) ⇒ Object


198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
# File 'lib/msf/util/windows_registry_parser.rb', line 198

def get_value(reg_key, reg_value = nil)
  reg_key = find_key(reg_key)
  return nil unless reg_key

  if reg_key.data.num_values > 0
    value_list = get_value_blocks(reg_key.data.offset_value_list, reg_key.data.num_values + 1)
    value_list.each do |value|
      if value.data.name == reg_value.to_s ||
         reg_value.nil? && value.data.flag <= 0
        return value.data.value_type, get_value_data(value.data)
      end
    end
  end
  nil
end

#get_value_blocks(offset, count) ⇒ Object


293
294
295
296
297
298
299
300
301
302
303
304
305
306
# File 'lib/msf/util/windows_registry_parser.rb', line 293

def get_value_blocks(offset, count)
  value_list = []
  res = []
  count.times do |i|
    value_list << @hive_data[4096+offset+i*4, 4].unpack('<l').first
  end
  value_list.each do |value_offset|
    if value_offset > 0
      block = get_block(value_offset)
      res << block
    end
  end
  return res
end

#get_value_data(record) ⇒ Object


308
309
310
311
312
313
314
315
316
# File 'lib/msf/util/windows_registry_parser.rb', line 308

def get_value_data(record)
  unless record&.magic == VK_MAGIC
    raise ArgumentError, "get_value_data: record must be a VK record"
  end
  return '' if record.data_len == 0
  # if DataLen < 5 the value itself is stored in the Offset field
  return record.offset_data.to_binary_s if record.data_len < 0
  return self.get_data(record.offset_data, record.data_len + 4)
end