Handle documented key type and unkown one + add code to handle badly encoded values (REG_MULTI_SZ notably)

This commit is contained in:
hakril
2019-08-07 14:48:26 +02:00
parent 670bb713a2
commit 8a32e00f68
2 changed files with 81 additions and 18 deletions
+39 -3
View File
@@ -34,16 +34,46 @@ def test_registry_set_get_simple_values(value):
# TODO: test with other registry type (the stranges ones)
@pytest.mark.parametrize("value, type", [
(0x11223344, gdef.REG_DWORD),
(0x1122334455667788, gdef.REG_QWORD),
(0x11223344, gdef.REG_DWORD), # same as gdef.REG_DWORD_LITTLE_ENDIAN
(0x11223344, gdef.REG_DWORD_BIG_ENDIAN),
(0x1122334455667788, gdef.REG_QWORD), # same as gdef.REG_QWORD_LITTLE_ENDIAN
("", gdef.REG_SZ),
("Hello world", gdef.REG_SZ),
("Hello world %path%", gdef.REG_EXPAND_SZ),
(["AAAA", "BBBB", "CCCC"], gdef.REG_MULTI_SZ),
# Binary format and associated
("123\x00123" + "".join(chr(c) for c in range(256)), gdef.REG_BINARY),
("Hello world", gdef.REG_LINK),
("", gdef.REG_NONE),
("Not really None :)\x11\x22\x00ABCD", gdef.REG_NONE),
("Test-Unknown-format", 0x11223344), # Unknown registry type
("Test-Unknown-format\x00\x01\xff\xfe Lol", 0xffffffff), # Unknown registry type
])
def test_registry_set_get_simple_values_with_types(value, type):
basekeytest["tst2"] = (value, type)
assert basekeytest["tst2"].value == value
@pytest.mark.parametrize("value, type", [
# "\xff\xd8".decode("utf-16") -> UnicodeDecodeError
("\xff\xd8", gdef.REG_MULTI_SZ),
# Is NOT valid UTF-16 (len == 33)
("Hello\x00World\x00This is not unicode\x00\x00", gdef.REG_MULTI_SZ),
# Is valid UTF-16 (len == 40)
# Should the decoding be completly different ?
("Hello\x00World\x00This is not really unicode\x00\x00", gdef.REG_MULTI_SZ),
])
def test_registry_badly_encoded_values(value, type):
# Bypass any encoding logic to setup bad key
keyname = "bad_encoding"
buffer = windows.utils.BUFFER(gdef.BYTE).from_buffer_copy(value)
windows.winproxy.RegSetValueExW(basekeytest.phkey, keyname, 0, type, buffer, len(buffer))
# Not the best decoded value
# But should not crash
assert basekeytest[keyname]
UNICODE_PATH_NAME = u'\u4e2d\u56fd\u94f6\u884c\u7f51\u94f6\u52a9\u624b'
UNICODE_RU_STRING = u"\u0441\u0443\u043a\u0430\u0020\u0431\u043b\u044f\u0442\u044c" # CYKA BLYAT in Cyrillic
@@ -51,7 +81,13 @@ UNICODE_RU_STRING = u"\u0441\u0443\u043a\u0430\u0020\u0431\u043b\u044f\u0442\u04
# But was the cause a special bug / reimplem due to _winreg using ANSI functions
# So create a special test with a very identifiable name / bug cause
@pytest.mark.parametrize("unistr", ['\u52a9' * 126, UNICODE_PATH_NAME, UNICODE_RU_STRING, u""])
@pytest.mark.parametrize("unistr", [
u'c:\\users\\hakril\\appdata\\local\\temp\\test_unicode_\u4e2d\u56fd\u94f6\u884c\u7f51\u94f6\u52a9\u624bdbqsm3',
'\u52a9' * 126,
UNICODE_PATH_NAME,
UNICODE_RU_STRING,
u""
])
def test_registry_unicode_string_value(unistr):
basekeytest["tst3"] = unistr
assert basekeytest["tst3"].value == unistr
+42 -15
View File
@@ -2,7 +2,7 @@ import sys
import ctypes
import itertools
import struct
from collections import namedtuple
from collections import namedtuple, defaultdict
import windows
from windows.dbgprint import dbgprint
@@ -51,6 +51,15 @@ def Reg2Py_DWORD(buffer, size):
def Py2Reg_DWORD(obj):
return struct.pack("<I", obj)
def Reg2Py_DWORD_BIG_ENDIAN(buffer, size):
# Check size ?
return (buffer[0] << 24) + (buffer[1] << 16) + (buffer[2] << 8) + buffer[3]
def Py2Reg_DWORD_BIG_ENDIAN(obj):
return struct.pack(">I", obj)
def Reg2Py_BINARY(buffer, size):
return str(bytearray(buffer[:size]))
@@ -74,14 +83,18 @@ def Py2Reg_SZ(obj):
def Reg2Py_Multi_SZ(buffer, size):
if not size:
return []
# Simple path
rawstr = "".join([chr(c) for c in buffer[:size]])
if rawstr[-4:] != "\x00\x00\x00\x00": # 2 UNICODE NULL bytes (4 \x00)
rawstr += "\x00\x00\x00\x00"
# Decode as utf-16 to get multiple unicode string sepated by NULL BYTE
unistr = rawstr.decode(WENCODING)
# Remove final \x00
unistr = unistr[:-2] # 2 UTF-16 NULL BITS (was 4 bits in encoded)
return unistr.split(u"\x00") # Return as list of string
try:
unistr = rawstr.decode(WENCODING)
return unistr.rstrip(u"\x00").split(u"\x00")
except UnicodeDecodeError as e:
pass
# Complexe-path
# This is not some valide UTF-16
# Try our best to extract some stuff from raw
return rawstr.rstrip("\x00").split("\x00")
def Py2Reg_Multi_SZ(obj):
# Work on encoded values (to prevent str/unicode errors)
@@ -91,19 +104,33 @@ def Py2Reg_Multi_SZ(obj):
# Add UTF-16 NULL byte for final string + final UTF-16 \x00 (4 \x00)
return uni_str + "\x00\x00\x00\x00"
DECODE_METHOD = 0
ENCODE_METHOD = 1
ENCODE_DECODE_METHODS = {
KNOWN_ENCODE_DECODE_METHODS = {
gdef.REG_SZ: (Reg2Py_SZ, Py2Reg_SZ),
gdef.REG_EXPAND_SZ: (Reg2Py_SZ, Py2Reg_SZ),
gdef.REG_MULTI_SZ: (Reg2Py_Multi_SZ, Py2Reg_Multi_SZ),
gdef.REG_BINARY: (Reg2Py_BINARY, Py2Reg_BINARY),
gdef.REG_DWORD: (Reg2Py_DWORD, Py2Reg_DWORD),
gdef.REG_DWORD_BIG_ENDIAN: (Reg2Py_DWORD_BIG_ENDIAN, Py2Reg_DWORD_BIG_ENDIAN),
gdef.REG_QWORD: (Reg2Py_QWORD, Py2Reg_QWORD),
# Binary formats
gdef.REG_LINK: (Reg2Py_BINARY, Py2Reg_BINARY), # TESTING
gdef.REG_BINARY: (Reg2Py_BINARY, Py2Reg_BINARY),
gdef.REG_NONE: (Reg2Py_BINARY, Py2Reg_BINARY),
}
# All unknown format are seens as binary data
UNKNOWM_FORMAT = (Reg2Py_BINARY, Py2Reg_BINARY)
ENCODE_DECODE_METHODS = defaultdict(lambda: UNKNOWM_FORMAT, KNOWN_ENCODE_DECODE_METHODS)
def decode_registry_buffer(type, buffer, size):
try:
return ENCODE_DECODE_METHODS[type][DECODE_METHOD](buffer, size)
except UnicodeDecodeError as e:
# Best effort if any decoding error happen
return "".join(chr(c) for c in buffer[:size])
KeyValue = namedtuple("KeyValue", ["name", "value", "type"])
@@ -178,7 +205,7 @@ class PyHKey(object):
for i in itertools.count():
name_size.value = default_name_size
winproxy.RegEnumKeyExW(self.phkey, i, name_buffer, name_size, None, None, None, None)
res.append(name_buffer.value)
res.append(name_buffer[:name_size.value]) # Will allow key name with \x00 inside
return [PyHKey(self, n) for n in res]
def get_key_size_info(self):
@@ -223,7 +250,7 @@ class PyHKey(object):
keyname = ctypes.create_unicode_buffer(namesize.value)
datasize = gdef.DWORD(max_data_size)
databuffer = windows.utils.BUFFER(gdef.BYTE, nbelt=datasize.value)()
vobj = ENCODE_DECODE_METHODS[value_type.value][DECODE_METHOD](databuffer, datasize.value)
vobj = decode_registry_buffer(value_type.value, databuffer, datasize.value)
res.append(KeyValue(keyname.value, vobj, value_type.value))
return res
@@ -260,7 +287,7 @@ class PyHKey(object):
size.value *= 2
buffer = windows.utils.BUFFER(gdef.BYTE, nbelt=size.value)()
continue
vobj = ENCODE_DECODE_METHODS[type.value][DECODE_METHOD](buffer, size.value)
vobj = decode_registry_buffer(type.value, buffer, size.value)
return KeyValue(value_name, vobj, type.value)
def _guess_value_type(self, value):