new: add DeobfuscateStackTrace project
parent
b244293658
commit
d2fac1dab3
|
@ -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>
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"profiles": {
|
||||
"DeobfuscateStackTrace": {
|
||||
"commandName": "Project",
|
||||
"commandLineArgs": "-m ../../../mapping.xml -i ../../../obfuscated.log -o ../../../deobfuscated.log"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
Loading…
Reference in New Issue