mirror of
https://github.com/hakril/PythonForWindows
synced 2026-06-08 14:31:45 +00:00
Handle documented key type and unkown one + add code to handle badly encoded values (REG_MULTI_SZ notably)
This commit is contained in:
+39
-3
@@ -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
|
||||
|
||||
@@ -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):
|
||||
|
||||
Reference in New Issue
Block a user