Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Port UnhollowerPdbGen to Il2CppInterop #155

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions Il2CppInterop.Pdb.Generator/Il2CppInterop.Pdb.Generator.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<OutputType>Exe</OutputType>
<RootNamespace>Il2CppInterop.Pdb.Generator</RootNamespace>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="AsmResolver.DotNet" Version="6.0.0-beta.1" />
<PackageReference Include="AssetRipper.Bindings.MsPdbCore" Version="1.0.1" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Il2CppInterop.Common\Il2CppInterop.Common.csproj" />
</ItemGroup>

</Project>
29 changes: 29 additions & 0 deletions Il2CppInterop.Pdb.Generator/MethodAddressToTokenMap.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using AsmResolver.DotNet;
using Il2CppInterop.Common.Maps;

#nullable enable

namespace Il2CppInterop.Pdb.Generator;

public class MethodAddressToTokenMap : MethodAddressToTokenMapBase<AssemblyDefinition, MethodDefinition>
{
public MethodAddressToTokenMap(string filePath) : base(filePath)
{
}

protected override AssemblyDefinition? LoadAssembly(string assemblyName)
{
var filesDirt = Path.GetDirectoryName(myFilePath)!;
assemblyName = assemblyName.Substring(0, assemblyName.IndexOf(','));
return AssemblyDefinition.FromFile(Path.Combine(filesDirt, assemblyName + ".dll"));
}

protected override MethodDefinition? ResolveMethod(AssemblyDefinition? assembly, int token)
{
if (assembly?.ManifestModule?.TryLookupMember(token, out MethodDefinition? result) ?? false)
{
return result;
}
return null;
}
}
102 changes: 102 additions & 0 deletions Il2CppInterop.Pdb.Generator/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
using AssetRipper.Bindings.MsPdbCore;
using System.Reflection.PortableExecutable;

namespace Il2CppInterop.Pdb.Generator;

internal static unsafe class Program
{
public static void Main(string[] args)
{
if (args.Length <= 1)
{
Console.WriteLine($"Usage: Il2CppInterop.Pdb.Generator.exe <path to GameAssembly.dll> <path to {MethodAddressToTokenMap.FileName}>");
}
var rootPath = Path.GetDirectoryName(args[0])!;
var map = new MethodAddressToTokenMap(args[1]);

using var peStream = new FileStream(args[0], FileMode.Open, FileAccess.Read);
using var peReader = new PEReader(peStream);


string openError;
PDBErrors err;
var pdbFilePath = Path.Combine(rootPath, "GameAssembly.pdb");
MsPdbCore.PDBOpen2W(pdbFilePath, "w", out err, out openError, out var pdb);

MsPdbCore.PDBOpenDBI(pdb, "w", "", out var dbi);

MsPdbCore.DBIOpenModW(dbi, "__Globals", "__Globals", out var mod);

ushort secNum = 1;
ushort i2cs = 1;
foreach (var sectionHeader in peReader.PEHeaders.SectionHeaders)
{
if (sectionHeader.Name == "il2cpp") i2cs = secNum;
MsPdbCore.DBIAddSec(dbi, secNum++, 0 /* TODO? */, sectionHeader.VirtualAddress, sectionHeader.VirtualSize);
}

foreach (var valueTuple in map)
{
ushort targetSect = 0;
long tsva = 0;
ushort sc = 1;
foreach (var sectionHeader in peReader.PEHeaders.SectionHeaders)
{
if (valueTuple.Item1 > sectionHeader.VirtualAddress)
{
targetSect = sc;
tsva = sectionHeader.VirtualAddress;
}
else
break;

sc++;
}

if (targetSect == 0) throw new ApplicationException("Bad segment");
MsPdbCore.ModAddPublic2(mod, valueTuple.Item2.FullName, targetSect, (int)(valueTuple.Item1 - tsva * 2), CV_PUBSYMFLAGS_e.Function);
}

MsPdbCore.ModClose(mod);
MsPdbCore.DBIClose(dbi);

MsPdbCore.PDBCommit(pdb);

MsPdbCore.PDBQuerySignature2(pdb, out var wrongGuid);

MsPdbCore.PDBClose(pdb);

// Hack: manually replace guid and age in generated .pdb, because there's no API on mspdbcore to set them manually
var targetDebugInfo = peReader.ReadCodeViewDebugDirectoryData(peReader.ReadDebugDirectory()
.Single(it => it.Type == DebugDirectoryEntryType.CodeView));

var wrongGuidBytes = wrongGuid.ToByteArray();
var allPdbBytes = File.ReadAllBytes(pdbFilePath);

var patchTarget = IndexOfBytes(allPdbBytes, wrongGuidBytes);
targetDebugInfo.Guid.TryWriteBytes(allPdbBytes.AsSpan(patchTarget));

Console.WriteLine(targetDebugInfo.Guid);
Console.WriteLine(targetDebugInfo.Age);

BitConverter.TryWriteBytes(allPdbBytes.AsSpan(patchTarget - 4), targetDebugInfo.Age);
File.WriteAllBytes(pdbFilePath, allPdbBytes);
}

private static int IndexOfBytes(byte[] haystack, byte[] needle)
{
for (var i = 0; i < haystack.Length - needle.Length; i++)
{
for (var j = 0; j < needle.Length; j++)
{
if (haystack[i + j] != needle[j])
goto moveOn;
}

return i;
moveOn:;
}

return -1;
}
}
17 changes: 17 additions & 0 deletions Il2CppInterop.Pdb.Generator/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# PDB generator

This is an executable that can be ran to generate a Microsoft PDB file (debug symbols) for GameAssembly.dll based on unhollower-generated names.

This can be useful for analyzing code of obfuscated games. For unobfuscated games, using [Il2CppInspector](https://github.com/djkaty/Il2CppInspector) might provide better results for code analysis.

Generated PDBs were tested with windbg, lldb, WPA viewer/ETL performance analysis and IDA.

Generated PDBs only include generated methods, and don't include type info, generic method info and IL2CPP internals.

You need to manually copy the following Microsoft-provided libraries from Visual Studio (or other build tools) for this to work. They cannot be redistributed because the license on them is not clear.

* `mspdbcore.dll`
* `msobj140.dll`
* `tbbmalloc.dll`

These need to be placed next to the built executable file. Use file search to find `mspdbcore` in the Visual Studio install directory. By default, they are in `C:\Program Files\Microsoft Visual Studio\2022\Community\Common7\IDE\`.
24 changes: 15 additions & 9 deletions Il2CppInterop.sln
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.31402.337
# Visual Studio Version 17
VisualStudioVersion = 17.11.35222.181
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Il2CppInterop.Generator", "Il2CppInterop.Generator\Il2CppInterop.Generator.csproj", "{7C3FD45B-A563-47AF-90DF-8B051A8C33A0}"
EndProject
Expand All @@ -10,33 +10,35 @@ EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{463B7E3B-94E8-4EEB-B54A-EF15AD9C7C7E}"
ProjectSection(SolutionItems) = preProject
.gitignore = .gitignore
README.md = README.md
Directory.Build.props = Directory.Build.props
build.cake = build.cake
Directory.Build.props = Directory.Build.props
README.md = README.md
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Documentation", "Documentation", "{99E71726-78FB-4376-89DA-90E5C08F5CD4}"
ProjectSection(SolutionItems) = preProject
Documentation\Class-Injection.md = Documentation\Class-Injection.md
Documentation\Command-Line-Usage.md = Documentation\Command-Line-Usage.md
Documentation\Common-Problems.md = Documentation\Common-Problems.md
Documentation\Injected-Components-In-Asset-Bundles.md = Documentation\Injected-Components-In-Asset-Bundles.md
Documentation\Implementing-Interfaces.md = Documentation\Implementing-Interfaces.md
Documentation\Injected-Components-In-Asset-Bundles.md = Documentation\Injected-Components-In-Asset-Bundles.md
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Il2CppInterop.CLI", "Il2CppInterop.CLI\Il2CppInterop.CLI.csproj", "{F5AA88D1-5E62-46B8-A11C-3FAC40C25600}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Il2CppInterop.CLI", "Il2CppInterop.CLI\Il2CppInterop.CLI.csproj", "{F5AA88D1-5E62-46B8-A11C-3FAC40C25600}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Workflows", "Workflows", "{771D8BE3-C373-4754-94EA-4A68B117BAEF}"
ProjectSection(SolutionItems) = preProject
.github\workflows\dotnet.yml = .github\workflows\dotnet.yml
.github\workflows\format_check.yml = .github\workflows\format_check.yml
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Il2CppInterop.StructGenerator", "Il2CppInterop.StructGenerator\Il2CppInterop.StructGenerator.csproj", "{DE781BD4-650F-4ED8-B615-5CB36E6D476A}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Il2CppInterop.StructGenerator", "Il2CppInterop.StructGenerator\Il2CppInterop.StructGenerator.csproj", "{DE781BD4-650F-4ED8-B615-5CB36E6D476A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Il2CppInterop.Common", "Il2CppInterop.Common\Il2CppInterop.Common.csproj", "{E7D3A81B-11CD-402C-A447-015B00DCA3FD}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Il2CppInterop.Common", "Il2CppInterop.Common\Il2CppInterop.Common.csproj", "{E7D3A81B-11CD-402C-A447-015B00DCA3FD}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Il2CppInterop.HarmonySupport", "Il2CppInterop.HarmonySupport\Il2CppInterop.HarmonySupport.csproj", "{EBC23884-3417-4F24-8623-E913C5151CC3}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Il2CppInterop.HarmonySupport", "Il2CppInterop.HarmonySupport\Il2CppInterop.HarmonySupport.csproj", "{EBC23884-3417-4F24-8623-E913C5151CC3}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Il2CppInterop.Pdb.Generator", "Il2CppInterop.Pdb.Generator\Il2CppInterop.Pdb.Generator.csproj", "{55566454-FF96-4C35-9CF4-479F7130E4D1}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down Expand Up @@ -68,6 +70,10 @@ Global
{EBC23884-3417-4F24-8623-E913C5151CC3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EBC23884-3417-4F24-8623-E913C5151CC3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EBC23884-3417-4F24-8623-E913C5151CC3}.Release|Any CPU.Build.0 = Release|Any CPU
{55566454-FF96-4C35-9CF4-479F7130E4D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{55566454-FF96-4C35-9CF4-479F7130E4D1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{55566454-FF96-4C35-9CF4-479F7130E4D1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{55566454-FF96-4C35-9CF4-479F7130E4D1}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
Loading