CLI/GUI: Add support for saving processed metadata and binary
This commit is contained in:
@@ -38,6 +38,12 @@ namespace Il2CppInspector.CLI
|
|||||||
[Option('o', "json-out", Required = false, HelpText = "JSON metadata output file", Default = "metadata.json")]
|
[Option('o', "json-out", Required = false, HelpText = "JSON metadata output file", Default = "metadata.json")]
|
||||||
public string JsonOutPath { get; set; }
|
public string JsonOutPath { get; set; }
|
||||||
|
|
||||||
|
[Option("metadata-out", Required = false, HelpText = "IL2CPP metadata file output (for extracted or decrypted metadata; ignored otherwise)")]
|
||||||
|
public string MetadataFileOut { get; set; }
|
||||||
|
|
||||||
|
[Option("binary-out", Required = false, HelpText = "IL2CPP binary file output (for extracted or decrypted binaries; ignored otherwise; suffixes will be appended for multiple files)")]
|
||||||
|
public string BinaryFileOut { get; set; }
|
||||||
|
|
||||||
[Option('e', "exclude-namespaces", Required = false, Separator = ',', HelpText = "Comma-separated list of namespaces to suppress in C# output, or 'none' to include all namespaces",
|
[Option('e', "exclude-namespaces", Required = false, Separator = ',', HelpText = "Comma-separated list of namespaces to suppress in C# output, or 'none' to include all namespaces",
|
||||||
Default = new [] {
|
Default = new [] {
|
||||||
"System",
|
"System",
|
||||||
@@ -179,11 +185,13 @@ namespace Il2CppInspector.CLI
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check files exist and determine whether they're archives or not
|
// Check files exist and determine whether they're archives or not
|
||||||
|
bool isExtractedFromPackage = false;
|
||||||
List<Il2CppInspector> il2cppInspectors;
|
List<Il2CppInspector> il2cppInspectors;
|
||||||
using (new Benchmark("Analyze IL2CPP data")) {
|
using (new Benchmark("Analyze IL2CPP data")) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
il2cppInspectors = Il2CppInspector.LoadFromPackage(options.BinaryFiles);
|
il2cppInspectors = Il2CppInspector.LoadFromPackage(options.BinaryFiles);
|
||||||
|
isExtractedFromPackage = true;
|
||||||
}
|
}
|
||||||
catch (Exception ex) {
|
catch (Exception ex) {
|
||||||
Console.Error.WriteLine(ex.Message);
|
Console.Error.WriteLine(ex.Message);
|
||||||
@@ -191,6 +199,8 @@ namespace Il2CppInspector.CLI
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (il2cppInspectors == null) {
|
if (il2cppInspectors == null) {
|
||||||
|
isExtractedFromPackage = false;
|
||||||
|
|
||||||
if (!File.Exists(options.MetadataFile)) {
|
if (!File.Exists(options.MetadataFile)) {
|
||||||
Console.Error.WriteLine($"File {options.MetadataFile} does not exist");
|
Console.Error.WriteLine($"File {options.MetadataFile} does not exist");
|
||||||
return 1;
|
return 1;
|
||||||
@@ -209,6 +219,36 @@ namespace Il2CppInspector.CLI
|
|||||||
if (il2cppInspectors == null)
|
if (il2cppInspectors == null)
|
||||||
Environment.Exit(1);
|
Environment.Exit(1);
|
||||||
|
|
||||||
|
// Save metadata and binary if extracted or modified and save requested
|
||||||
|
if (!string.IsNullOrEmpty(options.MetadataFileOut)) {
|
||||||
|
if (isExtractedFromPackage || il2cppInspectors[0].Metadata.IsModified) {
|
||||||
|
Console.WriteLine($"Saving metadata file to {options.MetadataFileOut}");
|
||||||
|
|
||||||
|
il2cppInspectors[0].SaveMetadataToFile(options.MetadataFileOut);
|
||||||
|
} else
|
||||||
|
Console.WriteLine("Metadata file was not modified - skipping save");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(options.BinaryFileOut)) {
|
||||||
|
var outputIndex = 0;
|
||||||
|
foreach (var il2cpp in il2cppInspectors) {
|
||||||
|
// If there's an extension, strip the leading period
|
||||||
|
var ext = Path.GetExtension(options.BinaryFileOut);
|
||||||
|
if (ext.Length > 0)
|
||||||
|
ext = ext.Substring(1);
|
||||||
|
var outPath = getOutputPath(options.BinaryFileOut, ext, outputIndex);
|
||||||
|
|
||||||
|
if (isExtractedFromPackage || il2cpp.Binary.IsModified) {
|
||||||
|
Console.WriteLine($"Saving binary file to {outPath}");
|
||||||
|
|
||||||
|
il2cpp.SaveBinaryToFile(outPath);
|
||||||
|
} else
|
||||||
|
Console.WriteLine("Binary file was not modified - skipping save");
|
||||||
|
|
||||||
|
outputIndex++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Write output files for each binary
|
// Write output files for each binary
|
||||||
int imageIndex = 0;
|
int imageIndex = 0;
|
||||||
foreach (var il2cpp in il2cppInspectors) {
|
foreach (var il2cpp in il2cppInspectors) {
|
||||||
|
|||||||
@@ -3,8 +3,10 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using Il2CppInspector;
|
using Il2CppInspector;
|
||||||
@@ -17,10 +19,21 @@ namespace Il2CppInspectorGUI
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Interaction logic for App.xaml
|
/// Interaction logic for App.xaml
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class App : Application
|
public partial class App : Application, INotifyPropertyChanged
|
||||||
{
|
{
|
||||||
private Metadata metadata;
|
private Metadata metadata;
|
||||||
|
|
||||||
|
// True if we extracted from an APK, IPA, zip file etc.
|
||||||
|
private bool isExtractedFromPackage;
|
||||||
|
public bool IsExtractedFromPackage {
|
||||||
|
get => isExtractedFromPackage;
|
||||||
|
set {
|
||||||
|
if (value == isExtractedFromPackage) return;
|
||||||
|
isExtractedFromPackage = value;
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public List<AppModel> AppModels { get; } = new List<AppModel>();
|
public List<AppModel> AppModels { get; } = new List<AppModel>();
|
||||||
|
|
||||||
public Exception LastException { get; private set; }
|
public Exception LastException { get; private set; }
|
||||||
@@ -32,6 +45,8 @@ namespace Il2CppInspectorGUI
|
|||||||
|
|
||||||
// Attempt to load an IL2CPP application package (APK or IPA)
|
// Attempt to load an IL2CPP application package (APK or IPA)
|
||||||
public async Task<bool> LoadPackageAsync(IEnumerable<string> packageFiles) {
|
public async Task<bool> LoadPackageAsync(IEnumerable<string> packageFiles) {
|
||||||
|
IsExtractedFromPackage = false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
OnStatusUpdate?.Invoke(this, "Extracting package");
|
OnStatusUpdate?.Invoke(this, "Extracting package");
|
||||||
|
|
||||||
@@ -39,7 +54,8 @@ namespace Il2CppInspectorGUI
|
|||||||
if (streams == null)
|
if (streams == null)
|
||||||
throw new InvalidOperationException("The supplied package is not an APK or IPA file, or does not contain a complete IL2CPP application");
|
throw new InvalidOperationException("The supplied package is not an APK or IPA file, or does not contain a complete IL2CPP application");
|
||||||
|
|
||||||
return await LoadMetadataAsync(streams.Value.Metadata) && await LoadBinaryAsync(streams.Value.Binary);
|
IsExtractedFromPackage = await LoadMetadataAsync(streams.Value.Metadata) && await LoadBinaryAsync(streams.Value.Binary);
|
||||||
|
return IsExtractedFromPackage;
|
||||||
}
|
}
|
||||||
catch (Exception ex) {
|
catch (Exception ex) {
|
||||||
LastException = ex;
|
LastException = ex;
|
||||||
@@ -49,6 +65,7 @@ namespace Il2CppInspectorGUI
|
|||||||
|
|
||||||
// Attempt to load an IL2CPP metadata file
|
// Attempt to load an IL2CPP metadata file
|
||||||
public async Task<bool> LoadMetadataAsync(string metadataFile) {
|
public async Task<bool> LoadMetadataAsync(string metadataFile) {
|
||||||
|
IsExtractedFromPackage = false;
|
||||||
var stream = new MemoryStream(await File.ReadAllBytesAsync(metadataFile));
|
var stream = new MemoryStream(await File.ReadAllBytesAsync(metadataFile));
|
||||||
return await LoadMetadataAsync(stream);
|
return await LoadMetadataAsync(stream);
|
||||||
}
|
}
|
||||||
@@ -120,5 +137,11 @@ namespace Il2CppInspectorGUI
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Property change notifier for IsExtractedFromPackage binding
|
||||||
|
public event PropertyChangedEventHandler PropertyChanged;
|
||||||
|
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) {
|
||||||
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -175,6 +175,45 @@
|
|||||||
<Label Grid.Row="8" Grid.Column="0" Content="Il2CppCodeRegistration function"></Label>
|
<Label Grid.Row="8" Grid.Column="0" Content="Il2CppCodeRegistration function"></Label>
|
||||||
<Label Grid.Row="8" Grid.Column="1" Content="{Binding Path=Binary.RegistrationFunctionPointer}" ContentStringFormat="0x{0:x8}"></Label>
|
<Label Grid.Row="8" Grid.Column="1" Content="{Binding Path=Binary.RegistrationFunctionPointer}" ContentStringFormat="0x{0:x8}"></Label>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
|
<!-- Save file buttons -->
|
||||||
|
<Button Name="btnSaveMetadata" Click="BtnSaveMetadata_OnClick" DockPanel.Dock="Top" Margin="10" Padding="5" HorizontalAlignment="Left" VerticalAlignment="Top" FontSize="14" Width="260" Content="Save extracted/decrypted metadata...">
|
||||||
|
<Button.Style>
|
||||||
|
<Style BasedOn="{StaticResource LightBoxButton}" TargetType="{x:Type Button}">
|
||||||
|
<Style.Triggers>
|
||||||
|
<DataTrigger Binding="{Binding ElementName=areaBusyIndicator, Path=Visibility}" Value="Visible">
|
||||||
|
<Setter Property="Button.IsEnabled" Value="False"/>
|
||||||
|
</DataTrigger>
|
||||||
|
<MultiDataTrigger>
|
||||||
|
<MultiDataTrigger.Conditions>
|
||||||
|
<Condition Binding="{Binding ElementName=lstImages, Path=SelectedItem.Package.Metadata.IsModified}" Value="False" />
|
||||||
|
<Condition Binding="{Binding Path=IsExtractedFromPackage}" Value="False" />
|
||||||
|
</MultiDataTrigger.Conditions>
|
||||||
|
<Setter Property="Button.IsEnabled" Value="False"/>
|
||||||
|
</MultiDataTrigger>
|
||||||
|
</Style.Triggers>
|
||||||
|
</Style>
|
||||||
|
</Button.Style>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button Name="btnSaveBinary" Click="BtnSaveBinary_OnClick" DockPanel.Dock="Top" Margin="10,2,10,10" Padding="5" HorizontalAlignment="Left" VerticalAlignment="Top" FontSize="14" Width="260" Content="Save extracted/decrypted binary...">
|
||||||
|
<Button.Style>
|
||||||
|
<Style BasedOn="{StaticResource LightBoxButton}" TargetType="{x:Type Button}">
|
||||||
|
<Style.Triggers>
|
||||||
|
<DataTrigger Binding="{Binding ElementName=areaBusyIndicator, Path=Visibility}" Value="Visible">
|
||||||
|
<Setter Property="Button.IsEnabled" Value="False"/>
|
||||||
|
</DataTrigger>
|
||||||
|
<MultiDataTrigger>
|
||||||
|
<MultiDataTrigger.Conditions>
|
||||||
|
<Condition Binding="{Binding ElementName=lstImages, Path=SelectedItem.Package.Binary.IsModified}" Value="False" />
|
||||||
|
<Condition Binding="{Binding Path=IsExtractedFromPackage}" Value="False" />
|
||||||
|
</MultiDataTrigger.Conditions>
|
||||||
|
<Setter Property="Button.IsEnabled" Value="False"/>
|
||||||
|
</MultiDataTrigger>
|
||||||
|
</Style.Triggers>
|
||||||
|
</Style>
|
||||||
|
</Button.Style>
|
||||||
|
</Button>
|
||||||
</DockPanel>
|
</DockPanel>
|
||||||
<Separator Grid.Row="0" Grid.Column="1" Grid.RowSpan="2" Margin="5,5,10,5">
|
<Separator Grid.Row="0" Grid.Column="1" Grid.RowSpan="2" Margin="5,5,10,5">
|
||||||
<Separator.LayoutTransform>
|
<Separator.LayoutTransform>
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ using Il2CppInspector.Reflection;
|
|||||||
using Ookii.Dialogs.Wpf;
|
using Ookii.Dialogs.Wpf;
|
||||||
using Path = System.IO.Path;
|
using Path = System.IO.Path;
|
||||||
using Il2CppInspector.Cpp.UnityHeaders;
|
using Il2CppInspector.Cpp.UnityHeaders;
|
||||||
|
using System.IO.Packaging;
|
||||||
|
|
||||||
namespace Il2CppInspectorGUI
|
namespace Il2CppInspectorGUI
|
||||||
{
|
{
|
||||||
@@ -42,6 +43,9 @@ namespace Il2CppInspectorGUI
|
|||||||
public MainWindow() {
|
public MainWindow() {
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
|
// Allow XAML to access properties in the App class
|
||||||
|
DataContext = ((App) Application.Current);
|
||||||
|
|
||||||
// Subscribe to status update events
|
// Subscribe to status update events
|
||||||
((App) Application.Current).OnStatusUpdate += OnStatusUpdate;
|
((App) Application.Current).OnStatusUpdate += OnStatusUpdate;
|
||||||
|
|
||||||
@@ -314,6 +318,43 @@ namespace Il2CppInspectorGUI
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Save extracted or decrypted files
|
||||||
|
/// </summary>
|
||||||
|
private async void BtnSaveMetadata_OnClick(object sender, RoutedEventArgs e) {
|
||||||
|
var package = ((AppModel) lstImages.SelectedItem).TypeModel.Package;
|
||||||
|
var saveFileDialog = new SaveFileDialog {
|
||||||
|
Filter = "IL2CPP metadata files (*.dat)|*.dat|All files (*.*)|*.*",
|
||||||
|
FileName = "global-metadata.dat",
|
||||||
|
CheckFileExists = false,
|
||||||
|
OverwritePrompt = true
|
||||||
|
};
|
||||||
|
|
||||||
|
if (saveFileDialog.ShowDialog() == false)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var outPath = saveFileDialog.FileName;
|
||||||
|
await Task.Run(() => package.SaveMetadataToFile(outPath));
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void BtnSaveBinary_OnClick(object sender, RoutedEventArgs e) {
|
||||||
|
var package = ((AppModel) lstImages.SelectedItem).TypeModel.Package;
|
||||||
|
var binaryName = package.BinaryImage.DefaultFilename;
|
||||||
|
|
||||||
|
var saveFileDialog = new SaveFileDialog {
|
||||||
|
Filter = "All files (*.*)|*.*",
|
||||||
|
FileName = binaryName,
|
||||||
|
CheckFileExists = false,
|
||||||
|
OverwritePrompt = true
|
||||||
|
};
|
||||||
|
|
||||||
|
if (saveFileDialog.ShowDialog() == false)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var outPath = saveFileDialog.FileName;
|
||||||
|
await Task.Run(() => package.SaveBinaryToFile(outPath));
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Perform export
|
/// Perform export
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
Reference in New Issue
Block a user