From f2631d375b46e49d8d65a90710e8dc6c49e16c52 Mon Sep 17 00:00:00 2001 From: js6pak Date: Tue, 16 Feb 2021 15:29:09 +0100 Subject: [PATCH] Improve multi platform support, fix signatures again --- Reactor.Greenhouse/Extensions.cs | 8 ++ Reactor.Greenhouse/Generation/TypeContext.cs | 12 +-- Reactor.Greenhouse/Program.cs | 50 ++++--------- Reactor.Greenhouse/Reactor.Greenhouse.csproj | 1 - Reactor.Greenhouse/Setup/Game.cs | 32 +++++--- Reactor.Greenhouse/Setup/GameManager.cs | 59 ++++++--------- Reactor.Greenhouse/Setup/GameVersion.cs | 75 +++++++++++++++++++ .../Setup/Provider/AndroidProvider.cs | 45 +++++++++++ .../Setup/Provider/BaseProvider.cs | 6 ++ .../Setup/Provider/ItchProvider.cs | 22 ++++-- .../Setup/Provider/SteamProvider.cs | 43 +++++------ .../Reactor.OxygenFilter.MSBuild.csproj | 2 +- Reactor.OxygenFilter.MSBuild/Reobfuscate.cs | 24 +++++- Reactor.OxygenFilter/Extensions.cs | 66 ++++++++++++---- Reactor.OxygenFilter/Mappings.cs | 9 +++ .../Reactor.OxygenFilter.csproj | 5 +- 16 files changed, 315 insertions(+), 144 deletions(-) create mode 100644 Reactor.Greenhouse/Setup/GameVersion.cs create mode 100644 Reactor.Greenhouse/Setup/Provider/AndroidProvider.cs diff --git a/Reactor.Greenhouse/Extensions.cs b/Reactor.Greenhouse/Extensions.cs index 0437c88..4c815f5 100644 --- a/Reactor.Greenhouse/Extensions.cs +++ b/Reactor.Greenhouse/Extensions.cs @@ -1,5 +1,7 @@ using System; using System.Diagnostics; +using System.IO; +using System.IO.Compression; using System.Linq; using Mono.Cecil; @@ -36,5 +38,11 @@ public static TimeSpan Time(this Action action) _stopwatch.Stop(); return _stopwatch.Elapsed; } + + public static void ForceExtractToFile(this ZipArchiveEntry source, string destinationFileName) + { + Directory.GetParent(destinationFileName)!.Create(); + source.ExtractToFile(destinationFileName, true); + } } } diff --git a/Reactor.Greenhouse/Generation/TypeContext.cs b/Reactor.Greenhouse/Generation/TypeContext.cs index eb8b1a9..f7c4f74 100644 --- a/Reactor.Greenhouse/Generation/TypeContext.cs +++ b/Reactor.Greenhouse/Generation/TypeContext.cs @@ -102,11 +102,7 @@ public MappedType ToMappedType(TypeDefinition obfuscatedType) { var matched = matching.Single(); - mappedType.Methods.Add(new MappedMethod(new OriginalDescriptor - { - Name = matched.Name, - Signature = matched.GetSignature() - }, cleanMethod.Name)); + mappedType.Methods.Add(new MappedMethod(matched, cleanMethod.Name)); } } @@ -153,11 +149,7 @@ public MappedType ToMappedType(TypeDefinition obfuscatedType) if (methodMatch.Success) { - nested.Methods.Add(new MappedMethod(new OriginalDescriptor - { - Name = nestedMethod.Name, - Signature = nestedMethod.GetSignature() - }, methodMatch.Groups[1].Value)); + nested.Methods.Add(new MappedMethod(nestedMethod, methodMatch.Groups[1].Value)); } } diff --git a/Reactor.Greenhouse/Program.cs b/Reactor.Greenhouse/Program.cs index b484c71..d83c742 100644 --- a/Reactor.Greenhouse/Program.cs +++ b/Reactor.Greenhouse/Program.cs @@ -1,8 +1,6 @@ using System; using System.Collections; using System.Collections.Generic; -using System.CommandLine; -using System.CommandLine.Invocation; using System.IO; using System.Linq; using System.Reflection; @@ -12,31 +10,25 @@ using Newtonsoft.Json.Serialization; using Reactor.Greenhouse.Generation; using Reactor.Greenhouse.Setup; -using Reactor.Greenhouse.Setup.Provider; using Reactor.OxygenFilter; namespace Reactor.Greenhouse { internal static class Program { - private static Task Main(string[] args) + public static async Task Main(string[] args) { - var rootCommand = new RootCommand + if (!args.Any()) { - new Option("steam"), - new Option("itch"), - }; + Console.WriteLine("No game versions used!"); + return; + } - rootCommand.Handler = CommandHandler.Create(GenerateAsync); + var gameVersions = args.Select(x => new GameVersion(x)).ToArray(); - return rootCommand.InvokeAsync(args); - } + var gameManager = new GameManager(gameVersions); - public static async Task GenerateAsync(bool steam, bool itch) - { - var gameManager = new GameManager(); - - await gameManager.SetupAsync(steam, itch); + await gameManager.SetupAsync(); JsonConvert.DefaultSettings = () => new JsonSerializerSettings { @@ -50,42 +42,30 @@ public static async Task GenerateAsync(bool steam, bool itch) Console.WriteLine($"Generating mappings from {oldFile}"); using var cleanModule = ModuleDefinition.ReadModule(File.OpenRead(oldFile)); - if (!steam && !itch) - { - Console.WriteLine("No game providers used! (use --help for more info)"); - return; - } - - if (steam) - { - await GenerateAsync(gameManager.Steam, cleanModule); - } - - if (itch) + foreach (var game in gameManager.Games) { - await GenerateAsync(gameManager.Itch, cleanModule); + await GenerateAsync(game, cleanModule); } } private static async Task GenerateAsync(Game game, ModuleDefinition cleanModule) { - Console.WriteLine($"Compiling mappings for {game.Name} ({game.Version})"); + var version = game.Provider.Version; + Console.WriteLine($"Compiling mappings for {game.Name} ({version})"); using var moduleDef = ModuleDefinition.ReadModule(File.OpenRead(game.Dll)); - var version = game.Version; - var postfix = game.Postfix; var generated = Generator.Generate(new GenerationContext(cleanModule, moduleDef)); - await File.WriteAllTextAsync(Path.Combine("work", version + postfix + ".generated.json"), JsonConvert.SerializeObject(generated, Formatting.Indented)); + await File.WriteAllTextAsync(Path.Combine("work", version + ".generated.json"), JsonConvert.SerializeObject(generated, Formatting.Indented)); Apply(generated, Path.Combine("universal.json")); - Apply(generated, Path.Combine(version + postfix + ".json")); + Apply(generated, Path.Combine(version + ".json")); generated.Compile(moduleDef); Directory.CreateDirectory(Path.Combine("bin")); - await File.WriteAllTextAsync(Path.Combine("bin", version + postfix + ".json"), JsonConvert.SerializeObject(generated)); + await File.WriteAllTextAsync(Path.Combine("bin", version + ".json"), JsonConvert.SerializeObject(generated)); } private static void Apply(Mappings generated, string file) diff --git a/Reactor.Greenhouse/Reactor.Greenhouse.csproj b/Reactor.Greenhouse/Reactor.Greenhouse.csproj index d7c7d66..c4735d8 100644 --- a/Reactor.Greenhouse/Reactor.Greenhouse.csproj +++ b/Reactor.Greenhouse/Reactor.Greenhouse.csproj @@ -12,7 +12,6 @@ - diff --git a/Reactor.Greenhouse/Setup/Game.cs b/Reactor.Greenhouse/Setup/Game.cs index 6ce2ef4..dddee80 100644 --- a/Reactor.Greenhouse/Setup/Game.cs +++ b/Reactor.Greenhouse/Setup/Game.cs @@ -13,17 +13,14 @@ public class Game { public BaseProvider Provider { get; } public string Name { get; } - public char Postfix { get; } public string Path { get; } public string Dll { get; } - public string Version { get; private set; } public Game(BaseProvider provider, string name, string path) { Provider = provider; provider.Game = this; Name = name; - Postfix = char.ToLowerInvariant(name[0]); Path = path; Dll = IOPath.Combine(Path, "DummyDll", "Assembly-CSharp.dll"); } @@ -32,23 +29,36 @@ public async Task DownloadAsync() { Provider.Setup(); await Provider.DownloadAsync(); - UpdateVersion(); + CheckVersion(); } - public void UpdateVersion() + public void CheckVersion() { - if (Version != null) - return; + if (Provider.Version.Platform == GamePlatform.Android) + { + return; // TODO + } + + var version = GameVersionParser.Parse(System.IO.Path.Combine(Path, "Among Us_Data", "globalgamemanagers")); - Version = GameVersionParser.Parse(System.IO.Path.Combine(Path, "Among Us_Data", "globalgamemanagers")); + if (!Provider.Version.Equals(new GameVersion(version), true)) + { + throw new Exception("Downloaded game has invalid version"); + } } public void Dump() { Console.WriteLine($"Dumping {Name}"); - var hash = ComputeHash(IOPath.Combine(Path, "GameAssembly.dll")); - var hashFile = IOPath.Combine(Path, "Assembly-CSharp.dll.md5"); + var gameAssembly = Provider.Version.Platform switch + { + GamePlatform.Android => "libil2cpp.so", + _ => "GameAssembly.dll" + }; + + var hash = ComputeHash(IOPath.Combine(Path, gameAssembly)); + var hashFile = IOPath.Combine(Path, gameAssembly + ".md5"); if (File.Exists(hashFile) && File.ReadAllText(hashFile) == hash) { @@ -56,7 +66,7 @@ public void Dump() } if (!Il2CppDumper.Il2CppDumper.PerformDump( - IOPath.Combine(Path, "GameAssembly.dll"), + IOPath.Combine(Path, gameAssembly), IOPath.Combine(Path, "Among Us_Data", "il2cpp_data", "Metadata", "global-metadata.dat"), Path, new Config diff --git a/Reactor.Greenhouse/Setup/GameManager.cs b/Reactor.Greenhouse/Setup/GameManager.cs index 35aa6b9..984bcb4 100644 --- a/Reactor.Greenhouse/Setup/GameManager.cs +++ b/Reactor.Greenhouse/Setup/GameManager.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using System.Linq; using System.Threading.Tasks; using DepotDownloader; using Reactor.Greenhouse.Setup.Provider; @@ -10,58 +11,40 @@ public class GameManager { public string WorkPath { get; } - public Game Steam { get; } - public Game Itch { get; } + public Game[] Games { get; } - public GameManager() + public GameManager(GameVersion[] gameVersions) { WorkPath = Path.GetFullPath("work"); - Steam = new Game(new SteamProvider(false), "steam", Path.Combine(WorkPath, "steam")); - Itch = new Game(new ItchProvider(), "itch", Path.Combine(WorkPath, "itch")); + + Games = gameVersions.Select(gameVersion => new Game( + gameVersion.Platform switch + { + GamePlatform.Steam => new SteamProvider(gameVersion), + GamePlatform.Itch => new ItchProvider(gameVersion), + GamePlatform.Android => new AndroidProvider(gameVersion), + _ => throw new ArgumentOutOfRangeException(nameof(gameVersions)) + }, gameVersion.ToString(), Path.Combine(WorkPath, gameVersion.ToString()) + )).ToArray(); } - public async Task SetupAsync(bool setupSteam, bool setupItch) + public async Task SetupAsync() { - var steam = setupSteam && Steam.Provider.IsUpdateNeeded(); - var itch = setupItch && Itch.Provider.IsUpdateNeeded(); - - if (steam || itch) + foreach (var game in Games) { - ContentDownloader.ShutdownSteam3(); - - if (steam) + if (game.Provider.IsUpdateNeeded()) { - await Steam.DownloadAsync(); - Console.WriteLine($"Downloaded {nameof(Steam)} ({Steam.Version})"); - } - - if (itch) - { - await Itch.DownloadAsync(); - Console.WriteLine($"Downloaded {nameof(Itch)} ({Itch.Version})"); + await game.DownloadAsync(); + Console.WriteLine($"Downloaded {game.Provider.Version}"); } } ContentDownloader.ShutdownSteam3(); - if (setupSteam) - { - Steam.UpdateVersion(); - } - - if (setupItch) - { - Itch.UpdateVersion(); - } - - if (setupSteam) - { - Steam.Dump(); - } - - if (setupItch) + foreach (var game in Games) { - Itch.Dump(); + game.CheckVersion(); + game.Dump(); } } } diff --git a/Reactor.Greenhouse/Setup/GameVersion.cs b/Reactor.Greenhouse/Setup/GameVersion.cs new file mode 100644 index 0000000..ea88653 --- /dev/null +++ b/Reactor.Greenhouse/Setup/GameVersion.cs @@ -0,0 +1,75 @@ +using System; +using System.Text.RegularExpressions; + +namespace Reactor.Greenhouse.Setup +{ + public class GameVersion + { + private static readonly Regex _regex = new Regex(@"^(?[0-9]+)\.(?[0-9]+)\.(?[0-9]+)(?[sia])?", RegexOptions.Compiled); + + public static GamePlatform GamePlatformFromShorthand(string shorthand) + { + return shorthand switch + { + "s" => GamePlatform.Steam, + "i" => GamePlatform.Itch, + "a" => GamePlatform.Android, + _ => throw new ArgumentOutOfRangeException(nameof(shorthand)) + }; + } + + public int Major { get; } + public int Minor { get; } + public int Patch { get; } + public GamePlatform? Platform { get; } + + public GameVersion(string version) + { + var match = _regex.Match(version); + + Major = int.Parse(match.Groups["major"].Value); + Minor = int.Parse(match.Groups["minor"].Value); + Patch = int.Parse(match.Groups["patch"].Value); + + var platform = match.Groups["platform"]; + Platform = platform.Success && !string.IsNullOrEmpty(platform.Value) ? GamePlatformFromShorthand(platform.Value) : null; + } + + public override string ToString() + { + return $"{Major}.{Minor}.{Patch}" + Platform switch + { + GamePlatform.Steam => "s", + GamePlatform.Itch => "i", + GamePlatform.Android => "a", + null => string.Empty, + _ => throw new ArgumentOutOfRangeException() + }; + } + + public bool Equals(GameVersion other, bool ignorePlatform = false) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + + return Major == other.Major && Minor == other.Minor && Patch == other.Patch && (ignorePlatform || Platform == other.Platform); + } + + public override bool Equals(object obj) + { + return Equals((GameVersion) obj); + } + + public override int GetHashCode() + { + return HashCode.Combine(Major, Minor, Patch, Platform); + } + } + + public enum GamePlatform + { + Steam, + Itch, + Android + } +} diff --git a/Reactor.Greenhouse/Setup/Provider/AndroidProvider.cs b/Reactor.Greenhouse/Setup/Provider/AndroidProvider.cs new file mode 100644 index 0000000..e62a8d1 --- /dev/null +++ b/Reactor.Greenhouse/Setup/Provider/AndroidProvider.cs @@ -0,0 +1,45 @@ +using System.Collections.Generic; +using System.IO; +using System.IO.Compression; +using System.Net.Http; +using System.Threading.Tasks; + +namespace Reactor.Greenhouse.Setup.Provider +{ + public class AndroidProvider : BaseProvider + { + private Dictionary VersionMap { get; } = new Dictionary + { + [new GameVersion("2020.11.17a")] = "https://www.apkmirror.com/wp-content/themes/APKMirror/download.php?id=1753109" + }; + + public string DownloadUrl { get; } + + public AndroidProvider(GameVersion version) : base(version) + { + DownloadUrl = VersionMap[version]; + } + + private HttpClient HttpClient { get; } = new HttpClient(); + + public override void Setup() + { + } + + public override async Task DownloadAsync() + { + using var zipArchive = new ZipArchive(await HttpClient.GetStreamAsync(DownloadUrl), ZipArchiveMode.Read); + + zipArchive.GetEntry("lib/arm64-v8a/libil2cpp.so")! + .ForceExtractToFile(Path.Combine(Game.Path, "libil2cpp.so")); + + zipArchive.GetEntry("assets/bin/Data/Managed/Metadata/global-metadata.dat")! + .ForceExtractToFile(Path.Combine(Game.Path, "Among Us_Data", "il2cpp_data", "Metadata", "global-metadata.dat")); + } + + public override bool IsUpdateNeeded() + { + return !Directory.Exists(Game.Path); + } + } +} diff --git a/Reactor.Greenhouse/Setup/Provider/BaseProvider.cs b/Reactor.Greenhouse/Setup/Provider/BaseProvider.cs index 5c72ef5..c790b49 100644 --- a/Reactor.Greenhouse/Setup/Provider/BaseProvider.cs +++ b/Reactor.Greenhouse/Setup/Provider/BaseProvider.cs @@ -6,6 +6,12 @@ namespace Reactor.Greenhouse.Setup.Provider public abstract class BaseProvider { public Game Game { get; internal set; } + public GameVersion Version { get; } + + protected BaseProvider(GameVersion version) + { + Version = version; + } public abstract void Setup(); public abstract Task DownloadAsync(); diff --git a/Reactor.Greenhouse/Setup/Provider/ItchProvider.cs b/Reactor.Greenhouse/Setup/Provider/ItchProvider.cs index e56d5aa..828ad6e 100644 --- a/Reactor.Greenhouse/Setup/Provider/ItchProvider.cs +++ b/Reactor.Greenhouse/Setup/Provider/ItchProvider.cs @@ -16,6 +16,18 @@ namespace Reactor.Greenhouse.Setup.Provider { public class ItchProvider : BaseProvider { + private Dictionary VersionMap { get; } = new Dictionary + { + [new GameVersion("2020.11.17i")] = 1047908, + }; + + public int UploadId { get; } + + public ItchProvider(GameVersion version) : base(version) + { + UploadId = VersionMap[version]; + } + private HttpClient HttpClient { get; } = new HttpClient(new HttpClientHandler { CookieContainer = new CookieContainer() @@ -71,7 +83,7 @@ public override async Task DownloadAsync() var downloadPageUrl = ((IHtmlAnchorElement) pageDocument.QuerySelector("a[class=button]")).Href; var downloadDocument = htmlParser.ParseDocument(await HttpClient.GetStringAsync(downloadPageUrl)); - var uploadId = ((IHtmlAnchorElement) downloadDocument.QuerySelector("a[class='button download_btn']")).Dataset["upload_id"]; + // var uploadId = ((IHtmlAnchorElement) downloadDocument.QuerySelector("a[class='button download_btn']")).Dataset["upload_id"]; var keyRegex = new Regex("key\":\"(.+)\","); var key = downloadDocument.QuerySelectorAll("script[type='text/javascript']") @@ -80,7 +92,7 @@ public override async Task DownloadAsync() .Single(x => x.Success) .Groups[1].Value; - var json = await HttpClient.PostAsync($"https://innersloth.itch.io/among-us/file/{uploadId}?key={key}", null); + var json = await HttpClient.PostAsync($"https://innersloth.itch.io/among-us/file/{UploadId}?key={key}", null); var response = JsonConvert.DeserializeObject(await json.Content.ReadAsStringAsync()); Console.WriteLine($"Downloading {response.Url}"); @@ -91,10 +103,8 @@ public override async Task DownloadAsync() { if (files.Contains(entry.Name)) { - var path = Path.Combine("work", "itch", entry.FullName.Substring(entry.FullName.IndexOf("/", StringComparison.Ordinal) + 1)); - - Directory.GetParent(path)!.Create(); - entry.ExtractToFile(path, true); + var path = Path.Combine(Game.Path, entry.FullName[(entry.FullName.IndexOf("/", StringComparison.Ordinal) + 1)..]); + entry.ForceExtractToFile(path); } } } diff --git a/Reactor.Greenhouse/Setup/Provider/SteamProvider.cs b/Reactor.Greenhouse/Setup/Provider/SteamProvider.cs index 6efbe84..ea70508 100644 --- a/Reactor.Greenhouse/Setup/Provider/SteamProvider.cs +++ b/Reactor.Greenhouse/Setup/Provider/SteamProvider.cs @@ -4,7 +4,6 @@ using System.Text.RegularExpressions; using System.Threading.Tasks; using DepotDownloader; -using SteamKit2; namespace Reactor.Greenhouse.Setup.Provider { @@ -12,43 +11,37 @@ public class SteamProvider : BaseProvider { private const uint AppId = 945360; private const uint DepotId = 945361; - private const ulong PreObfuscationManifest = 3596575937380717449; - public bool IsPreObfuscation { get; } + private Dictionary VersionMap { get; } = new Dictionary + { + [new GameVersion("2019.10.10s")] = 3162069540887216240, + [new GameVersion("2020.12.9s")] = 3306639722673334636 + }; + + public ulong Manifest { get; } - public SteamProvider(bool isPreObfuscation) + public SteamProvider(GameVersion version) : base(version) { - IsPreObfuscation = isPreObfuscation; + Manifest = VersionMap[version]; } public override bool IsUpdateNeeded() { - DepotConfigStore.LoadFromFile(Path.Combine(Game.Path, ".DepotDownloader", "depot.config")); - if (DepotConfigStore.Instance.InstalledManifestIDs.TryGetValue(DepotId, out var installedManifest)) + try { - if (IsPreObfuscation) + DepotConfigStore.LoadFromFile(Path.Combine(Game.Path, ".DepotDownloader", "depot.config")); + if (DepotConfigStore.Instance.InstalledManifestIDs.TryGetValue(DepotId, out var installedManifest)) { - if (installedManifest == PreObfuscationManifest) - { - return false; - } - } - else - { - if (ContentDownloader.steam3 == null) - { - ContentDownloader.InitializeSteam3(); - } - - ContentDownloader.steam3!.RequestAppInfo(AppId); - - var depots = ContentDownloader.GetSteam3AppSection(AppId, EAppInfoSection.Depots); - if (installedManifest == depots[DepotId.ToString()]["manifests"][ContentDownloader.DEFAULT_BRANCH].AsUnsignedLong()) + if (installedManifest == Manifest) { return false; } } } + finally + { + ContentDownloader.ShutdownSteam3(); + } return true; } @@ -114,7 +107,7 @@ public override void Setup() public override Task DownloadAsync() { ContentDownloader.Config.InstallDirectory = Game.Path; - return ContentDownloader.DownloadAppAsync(AppId, DepotId, IsPreObfuscation ? PreObfuscationManifest : ContentDownloader.INVALID_MANIFEST_ID); + return ContentDownloader.DownloadAppAsync(AppId, DepotId, Manifest); } } } diff --git a/Reactor.OxygenFilter.MSBuild/Reactor.OxygenFilter.MSBuild.csproj b/Reactor.OxygenFilter.MSBuild/Reactor.OxygenFilter.MSBuild.csproj index b606e5e..5b87c9b 100644 --- a/Reactor.OxygenFilter.MSBuild/Reactor.OxygenFilter.MSBuild.csproj +++ b/Reactor.OxygenFilter.MSBuild/Reactor.OxygenFilter.MSBuild.csproj @@ -4,7 +4,7 @@ latest true - 0.2.6 + 0.2.7 git https://github.com/NuclearPowered/Reactor.OxygenFilter LGPL-3.0-or-later diff --git a/Reactor.OxygenFilter.MSBuild/Reobfuscate.cs b/Reactor.OxygenFilter.MSBuild/Reobfuscate.cs index e15fb8a..aa0fc74 100644 --- a/Reactor.OxygenFilter.MSBuild/Reobfuscate.cs +++ b/Reactor.OxygenFilter.MSBuild/Reobfuscate.cs @@ -229,8 +229,28 @@ void ReobfuscateType(TypeReference t) } // get same method from obfuscated assembly - var signature = deobfuscatedCall.GetSignature(t => GetObfuscated(t) ?? t.FullName); - MethodReference definition = typeDefinition.GetMethods().Single(x => x.Name == obfuscated && x.GetSignature() == signature); + var nameMatched = typeDefinition.GetMethods().Where(x => x.Name == obfuscated).ToArray(); + + var signature = deobfuscatedCall.GetSignature((member, original) => + { + if (member is TypeReference typeReference && typeReference.Scope.Name == "Assembly-CSharp.dll-Deobfuscated") + { + var resolved = typeReference.Resolve(); + if (resolved != null) + { + var s = GetObfuscated(resolved); + + if (s != null) + { + return s; + } + } + } + + return original; + }); + + MethodReference definition = nameMatched.Length <= 1 ? nameMatched.Single() : nameMatched.Single(x => x.GetSignature() == signature); // obfuscate generics if (deobfuscatedCallReference is GenericInstanceMethod generic) diff --git a/Reactor.OxygenFilter/Extensions.cs b/Reactor.OxygenFilter/Extensions.cs index 284366c..7c2c2d4 100644 --- a/Reactor.OxygenFilter/Extensions.cs +++ b/Reactor.OxygenFilter/Extensions.cs @@ -1,5 +1,7 @@ using System; using System.Linq; +using System.Reflection; +using System.Reflection.Emit; using System.Text; using Mono.Cecil; @@ -7,34 +9,72 @@ namespace Reactor.OxygenFilter { public static class Extensions { - public static bool IsObfuscated(this string text) + private static readonly PropertyInfo _suffix = typeof(ArrayType).GetProperty("Suffix", BindingFlags.NonPublic | BindingFlags.Instance); + + private static readonly Func _callBaseFullName; + + static Extensions() { - return text.Length == 11 && text.All(char.IsUpper); + var dynamicMethod = new DynamicMethod("CallBaseFullname", typeof(string), new[] { typeof(TypeSpecification) }, typeof(Extensions)); + var il = dynamicMethod.GetILGenerator(); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Call, typeof(TypeSpecification).GetProperty(nameof(TypeSpecification.FullName))!.GetMethod); + il.Emit(OpCodes.Ret); + + _callBaseFullName = (Func) dynamicMethod.CreateDelegate(typeof(Func)); } - public static string GetSignature(this MethodDefinition methodDefinition, Func map = null) + public delegate string MapDelegate(MemberReference member, string original); + + public static void AppendMappedFullName(this StringBuilder sb, MemberReference member, MapDelegate map = null) { - map ??= provider => provider.FullName; + map ??= (_, original) => original; - string MapIfResolved(TypeReference typeReference) + if (member is TypeSpecification typeSpecification) { - TypeDefinition resolved = null; + sb.Append(map(typeSpecification, _callBaseFullName(typeSpecification))); - try + if (typeSpecification is GenericInstanceType genericInstanceType) { - resolved = typeReference.Resolve(); + sb.Append("<"); + var arguments = genericInstanceType.GenericArguments; + for (var i = 0; i < arguments.Count; i++) + { + if (i > 0) + sb.Append(","); + + sb.AppendMappedFullName(arguments[i], map); + } + + sb.Append(">"); } - catch + + if (typeSpecification is ArrayType arrayType) { - // ignored + sb.Append((string) _suffix.GetValue(arrayType)); } - return resolved != null ? map(resolved) : typeReference.FullName; + if (typeSpecification is ByReferenceType) + { + sb.Append("&"); + } + } + else + { + sb.Append(map(member, member.FullName)); } + } + + public static bool IsObfuscated(this string text) + { + return text.Length == 11 && text.All(char.IsUpper); + } + public static string GetSignature(this MethodDefinition methodDefinition, MapDelegate map = null) + { var sb = new StringBuilder(); - sb.Append(MapIfResolved(methodDefinition.ReturnType)); + sb.AppendMappedFullName(methodDefinition.ReturnType, map); sb.Append(" "); sb.Append("("); @@ -50,7 +90,7 @@ string MapIfResolved(TypeReference typeReference) if (parameterType is SentinelType) sb.Append("...,"); - sb.Append(MapIfResolved(parameterType)); + sb.AppendMappedFullName(parameterType, map); } } diff --git a/Reactor.OxygenFilter/Mappings.cs b/Reactor.OxygenFilter/Mappings.cs index 1aaa803..d49519c 100644 --- a/Reactor.OxygenFilter/Mappings.cs +++ b/Reactor.OxygenFilter/Mappings.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Mono.Cecil; using Newtonsoft.Json; namespace Reactor.OxygenFilter @@ -188,6 +189,14 @@ public MappedMethod() public MappedMethod(OriginalDescriptor original, string mapped) : base(original, mapped) { } + + public MappedMethod(MethodDefinition methodDefinition, string mapped) : base(new OriginalDescriptor + { + Name = methodDefinition.Name, + Signature = methodDefinition.GetSignature() + }, mapped) + { + } } public class MappedType : MappedMember diff --git a/Reactor.OxygenFilter/Reactor.OxygenFilter.csproj b/Reactor.OxygenFilter/Reactor.OxygenFilter.csproj index ffee4ce..9009231 100644 --- a/Reactor.OxygenFilter/Reactor.OxygenFilter.csproj +++ b/Reactor.OxygenFilter/Reactor.OxygenFilter.csproj @@ -3,15 +3,16 @@ net472;netstandard2.0 latest - 0.1.1 + 0.1.2 git https://github.com/NuclearPowered/Reactor.OxygenFilter LGPL-3.0-or-later Library for deobfuscating Among Us - + + \ No newline at end of file