├── .gitignore ├── LICENSE ├── README.md ├── getnetguids.py └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | .idea 6 | 7 | # C extensions 8 | *.so 9 | 10 | # Distribution / packaging 11 | .Python 12 | env/ 13 | build/ 14 | develop-eggs/ 15 | dist/ 16 | downloads/ 17 | eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .cache 42 | nosetests.xml 43 | coverage.xml 44 | 45 | # Translations 46 | *.mo 47 | *.pot 48 | 49 | # Django stuff: 50 | *.log 51 | 52 | # Sphinx documentation 53 | docs/_build/ 54 | 55 | # PyBuilder 56 | target/ 57 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 CylanceSPEAR 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GetNETGUIDs 2 | 3 | A tool to extract GUIDs from .NET assemblies to help identify the projects they belong to. 4 | 5 | http://blog.cylance.com/you-me-and-.net-guids 6 | 7 | ## Help Menu 8 | 9 | ``` 10 | $ getnetguids -h 11 | usage: getnetguids [-h] [-v] [-r] [-c] [path [path ...]] 12 | 13 | Extracts Typelib IDs and MVIDs from .NET assemblies. 14 | 15 | positional arguments: 16 | path Paths to files or directories to scan 17 | 18 | optional arguments: 19 | -h, --help show this help message and exit 20 | -v, --version show program's version number and exit 21 | -r, --recursive Scan paths recursively 22 | -c, --csv Save to CSV 23 | 24 | getnetguids v1.4.2 by Brian Wallace (@botnet_hunter) 25 | 26 | ``` 27 | -------------------------------------------------------------------------------- /getnetguids.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | import pefile 3 | import struct 4 | import re 5 | import datetime 6 | import csv 7 | import hashlib 8 | 9 | guid_regex = re.compile("[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}") 10 | 11 | 12 | def format_guid_from_hex(hex_string): 13 | first = hex_string[6:8] + hex_string[4:6] + hex_string[2:4] + hex_string[:2] 14 | second = hex_string[10:12] + hex_string[8:10] 15 | third = hex_string[14:16] + hex_string[12:14] 16 | return "{0}-{1}-{2}-{3}-{4}".format(first, second, third, hex_string[16:20], hex_string[20:]) 17 | 18 | 19 | def read_blob(blob): 20 | if len(blob) == 0: 21 | return "" 22 | first_byte = ord(blob[0]) 23 | if first_byte & 0x80 == 0: 24 | # easy one 25 | raw_string = blob[1:][:first_byte] 26 | length_determined_string = raw_string[2:][:-2] 27 | if len(length_determined_string) != 0: 28 | return length_determined_string[1:] 29 | return length_determined_string 30 | # Our string is not very long 31 | return "" 32 | 33 | 34 | def is_dot_net_assembly(pe): 35 | return pe.OPTIONAL_HEADER.DATA_DIRECTORY[14].VirtualAddress != 0 36 | 37 | 38 | def get_assembly_guids(assembly_path): 39 | try: 40 | try: 41 | pe = pefile.PE(assembly_path) 42 | 43 | txt_start = None 44 | txt_end = None 45 | for section in pe.sections: 46 | if section.Name.startswith(".text\x00"): 47 | txt_start = section.PointerToRawData 48 | txt_end = txt_start + section.SizeOfRawData 49 | except pefile.PEFormatError: 50 | return None 51 | if not is_dot_net_assembly(pe): 52 | return None 53 | 54 | #Compile TimeDateStamp 55 | try: 56 | compiled = get_compiletime(pe) 57 | except Exception: 58 | compiled = "Error" 59 | 60 | # Removed strict parsing and opted for simple searching method to support malformed assemblies 61 | with open(assembly_path, "rb") as assembly_file_handler: 62 | file_data = assembly_file_handler.read() 63 | 64 | text_section = file_data[txt_start:][:txt_end] 65 | 66 | 67 | mdo = pe.get_offset_from_rva(struct.unpack("= 2**(16 - 5) for x in has_custom_attribute_tables]) 153 | big_custom_attribute_type = any([row_counts[x] >= 2**(16 - 3) for x in custom_attribute_type_tables]) 154 | big_resolution_scope = any([row_counts[x] >= 2**(16 - 2) for x in resolution_scope_tables]) 155 | big_type_def_or_ref = any([row_counts[x] >= 2**(16 - 2) for x in type_def_or_ref_tables]) 156 | big_member_ref_parent = any([row_counts[x] >= 2**(16 - 3) for x in member_ref_tables]) 157 | 158 | # Build row length for each type up to CustomAttr 159 | row_type_widths = [ 160 | # 0x00 Module = Generation (2 bytes) + Name (String heap index) + Mvid (Guid heap index) + 161 | # EncId (Guid heap index) + EncBaseId (Guid heap index) 162 | 2 + strings_heap_index_length + (guid_heap_index_length * 3), 163 | 164 | # 0x01 TypeRef = ResolutionScope (ResolutionScope index) + TypeName (String heap) + 165 | # TypeNamespace (String heap) 166 | (4 if big_resolution_scope else 2) + (strings_heap_index_length * 2), 167 | # 0x02 TypeDef = Flags(2 bytes) + TypeName(String heap index) +TypeNamespace(String heap index)+ 168 | # Extends (TypeDefOrRef index) + FieldList (index into field table) + 169 | # MethodList (index into MethodDef table) + ? 170 | 8 + (4 if big_type_def_or_ref else 2) + (strings_heap_index_length * 2), 171 | 0, # 0x03 None 172 | # 0x04 Field = Flags (2 bytes) + Name (String heap index) + Signature (Blob heap index) 173 | 2 + strings_heap_index_length + blob_heap_index_length, 174 | 0, # 0x05 None 175 | # 0x06 MethodDef = RVA(4 bytes) + ImplFlags(2 bytes) + Flags(2 bytes) + Name(String heap index)+ 176 | # Signature (Blob heap index) + ParamList (index to param table) 177 | 10 + strings_heap_index_length + blob_heap_index_length, 178 | 0, # 0x07 None 179 | # 0x08 Param = Flags (2 bytes) + Sequence (2 bytes) + Name (String heap index) 180 | 4 + strings_heap_index_length, 181 | # 0x09 InterfaceImpl = Class (TypeDef index) + Interface (TypeDefOrRef index) 182 | 2 + (4 if big_type_def_or_ref else 2), 183 | # 0x0a MemberRef = Class(MemberRefParent) + Name(String heap index) + Signature(Blob heap index) 184 | (4 if big_member_ref_parent else 2) + strings_heap_index_length + blob_heap_index_length, 185 | # 0x0b Constant = Type (?) + Parent + Value (Blob heap index) 186 | 4 + blob_heap_index_length, 187 | # 0x0c CustomAttr = Parent + Type (CustomAttributeType) + Value (Blob heap index) 188 | (4 if big_has_custom_attribute else 2) + (4 if big_custom_attribute_type else 2) + blob_heap_index_length, 189 | # Don't care about the rest 190 | ] 191 | 192 | for index in xrange(0x0c): 193 | t_offset += row_type_widths[index] * row_counts[index] 194 | 195 | for index in xrange(row_counts[0x0c]): 196 | # In the most strict interpretation, a typelib id is expressed as a 197 | # GuidAttribute on the current assembly. 198 | # To check that it's actually a GuidAttribute we'd have to support parsing 199 | # .NET signatures, so it's safer to assume a MemberRef attribute owned by a 200 | # TypeRef on an AssemblyRow with a value matching a guid is PROBABLY the typelib id 201 | 202 | row_offset = t_offset 203 | 204 | if big_has_custom_attribute: 205 | parent_index = struct.unpack("