public static void DoPass(RewriteGlobalContext context, UnhollowerOptions options) { var registerMethod = typeof(DefaultAssemblyResolver).GetMethod("RegisterAssembly", BindingFlags.Instance | BindingFlags.NonPublic); foreach (AssemblyRewriteContext asmContext in context.Assemblies) { var module = asmContext.NewAssembly.MainModule; if (module.AssemblyResolver is DefaultAssemblyResolver resolver) { foreach (AssemblyNameReference reference in module.AssemblyReferences) { var match = context.Assemblies.FirstOrDefault(f => f.NewAssembly.FullName == reference.FullName); if (match != null) { registerMethod !.Invoke(resolver, new object[] { match.NewAssembly }); } } } } var tasks = context.Assemblies.Where(it => !options.AdditionalAssembliesBlacklist.Contains(it.NewAssembly.Name.Name)).Select(assemblyContext => Task.Run(() => { assemblyContext.NewAssembly.Write(options.OutputDir + "/" + assemblyContext.NewAssembly.Name.Name + ".dll"); })).ToArray(); Task.WaitAll(tasks); }
public static void DoPass(RewriteGlobalContext context, UnhollowerOptions options) { var tasks = context.Assemblies.Where(it => !options.AdditionalAssembliesBlacklist.Contains(it.NewAssembly.Name.Name)).Select(assemblyContext => Task.Run(() => { assemblyContext.NewAssembly.Write(options.OutputDir + "/" + assemblyContext.NewAssembly.Name.Name + ".dll"); })).ToArray(); Task.WaitAll(tasks); }
public void GenerateAssembliesInternal(AppDomainListener listener) { listener.DoPreloaderLog("Generating Il2CppUnhollower assemblies", LogLevel.Message); Directory.CreateDirectory(Preloader.IL2CPPUnhollowedPath); foreach (var dllFile in Directory.EnumerateFiles(Preloader.IL2CPPUnhollowedPath, "*.dll", SearchOption.TopDirectoryOnly)) { File.Delete(dllFile); } var tempDumperDirectory = Path.Combine(Preloader.IL2CPPUnhollowedPath, "temp"); Directory.CreateDirectory(tempDumperDirectory); var dumperConfig = new Config { GenerateScript = false, GenerateDummyDll = true }; listener.DoPreloaderLog("Generating Il2CppDumper intermediate assemblies", LogLevel.Info); Il2CppDumper.Il2CppDumper.PerformDump(GameAssemblyPath, Path.Combine(Paths.GameRootPath, $"{Paths.ProcessName}_Data", "il2cpp_data", "Metadata", "global-metadata.dat"), tempDumperDirectory, dumperConfig, s => listener.DoDumperLog(s, LogLevel.Debug)); listener.DoPreloaderLog("Executing Il2CppUnhollower generator", LogLevel.Info); LogSupport.InfoHandler += s => listener.DoUnhollowerLog(s, LogLevel.Info); LogSupport.WarningHandler += s => listener.DoUnhollowerLog(s, LogLevel.Warning); LogSupport.TraceHandler += s => listener.DoUnhollowerLog(s, LogLevel.Debug); LogSupport.ErrorHandler += s => listener.DoUnhollowerLog(s, LogLevel.Error); var unhollowerOptions = new UnhollowerOptions { GameAssemblyPath = GameAssemblyPath, MscorlibPath = Path.Combine(Paths.ManagedPath, "mscorlib.dll"), SourceDir = Path.Combine(tempDumperDirectory, "DummyDll"), OutputDir = Preloader.IL2CPPUnhollowedPath, UnityBaseLibsDir = Directory.Exists(UnityBaseLibsDirectory) ? UnityBaseLibsDirectory : null, NoCopyUnhollowerLibs = true }; try { Program.Main(unhollowerOptions); } catch (Exception e) { listener.DoUnhollowerLog($"Exception while unhollowing: {e}", LogLevel.Error); } }
public static string UnSystemify(this string str, UnhollowerOptions options) { foreach (var prefix in options.NamespacesAndAssembliesToPrefix) { if (str.StartsWith(prefix)) { return("Il2Cpp" + str); } } return(str); }
public static bool IsObfuscated(this string str, UnhollowerOptions options) { if (options.ObfuscatedNamesRegex != null) { return(options.ObfuscatedNamesRegex.IsMatch(str)); } foreach (var it in str) { if (!char.IsDigit(it) && !(it >= 'a' && it <= 'z' || it >= 'A' && it <= 'Z') && it != '_' && it != '`' && it != '.' && it != '<' && it != '>') { return(true); } } return(false); }
public override void Run(BuildContext context) { var dumperConfig = new Il2CppDumper.Config { //GenerateScript = false, GenerateDummyDll = true }; Console.WriteLine("Test2"); context.Information("Generating Il2CppDumper intermediate assemblies"); var gameAssemblyPath = Path.Combine(context.AmongUsPath, "GameAssembly.dll"); if (!Directory.Exists(context.TempPath)) { Directory.CreateDirectory(context.TempPath); } Il2CppDumper.Il2CppDumper.PerformDump( gameAssemblyPath, Path.Combine(context.AmongUsPath, "Among Us_Data", "il2cpp_data", "Metadata", "global-metadata.dat"), context.TempPath, dumperConfig, context.Debug ); context.Information("Executing Il2CppUnhollower generator"); UnhollowerBaseLib.LogSupport.InfoHandler += context.Information; UnhollowerBaseLib.LogSupport.WarningHandler += context.Warning; UnhollowerBaseLib.LogSupport.TraceHandler += context.Debug; UnhollowerBaseLib.LogSupport.ErrorHandler += context.Error; var unhollowerOptions = new UnhollowerOptions { GameAssemblyPath = gameAssemblyPath, MscorlibPath = Path.Combine(context.AmongUsPath, "mono", "Managed", "mscorlib.dll"), SourceDir = Path.Combine(context.TempPath, "DummyDll"), OutputDir = Path.Combine(context.AmongUsPath, "BepInEx", "unhollowed"), UnityBaseLibsDir = Path.Combine(context.AmongUsPath, "BepInEx", "unity-libs"), NoCopyUnhollowerLibs = true }; AssemblyUnhollower.Program.Main(unhollowerOptions); }
private string UnmangleFieldNameBase(FieldDefinition field, UnhollowerOptions options) { if (options.PassthroughNames) { return(field.Name); } if (!field.Name.IsObfuscated(options)) { if (!field.Name.IsInvalidInSource()) { return(field.Name); } return(field.Name.FilterInvalidInSourceChars()); } var accessModString = MethodAccessTypeLabels[(int)(field.Attributes & FieldAttributes.FieldAccessMask)]; var staticString = field.IsStatic ? "_Static" : ""; return("field_" + accessModString + staticString + "_" + DeclaringType.AssemblyContext.RewriteTypeRef(field.FieldType).GetUnmangledName()); }
private string UnmangleFieldName(FieldDefinition field, UnhollowerOptions options, Dictionary <string, int>?renamedFieldCounts) { if (options.PassthroughNames) { return(field.Name); } if (!field.Name.IsObfuscated(options)) { if (!field.Name.IsInvalidInSource()) { return(field.Name); } return(field.Name.FilterInvalidInSourceChars()); } if (renamedFieldCounts == null) { throw new ArgumentNullException(nameof(renamedFieldCounts)); } var unmangleFieldNameBase = UnmangleFieldNameBase(field, options); renamedFieldCounts.TryGetValue(unmangleFieldNameBase, out var count); renamedFieldCounts[unmangleFieldNameBase] = count + 1; unmangleFieldNameBase += "_" + count; if (DeclaringType.AssemblyContext.GlobalContext.Options.RenameMap.TryGetValue( DeclaringType.NewType.GetNamespacePrefix() + "." + DeclaringType.NewType.Name + "::" + unmangleFieldNameBase, out var newName)) { unmangleFieldNameBase = newName; } return(unmangleFieldNameBase); }
public static void DoPass(RewriteGlobalContext context, UnhollowerOptions options) { if (string.IsNullOrEmpty(options.GameAssemblyPath)) { Pass15GenerateMemberContexts.HasObfuscatedMethods = false; return; } if (!Pass15GenerateMemberContexts.HasObfuscatedMethods) { return; } var methodToCallersMap = new Dictionary <long, List <long> >(); var methodToCalleesMap = new Dictionary <long, List <long> >(); using var mappedFile = MemoryMappedFile.CreateFromFile(options.GameAssemblyPath, FileMode.Open, null, 0, MemoryMappedFileAccess.Read); using var accessor = mappedFile.CreateViewAccessor(0, 0, MemoryMappedFileAccess.Read); IntPtr gameAssemblyPtr; unsafe { byte *fileStartPtr = null; accessor.SafeMemoryMappedViewHandle.AcquirePointer(ref fileStartPtr); gameAssemblyPtr = (IntPtr)fileStartPtr; } // Scan xrefs foreach (var assemblyRewriteContext in context.Assemblies) { foreach (var typeRewriteContext in assemblyRewriteContext.Types) { foreach (var originalTypeMethod in typeRewriteContext.Methods) { var address = originalTypeMethod.FileOffset; if (address == 0) { continue; } foreach (var callTarget in XrefScannerLowLevel.CallAndIndirectTargets(IntPtr.Add(gameAssemblyPtr, (int)address))) { var targetRelative = (long)callTarget - (long)gameAssemblyPtr; methodToCallersMap.GetOrCreate(targetRelative, _ => new List <long>()).Add(address); methodToCalleesMap.GetOrCreate(address, _ => new List <long>()).Add(targetRelative); } } } } MapOfCallers = methodToCallersMap; void MarkMethodAlive(long address) { if (!NonDeadMethods.Add(address)) { return; } if (!methodToCalleesMap.TryGetValue(address, out var calleeList)) { return; } foreach (var callee in calleeList) { MarkMethodAlive(callee); } } // Now decided which of them are possible dead code foreach (var assemblyRewriteContext in context.Assemblies) { foreach (var typeRewriteContext in assemblyRewriteContext.Types) { foreach (var methodRewriteContext in typeRewriteContext.Methods) { if (methodRewriteContext.FileOffset == 0) { continue; } var originalMethod = methodRewriteContext.OriginalMethod; if (!originalMethod.Name.IsObfuscated() || originalMethod.IsVirtual) { MarkMethodAlive(methodRewriteContext.FileOffset); } } } } }
public static void DoPass(RewriteGlobalContext context, UnhollowerOptions options) { var data = new List <(long, int, int)>(); var assemblyList = new List <string>(); foreach (var assemblyRewriteContext in context.Assemblies) { assemblyList.Add(assemblyRewriteContext.NewAssembly.FullName); foreach (var typeRewriteContext in assemblyRewriteContext.Types) { foreach (var methodRewriteContext in typeRewriteContext.Methods) { var address = methodRewriteContext.Rva; if (address != 0) { data.Add((address, methodRewriteContext.NewMethod.MetadataToken.ToInt32(), assemblyList.Count - 1)); } } } } data.Sort((a, b) => a.Item1.CompareTo(b.Item1)); var header = new MethodAddressToTokenMap.FileHeader { Magic = MethodAddressToTokenMap.Magic, Version = MethodAddressToTokenMap.Version, NumMethods = data.Count, NumAssemblies = assemblyList.Count }; using var writer = new BinaryWriter(new FileStream(Path.Combine(options.OutputDir, MethodAddressToTokenMap.FileName), FileMode.Create, FileAccess.Write), Encoding.UTF8, false); writer.Write(header); foreach (var s in assemblyList) { writer.Write(s); } header.DataOffset = (int)writer.BaseStream.Position; foreach (var valueTuple in data) { writer.Write(valueTuple.Item1); } foreach (var valueTuple in data) { writer.Write(valueTuple.Item2); writer.Write(valueTuple.Item3); } writer.BaseStream.Position = 0; writer.Write(header); if (options.Verbose) { using var plainTextWriter = new StreamWriter(Path.Combine(options.OutputDir, MethodAddressToTokenMap.FileName + ".txt")); for (var i = 0; i < data.Count; i++) { plainTextWriter.WriteLine($"{i}\t{data[i].Item1}\t{data[i].Item2}\t{data[i].Item3}"); } } }
public static void DoPass(RewriteGlobalContext context, UnhollowerOptions options) { if (string.IsNullOrEmpty(options.GameAssemblyPath)) { Pass15GenerateMemberContexts.HasObfuscatedMethods = false; return; } using var mappedFile = MemoryMappedFile.CreateFromFile(options.GameAssemblyPath, FileMode.Open, null, 0, MemoryMappedFileAccess.Read); using var accessor = mappedFile.CreateViewAccessor(0, 0, MemoryMappedFileAccess.Read); IntPtr gameAssemblyPtr; unsafe { byte *fileStartPtr = null; accessor.SafeMemoryMappedViewHandle.AcquirePointer(ref fileStartPtr); gameAssemblyPtr = (IntPtr)fileStartPtr; } context.HasGcWbarrierFieldWrite = FindByteSequence(gameAssemblyPtr, accessor.Capacity, nameof(IL2CPP.il2cpp_gc_wbarrier_set_field)); if (!Pass15GenerateMemberContexts.HasObfuscatedMethods) { return; } var methodToCallersMap = new ConcurrentDictionary <long, List <XrefInstance> >(); var methodToCalleesMap = new ConcurrentDictionary <long, List <long> >(); context.MethodStartAddresses.Sort(); // Scan xrefs context.Assemblies.SelectMany(it => it.Types).SelectMany(it => it.Methods).AsParallel().ForAll( originalTypeMethod => { var address = originalTypeMethod.FileOffset; if (address == 0) { return; } if (!options.NoXrefCache) { var pair = XrefScanMetadataGenerationUtil.FindMetadataInitForMethod(originalTypeMethod, (long)gameAssemblyPtr); originalTypeMethod.MetadataInitFlagRva = pair.FlagRva; originalTypeMethod.MetadataInitTokenRva = pair.TokenRva; } var nextMethodStart = context.MethodStartAddresses.BinarySearch(address + 1); if (nextMethodStart < 0) { nextMethodStart = ~nextMethodStart; } var length = nextMethodStart >= context.MethodStartAddresses.Count ? 1024 * 1024 : (context.MethodStartAddresses[nextMethodStart] - address); foreach (var callTargetGlobal in XrefScanner.XrefScanImpl(XrefScanner.DecoderForAddress(IntPtr.Add(gameAssemblyPtr, (int)address), (int)length), true)) { var callTarget = callTargetGlobal.RelativeToBase((long)gameAssemblyPtr + originalTypeMethod.FileOffset - originalTypeMethod.Rva); if (callTarget.Type == XrefType.Method) { var targetRelative = (long)callTarget.Pointer; methodToCallersMap.GetOrAdd(targetRelative, _ => new List <XrefInstance>()).AddLocked(new XrefInstance(XrefType.Method, (IntPtr)originalTypeMethod.Rva, callTarget.FoundAt)); methodToCalleesMap.GetOrAdd(originalTypeMethod.Rva, _ => new List <long>()).AddLocked(targetRelative); } if (!options.NoXrefCache) { originalTypeMethod.XrefScanResults.Add(callTarget); } } }); MapOfCallers = methodToCallersMap; void MarkMethodAlive(long address) { if (!NonDeadMethods.Add(address)) { return; } if (!methodToCalleesMap.TryGetValue(address, out var calleeList)) { return; } foreach (var callee in calleeList) { MarkMethodAlive(callee); } } // Now decided which of them are possible dead code foreach (var assemblyRewriteContext in context.Assemblies) { foreach (var typeRewriteContext in assemblyRewriteContext.Types) { foreach (var methodRewriteContext in typeRewriteContext.Methods) { if (methodRewriteContext.FileOffset == 0) { continue; } var originalMethod = methodRewriteContext.OriginalMethod; if (!originalMethod.Name.IsObfuscated(options) || originalMethod.IsVirtual) { MarkMethodAlive(methodRewriteContext.Rva); } } } } }
public static void DoPass(RewriteGlobalContext context, UnhollowerOptions options) { var data = new List <MethodXrefScanCache.MethodData>(); var existingAttributesPerAddress = new Dictionary <long, CachedScanResultsAttribute>(); if (options.NoXrefCache) { goto skipDataGather; } foreach (var assemblyRewriteContext in context.Assemblies) { if (options.AdditionalAssembliesBlacklist.Contains(assemblyRewriteContext.NewAssembly.Name.Name)) { continue; } var imports = assemblyRewriteContext.Imports; foreach (var typeRewriteContext in assemblyRewriteContext.Types) { foreach (var methodRewriteContext in typeRewriteContext.Methods) { var address = methodRewriteContext.Rva; if (existingAttributesPerAddress.TryGetValue(address, out var attribute)) { methodRewriteContext.NewMethod.CustomAttributes.Add( new CustomAttribute(imports.CachedScanResultsAttributeCtor) { Fields = { new CustomAttributeNamedArgument( nameof(CachedScanResultsAttribute.RefRangeStart), new CustomAttributeArgument(imports.Int, attribute.RefRangeStart)), new CustomAttributeNamedArgument( nameof(CachedScanResultsAttribute.RefRangeEnd), new CustomAttributeArgument(imports.Int, attribute.RefRangeEnd)), new CustomAttributeNamedArgument( nameof(CachedScanResultsAttribute.XrefRangeStart), new CustomAttributeArgument(imports.Int, attribute.RefRangeStart)), new CustomAttributeNamedArgument( nameof(CachedScanResultsAttribute.XrefRangeEnd), new CustomAttributeArgument(imports.Int, attribute.RefRangeEnd)), new CustomAttributeNamedArgument( nameof(CachedScanResultsAttribute.MetadataInitTokenRva), new CustomAttributeArgument(imports.Int, attribute.MetadataInitTokenRva)), new CustomAttributeNamedArgument( nameof(CachedScanResultsAttribute.MetadataInitFlagRva), new CustomAttributeArgument(imports.Int, attribute.MetadataInitFlagRva)), } }); continue; } var xrefStart = data.Count; foreach (var xrefScanResult in methodRewriteContext.XrefScanResults) { data.Add(MethodXrefScanCache.MethodData.FromXrefInstance(xrefScanResult)); } var xrefEnd = data.Count; var refStart = 0; var refEnd = 0; if (address != 0) { if (Pass16ScanMethodRefs.MapOfCallers.TryGetValue(address, out var callerMap)) { refStart = data.Count; foreach (var xrefInstance in callerMap) { data.Add(MethodXrefScanCache.MethodData.FromXrefInstance(xrefInstance)); } refEnd = data.Count; } } if (xrefEnd != xrefStart || refStart != refEnd) { methodRewriteContext.NewMethod.CustomAttributes.Add( new CustomAttribute(imports.CachedScanResultsAttributeCtor) { Fields = { new CustomAttributeNamedArgument( nameof(CachedScanResultsAttribute.RefRangeStart), new CustomAttributeArgument(imports.Int, refStart)), new CustomAttributeNamedArgument( nameof(CachedScanResultsAttribute.RefRangeEnd), new CustomAttributeArgument(imports.Int, refEnd)), new CustomAttributeNamedArgument( nameof(CachedScanResultsAttribute.XrefRangeStart), new CustomAttributeArgument(imports.Int, xrefStart)), new CustomAttributeNamedArgument( nameof(CachedScanResultsAttribute.XrefRangeEnd), new CustomAttributeArgument(imports.Int, xrefEnd)), new CustomAttributeNamedArgument( nameof(CachedScanResultsAttribute.MetadataInitTokenRva), new CustomAttributeArgument(imports.Long, methodRewriteContext.MetadataInitTokenRva)), new CustomAttributeNamedArgument( nameof(CachedScanResultsAttribute.MetadataInitFlagRva), new CustomAttributeArgument(imports.Long, methodRewriteContext.MetadataInitFlagRva)), } }); existingAttributesPerAddress[address] = new CachedScanResultsAttribute { RefRangeStart = refStart, RefRangeEnd = refEnd, XrefRangeStart = xrefStart, XrefRangeEnd = xrefEnd, MetadataInitFlagRva = methodRewriteContext.MetadataInitFlagRva, MetadataInitTokenRva = methodRewriteContext.MetadataInitTokenRva }; } } } } skipDataGather: var header = new MethodXrefScanCache.FileHeader { Magic = MethodXrefScanCache.Magic, Version = MethodXrefScanCache.Version, InitMethodMetadataRva = XrefScanMetadataGenerationUtil.MetadataInitForMethodRva }; using var writer = new BinaryWriter(new FileStream(Path.Combine(options.OutputDir, MethodXrefScanCache.FileName), FileMode.Create, FileAccess.Write), Encoding.UTF8, false); writer.Write(header); foreach (var valueTuple in data) { writer.Write(valueTuple); } if (options.Verbose) { using var plainTextWriter = new StreamWriter(Path.Combine(options.OutputDir, MethodXrefScanCache.FileName + ".txt")); for (var i = 0; i < data.Count; i++) { plainTextWriter.WriteLine($"{i}\t{data[i].Type}\t{data[i].Address}\t{data[i].FoundAt}"); } } }