Skip to content
This repository has been archived by the owner on Jul 1, 2024. It is now read-only.

AssemblyUnhollower deobfuscation map generation | Fix for MissingKeyException and Type comparison #94

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions AssemblyUnhollower/AssemblyKnownImports.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ public static AssemblyKnownImports For(ModuleDefinition module, RewriteGlobalCon
private readonly Lazy<TypeReference> myIl2CppArrayBase;
private readonly Lazy<TypeReference> myIl2CppArrayBaseSetlfSubst;
private readonly Lazy<TypeReference> myDefaultMemberAttribute;
private readonly Lazy<TypeReference> myIl2CppObjectReference;

public TypeReference Void => myVoidReference.Value;
public TypeReference IntPtr => myIntPtrReference.Value;
Expand Down Expand Up @@ -184,7 +185,7 @@ public AssemblyKnownImports(ModuleDefinition module, RewriteGlobalContext contex
myIl2CppArrayBaseSetlfSubst = new Lazy<TypeReference>(() => Module.ImportReference(new GenericInstanceType(Il2CppArrayBase) { GenericArguments = { Il2CppArrayBase.GenericParameters[0] }}));
myIl2CppObjectBaseReference = new Lazy<TypeReference>(() => Module.ImportReference(typeof(Il2CppObjectBase)));
myDefaultMemberAttribute = new Lazy<TypeReference>(() => Module.ImportReference(TargetTypeSystemHandler.DefaultMemberAttribute));
// myIl2CppObjectReference = new Lazy<TypeReference>(() => Module.ImportReference(TargetTypeSystemHandler.Object));// todo!
myIl2CppObjectReference = new Lazy<TypeReference>(() => Module.ImportReference(TargetTypeSystemHandler.Object));

myIl2CppObjectToPointer = new Lazy<MethodReference>(() => Module.ImportReference(typeof(IL2CPP).GetMethod("Il2CppObjectBaseToPtr")));
myIl2CppObjectToPointerNotNull = new Lazy<MethodReference>(() => Module.ImportReference(typeof(IL2CPP).GetMethod("Il2CppObjectBaseToPtrNotNull")));
Expand Down Expand Up @@ -264,4 +265,4 @@ public AssemblyKnownImports(ModuleDefinition module, RewriteGlobalContext contex
);
}
}
}
}
68 changes: 50 additions & 18 deletions AssemblyUnhollower/Contexts/AssemblyRewriteContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace AssemblyUnhollower.Contexts
public class AssemblyRewriteContext
{
public readonly RewriteGlobalContext GlobalContext;

public readonly AssemblyDefinition OriginalAssembly;
public readonly AssemblyDefinition NewAssembly;

Expand All @@ -28,7 +28,21 @@ public AssemblyRewriteContext(RewriteGlobalContext globalContext, AssemblyDefini
Imports = AssemblyKnownImports.For(newAssembly.MainModule, globalContext);
}

public TypeRewriteContext GetContextForOriginalType(TypeDefinition type) => myOldTypeMap[type];
public TypeRewriteContext GetContextForOriginalType(TypeDefinition type)
{
try
{
return myOldTypeMap[type];
}
catch
{
foreach (var oldtype in myOldTypeMap.Keys)
{
if (type.Name == oldtype.Name) return myOldTypeMap[oldtype];
}
return myNewTypeMap[type];
}
}
public TypeRewriteContext? TryGetContextForOriginalType(TypeDefinition type) => myOldTypeMap.TryGetValue(type, out var result) ? result : null;
public TypeRewriteContext GetContextForNewType(TypeDefinition type) => myNewTypeMap[type];

Expand All @@ -43,37 +57,38 @@ public void RegisterTypeRewrite(TypeRewriteContext context)
public MethodReference RewriteMethodRef(MethodReference methodRef)
{
var newType = GlobalContext.GetNewTypeForOriginal(methodRef.DeclaringType.Resolve());
return newType.GetMethodByOldMethod(methodRef.Resolve()).NewMethod;
return newType.GetMethodByOldMethod(methodRef.Resolve()).NewMethod;
}

public TypeReference RewriteTypeRef(TypeReference? typeRef)
{
if (typeRef == null) return Imports.Il2CppObjectBase;

var sourceModule = NewAssembly.MainModule;

if (typeRef is ArrayType arrayType)
{
if (arrayType.Rank != 1)
return Imports.Il2CppObjectBase;

var elementType = arrayType.ElementType;
if (elementType.FullName == "System.String")
return Imports.Il2CppStringArray;

var convertedElementType = RewriteTypeRef(elementType);
if (elementType.IsGenericParameter)
return new GenericInstanceType(Imports.Il2CppArrayBase) {GenericArguments = {convertedElementType}};
return new GenericInstanceType(Imports.Il2CppArrayBase) { GenericArguments = { convertedElementType } };

return new GenericInstanceType(convertedElementType.IsValueType
? Imports.Il2CppStructArray
: Imports.Il2CppReferenceArray) {GenericArguments = {convertedElementType}};
: Imports.Il2CppReferenceArray)
{ GenericArguments = { convertedElementType } };
}

if (typeRef is GenericParameter genericParameter)
{
var genericParameterDeclaringType = genericParameter.DeclaringType;
if(genericParameterDeclaringType != null)
if (genericParameterDeclaringType != null)
return RewriteTypeRef(genericParameterDeclaringType).GenericParameters[genericParameter.Position];

return RewriteMethodRef(genericParameter.DeclaringMethod).GenericParameters[genericParameter.Position];
Expand All @@ -82,7 +97,7 @@ public TypeReference RewriteTypeRef(TypeReference? typeRef)
if (typeRef is ByReferenceType byRef)
return new ByReferenceType(RewriteTypeRef(byRef.ElementType));

if(typeRef is PointerType pointerType)
if (typeRef is PointerType pointerType)
return new PointerType(RewriteTypeRef(pointerType.ElementType));

if (typeRef is GenericInstanceType genericInstance)
Expand All @@ -96,31 +111,48 @@ public TypeReference RewriteTypeRef(TypeReference? typeRef)

if (typeRef.IsPrimitive || typeRef.FullName == "System.TypedReference")
return sourceModule.ImportReference(TargetTypeSystemHandler.Object.Module.GetType(typeRef.Namespace, typeRef.Name));

if (typeRef.FullName == "System.Void")
return Imports.Void;

if (typeRef.FullName == "System.String")
return Imports.String;

if(typeRef.FullName == "System.Object")
if (typeRef.FullName == "System.Object")
return sourceModule.ImportReference(GlobalContext.GetAssemblyByName("mscorlib").GetTypeByName("System.Object").NewType);

if (typeRef.FullName == "Il2CppSystem.Object")
return Imports.Object;

if (typeRef.FullName == "System.Attribute")
return sourceModule.ImportReference(GlobalContext.GetAssemblyByName("mscorlib").GetTypeByName("System.Attribute").NewType);

var originalTypeDef = typeRef.Resolve();
TypeDefinition? originalTypeDef;
try
{
originalTypeDef = typeRef.Resolve();
}
catch
{
return Imports.Il2CppObjectBase;
}
var targetAssembly = GlobalContext.GetNewAssemblyForOriginal(originalTypeDef.Module.Assembly);
var target = targetAssembly.GetContextForOriginalType(originalTypeDef).NewType;
var target = targetAssembly?.GetContextForOriginalType(originalTypeDef).NewType;

return sourceModule.ImportReference(target);
}

public TypeRewriteContext GetTypeByName(string name)
public TypeRewriteContext? GetTypeByName(string name)
{
return myNameTypeMap[name];
return myNameTypeMap.TryGetValue(name, out var result1) ?
result1 :
myNameTypeMap.TryGetValue(name.Replace("System", "Il2CppSystem"), out var result2) ?
result2 :
myNameTypeMap.TryGetValue(name.Replace("Il2CppSystem", "System"), out var result3) ?
result3 :
null;
}

public TypeRewriteContext? TryGetTypeByName(string name)
{
return myNameTypeMap.TryGetValue(name, out var result) ? result : null;
Expand Down
62 changes: 43 additions & 19 deletions AssemblyUnhollower/Contexts/RewriteGlobalContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,34 +12,34 @@ public class RewriteGlobalContext : IDisposable
public IIl2CppMetadataAccess GameAssemblies { get; }
public IMetadataAccess SystemAssemblies { get; }
public IMetadataAccess UnityAssemblies { get; }

private readonly Dictionary<string, AssemblyRewriteContext> myAssemblies = new Dictionary<string, AssemblyRewriteContext>();
private readonly Dictionary<AssemblyDefinition, AssemblyRewriteContext> myAssembliesByOld = new Dictionary<AssemblyDefinition, AssemblyRewriteContext>();

internal readonly Dictionary<(object, string, int), List<TypeDefinition>> RenameGroups = new Dictionary<(object, string, int), List<TypeDefinition>>();
internal readonly Dictionary<TypeDefinition, string> RenamedTypes = new Dictionary<TypeDefinition, string>();
internal readonly Dictionary<TypeDefinition, string> PreviousRenamedTypes = new Dictionary<TypeDefinition, string>();

internal readonly List<long> MethodStartAddresses = new List<long>();

public IEnumerable<AssemblyRewriteContext> Assemblies => myAssemblies.Values;

internal bool HasGcWbarrierFieldWrite { get; set; }

public RewriteGlobalContext(UnhollowerOptions options, IIl2CppMetadataAccess gameAssemblies, IMetadataAccess systemAssemblies, IMetadataAccess unityAssemblies)
{
Options = options;
GameAssemblies = gameAssemblies;
SystemAssemblies = systemAssemblies;
UnityAssemblies = unityAssemblies;

TargetTypeSystemHandler.Init(systemAssemblies);

foreach (var sourceAssembly in gameAssemblies.Assemblies)
{
var assemblyName = sourceAssembly.Name.Name;
if (assemblyName == "Il2CppDummyDll") continue;

var newAssembly = AssemblyDefinition.CreateAssembly(
new AssemblyNameDefinition(sourceAssembly.Name.Name.UnSystemify(options), sourceAssembly.Name.Version),
sourceAssembly.MainModule.Name.UnSystemify(options), sourceAssembly.MainModule.Kind);
Expand All @@ -56,47 +56,71 @@ internal void AddAssemblyContext(string assemblyName, AssemblyRewriteContext con
myAssembliesByOld[context.OriginalAssembly] = context;
}

public AssemblyRewriteContext GetNewAssemblyForOriginal(AssemblyDefinition oldAssembly)
public AssemblyRewriteContext? GetNewAssemblyForOriginal(AssemblyDefinition oldAssembly)
{
return myAssembliesByOld[oldAssembly];
try
{
return myAssembliesByOld[oldAssembly];
}
catch
{
foreach (var assembly in myAssembliesByOld.Keys)
{
if (assembly.Name == oldAssembly.Name)
return myAssembliesByOld[assembly];
}
return myAssemblies.TryGetValue("Il2Cppmscorlib", out var result2) ? result2 : null;
}
}

public TypeRewriteContext GetNewTypeForOriginal(TypeDefinition originalType)
public TypeRewriteContext? GetNewTypeForOriginal(TypeDefinition originalType)
{
return GetNewAssemblyForOriginal(originalType.Module.Assembly)
.GetContextForOriginalType(originalType);
return (GetNewAssemblyForOriginal(originalType.Module.Assembly) ??
(myAssemblies.TryGetValue("Il2Cppmscorlib", out var result1) ?
result1 :
null))?
.GetContextForOriginalType(originalType) ??
(myAssemblies.TryGetValue("Il2Cppmscorlib", out var result2) ?
result2.GetContextForOriginalType(originalType) :
null);
}

public TypeRewriteContext? TryGetNewTypeForOriginal(TypeDefinition originalType)
{
if (!myAssembliesByOld.TryGetValue(originalType.Module.Assembly, out var assembly))
return null;
return assembly.TryGetContextForOriginalType(originalType);
}

public TypeRewriteContext.TypeSpecifics JudgeSpecificsByOriginalType(TypeReference typeRef)
{
if (typeRef.IsPrimitive || typeRef.IsPointer || typeRef.FullName == "System.TypedReference") return TypeRewriteContext.TypeSpecifics.BlittableStruct;
if (typeRef.FullName == "System.String" || typeRef.FullName == "System.Object" || typeRef.IsArray || typeRef.IsByReference || typeRef.IsGenericParameter || typeRef.IsGenericInstance)
return TypeRewriteContext.TypeSpecifics.ReferenceType;

var fieldTypeContext = GetNewTypeForOriginal(typeRef.Resolve());
return fieldTypeContext.ComputedTypeSpecifics;
return fieldTypeContext != null ? fieldTypeContext.ComputedTypeSpecifics : TypeRewriteContext.TypeSpecifics.NotComputed;
}

public AssemblyRewriteContext GetAssemblyByName(string name)
public AssemblyRewriteContext? GetAssemblyByName(string name)
{
return myAssemblies[name];
return myAssemblies.TryGetValue(name, out var result1) ?
result1 :
myAssemblies.TryGetValue("mscorlib", out var result2) ?
result2 :
myAssemblies.TryGetValue("Il2Cppmscorlib", out var result3) ?
result3 :
null;
}

public AssemblyRewriteContext? TryGetAssemblyByName(string name)
{
if (myAssemblies.TryGetValue(name, out var result))
return result;

if (name == "netstandard")
return myAssemblies.TryGetValue("mscorlib", out var result2) ? result2 : null;

return null;
}

Expand Down
15 changes: 10 additions & 5 deletions AssemblyUnhollower/DeobfuscationMapGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,11 @@ void DoEnum(TypeRewriteContext obfuscatedType, TypeRewriteContext cleanType)
{
if (!originalTypeField.Name.IsObfuscated(obfuscatedType.AssemblyContext.GlobalContext.Options)) continue;
var matchedField = cleanType.OriginalType.Fields[obfuscatedType.OriginalType.Fields.IndexOf(originalTypeField)];

writer.WriteLine(obfuscatedType.NewType.GetNamespacePrefix() + "." + obfuscatedType.NewType.Name + "::" + Pass22GenerateEnums.GetUnmangledName(originalTypeField) + ";" + matchedField.Name + ";0");


string maybeWithDot = obfuscatedType.NewType.GetNamespacePrefix() + ".";
//if (maybeWithDot.IndexOf('.') == 0) maybeWithDot = maybeWithDot.Substring(1);
writer.WriteLine(obfuscatedType.NewType.GetNamespacePrefix() + obfuscatedType.NewType.Name + "::" + Pass22GenerateEnums.GetUnmangledName(originalTypeField) + ";" + matchedField.Name + ";0");
}
}

Expand All @@ -109,14 +112,16 @@ void DoEnum(TypeRewriteContext obfuscatedType, TypeRewriteContext cleanType)

void DoType(TypeRewriteContext typeContext, TypeRewriteContext? enclosingType)
{
if (!typeContext.OriginalNameWasObfuscated) return;
if(cleanAssembly.TryGetTypeByName(typeContext.NewType.Name) != null) return;

var cleanType = FindBestMatchType(typeContext, cleanAssembly, enclosingType);
if (cleanType.Item1 == null) return;

if (!usedNames.TryGetValue(cleanType.Item1.NewType, out var existing) || existing.Item2 < cleanType.Item2)
if (!usedNames.TryGetValue(cleanType.Item1.NewType, out var existing) || existing.Penalty < cleanType.Item2)
{
usedNames[cleanType.Item1.NewType] = (typeContext.NewType.GetNamespacePrefix() + "." + typeContext.NewType.Name, cleanType.Item2, typeContext.OriginalType.Namespace != cleanType.Item1.OriginalType.Namespace);
string maybeWithDot = typeContext.NewType.GetNamespacePrefix() + ".";
//if (maybeWithDot.IndexOf('.') == 0) maybeWithDot = maybeWithDot.Substring(1);
usedNames[cleanType.Item1.NewType] = (maybeWithDot + typeContext.NewType.Name, cleanType.Item2, typeContext.OriginalType.Namespace != cleanType.Item1.OriginalType.Namespace);
} else
return;

Expand Down
2 changes: 2 additions & 0 deletions AssemblyUnhollower/Passes/Pass11ComputeTypeSpecifics.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public static void DoPass(RewriteGlobalContext context)

private static void ComputeSpecifics(TypeRewriteContext typeContext)
{
if (typeContext == null) return;
if (typeContext.ComputedTypeSpecifics != TypeRewriteContext.TypeSpecifics.NotComputed) return;
typeContext.ComputedTypeSpecifics = TypeRewriteContext.TypeSpecifics.Computing;

Expand All @@ -32,6 +33,7 @@ private static void ComputeSpecifics(TypeRewriteContext typeContext)
}

var fieldTypeContext = typeContext.AssemblyContext.GlobalContext.GetNewTypeForOriginal(fieldType.Resolve());
if (fieldTypeContext == null) return;
ComputeSpecifics(fieldTypeContext);
if (fieldTypeContext.ComputedTypeSpecifics != TypeRewriteContext.TypeSpecifics.BlittableStruct)
{
Expand Down