new: add DeobfuscateStackTrace project

before-split
walon 2025-05-17 13:59:03 +08:00
parent b244293658
commit d2fac1dab3
7 changed files with 3971 additions and 0 deletions

View File

@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>disable</Nullable>
<Version>1.0.0</Version>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="CommandLineParser" Version="2.9.1" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,24 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.13.35931.197
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DeobfuscateStackTrace", "DeobfuscateStackTrace.csproj", "{B7192F39-1EEA-4F31-885B-B606D700FC79}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{B7192F39-1EEA-4F31-885B-B606D700FC79}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B7192F39-1EEA-4F31-885B-B606D700FC79}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B7192F39-1EEA-4F31-885B-B606D700FC79}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B7192F39-1EEA-4F31-885B-B606D700FC79}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {9F39E3ED-EF31-43DE-B085-0F7BF60844E8}
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,57 @@
using CommandLine;
namespace DeobfuscateStackTrace
{
internal class Program
{
private class CommandOptions
{
[Option('m', "mappingFile", Required = true, HelpText = "mapping xml file")]
public string MappingFile { get; set; }
[Option('i', "input", Required = true, HelpText = "input obfuscated log file")]
public string InputFile { get; set; }
[Option('o', "output", Required = true, HelpText = "output deobfuscated log file")]
public string OutputFile { get; set; }
}
static void Main(string[] args)
{
CommandOptions opt = ParseArgs(args);
if (!File.Exists(opt.MappingFile))
{
Console.Error.WriteLine($"Mapping file {opt.MappingFile} not found");
Environment.Exit(1);
}
if (!File.Exists(opt.InputFile))
{
Console.Error.WriteLine($"Input file {opt.InputFile} not found");
Environment.Exit(1);
}
var reader = new SymbolMappingReader(opt.MappingFile);
StackTraceDeObfuscator.Convert(reader, opt.InputFile, opt.OutputFile);
}
private static CommandOptions ParseArgs(string[] args)
{
var helpWriter = new StringWriter();
var parser = new Parser(settings =>
{
settings.AllowMultiInstance = true;
settings.HelpWriter = helpWriter;
});
var result = parser.ParseArguments<CommandOptions>(args);
if (result.Tag == ParserResultType.NotParsed)
{
Console.Error.WriteLine(helpWriter.ToString());
Environment.Exit(1);
}
return ((Parsed<CommandOptions>)result).Value;
}
}
}

View File

@ -0,0 +1,8 @@
{
"profiles": {
"DeobfuscateStackTrace": {
"commandName": "Project",
"commandLineArgs": "-m ../../../mapping.xml -i ../../../obfuscated.log -o ../../../deobfuscated.log"
}
}
}

View File

@ -0,0 +1,49 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DeobfuscateStackTrace
{
public class StackTraceDeObfuscator
{
public static void Convert(SymbolMappingReader reader, string oldLogFile, string newLogFile)
{
var obfuscatedLines = File.ReadAllLines(oldLogFile, Encoding.UTF8);
var deObfuscatedLines = new List<string>();
bool logLineFound = false;
foreach (string line in obfuscatedLines)
{
if (TryConvertLine(line, reader, ref logLineFound, out var newLine))
{
deObfuscatedLines.Add(newLine);
}
else
{
deObfuscatedLines.Add(line);
}
}
File.WriteAllLines(newLogFile, deObfuscatedLines, Encoding.UTF8);
}
private static bool TryConvertLine(string line, SymbolMappingReader reader, ref bool logLineFound, out string deObfuscatedStackTrace)
{
deObfuscatedStackTrace = line;
if (string.IsNullOrEmpty(line))
{
logLineFound = false;
return false;
}
if (!logLineFound)
{
logLineFound = line.StartsWith("UnityEngine.DebugLogHandler:Internal_Log")
|| line.StartsWith("UnityEngine.DebugLogHandler:LogFormat")
|| line.StartsWith("UnityEngine.Logger:Log");
return false;
}
return reader.TryDeObfuscateStackTrace(line, out deObfuscatedStackTrace);
}
}
}

View File

@ -0,0 +1,204 @@
using System.Xml;
namespace DeobfuscateStackTrace
{
public class SymbolMappingReader
{
private readonly Dictionary<string, List<string>> _fullSignatureMapper = new Dictionary<string, List<string>>();
private readonly Dictionary<string, List<string>> _signatureWithParamsMapper = new Dictionary<string, List<string>>();
private enum RenameStatus
{
NotRenamed,
Renamed,
}
private class RenameRecord
{
public RenameStatus status;
public string signature;
public string oldName;
public string newName;
public string oldStackTraceSignature; // only for MethodDef
public object renameMappingData;
}
private class RenameMappingField
{
public RenameStatus status;
public string signature;
public string newName;
}
private class RenameMappingMethod
{
public RenameStatus status;
public string signature;
public string newName;
public List<RenameMappingMethodParam> parameters = new List<RenameMappingMethodParam>();
}
private class RenameMappingMethodParam
{
public RenameStatus status;
public int index;
public string newName;
}
private class RenameMappingProperty
{
public RenameStatus status;
public string signature;
public string newName;
}
private class RenameMappingEvent
{
public RenameStatus status;
public string signature;
public string newName;
}
private class RenameMappingType
{
public RenameStatus status;
public string oldFullName;
public string newFullName;
public Dictionary<string, RenameMappingField> fields = new Dictionary<string, RenameMappingField>();
public Dictionary<string, RenameMappingMethod> methods = new Dictionary<string, RenameMappingMethod>();
public Dictionary<string, RenameMappingProperty> properties = new Dictionary<string, RenameMappingProperty>();
public Dictionary<string, RenameMappingEvent> events = new Dictionary<string, RenameMappingEvent>();
}
private class RenameMappingAssembly
{
public RenameStatus status;
public string oldAssName;
public string newAssName;
public Dictionary<string, RenameMappingType> types = new Dictionary<string, RenameMappingType>();
}
public SymbolMappingReader(string mappingFile)
{
LoadXmlMappingFile(mappingFile);
}
private void LoadXmlMappingFile(string mappingFile)
{
var doc = new XmlDocument();
doc.Load(mappingFile);
var root = doc.DocumentElement;
foreach (XmlNode node in root.ChildNodes)
{
if (!(node is XmlElement element))
{
continue;
}
LoadAssemblyMapping(element);
}
}
private void LoadAssemblyMapping(XmlElement ele)
{
if (ele.Name != "assembly")
{
throw new System.Exception($"Invalid node name: {ele.Name}. Expected 'assembly'.");
}
foreach (XmlNode node in ele.ChildNodes)
{
if (!(node is XmlElement element))
{
continue;
}
if (element.Name == "type")
{
LoadTypeMapping(element);
}
}
}
private void LoadTypeMapping(XmlElement ele)
{
foreach (XmlNode node in ele.ChildNodes)
{
if (!(node is XmlElement c))
{
continue;
}
if (node.Name == "method")
{
LoadMethodMapping(c);
}
}
}
private string GetMethodSignatureWithoutParams(string signature)
{
int index = signature.IndexOf('(');
if (index < 0)
{
return signature;
}
return signature.Substring(0, index);
}
private void LoadMethodMapping(XmlElement ele)
{
if (!ele.HasAttribute("oldStackTraceSignature"))
{
throw new System.Exception($"Invalid node name: {ele.Name}. attribute 'oldStackTraceSignature' missing.");
}
if (!ele.HasAttribute("newStackTraceSignature"))
{
throw new System.Exception($"Invalid node name: {ele.Name}. attribute 'newStackTraceSignature' missing.");
}
string oldStackTraceSignature = ele.Attributes["oldStackTraceSignature"].Value;
string newStackTraceSignature = ele.Attributes["newStackTraceSignature"].Value;
if (!_fullSignatureMapper.TryGetValue(newStackTraceSignature, out var oldFullSignatures))
{
oldFullSignatures = new List<string>();
_fullSignatureMapper[newStackTraceSignature] = oldFullSignatures;
}
oldFullSignatures.Add(oldStackTraceSignature);
string oldStackTraceSignatureWithoutParams = GetMethodSignatureWithoutParams(oldStackTraceSignature);
string newStackTraceSignatureWithoutParams = GetMethodSignatureWithoutParams(newStackTraceSignature);
if (!_signatureWithParamsMapper.TryGetValue(newStackTraceSignatureWithoutParams, out var oldSignaturesWithoutParams))
{
oldSignaturesWithoutParams = new List<string>();
_signatureWithParamsMapper[newStackTraceSignatureWithoutParams] = oldSignaturesWithoutParams;
}
oldSignaturesWithoutParams.Add(oldStackTraceSignatureWithoutParams);
}
public bool TryDeObfuscateStackTrace(string obfuscatedStackTraceLog, out string deObfuscatedStackTrace)
{
obfuscatedStackTraceLog = obfuscatedStackTraceLog.Trim();
if (_fullSignatureMapper.TryGetValue(obfuscatedStackTraceLog, out var oldFullSignatures))
{
deObfuscatedStackTrace = string.Join("|", oldFullSignatures);
return true;
}
string obfuscatedStackTraceSignatureWithoutParams = GetMethodSignatureWithoutParams(obfuscatedStackTraceLog);
if (_signatureWithParamsMapper.TryGetValue(obfuscatedStackTraceSignatureWithoutParams, out var oldSignaturesWithoutParams))
{
deObfuscatedStackTrace = obfuscatedStackTraceLog.Replace(obfuscatedStackTraceSignatureWithoutParams, string.Join("|", oldSignaturesWithoutParams));
return true;
}
deObfuscatedStackTrace = null;
return false;
}
}
}

File diff suppressed because it is too large Load Diff