# TypeRef hash (modified)

The TypeRef hash was made popular by GData, but it seems this method was introduced earlier in ClrGuard. The implementation by GData takes the referenced type names and their corresponding namespace names from the TypeRef table and creates a SHA-256 hash value. For a detailed description, please take a look at their blog post.

We have slightly modified GData's implementation in the following way. Instead of the namespace names, we use the resolution scope names as they're always present. While every reference type has a corresponding resolution scope, it doesn't need to have a corresponding namespace. Additionally, you can optionally skip types that reference each other as added by some .NET protectors (see TypeRef table has self-referencing entries). This results in some sort of cleared TypeRef hash that might be slightly more reliable. Furthermore, the list of scope and type names can be sorted alphabetically after the type names before being hashed. Strings are also used case-sensitive and everything else is also identical.

# Malware example

Hash (SHA-256): 6f30280da560a30bbe25835f00d881dad94e5a7641b63e028080628c1f82a992

This is an unobfuscated .NET EXE malware with the following TypeRef table:

TypeRef table content of the malware
TypeRef table content of the malware

As can be seen, there are referenced types like SpecialFolder that don't have a corresponding namespace, but always have a resolution scope.

We can calculate the different TypeRef hash possibilities with the following example code:

# 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>/6f30280da560a30bbe25835f00d881dad94e5a7641b63e028080628c1f82a992')

# Check if the "TypeRef" table exists and print out TypeRef hashes
if dotnet_file.metadata_table_exists('TypeRef'):
    print(f'TypeRef hash (unsorted, exclude self-referenced entries): {dotnet_file.TypeRef.get_typeref_hash()} (SHA-256)')
    print(f'TypeRef hash (unsorted, include self-referenced entries) {dotnet_file.TypeRef.get_typeref_hash(dotnet_file.Type.Hash.SHA256, False, False)} (SHA-256)')
    print(f'TypeRef hash (sorted, exclude self-referenced entries) {dotnet_file.TypeRef.get_typeref_hash(dotnet_file.Type.Hash.SHA256, True, True)} (SHA-256)')
    print(f'TypeRef hash (sorted, include self-referenced entries) {dotnet_file.TypeRef.get_typeref_hash(dotnet_file.Type.Hash.SHA256, False, True)} (SHA-256)')

The result is as follows:

TypeRef hash (unsorted, exclude self-referenced entries): 580bf8658b52f3e9f9ec56e7ca0182dfcbd5b5afc0e5ba7a33fde9391d600e2c (SHA-256)
TypeRef hash (unsorted, include self-referenced entries) 9e4acafdf71318f6c2beceb74c7b92f47314074b6fa47d3d5d36bb0191e232a0 (SHA-256)
TypeRef hash (sorted, exclude self-referenced entries) 70aced87f5363697f0fe065f1f30d61f599fd925c6f309d542a64aafc7f5c289 (SHA-256)
TypeRef hash (sorted, include self-referenced entries) f698897849536ed3ab859477a9dcc9e612c54f9b0a170b4d62eff1c3e24804d4 (SHA-256)

Which combination makes more sense is up to you, if you want to use this hash method.

As an example of how the list of <ResolutionScope>-<ReferencedType> name pairs looks like before it gets hashed, the following list excerpt shows the unsorted + excluded self-referenced entries version: