#
General
General methods are functions that can't be assigned to a specific table or header and are thus part of the main DotNetPE class.
#
Metadata table existence
metadata_table_exists(name: str) -> bool
Checks if a metadata table exists based on its name.
Parameters:
- name Name of the table
Return value:
True or False
Example:
# Import class DotNetPE from module dotnetfile
from dotnetfile import DotNetPE
# Create an instance of DotNetPE with the file path as a parameter
dotnet_file = DotNetPE('/Users/<username>/my_dotnet_assembly.exe')
# Check if table "MethodDef" exists in .NET assembly and print out result
if dotnet_file.metadata_table_exists('MethodDef'):
print('Table exists.')
else:
print('Table does not exist.')
#
Get existent metadata tables
existent_metadata_tables() -> List[str]
Get a list of existent metadata tables.
Parameters:
-
Return value:
List with metadata table name strings
Example:
# Import class DotNetPE from module dotnetfile
from dotnetfile import DotNetPE
# Create an instance of DotNetPE with the file path as a parameter
dotnet_file = DotNetPE('/Users/<username>/my_dotnet_assembly.exe')
# Get all existent metadata tables of the assembly
available_tables = dotnet_file.existent_metadata_tables()
# Print out each table name
for table in available_tables:
print(f'{table}')
#
Native image check
is_native_image() -> bool
Check if assembly is a native image.
Native images are .NET files that are created with the Windows Native Image Generator Ngen.exe
. This tool creates a precompiled native version of a .NET assembly which can be seen as a cached version of the original file. Such a file already contains the native code that is usually created by the JIT compiler at runtime. Thus, these samples are often faster in execution. Consequentially, native images are platform dependant. Also, a disadvantage of native images is that they are much bigger size of the file compared to the original version.
Native Image Service
and starting from Windows 8 and .NET Framework 4.5 the Windows Image Task
which seem to automatically create them. Again, how and on what conditions this happens is not documented.
Parameters:
-
Return value:
True or False
Example:
# Import class DotNetPE from module dotnetfile
from dotnetfile import DotNetPE
# Create an instance of DotNetPE with the file path as a parameter
dotnet_file = DotNetPE('/Users/<username>/my_dotnet_assembly.exe')
# Print out if assembly is a native image
print(f'Is a native image (precompiled) created by Ngen: {dotnet_file.is_native_image()}')
#
Mixed assembly check
is_mixed_assembly() -> bool
Check if assembly is a mixed assembly.
Mixed assemblies are .NET files that have both managed and unmanaged code. Compared to normal .NET assemblies, they have more than just the one imported function _CoreExeMain
(EXE) or _CorDllMain
(DLL) from mscoree.dll
on the native side. But also the managed side looks much different with a plethora of initialization namespace, types and methods.
C++/CLI extension
. C++/CLI is some sort of dialect of C++ created for the production of .NET assemblies with the C++ compiler.
<CrtImplementationDetails>
and <CppImplementationDetails>
in mixed assemblies, but there are also exceptions. You can also build mixed assemblies "manually". An example can be found as part of an open-source code for CVE-2019-18935:
The example shows how to use an empty dummy C# file for the managed part and shellcode for the unmanaged one. The result is a .NET assembly with no managed code and the whole functionality contained in the native part.
Parameters:
-
Return value:
True or False
Example:
# Import class DotNetPE from module dotnetfile
from dotnetfile import DotNetPE
# Create an instance of DotNetPE with the file path as a parameter
dotnet_file = DotNetPE('/Users/<username>/my_dotnet_assembly.exe')
# Print out if assembly is a mixed assembly
print(f'Is a .NET mixed assembly (managed + native code): {dotnet_file.is_mixed_assembly()}')
#
Native entry point existence
has_native_entry_point() -> bool
Check if assembly has a native entry point.
A .NET assembly that has a native entry point is always a mixed assembly.
Parameters:
-
Return value:
True or False
Example:
# Import class DotNetPE from module dotnetfile
from dotnetfile import DotNetPE
# Create an instance of DotNetPE with the file path as a parameter
dotnet_file = DotNetPE('/Users/<username>/my_dotnet_assembly.exe')
# Print out if assembly has a native entry point
print(f'Has a native entry point: {dotnet_file.has_native_entry_point()}')
#
Windows Forms app check
is_windows_forms_app() -> bool
Check if assembly is a Windows Forms app.
Parameters:
-
Return value:
True or False
Example:
# Import class DotNetPE from module dotnetfile
from dotnetfile import DotNetPE
# Create an instance of DotNetPE with the file path as a parameter
dotnet_file = DotNetPE('/Users/<username>/my_dotnet_assembly.exe')
# Print out if assembly is a Windows Forms app
print(f'Is a Windows Forms app: {dotnet_file.is_windows_forms_app()}')
#
Get stream names
get_stream_names() -> List[str]
Get a list of stream names.
Parameters:
-
Return value:
A list with stream names
Example:
# Import class DotNetPE from module dotnetfile
from dotnetfile import DotNetPE
# Create an instance of DotNetPE with the file path as a parameter
dotnet_file = DotNetPE('/Users/<username>/my_dotnet_assembly.exe')
# Get list of stream names
stream_names = dotnet_file.get_stream_names()
# Print out each stream name
for stream_name in stream_names:
print(f'\t{stream_name}')
#
Resources check
has_resources() -> bool
Check if assembly has resources.
Parameters:
-
Return value:
True or False
Example:
# Import class DotNetPE from module dotnetfile
from dotnetfile import DotNetPE
# Create an instance of DotNetPE with the file path as a parameter
dotnet_file = DotNetPE('/Users/<username>/my_dotnet_assembly.exe')
# Print out if assembly has resources
print(f'Has .NET resources: {dotnet_file.has_resources()}')
#
Get resources
get_resources() -> List[Dict]
Get .NET (sub-)resources data and information.
Parameters:
-
Return value:
The resulting dictionary has the following keys and values :
- Name str Resource name
-
Visibility
str
Resource visibility. Options:
- public
- private
- unknown
- Size int Resource size
- Data bytes Resource data
- NumberOfSubResources int Number of sub-resources
-
SubResources
List[Dict]
Sub-resources:
- Name str Sub-resource name
-
Type
str
Sub-resource type. Options:
- Null
- String
- Boolean
- Char
- Byte
- SByte
- Int16
- UInt16
- Int32
- UInt32
- Int64
- UInt64
- Single
- Double
- Decimal
- DateTime
- Timespan
- ByteArray
- Stream
- UserType
-
TypeDetails
Dict
Sub-resource type details:
- TypeEx str Sub-resource detailed type information
-
TypeCode
int
Sub-resource type code. Options:
- 0 (Null)
- 1 (String)
- 2 (Boolean)
- 3 (Char)
- 4 (Byte)
- 5 (SByte)
- 6 (Int16)
- 7 (UInt16)
- 8 (Int32)
- 9 (UInt32)
- 10 (Int64)
- 11 (UInt64)
- 12 (Single)
- 13 (Double)
- 14 (Decimal)
- 15 (DateTime)
- 16 (Timespan)
- 32 (ByteArray)
- 33 (Stream)
- 64 (UserType)
- Size int Sub-resource size
- Data bytes Sub-resource data
Example:
# Import class DotNetPE from module dotnetfile
from dotnetfile import DotNetPE
# Create an instance of DotNetPE with the file path as a parameter
dotnet_file = DotNetPE('/Users/<username>/my_dotnet_assembly.exe')
# Get resource data
resource_data = dotnet_file.get_resources()
# Print out resource and subresource data
for data in resource_data:
for resource_item in data.items():
if resource_item[0] == 'SubResources':
if resource_item[1]:
print(f'Sub-resources:')
for sub_resource in resource_item[1]:
for sub_resource_item in sub_resource.items():
print(f'\t{sub_resource_item[0]}: {sub_resource_item[1]}')
print('\t---')
else:
print(f'{resource_item[0]}: {resource_item[1]}')
print('---')
#
Get runtime target version
get_runtime_target_version() -> str
Get the .NET runtime target version of the assembly.
Parameters:
-
Return value:
Runtime target version string
Example:
# Import class DotNetPE from module dotnetfile
from dotnetfile import DotNetPE
# Create an instance of DotNetPE with the file path as a parameter
dotnet_file = DotNetPE('/Users/<username>/my_dotnet_assembly.exe')
# Print out CLR target version of assembly
print(f'.NET runtime target version: {dotnet_file.get_runtime_target_version()}')
#
Get number of streams
get_number_of_streams() -> int
Get the number of metadata streams.
Parameters:
-
Return value:
Number of streams
Example:
# Import class DotNetPE from module dotnetfile
from dotnetfile import DotNetPE
# Create an instance of DotNetPE with the file path as a parameter
dotnet_file = DotNetPE('/Users/<username>/my_dotnet_assembly.exe')
# Print out number of streams
print(f'Number of streams: {dotnet_file.get_number_of_streams()}')
#
Get referenced libraries
get_all_references() -> List[str]
Get a list of all referenced libraries. This includes system, 3rd party and unmanaged libraries.
Parameters:
-
Return value:
List with library name strings
Example:
# Import class DotNetPE from module dotnetfile
from dotnetfile import DotNetPE
# Create an instance of DotNetPE with the file path as a parameter
dotnet_file = DotNetPE('/Users/<username>/my_dotnet_assembly.exe')
# Get all referenced libraries of the assembly
referenced_libraries = dotnet_file.get_all_references()
# Print out each library name
for library in referenced_libraries:
print(f'{library}')
#
Get #Strings stream strings
get_strings_stream_strings() -> List[str]
Get all strings from the #Strings
stream.
The #Strings
heap contains all the names of the assembly, namespaces, types and members.
Parameters:
-
Return value:
List with #Strings
stream strings
Example:
# Import class DotNetPE from module dotnetfile
from dotnetfile import DotNetPE
# Create an instance of DotNetPE with the file path as a parameter
dotnet_file = DotNetPE('/Users/<username>/my_dotnet_assembly.exe')
# Get all strings of the #Strings stream
strings_stream_strings = dotnet_file.get_strings_stream_strings()
# Print out each library name
for string in strings_stream_strings:
print(f'{string}')
#
Get #US stream strings
get_user_stream_strings() -> List[str]
Get all strings from the #US
stream.
The #US
heap contains all strings directly defined by the programmer in method code. The strings are stored in Unicode format.
Parameters:
-
Return value:
List with #US
stream strings
Example:
# Import class DotNetPE from module dotnetfile
from dotnetfile import DotNetPE
# Create an instance of DotNetPE with the file path as a parameter
dotnet_file = DotNetPE('/Users/<username>/my_dotnet_assembly.exe')
# Get all strings of the #US stream
us_stream_strings = dotnet_file.get_user_stream_strings()
# Print out each library name
for string in us_stream_strings:
print(f'{string}')
#
Get #Strings stream string
get_string(string_address: int) -> str
Get a string from the #Streams
heap string table of an assembly. This includes overlapping strings which are part of other strings, e.g. MyString
in ThisIsMyString
.
This method is more like an internal one, but might be still useful in some situations.
Parameters:
- string_address Address/index of the string in the #Strings stream
Return value:
#Strings stream string
Example:
# Import class DotNetPE from module dotnetfile
from dotnetfile import DotNetPE
# Create an instance of DotNetPE with the file path as a parameter
dotnet_file = DotNetPE('/Users/<username>/my_dotnet_assembly.exe')
# Print out string with index 10
print(f'String at index 10: {dotnet_file.get_string(10)}')
#
Get hash from strings
get_hash(hash_type: Type.Hash, string_list: List) -> str
Get a MD5
, SHA-1
or SHA-256
hash from a list of strings.
This method is more like an internal one, but might be still useful in some situations.
Parameters:
- hash_type Hash type ( Type.Hash.MD5 , Type.Hash.SHA1 or Type.Hash.SHA256 )
- string_list List with strings
Return value:
Hash as a string
Example:
# Import class DotNetPE from module dotnetfile
from dotnetfile import DotNetPE
# Create an instance of DotNetPE with the file path as a parameter
dotnet_file = DotNetPE('/Users/<username>/my_dotnet_assembly.exe')
# Get all strings from the #US stream (see below)
us_stream_strings = dotnet_file.get_user_stream_strings()
# Print out string with index 10
print(f'SHA-256 hash of all #US stream strings: {dotnet_file.get_hash(dotnet_file.Type.Hash.SHA256, us_stream_strings)}')