├── .gitignore ├── LICENSE ├── README.md ├── examples └── etw-templates │ ├── kernel-process.yml │ ├── prefetch-changes.yml │ └── registry-values.yml ├── scripts ├── etw_mon.md ├── etw_mon.py ├── print_handles.md ├── print_handles.py ├── print_processes.py ├── print_publishers.md ├── print_publishers.py ├── userassist_monitor.md └── userassist_monitor.py ├── setup.py ├── tests ├── test_consumer.py ├── test_filter.py ├── test_mappings.py ├── test_process.py ├── test_provider.py └── test_wevtapi.py └── winthingies ├── __init__.py ├── filter.py ├── handle.py ├── process.py ├── runtime.py ├── trace.py └── win32 ├── __init__.py ├── advapi32.py ├── const.py ├── guid.py ├── helpers.py ├── kernel32.py ├── ntdll.py ├── psapi.py ├── psapi_helpers.py ├── tdh.py ├── wevtapi.py ├── wevtapi_helpers.py └── winstructs.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | .idea/ 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .coverage 43 | .coverage.* 44 | .cache 45 | nosetests.xml 46 | coverage.xml 47 | *.cover 48 | .hypothesis/ 49 | .pytest_cache/ 50 | 51 | # Translations 52 | *.mo 53 | *.pot 54 | 55 | # Django stuff: 56 | *.log 57 | local_settings.py 58 | db.sqlite3 59 | 60 | # Flask stuff: 61 | instance/ 62 | .webassets-cache 63 | 64 | # Scrapy stuff: 65 | .scrapy 66 | 67 | # Sphinx documentation 68 | docs/_build/ 69 | 70 | # PyBuilder 71 | target/ 72 | 73 | # Jupyter Notebook 74 | .ipynb_checkpoints 75 | 76 | # pyenv 77 | .python-version 78 | 79 | # celery beat schedule file 80 | celerybeat-schedule 81 | 82 | # SageMath parsed files 83 | *.sage.py 84 | 85 | # Environments 86 | .env 87 | .venv 88 | env/ 89 | venv/ 90 | ENV/ 91 | env.bak/ 92 | venv.bak/ 93 | 94 | # Spyder project settings 95 | .spyderproject 96 | .spyproject 97 | 98 | # Rope project settings 99 | .ropeproject 100 | 101 | # mkdocs documentation 102 | /site 103 | 104 | # mypy 105 | .mypy_cache/ 106 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2018-2019 Matthew Seyer 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Cool win-thingies 2 | My repository for doing dfir windows things in real time. 3 | 4 | # Scripts 5 | ## scripts/etw_mon.py 6 | See [etw_mon docs](https://github.com/forensicmatt/PyWindowsThingies/blob/master/scripts/etw_mon.md) 7 | 8 | ## scripts/userassist_monitor.py 9 | See [userassist_monitor docs](https://github.com/forensicmatt/PyWindowsThingies/blob/master/scripts/userassist_monitor.md) 10 | 11 | ## scripts/print_handles.py 12 | See [print_handles docs](https://github.com/forensicmatt/PyWindowsThingies/blob/master/scripts/print_handles.md) 13 | 14 | ## scripts/print_publishers.py 15 | See [print_publishers docs](https://github.com/forensicmatt/PyWindowsThingies/blob/master/scripts/print_publishers.md) 16 | 17 | 18 | # Thanks 19 | Thanks to other people's work that were great win32 ctype references. 20 | - https://github.com/rabbitstack/fibratus 21 | - https://github.com/fireeye/pywintrace 22 | - https://github.com/hakril/PythonForWindows 23 | - https://github.com/NadavRazDev/dotfiles 24 | -------------------------------------------------------------------------------- /examples/etw-templates/kernel-process.yml: -------------------------------------------------------------------------------- 1 | session_name: Registry Value Changes 2 | providers: 3 | - name: Microsoft-Windows-Kernel-Process 4 | guid: "{22FB2CD6-0E7B-422B-A0C7-2FAD1FD0E716}" 5 | match_any_keyword: 0x0000000000000010 6 | output: 7 | format: "{record['TimeStamp']}: Task: {record['EventDescriptor']['Task']} {record['ImageName']}" -------------------------------------------------------------------------------- /examples/etw-templates/prefetch-changes.yml: -------------------------------------------------------------------------------- 1 | session_name: NT Kernel Logger 2 | providers: 3 | - name: Windows Kernel Trace 4 | guid: "{9E814AAD-3204-11D2-9A82-006008A86939}" 5 | match_any_keyword: 0x04000000 6 | filter: 7 | query: "ends_with(to_string(OpenPath), '.pf')" 8 | output: 9 | format: "{record['TimeStamp']}: {record['OpenPath']}" 10 | -------------------------------------------------------------------------------- /examples/etw-templates/registry-values.yml: -------------------------------------------------------------------------------- 1 | session_name: Registry Value Changes 2 | providers: 3 | - name: Microsoft-Windows-Kernel-Registry 4 | guid: "{70EB4F03-C1DE-4F73-A051-33D13D5413BD}" 5 | filter: 6 | query: "ValueName" 7 | output: 8 | format: "{record['TimeStamp']}: {record['ValueName']}" 9 | -------------------------------------------------------------------------------- /scripts/etw_mon.md: -------------------------------------------------------------------------------- 1 | # etw_mon.py 2 | `etw_mon.py` is a tool that can custom monitor etw based off of yaml templates. 3 | 4 | ``` 5 | usage: etw_mon.py [-h] -t TEMPLATE 6 | 7 | Monitor ETW. 8 | 9 | optional arguments: 10 | -h, --help show this help message and exit 11 | -t TEMPLATE, --template TEMPLATE 12 | The template. 13 | ``` 14 | 15 | # Example YML Templates 16 | ```yaml 17 | session_name: NT Kernel Logger 18 | providers: 19 | - name: Windows Kernel Trace 20 | guid: "{9E814AAD-3204-11D2-9A82-006008A86939}" 21 | match_any_keyword: 0x04000000 22 | filter: 23 | query: "ends_with(to_string(OpenPath), '.pf')" 24 | output: 25 | format: "{record['TimeStamp']}: {record['OpenPath']}" 26 | ``` 27 | *kernel-fileio.yml* 28 | 29 | ``` 30 | C:\Python36\python.exe etw_mon.py -t kernel-fileio.yml 31 | 2019-02-24 22:30:16.696926: \Device\HarddiskVolume6\Windows\Prefetch\BACKGROUNDTASKHOST.EXE-E08DE009.pf 32 | 2019-02-24 22:30:16.699686: \Device\HarddiskVolume6\Windows\Prefetch\BACKGROUNDTASKHOST.EXE-E08DE009.pf 33 | 2019-02-24 22:30:16.699798: \Device\HarddiskVolume6\Windows\Prefetch\BACKGROUNDTASKHOST.EXE-E08DE009.pf 34 | 2019-02-24 22:30:17.700542: \Device\HarddiskVolume6\Windows\Prefetch\BACKGROUNDTASKHOST.EXE-E08DE009.pf 35 | 2019-02-24 22:30:23.394640: \Device\HarddiskVolume6\Windows\Prefetch\CALC.EXE-A7D3F5D3.pf 36 | 2019-02-24 22:30:23.396876: \Device\HarddiskVolume6\Windows\Prefetch\CALC.EXE-A7D3F5D3.pf 37 | 2019-02-24 22:30:23.397020: \Device\HarddiskVolume6\Windows\Prefetch\CALC.EXE-A7D3F5D3.pf 38 | ``` 39 | 40 | # Template Reference 41 | `session_name`: The Session name for this ETW trace. (Must be 'NT Kernel Logger' 42 | if using Kernel Trace providers) 43 | 44 | `providers`: A list of providers. 45 | - `name`: The name of the provider 46 | - `guid`: The GUID of the provider 47 | - `match_any_keyword`: A bitmask of keywords that determine the category of 48 | events that you want the provider to write. The provider writes the event 49 | if any of the event's keyword bits match any of the bits set in this mask. 50 | - `match_all_keyword`: This bitmask is optional. This mask further restricts 51 | the category of events that you want the provider to write. If the event's 52 | keyword meets the MatchAnyKeyword condition, the provider will write the 53 | event only if all of the bits in this mask exist in the event's keyword. 54 | This mask is not used if MatchAnyKeyword is zero 55 | 56 | `filter.query`: A JMES Path query that must evaluate as true for the 57 | record to be processed. 58 | 59 | `output.format`: A specific output format fstring. 60 | 61 | # References 62 | https://docs.microsoft.com/en-us/windows/desktop/etw/enabletraceex2 -------------------------------------------------------------------------------- /scripts/etw_mon.py: -------------------------------------------------------------------------------- 1 | import sys 2 | sys.path.append("..") 3 | import fmt 4 | import yaml 5 | import argparse 6 | from functools import partial 7 | from winthingies.filter import Filter 8 | from winthingies.trace import TraceProvider 9 | from winthingies.trace import EventTraceHandler 10 | 11 | 12 | class EventHandler(object): 13 | def __init__(self, output_format=None, event_filter=None): 14 | self.event_filter = event_filter 15 | self.output_format = output_format 16 | 17 | def custom_format(self, record): 18 | return fmt(self.output_format) 19 | 20 | def event_callback(self, lp_event_record): 21 | event_dict = lp_event_record.contents.as_dict() 22 | 23 | if self.event_filter: 24 | if self.event_filter.is_true(event_dict): 25 | if self.output_format: 26 | print(self.custom_format(event_dict)) 27 | else: 28 | print(event_dict) 29 | else: 30 | if self.output_format: 31 | print(self.custom_format(event_dict)) 32 | else: 33 | print(event_dict) 34 | 35 | 36 | def get_arguments(): 37 | usage = "Monitor ETW." 38 | 39 | arguments = argparse.ArgumentParser( 40 | description=usage, 41 | formatter_class=argparse.RawDescriptionHelpFormatter 42 | ) 43 | 44 | arguments.add_argument( 45 | "-t", "--template", 46 | dest="template", 47 | action="store", 48 | required=True, 49 | help="The template." 50 | ) 51 | 52 | return arguments 53 | 54 | 55 | def get_providers(provider_list): 56 | """Iterate list of provider dictionaries 57 | 58 | :param provider_list: (list) list of provider dictionaries 59 | :return: (list) list of TraceProvider objects 60 | """ 61 | providers = [] 62 | for provider_dict in provider_list: 63 | trace_provider = TraceProvider( 64 | provider_dict.get("name"), 65 | provider_dict.get("guid"), 66 | match_any_keyword=provider_dict.get("match_any_keyword", 0), 67 | match_all_keyword=provider_dict.get("match_all_keyword", 0) 68 | ) 69 | providers.append( 70 | trace_provider 71 | ) 72 | return providers 73 | 74 | 75 | def main(): 76 | arguments = get_arguments() 77 | options = arguments.parse_args() 78 | 79 | with open(options.template, "r") as template_fh: 80 | template_dict = yaml.load(template_fh) 81 | session_name = template_dict.get("session_name") 82 | provider_list = template_dict.get("providers") 83 | providers = get_providers(provider_list) 84 | 85 | event_filter = template_dict.get("filter", None) 86 | if event_filter: 87 | event_filter = Filter.from_dict( 88 | event_filter 89 | ) 90 | 91 | output_format = None 92 | if "output" in template_dict: 93 | output_format = template_dict["output"].get( 94 | "format", None 95 | ) 96 | 97 | event_handler = EventHandler( 98 | output_format=output_format, 99 | event_filter=event_filter 100 | ) 101 | 102 | event_trace_handler = EventTraceHandler( 103 | session_name, 104 | providers, 105 | partial(EventHandler.event_callback, event_handler) 106 | ) 107 | 108 | event_trace_handler.start_session() 109 | 110 | 111 | if __name__ == "__main__": 112 | main() 113 | -------------------------------------------------------------------------------- /scripts/print_handles.md: -------------------------------------------------------------------------------- 1 | ## print_handles.py 2 | Print system handles. 3 | ``` 4 | usage: print_handles.py [-h] [-p PID] [-t TYPE] 5 | 6 | Print Open Handles. I recommend using the --type param. 7 | Some handle enumeration can cause hanging. Working on fix... 8 | 9 | optional arguments: 10 | -h, --help show this help message and exit 11 | -p PID, --pid PID A specific PID. 12 | -t TYPE, --type TYPE Only print specific handle types. [File, Key, etc.] 13 | ``` 14 | 15 | ### Example Output 16 | Get opened Key handles for Explore.exe (with pid 6604): 17 | 18 | `print_handles.py -p 6604 -t Key` 19 | 20 | ``` 21 | ... 22 | {"CreatorBackTraceIndex":0,"Reserved":0,"Name":"\\REGISTRY\\USER\\S-1-5-21-2350377626-499376046-3523757530-1001\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\UserAssist\\{CEBFF5CD-ACE2-4F4F-9178-9926F41749EA}\\Count","HandleValue":10092,"HandleAttributes":0,"ObjectTypeIndex":43,"Type":"Key","GrantedAccess":196639,"Object":18446626440585191536,"UniqueProcessId":6604} 23 | {"CreatorBackTraceIndex":0,"Reserved":0,"Name":"\\REGISTRY\\USER\\S-1-5-21-2350377626-499376046-3523757530-1001\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\UserAssist\\{F4E57C4B-2036-45F0-A9AB-443BCFE33D9F}\\Count","HandleValue":10096,"HandleAttributes":0,"ObjectTypeIndex":43,"Type":"Key","GrantedAccess":196639,"Object":18446626440585261776,"UniqueProcessId":6604} 24 | {"CreatorBackTraceIndex":0,"Reserved":0,"Name":"\\REGISTRY\\USER\\S-1-5-21-2350377626-499376046-3523757530-1001\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\UserAssist\\{B267E3AD-A825-4A09-82B9-EEC22AA3B847}\\Count","HandleValue":10100,"HandleAttributes":0,"ObjectTypeIndex":43,"Type":"Key","GrantedAccess":196639,"Object":18446626440588625584,"UniqueProcessId":6604} 25 | {"CreatorBackTraceIndex":0,"Reserved":0,"Name":"\\REGISTRY\\USER\\S-1-5-21-2350377626-499376046-3523757530-1001\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\UserAssist\\{9E04CAB2-CC14-11DF-BB8C-A2F1DED72085}\\Count","HandleValue":10104,"HandleAttributes":0,"ObjectTypeIndex":43,"Type":"Key","GrantedAccess":196639,"Object":18446626440586308592,"UniqueProcessId":6604} 26 | {"CreatorBackTraceIndex":0,"Reserved":0,"Name":"\\REGISTRY\\USER\\S-1-5-21-2350377626-499376046-3523757530-1001\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\UserAssist\\{FA99DFC7-6AC2-453A-A5E2-5E2AFF4507BD}\\Count","HandleValue":10108,"HandleAttributes":0,"ObjectTypeIndex":43,"Type":"Key","GrantedAccess":196639,"Object":18446626440587252752,"UniqueProcessId":6604} 27 | {"CreatorBackTraceIndex":0,"Reserved":0,"Name":"\\REGISTRY\\USER\\S-1-5-21-2350377626-499376046-3523757530-1001\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\UserAssist\\{CAA59E3C-4792-41A5-9909-6A6A8D32490E}\\Count","HandleValue":10112,"HandleAttributes":0,"ObjectTypeIndex":43,"Type":"Key","GrantedAccess":196639,"Object":18446626440587730624,"UniqueProcessId":6604} 28 | {"CreatorBackTraceIndex":0,"Reserved":0,"Name":"\\REGISTRY\\USER\\S-1-5-21-2350377626-499376046-3523757530-1001\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\UserAssist\\{A3D53349-6E61-4557-8FC7-0028EDCEEBF6}\\Count","HandleValue":10116,"HandleAttributes":0,"ObjectTypeIndex":43,"Type":"Key","GrantedAccess":196639,"Object":18446626440587767920,"UniqueProcessId":6604} 29 | {"CreatorBackTraceIndex":0,"Reserved":0,"Name":"\\REGISTRY\\USER\\S-1-5-21-2350377626-499376046-3523757530-1001\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\UserAssist\\{F2A1CB5A-E3CC-4A2E-AF9D-505A7009D442}\\Count","HandleValue":10120,"HandleAttributes":0,"ObjectTypeIndex":43,"Type":"Key","GrantedAccess":196639,"Object":18446626440569601136,"UniqueProcessId":6604} 30 | {"CreatorBackTraceIndex":0,"Reserved":0,"Name":"\\REGISTRY\\USER\\S-1-5-21-2350377626-499376046-3523757530-1001\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\UserAssist\\{BCB48336-4DDD-48FF-BB0B-D3190DACB3E2}\\Count","HandleValue":10124,"HandleAttributes":0,"ObjectTypeIndex":43,"Type":"Key","GrantedAccess":196639,"Object":18446626440562674192,"UniqueProcessId":6604} 31 | ... 32 | ``` 33 | -------------------------------------------------------------------------------- /scripts/print_handles.py: -------------------------------------------------------------------------------- 1 | import sys 2 | sys.path.append("..") 3 | import ujson 4 | import logging 5 | import argparse 6 | from winthingies.handle import iterate_handles 7 | 8 | logging.basicConfig( 9 | level=logging.DEBUG 10 | ) 11 | 12 | 13 | def get_arguments(): 14 | usage = """Print Open Handles. I recommend using the --type param. 15 | Some handle enumeration can cause hanging. Working on fix... 16 | """ 17 | 18 | arguments = argparse.ArgumentParser( 19 | description=usage, 20 | formatter_class=argparse.RawDescriptionHelpFormatter 21 | ) 22 | 23 | arguments.add_argument( 24 | "-p", "--pid", 25 | dest="pid", 26 | action="store", 27 | required=False, 28 | default=None, 29 | type=int, 30 | help="A specific PID." 31 | ) 32 | 33 | arguments.add_argument( 34 | "-t", "--type", 35 | dest="type", 36 | action="store", 37 | required=False, 38 | default=None, 39 | help="Only print specific handle types. [File, Key, etc.]" 40 | ) 41 | 42 | return arguments 43 | 44 | 45 | def main(): 46 | arguments = get_arguments() 47 | options = arguments.parse_args() 48 | 49 | for handle in iterate_handles(pid=options.pid): 50 | if options.type is not None: 51 | try: 52 | if handle.type_name: 53 | if handle.type_name.lower() == options.type.lower(): 54 | print(ujson.dumps(handle.as_dict())) 55 | except Exception as error: 56 | logging.debug(error) 57 | else: 58 | print(ujson.dumps(handle.as_dict())) 59 | 60 | 61 | if __name__ == "__main__": 62 | main() 63 | -------------------------------------------------------------------------------- /scripts/print_processes.py: -------------------------------------------------------------------------------- 1 | import ujson 2 | import logging 3 | import argparse 4 | from winthingies.process import iterate_processes 5 | 6 | logging.basicConfig( 7 | level=logging.INFO 8 | ) 9 | 10 | 11 | def get_arguments(): 12 | usage = """List Processes.""" 13 | 14 | arguments = argparse.ArgumentParser( 15 | description=usage, 16 | formatter_class=argparse.RawDescriptionHelpFormatter 17 | ) 18 | 19 | arguments.add_argument( 20 | "-n", "--name", 21 | dest="name", 22 | action="store", 23 | required=False, 24 | default=None, 25 | help="Processes with a specific name." 26 | ) 27 | 28 | return arguments 29 | 30 | 31 | def main(): 32 | arguments = get_arguments() 33 | options = arguments.parse_args() 34 | 35 | for process_entry32 in iterate_processes(): 36 | if options.name: 37 | if process_entry32.szExeFile.decode("utf-8").lower() == options.name.lower(): 38 | print(ujson.dumps(process_entry32.as_dict())) 39 | else: 40 | print(ujson.dumps(process_entry32.as_dict())) 41 | 42 | 43 | if __name__ == "__main__": 44 | main() 45 | -------------------------------------------------------------------------------- /scripts/print_publishers.md: -------------------------------------------------------------------------------- 1 | # Usage 2 | ``` 3 | usage: print_publishers.py [-h] [-p PUBLISHER] 4 | 5 | Print metadata of all registered Windows Event publishers or a specific publisher. Version: 0.0.1 6 | 7 | optional arguments: 8 | -h, --help show this help message and exit 9 | -p PUBLISHER, --publisher PUBLISHER 10 | A specific publisher. 11 | ``` 12 | 13 | # Output 14 | `python print_publishers.py` 15 | 16 | ``` 17 | ... 18 | ---------------------------------------------- 19 | Publisher: Microsoft-Windows-Kernel-Process 20 | GUID: 22FB2CD6-0E7B-422B-A0C7-2FAD1FD0E716 21 | ---------------------------------------------- 22 | Resource Path: C:\WINDOWS\system32\Microsoft-Windows-System-Events.dll 23 | Message Path: C:\WINDOWS\system32\Microsoft-Windows-System-Events.dll 24 | Help Link: https://go.microsoft.com/fwlink/events.asp?CoName=Microsoft%20Corporation&ProdName=Microsoft%c2%ae%20Windows%c2%ae%20Operating%20System&ProdVer=10.0.17134.1&FileName=Microsoft-Windows-System-Events.dll&FileVer=10.0.17134.1 25 | --- Channels --- 26 | 0x0000000000000000: Microsoft-Windows-Kernel-Process/Analytic 27 | --- Keywords --- 28 | 0x0000000000000010: WINEVENT_KEYWORD_PROCESS 29 | 0x0000000000000020: WINEVENT_KEYWORD_THREAD 30 | 0x0000000000000040: WINEVENT_KEYWORD_IMAGE 31 | 0x0000000000000080: WINEVENT_KEYWORD_CPU_PRIORITY 32 | 0x0000000000000100: WINEVENT_KEYWORD_OTHER_PRIORITY 33 | 0x0000000000000200: WINEVENT_KEYWORD_PROCESS_FREEZE 34 | 0x0000000000000400: WINEVENT_KEYWORD_JOB 35 | 0x0000000000000800: WINEVENT_KEYWORD_ENABLE_PROCESS_TRACING_CALLBACKS 36 | 0x0000000000001000: WINEVENT_KEYWORD_JOB_IO 37 | 0x0000000000002000: WINEVENT_KEYWORD_WORK_ON_BEHALF 38 | 0x0000000000004000: WINEVENT_KEYWORD_JOB_SILO 39 | --- Operations --- 40 | 0x0000000000000000: win:Info [Info] 41 | 0x0000000000010000: win:Start [Start] 42 | 0x0000000000020000: win:Stop [Stop] 43 | --- Levels --- 44 | 0x0000000000000004: win:Informational [Information] 45 | --- Tasks --- 46 | 0x0000000000000001: ProcessStart 47 | 0x0000000000000002: ProcessStop 48 | 0x0000000000000003: ThreadStart 49 | 0x0000000000000004: ThreadStop 50 | 0x0000000000000005: ImageLoad 51 | 0x0000000000000006: ImageUnload 52 | 0x0000000000000007: CpuBasePriorityChange 53 | 0x0000000000000008: CpuPriorityChange 54 | 0x0000000000000009: PagePriorityChange 55 | 0x000000000000000A: IoPriorityChange 56 | 0x000000000000000B: ProcessFreeze 57 | 0x000000000000000D: JobStart 58 | 0x000000000000000E: JobTerminate 59 | 0x000000000000000F: ProcessRundown 60 | 0x0000000000000010: PsDiskIoAttribution 61 | 0x0000000000000011: PsIoRateControl 62 | 0x0000000000000012: ThreadWorkOnBehalfUpdate 63 | 0x0000000000000013: JobServerSiloStart 64 | 65 | ---------------------------------------------- 66 | Publisher: Microsoft-Windows-Kernel-Processor-Power 67 | GUID: 0F67E49F-FE51-4E9F-B490-6F2948CC6027 68 | ---------------------------------------------- 69 | Resource Path: C:\WINDOWS\system32\microsoft-windows-kernel-processor-power-events.dll 70 | Message Path: C:\WINDOWS\system32\microsoft-windows-kernel-processor-power-events.dll 71 | Help Link: https://go.microsoft.com/fwlink/events.asp?CoName=Microsoft%20Corporation&ProdName=Microsoft%c2%ae%20Windows%c2%ae%20Operating%20System&ProdVer=10.0.17134.1&FileName=microsoft-windows-kernel-processor-power-events.dll&FileVer=10.0.17134.1 72 | --- Channels --- 73 | 0x0000000000000000: System [System] 74 | 0x0000000000000001: Microsoft-Windows-Kernel-Processor-Power/Diagnostic 75 | --- Keywords --- 76 | 0x0000000000000001: Perf 77 | 0x0000000000000002: Diag 78 | 0x0000000000000004: PowerDiagnostics 79 | 0x0000000000000008: Lpi 80 | 0x0000000000000010: SleepStudy 81 | 0x0000000000000020: Algorithm 82 | 0x0000000000000040: Profiles 83 | 0x0000000000000080: PerfDiag 84 | 0x0000000000000100: EnergyEstimation 85 | --- Operations --- 86 | 0x0000000000000000: win:Info [Info] 87 | 0x0000000000010000: win:Start [Start] 88 | 0x0000000000020000: win:Stop [Stop] 89 | 0x0000000000210000: Makeup 90 | 0x0000000000220000: FailedStart 91 | --- Levels --- 92 | 0x0000000000000002: win:Error [Error] 93 | 0x0000000000000003: win:Warning [Warning] 94 | 0x0000000000000004: win:Informational [Information] 95 | 0x0000000000000005: win:Verbose [Verbose] 96 | --- Tasks --- 97 | 0x0000000000000001: IdleStatesError 98 | 0x0000000000000002: PerfStatesError 99 | 0x0000000000000003: ThrottleStatesError 100 | 0x0000000000000004: Summary 101 | ... 102 | ``` -------------------------------------------------------------------------------- /scripts/print_publishers.py: -------------------------------------------------------------------------------- 1 | import sys 2 | sys.path.append("..") 3 | import logging 4 | import argparse 5 | from winthingies.win32.wevtapi import * 6 | from winthingies.win32.wevtapi_helpers import PublisherMetadata 7 | 8 | VERSION = "0.0.1" 9 | VALID_DEBUG_LEVELS = ["CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG", "NOTSET"] 10 | 11 | 12 | def set_debug_level(debug_level): 13 | if debug_level in VALID_DEBUG_LEVELS: 14 | logging.basicConfig( 15 | level=getattr(logging, debug_level) 16 | ) 17 | else: 18 | raise (Exception("{} is not a valid debug level.".format(debug_level))) 19 | 20 | 21 | def get_arguments(): 22 | usage = ("Print metadata of all registered Windows Event publishers" 23 | " or a specific publisher. Version: {}").format( 24 | VERSION 25 | ) 26 | 27 | arguments = argparse.ArgumentParser( 28 | description=usage, 29 | formatter_class=argparse.RawTextHelpFormatter 30 | ) 31 | 32 | arguments.add_argument( 33 | "-p", "--publisher", 34 | dest="publisher", 35 | action="store", 36 | required=False, 37 | default=None, 38 | help="A specific publisher." 39 | ) 40 | 41 | arguments.add_argument( 42 | "--debug", 43 | dest="debug", 44 | action="store", 45 | default="CRITICAL", 46 | choices=VALID_DEBUG_LEVELS, 47 | help="Debug level [default=CRITICAL]" 48 | ) 49 | 50 | return arguments 51 | 52 | 53 | def print_publisher_info(publisher_name): 54 | try: 55 | metadata_handle = PublisherMetadata( 56 | publisher_name 57 | ) 58 | except WindowsError as error: 59 | logging.error("Error opening provider metadata {}; {}".format( 60 | publisher_name, error 61 | )) 62 | return 63 | 64 | print("----------------------------------------------") 65 | print("Publisher: {}".format(publisher_name)) 66 | print("GUID: {}".format(metadata_handle.guid)) 67 | print("----------------------------------------------") 68 | 69 | if metadata_handle.resource_file_path: 70 | print("Resource Path: {}".format(metadata_handle.resource_file_path)) 71 | 72 | if metadata_handle.message_file_path: 73 | print("Message Path: {}".format(metadata_handle.message_file_path)) 74 | 75 | if metadata_handle.parameter_file_path: 76 | print("Parameter Path: {}".format(metadata_handle.parameter_file_path)) 77 | 78 | if metadata_handle.help_link: 79 | print("Help Link: {}".format(metadata_handle.help_link)) 80 | 81 | print("--- Channels ---") 82 | channel_mapping = metadata_handle.channel_mapping 83 | if channel_mapping: 84 | for channel_value, channel_info in channel_mapping.items(): 85 | desc = "" 86 | if channel_info["message"]: 87 | desc = " [{}]".format(channel_info["message"]) 88 | 89 | print("0x{:016X}: {}{}".format( 90 | channel_value, 91 | channel_info["path"], 92 | desc 93 | )) 94 | 95 | print("--- Keywords ---") 96 | keyword_mapping = metadata_handle.keyword_mapping 97 | if keyword_mapping: 98 | for keyword_value, keyword_info in keyword_mapping.items(): 99 | desc = "" 100 | if keyword_info["message"]: 101 | desc = " [{}]".format(keyword_info["message"]) 102 | 103 | print("0x{:016X}: {}{}".format( 104 | keyword_value, 105 | keyword_info["name"], 106 | desc 107 | )) 108 | 109 | print("--- Operations ---") 110 | opcode_mapping = metadata_handle.opcode_mapping 111 | if opcode_mapping: 112 | for opcode_value, opcode_info in opcode_mapping.items(): 113 | desc = "" 114 | if opcode_info["message"]: 115 | desc = " [{}]".format(opcode_info["message"]) 116 | 117 | print("0x{:016X}: {}{}".format( 118 | opcode_value, 119 | opcode_info["name"], 120 | desc 121 | )) 122 | 123 | print("--- Levels ---") 124 | level_mapping = metadata_handle.level_mapping 125 | if level_mapping: 126 | for level_value, level_info in level_mapping.items(): 127 | desc = "" 128 | if level_info["message"]: 129 | desc = " [{}]".format(level_info["message"]) 130 | 131 | print("0x{:016X}: {}{}".format( 132 | level_value, 133 | level_info["name"], 134 | desc 135 | )) 136 | 137 | print("--- Tasks ---") 138 | task_mapping = metadata_handle.task_mapping 139 | if task_mapping: 140 | for task_value, task_info in task_mapping.items(): 141 | desc = "" 142 | if task_info["message"]: 143 | desc = " [{}]".format(task_info["message"]) 144 | 145 | print("0x{:016X}: {}{}".format( 146 | task_value, 147 | task_info["name"], 148 | desc 149 | )) 150 | 151 | 152 | def main(): 153 | arguments = get_arguments() 154 | options = arguments.parse_args() 155 | 156 | set_debug_level( 157 | options.debug 158 | ) 159 | 160 | if options.publisher: 161 | print_publisher_info( 162 | options.publisher 163 | ) 164 | else: 165 | publisher_list_handle = EvtOpenPublisherEnum() 166 | 167 | publisher_list = [] 168 | while True: 169 | publisher_name = EvtNextPublisherId( 170 | publisher_list_handle 171 | ) 172 | if publisher_name is not None: 173 | publisher_list.append( 174 | publisher_name 175 | ) 176 | else: 177 | break 178 | 179 | for publisher_name in sorted(publisher_list): 180 | print_publisher_info(publisher_name) 181 | print() 182 | 183 | wevtapi.EvtClose( 184 | publisher_list_handle 185 | ) 186 | 187 | 188 | if __name__ == "__main__": 189 | main() 190 | -------------------------------------------------------------------------------- /scripts/userassist_monitor.md: -------------------------------------------------------------------------------- 1 | # userassist_monitor.py 2 | `userassist_monitor.py` is a tool that utilizes realtime ETL monitoring to watch UserAssist 3 | registry keys for changes and parse values. 4 | 5 | ``` 6 | usage: userassist_monitor.py [-h] [--format FORMAT_STR] 7 | 8 | Monitor UserAssist via ETL. Version: 0.0.1 9 | 10 | 11 | optional arguments: 12 | -h, --help show this help message and exit 13 | --format FORMAT_STR This is a python fstring to format output. Default 14 | prints json lines. 15 | ``` 16 | 17 | ### Example 18 | 19 | #### JSONL Output 20 | ``` 21 | C:\Python36\python.exe userassist_monitor.py 22 | {'ProcessId': 8116, 'ThreadId': 14252, 'TimeStamp': '2019-02-10 04:13:14.445706', 'EventDescriptor': {'Opcode': 38, 'Keyword': 9223372036854776832, 'KeywordStr': 'QueryValueKey'}, 'KeyObject': 18446652304217692176, 'Status': 0, 'InfoClass': 2, 'DataSize': 84, 'KeyName': '', 'ValueName': '{6Q809377-6NS0-444O-8957-N3773S02200R}\\WrgOenvaf\\ClPunez Pbzzhavgl Rqvgvba 2018.3.3\\ova\\clpunez64.rkr', 'CapturedDataSize': 0, 'CapturedData': None, 'ValueNameDecoded': '\\REGISTRY\\USER\\S-1-5-21-2350377626-499376046-3523757530-1001\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\UserAssist\\{CEBFF5CD-ACE2-4F4F-9178-9926F41749EA}\\Count\\{6D809377-6AF0-444B-8957-A3773F02200E}\\JetBrains\\PyCharm Community Edition 2018.3.3\\bin\\pycharm64.exe', 'ValueFullPath': '\\REGISTRY\\USER\\S-1-5-21-2350377626-499376046-3523757530-1001\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\UserAssist\\{CEBFF5CD-ACE2-4F4F-9178-9926F41749EA}\\Count\\{6Q809377-6NS0-444O-8957-N3773S02200R}\\WrgOenvaf\\ClPunez Pbzzhavgl Rqvgvba 2018.3.3\\ova\\clpunez64.rkr', 'UserAssist': {'session': 6, 'run_count': 28, 'focus_count': 757, 'focus_time': 3212836864, 'last_execution': '2019-02-10 00:21:45.266000'}} 23 | {'ProcessId': 8116, 'ThreadId': 14252, 'TimeStamp': '2019-02-10 04:13:14.445838', 'EventDescriptor': {'Opcode': 36, 'Keyword': 9223372036854776064, 'KeywordStr': 'SetValueKey'}, 'KeyObject': 18446652304217692176, 'Status': 0, 'Type': 3, 'DataSize': 72, 'KeyName': '', 'ValueName': '{6Q809377-6NS0-444O-8957-N3773S02200R}\\WrgOenvaf\\ClPunez Pbzzhavgl Rqvgvba 2018.3.3\\ova\\clpunez64.rkr', 'CapturedDataSize': 0, 'CapturedData': None, 'PreviousDataType': 0, 'PreviousDataSize': 0, 'PreviousDataCapturedSize': 0, 'PreviousData': None, 'ValueNameDecoded': '\\REGISTRY\\USER\\S-1-5-21-2350377626-499376046-3523757530-1001\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\UserAssist\\{CEBFF5CD-ACE2-4F4F-9178-9926F41749EA}\\Count\\{6D809377-6AF0-444B-8957-A3773F02200E}\\JetBrains\\PyCharm Community Edition 2018.3.3\\bin\\pycharm64.exe', 'ValueFullPath': '\\REGISTRY\\USER\\S-1-5-21-2350377626-499376046-3523757530-1001\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\UserAssist\\{CEBFF5CD-ACE2-4F4F-9178-9926F41749EA}\\Count\\{6Q809377-6NS0-444O-8957-N3773S02200R}\\WrgOenvaf\\ClPunez Pbzzhavgl Rqvgvba 2018.3.3\\ova\\clpunez64.rkr', 'UserAssist': {'session': 6, 'run_count': 28, 'focus_count': 757, 'focus_time': 3212836864, 'last_execution': '2019-02-10 00:21:45.266000'}} 24 | {'ProcessId': 8116, 'ThreadId': 14252, 'TimeStamp': '2019-02-10 04:13:14.445878', 'EventDescriptor': {'Opcode': 36, 'Keyword': 9223372036854776064, 'KeywordStr': 'SetValueKey'}, 'KeyObject': 18446652304217692176, 'Status': 0, 'Type': 3, 'DataSize': 1612, 'KeyName': '', 'ValueName': 'HRZR_PGYFRFFVBA', 'CapturedDataSize': 0, 'CapturedData': None, 'PreviousDataType': 0, 'PreviousDataSize': 0, 'PreviousDataCapturedSize': 0, 'PreviousData': None, 'ValueNameDecoded': '\\REGISTRY\\USER\\S-1-5-21-2350377626-499376046-3523757530-1001\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\UserAssist\\{CEBFF5CD-ACE2-4F4F-9178-9926F41749EA}\\Count\\UEME_CTLSESSION', 'ValueFullPath': '\\REGISTRY\\USER\\S-1-5-21-2350377626-499376046-3523757530-1001\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\UserAssist\\{CEBFF5CD-ACE2-4F4F-9178-9926F41749EA}\\Count\\HRZR_PGYFRFFVBA', 'UserAssist': {'session': 6, 'run_count': 352, 'focus_count': 2333, 'focus_time': 7471203, 'last_execution': '1708-01-15 02:09:16.103692'}} 25 | {'ProcessId': 8116, 'ThreadId': 12156, 'TimeStamp': '2019-02-10 04:13:17.570646', 'EventDescriptor': {'Opcode': 38, 'Keyword': 9223372036854776832, 'KeywordStr': 'QueryValueKey'}, 'KeyObject': 18446652304217692176, 'Status': 0, 'InfoClass': 2, 'DataSize': 84, 'KeyName': '', 'ValueName': 'Zvpebfbsg.Jvaqbjf.Pbegnan_pj5a1u2gklrjl!PbegnanHV', 'CapturedDataSize': 0, 'CapturedData': None, 'ValueNameDecoded': '\\REGISTRY\\USER\\S-1-5-21-2350377626-499376046-3523757530-1001\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\UserAssist\\{CEBFF5CD-ACE2-4F4F-9178-9926F41749EA}\\Count\\Microsoft.Windows.Cortana_cw5n1h2txyewy!CortanaUI', 'ValueFullPath': '\\REGISTRY\\USER\\S-1-5-21-2350377626-499376046-3523757530-1001\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\UserAssist\\{CEBFF5CD-ACE2-4F4F-9178-9926F41749EA}\\Count\\Zvpebfbsg.Jvaqbjf.Pbegnan_pj5a1u2gklrjl!PbegnanHV', 'UserAssist': {'session': 6, 'run_count': 0, 'focus_count': 35, 'focus_time': 1027558284, 'last_execution': '1601-01-01 00:00:00'}} 26 | {'ProcessId': 8116, 'ThreadId': 12156, 'TimeStamp': '2019-02-10 04:13:17.570752', 'EventDescriptor': {'Opcode': 36, 'Keyword': 9223372036854776064, 'KeywordStr': 'SetValueKey'}, 'KeyObject': 18446652304217692176, 'Status': 0, 'Type': 3, 'DataSize': 72, 'KeyName': '', 'ValueName': 'Zvpebfbsg.Jvaqbjf.Pbegnan_pj5a1u2gklrjl!PbegnanHV', 'CapturedDataSize': 0, 'CapturedData': None, 'PreviousDataType': 0, 'PreviousDataSize': 0, 'PreviousDataCapturedSize': 0, 'PreviousData': None, 'ValueNameDecoded': '\\REGISTRY\\USER\\S-1-5-21-2350377626-499376046-3523757530-1001\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\UserAssist\\{CEBFF5CD-ACE2-4F4F-9178-9926F41749EA}\\Count\\Microsoft.Windows.Cortana_cw5n1h2txyewy!CortanaUI', 'ValueFullPath': '\\REGISTRY\\USER\\S-1-5-21-2350377626-499376046-3523757530-1001\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\UserAssist\\{CEBFF5CD-ACE2-4F4F-9178-9926F41749EA}\\Count\\Zvpebfbsg.Jvaqbjf.Pbegnan_pj5a1u2gklrjl!PbegnanHV', 'UserAssist': {'session': 6, 'run_count': 0, 'focus_count': 35, 'focus_time': 1027558284, 'last_execution': '1601-01-01 00:00:00'}} 27 | {'ProcessId': 8116, 'ThreadId': 12156, 'TimeStamp': '2019-02-10 04:13:17.570788', 'EventDescriptor': {'Opcode': 36, 'Keyword': 9223372036854776064, 'KeywordStr': 'SetValueKey'}, 'KeyObject': 18446652304217692176, 'Status': 0, 'Type': 3, 'DataSize': 1612, 'KeyName': '', 'ValueName': 'HRZR_PGYFRFFVBA', 'CapturedDataSize': 0, 'CapturedData': None, 'PreviousDataType': 0, 'PreviousDataSize': 0, 'PreviousDataCapturedSize': 0, 'PreviousData': None, 'ValueNameDecoded': '\\REGISTRY\\USER\\S-1-5-21-2350377626-499376046-3523757530-1001\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\UserAssist\\{CEBFF5CD-ACE2-4F4F-9178-9926F41749EA}\\Count\\UEME_CTLSESSION', 'ValueFullPath': '\\REGISTRY\\USER\\S-1-5-21-2350377626-499376046-3523757530-1001\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\UserAssist\\{CEBFF5CD-ACE2-4F4F-9178-9926F41749EA}\\Count\\HRZR_PGYFRFFVBA', 'UserAssist': {'session': 6, 'run_count': 353, 'focus_count': 2334, 'focus_time': 7471203, 'last_execution': '1708-01-15 02:09:16.103692'}} 28 | {'ProcessId': 8116, 'ThreadId': 12156, 'TimeStamp': '2019-02-10 04:13:17.812922', 'EventDescriptor': {'Opcode': 38, 'Keyword': 9223372036854776832, 'KeywordStr': 'QueryValueKey'}, 'KeyObject': 18446652304215097792, 'Status': 0, 'InfoClass': 2, 'DataSize': 84, 'KeyName': '', 'ValueName': '{N77S5Q77-2R2O-44P3-N6N2-NON601054N51}\\Flfgrz Gbbyf\\Pbzznaq Cebzcg.yax', 'CapturedDataSize': 0, 'CapturedData': None, 'ValueNameDecoded': '\\REGISTRY\\USER\\S-1-5-21-2350377626-499376046-3523757530-1001\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\UserAssist\\{F4E57C4B-2036-45F0-A9AB-443BCFE33D9F}\\Count\\{A77F5D77-2E2B-44C3-A6A2-ABA601054A51}\\System Tools\\Command Prompt.lnk', 'ValueFullPath': '\\REGISTRY\\USER\\S-1-5-21-2350377626-499376046-3523757530-1001\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\UserAssist\\{F4E57C4B-2036-45F0-A9AB-443BCFE33D9F}\\Count\\{N77S5Q77-2R2O-44P3-N6N2-NON601054N51}\\Flfgrz Gbbyf\\Pbzznaq Cebzcg.yax', 'UserAssist': {'session': 6, 'run_count': 16, 'focus_count': 0, 'focus_time': 3212836864, 'last_execution': '2019-02-10 04:13:17.812000'}} 29 | {'ProcessId': 8116, 'ThreadId': 12156, 'TimeStamp': '2019-02-10 04:13:17.813002', 'EventDescriptor': {'Opcode': 36, 'Keyword': 9223372036854776064, 'KeywordStr': 'SetValueKey'}, 'KeyObject': 18446652304215097792, 'Status': 0, 'Type': 3, 'DataSize': 72, 'KeyName': '', 'ValueName': '{N77S5Q77-2R2O-44P3-N6N2-NON601054N51}\\Flfgrz Gbbyf\\Pbzznaq Cebzcg.yax', 'CapturedDataSize': 0, 'CapturedData': None, 'PreviousDataType': 0, 'PreviousDataSize': 0, 'PreviousDataCapturedSize': 0, 'PreviousData': None, 'ValueNameDecoded': '\\REGISTRY\\USER\\S-1-5-21-2350377626-499376046-3523757530-1001\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\UserAssist\\{F4E57C4B-2036-45F0-A9AB-443BCFE33D9F}\\Count\\{A77F5D77-2E2B-44C3-A6A2-ABA601054A51}\\System Tools\\Command Prompt.lnk', 'ValueFullPath': '\\REGISTRY\\USER\\S-1-5-21-2350377626-499376046-3523757530-1001\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\UserAssist\\{F4E57C4B-2036-45F0-A9AB-443BCFE33D9F}\\Count\\{N77S5Q77-2R2O-44P3-N6N2-NON601054N51}\\Flfgrz Gbbyf\\Pbzznaq Cebzcg.yax', 'UserAssist': {'session': 6, 'run_count': 16, 'focus_count': 0, 'focus_time': 3212836864, 'last_execution': '2019-02-10 04:13:17.812000'}} 30 | {'ProcessId': 8116, 'ThreadId': 12156, 'TimeStamp': '2019-02-10 04:13:17.813026', 'EventDescriptor': {'Opcode': 36, 'Keyword': 9223372036854776064, 'KeywordStr': 'SetValueKey'}, 'KeyObject': 18446652304215097792, 'Status': 0, 'Type': 3, 'DataSize': 1612, 'KeyName': '', 'ValueName': 'HRZR_PGYFRFFVBA', 'CapturedDataSize': 0, 'CapturedData': None, 'PreviousDataType': 0, 'PreviousDataSize': 0, 'PreviousDataCapturedSize': 0, 'PreviousData': None, 'ValueNameDecoded': '\\REGISTRY\\USER\\S-1-5-21-2350377626-499376046-3523757530-1001\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\UserAssist\\{F4E57C4B-2036-45F0-A9AB-443BCFE33D9F}\\Count\\UEME_CTLSESSION', 'ValueFullPath': '\\REGISTRY\\USER\\S-1-5-21-2350377626-499376046-3523757530-1001\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\UserAssist\\{F4E57C4B-2036-45F0-A9AB-443BCFE33D9F}\\Count\\HRZR_PGYFRFFVBA', 'UserAssist': {'session': 6, 'run_count': 248, 'focus_count': 0, 'focus_time': 3342405, 'last_execution': '1641-02-20 09:48:19.852295'}} 31 | {'ProcessId': 8116, 'ThreadId': 12156, 'TimeStamp': '2019-02-10 04:13:17.813032', 'EventDescriptor': {'Opcode': 38, 'Keyword': 9223372036854776832, 'KeywordStr': 'QueryValueKey'}, 'KeyObject': 18446652304215097792, 'Status': 0, 'InfoClass': 2, 'DataSize': 84, 'KeyName': '', 'ValueName': '{N77S5Q77-2R2O-44P3-N6N2-NON601054N51}\\Flfgrz Gbbyf\\Pbzznaq Cebzcg.yax', 'CapturedDataSize': 0, 'CapturedData': None, 'ValueNameDecoded': '\\REGISTRY\\USER\\S-1-5-21-2350377626-499376046-3523757530-1001\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\UserAssist\\{F4E57C4B-2036-45F0-A9AB-443BCFE33D9F}\\Count\\{A77F5D77-2E2B-44C3-A6A2-ABA601054A51}\\System Tools\\Command Prompt.lnk', 'ValueFullPath': '\\REGISTRY\\USER\\S-1-5-21-2350377626-499376046-3523757530-1001\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\UserAssist\\{F4E57C4B-2036-45F0-A9AB-443BCFE33D9F}\\Count\\{N77S5Q77-2R2O-44P3-N6N2-NON601054N51}\\Flfgrz Gbbyf\\Pbzznaq Cebzcg.yax', 'UserAssist': {'session': 6, 'run_count': 16, 'focus_count': 0, 'focus_time': 3212836864, 'last_execution': '2019-02-10 04:13:17.812000'}} 32 | {'ProcessId': 8116, 'ThreadId': 12156, 'TimeStamp': '2019-02-10 04:13:17.813048', 'EventDescriptor': {'Opcode': 36, 'Keyword': 9223372036854776064, 'KeywordStr': 'SetValueKey'}, 'KeyObject': 18446652304215097792, 'Status': 0, 'Type': 3, 'DataSize': 72, 'KeyName': '', 'ValueName': '{N77S5Q77-2R2O-44P3-N6N2-NON601054N51}\\Flfgrz Gbbyf\\Pbzznaq Cebzcg.yax', 'CapturedDataSize': 0, 'CapturedData': None, 'PreviousDataType': 0, 'PreviousDataSize': 0, 'PreviousDataCapturedSize': 0, 'PreviousData': None, 'ValueNameDecoded': '\\REGISTRY\\USER\\S-1-5-21-2350377626-499376046-3523757530-1001\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\UserAssist\\{F4E57C4B-2036-45F0-A9AB-443BCFE33D9F}\\Count\\{A77F5D77-2E2B-44C3-A6A2-ABA601054A51}\\System Tools\\Command Prompt.lnk', 'ValueFullPath': '\\REGISTRY\\USER\\S-1-5-21-2350377626-499376046-3523757530-1001\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\UserAssist\\{F4E57C4B-2036-45F0-A9AB-443BCFE33D9F}\\Count\\{N77S5Q77-2R2O-44P3-N6N2-NON601054N51}\\Flfgrz Gbbyf\\Pbzznaq Cebzcg.yax', 'UserAssist': {'session': 6, 'run_count': 16, 'focus_count': 0, 'focus_time': 3212836864, 'last_execution': '2019-02-10 04:13:17.812000'}} 33 | {'ProcessId': 8116, 'ThreadId': 12156, 'TimeStamp': '2019-02-10 04:13:17.813060', 'EventDescriptor': {'Opcode': 36, 'Keyword': 9223372036854776064, 'KeywordStr': 'SetValueKey'}, 'KeyObject': 18446652304215097792, 'Status': 0, 'Type': 3, 'DataSize': 1612, 'KeyName': '', 'ValueName': 'HRZR_PGYFRFFVBA', 'CapturedDataSize': 0, 'CapturedData': None, 'PreviousDataType': 0, 'PreviousDataSize': 0, 'PreviousDataCapturedSize': 0, 'PreviousData': None, 'ValueNameDecoded': '\\REGISTRY\\USER\\S-1-5-21-2350377626-499376046-3523757530-1001\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\UserAssist\\{F4E57C4B-2036-45F0-A9AB-443BCFE33D9F}\\Count\\UEME_CTLSESSION', 'ValueFullPath': '\\REGISTRY\\USER\\S-1-5-21-2350377626-499376046-3523757530-1001\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\UserAssist\\{F4E57C4B-2036-45F0-A9AB-443BCFE33D9F}\\Count\\HRZR_PGYFRFFVBA', 'UserAssist': {'session': 6, 'run_count': 248, 'focus_count': 0, 'focus_time': 3342405, 'last_execution': '1641-02-20 09:48:19.852295'}} 34 | {'ProcessId': 8116, 'ThreadId': 12156, 'TimeStamp': '2019-02-10 04:13:17.813082', 'EventDescriptor': {'Opcode': 38, 'Keyword': 9223372036854776832, 'KeywordStr': 'QueryValueKey'}, 'KeyObject': 18446652304217692176, 'Status': 0, 'InfoClass': 2, 'DataSize': 84, 'KeyName': '', 'ValueName': '{1NP14R77-02R7-4R5Q-O744-2RO1NR5198O7}\\pzq.rkr', 'CapturedDataSize': 0, 'CapturedData': None, 'ValueNameDecoded': '\\REGISTRY\\USER\\S-1-5-21-2350377626-499376046-3523757530-1001\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\UserAssist\\{CEBFF5CD-ACE2-4F4F-9178-9926F41749EA}\\Count\\{1AC14E77-02E7-4E5D-B744-2EB1AE5198B7}\\cmd.exe', 'ValueFullPath': '\\REGISTRY\\USER\\S-1-5-21-2350377626-499376046-3523757530-1001\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\UserAssist\\{CEBFF5CD-ACE2-4F4F-9178-9926F41749EA}\\Count\\{1NP14R77-02R7-4R5Q-O744-2RO1NR5198O7}\\pzq.rkr', 'UserAssist': {'session': 6, 'run_count': 16, 'focus_count': 156, 'focus_time': 1048515261, 'last_execution': '2019-02-10 04:13:17.812000'}} 35 | {'ProcessId': 8116, 'ThreadId': 12156, 'TimeStamp': '2019-02-10 04:13:17.813110', 'EventDescriptor': {'Opcode': 36, 'Keyword': 9223372036854776064, 'KeywordStr': 'SetValueKey'}, 'KeyObject': 18446652304217692176, 'Status': 0, 'Type': 3, 'DataSize': 72, 'KeyName': '', 'ValueName': '{1NP14R77-02R7-4R5Q-O744-2RO1NR5198O7}\\pzq.rkr', 'CapturedDataSize': 0, 'CapturedData': None, 'PreviousDataType': 0, 'PreviousDataSize': 0, 'PreviousDataCapturedSize': 0, 'PreviousData': None, 'ValueNameDecoded': '\\REGISTRY\\USER\\S-1-5-21-2350377626-499376046-3523757530-1001\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\UserAssist\\{CEBFF5CD-ACE2-4F4F-9178-9926F41749EA}\\Count\\{1AC14E77-02E7-4E5D-B744-2EB1AE5198B7}\\cmd.exe', 'ValueFullPath': '\\REGISTRY\\USER\\S-1-5-21-2350377626-499376046-3523757530-1001\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\UserAssist\\{CEBFF5CD-ACE2-4F4F-9178-9926F41749EA}\\Count\\{1NP14R77-02R7-4R5Q-O744-2RO1NR5198O7}\\pzq.rkr', 'UserAssist': {'session': 6, 'run_count': 16, 'focus_count': 156, 'focus_time': 1048515261, 'last_execution': '2019-02-10 04:13:17.812000'}} 36 | {'ProcessId': 8116, 'ThreadId': 12156, 'TimeStamp': '2019-02-10 04:13:17.813124', 'EventDescriptor': {'Opcode': 36, 'Keyword': 9223372036854776064, 'KeywordStr': 'SetValueKey'}, 'KeyObject': 18446652304217692176, 'Status': 0, 'Type': 3, 'DataSize': 1612, 'KeyName': '', 'ValueName': 'HRZR_PGYFRFFVBA', 'CapturedDataSize': 0, 'CapturedData': None, 'PreviousDataType': 0, 'PreviousDataSize': 0, 'PreviousDataCapturedSize': 0, 'PreviousData': None, 'ValueNameDecoded': '\\REGISTRY\\USER\\S-1-5-21-2350377626-499376046-3523757530-1001\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\UserAssist\\{CEBFF5CD-ACE2-4F4F-9178-9926F41749EA}\\Count\\UEME_CTLSESSION', 'ValueFullPath': '\\REGISTRY\\USER\\S-1-5-21-2350377626-499376046-3523757530-1001\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\UserAssist\\{CEBFF5CD-ACE2-4F4F-9178-9926F41749EA}\\Count\\HRZR_PGYFRFFVBA', 'UserAssist': {'session': 6, 'run_count': 353, 'focus_count': 2334, 'focus_time': 7471203, 'last_execution': '1708-01-15 02:09:16.103692'}} 37 | {'ProcessId': 8116, 'ThreadId': 100, 'TimeStamp': '2019-02-10 04:13:17.813684', 'EventDescriptor': {'Opcode': 38, 'Keyword': 9223372036854776832, 'KeywordStr': 'QueryValueKey'}, 'KeyObject': 18446652304217692176, 'Status': 0, 'InfoClass': 2, 'DataSize': 84, 'KeyName': '', 'ValueName': '{1NP14R77-02R7-4R5Q-O744-2RO1NR5198O7}\\pzq.rkr', 'CapturedDataSize': 0, 'CapturedData': None, 'ValueNameDecoded': '\\REGISTRY\\USER\\S-1-5-21-2350377626-499376046-3523757530-1001\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\UserAssist\\{CEBFF5CD-ACE2-4F4F-9178-9926F41749EA}\\Count\\{1AC14E77-02E7-4E5D-B744-2EB1AE5198B7}\\cmd.exe', 'ValueFullPath': '\\REGISTRY\\USER\\S-1-5-21-2350377626-499376046-3523757530-1001\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\UserAssist\\{CEBFF5CD-ACE2-4F4F-9178-9926F41749EA}\\Count\\{1NP14R77-02R7-4R5Q-O744-2RO1NR5198O7}\\pzq.rkr', 'UserAssist': {'session': 6, 'run_count': 16, 'focus_count': 156, 'focus_time': 1048515261, 'last_execution': '2019-02-10 04:13:17.812000'}} 38 | {'ProcessId': 8116, 'ThreadId': 100, 'TimeStamp': '2019-02-10 04:13:17.813690', 'EventDescriptor': {'Opcode': 38, 'Keyword': 9223372036854776832, 'KeywordStr': 'QueryValueKey'}, 'KeyObject': 18446652304217692176, 'Status': 0, 'InfoClass': 2, 'DataSize': 84, 'KeyName': '', 'ValueName': '{1NP14R77-02R7-4R5Q-O744-2RO1NR5198O7}\\pzq.rkr', 'CapturedDataSize': 0, 'CapturedData': None, 'ValueNameDecoded': '\\REGISTRY\\USER\\S-1-5-21-2350377626-499376046-3523757530-1001\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\UserAssist\\{CEBFF5CD-ACE2-4F4F-9178-9926F41749EA}\\Count\\{1AC14E77-02E7-4E5D-B744-2EB1AE5198B7}\\cmd.exe', 'ValueFullPath': '\\REGISTRY\\USER\\S-1-5-21-2350377626-499376046-3523757530-1001\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\UserAssist\\{CEBFF5CD-ACE2-4F4F-9178-9926F41749EA}\\Count\\{1NP14R77-02R7-4R5Q-O744-2RO1NR5198O7}\\pzq.rkr', 'UserAssist': {'session': 6, 'run_count': 16, 'focus_count': 156, 'focus_time': 1048515261, 'last_execution': '2019-02-10 04:13:17.812000'}} 39 | ``` 40 | 41 | #### Formatted Output 42 | ``` 43 | C:\Python36\python.exe userassist_monitor.py --format "{record['TimeStamp']} [{record['EventDescriptor']['KeywordStr']}]: App: {os.path.basename(record['ValueNameDecoded'])} Last Execution: {record['UserAssist']['last_execution']} Run Count: {record['UserAssist']['run_count']} Focus Count: {record['UserAssist']['focus_count']}" 44 | 2019-02-10 04:10:20.160322 [QueryValueKey]: App: pycharm64.exe Last Execution: 2019-02-10 00:21:45.266000 Run Count: 28 Focus Count: 755 45 | 2019-02-10 04:10:20.160456 [SetValueKey]: App: pycharm64.exe Last Execution: 2019-02-10 00:21:45.266000 Run Count: 28 Focus Count: 755 46 | 2019-02-10 04:10:20.160520 [SetValueKey]: App: UEME_CTLSESSION Last Execution: 1708-01-15 02:09:16.103692 Run Count: 350 Focus Count: 2330 47 | 2019-02-10 04:10:22.787306 [QueryValueKey]: App: Microsoft.WindowsCalculator_8wekyb3d8bbwe!App Last Execution: 2019-02-10 04:10:22.787000 Run Count: 14 Focus Count: 11 48 | 2019-02-10 04:10:22.787386 [SetValueKey]: App: Microsoft.WindowsCalculator_8wekyb3d8bbwe!App Last Execution: 2019-02-10 04:10:22.787000 Run Count: 14 Focus Count: 11 49 | 2019-02-10 04:10:22.787410 [SetValueKey]: App: UEME_CTLSESSION Last Execution: 1708-01-15 02:09:16.103692 Run Count: 351 Focus Count: 2330 50 | 2019-02-10 04:10:22.787868 [QueryValueKey]: App: Microsoft.WindowsCalculator_8wekyb3d8bbwe!App Last Execution: 2019-02-10 04:10:22.787000 Run Count: 14 Focus Count: 11 51 | 2019-02-10 04:10:22.787872 [QueryValueKey]: App: Microsoft.WindowsCalculator_8wekyb3d8bbwe!App Last Execution: 2019-02-10 04:10:22.787000 Run Count: 14 Focus Count: 11 52 | 2019-02-10 04:10:22.792662 [QueryValueKey]: App: Microsoft.Windows.Cortana_cw5n1h2txyewy!CortanaUI Last Execution: 1601-01-01 00:00:00 Run Count: 0 Focus Count: 34 53 | 2019-02-10 04:10:22.792700 [SetValueKey]: App: Microsoft.Windows.Cortana_cw5n1h2txyewy!CortanaUI Last Execution: 1601-01-01 00:00:00 Run Count: 0 Focus Count: 34 54 | 2019-02-10 04:10:22.792720 [SetValueKey]: App: UEME_CTLSESSION Last Execution: 1708-01-15 02:09:16.103692 Run Count: 351 Focus Count: 2330 55 | 2019-02-10 04:10:25.495542 [QueryValueKey]: App: Microsoft.WindowsCalculator_8wekyb3d8bbwe!App Last Execution: 2019-02-10 04:10:22.787000 Run Count: 14 Focus Count: 11 56 | 2019-02-10 04:10:25.495604 [SetValueKey]: App: Microsoft.WindowsCalculator_8wekyb3d8bbwe!App Last Execution: 2019-02-10 04:10:22.787000 Run Count: 14 Focus Count: 11 57 | 2019-02-10 04:10:25.495630 [SetValueKey]: App: UEME_CTLSESSION Last Execution: 1708-01-15 02:09:16.103692 Run Count: 352 Focus Count: 2330 58 | 2019-02-10 04:10:26.326290 [QueryValueKey]: App: pycharm64.exe Last Execution: 2019-02-10 00:21:45.266000 Run Count: 28 Focus Count: 755 59 | 2019-02-10 04:10:26.326428 [SetValueKey]: App: pycharm64.exe Last Execution: 2019-02-10 00:21:45.266000 Run Count: 28 Focus Count: 755 60 | 2019-02-10 04:10:26.326468 [SetValueKey]: App: UEME_CTLSESSION Last Execution: 1708-01-15 02:09:16.103692 Run Count: 352 Focus Count: 2330 61 | ``` 62 | -------------------------------------------------------------------------------- /scripts/userassist_monitor.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | sys.path.append("..") 4 | import fmt 5 | import codecs 6 | import struct 7 | import argparse 8 | import datetime 9 | import win32api 10 | import win32con 11 | from functools import partial 12 | from winthingies.trace import EventTraceHandler 13 | from winthingies.trace import TraceProvider 14 | from winthingies.process import iterate_processes 15 | from winthingies.handle import iterate_handles 16 | 17 | VERSION = "0.0.1" 18 | 19 | CloseKey = 0x0000000000000001 20 | QuerySecurityKey = 0x0000000000000002 21 | SetSecurityKey = 0x0000000000000004 22 | EnumerateValueKey = 0x0000000000000010 23 | QueryMultipleValueKey = 0x0000000000000020 24 | SetInformationKey = 0x0000000000000040 25 | FlushKey = 0x0000000000000080 26 | SetValueKey = 0x0000000000000100 27 | DeleteValueKey = 0x0000000000000200 28 | QueryValueKey = 0x0000000000000400 29 | EnumerateKey = 0x0000000000000800 30 | CreateKey = 0x0000000000001000 31 | OpenKey = 0x0000000000002000 32 | DeleteKey = 0x0000000000004000 33 | QueryKey = 0x0000000000008000 34 | 35 | 36 | KEYWORD_OPS = { 37 | 0x0000000000000001: "CloseKey", 38 | 0x0000000000000002: "QuerySecurityKey", 39 | 0x0000000000000004: "SetSecurityKey", 40 | 0x0000000000000010: "EnumerateValueKey", 41 | 0x0000000000000020: "QueryMultipleValueKey", 42 | 0x0000000000000040: "SetInformationKey", 43 | 0x0000000000000080: "FlushKey", 44 | 0x0000000000000100: "SetValueKey", 45 | 0x0000000000000200: "DeleteValueKey", 46 | 0x0000000000000400: "QueryValueKey", 47 | 0x0000000000000800: "EnumerateKey", 48 | 0x0000000000001000: "CreateKey", 49 | 0x0000000000002000: "OpenKey", 50 | 0x0000000000004000: "DeleteKey", 51 | 0x0000000000008000: "QueryKey" 52 | } 53 | 54 | 55 | def get_arguments(): 56 | usage = u"""Monitor UserAssist via ETL. Version: {} 57 | """.format(VERSION) 58 | 59 | arguments = argparse.ArgumentParser( 60 | description=usage, 61 | formatter_class=argparse.RawDescriptionHelpFormatter 62 | ) 63 | 64 | arguments.add_argument( 65 | "--format", 66 | dest="format_str", 67 | action="store", 68 | default=None, 69 | help="This is a python fstring to format output. Default prints json lines." 70 | ) 71 | 72 | return arguments 73 | 74 | 75 | class UserAssist(object): 76 | """ 77 | Structure resources: 78 | https://github.com/EricZimmerman/RegistryPlugins/blob/8822318182ec28a385a9544422d8ae4d14df7fd9/RegistryPlugin.UserAssist/UserAssist.cs#L79 79 | https://www.aldeid.com/wiki/Windows-userassist-keys 80 | https://github.com/keydet89/RegRipper2.8/blob/master/plugins/userassist_tln.pl#L85 81 | """ 82 | def __init__(self, buf): 83 | self.session = struct.unpack(") List of providers to start. 175 | """ 176 | self._session_name = session_name 177 | self._providers = providers 178 | 179 | if len(self._providers) < 1: 180 | raise Exception( 181 | "At least 1 provider is needed for a trace session." 182 | ) 183 | 184 | # Flag for kernel trace 185 | self._is_kernel_trace = False 186 | 187 | # The trace session properties 188 | self._properties = TraceProperties() 189 | 190 | # The trace session handle 191 | self._session_handle = TRACEHANDLE() 192 | 193 | if self._session_name == KERNEL_LOGGER_NAME: 194 | if len(self._providers) > 1: 195 | raise Exception( 196 | "A Kernel Logger can only have 1 provider!" 197 | ) 198 | 199 | self._is_kernel_trace = True 200 | self._properties._props.contents.Wnode.Guid = self._providers[0].guid 201 | self._properties._props.contents.LogFileMode |= EVENT_TRACE_SYSTEM_LOGGER_MODE 202 | 203 | if self._providers[0].match_any_keyword is not None: 204 | self._properties._props.contents.EnableFlags = self._providers[0].match_any_keyword 205 | else: 206 | self._properties._props.contents.EnableFlags = DEFAULT_NT_KERNEL_LOGGER_FLAGS 207 | 208 | def __del__(self): 209 | if self._session_handle: 210 | self.stop() 211 | 212 | def start(self): 213 | """Starts the Trace Provider. 214 | """ 215 | # Create new session handle 216 | session_handle = TRACEHANDLE() 217 | 218 | # Get session properties 219 | trace_properties = self._properties.get_trace_properties() 220 | 221 | # Start trace 222 | status = advapi32.StartTrace( 223 | byref(session_handle), 224 | self._session_name, 225 | trace_properties 226 | ) 227 | 228 | # Set current session to new session handle 229 | self._session_handle = session_handle 230 | 231 | # Check status 232 | if status == ERROR_ALREADY_EXISTS: 233 | self.stop() 234 | 235 | status = advapi32.StartTrace( 236 | byref(session_handle), 237 | self._session_name, 238 | trace_properties 239 | ) 240 | if status != ERROR_SUCCESS: 241 | raise( 242 | Exception('Unable to start event trace') 243 | ) 244 | 245 | self._session_handle = session_handle 246 | elif status == ERROR_ACCESS_DENIED: 247 | raise( 248 | Exception("Access Denied") 249 | ) 250 | elif status == ERROR_BAD_LENGTH: 251 | raise( 252 | Exception('Incorrect buffer size for the trace buffer') 253 | ) 254 | elif status == ERROR_INVALID_PARAMETER: 255 | raise( 256 | Exception('Invalid trace parameter') 257 | ) 258 | elif status != ERROR_SUCCESS: 259 | raise( 260 | Exception('Unable to start event trace') 261 | ) 262 | 263 | if not self._is_kernel_trace: 264 | # If the trace session is not a Kernel level we can use multiple providers 265 | self._start_providers() 266 | 267 | def _start_providers(self): 268 | """A non-kernel level trace session can have multiple providers. 269 | This functions iterates through this session's providers and enables 270 | them. 271 | 272 | :return: (None) 273 | """ 274 | for provider in self._providers: 275 | if provider.enable_parameters: 276 | raise Exception( 277 | "Provider enable_parameters are not yet implemented." 278 | ) 279 | 280 | status = advapi32.EnableTraceEx2( 281 | self._session_handle, 282 | ctypes.byref(provider.guid), 283 | EVENT_CONTROL_CODE_ENABLE_PROVIDER, 284 | provider.level, 285 | provider.match_any_keyword, 286 | provider.match_all_keyword, 287 | 0, 288 | provider.enable_parameters 289 | ) 290 | 291 | if status != ERROR_SUCCESS: 292 | raise ctypes.WinError( 293 | status 294 | ) 295 | 296 | def stop(self): 297 | """Stops the Trace Provider. 298 | """ 299 | # Get trace properties 300 | trace_properties = self._properties.get_trace_properties() 301 | 302 | # Get current session handle 303 | current_session_handle = self._session_handle 304 | 305 | # Set session handle to an empty session 306 | self._session_handle = TRACEHANDLE() 307 | 308 | # Stop current session handle 309 | status = advapi32.ControlTrace( 310 | current_session_handle, 311 | self._session_name, 312 | trace_properties, 313 | EVENT_TRACE_CONTROL_STOP 314 | ) 315 | 316 | if status != ERROR_SUCCESS: 317 | raise Exception( 318 | 'Unable to stop trace {}. [return value: {}]'.format( 319 | self._session_name, 320 | status 321 | ) 322 | ) 323 | 324 | 325 | class EventTraceHandler(object): 326 | """The EventTraceHandler will handle both starting providers for event generation 327 | and starting the consumer for event handling. Using this class allows you to 328 | use KeyboardInterrupt to stop consumption. 329 | """ 330 | def __init__(self, session_name, providers, callback): 331 | """ 332 | 333 | :param session_name: (str) Then name to use for this session. ('NT Kernel Logger' 334 | must be used if Kernel trace providers are used) 335 | :param providers: (list) This is a list of TraceProvider objects. 336 | :param callback: (function) This gets called for every event. 337 | """ 338 | self._session_name = session_name 339 | self._providers = providers 340 | self._callback = callback 341 | 342 | def start_session(self): 343 | """This starts the tracing session. 344 | 345 | :return: (None) 346 | """ 347 | t_session = TraceSession( 348 | self._session_name, 349 | self._providers 350 | ) 351 | t_session.start() 352 | 353 | t_consumer = TraceConsumer( 354 | self._session_name, 355 | self._callback 356 | ) 357 | t_consumer.start() 358 | 359 | try: 360 | while True: 361 | time.sleep(.25) 362 | except KeyboardInterrupt: 363 | t_consumer.stop() 364 | t_consumer.join() 365 | -------------------------------------------------------------------------------- /winthingies/win32/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forensicmatt/PyWindowsThingies/000ff216c15ef9cd92924a692fce907ec829bc9d/winthingies/win32/__init__.py -------------------------------------------------------------------------------- /winthingies/win32/advapi32.py: -------------------------------------------------------------------------------- 1 | import ctypes 2 | from winthingies.win32.winstructs import * 3 | 4 | advapi32 = ctypes.WinDLL('advapi32', use_last_error=True) 5 | 6 | 7 | StartTrace = advapi32.StartTraceW 8 | StartTrace.argtypes = [ 9 | ctypes.POINTER(TRACEHANDLE), 10 | ctypes.c_wchar_p, 11 | ctypes.POINTER(EVENT_TRACE_PROPERTIES) 12 | ] 13 | StartTrace.restype = ULONG 14 | 15 | 16 | ControlTrace = advapi32.ControlTraceW 17 | ControlTrace.argtypes = [ 18 | TRACEHANDLE, 19 | PWCHAR, 20 | ctypes.POINTER( 21 | EVENT_TRACE_PROPERTIES 22 | ), 23 | ULONG 24 | ] 25 | ControlTrace.restype = ULONG 26 | 27 | 28 | OpenTraceW = advapi32.OpenTraceW 29 | OpenTraceW.argtypes = [ 30 | ctypes.POINTER( 31 | EVENT_TRACE_LOGFILE 32 | ) 33 | ] 34 | OpenTraceW.restype = TRACEHANDLE 35 | 36 | 37 | ProcessTrace = advapi32.ProcessTrace 38 | ProcessTrace.argtypes = [ 39 | ctypes.POINTER(TRACEHANDLE), 40 | ULONG, 41 | ctypes.POINTER( 42 | FILETIME 43 | ), 44 | ctypes.POINTER( 45 | FILETIME 46 | ) 47 | ] 48 | ProcessTrace.restype = ULONG 49 | 50 | 51 | CloseTrace = advapi32.CloseTrace 52 | CloseTrace.argtypes = [ 53 | TRACEHANDLE 54 | ] 55 | CloseTrace.restype = ULONG 56 | 57 | 58 | EnableTraceEx2 = advapi32.EnableTraceEx2 59 | EnableTraceEx2.argtypes = [ 60 | TRACEHANDLE, 61 | ctypes.POINTER( 62 | GUID 63 | ), 64 | ULONG, 65 | BYTE, 66 | ULONGLONG, 67 | ULONGLONG, 68 | ULONG, 69 | ctypes.POINTER( 70 | ENABLE_TRACE_PARAMETERS 71 | ) 72 | ] 73 | EnableTraceEx2.restype = ULONG -------------------------------------------------------------------------------- /winthingies/win32/const.py: -------------------------------------------------------------------------------- 1 | import win32con 2 | 3 | MAX_UINT = (2 ** 32) - 1 4 | 5 | SystemExtendedHandleInformation = 64 6 | 7 | ERROR_SUCCESS = 0x00 8 | ERROR_ACCESS_DENIED = 0x05 9 | ERROR_BAD_LENGTH = 0x18 10 | ERROR_INVALID_PARAMETER = 0x57 11 | ERROR_ALREADY_EXISTS = 0xB7 12 | ERROR_INSUFFICIENT_BUFFER = 0x7A 13 | ERROR_NOT_FOUND = 0x490 14 | ERROR_EVT_INVALID_EVENT_DATA = 0x3A9D 15 | ERROR_NO_MORE_ITEMS = 0x103 16 | 17 | EVENT_TRACE_CONTROL_QUERY = 0 18 | EVENT_TRACE_CONTROL_STOP = 1 19 | EVENT_TRACE_CONTROL_UPDATE = 2 20 | 21 | TH32CS_SNAPHEAPLIST = 0x00000001 22 | TH32CS_SNAPPROCESS = 0x00000002 23 | TH32CS_SNAPTHREAD = 0x00000004 24 | TH32CS_SNAPMODULE = 0x00000008 25 | TH32CS_SNAPMODULE32 = 0x00000010 26 | TH32CS_SNAPALL = TH32CS_SNAPHEAPLIST | TH32CS_SNAPPROCESS | TH32CS_SNAPTHREAD | TH32CS_SNAPMODULE 27 | TH32CS_INHERIT = 0x80000000 28 | 29 | STATUS_INFO_LENGTH_MISMATCH = 0xC0000004 30 | STATUS_BUFFER_OVERFLOW = 0x80000005 31 | STATUS_INVALID_HANDLE = 0xC0000008 32 | STATUS_BUFFER_TOO_SMALL = 0xC0000023 33 | STATUS_SUCCESS = 0 34 | 35 | PROCESS_TERMINATE = 0x0001 36 | PROCESS_CREATE_THREAD = 0x0002 37 | PROCESS_SET_SESSIONID = 0x0004 38 | PROCESS_VM_OPERATION = 0x0008 39 | PROCESS_VM_READ = 0x0010 40 | PROCESS_VM_WRITE = 0x0020 41 | PROCESS_DUP_HANDLE = 0x0040 42 | PROCESS_CREATE_PROCESS = 0x0080 43 | PROCESS_SET_QUOTA = 0x0100 44 | PROCESS_SET_INFORMATION = 0x0200 45 | PROCESS_QUERY_INFORMATION = 0x0400 46 | PROCESS_SUSPEND_RESUME = 0x0800 47 | PROCESS_QUERY_LIMITED_INFORMATION = 0x1000 48 | PROCESS_SET_LIMITED_INFORMATION = 0x2000 49 | PROCESS_ALL_ACCESS = win32con.PROCESS_ALL_ACCESS 50 | 51 | DUPLICATE_CLOSE_SOURCE = 0x00000001 52 | DUPLICATE_SAME_ACCESS = 0x00000002 53 | 54 | # Logger Mode flags 55 | # https://docs.microsoft.com/en-us/windows/desktop/etw/logging-mode-constants 56 | # https://github.com/fireeye/pywintrace/blob/master/etw/evntrace.py 57 | EVENT_TRACE_FILE_MODE_NONE = 0x00000000 # Logfile is off 58 | EVENT_TRACE_FILE_MODE_SEQUENTIAL = 0x00000001 # Log sequentially 59 | EVENT_TRACE_FILE_MODE_CIRCULAR = 0x00000002 # Log in circular manner 60 | EVENT_TRACE_FILE_MODE_APPEND = 0x00000004 # Append sequential log 61 | EVENT_TRACE_REAL_TIME_MODE = 0x00000100 # Real time mode on 62 | EVENT_TRACE_DELAY_OPEN_FILE_MODE = 0x00000200 # Delay opening file 63 | EVENT_TRACE_BUFFERING_MODE = 0x00000400 # Buffering mode only 64 | EVENT_TRACE_PRIVATE_LOGGER_MODE = 0x00000800 # Process Private Logger 65 | EVENT_TRACE_ADD_HEADER_MODE = 0x00001000 # Add a logfile header 66 | EVENT_TRACE_USE_GLOBAL_SEQUENCE = 0x00004000 # Use global sequence no. 67 | EVENT_TRACE_USE_LOCAL_SEQUENCE = 0x00008000 # Use local sequence no. 68 | EVENT_TRACE_RELOG_MODE = 0x00010000 # Relogger 69 | EVENT_TRACE_USE_PAGED_MEMORY = 0x01000000 # Use pageable buffers 70 | # Logger Mode flags on XP and above 71 | EVENT_TRACE_FILE_MODE_NEWFILE = 0x00000008 # Auto-switch log file 72 | EVENT_TRACE_FILE_MODE_PREALLOCATE = 0x00000020 # Pre-allocate mode 73 | # Logger Mode flags on Vista and above 74 | EVENT_TRACE_NONSTOPPABLE_MODE = 0x00000040 # Session cannot be stopped (Autologger only) 75 | EVENT_TRACE_SECURE_MODE = 0x00000080 # Secure session 76 | EVENT_TRACE_USE_KBYTES_FOR_SIZE = 0x00002000 # Use KBytes as file size unit 77 | EVENT_TRACE_PRIVATE_IN_PROC = 0x00020000 # In process private logger 78 | EVENT_TRACE_MODE_RESERVED = 0x00100000 # Reserved bit, used to signal Heap/Critsec tracing 79 | # Logger Mode flags on Win7 and above 80 | EVENT_TRACE_NO_PER_PROCESSOR_BUFFERING = 0x10000000 # Use this for low frequency sessions. 81 | # Logger Mode flags on Win8 and above 82 | EVENT_TRACE_SYSTEM_LOGGER_MODE = 0x02000000 # Receive events from SystemTraceProvider 83 | EVENT_TRACE_ADDTO_TRIAGE_DUMP = 0x80000000 # Add ETW buffers to triage dumps 84 | EVENT_TRACE_STOP_ON_HYBRID_SHUTDOWN = 0x00400000 # Stop on hybrid shutdown 85 | EVENT_TRACE_PERSIST_ON_HYBRID_SHUTDOWN = 0x00800000 # Persist on hybrid shutdown 86 | # Logger Mode flags on Blue and above 87 | EVENT_TRACE_INDEPENDENT_SESSION_MODE = 0x08000000 # Independent logger session 88 | 89 | EVENT_TRACE_FLAG_PROCESS = 0x00000001 90 | EVENT_TRACE_FLAG_THREAD = 0x00000002 91 | EVENT_TRACE_FLAG_IMAGE_LOAD = 0x00000004 92 | EVENT_TRACE_FLAG_DISK_IO = 0x00000100 93 | EVENT_TRACE_FLAG_DISK_FILE_IO = 0x00000200 94 | EVENT_TRACE_FLAG_MEMORY_PAGE_FAULTS = 0x00001000 95 | EVENT_TRACE_FLAG_MEMORY_HARD_FAULTS = 0x00002000 96 | EVENT_TRACE_FLAG_NETWORK_TCPIP = 0x00010000 97 | EVENT_TRACE_FLAG_REGISTRY = 0x00020000 98 | EVENT_TRACE_FLAG_DBGPRINT = 0x00040000 99 | EVENT_TRACE_FLAG_PROCESS_COUNTERS = 0x00000008 100 | EVENT_TRACE_FLAG_CSWITCH = 0x00000010 101 | EVENT_TRACE_FLAG_DPC = 0x00000020 102 | EVENT_TRACE_FLAG_INTERRUPT = 0x00000040 103 | EVENT_TRACE_FLAG_SYSTEMCALL = 0x00000080 104 | EVENT_TRACE_FLAG_DISK_IO_INIT = 0x00000400 105 | EVENT_TRACE_FLAG_ALPC = 0x00100000 106 | EVENT_TRACE_FLAG_SPLIT_IO = 0x00200000 107 | EVENT_TRACE_FLAG_DRIVER = 0x00800000 108 | EVENT_TRACE_FLAG_PROFILE = 0x01000000 109 | EVENT_TRACE_FLAG_FILE_IO = 0x02000000 110 | EVENT_TRACE_FLAG_FILE_IO_INIT = 0x04000000 111 | EVENT_TRACE_FLAG_DISPATCHER = 0x00000800 112 | EVENT_TRACE_FLAG_VIRTUAL_ALLOC = 0x00004000 113 | 114 | EVENT_TRACE_CONTROL_QUERY = 0 115 | EVENT_TRACE_CONTROL_STOP = 1 116 | EVENT_TRACE_CONTROL_UPDATE = 2 117 | 118 | EVENT_CONTROL_CODE_DISABLE_PROVIDER = 0 119 | EVENT_CONTROL_CODE_ENABLE_PROVIDER = 1 120 | EVENT_CONTROL_CODE_CAPTURE_STATE = 2 121 | 122 | PROCESS_TRACE_MODE_REAL_TIME = 0x00000100 123 | PROCESS_TRACE_MODE_RAW_TIMESTAMP = 0x00001000 124 | PROCESS_TRACE_MODE_EVENT_RECORD = 0x10000000 125 | 126 | TRACE_LEVEL_NONE = 0 127 | TRACE_LEVEL_CRITICAL = 1 128 | TRACE_LEVEL_ERROR = 2 129 | TRACE_LEVEL_WARNING = 3 130 | TRACE_LEVEL_INFORMATION = 4 131 | TRACE_LEVEL_VERBOSE = 5 132 | TRACE_LEVEL_RESERVED6 = 6 133 | TRACE_LEVEL_RESERVED7 = 7 134 | TRACE_LEVEL_RESERVED8 = 8 135 | TRACE_LEVEL_RESERVED9 = 9 136 | 137 | # https://msdn.microsoft.com/de-de/vstudio/aa964745(v=vs.80) 138 | DecodingSourceXMLFile = 0 139 | DecodingSourceWbem = 1 140 | DecodingSourceWPP = 2 141 | DecodingSourceTlg = 3 142 | 143 | # https://docs.microsoft.com/en-us/windows/desktop/api/tdh/ne-tdh-_property_flags 144 | PropertyStruct = 0x1 145 | PropertyParamLength = 0x2 146 | PropertyParamCount = 0x4 147 | PropertyWBEMXmlFragment = 0x8 148 | PropertyParamFixedLength = 0x10 149 | PropertyParamFixedCount = 0x20 150 | PropertyHasTags = 0x40 151 | PropertyHasCustomSchema = 0x80 152 | 153 | EVENTMAP_INFO_FLAG_MANIFEST_VALUEMAP = 1 154 | EVENTMAP_INFO_FLAG_MANIFEST_BITMAP = 2 155 | EVENTMAP_INFO_FLAG_MANIFEST_PATTERNMAP = 4 156 | EVENTMAP_INFO_FLAG_WBEM_VALUEMAP = 8 157 | EVENTMAP_INFO_FLAG_WBEM_BITMAP = 16 158 | EVENTMAP_INFO_FLAG_WBEM_FLAG = 32 159 | EVENTMAP_INFO_FLAG_WBEM_NO_MAP = 64 160 | 161 | TDH_INTYPE_NULL = 0 162 | TDH_INTYPE_UNICODESTRING = 1 163 | TDH_INTYPE_ANSISTRING = 2 164 | TDH_INTYPE_INT8 = 3 165 | TDH_INTYPE_UINT8 = 4 166 | TDH_INTYPE_INT16 = 5 167 | TDH_INTYPE_UINT16 = 6 168 | TDH_INTYPE_INT32 = 7 169 | TDH_INTYPE_UINT32 = 8 170 | TDH_INTYPE_INT64 = 9 171 | TDH_INTYPE_UINT64 = 10 172 | TDH_INTYPE_FLOAT = 11 173 | TDH_INTYPE_DOUBLE = 12 174 | TDH_INTYPE_BOOLEAN = 13 175 | TDH_INTYPE_BINARY = 14 176 | TDH_INTYPE_GUID = 15 177 | TDH_INTYPE_POINTER = 16 178 | TDH_INTYPE_FILETIME = 17 179 | TDH_INTYPE_SYSTEMTIME = 18 180 | TDH_INTYPE_SID = 19 181 | TDH_INTYPE_HEXINT32 = 20 182 | TDH_INTYPE_HEXINT64 = 21 183 | TDH_INTYPE_COUNTEDSTRING = 300 184 | TDH_INTYPE_COUNTEDANSISTRING = 301 185 | TDH_INTYPE_REVERSEDCOUNTEDSTRING = 302 186 | TDH_INTYPE_REVERSEDCOUNTEDANSISTRING = 303 187 | TDH_INTYPE_NONNULLTERMINATEDSTRING = 304 188 | TDH_INTYPE_NONNULLTERMINATEDANSISTRING = 305 189 | TDH_INTYPE_UNICODECHAR = 306 190 | TDH_INTYPE_ANSICHAR = 307 191 | TDH_INTYPE_SIZET = 308 192 | TDH_INTYPE_HEXDUMP = 309 193 | TDH_INTYPE_WBEMSID = 310 194 | 195 | TDH_OUTTYPE_NULL = 0 196 | TDH_OUTTYPE_STRING = 1 197 | TDH_OUTTYPE_DATETIME = 2 198 | TDH_OUTTYPE_BYTE = 3 199 | TDH_OUTTYPE_UNSIGNEDBYTE = 4 200 | TDH_OUTTYPE_SHORT = 5 201 | TDH_OUTTYPE_UNSIGNEDSHORT = 6 202 | TDH_OUTTYPE_INT = 7 203 | TDH_OUTTYPE_UNSIGNEDINT = 8 204 | TDH_OUTTYPE_LONG = 9 205 | TDH_OUTTYPE_UNSIGNEDLONG = 10 206 | TDH_OUTTYPE_FLOAT = 11 207 | TDH_OUTTYPE_DOUBLE = 12 208 | TDH_OUTTYPE_BOOLEAN = 13 209 | TDH_OUTTYPE_GUID = 14 210 | TDH_OUTTYPE_HEXBINARY = 15 211 | TDH_OUTTYPE_HEXINT8 = 16 212 | TDH_OUTTYPE_HEXINT16 = 17 213 | TDH_OUTTYPE_HEXINT32 = 18 214 | TDH_OUTTYPE_HEXINT64 = 19 215 | TDH_OUTTYPE_PID = 20 216 | TDH_OUTTYPE_TID = 21 217 | TDH_OUTTYPE_PORT = 22 218 | TDH_OUTTYPE_IPV4 = 23 219 | TDH_OUTTYPE_IPV6 = 24 220 | TDH_OUTTYPE_SOCKETADDRESS = 25 221 | TDH_OUTTYPE_CIMDATETIME = 26 222 | TDH_OUTTYPE_ETWTIME = 27 223 | TDH_OUTTYPE_XML = 28 224 | TDH_OUTTYPE_ERRORCODE = 29 225 | TDH_OUTTYPE_WIN32ERROR = 30 226 | TDH_OUTTYPE_NTSTATUS = 31 227 | TDH_OUTTYPE_HRESULT = 32 228 | TDH_OUTTYPE_CULTURE_INSENSITIVE_DATETIME = 33 229 | TDH_OUTTYPE_JSON = 34 230 | TDH_OUTTYPE_REDUCEDSTRING = 300 231 | TDH_OUTTYPE_NOPRIN = 301 232 | 233 | # https://docs.microsoft.com/en-us/windows/desktop/api/winevt/ne-winevt-_evt_publisher_metadata_property_id 234 | EvtPublisherMetadataPublisherGuid = 0x0 235 | EvtPublisherMetadataResourceFilePath = 0x1 236 | EvtPublisherMetadataParameterFilePath = 0x2 237 | EvtPublisherMetadataMessageFilePath = 0x3 238 | EvtPublisherMetadataHelpLink = 0x4 239 | EvtPublisherMetadataPublisherMessageID = 0x5 240 | EvtPublisherMetadataChannelReferences = 0x6 241 | EvtPublisherMetadataChannelReferencePath = 0x7 242 | EvtPublisherMetadataChannelReferenceIndex = 0x8 243 | EvtPublisherMetadataChannelReferenceID = 0x9 244 | EvtPublisherMetadataChannelReferenceFlags = 0xa 245 | EvtPublisherMetadataChannelReferenceMessageID = 0xb 246 | EvtPublisherMetadataLevels = 0xc 247 | EvtPublisherMetadataLevelName = 0xd 248 | EvtPublisherMetadataLevelValue = 0xe 249 | EvtPublisherMetadataLevelMessageID = 0xf 250 | EvtPublisherMetadataTasks = 0x10 251 | EvtPublisherMetadataTaskName = 0x11 252 | EvtPublisherMetadataTaskEventGuid = 0x12 253 | EvtPublisherMetadataTaskValue = 0x13 254 | EvtPublisherMetadataTaskMessageID = 0x14 255 | EvtPublisherMetadataOpcodes = 0x15 256 | EvtPublisherMetadataOpcodeName = 0x16 257 | EvtPublisherMetadataOpcodeValue = 0x17 258 | EvtPublisherMetadataOpcodeMessageID = 0x18 259 | EvtPublisherMetadataKeywords = 0x19 260 | EvtPublisherMetadataKeywordName = 0x1a 261 | EvtPublisherMetadataKeywordValue = 0x1b 262 | EvtPublisherMetadataKeywordMessageID = 0x1c 263 | EvtPublisherMetadataPropertyIdEND = 0x1d 264 | 265 | # https://docs.microsoft.com/en-us/windows/desktop/api/winevt/ne-winevt-_evt_format_message_flags 266 | EvtFormatMessageEvent = 0x1 267 | EvtFormatMessageLevel = 0x2 268 | EvtFormatMessageTask = 0x3 269 | EvtFormatMessageOpcode = 0x4 270 | EvtFormatMessageKeyword = 0x5 271 | EvtFormatMessageChannel = 0x6 272 | EvtFormatMessageProvider = 0x7 273 | EvtFormatMessageId = 0x8 274 | EvtFormatMessageXml = 0x9 275 | 276 | -------------------------------------------------------------------------------- /winthingies/win32/guid.py: -------------------------------------------------------------------------------- 1 | import re 2 | import ctypes 3 | import struct 4 | from ctypes import Structure 5 | from ctypes.wintypes import * 6 | 7 | RE_GUID_STRING = re.compile( 8 | '{([0-9A-F]{8})-([0-9A-F]{4})-([0-9A-F]{4})-([0-9A-F]{2})([0-9A-F]{2})-' 9 | '([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})' 10 | '([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})}', 11 | re.I 12 | ) 13 | 14 | 15 | class GUID(Structure): 16 | _fields_ = [ 17 | ("Data1", DWORD), 18 | ("Data2", WORD), 19 | ("Data3", WORD), 20 | ("Data4", ctypes.c_ubyte * 8) 21 | ] 22 | 23 | @staticmethod 24 | def from_string(guid_str): 25 | guid = GUID() 26 | match = RE_GUID_STRING.match(guid_str) 27 | if not match: 28 | raise Exception( 29 | "Not the correct GUID string format: {}".format( 30 | guid_str 31 | ) 32 | ) 33 | 34 | g = [int(i, 16) for i in match.groups()] 35 | 36 | guid.Data1 = g[0] 37 | guid.Data2 = g[1] 38 | guid.Data3 = g[2] 39 | for i in range(8): 40 | guid.Data4[i] = g[3 + i] 41 | 42 | return guid 43 | 44 | def __str__(self): 45 | raw_buffer = ctypes.string_at( 46 | ctypes.byref(self), 47 | ctypes.sizeof(self) 48 | ) 49 | return "{:08X}-{:04X}-{:04X}-{:02X}{:02X}-{:02X}{:02X}{:02X}{:02X}{:02X}{:02X}".format( 50 | struct.unpack(" 0: 159 | for index in range(array_size._obj.value): 160 | info = {} 161 | 162 | message_id_property = get_property( 163 | array_handle, 164 | index, 165 | EvtPublisherMetadataChannelReferenceMessageID 166 | ) 167 | info['message'] = "" 168 | message_id = message_id_property._VARIANT_VALUE.Int32Val 169 | if message_id != -1: 170 | # We have a description 171 | message_str = get_message( 172 | metadata_handle, 173 | message_id 174 | ) 175 | if message_str: 176 | info['message'] = message_str 177 | 178 | path_property = get_property( 179 | array_handle, 180 | index, 181 | EvtPublisherMetadataChannelReferencePath 182 | ) 183 | name = path_property._VARIANT_VALUE.StringVal 184 | info['path'] = name 185 | 186 | index_property = get_property( 187 | array_handle, 188 | index, 189 | EvtPublisherMetadataChannelReferenceIndex 190 | ) 191 | index = index_property._VARIANT_VALUE.UInt32Val 192 | info['index'] = index 193 | 194 | id_property = get_property( 195 | array_handle, 196 | index, 197 | EvtPublisherMetadataChannelReferenceID 198 | ) 199 | info['id'] = None 200 | if id_property is not None: 201 | info['id'] = id_property._VARIANT_VALUE.UInt32Val 202 | 203 | flags_property = get_property( 204 | array_handle, 205 | index, 206 | EvtPublisherMetadataChannelReferenceFlags 207 | ) 208 | info['flags'] = None 209 | if flags_property is not None: 210 | info['flags'] = flags_property._VARIANT_VALUE.UInt32Val 211 | 212 | channel_map[index] = info 213 | 214 | wevtapi.EvtClose( 215 | array_handle 216 | ) 217 | 218 | return channel_map 219 | 220 | 221 | def get_level_mapping(metadata_handle): 222 | """Get a dictionary of level info. 223 | 224 | :param metadata_handle: (EVT_HANDLE) The handle returned by EvtOpenPublisherMetadata 225 | :return: 226 | """ 227 | level_map = {} 228 | meta_prop_variant = EvtGetPublisherMetadataProperty( 229 | metadata_handle, 230 | EvtPublisherMetadataLevels 231 | ) 232 | 233 | if meta_prop_variant is None: 234 | return 235 | 236 | array_handle = meta_prop_variant._VARIANT_VALUE.EvtHandleVal 237 | array_size = byref(DWORD()) 238 | 239 | wevtapi.EvtGetObjectArraySize( 240 | array_handle, 241 | array_size 242 | ) 243 | if array_size._obj.value > 0: 244 | for index in range(array_size._obj.value): 245 | info = {} 246 | 247 | message_id_property = get_property( 248 | array_handle, 249 | index, 250 | EvtPublisherMetadataLevelMessageID 251 | ) 252 | info['message'] = "" 253 | message_id = message_id_property._VARIANT_VALUE.Int32Val 254 | if message_id != -1: 255 | # We have a description 256 | message_str = get_message( 257 | metadata_handle, 258 | message_id 259 | ) 260 | if message_str: 261 | info['message'] = message_str 262 | 263 | name_property = get_property( 264 | array_handle, 265 | index, 266 | EvtPublisherMetadataLevelName 267 | ) 268 | name = name_property._VARIANT_VALUE.StringVal 269 | info['name'] = name 270 | 271 | value_property = get_property( 272 | array_handle, 273 | index, 274 | EvtPublisherMetadataLevelValue 275 | ) 276 | value = value_property._VARIANT_VALUE.UInt64Val 277 | info['value'] = value 278 | 279 | level_map[value] = info 280 | 281 | wevtapi.EvtClose( 282 | array_handle 283 | ) 284 | 285 | return level_map 286 | 287 | 288 | def get_task_mapping(metadata_handle): 289 | """Get a dictionary of task info. 290 | 291 | :param metadata_handle: (EVT_HANDLE) The handle returned by EvtOpenPublisherMetadata 292 | :return: 293 | """ 294 | task_map = {} 295 | meta_prop_variant = EvtGetPublisherMetadataProperty( 296 | metadata_handle, 297 | EvtPublisherMetadataTasks 298 | ) 299 | 300 | if meta_prop_variant is None: 301 | return 302 | 303 | array_handle = meta_prop_variant._VARIANT_VALUE.EvtHandleVal 304 | array_size = byref(DWORD()) 305 | 306 | wevtapi.EvtGetObjectArraySize( 307 | array_handle, 308 | array_size 309 | ) 310 | 311 | if array_size._obj.value > 0: 312 | for index in range(array_size._obj.value): 313 | info = {} 314 | 315 | message_id_property = get_property( 316 | array_handle, 317 | index, 318 | EvtPublisherMetadataTaskMessageID 319 | ) 320 | info['message'] = "" 321 | message_id = message_id_property._VARIANT_VALUE.Int32Val 322 | if message_id != -1: 323 | # We have a description 324 | message_str = get_message( 325 | metadata_handle, 326 | message_id 327 | ) 328 | if message_str: 329 | info['message'] = message_str 330 | 331 | name_property = get_property( 332 | array_handle, 333 | index, 334 | EvtPublisherMetadataTaskName 335 | ) 336 | opcode_name = name_property._VARIANT_VALUE.StringVal 337 | info['name'] = opcode_name 338 | 339 | guid_property = get_property( 340 | array_handle, 341 | index, 342 | EvtPublisherMetadataTaskEventGuid 343 | ) 344 | task_guid = guid_property._VARIANT_VALUE.GuidVal 345 | info['guid'] = str(task_guid) 346 | 347 | value_property = get_property( 348 | array_handle, 349 | index, 350 | EvtPublisherMetadataTaskValue 351 | ) 352 | task_value = value_property._VARIANT_VALUE.UInt64Val 353 | info['value'] = task_value 354 | 355 | task_map[task_value] = info 356 | 357 | wevtapi.EvtClose( 358 | array_handle 359 | ) 360 | 361 | return task_map 362 | 363 | 364 | def get_opcode_mapping(metadata_handle): 365 | """Get a dictionary of operation info. 366 | 367 | :param metadata_handle: (EVT_HANDLE) The handle returned by EvtOpenPublisherMetadata 368 | :return: 369 | """ 370 | opcode_map = {} 371 | meta_prop_variant = EvtGetPublisherMetadataProperty( 372 | metadata_handle, 373 | EvtPublisherMetadataOpcodes 374 | ) 375 | 376 | if meta_prop_variant is None: 377 | return 378 | 379 | array_handle = meta_prop_variant._VARIANT_VALUE.EvtHandleVal 380 | array_size = byref(DWORD()) 381 | 382 | wevtapi.EvtGetObjectArraySize( 383 | array_handle, 384 | array_size 385 | ) 386 | 387 | if array_size._obj.value > 0: 388 | for index in range(array_size._obj.value): 389 | info = {} 390 | 391 | message_id_property = get_property( 392 | array_handle, 393 | index, 394 | EvtPublisherMetadataOpcodeMessageID 395 | ) 396 | info['message'] = "" 397 | message_id = message_id_property._VARIANT_VALUE.Int32Val 398 | if message_id != -1: 399 | # We have a description 400 | message_str = get_message( 401 | metadata_handle, 402 | message_id 403 | ) 404 | if message_str: 405 | info['message'] = message_str 406 | 407 | name_property = get_property( 408 | array_handle, 409 | index, 410 | EvtPublisherMetadataOpcodeName 411 | ) 412 | opcode_name = name_property._VARIANT_VALUE.StringVal 413 | info['name'] = opcode_name 414 | 415 | mask_property = get_property( 416 | array_handle, 417 | index, 418 | EvtPublisherMetadataOpcodeValue 419 | ) 420 | opcode_value = mask_property._VARIANT_VALUE.UInt64Val 421 | info['value'] = opcode_value 422 | 423 | opcode_map[opcode_value] = info 424 | 425 | wevtapi.EvtClose( 426 | array_handle 427 | ) 428 | 429 | return opcode_map 430 | 431 | 432 | def get_keyword_mapping(metadata_handle): 433 | """Get a dictionary of keyword info. 434 | 435 | :param metadata_handle: (EVT_HANDLE) The handle returned by EvtOpenPublisherMetadata 436 | :return: (dict) 437 | """ 438 | keyword_map = {} 439 | meta_prop_variant = EvtGetPublisherMetadataProperty( 440 | metadata_handle, 441 | EvtPublisherMetadataKeywords 442 | ) 443 | 444 | if meta_prop_variant is None: 445 | return 446 | 447 | array_handle = meta_prop_variant._VARIANT_VALUE.EvtHandleVal 448 | array_size = byref(DWORD()) 449 | 450 | wevtapi.EvtGetObjectArraySize( 451 | array_handle, 452 | array_size 453 | ) 454 | 455 | if array_size._obj.value > 0: 456 | for index in range(array_size._obj.value): 457 | info = {} 458 | 459 | name_property = get_property( 460 | array_handle, 461 | index, 462 | EvtPublisherMetadataKeywordName 463 | ) 464 | info['name'] = name_property._VARIANT_VALUE.StringVal 465 | 466 | desc_property = get_property( 467 | array_handle, 468 | index, 469 | EvtPublisherMetadataKeywordMessageID 470 | ) 471 | info['message'] = "" 472 | message_id = desc_property._VARIANT_VALUE.Int32Val 473 | if message_id != -1: 474 | # We have a description 475 | message_str = get_message( 476 | metadata_handle, 477 | message_id 478 | ) 479 | if message_str: 480 | info['message'] = message_str 481 | 482 | mask_property = get_property( 483 | array_handle, 484 | index, 485 | EvtPublisherMetadataKeywordValue 486 | ) 487 | info['value'] = mask_property._VARIANT_VALUE.UInt64Val 488 | 489 | keyword_map[info['value']] = info 490 | 491 | wevtapi.EvtClose( 492 | array_handle 493 | ) 494 | 495 | return keyword_map 496 | 497 | 498 | def get_message(metadata_handle, message_id): 499 | """Get a message given the message id. 500 | 501 | :param metadata_handle: (EVT_HANDLE) 502 | :param message_id: (DWORD) 503 | :return: 504 | """ 505 | buffer_size = 0 506 | variant = None 507 | buffer_used = byref(DWORD()) 508 | 509 | result = wevtapi.EvtFormatMessage( 510 | metadata_handle, 511 | None, 512 | message_id, 513 | 0, 514 | None, 515 | EvtFormatMessageId, 516 | buffer_size, 517 | variant, 518 | buffer_used 519 | ) 520 | 521 | if result == 0: 522 | # Check our last error 523 | status = kernel32.GetLastError() 524 | 525 | if status == ERROR_INSUFFICIENT_BUFFER: 526 | buffer_size = buffer_used._obj.value 527 | c_buffer = ctypes.c_buffer(buffer_size*2) 528 | unicode_buffer = ctypes.cast(c_buffer, LPWSTR) 529 | 530 | result = wevtapi.EvtFormatMessage( 531 | metadata_handle, 532 | None, 533 | message_id, 534 | 0, 535 | None, 536 | EvtFormatMessageId, 537 | buffer_size, 538 | unicode_buffer, 539 | buffer_used 540 | ) 541 | 542 | return unicode_buffer.value 543 | else: 544 | err_no = kernel32.GetLastError() 545 | logging.error( 546 | str(WindowsError( 547 | err_no, ctypes.FormatError(err_no) 548 | )) 549 | ) 550 | 551 | 552 | def get_property(evt_handle, index, property_id): 553 | """Get the metadata property for an object in the array. 554 | 555 | :param evt_handle: (EVT_HANDLE) 556 | :param index: (DWORD) 557 | :param property_id: (EVT_PUBLISHER_METADATA_PROPERTY_ID) 558 | :return: (EVT_VARIANT|None) 559 | """ 560 | # The first thing we need to do is find out how large our variant buffer will be 561 | # to do this, we set our variant to Null, the result will be 0, and set an error. 562 | buffer_size = 0 563 | variant = None 564 | buffer_used = byref(DWORD()) 565 | result = wevtapi.EvtGetObjectArrayProperty( 566 | evt_handle, 567 | property_id, 568 | index, 569 | 0, 570 | buffer_size, 571 | variant, 572 | buffer_used 573 | ) 574 | 575 | if result == 0: 576 | # Check our last error 577 | status = kernel32.GetLastError() 578 | 579 | if status == ERROR_INSUFFICIENT_BUFFER: 580 | # If the error is ERROR_INSUFFICIENT_BUFFER, 581 | # we can now determine the buffer size needed. 582 | buffer_size = buffer_used._obj.value 583 | variant_buffer = ctypes.create_string_buffer( 584 | buffer_size 585 | ) 586 | variant = EVT_VARIANT.from_buffer( 587 | variant_buffer 588 | ) 589 | result = wevtapi.EvtGetObjectArrayProperty( 590 | evt_handle, 591 | property_id, 592 | index, 593 | 0, 594 | buffer_size, 595 | variant, 596 | buffer_used 597 | ) 598 | 599 | return variant 600 | else: 601 | err_no = kernel32.GetLastError() 602 | logging.error( 603 | str(WindowsError( 604 | err_no, ctypes.FormatError(err_no) 605 | )) 606 | ) 607 | -------------------------------------------------------------------------------- /winthingies/win32/winstructs.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import ctypes 3 | from ctypes import * 4 | from ctypes.wintypes import * 5 | from winthingies.win32.const import * 6 | from winthingies.win32.guid import GUID 7 | from winthingies.win32.helpers import get_formatted_value, rel_ptr_to_str 8 | 9 | LOGGER = logging.getLogger(__name__) 10 | 11 | PWSTR = LPWSTR 12 | PVOID = c_void_p 13 | ACCESS_MASK = DWORD 14 | ULONG_PTR = PVOID 15 | TRACEHANDLE = c_uint64 16 | UCHAR = c_ubyte 17 | ULONG64 = c_uint64 18 | ULONGLONG = c_ulonglong 19 | LONGLONG = c_longlong 20 | PWCHAR = c_wchar_p 21 | DECODING_SOURCE = ctypes.c_uint 22 | PROPERTY_FLAGS = ctypes.c_uint 23 | TDH_CONTEXT_TYPE = ctypes.c_uint 24 | MAP_FLAGS = ctypes.c_uint 25 | EVT_HANDLE = HANDLE 26 | INT8 = c_byte 27 | INT16 = SHORT 28 | INT32 = INT 29 | INT64 = LONGLONG 30 | UINT8 = BYTE 31 | UINT16 = USHORT 32 | UINT32 = UINT 33 | UINT64 = ULONGLONG 34 | PSID = PVOID 35 | SIZE_T = c_size_t 36 | EVT_OBJECT_ARRAY_PROPERTY_HANDLE = HANDLE 37 | 38 | 39 | class FILETIME(Structure): 40 | _fields_ = [ 41 | ('dwLowDateTime', DWORD), 42 | ('dwHighDateTime', DWORD) 43 | ] 44 | 45 | 46 | class SYSTEMTIME(Structure): 47 | _fields_ = [ 48 | ('wYear', WORD), 49 | ('wMonth', WORD), 50 | ('wDayOfWeek', WORD), 51 | ('wDay', WORD), 52 | ('wHour', WORD), 53 | ('wMinute', WORD), 54 | ('wSecond', WORD), 55 | ('wMilliseconds', WORD) 56 | ] 57 | 58 | 59 | class TIME_ZONE_INFORMATION(Structure): 60 | _fields_ = [ 61 | ('Bias', LONG), 62 | ('StandardName', WCHAR * 32), 63 | ('StandardDate', SYSTEMTIME), 64 | ('StandardBias', LONG), 65 | ('DaylightName', WCHAR * 32), 66 | ('DaylightDate', SYSTEMTIME), 67 | ('DaylightBias', LONG) 68 | ] 69 | 70 | 71 | class _SYSTEM_HANDLE(Structure): 72 | _fields_ = [ 73 | ("dwProcessId", DWORD), 74 | ("bObjectType", BYTE), 75 | ("bFlags", BYTE), 76 | ("wValue", WORD), 77 | ("pAddress", PVOID), 78 | ("GrantedAccess", DWORD), 79 | ] 80 | SYSTEM_HANDLE = _SYSTEM_HANDLE 81 | 82 | 83 | class _SYSTEM_HANDLE_INFORMATION(Structure): 84 | _fields_ = [ 85 | ("HandleCount", ULONG), 86 | ("Handles", SYSTEM_HANDLE * 1), 87 | ] 88 | PSYSTEM_HANDLE_INFORMATION = POINTER(_SYSTEM_HANDLE_INFORMATION) 89 | SYSTEM_HANDLE_INFORMATION = _SYSTEM_HANDLE_INFORMATION 90 | 91 | 92 | class SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX(Structure): 93 | _fields_ = [ 94 | ("Object", PVOID), 95 | ("UniqueProcessId", PVOID), 96 | ("HandleValue", PVOID), 97 | ("GrantedAccess", ULONG), 98 | ("CreatorBackTraceIndex", USHORT), 99 | ("ObjectTypeIndex", USHORT), 100 | ("HandleAttributes", ULONG), 101 | ("Reserved", ULONG), 102 | ] 103 | 104 | 105 | class SYSTEM_HANDLE_INFORMATION_EX(Structure): 106 | _fields_ = [ 107 | ("NumberOfHandles", PVOID), 108 | ("Reserved", PVOID), 109 | ("Handles", SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX * 1), 110 | ] 111 | 112 | 113 | class _LSA_UNICODE_STRING(Structure): 114 | _fields_ = [ 115 | ("Length", USHORT), 116 | ("MaximumLength", USHORT), 117 | ("Buffer", PVOID), 118 | ] 119 | PUNICODE_STRING = POINTER(_LSA_UNICODE_STRING) 120 | UNICODE_STRING = _LSA_UNICODE_STRING 121 | LSA_UNICODE_STRING = _LSA_UNICODE_STRING 122 | PLSA_UNICODE_STRING = POINTER(_LSA_UNICODE_STRING) 123 | 124 | INITIAL_LSA_UNICODE_STRING = _LSA_UNICODE_STRING 125 | 126 | class _LSA_UNICODE_STRING(INITIAL_LSA_UNICODE_STRING): 127 | @property 128 | def str(self): 129 | """Special thanks to 130 | https://github.com/hakril/PythonForWindows/blob/master/windows/generated_def/winstructs.py#L4034 131 | 132 | :type: :class:`unicode` 133 | """ 134 | if not self.Length: 135 | return "" 136 | if getattr(self, "_target", None) is not None: 137 | raw_data = self._target.read_memory(self.Buffer, self.Length) 138 | return raw_data.decode("utf16") 139 | size = self.Length // 2 140 | return (ctypes.c_wchar * size).from_address(self.Buffer)[:] 141 | 142 | def __repr__(self): 143 | return """<{0} "{1}" at {2}>""".format( 144 | type(self).__name__, self.str, hex(id(self)) 145 | ) 146 | 147 | def __sprint__(self): 148 | try: 149 | return self.__repr__() 150 | except TypeError as e: 151 | # Bad buffer: print raw infos 152 | return """<{0} len={1} maxlen={2} buffer={3}>""".format( 153 | type(self).__name__, self.Length, self.MaximumLength, self.Buffer 154 | ) 155 | 156 | PUNICODE_STRING = POINTER(_LSA_UNICODE_STRING) 157 | UNICODE_STRING = _LSA_UNICODE_STRING 158 | LSA_UNICODE_STRING = _LSA_UNICODE_STRING 159 | PLSA_UNICODE_STRING = POINTER(_LSA_UNICODE_STRING) 160 | 161 | 162 | class __PUBLIC_OBJECT_TYPE_INFORMATION(Structure): 163 | _fields_ = [ 164 | ("TypeName", UNICODE_STRING), 165 | ("Reserved", ULONG * 22), 166 | ] 167 | PPUBLIC_OBJECT_TYPE_INFORMATION = POINTER(__PUBLIC_OBJECT_TYPE_INFORMATION) 168 | PUBLIC_OBJECT_TYPE_INFORMATION = __PUBLIC_OBJECT_TYPE_INFORMATION 169 | 170 | 171 | class _PUBLIC_OBJECT_BASIC_INFORMATION(Structure): 172 | _fields_ = [ 173 | ("Attributes", ULONG), 174 | ("GrantedAccess", ACCESS_MASK), 175 | ("HandleCount", ULONG), 176 | ("PointerCount", ULONG), 177 | ("Reserved", ULONG * 10), 178 | ] 179 | PUBLIC_OBJECT_BASIC_INFORMATION = _PUBLIC_OBJECT_BASIC_INFORMATION 180 | PPUBLIC_OBJECT_BASIC_INFORMATION = POINTER(_PUBLIC_OBJECT_BASIC_INFORMATION) 181 | 182 | 183 | class PROCESSENTRY32(Structure): 184 | _fields_ = [ 185 | ("dwSize", DWORD), 186 | ("cntUsage", DWORD), 187 | ("th32ProcessID", DWORD), 188 | ("th32DefaultHeapID", ULONG_PTR), 189 | ("th32ModuleID", DWORD), 190 | ("cntThreads", DWORD), 191 | ("th32ParentProcessID", DWORD), 192 | ("pcPriClassBase", LONG), 193 | ("dwFlags", DWORD), 194 | ("szExeFile", CHAR * MAX_PATH), 195 | ] 196 | 197 | def clone(self): 198 | return PROCESSENTRY32( 199 | self.dwSize, 200 | self.cntUsage, 201 | self.th32ProcessID, 202 | self.th32DefaultHeapID, 203 | self.th32ModuleID, 204 | self.cntThreads, 205 | self.th32ParentProcessID, 206 | self.pcPriClassBase, 207 | self.dwFlags, 208 | self.szExeFile 209 | ) 210 | 211 | def as_dict(self): 212 | return { 213 | "dwSize": self.dwSize, 214 | "cntUsage": self.cntUsage, 215 | "th32ProcessID": self.th32ProcessID, 216 | "th32DefaultHeapID": self.th32DefaultHeapID, 217 | "th32ModuleID": self.th32ModuleID, 218 | "cntThreads": self.cntThreads, 219 | "th32ParentProcessID": self.th32ParentProcessID, 220 | "pcPriClassBase": self.pcPriClassBase, 221 | "dwFlags": self.dwFlags, 222 | "szExeFile": self.szExeFile.decode("utf-8") 223 | } 224 | 225 | 226 | ######################################################################### 227 | # Event Structs 228 | ######################################################################### 229 | class EVENT_DESCRIPTOR(Structure): 230 | _fields_ = [ 231 | ('Id', USHORT), 232 | ('Version', UCHAR), 233 | ('Channel', UCHAR), 234 | ('Level', UCHAR), 235 | ('Opcode', UCHAR), 236 | ('Task', USHORT), 237 | ('Keyword', ULONGLONG) 238 | ] 239 | 240 | def as_dict(self): 241 | return { 242 | "Id": self.Id, 243 | "Version": self.Version, 244 | "Channel": self.Channel, 245 | "Level": self.Level, 246 | "Opcode": self.Opcode, 247 | "Task": self.Task, 248 | "Keyword": self.Keyword, 249 | } 250 | 251 | 252 | class EVENT_HEADER(Structure): 253 | _fields_ = [ 254 | ('Size', USHORT), 255 | ('HeaderType', USHORT), 256 | ('Flags', USHORT), 257 | ('EventProperty', USHORT), 258 | ('ThreadId', ULONG), 259 | ('ProcessId', ULONG), 260 | ('TimeStamp', LARGE_INTEGER), 261 | ('ProviderId', GUID), 262 | ('EventDescriptor', EVENT_DESCRIPTOR), 263 | ('KernelTime', ULONG), 264 | ('UserTime', ULONG), 265 | ('ActivityId', GUID) 266 | ] 267 | 268 | def as_dict(self): 269 | return { 270 | "Size": self.Size, 271 | "HeaderType": self.HeaderType, 272 | "Flags": self.Flags, 273 | "EventProperty": self.EventProperty, 274 | "ThreadId": self.ThreadId, 275 | "ProcessId": self.ProcessId, 276 | "TimeStamp": self.TimeStamp, 277 | "ProviderId": str(self.ProviderId), 278 | "EventDescriptor": self.EventDescriptor.as_dict(), 279 | "KernelTime": self.KernelTime, 280 | "UserTime": self.UserTime, 281 | "ActivityId": str(self.ActivityId) 282 | } 283 | 284 | 285 | class ETW_BUFFER_CONTEXT(Structure): 286 | _fields_ = [ 287 | ('ProcessorNumber', UCHAR), 288 | ('Alignment', UCHAR), 289 | ('LoggerId', USHORT) 290 | ] 291 | 292 | 293 | class EVENT_HEADER_EXTENDED_DATA_ITEM(Structure): 294 | _fields_ = [ 295 | ('Reserved1', USHORT), 296 | ('ExtType', USHORT), 297 | ('Linkage', USHORT), 298 | ('DataSize', USHORT), 299 | ('DataPtr', ULONGLONG) 300 | ] 301 | 302 | 303 | class EVENT_RECORD(Structure): 304 | _fields_ = [ 305 | ('EventHeader', EVENT_HEADER), 306 | ('BufferContext', ETW_BUFFER_CONTEXT), 307 | ('ExtendedDataCount', USHORT), 308 | ('UserDataLength', USHORT), 309 | ('ExtendedData', POINTER(EVENT_HEADER_EXTENDED_DATA_ITEM)), 310 | ('UserData', PVOID), 311 | ('UserContext', PVOID) 312 | ] 313 | 314 | @property 315 | def TraceEventInfo(self): 316 | trace_event_info = ctypes.POINTER( 317 | TRACE_EVENT_INFO 318 | )() 319 | buffer_size = DWORD() 320 | 321 | # Call TdhGetEventInformation once to get the required buffer size and again to actually populate the structure. 322 | status = tdh.TdhGetEventInformation( 323 | self, 324 | 0, 325 | None, 326 | None, 327 | ctypes.byref(buffer_size) 328 | ) 329 | if ERROR_INSUFFICIENT_BUFFER == status: 330 | trace_event_info = ctypes.cast( 331 | (ctypes.c_byte * buffer_size.value)(), 332 | ctypes.POINTER(TRACE_EVENT_INFO) 333 | ) 334 | status = tdh.TdhGetEventInformation( 335 | self, 336 | 0, 337 | None, 338 | trace_event_info, 339 | ctypes.byref(buffer_size) 340 | ) 341 | 342 | if ERROR_SUCCESS != status: 343 | raise ctypes.WinError(status) 344 | 345 | return trace_event_info.contents 346 | 347 | @property 348 | def Properties(self): 349 | trace_event_info = self.TraceEventInfo 350 | for event_property_info in trace_event_info.iter_properties(): 351 | prop_name = event_property_info.get_property_name( 352 | trace_event_info 353 | ) 354 | 355 | prop_data = event_property_info.get_property_data( 356 | self, 357 | trace_event_info 358 | ) 359 | 360 | yield prop_name, prop_data 361 | 362 | def as_dict(self): 363 | info = { 364 | "EventHeader": self.EventHeader.as_dict(), 365 | "EventInfo": self.TraceEventInfo.as_dict(), 366 | "Properties": dict(self.Properties) 367 | } 368 | return info 369 | 370 | # https://docs.microsoft.com/en-us/windows/desktop/api/evntrace/ns-evntrace-event_trace_header 371 | # Class 372 | class EVENT_TRACE_HEADER_CLASS(Structure): 373 | _fields_ = [ 374 | ('Type', UCHAR), 375 | ('Level', UCHAR), 376 | ('Version', USHORT) 377 | ] 378 | 379 | 380 | class EVENT_TRACE_HEADER(Structure): 381 | _fields_ = [ 382 | ('Size', USHORT), 383 | ('HeaderType', UCHAR), 384 | ('MarkerFlags', UCHAR), 385 | ('Class', EVENT_TRACE_HEADER_CLASS), 386 | ('ThreadId', ULONG), 387 | ('ProcessId', ULONG), 388 | ('TimeStamp', LARGE_INTEGER), 389 | ('Guid', GUID), 390 | ('ClientContext', ULONG), 391 | ('Flags', ULONG) 392 | ] 393 | 394 | 395 | class EVENT_TRACE(Structure): 396 | _fields_ = [ 397 | ('Header', EVENT_TRACE_HEADER), 398 | ('InstanceId', ULONG), 399 | ('ParentInstanceId', ULONG), 400 | ('ParentGuid', GUID), 401 | ('MofData', PVOID), 402 | ('MofLength', ULONG), 403 | ('ClientContext', ULONG) 404 | ] 405 | 406 | 407 | class TRACE_LOGFILE_HEADER(Structure): 408 | _fields_ = [ 409 | ('BufferSize', ULONG), 410 | ('MajorVersion', BYTE), 411 | ('MinorVersion', BYTE), 412 | ('SubVersion', BYTE), 413 | ('SubMinorVersion', BYTE), 414 | ('ProviderVersion', ULONG), 415 | ('NumberOfProcessors', ULONG), 416 | ('EndTime', LARGE_INTEGER), 417 | ('TimerResolution', ULONG), 418 | ('MaximumFileSize', ULONG), 419 | ('LogFileMode', ULONG), 420 | ('BuffersWritten', ULONG), 421 | ('StartBuffers', ULONG), 422 | ('PointerSize', ULONG), 423 | ('EventsLost', ULONG), 424 | ('CpuSpeedInMHz', ULONG), 425 | ('LoggerName', PWCHAR), 426 | ('LogFileName', PWCHAR), 427 | ('TimeZone', TIME_ZONE_INFORMATION), 428 | ('BootTime', LARGE_INTEGER), 429 | ('PerfFreq', LARGE_INTEGER), 430 | ('StartTime', LARGE_INTEGER), 431 | ('ReservedFlags', ULONG), 432 | ('BuffersLost', ULONG) 433 | ] 434 | 435 | 436 | ######################################################################### 437 | # ETW Structs 438 | ######################################################################### 439 | class WNODE_HEADER(Structure): 440 | _fields_ = [ 441 | ('BufferSize', ULONG), 442 | ('ProviderId', ULONG), 443 | ('HistoricalContext', ULONG64), 444 | ('Timestamp', LARGE_INTEGER), 445 | ('Guid', GUID), 446 | ('ClientContext', ULONG), 447 | ('Flags', ULONG) 448 | ] 449 | 450 | 451 | class EVENT_TRACE_PROPERTIES(Structure): 452 | _fields_ = [ 453 | ('Wnode', WNODE_HEADER), 454 | ('BufferSize', ULONG), 455 | ('MinimumBuffers', ULONG), 456 | ('MaximumBuffers', ULONG), 457 | ('MaximumFileSize', ULONG), 458 | ('LogFileMode', ULONG), 459 | ('FlushTimer', ULONG), 460 | ('EnableFlags', ULONG), 461 | ('AgeLimit', LONG), 462 | ('NumberOfBuffers', ULONG), 463 | ('FreeBuffers', ULONG), 464 | ('EventsLost', ULONG), 465 | ('BuffersWritten', ULONG), 466 | ('LogBuffersLost', ULONG), 467 | ('RealTimeBufferLost', ULONG), 468 | ('LoggerThreadId', HANDLE), 469 | ('LogFileNameOffset', ULONG), 470 | ('LoggerNameOffset', ULONG) 471 | ] 472 | 473 | 474 | # This must be "forward declared", because of the callback type below, 475 | # which is contained in the ct.Structure. 476 | class EVENT_TRACE_LOGFILE(Structure): 477 | pass 478 | 479 | 480 | # The type for event trace callbacks. 481 | EVENT_RECORD_CALLBACK = WINFUNCTYPE( 482 | None, 483 | POINTER(EVENT_RECORD) 484 | ) 485 | EVENT_TRACE_BUFFER_CALLBACK = WINFUNCTYPE( 486 | ULONG, 487 | POINTER(EVENT_TRACE_LOGFILE) 488 | ) 489 | EVENT_TRACE_LOGFILE._fields_ = [ 490 | ('LogFileName', PWCHAR), 491 | ('LoggerName', PWCHAR), 492 | ('CurrentTime', LONGLONG), 493 | ('BuffersRead', ULONG), 494 | ('ProcessTraceMode', ULONG), 495 | ('CurrentEvent', EVENT_TRACE), 496 | ('LogfileHeader', TRACE_LOGFILE_HEADER), 497 | ('BufferCallback', EVENT_TRACE_BUFFER_CALLBACK), 498 | ('BufferSize', ULONG), 499 | ('Filled', ULONG), 500 | ('EventsLost', ULONG), 501 | ('EventRecordCallback', EVENT_RECORD_CALLBACK), 502 | ('IsKernelTrace', ULONG), 503 | ('Context', PVOID) 504 | ] 505 | 506 | 507 | class EVENT_FILTER_DESCRIPTOR(Structure): 508 | _fields_ = [ 509 | ('Ptr', ULONGLONG), 510 | ('Size', ULONG), 511 | ('Type', ULONG) 512 | ] 513 | 514 | 515 | class ENABLE_TRACE_PARAMETERS(Structure): 516 | _fields_ = [ 517 | ('Version', ULONG), 518 | ('EnableProperty', ULONG), 519 | ('ControlFlags', ULONG), 520 | ('SourceId', GUID), 521 | ('EnableFilterDesc', POINTER(EVENT_FILTER_DESCRIPTOR)), 522 | ('FilterDescCount', ULONG) 523 | ] 524 | 525 | 526 | # https://docs.microsoft.com/en-us/windows/desktop/api/tdh/ns-tdh-_event_property_info 527 | class nonStructType(Structure): 528 | _fields_ = [ 529 | ('InType', USHORT), 530 | ('OutType', USHORT), 531 | ('MapNameOffset', ULONG) 532 | ] 533 | 534 | 535 | class structType(Structure): 536 | _fields_ = [ 537 | ('StructStartIndex', USHORT), 538 | ('NumOfStructMembers', USHORT), 539 | ('padding', ULONG) 540 | ] 541 | 542 | 543 | class customSchemaType(Structure): 544 | _fields_ = [ 545 | ('padding2', USHORT), 546 | ('OutType', USHORT), 547 | ('CustomSchemaOffset', ULONG) 548 | ] 549 | 550 | 551 | class EpiU1(Union): 552 | _fields_ = [ 553 | ("nonStructType", nonStructType), 554 | ("structType", structType), 555 | ("customSchemaType", customSchemaType) 556 | ] 557 | 558 | 559 | class EpiU2(Union): 560 | _fields_ = [ 561 | ("count", USHORT), 562 | ("countPropertyIndex", USHORT) 563 | ] 564 | 565 | 566 | class EpiU3(Union): 567 | _fields_ = [ 568 | ("length", USHORT), 569 | ("lengthPropertyIndex", USHORT) 570 | ] 571 | 572 | 573 | class EpiU4(Union): 574 | _fields_ = [ 575 | ("Reserved", ULONG), 576 | ("Tags", ULONG) 577 | ] 578 | 579 | 580 | class EVENT_PROPERTY_INFO(Structure): 581 | _fields_ = [ 582 | ('Flags', PROPERTY_FLAGS), 583 | ('NameOffset', ULONG), 584 | ('epi_u1', EpiU1), 585 | ('epi_u2', EpiU2), 586 | ('epi_u3', EpiU3), 587 | ('epi_u4', EpiU4) 588 | ] 589 | 590 | def get_event_map_info(self, event_record, event_info): 591 | """ 592 | When parsing a field in the event property structure, there may be a mapping between a given 593 | name and the structure it represents. If it exists, we retrieve that mapping here. 594 | 595 | Because this may legitimately return a NULL value we return a tuple containing the success or 596 | failure status as well as either None (NULL) or an EVENT_MAP_INFO pointer. 597 | 598 | :param self: The EVENT_PROPERTY_INFO structure for the TopLevelProperty of the event we are parsing 599 | :param event_record: The EventRecord structure for the event we are parsing 600 | :param event_info: The TraceEventInfo structure for the event we are parsing 601 | :return: A tuple of the map_info structure and boolean indicating whether we succeeded or not 602 | """ 603 | map_name = rel_ptr_to_str( 604 | pointer(event_info), 605 | self.epi_u1.nonStructType.MapNameOffset 606 | ) 607 | map_size = DWORD() 608 | map_info = ctypes.POINTER(EVENT_MAP_INFO)() 609 | 610 | status = tdh.TdhGetEventMapInformation( 611 | event_record, 612 | map_name, 613 | None, 614 | ctypes.byref(map_size) 615 | ) 616 | if ERROR_INSUFFICIENT_BUFFER == status: 617 | map_info = ctypes.cast( 618 | (ctypes.c_char * map_size.value)(), 619 | ctypes.POINTER(EVENT_MAP_INFO) 620 | ) 621 | status = tdh.TdhGetEventMapInformation( 622 | event_record, 623 | map_name, 624 | map_info, 625 | ctypes.byref(map_size) 626 | ) 627 | 628 | if ERROR_SUCCESS == status: 629 | return map_info, True 630 | 631 | # ERROR_NOT_FOUND is actually a perfectly acceptable status 632 | if ERROR_NOT_FOUND == status: 633 | return None, True 634 | 635 | # We actually failed. 636 | raise ctypes.WinError() 637 | 638 | def get_property_name(self, event_info): 639 | """Get the name for a property. 640 | 641 | :param trace_event_info: TRACE_EVENT_INFO 642 | :return: (str) 643 | """ 644 | name_field = rel_ptr_to_str( 645 | pointer(event_info), 646 | self.NameOffset 647 | ) 648 | return name_field 649 | 650 | def get_property_data(self, event_record, event_info): 651 | if self.Flags & PropertyStruct: 652 | LOGGER.info( 653 | "EVENT_PROPERTY_INFO. Flag of type PropertyStruct is not yet supported." 654 | ) 655 | else: 656 | # The format of the property data 657 | in_type = self.epi_u1.nonStructType.InType 658 | 659 | # Create PROPERTY_DATA_DESCRIPTOR 660 | data_descriptor = PROPERTY_DATA_DESCRIPTOR() 661 | 662 | data_descriptor.PropertyName = ( 663 | ctypes.cast(pointer(event_info), ctypes.c_voidp).value + 664 | self.NameOffset 665 | ) 666 | data_descriptor.ArrayIndex = MAX_UINT 667 | 668 | property_data_length = DWORD() 669 | 670 | status = tdh.TdhGetPropertySize( 671 | pointer(event_record), 672 | 0, 673 | None, 674 | 1, 675 | ctypes.byref(data_descriptor), 676 | ctypes.byref(property_data_length) 677 | ) 678 | if status != ERROR_SUCCESS: 679 | raise WindowsError( 680 | status, ctypes.FormatError(status) 681 | ) 682 | 683 | if property_data_length.value == 0: 684 | return None 685 | 686 | property_buffer = ctypes.cast( 687 | (ctypes.c_byte * property_data_length.value)(), 688 | ctypes.POINTER(ctypes.c_byte) 689 | ) 690 | 691 | status = tdh.TdhGetProperty( 692 | pointer(event_record), 693 | 0, 694 | None, 695 | 1, 696 | ctypes.byref(data_descriptor), 697 | property_data_length.value, 698 | property_buffer 699 | ) 700 | 701 | if status != ERROR_SUCCESS: 702 | raise WindowsError( 703 | status, ctypes.FormatError(status) 704 | ) 705 | 706 | return get_formatted_value( 707 | in_type, 708 | property_buffer 709 | ) 710 | 711 | def get_property_length(self, event_record, event_info): 712 | data_descriptor = PROPERTY_DATA_DESCRIPTOR() 713 | length = DWORD() 714 | 715 | data_descriptor.PropertyName = ( 716 | ctypes.cast(event_info, ctypes.c_voidp).value + 717 | self.NameOffset 718 | ) 719 | data_descriptor.ArrayIndex = MAX_UINT 720 | 721 | status = tdh.TdhGetPropertySize( 722 | event_record, 723 | 0, 724 | None, 725 | 1, 726 | ctypes.byref(data_descriptor), 727 | ctypes.byref(length) 728 | ) 729 | if status != ERROR_SUCCESS: 730 | raise ctypes.WinError(status) 731 | 732 | return length.value 733 | 734 | 735 | class TRACE_EVENT_INFO(Structure): 736 | _fields_ = [ 737 | ('ProviderGuid', GUID), 738 | ('EventGuid', GUID), 739 | ('EventDescriptor', EVENT_DESCRIPTOR), 740 | ('DecodingSource', DECODING_SOURCE), 741 | ('ProviderNameOffset', ULONG), 742 | ('LevelNameOffset', ULONG), 743 | ('ChannelNameOffset', ULONG), 744 | ('KeywordsNameOffset', ULONG), 745 | ('TaskNameOffset', ULONG), 746 | ('OpcodeNameOffset', ULONG), 747 | ('EventMessageOffset', ULONG), 748 | ('ProviderMessageOffset', ULONG), 749 | ('BinaryXMLOffset', ULONG), 750 | ('BinaryXMLSize', ULONG), 751 | ('ActivityIDNameOffset', ULONG), 752 | ('RelatedActivityIDNameOffset', ULONG), 753 | ('PropertyCount', ULONG), 754 | ('TopLevelPropertyCount', ULONG), 755 | ('Flags', ULONG), 756 | ('EventPropertyInfoArray', EVENT_PROPERTY_INFO * 0) 757 | ] 758 | 759 | @property 760 | def ProviderName(self): 761 | if self.ProviderNameOffset > 0: 762 | return rel_ptr_to_str( 763 | pointer(self), 764 | self.ProviderNameOffset 765 | ) 766 | return "" 767 | 768 | @property 769 | def LevelName(self): 770 | if self.LevelNameOffset > 0: 771 | return rel_ptr_to_str( 772 | pointer(self), 773 | self.LevelNameOffset 774 | ) 775 | return "" 776 | 777 | @property 778 | def ChannelName(self): 779 | if self.ChannelNameOffset > 0: 780 | return rel_ptr_to_str( 781 | pointer(self), 782 | self.ChannelNameOffset 783 | ) 784 | return "" 785 | 786 | @property 787 | def KeywordsName(self): 788 | if self.KeywordsNameOffset > 0: 789 | return rel_ptr_to_str( 790 | pointer(self), 791 | self.KeywordsNameOffset 792 | ) 793 | return "" 794 | 795 | @property 796 | def TaskName(self): 797 | if self.TaskNameOffset > 0: 798 | return rel_ptr_to_str( 799 | pointer(self), 800 | self.TaskNameOffset 801 | ) 802 | return "" 803 | 804 | @property 805 | def OpcodeName(self): 806 | if self.OpcodeNameOffset > 0: 807 | return rel_ptr_to_str( 808 | pointer(self), 809 | self.OpcodeNameOffset 810 | ) 811 | return "" 812 | 813 | @property 814 | def EventMessage(self): 815 | if self.EventMessageOffset > 0: 816 | return rel_ptr_to_str( 817 | pointer(self), 818 | self.EventMessageOffset 819 | ) 820 | return "" 821 | 822 | @property 823 | def ProviderMessage(self): 824 | if self.ProviderMessageOffset > 0: 825 | return rel_ptr_to_str( 826 | pointer(self), 827 | self.ProviderMessageOffset 828 | ) 829 | return "" 830 | 831 | def iter_properties(self): 832 | property_array = ctypes.cast( 833 | self.EventPropertyInfoArray, 834 | ctypes.POINTER(EVENT_PROPERTY_INFO) 835 | ) 836 | 837 | for i in range(self.TopLevelPropertyCount): 838 | event_property_info = property_array[i] 839 | yield event_property_info 840 | 841 | def as_dict(self): 842 | return { 843 | "ProviderGuid": str(self.ProviderGuid), 844 | "EventGuid": str(self.EventGuid), 845 | "ProviderName": self.ProviderName, 846 | "LevelName": self.LevelName, 847 | "ChannelName": self.ChannelName, 848 | "KeywordsName": self.KeywordsName, 849 | "TaskName": self.TaskName, 850 | "OpcodeName": self.OpcodeName, 851 | "EventMessage": self.EventMessage, 852 | "ProviderMessage": self.ProviderMessage, 853 | } 854 | 855 | 856 | class TDH_CONTEXT(Structure): 857 | _fields_ = [ 858 | ('ParameterValue', ULONGLONG), 859 | ('ParameterType', TDH_CONTEXT_TYPE), 860 | ('ParameterSize', ULONG) 861 | ] 862 | 863 | 864 | class EVENT_MAP_ENTRY(Structure): 865 | _fields_ = [ 866 | ('OutputOffset', ULONG), 867 | ('InputOffset', ULONG) 868 | ] 869 | 870 | 871 | class EVENT_MAP_INFO(Structure): 872 | _fields_ = [ 873 | ('NameOffset', ULONG), 874 | ('Flag', MAP_FLAGS), 875 | ('EntryCount', ULONG), 876 | ('FormatStringOffset', ULONG), 877 | ('MapEntryArray', EVENT_MAP_ENTRY * 0) 878 | ] 879 | 880 | 881 | class PROPERTY_DATA_DESCRIPTOR(Structure): 882 | _fields_ = [ 883 | ('PropertyName', ULONGLONG), 884 | ('ArrayIndex', ULONG), 885 | ('Reserved', ULONG) 886 | ] 887 | 888 | 889 | class IN6_ADDR(Structure): 890 | _fields_ = [('Byte', c_byte * 16)] 891 | 892 | 893 | # https://docs.microsoft.com/en-us/windows/desktop/api/winevt/ns-winevt-_evt_variant 894 | class EVT_VARIANT_UNION(Union): 895 | _fields_ = [ 896 | ("BooleanVal", BOOL), 897 | ("SByteVal", INT8), 898 | ("Int16Val", INT16), 899 | ("Int32Val", INT32), 900 | ("Int64Val", INT64), 901 | ("ByteVal", UINT8), 902 | ("UInt16Val", UINT16), 903 | ("UInt32Val", UINT32), 904 | ("UInt64Val", UINT64), 905 | ("SingleVal", FLOAT), 906 | ("DoubleVal", DOUBLE), 907 | ("FileTimeVal", ULONGLONG), 908 | ("SysTimeVal", POINTER(SYSTEMTIME)), 909 | ("GuidVal", POINTER(GUID)), 910 | ("StringVal", LPCWSTR), 911 | ("AnsiStringVal", LPCSTR), 912 | ("BinaryVal", PBYTE), 913 | ("SidVal", PSID), 914 | ("SizeTVal", SIZE_T), 915 | ("EvtHandleVal", EVT_HANDLE), 916 | ("BooleanArr", POINTER(BOOL)), 917 | ("SByteArr", POINTER(INT8)), 918 | ("Int16Arr", POINTER(INT16)), 919 | ("Int32Arr", POINTER(INT32)), 920 | ("Int64Arr", POINTER(INT64)), 921 | ("ByteArr", POINTER(UINT8)), 922 | ("UInt16Arr", POINTER(UINT16)), 923 | ("UInt32Arr", POINTER(UINT32)), 924 | ("UInt64Arr", POINTER(UINT64)), 925 | ("SingleArr", POINTER(FLOAT)), 926 | ("DoubleArr", POINTER(DOUBLE)), 927 | ("FileTimeArr", POINTER(FILETIME)), 928 | ("SysTimeArr", POINTER(SYSTEMTIME)), 929 | ("GuidArr", POINTER(GUID)), 930 | ("StringArr", POINTER(LPWSTR)), 931 | ("AnsiStringArr", POINTER(LPSTR)), 932 | ("SidArr", POINTER(PSID)), 933 | ("SizeTArr", POINTER(SIZE_T)), 934 | ("XmlVal", LPCWSTR), 935 | ("XmlValArr", POINTER(LPCWSTR)), 936 | ] 937 | class EVT_VARIANT(Structure): 938 | _fields_ = [ 939 | ("_VARIANT_VALUE", EVT_VARIANT_UNION), 940 | ("Count", DWORD), 941 | ("Type", DWORD), 942 | ] 943 | PEVT_VARIANT = POINTER(EVT_VARIANT) 944 | 945 | # Because the following packages rely on winstructs, we import them at the end 946 | from winthingies.win32.tdh import tdh 947 | --------------------------------------------------------------------------------