Skip to content
Open
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
30 changes: 30 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: CI

on:
push:
pull_request:

jobs:
build:
runs-on: windows-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup .NET 8
uses: actions/setup-dotnet@v3
with:
dotnet-version: '8.0.x'
- name: Run publish for net8.0
shell: pwsh
run: |
pwsh -NoProfile -ExecutionPolicy Bypass -File .\build-ci.ps1 -TargetFrameworks 'net8.0'

- name: Setup .NET 9 (optional)
uses: actions/setup-dotnet@v3
with:
dotnet-version: '9.0.x'
- name: Run publish for net9.0 (optional)
shell: pwsh
run: |
pwsh -NoProfile -ExecutionPolicy Bypass -File .\build-ci.ps1 -TargetFrameworks 'net9.0' -Rids 'win-x64'
6 changes: 6 additions & 0 deletions Il2CppDumper.sln
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,19 @@ EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{2087F99A-A655-41C1-84BB-54798AEA4080}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2087F99A-A655-41C1-84BB-54798AEA4080}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2087F99A-A655-41C1-84BB-54798AEA4080}.Debug|x86.ActiveCfg = Debug|x86
{2087F99A-A655-41C1-84BB-54798AEA4080}.Debug|x86.Build.0 = Debug|x86
{2087F99A-A655-41C1-84BB-54798AEA4080}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2087F99A-A655-41C1-84BB-54798AEA4080}.Release|Any CPU.Build.0 = Release|Any CPU
{2087F99A-A655-41C1-84BB-54798AEA4080}.Release|x86.ActiveCfg = Release|x86
{2087F99A-A655-41C1-84BB-54798AEA4080}.Release|x86.Build.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
20 changes: 17 additions & 3 deletions Il2CppDumper/ExecutableFormats/NSO.cs
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,7 @@ public NSO UnCompress()
var unCompressedData = new byte[header.TextSegment.DecompressedSize];
using (var decoder = new Lz4DecoderStream(new MemoryStream(textBytes)))
{
decoder.Read(unCompressedData, 0, unCompressedData.Length);
ReadAll(decoder, unCompressedData);
}
writer.Write(unCompressedData);
}
Expand All @@ -293,7 +293,7 @@ public NSO UnCompress()
var unCompressedData = new byte[header.RoDataSegment.DecompressedSize];
using (var decoder = new Lz4DecoderStream(new MemoryStream(roDataBytes)))
{
decoder.Read(unCompressedData, 0, unCompressedData.Length);
ReadAll(decoder, unCompressedData);
}
writer.Write(unCompressedData);
}
Expand All @@ -307,7 +307,7 @@ public NSO UnCompress()
var unCompressedData = new byte[header.DataSegment.DecompressedSize];
using (var decoder = new Lz4DecoderStream(new MemoryStream(dataBytes)))
{
decoder.Read(unCompressedData, 0, unCompressedData.Length);
ReadAll(decoder, unCompressedData);
}
writer.Write(unCompressedData);
}
Expand All @@ -322,6 +322,20 @@ public NSO UnCompress()
return this;
}

private static void ReadAll(Stream s, byte[] buffer)
{
int offset = 0;
while (offset < buffer.Length)
{
int read = s.Read(buffer, offset, buffer.Length - offset);
if (read == 0)
{
throw new EndOfStreamException("Unexpected end of stream while reading decompressed data.");
}
offset += read;
}
}

public override SectionHelper GetSectionHelper(int methodCount, int typeDefinitionsCount, int imageCount)
{
var sectionHelper = new SectionHelper(this, methodCount, typeDefinitionsCount, metadataUsagesCount, imageCount);
Expand Down
3 changes: 2 additions & 1 deletion Il2CppDumper/Il2CppDumper.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>net6.0;net8.0</TargetFrameworks>
<TargetFramework>net9.0-windows8.0</TargetFramework>
<Version>1.0.0.0</Version>
<AssemblyVersion>1.0.0.0</AssemblyVersion>
<FileVersion>1.0.0.0</FileVersion>
<Copyright>Copyright © Perfare 2016-2024</Copyright>
<DebugType>embedded</DebugType>
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
<Platforms>AnyCPU;x86</Platforms>
</PropertyGroup>

<ItemGroup>
Expand Down
151 changes: 77 additions & 74 deletions Il2CppDumper/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
using System.Linq;
using System.Runtime.InteropServices;
using System.Text.Json;
using System.Collections.Generic;
using System.Threading;
// Add WinForms reference for dialogs

namespace Il2CppDumper
{
Expand All @@ -13,97 +16,82 @@ class Program
[STAThread]
static void Main(string[] args)
{
config = JsonSerializer.Deserialize<Config>(File.ReadAllText(AppDomain.CurrentDomain.BaseDirectory + @"config.json"));
// Ensure STAThread for WinForms dialogs
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && Thread.CurrentThread.GetApartmentState() != ApartmentState.STA)
{
var thread = new Thread(() => Main(args));
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
thread.Join();
return;
}

config = JsonSerializer.Deserialize<Config>(
File.ReadAllText(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "config.json")));

string il2cppPath = null;
string metadataPath = null;
string outputDir = null;

if (args.Length == 1)
// ─────────────── simple flag parser ───────────────
var it = ((IEnumerable<string>)args).GetEnumerator();
while (it.MoveNext())
{
if (args[0] == "-h" || args[0] == "--help" || args[0] == "/?" || args[0] == "/h")
var arg = it.Current;
switch (arg)
{
ShowHelp();
return;
case "-h": case "--help": ShowHelp(); return;
case "-i":
case "--input":
if (!it.MoveNext()) { ShowHelp(); return; }
il2cppPath = it.Current; break;
case "-m":
case "--meta":
if (!it.MoveNext()) { ShowHelp(); return; }
metadataPath = it.Current; break;
case "-o":
case "--output":
if (!it.MoveNext()) { ShowHelp(); return; }
outputDir = it.Current; break;
default:
Console.WriteLine($"Unknown arg: {arg}"); ShowHelp(); return;
}
}
if (args.Length > 3)
// ─────────────────────────────────────────────────

// GUI fall-backs
if (il2cppPath == null)
{
ShowHelp();
return;
var ofd = new OpenFileDialog { Filter = "Il2Cpp binary|*.*" };
if (!ofd.ShowDialog()) return;
il2cppPath = ofd.FileName;
}
if (args.Length > 1)
if (metadataPath == null)
{
foreach (var arg in args)
{
if (File.Exists(arg))
{
var file = File.ReadAllBytes(arg);
if (BitConverter.ToUInt32(file, 0) == 0xFAB11BAF)
{
metadataPath = arg;
}
else
{
il2cppPath = arg;
}
}
else if (Directory.Exists(arg))
{
outputDir = Path.GetFullPath(arg) + Path.DirectorySeparatorChar;
}
}
var ofd = new OpenFileDialog { Filter = "global-metadata.dat|global-metadata.dat" };
if (!ofd.ShowDialog()) return;
metadataPath = ofd.FileName;
}
outputDir ??= AppDomain.CurrentDomain.BaseDirectory;
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
if (outputDir == null)
{
if (il2cppPath == null)
{
var ofd = new OpenFileDialog
{
Filter = "Il2Cpp binary file|*.*"
};
if (ofd.ShowDialog())
{
il2cppPath = ofd.FileName;
ofd.Filter = "global-metadata|global-metadata.dat";
if (ofd.ShowDialog())
{
metadataPath = ofd.FileName;
}
else
{
return;
}
}
else
{
return;
}
}
var fbd = new FolderBrowserDialog { Description = "Select output folder" };
if (!fbd.ShowDialog()) return;
outputDir = fbd.SelectedPath + Path.DirectorySeparatorChar;
}
if (il2cppPath == null)

// ensure absolute + trailing slash
outputDir = Path.GetFullPath(outputDir) + Path.DirectorySeparatorChar;

try
{
ShowHelp();
return;
if (Init(il2cppPath, metadataPath, out var metadata, out var il2Cpp))
Dump(metadata, il2Cpp, outputDir);
}
if (metadataPath == null)
catch (Exception ex)
{
Console.WriteLine($"ERROR: Metadata file not found or encrypted.");
}
else
{
try
{
if (Init(il2cppPath, metadataPath, out var metadata, out var il2Cpp))
{
Dump(metadata, il2Cpp, outputDir);
}
}
catch (Exception e)
{
Console.WriteLine(e);
}
Console.WriteLine(ex);
}

if (config.RequireAnyKey)
{
Console.WriteLine("Press any key to exit...");
Expand All @@ -113,7 +101,22 @@ static void Main(string[] args)

static void ShowHelp()
{
Console.WriteLine($"usage: {AppDomain.CurrentDomain.FriendlyName} <executable-file> <global-metadata> <output-directory>");
Console.WriteLine("Il2CppDumper - Unity il2cpp reverse engineering tool\n");
Console.WriteLine("Usage:");
Console.WriteLine($" {AppDomain.CurrentDomain.FriendlyName} -i <il2cpp-binary> -m <global-metadata> -o <output-directory>");
Console.WriteLine();
Console.WriteLine("Options:");
Console.WriteLine(" -h, --help Show this help message and exit");
Console.WriteLine(" -i, --input Path to il2cpp binary file");
Console.WriteLine(" -m, --meta Path to global-metadata.dat file");
Console.WriteLine(" -o, --output Output directory");
Console.WriteLine();
Console.WriteLine("If no arguments are provided, GUI dialogs will be shown.");
if (config.RequireAnyKey)
{
Console.WriteLine("Press any key to exit...");
Console.ReadKey(true);
}
}

private static bool Init(string il2cppPath, string metadataPath, out Metadata metadata, out Il2Cpp il2Cpp)
Expand Down
26 changes: 13 additions & 13 deletions Il2CppDumper/Resource1.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading