From 5b662426806cbd38a26397d254af7dfc56cb3646 Mon Sep 17 00:00:00 2001 From: Clement Rouault Date: Mon, 18 Apr 2016 23:09:26 +0200 Subject: [PATCH] writing some doc --- TODO | 6 ++-- doc/source/index.rst | 1 + doc/source/registry.rst | 10 +++++- doc/source/sample.rst | 68 +++++++++++++++++++++++++++++++++++ doc/source/volume.rst | 12 ++++++- doc/source/wintrust.rst | 40 +++++++++++++++++++++ doc/source/wmi.rst | 2 -- samples/local_debugger.py | 1 + samples/system.py | 41 +++++++++++++++++++++ samples/veh_segv.py | 2 +- samples/wintrust.py | 29 +++++++++++++++ windows/winobject/registry.py | 1 + windows/winobject/system.py | 2 +- windows/winobject/volume.py | 21 ++++++++++- windows/wintrust.py | 34 ++++++++++++++++-- 15 files changed, 258 insertions(+), 12 deletions(-) create mode 100644 doc/source/wintrust.rst create mode 100644 samples/system.py create mode 100644 samples/wintrust.py diff --git a/TODO b/TODO index 200071b..02e0b72 100644 --- a/TODO +++ b/TODO @@ -1,6 +1,8 @@ TODO: - Documentation - Pass 0.2 when doc is done <3 + - type pass to dbg callback + - ProcessMemory object ? (metasm like) - remove pe_parse.transform_ctypes_fields (use utils.transform_ctypes_fields) @@ -40,16 +42,14 @@ TODO: CHANGELOG: * NEW REGISTRY -> change examples and documentation * WinProcess is not a PROCESSENTRY32 anymore (change doc) + * re-check every sample Documentation * Debugger * LocalDebugger - * volumes - * system.version stuff * windows.com * windows.wintrust * New stuff in exception ? - * system.firewall.network * system.services * New stuff in WMI ?(think not just change in internal) * windows.utils diff --git a/doc/source/index.rst b/doc/source/index.rst index 446bd28..fda44bf 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -17,6 +17,7 @@ Contents: native_exec.rst winproxy.rst utils.rst + wintrust.rst debug.rst iat_hook.rst wip.rst diff --git a/doc/source/registry.rst b/doc/source/registry.rst index 9660357..cf1f0ba 100644 --- a/doc/source/registry.rst +++ b/doc/source/registry.rst @@ -19,10 +19,18 @@ PyHKey .. autoclass:: PyHKey - .. function:: __getitem__(name) + .. function:: __call__(name) Alias for :func:`open_subkey` + .. function:: __getitem__(name) + + Alias for :func:`get` + + .. function:: __setitem__(name) + + Wrapper for :func:`set`, accept ``value`` or ``(value, type)`` + KeyValue """""""" diff --git a/doc/source/sample.rst b/doc/source/sample.rst index cb7383c..288b933 100644 --- a/doc/source/sample.rst +++ b/doc/source/sample.rst @@ -99,6 +99,49 @@ Output:: Sections: [, , , , ] + +.. _sample_system: + + +``windows.system`` +"""""""""""""""""" + +.. literalinclude:: ..\..\samples\system.py + +Output:: + + (cmd λ) python system.py + Basic system infos: + version = (6, 3) + bitness = 64 + computer_name = HAKRIL-PC + product_type = VER_NT_WORKSTATION(0x1L) + version_name = Windows 8.1 + + There is 95 processes + There is 1021 threads + + Dumping first logical drive: + + name = C:\ + type = DRIVE_FIXED(0x3L) + path = \Device\HarddiskVolume2 + + Dumping first service: + + name = ACPI + description = Microsoft ACPI Driver + status = ServiceStatus(type=SERVICE_KERNEL_DRIVER(0x1L), state=SERVICE_RUNNING(0x4L), control_accepted=1L, flags=0L) + process = None + + Finding a service in a user process: + + name = Appinfo + description = Application Information + status = ServiceStatus(type=SERVICE_WIN32_SHARE_PROCESS(0x20L), state=SERVICE_RUNNING(0x4L), control_accepted=129L, flags=0L) + process = + + .. _sample_iat_hook: IAT hooking @@ -190,6 +233,31 @@ Output:: registered owner = +.. _sample_wintrust: + +``windows.wintrust`` +"""""""""""""""""""" + +.. literalinclude:: ..\..\samples\wintrust.py + +Output:: + + (cmd λ) python .\wintrust.py + Checking signature of + is_signed: + check_signature: <0> + full_signature_information: + * signed + * catalog + * catalogsigned + * additionalinfo <0> + Checking signature of some loaded DLL + : False (TRUST_E_NOSIGNATURE(0x800b0100L)) + : True + : True + : True + : False (TRUST_E_NOSIGNATURE(0x800b0100L)) + .. _sample_vectoredexception: :func:`VectoredException` diff --git a/doc/source/volume.rst b/doc/source/volume.rst index 7741ae1..cda4be7 100644 --- a/doc/source/volume.rst +++ b/doc/source/volume.rst @@ -1,2 +1,12 @@ Volume -- The logical drives -============================ \ No newline at end of file +============================ + +.. module:: windows.winobject.volume + +.. autoclass:: LogicalDrive + + .. data:: name + + Name of the logical drive + + :type: :class:`str` \ No newline at end of file diff --git a/doc/source/wintrust.rst b/doc/source/wintrust.rst new file mode 100644 index 0000000..5d230b4 --- /dev/null +++ b/doc/source/wintrust.rst @@ -0,0 +1,40 @@ +``windows.wintrust`` -- Checking signature +****************************************** + +.. module:: windows.wintrust + +.. note:: + + See sample :ref:`sample_wintrust` + +The :mod:`wintrust` module offers wrapper around ``wintrust.dll``. +It allows to check the signature of a file. + +The signature of a file can be at two differents place: + + * In the file itself (:func:`check_signature`) + * In a catalog file (:func:`full_signature_information`) + +.. note:: + + `Explanation about catalog files `_ + + +API +""" + +.. autofunction:: is_signed + +.. autofunction:: full_signature_information + +.. autofunction:: check_signature + + +SignatureData +''''''''''''' + +.. autoclass:: SignatureData + :exclude-members: count, index + + + diff --git a/doc/source/wmi.rst b/doc/source/wmi.rst index c416ab9..f7b41b3 100644 --- a/doc/source/wmi.rst +++ b/doc/source/wmi.rst @@ -3,10 +3,8 @@ WMI -- Make request to WMI .. module:: windows.winobject.wmi - .. note:: See sample :ref:`wmi_request` - .. autoclass:: WmiRequester \ No newline at end of file diff --git a/samples/local_debugger.py b/samples/local_debugger.py index 82f948c..d2911a2 100644 --- a/samples/local_debugger.py +++ b/samples/local_debugger.py @@ -20,6 +20,7 @@ class YoloDebugger(windows.debug.LocalDebugger): if self.single_step_count: self.single_step_count -= 1 return self.single_step() + return EXCEPTION_CONTINUE_EXECUTION class YoloHXBP(windows.debug.HXBreakpoint): diff --git a/samples/system.py b/samples/system.py new file mode 100644 index 0000000..5b0f0eb --- /dev/null +++ b/samples/system.py @@ -0,0 +1,41 @@ +import sys +import os.path +sys.path.append(os.path.abspath(__file__ + "\..\..")) + +import windows +system = windows.system + +print("Basic system infos:") +print(" version = {0}".format(system.version)) +print(" bitness = {0}".format(system.bitness)) +print(" computer_name = {0}".format(system.computer_name)) +print(" product_type = {0}".format(system.product_type)) +print(" version_name = {0}".format(system.version_name)) +print("") +print("There is {0} processes".format(len(system.processes))) +print("There is {0} threads".format(len(system.threads))) +print("") +print("Dumping first logical drive:") +drive = system.logicaldrives[0] +print(" " + str(drive)) +print((" " * 8) + "name = {0}".format(drive.name)) +print((" " * 8) + "type = {0}".format(drive.type)) +print((" " * 8) + "path = {0}".format(drive.path)) +print("") + +print("Dumping first service:") +serv = windows.system.services[0] +print(" " + str(serv)) +print((" " * 8) + "name = {0}".format(serv.name)) +print((" " * 8) + "description = {0}".format(serv.description)) +print((" " * 8) + "status = {0}".format(serv.status)) +print((" " * 8) + "process = {0}".format(repr(serv.process))) +print("") + +print("Finding a service in a user process:") +serv = [s for s in windows.system.services if s.process][0] +print(" " + str(serv)) +print((" " * 8) + "name = {0}".format(serv.name)) +print((" " * 8) + "description = {0}".format(serv.description)) +print((" " * 8) + "status = {0}".format(serv.status)) +print((" " * 8) + "process = {0}".format(repr(serv.process))) \ No newline at end of file diff --git a/samples/veh_segv.py b/samples/veh_segv.py index 5770507..0211a7a 100644 --- a/samples/veh_segv.py +++ b/samples/veh_segv.py @@ -1,6 +1,6 @@ import ctypes import windows -from windows.exception import VectoredException +from windows.winobject.exception import VectoredException import windows.generated_def.windef as windef from windows.generated_def.winstructs import * diff --git a/samples/wintrust.py b/samples/wintrust.py new file mode 100644 index 0000000..f3572af --- /dev/null +++ b/samples/wintrust.py @@ -0,0 +1,29 @@ +import sys +import os.path +sys.path.append(os.path.abspath(__file__ + "\..\..")) + +import windows.wintrust + +TARGET_FILE = r"C:\windows\system32\ntdll.dll" +print("Checking signature of <{0}>".format(TARGET_FILE)) +print(" is_signed: <{0}>".format(windows.wintrust.is_signed(TARGET_FILE))) +print(" check_signature: <{0}>".format(windows.wintrust.check_signature(TARGET_FILE))) + +sign_info = windows.wintrust.full_signature_information(TARGET_FILE) +print(" full_signature_information:") +print(" * signed <{0}>".format(sign_info.signed)) +print(" * catalog <{0}>".format(sign_info.catalog)) +print(" * catalogsigned <{0}>".format(sign_info.catalogsigned)) +print(" * additionalinfo <{0}>".format(sign_info.additionalinfo)) + +print("Checking signature of some loaded DLL") +for module in windows.current_process.peb.modules[:5]: + path = module.fullname + is_signed = windows.wintrust.is_signed(path) + if is_signed: + print("<{0}> : {1}".format(path, is_signed)) + else: + sign_info = windows.wintrust.full_signature_information(path) + print("<{0}> : {1} ({2})".format(path, is_signed, sign_info[3])) + + diff --git a/windows/winobject/registry.py b/windows/winobject/registry.py index 1dbb567..394afd4 100644 --- a/windows/winobject/registry.py +++ b/windows/winobject/registry.py @@ -93,6 +93,7 @@ class PyHKey(object): def set(self, name, value, type=None): + """Set the value for ``name`` to ``value``. if ``type`` is None try to guess items""" if type is None: type = self._guess_value_type(value) return _winreg.SetValueEx(self.phkey, name, 0, type, value) diff --git a/windows/winobject/system.py b/windows/winobject/system.py index 79ccddc..ead9b9b 100644 --- a/windows/winobject/system.py +++ b/windows/winobject/system.py @@ -20,7 +20,7 @@ from windows.winobject import kernobj from windows.generated_def.winstructs import * class System(object): - """Represent the current ``Windows`` system ``Python`` is running on""" + """The state of the current ``Windows`` system ``Python`` is running on""" network = network.Network() """Object of class :class:`windows.winobject.network.Network`""" diff --git a/windows/winobject/volume.py b/windows/winobject/volume.py index 724a221..7d1f785 100644 --- a/windows/winobject/volume.py +++ b/windows/winobject/volume.py @@ -16,12 +16,31 @@ class LogicalDrive(object): @property def type(self): + """The type of drive, values are: + + * DRIVE_UNKNOWN(0x0L) + * DRIVE_NO_ROOT_DIR(0x1L) + * DRIVE_REMOVABLE(0x2L) + * DRIVE_FIXED(0x3L) + * DRIVE_REMOTE(0x4L) + * DRIVE_CDROM(0x5L) + * DRIVE_RAMDISK(0x6L) + + :type: :class:`long` or :class:`int` (or subclass) + """ t = winproxy.GetDriveTypeA(self.name) return self.DRIVE_TYPE.get(t,t) @property def path(self): - return query_dos_device(self.name.strip("\\")) + """The target path of the device + + :type: :class:`str`""" + res = query_dos_device(self.name.strip("\\")) + if len(res) != 1: + raise ValueError("[Unexpected result] query_dos_device(logicaldrive) returned multiple path") + return res[0] + def __repr__(self): return """<{0} "{1}" ({2})>""".format(type(self).__name__, self.name, self.type.name) diff --git a/windows/wintrust.py b/windows/wintrust.py index e13079e..8b6f483 100644 --- a/windows/wintrust.py +++ b/windows/wintrust.py @@ -79,9 +79,9 @@ wintrust_return_value_mapper = {x:x for x in wintrust_know_return_value} def check_signature(filename): - """Check if ``filename`` is a valid signed file + """Check if ``filename`` embeds a valid signature. - :return: 0 if file have a valid signature + :return: ``0`` if ``filename`` have a valid signature else the error """ file_data = WINTRUST_FILE_INFO() file_data.cbStruct = ctypes.sizeof(WINTRUST_FILE_INFO) @@ -155,8 +155,21 @@ def get_catalog_name_from_handle(handle): return cat_info.wszCatalogFile SignatureData = namedtuple("SignatureData", ["signed", "catalog", "catalogsigned", "additionalinfo"]) +"""Signature information for ``FILENAME``: + + * ``signed``: True if ``FILENAME`` embeds a valide signature + * ``catalog``: The filename of the catalog ``FILENAME`` is part of (if any) + * ``catalogsigned``: True if ``catalog`` embeds a valide signature + * ``additionalinfo``: The return error of ``check_signature(FILENAME)`` + +``additionalinfo`` is useful to know if ``FILENAME`` signature was rejected for an invalid root / expired cert. +""" def full_signature_information(filename): + """Returns more information about the signature of ``filename`` + + :return: :class:`SignatureData` + """ check_sign = check_signature(filename) signed = not bool(check_sign) catalog = get_catalog_for_filename(filename) @@ -164,3 +177,20 @@ def full_signature_information(filename): return SignatureData(signed, None, False, check_sign) catalogsigned = not bool(check_signature(catalog)) return SignatureData(signed, catalog, catalogsigned, check_sign) + +def is_signed(filename): + """Check if ``filename`` is signed: + + * File embeds a valid signature + * File is part of a signed catalog file + + :return: :class:`bool` + """ + check_sign = check_signature(filename) + if check_sign == 0: + return True + catalog = get_catalog_for_filename(filename) + if catalog is None: + return False + catalogsigned = not bool(check_signature(catalog)) + return catalogsigned