Skip to content

Commit

Permalink
Add token and address attributes
Browse files Browse the repository at this point in the history
  • Loading branch information
Sam Byass committed Sep 6, 2020
1 parent 238ac59 commit 99f1aed
Show file tree
Hide file tree
Showing 4 changed files with 139 additions and 9 deletions.
139 changes: 131 additions & 8 deletions Cpp2IL/AssemblyBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,20 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using Cpp2IL.Analysis;
using Cpp2IL.Metadata;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Mono.Cecil.Rocks;
using CustomAttributeNamedArgument = Mono.Cecil.CustomAttributeNamedArgument;
using EventAttributes = Mono.Cecil.EventAttributes;
using FieldAttributes = Mono.Cecil.FieldAttributes;
using MethodAttributes = Mono.Cecil.MethodAttributes;
using ParameterAttributes = Mono.Cecil.ParameterAttributes;
using PropertyAttributes = Mono.Cecil.PropertyAttributes;
using TypeAttributes = Mono.Cecil.TypeAttributes;

namespace Cpp2IL
{
Expand Down Expand Up @@ -116,13 +124,71 @@ public static void ConfigureHierarchy(Il2CppMetadata metadata, PE.PE theDll)
}
}

private static void CreateDefaultConstructor(TypeDefinition typeDefinition)
{
var module = typeDefinition.Module;
var defaultConstructor = new MethodDefinition(
".ctor",
MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName,
module.ImportReference(typeof(void))
);

var processor = defaultConstructor.Body.GetILProcessor();
processor.Emit(OpCodes.Ldarg_0);
processor.Emit(OpCodes.Call, module.ImportReference(typeof(Attribute).GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance)[0]));
processor.Emit(OpCodes.Ret);

typeDefinition.Methods.Add(defaultConstructor);
}

private static void InjectCustomAttributes(AssemblyDefinition imageDef)
{
//From il2cppdumper. Credit perfare
var namespaceName = "Cpp2IlInjected";

var stringTypeReference = imageDef.MainModule.ImportReference(typeof(string));
var attributeTypeReference = imageDef.MainModule.ImportReference(typeof(Attribute));

var addressAttribute = new TypeDefinition(namespaceName, "AddressAttribute", (TypeAttributes) 0x100001, attributeTypeReference);
addressAttribute.Fields.Add(new FieldDefinition("RVA", FieldAttributes.Public, stringTypeReference));
addressAttribute.Fields.Add(new FieldDefinition("Offset", FieldAttributes.Public, stringTypeReference));
addressAttribute.Fields.Add(new FieldDefinition("VA", FieldAttributes.Public, stringTypeReference));
addressAttribute.Fields.Add(new FieldDefinition("Slot", FieldAttributes.Public, stringTypeReference));
imageDef.MainModule.Types.Add(addressAttribute);
CreateDefaultConstructor(addressAttribute);

var fieldOffsetAttribute = new TypeDefinition(namespaceName, "FieldOffsetAttribute", (TypeAttributes) 0x100001, attributeTypeReference);
fieldOffsetAttribute.Fields.Add(new FieldDefinition("Offset", FieldAttributes.Public, stringTypeReference));
imageDef.MainModule.Types.Add(fieldOffsetAttribute);
CreateDefaultConstructor(fieldOffsetAttribute);

var attributeAttribute = new TypeDefinition(namespaceName, "AttributeAttribute", (TypeAttributes) 0x100001, attributeTypeReference);
attributeAttribute.Fields.Add(new FieldDefinition("Name", FieldAttributes.Public, stringTypeReference));
attributeAttribute.Fields.Add(new FieldDefinition("RVA", FieldAttributes.Public, stringTypeReference));
attributeAttribute.Fields.Add(new FieldDefinition("Offset", FieldAttributes.Public, stringTypeReference));
imageDef.MainModule.Types.Add(attributeAttribute);
CreateDefaultConstructor(attributeAttribute);

var metadataOffsetAttribute = new TypeDefinition(namespaceName, "MetadataOffsetAttribute", (TypeAttributes) 0x100001, attributeTypeReference);
metadataOffsetAttribute.Fields.Add(new FieldDefinition("Offset", FieldAttributes.Public, stringTypeReference));
imageDef.MainModule.Types.Add(metadataOffsetAttribute);
CreateDefaultConstructor(metadataOffsetAttribute);

var tokenAttribute = new TypeDefinition(namespaceName, "TokenAttribute", (TypeAttributes) 0x100001, attributeTypeReference);
tokenAttribute.Fields.Add(new FieldDefinition("Token", FieldAttributes.Public, stringTypeReference));
imageDef.MainModule.Types.Add(tokenAttribute);
CreateDefaultConstructor(tokenAttribute);
}

public static List<(TypeDefinition type, List<CppMethodData> methods)> ProcessAssemblyTypes(Il2CppMetadata metadata, PE.PE theDll, Il2CppAssemblyDefinition imageDef)
{
var firstTypeDefinition = SharedState.TypeDefsByIndex[imageDef.firstTypeIndex];
var currentAssembly = firstTypeDefinition.Module.Assembly;

InjectCustomAttributes(currentAssembly);

//Ensure type directory exists
if(!Program.CommandLineOptions.SkipMetadataTextFiles && !Program.CommandLineOptions.SkipAnalysis)
if (!Program.CommandLineOptions.SkipMetadataTextFiles && !Program.CommandLineOptions.SkipAnalysis)
Directory.CreateDirectory(Path.Combine(Path.GetFullPath("cpp2il_out"), "types", currentAssembly.Name.Name));

var lastTypeIndex = imageDef.firstTypeIndex + imageDef.typeCount;
Expand All @@ -142,6 +208,7 @@ public static void ConfigureHierarchy(Il2CppMetadata metadata, PE.PE theDll)

private static List<CppMethodData> ProcessTypeContents(Il2CppMetadata metadata, PE.PE cppAssembly, Il2CppTypeDefinition cppTypeDefinition, TypeDefinition ilTypeDefinition, Il2CppAssemblyDefinition imageDef)
{
var imageName = metadata.GetStringFromIndex(imageDef.nameIndex);
var typeMetaText = new StringBuilder();
typeMetaText.Append($"Type: {ilTypeDefinition.FullName}:")
.Append($"\n\tBase Class: \n\t\t{ilTypeDefinition.BaseType}\n")
Expand All @@ -152,6 +219,18 @@ private static List<CppMethodData> ProcessTypeContents(Il2CppMetadata metadata,
typeMetaText.Append($"\t\t{iface.InterfaceType.FullName}\n");
}

var addressAttribute = ilTypeDefinition.Module.Types.First(x => x.Name == "AddressAttribute").Methods[0];
var fieldOffsetAttribute = ilTypeDefinition.Module.Types.First(x => x.FullName == "Cpp2IlInjected.FieldOffsetAttribute").Methods[0];
var attributeAttribute = ilTypeDefinition.Module.Types.First(x => x.Name == "AttributeAttribute").Methods[0];
var metadataOffsetAttribute = ilTypeDefinition.Module.Types.First(x => x.Name == "MetadataOffsetAttribute").Methods[0];
var tokenAttribute = ilTypeDefinition.Module.Types.First(x => x.Name == "TokenAttribute").Methods[0];
var stringType = ilTypeDefinition.Module.ImportReference(typeof(string));

//Token attribute
var customTokenAttribute = new CustomAttribute(ilTypeDefinition.Module.ImportReference(tokenAttribute));
customTokenAttribute.Fields.Add(new CustomAttributeNamedArgument("Token", new CustomAttributeArgument(stringType, $"0x{cppTypeDefinition.token:X}")));
ilTypeDefinition.CustomAttributes.Add(customTokenAttribute);

//field
var fields = new List<FieldInType>();

Expand All @@ -161,9 +240,9 @@ private static List<CppMethodData> ProcessTypeContents(Il2CppMetadata metadata,
while (current.BaseType != null)
{
var targetName = current.BaseType.FullName;
if (targetName.Contains("<")) // types with generic parameters (Type'1<T>) are stored as Type'1, so I just removed the part that causes trouble and called it a day
targetName = targetName.Substring(0,targetName.IndexOf("<"));
if (targetName.Contains("<")) // types with generic parameters (Type'1<T>) are stored as Type'1, so I just removed the part that causes trouble and called it a day
targetName = targetName.Substring(0, targetName.IndexOf("<"));

current = SharedState.AllTypeDefinitions.Find(t => t.FullName == targetName);

if (current == null)
Expand All @@ -172,7 +251,7 @@ private static List<CppMethodData> ProcessTypeContents(Il2CppMetadata metadata,
break;
}

baseFields.InsertRange(0,current.Fields.Where(f => !f.IsStatic)); // each loop we go one inheritage level deeper, so these "new" fields should be inserted before the previous ones
baseFields.InsertRange(0, current.Fields.Where(f => !f.IsStatic)); // each loop we go one inheritage level deeper, so these "new" fields should be inserted before the previous ones
}

//Handle base fields
Expand Down Expand Up @@ -200,8 +279,17 @@ private static List<CppMethodData> ProcessTypeContents(Il2CppMetadata metadata,
}
}

customTokenAttribute = new CustomAttribute(ilTypeDefinition.Module.ImportReference(tokenAttribute));
customTokenAttribute.Fields.Add(new CustomAttributeNamedArgument("Token", new CustomAttributeArgument(stringType, $"0x{fieldDef.token:X}")));
fieldDefinition.CustomAttributes.Add(customTokenAttribute);

if (!fieldDefinition.IsStatic)
fieldOffset = HandleField(fieldTypeRef, fieldOffset, fieldName, fieldDefinition, ref fields, typeMetaText);

var customAttribute = new CustomAttribute(ilTypeDefinition.Module.ImportReference(metadataOffsetAttribute));
var offset = new CustomAttributeNamedArgument("Offset", new CustomAttributeArgument(stringType, $"0x{fieldOffset:X}"));
customAttribute.Fields.Add(offset);
fieldDefinition.CustomAttributes.Add(customAttribute);
}

fields.Sort(); //By offset
Expand Down Expand Up @@ -252,6 +340,11 @@ private static List<CppMethodData> ProcessTypeContents(Il2CppMetadata metadata,

ilTypeDefinition.Methods.Add(methodDefinition);
methodDefinition.ReturnType = Utils.ImportTypeInto(methodDefinition, methodReturnType, cppAssembly, metadata);

customTokenAttribute = new CustomAttribute(ilTypeDefinition.Module.ImportReference(tokenAttribute));
customTokenAttribute.Fields.Add(new CustomAttributeNamedArgument("Token", new CustomAttributeArgument(stringType, $"0x{methodDef.token:X}")));
methodDefinition.CustomAttributes.Add(customTokenAttribute);

if (methodDefinition.HasBody && ilTypeDefinition.BaseType?.FullName != "System.MulticastDelegate")
{
var ilprocessor = methodDefinition.Body.GetILProcessor();
Expand Down Expand Up @@ -284,6 +377,26 @@ private static List<CppMethodData> ProcessTypeContents(Il2CppMetadata metadata,
.Append($"\t\t\tType: {(parameterTypeRef.Namespace == "" ? "<None>" : parameterTypeRef.Namespace)}.{parameterTypeRef.Name}\n")
.Append($"\t\t\tDefault Value: {parameterDefinition.Constant}");
}

//Address attribute
var methodPointer = Program.ThePE.GetMethodPointer(methodDef.methodIndex, methodId, imageDef.assemblyIndex, methodDef.token);
if (methodPointer > 0)
{
var customAttribute = new CustomAttribute(ilTypeDefinition.Module.ImportReference(addressAttribute));
var fixedMethodPointer = Program.ThePE.GetRVA(methodPointer);
var rva = new CustomAttributeNamedArgument("RVA", new CustomAttributeArgument(stringType, $"0x{fixedMethodPointer:X}"));
var offsetArg = new CustomAttributeNamedArgument("Offset", new CustomAttributeArgument(stringType, $"0x{Program.ThePE.MapVirtualAddressToRaw(methodPointer):X}"));
var va = new CustomAttributeNamedArgument("VA", new CustomAttributeArgument(stringType, $"0x{methodPointer:X}"));
customAttribute.Fields.Add(rva);
customAttribute.Fields.Add(offsetArg);
customAttribute.Fields.Add(va);
if (methodDef.slot != ushort.MaxValue)
{
var slot = new CustomAttributeNamedArgument("Slot", new CustomAttributeArgument(stringType, methodDef.slot.ToString()));
customAttribute.Fields.Add(slot);
}
methodDefinition.CustomAttributes.Add(customAttribute);
}

if (methodDef.genericContainerIndex >= 0)
{
Expand Down Expand Up @@ -348,6 +461,11 @@ private static List<CppMethodData> ProcessTypeContents(Il2CppMetadata metadata,
GetMethod = getter,
SetMethod = setter
};

customTokenAttribute = new CustomAttribute(ilTypeDefinition.Module.ImportReference(tokenAttribute));
customTokenAttribute.Fields.Add(new CustomAttributeNamedArgument("Token", new CustomAttributeArgument(stringType, $"0x{propertyDef.token:X}")));
propertyDefinition.CustomAttributes.Add(customTokenAttribute);

ilTypeDefinition.Properties.Add(propertyDefinition);
}

Expand All @@ -366,10 +484,15 @@ private static List<CppMethodData> ProcessTypeContents(Il2CppMetadata metadata,
eventDefinition.RemoveMethod = SharedState.MethodsByIndex[cppTypeDefinition.firstMethodId + eventDef.remove];
if (eventDef.raise >= 0)
eventDefinition.InvokeMethod = SharedState.MethodsByIndex[cppTypeDefinition.firstMethodId + eventDef.raise];

customTokenAttribute = new CustomAttribute(ilTypeDefinition.Module.ImportReference(tokenAttribute));
customTokenAttribute.Fields.Add(new CustomAttributeNamedArgument("Token", new CustomAttributeArgument(stringType, $"0x{eventDef.token:X}")));
eventDefinition.CustomAttributes.Add(customTokenAttribute);

ilTypeDefinition.Events.Add(eventDefinition);
}

if(!Program.CommandLineOptions.SkipMetadataTextFiles)
if (!Program.CommandLineOptions.SkipMetadataTextFiles)
File.WriteAllText(Path.Combine(Path.GetFullPath("cpp2il_out"), "types", ilTypeDefinition.Module.Assembly.Name.Name, ilTypeDefinition.Name.Replace("<", "_").Replace(">", "_").Replace("|", "_") + "_metadata.txt"), typeMetaText.ToString());

if (cppTypeDefinition.genericContainerIndex < 0) return typeMethods; //Finished processing if not generic
Expand Down Expand Up @@ -408,7 +531,7 @@ private static ulong HandleField(TypeReference fieldTypeRef, ulong fieldOffset,
//TODO: 32-bit is boundaries of four and default length should be the same
fieldOffset = (ulong) ((Math.Floor(fieldOffset / (decimal) 8) + 1) * 8);
}

//ONE correction. String#start_char is remapped to a char[] not a char because the block allocated for all chars is directly sequential to the length of the string, because that's how c++ works.
if (fieldDefinition.DeclaringType.FullName == "System.String" && fieldTypeRef.FullName == "System.Char")
fieldTypeRef = fieldTypeRef.MakeArrayType();
Expand Down Expand Up @@ -529,4 +652,4 @@ internal static List<GlobalIdentifier> MapGlobalIdentifiers(Il2CppMetadata metad
return ret;
}
}
}
}
5 changes: 5 additions & 0 deletions Cpp2IL/PE/PE.cs
Original file line number Diff line number Diff line change
Expand Up @@ -665,5 +665,10 @@ public ulong GetVirtualAddressOfUnmanagedExportByName(string toFind)

return functionPointer + imageBase;
}

public ulong GetRVA(ulong pointer)
{
return pointer - imageBase;
}
}
}
3 changes: 2 additions & 1 deletion Cpp2IL/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ public static void Main(string[] args)

if (CommandLineOptions == null)
{
Console.WriteLine("Invalid command line. Exiting.");
return;
}

Expand Down Expand Up @@ -162,7 +163,7 @@ public static void Main(string[] args)
}

var unityVer = verString.ToString();
unityVer = unityVer.Substring(0, unityVer.IndexOf("f"));
unityVer = unityVer.Substring(0, unityVer.IndexOf("f", StringComparison.Ordinal));
Console.WriteLine("Read version string from globalgamemanagers: " + unityVer);
unityVerUseful = unityVer.Split(".").Select(int.Parse).ToArray();
}
Expand Down
1 change: 1 addition & 0 deletions Cpp2IL/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

// General Information about an assembly is controlled through the following
Expand Down

0 comments on commit 99f1aed

Please sign in to comment.