Compare commits
25 Commits
refactored
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4befde8ab4 | ||
|
|
6d674ecc8c | ||
|
|
8b93dda191 | ||
|
|
bba8a2913a | ||
|
|
193395db29 | ||
|
|
481d05668d | ||
|
|
ca6c958f9a | ||
|
|
7a621b40c6 | ||
|
|
1a418280fb | ||
|
|
f1a69cafe3 | ||
|
|
e5f2fa703d | ||
|
|
38aa333764 | ||
|
|
3ae9dba60d | ||
|
|
c94bc1c225 | ||
|
|
ac361bd90f | ||
|
|
a88e91451a | ||
|
|
079373815f | ||
|
|
3439ca912b | ||
|
|
e161e0f226 | ||
|
|
ec76447122 | ||
|
|
4e46c29cee | ||
|
|
507c42024e | ||
|
|
8403f8a62d | ||
|
|
5254c4b85a | ||
|
|
b05c03964a |
121
.github/workflows/build.yml
vendored
121
.github/workflows/build.yml
vendored
@@ -3,7 +3,111 @@ name: Il2CppInspectorRedux Build
|
|||||||
on: [push, workflow_dispatch]
|
on: [push, workflow_dispatch]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-gui:
|
build-redux-gui: # this already includes stuff only relevant for linux/macos for when the gui is released on those platforms
|
||||||
|
runs-on: windows-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: true
|
||||||
|
|
||||||
|
- name: Setup .NET SDK
|
||||||
|
uses: actions/setup-dotnet@v4
|
||||||
|
with:
|
||||||
|
dotnet-version: '9.0.x'
|
||||||
|
|
||||||
|
- name: Setup pnpm
|
||||||
|
uses: pnpm/action-setup@v4
|
||||||
|
with:
|
||||||
|
package_json_file: Il2CppInspector.Redux.GUI.UI/package.json
|
||||||
|
|
||||||
|
- name: Setup Node.JS
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: lts/*
|
||||||
|
cache: "pnpm"
|
||||||
|
cache-dependency-path: Il2CppInspector.Redux.GUI.UI/pnpm-lock.yaml
|
||||||
|
|
||||||
|
- name: Setup Rust
|
||||||
|
uses: dtolnay/rust-toolchain@stable
|
||||||
|
with:
|
||||||
|
targets: ${{ matrix.platform == 'macos-latest' && 'aarch64-apple-darwin,x86_64-apple-darwin' || '' }}
|
||||||
|
|
||||||
|
- name: Setup Tauri dependencies (ubuntu only)
|
||||||
|
if: matrix.platform == 'ubuntu-22.04'
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf
|
||||||
|
|
||||||
|
- name: Install frontend dependencies
|
||||||
|
run: pnpm install
|
||||||
|
working-directory: ./Il2CppInspector.Redux.GUI.UI
|
||||||
|
|
||||||
|
- name: Cache NuGet packages
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: ~/.nuget/packages
|
||||||
|
key: ${{ runner.os }}-nuget-redux-gui-${{ hashFiles('**/packages.lock.json') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-nuget-redux-gui-
|
||||||
|
|
||||||
|
- name: Restore NuGet packages
|
||||||
|
run: dotnet restore -r win-x64 ./Il2CppInspector.Redux.GUI
|
||||||
|
|
||||||
|
# note: we embed the exe directly into the c# component, and it it is built alongside it
|
||||||
|
# in another msbuild target.
|
||||||
|
- name: Build GUI
|
||||||
|
run: dotnet publish ./Il2CppInspector.Redux.GUI/Il2CppInspector.Redux.GUI.csproj -r win-x64 --no-self-contained
|
||||||
|
|
||||||
|
- name: Copy components to output directory
|
||||||
|
run: |
|
||||||
|
mkdir ./build_output
|
||||||
|
cp ./Il2CppInspector.Redux.GUI/bin/Release/net9.0/win-x64/publish/Il2CppInspector.Redux.GUI.exe ./build_output/
|
||||||
|
|
||||||
|
- name: Upload GUI Artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: Il2CppInspectorRedux.GUI
|
||||||
|
path: build_output
|
||||||
|
|
||||||
|
build-redux-cli:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
dotnet-version: [ '9.0.x' ]
|
||||||
|
rid: ['win-x64', 'linux-x64', 'linux-arm64', 'osx-x64', 'osx-arm64']
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: true
|
||||||
|
|
||||||
|
- name: Setup .NET SDK ${{ matrix.dotnet-version }}
|
||||||
|
uses: actions/setup-dotnet@v4
|
||||||
|
with:
|
||||||
|
dotnet-version: ${{ matrix.dotnet-version }}
|
||||||
|
|
||||||
|
- name: Cache NuGet packages
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: ~/.nuget/packages
|
||||||
|
key: ${{ runner.os }}-nuget-cli-${{ matrix.rid }}-${{ hashFiles('**/packages.lock.json') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-nuget-cli-${{ matrix.rid }}-
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: dotnet restore -r ${{ matrix.rid }} ./Il2CppInspector.Redux.CLI
|
||||||
|
|
||||||
|
- name: Build & Publish
|
||||||
|
run: dotnet publish -c Release --no-self-contained --no-restore -o ./${{ matrix.rid }} -r ${{ matrix.rid }} ./Il2CppInspector.Redux.CLI/Il2CppInspector.Redux.CLI.csproj
|
||||||
|
|
||||||
|
- name: Upload artifacts
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: Il2CppInspectorRedux.CLI-${{ matrix.rid }}
|
||||||
|
path: ./${{ matrix.rid }}
|
||||||
|
|
||||||
|
build-old-gui:
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
@@ -32,10 +136,10 @@ jobs:
|
|||||||
- name: Upload GUI Artifact
|
- name: Upload GUI Artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: Il2CppInspectorRedux.GUI
|
name: Il2CppInspectorRedux.Old.GUI
|
||||||
path: Il2CppInspector.GUI/bin/Release/net9.0-windows/win-x64/publish
|
path: Il2CppInspector.GUI/bin/Release/net9.0-windows/win-x64/publish
|
||||||
|
|
||||||
build-cli:
|
build-old-cli:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
@@ -47,10 +151,10 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
|
|
||||||
- name: Setup .NET SDK
|
- name: Setup .NET SDK ${{ matrix.dotnet-version }}
|
||||||
uses: actions/setup-dotnet@v4
|
uses: actions/setup-dotnet@v4
|
||||||
with:
|
with:
|
||||||
dotnet-version: '9.0.x'
|
dotnet-version: ${{ matrix.dotnet-version }}
|
||||||
|
|
||||||
- uses: actions/cache@v3
|
- uses: actions/cache@v3
|
||||||
with:
|
with:
|
||||||
@@ -59,11 +163,6 @@ jobs:
|
|||||||
restore-keys: |
|
restore-keys: |
|
||||||
${{ runner.os }}-nuget-cli-${{ matrix.rid }}-
|
${{ runner.os }}-nuget-cli-${{ matrix.rid }}-
|
||||||
|
|
||||||
- name: Setup .NET SDK ${{ matrix.dotnet-version }}
|
|
||||||
uses: actions/setup-dotnet@v3
|
|
||||||
with:
|
|
||||||
dotnet-version: ${{ matrix.dotnet-version }}
|
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: dotnet restore -r ${{ matrix.rid }} ./Il2CppInspector.CLI
|
run: dotnet restore -r ${{ matrix.rid }} ./Il2CppInspector.CLI
|
||||||
|
|
||||||
@@ -73,5 +172,5 @@ jobs:
|
|||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: Il2CppInspectorRedux.CLI-${{ matrix.rid }}
|
name: Il2CppInspectorRedux.Old.CLI-${{ matrix.rid }}
|
||||||
path: ./${{ matrix.rid }}
|
path: ./${{ matrix.rid }}
|
||||||
|
|||||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -268,3 +268,6 @@ paket-files/
|
|||||||
/Il2CppInspector.sln.DotSettings
|
/Il2CppInspector.sln.DotSettings
|
||||||
/Il2CppTests/samples
|
/Il2CppTests/samples
|
||||||
/Il2CppTests/TestCpp
|
/Il2CppTests/TestCpp
|
||||||
|
|
||||||
|
# for dotnet-tools.json
|
||||||
|
.config
|
||||||
2
.gitmodules
vendored
2
.gitmodules
vendored
@@ -1,3 +1,3 @@
|
|||||||
[submodule "Bin2Object"]
|
[submodule "Bin2Object"]
|
||||||
path = Bin2Object
|
path = Bin2Object
|
||||||
url = https://github.com/djkaty/Bin2Object
|
url = https://github.com/LukeFZ/Bin2Object
|
||||||
|
|||||||
Submodule Bin2Object updated: 3b09026a0d...603021c8ee
File diff suppressed because it is too large
Load Diff
@@ -4,8 +4,6 @@
|
|||||||
All rights reserved.
|
All rights reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace Il2CppInspector.Cpp
|
namespace Il2CppInspector.Cpp
|
||||||
{
|
{
|
||||||
// A field in a C++ type
|
// A field in a C++ type
|
||||||
|
|||||||
@@ -5,9 +5,6 @@
|
|||||||
All rights reserved.
|
All rights reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Il2CppInspector.Cpp
|
namespace Il2CppInspector.Cpp
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -65,9 +62,9 @@ namespace Il2CppInspector.Cpp
|
|||||||
// Uniquely name an object within the parent namespace
|
// Uniquely name an object within the parent namespace
|
||||||
public string GetName(T t) {
|
public string GetName(T t) {
|
||||||
// If we've named this particular object before, just return that name
|
// If we've named this particular object before, just return that name
|
||||||
string name;
|
if (names.TryGetValue(t, out var name))
|
||||||
if (names.TryGetValue(t, out name))
|
|
||||||
return name;
|
return name;
|
||||||
|
|
||||||
// Obtain the mangled name for the object
|
// Obtain the mangled name for the object
|
||||||
name = keyFunc(t);
|
name = keyFunc(t);
|
||||||
// Check if the mangled name has been given to another object - if it has,
|
// Check if the mangled name has been given to another object - if it has,
|
||||||
|
|||||||
@@ -456,4 +456,14 @@ namespace Il2CppInspector.Cpp
|
|||||||
return sb.ToString();
|
return sb.ToString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class CppForwardDefinitionType : CppType
|
||||||
|
{
|
||||||
|
public CppForwardDefinitionType(string name) : base(name)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString(string format = "") => $"struct {Name};";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,15 +4,11 @@
|
|||||||
All rights reserved.
|
All rights reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
using System;
|
using Il2CppInspector.Cpp.UnityHeaders;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using Il2CppInspector.Cpp.UnityHeaders;
|
|
||||||
|
|
||||||
namespace Il2CppInspector.Cpp
|
namespace Il2CppInspector.Cpp
|
||||||
{
|
{
|
||||||
@@ -23,7 +19,7 @@ namespace Il2CppInspector.Cpp
|
|||||||
public Dictionary<string, CppType> Types { get; }
|
public Dictionary<string, CppType> Types { get; }
|
||||||
|
|
||||||
// All of the literal typedef aliases
|
// All of the literal typedef aliases
|
||||||
public Dictionary<string, CppType> TypedefAliases { get; } = new Dictionary<string, CppType>();
|
public Dictionary<string, CppType> TypedefAliases { get; } = [];
|
||||||
|
|
||||||
public CppType this[string s] => Types.ContainsKey(s)? Types[s] :
|
public CppType this[string s] => Types.ContainsKey(s)? Types[s] :
|
||||||
TypedefAliases.ContainsKey(s)? TypedefAliases[s].AsAlias(s) : null;
|
TypedefAliases.ContainsKey(s)? TypedefAliases[s].AsAlias(s) : null;
|
||||||
@@ -34,7 +30,8 @@ namespace Il2CppInspector.Cpp
|
|||||||
// Architecture width in bits (32/64) - to determine pointer sizes
|
// Architecture width in bits (32/64) - to determine pointer sizes
|
||||||
public int WordSize { get; }
|
public int WordSize { get; }
|
||||||
|
|
||||||
private Dictionary<string, ComplexValueType> complexTypeMap = new Dictionary<string, ComplexValueType> {
|
private Dictionary<string, ComplexValueType> complexTypeMap = new()
|
||||||
|
{
|
||||||
["struct"] = ComplexValueType.Struct,
|
["struct"] = ComplexValueType.Struct,
|
||||||
["union"] = ComplexValueType.Union,
|
["union"] = ComplexValueType.Union,
|
||||||
["enum"] = ComplexValueType.Enum
|
["enum"] = ComplexValueType.Enum
|
||||||
@@ -44,22 +41,23 @@ namespace Il2CppInspector.Cpp
|
|||||||
private string currentGroup = string.Empty;
|
private string currentGroup = string.Empty;
|
||||||
public void SetGroup(string group) => currentGroup = group;
|
public void SetGroup(string group) => currentGroup = group;
|
||||||
|
|
||||||
private static readonly List<CppType> primitiveTypes = new List<CppType> {
|
private static readonly List<CppType> primitiveTypes =
|
||||||
new CppType("uint8_t", 8),
|
[
|
||||||
new CppType("uint16_t", 16),
|
new("uint8_t", 8),
|
||||||
new CppType("uint32_t", 32),
|
new("uint16_t", 16),
|
||||||
new CppType("uint64_t", 64),
|
new("uint32_t", 32),
|
||||||
new CppType("int8_t", 8),
|
new("uint64_t", 64),
|
||||||
new CppType("int16_t", 16),
|
new("int8_t", 8),
|
||||||
new CppType("int32_t", 32),
|
new("int16_t", 16),
|
||||||
new CppType("int64_t", 64),
|
new("int32_t", 32),
|
||||||
new CppType("char", 8),
|
new("int64_t", 64),
|
||||||
new CppType("int", 32),
|
new("char", 8),
|
||||||
new CppType("float", 32),
|
new("int", 32),
|
||||||
new CppType("double", 64),
|
new("float", 32),
|
||||||
new CppType("bool", 8),
|
new("double", 64),
|
||||||
new CppType("void", 0)
|
new("bool", 8),
|
||||||
};
|
new("void", 0)
|
||||||
|
];
|
||||||
|
|
||||||
public CppTypeCollection(int wordSize) {
|
public CppTypeCollection(int wordSize) {
|
||||||
if (wordSize != 32 && wordSize != 64)
|
if (wordSize != 32 && wordSize != 64)
|
||||||
@@ -538,15 +536,18 @@ namespace Il2CppInspector.Cpp
|
|||||||
public CppComplexType Struct(string name = "", int alignmentBytes = 0) {
|
public CppComplexType Struct(string name = "", int alignmentBytes = 0) {
|
||||||
if (!string.IsNullOrEmpty(name) && Types.TryGetValue(name, out var cppType))
|
if (!string.IsNullOrEmpty(name) && Types.TryGetValue(name, out var cppType))
|
||||||
return (CppComplexType) cppType;
|
return (CppComplexType) cppType;
|
||||||
|
|
||||||
var type = new CppComplexType(ComplexValueType.Struct) {Name = name, Group = currentGroup, AlignmentBytes = alignmentBytes};
|
var type = new CppComplexType(ComplexValueType.Struct) {Name = name, Group = currentGroup, AlignmentBytes = alignmentBytes};
|
||||||
if (!string.IsNullOrEmpty(name))
|
if (!string.IsNullOrEmpty(name))
|
||||||
Add(type);
|
Add(type);
|
||||||
|
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
public CppComplexType Union(string name = "", int alignmentBytes = 0) {
|
public CppComplexType Union(string name = "", int alignmentBytes = 0) {
|
||||||
if (!string.IsNullOrEmpty(name) && Types.TryGetValue(name, out var cppType))
|
if (!string.IsNullOrEmpty(name) && Types.TryGetValue(name, out var cppType))
|
||||||
return (CppComplexType) cppType;
|
return (CppComplexType) cppType;
|
||||||
|
|
||||||
var type = new CppComplexType(ComplexValueType.Union) {Name = name, Group = currentGroup, AlignmentBytes = alignmentBytes};
|
var type = new CppComplexType(ComplexValueType.Union) {Name = name, Group = currentGroup, AlignmentBytes = alignmentBytes};
|
||||||
if (!string.IsNullOrEmpty(name))
|
if (!string.IsNullOrEmpty(name))
|
||||||
Add(type);
|
Add(type);
|
||||||
@@ -554,9 +555,13 @@ namespace Il2CppInspector.Cpp
|
|||||||
}
|
}
|
||||||
|
|
||||||
public CppEnumType Enum(CppType underlyingType, string name = "") {
|
public CppEnumType Enum(CppType underlyingType, string name = "") {
|
||||||
|
if (!string.IsNullOrEmpty(name) && Types.TryGetValue(name, out var cppType))
|
||||||
|
return (CppEnumType)cppType;
|
||||||
|
|
||||||
var type = new CppEnumType(underlyingType) {Name = name, Group = currentGroup};
|
var type = new CppEnumType(underlyingType) {Name = name, Group = currentGroup};
|
||||||
if (!string.IsNullOrEmpty(name))
|
if (!string.IsNullOrEmpty(name))
|
||||||
Add(type);
|
Add(type);
|
||||||
|
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -585,11 +590,17 @@ namespace Il2CppInspector.Cpp
|
|||||||
cppTypes.AddFromDeclarationText(apis);
|
cppTypes.AddFromDeclarationText(apis);
|
||||||
|
|
||||||
// Don't allow any of the header type names or primitive type names to be re-used
|
// Don't allow any of the header type names or primitive type names to be re-used
|
||||||
foreach (var type in cppTypes.Types.Values)
|
foreach (var type in cppTypes.Types.Keys)
|
||||||
declGen?.TypeNamespace.TryReserveName(type.Name);
|
{
|
||||||
|
declGen?.TypeNamespace.TryReserveName(type);
|
||||||
|
declGen?.GlobalsNamespace.TryReserveName(type);
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var typedef in cppTypes.TypedefAliases.Values)
|
foreach (var typedef in cppTypes.TypedefAliases.Keys)
|
||||||
declGen?.GlobalsNamespace.TryReserveName(typedef.Name);
|
{
|
||||||
|
declGen?.TypeNamespace.TryReserveName(typedef);
|
||||||
|
declGen?.GlobalsNamespace.TryReserveName(typedef);
|
||||||
|
}
|
||||||
|
|
||||||
cppTypes.SetGroup("");
|
cppTypes.SetGroup("");
|
||||||
|
|
||||||
|
|||||||
2421
Il2CppInspector.Common/Cpp/UnityHeaders/35-6000.3.0a2.h
Normal file
2421
Il2CppInspector.Common/Cpp/UnityHeaders/35-6000.3.0a2.h
Normal file
File diff suppressed because it is too large
Load Diff
2421
Il2CppInspector.Common/Cpp/UnityHeaders/38-6000.3.0a5.h
Normal file
2421
Il2CppInspector.Common/Cpp/UnityHeaders/38-6000.3.0a5.h
Normal file
File diff suppressed because it is too large
Load Diff
2421
Il2CppInspector.Common/Cpp/UnityHeaders/39-6000.3.0b1.h
Normal file
2421
Il2CppInspector.Common/Cpp/UnityHeaders/39-6000.3.0b1.h
Normal file
File diff suppressed because it is too large
Load Diff
@@ -5,13 +5,14 @@
|
|||||||
All rights reserved.
|
All rights reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
using Il2CppInspector.Next;
|
||||||
|
using Spectre.Console;
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
using System.Linq;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using Il2CppInspector.Next;
|
|
||||||
using VersionedSerialization;
|
using VersionedSerialization;
|
||||||
|
|
||||||
namespace Il2CppInspector.Cpp.UnityHeaders
|
namespace Il2CppInspector.Cpp.UnityHeaders
|
||||||
@@ -140,7 +141,7 @@ namespace Il2CppInspector.Cpp.UnityHeaders
|
|||||||
// No il2cpp exports? Just return the earliest version from the header range
|
// No il2cpp exports? Just return the earliest version from the header range
|
||||||
// The API version may be incorrect but should be a subset of the real API and won't cause C++ compile errors
|
// The API version may be incorrect but should be a subset of the real API and won't cause C++ compile errors
|
||||||
if (!exports.Any()) {
|
if (!exports.Any()) {
|
||||||
Console.WriteLine("No IL2CPP API exports found in binary - IL2CPP APIs will be unavailable in C++ project");
|
AnsiConsole.WriteLine("No IL2CPP API exports found in binary - IL2CPP APIs will be unavailable in C++ project");
|
||||||
|
|
||||||
return typeHeaders.Select(t => new UnityHeaders(t,
|
return typeHeaders.Select(t => new UnityHeaders(t,
|
||||||
apis.Last(a => a.VersionRange.Intersect(t.VersionRange) != null))).ToList();
|
apis.Last(a => a.VersionRange.Intersect(t.VersionRange) != null))).ToList();
|
||||||
@@ -161,7 +162,7 @@ namespace Il2CppInspector.Cpp.UnityHeaders
|
|||||||
|
|
||||||
if (apiMatches.Any()) {
|
if (apiMatches.Any()) {
|
||||||
// Intersect all API ranges with all header ranges to produce final list of possible ranges
|
// Intersect all API ranges with all header ranges to produce final list of possible ranges
|
||||||
Console.WriteLine("IL2CPP API discovery was successful");
|
AnsiConsole.WriteLine("IL2CPP API discovery was successful");
|
||||||
|
|
||||||
return typeHeaders.SelectMany(
|
return typeHeaders.SelectMany(
|
||||||
t => apiMatches.Where(a => t.VersionRange.Intersect(a.VersionRange) != null)
|
t => apiMatches.Where(a => t.VersionRange.Intersect(a.VersionRange) != null)
|
||||||
@@ -170,7 +171,7 @@ namespace Il2CppInspector.Cpp.UnityHeaders
|
|||||||
|
|
||||||
// None of the possible API versions match the binary
|
// None of the possible API versions match the binary
|
||||||
// Select the oldest API version from the group - C++ project compilation will fail
|
// Select the oldest API version from the group - C++ project compilation will fail
|
||||||
Console.WriteLine("No exact match for IL2CPP APIs found in binary - IL2CPP API availability in C++ project will be partial");
|
AnsiConsole.WriteLine("No exact match for IL2CPP APIs found in binary - IL2CPP API availability in C++ project will be partial");
|
||||||
|
|
||||||
return typeHeaders.Select(t => new UnityHeaders(t,
|
return typeHeaders.Select(t => new UnityHeaders(t,
|
||||||
apis.Last(a => a.VersionRange.Intersect(t.VersionRange) != null))).ToList();
|
apis.Last(a => a.VersionRange.Intersect(t.VersionRange) != null))).ToList();
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
All rights reserved.
|
All rights reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
using Spectre.Console;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
@@ -51,7 +52,7 @@ namespace Il2CppInspector
|
|||||||
|
|
||||||
public override IFileFormatStream this[uint index] {
|
public override IFileFormatStream this[uint index] {
|
||||||
get {
|
get {
|
||||||
Console.WriteLine($"Extracting binary from {binaryFiles[index].FullName}");
|
AnsiConsole.WriteLine($"Extracting binary from {binaryFiles[index].FullName}");
|
||||||
IFileFormatStream loaded = null;
|
IFileFormatStream loaded = null;
|
||||||
|
|
||||||
// ZipArchiveEntry does not support seeking so we have to close and re-open for each possible load format
|
// ZipArchiveEntry does not support seeking so we have to close and re-open for each possible load format
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
All rights reserved.
|
All rights reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
using Spectre.Console;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
@@ -51,7 +52,7 @@ namespace Il2CppInspector
|
|||||||
|
|
||||||
public override IFileFormatStream this[uint index] {
|
public override IFileFormatStream this[uint index] {
|
||||||
get {
|
get {
|
||||||
Console.WriteLine($"Extracting binary from {binaryFiles[index].FullName}");
|
AnsiConsole.WriteLine($"Extracting binary from {binaryFiles[index].FullName}");
|
||||||
IFileFormatStream loaded = null;
|
IFileFormatStream loaded = null;
|
||||||
|
|
||||||
// ZipArchiveEntry does not support seeking so we have to close and re-open for each possible load format
|
// ZipArchiveEntry does not support seeking so we have to close and re-open for each possible load format
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
All rights reserved.
|
All rights reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
using Spectre.Console;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@@ -160,7 +161,7 @@ namespace Il2CppInspector
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"Got exception {ex} while parsing SHT - reverting to PHT");
|
AnsiConsole.WriteLine($"Got exception {ex} while parsing SHT - reverting to PHT");
|
||||||
preferPHT = true;
|
preferPHT = true;
|
||||||
SHT = [];
|
SHT = [];
|
||||||
}
|
}
|
||||||
@@ -170,12 +171,12 @@ namespace Il2CppInspector
|
|||||||
// These can happen as a result of conversions from other formats to ELF,
|
// These can happen as a result of conversions from other formats to ELF,
|
||||||
// or if the SHT has been deliberately stripped
|
// or if the SHT has been deliberately stripped
|
||||||
if (!SHT.Any()) {
|
if (!SHT.Any()) {
|
||||||
Console.WriteLine("ELF binary has no SHT - reverting to PHT");
|
AnsiConsole.WriteLine("ELF binary has no SHT - reverting to PHT");
|
||||||
preferPHT = true;
|
preferPHT = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (SHT.All(s => conv.ULong(s.sh_addr) == 0ul)) {
|
else if (SHT.All(s => conv.ULong(s.sh_addr) == 0ul)) {
|
||||||
Console.WriteLine("ELF binary SHT is all-zero - reverting to PHT");
|
AnsiConsole.WriteLine("ELF binary SHT is all-zero - reverting to PHT");
|
||||||
preferPHT = true;
|
preferPHT = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -192,7 +193,7 @@ namespace Il2CppInspector
|
|||||||
|
|
||||||
// If the first file offset of the first PHT is zero, assume a dumped image
|
// If the first file offset of the first PHT is zero, assume a dumped image
|
||||||
if (PHT.Any(t => conv.ULong(t.p_vaddr) == 0ul)) {
|
if (PHT.Any(t => conv.ULong(t.p_vaddr) == 0ul)) {
|
||||||
Console.WriteLine("ELF binary appears to be a dumped memory image");
|
AnsiConsole.WriteLine("ELF binary appears to be a dumped memory image");
|
||||||
isMemoryImage = true;
|
isMemoryImage = true;
|
||||||
}
|
}
|
||||||
preferPHT = true;
|
preferPHT = true;
|
||||||
@@ -202,7 +203,7 @@ namespace Il2CppInspector
|
|||||||
else {
|
else {
|
||||||
var shtOverlap = shtShouldBeOrdered.Aggregate((x, y) => x <= y? y : ulong.MaxValue) == ulong.MaxValue;
|
var shtOverlap = shtShouldBeOrdered.Aggregate((x, y) => x <= y? y : ulong.MaxValue) == ulong.MaxValue;
|
||||||
if (shtOverlap) {
|
if (shtOverlap) {
|
||||||
Console.WriteLine("ELF binary SHT contains invalid ranges - reverting to PHT");
|
AnsiConsole.WriteLine("ELF binary SHT contains invalid ranges - reverting to PHT");
|
||||||
preferPHT = true;
|
preferPHT = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -223,7 +224,20 @@ namespace Il2CppInspector
|
|||||||
|
|
||||||
// Get dynamic table if it exists (must be done after rebasing)
|
// Get dynamic table if it exists (must be done after rebasing)
|
||||||
if (GetProgramHeader(Elf.PT_DYNAMIC) is TPHdr PT_DYNAMIC)
|
if (GetProgramHeader(Elf.PT_DYNAMIC) is TPHdr PT_DYNAMIC)
|
||||||
DynamicTable = ReadArray<elf_dynamic<TWord>>(conv.Long(PT_DYNAMIC.p_offset), (int) (conv.Long(PT_DYNAMIC.p_filesz) / Sizeof(typeof(elf_dynamic<TWord>))));
|
{
|
||||||
|
// Important: do not use p_offset here!
|
||||||
|
// Only load sections should be loaded, which should also include the memory region that contains the dynamic section.
|
||||||
|
// This just provides the virtual address of the section.
|
||||||
|
// Some binaries may use the offset here to point to a fake version of the dynamic section,
|
||||||
|
// making relocation resolution and subsequent analysis fail.
|
||||||
|
// Reference for Android:
|
||||||
|
// phdr_table_get_dynamic_section, https://cs.android.com/android/platform/superproject/main/+/main:bionic/linker/linker_phdr.cpp
|
||||||
|
|
||||||
|
var dynamicAddr = conv.ULong(PT_DYNAMIC.p_vaddr);
|
||||||
|
var dynamicSize = (int)(conv.Long(PT_DYNAMIC.p_filesz) / Sizeof(typeof(elf_dynamic<TWord>)));
|
||||||
|
|
||||||
|
DynamicTable = ReadMappedArray<elf_dynamic<TWord>>(dynamicAddr, dynamicSize);
|
||||||
|
}
|
||||||
|
|
||||||
// Get offset of code section
|
// Get offset of code section
|
||||||
var codeSegment = PHT.First(x => ((Elf) x.p_flags & Elf.PF_X) == Elf.PF_X);
|
var codeSegment = PHT.First(x => ((Elf) x.p_flags & Elf.PF_X) == Elf.PF_X);
|
||||||
@@ -254,21 +268,6 @@ namespace Il2CppInspector
|
|||||||
|
|
||||||
StatusUpdate("Finding relocations");
|
StatusUpdate("Finding relocations");
|
||||||
|
|
||||||
// Two types: add value from offset in image, and add value from specified addend
|
|
||||||
foreach (var relSection in GetSections(Elf.SHT_REL)) {
|
|
||||||
reverseMapExclusions.Add(((uint) conv.Int(relSection.sh_offset), (uint) (conv.Int(relSection.sh_offset) + conv.Int(relSection.sh_size) - 1)));
|
|
||||||
rels.UnionWith(
|
|
||||||
from rel in ReadArray<elf_rel<TWord>>(conv.Long(relSection.sh_offset), conv.Int(conv.Div(relSection.sh_size, relSection.sh_entsize)))
|
|
||||||
select new ElfReloc(rel, SHT[relSection.sh_link].sh_offset));
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var relaSection in GetSections(Elf.SHT_RELA)) {
|
|
||||||
reverseMapExclusions.Add(((uint) conv.Int(relaSection.sh_offset), (uint) (conv.Int(relaSection.sh_offset) + conv.Int(relaSection.sh_size) - 1)));
|
|
||||||
rels.UnionWith(
|
|
||||||
from rela in ReadArray<elf_rela<TWord>>(conv.Long(relaSection.sh_offset), conv.Int(conv.Div(relaSection.sh_size, relaSection.sh_entsize)))
|
|
||||||
select new ElfReloc(rela, SHT[relaSection.sh_link].sh_offset));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Relocations in dynamic section
|
// Relocations in dynamic section
|
||||||
if (GetDynamicEntry(Elf.DT_REL) is elf_dynamic<TWord> dt_rel) {
|
if (GetDynamicEntry(Elf.DT_REL) is elf_dynamic<TWord> dt_rel) {
|
||||||
var dt_rel_count = conv.Int(conv.Div(GetDynamicEntry(Elf.DT_RELSZ).d_un, GetDynamicEntry(Elf.DT_RELENT).d_un));
|
var dt_rel_count = conv.Int(conv.Div(GetDynamicEntry(Elf.DT_RELSZ).d_un, GetDynamicEntry(Elf.DT_RELENT).d_un));
|
||||||
@@ -291,7 +290,7 @@ namespace Il2CppInspector
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Process relocations
|
// Process relocations
|
||||||
var relsz = Sizeof(typeof(TSym));
|
var relsz = (uint)Sizeof(typeof(TSym));
|
||||||
|
|
||||||
var currentRel = 0;
|
var currentRel = 0;
|
||||||
var totalRel = rels.Count();
|
var totalRel = rels.Count();
|
||||||
@@ -301,7 +300,26 @@ namespace Il2CppInspector
|
|||||||
if (currentRel % 1000 == 0)
|
if (currentRel % 1000 == 0)
|
||||||
StatusUpdate($"Processing relocations ({currentRel * 100 / totalRel:F0}%)");
|
StatusUpdate($"Processing relocations ({currentRel * 100 / totalRel:F0}%)");
|
||||||
|
|
||||||
var symValue = ReadObject<TSym>(conv.Long(rel.SymbolTable) + conv.Long(rel.SymbolIndex) * relsz).st_value; // S
|
TWord symValue;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// man this really needs a full overhaul
|
||||||
|
symValue = ReadMappedObject<TSym>(conv.ULong(rel.SymbolTable) + conv.ULong(rel.SymbolIndex) * relsz)
|
||||||
|
.st_value; // S
|
||||||
|
}
|
||||||
|
catch (InvalidOperationException)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
symValue = ReadObject<TSym>(conv.Long(rel.SymbolTable) + conv.Long(rel.SymbolIndex) * relsz)
|
||||||
|
.st_value; // S
|
||||||
|
}
|
||||||
|
catch (InvalidOperationException)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Ignore relocations into memory addresses not mapped from the image
|
// Ignore relocations into memory addresses not mapped from the image
|
||||||
try {
|
try {
|
||||||
@@ -344,7 +362,7 @@ namespace Il2CppInspector
|
|||||||
WriteWord(result.newValue);
|
WriteWord(result.newValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Console.WriteLine($"Processed {rels.Count} relocations");
|
AnsiConsole.WriteLine($"Processed {rels.Count} relocations");
|
||||||
|
|
||||||
// Build symbol and export tables
|
// Build symbol and export tables
|
||||||
processSymbols();
|
processSymbols();
|
||||||
@@ -388,7 +406,8 @@ namespace Il2CppInspector
|
|||||||
WriteArray(conv.Long(PT_DYNAMIC.p_offset), dt);
|
WriteArray(conv.Long(PT_DYNAMIC.p_offset), dt);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processSymbols() {
|
private void processSymbols()
|
||||||
|
{
|
||||||
StatusUpdate("Processing symbols");
|
StatusUpdate("Processing symbols");
|
||||||
|
|
||||||
// Three possible symbol tables in ELF files
|
// Three possible symbol tables in ELF files
|
||||||
@@ -436,7 +455,15 @@ namespace Il2CppInspector
|
|||||||
symbolTable.Clear();
|
symbolTable.Clear();
|
||||||
var exportTable = new Dictionary<string, Export>();
|
var exportTable = new Dictionary<string, Export>();
|
||||||
|
|
||||||
foreach (var pTab in pTables) {
|
var alreadyProcessed = new List<(TWord offset, TWord count)>();
|
||||||
|
|
||||||
|
foreach (var pTab in pTables)
|
||||||
|
{
|
||||||
|
if (alreadyProcessed.Any(x =>
|
||||||
|
conv.ULong(x.offset) == conv.ULong(pTab.offset)))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
alreadyProcessed.Add((pTab.offset, pTab.count));
|
||||||
var symbol_table = ReadArray<TSym>(conv.Long(pTab.offset), conv.Int(pTab.count));
|
var symbol_table = ReadArray<TSym>(conv.Long(pTab.offset), conv.Int(pTab.count));
|
||||||
|
|
||||||
foreach (var symbol in symbol_table)
|
foreach (var symbol in symbol_table)
|
||||||
@@ -463,7 +490,7 @@ namespace Il2CppInspector
|
|||||||
var symbolItem = new Symbol {Name = name, Type = type, VirtualAddress = conv.ULong(symbol.st_value) };
|
var symbolItem = new Symbol {Name = name, Type = type, VirtualAddress = conv.ULong(symbol.st_value) };
|
||||||
symbolTable.TryAdd(name, symbolItem);
|
symbolTable.TryAdd(name, symbolItem);
|
||||||
if (symbol.st_shndx != (ushort) Elf.SHN_UNDEF)
|
if (symbol.st_shndx != (ushort) Elf.SHN_UNDEF)
|
||||||
exportTable.TryAdd(name, new Export {Name = symbolItem.DemangledName, VirtualAddress = conv.ULong(symbol.st_value)});
|
exportTable.TryAdd(name, new Export {Name = symbolItem.Name, VirtualAddress = conv.ULong(symbol.st_value)});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -176,7 +176,7 @@ namespace Il2CppInspector
|
|||||||
try {
|
try {
|
||||||
if (type.GetMethod("Load", BindingFlags.FlattenHierarchy | BindingFlags.Static | BindingFlags.Public,
|
if (type.GetMethod("Load", BindingFlags.FlattenHierarchy | BindingFlags.Static | BindingFlags.Public,
|
||||||
null, new[] { typeof(BinaryObjectStream), typeof(LoadOptions), typeof(EventHandler<string>) }, null)
|
null, new[] { typeof(BinaryObjectStream), typeof(LoadOptions), typeof(EventHandler<string>) }, null)
|
||||||
.Invoke(null, new object[] { binaryObjectStream, loadOptions, statusCallback }) is IFileFormatStream loaded) {
|
.Invoke(null, [binaryObjectStream, loadOptions, statusCallback]) is IFileFormatStream loaded) {
|
||||||
|
|
||||||
loaded.IsModified |= preProcessResult.IsStreamModified;
|
loaded.IsModified |= preProcessResult.IsStreamModified;
|
||||||
return loaded;
|
return loaded;
|
||||||
@@ -207,8 +207,6 @@ namespace Il2CppInspector
|
|||||||
|
|
||||||
public virtual string Arch => throw new NotImplementedException();
|
public virtual string Arch => throw new NotImplementedException();
|
||||||
|
|
||||||
public virtual int Bits => throw new NotImplementedException();
|
|
||||||
|
|
||||||
// Extra parameters to be passed to a loader
|
// Extra parameters to be passed to a loader
|
||||||
protected LoadOptions LoadOptions;
|
protected LoadOptions LoadOptions;
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using NoisyCowStudios.Bin2Object;
|
using NoisyCowStudios.Bin2Object;
|
||||||
|
using VersionedSerialization.Attributes;
|
||||||
|
|
||||||
namespace Il2CppInspector
|
namespace Il2CppInspector
|
||||||
{
|
{
|
||||||
@@ -31,6 +32,7 @@ namespace Il2CppInspector
|
|||||||
LC_DYLD_INFO_ONLY = 0x80000022,
|
LC_DYLD_INFO_ONLY = 0x80000022,
|
||||||
LC_FUNCTION_STARTS = 0x26,
|
LC_FUNCTION_STARTS = 0x26,
|
||||||
LC_ENCRYPTION_INFO_64 = 0x2C,
|
LC_ENCRYPTION_INFO_64 = 0x2C,
|
||||||
|
LC_DYLD_CHAINED_FIXUPS = 0x80000034,
|
||||||
|
|
||||||
CPU_TYPE_X86 = 7,
|
CPU_TYPE_X86 = 7,
|
||||||
CPU_TYPE_X86_64 = 0x01000000 + CPU_TYPE_X86,
|
CPU_TYPE_X86_64 = 0x01000000 + CPU_TYPE_X86,
|
||||||
@@ -172,4 +174,47 @@ namespace Il2CppInspector
|
|||||||
public bool r_extern => ((r_data >> 27) & 1) == 1;
|
public bool r_extern => ((r_data >> 27) & 1) == 1;
|
||||||
public uint r_type => r_data >> 28;
|
public uint r_type => r_data >> 28;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[VersionedStruct]
|
||||||
|
public partial struct MachODyldChainedFixupsHeader
|
||||||
|
{
|
||||||
|
public uint FixupsVersion;
|
||||||
|
public uint StartsOffset;
|
||||||
|
public uint ImportsOffset;
|
||||||
|
public uint SymbolsOffset;
|
||||||
|
public uint ImportsCount;
|
||||||
|
public uint ImportsFormat;
|
||||||
|
public uint SymbolsFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
[VersionedStruct]
|
||||||
|
public partial struct MachODyldChainedStartsInSegment
|
||||||
|
{
|
||||||
|
public const ushort DYLD_CHAINED_PTR_START_NONE = 0xffff;
|
||||||
|
|
||||||
|
public uint StructSize;
|
||||||
|
public ushort PageSize;
|
||||||
|
public ushort PointerFormat;
|
||||||
|
public ulong SegmentOffset;
|
||||||
|
public uint MaxValidPointer;
|
||||||
|
public ushort PageCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum MachODyldChainedPtr
|
||||||
|
{
|
||||||
|
DYLD_CHAINED_PTR_64 = 2,
|
||||||
|
DYLD_CHAINED_PTR_64_OFFSET = 6,
|
||||||
|
}
|
||||||
|
|
||||||
|
[VersionedStruct]
|
||||||
|
public partial struct MachODyldChainedPtr64Rebase
|
||||||
|
{
|
||||||
|
private ulong _value;
|
||||||
|
|
||||||
|
public ulong Target => _value & 0xfffffffff;
|
||||||
|
public ulong High8 => (_value >> 36) & 0xff;
|
||||||
|
public ulong Reserved => (_value >> (36 + 8)) & 0x7f;
|
||||||
|
public ulong Next => (_value >> (36 + 8 + 7)) & 0xfff;
|
||||||
|
public bool Bind => ((_value >> (36 + 8 + 7 + 12)) & 0x1) == 0x1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,12 +4,13 @@
|
|||||||
All rights reserved.
|
All rights reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
using NoisyCowStudios.Bin2Object;
|
||||||
|
using Spectre.Console;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NoisyCowStudios.Bin2Object;
|
|
||||||
|
|
||||||
namespace Il2CppInspector
|
namespace Il2CppInspector
|
||||||
{
|
{
|
||||||
@@ -172,15 +173,21 @@ namespace Il2CppInspector
|
|||||||
if (encryptionInfo.CryptID != 0)
|
if (encryptionInfo.CryptID != 0)
|
||||||
throw new NotImplementedException("This Mach-O executable is encrypted with FairPlay DRM and cannot be processed. Please provide a decrypted version of the executable.");
|
throw new NotImplementedException("This Mach-O executable is encrypted with FairPlay DRM and cannot be processed. Please provide a decrypted version of the executable.");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case MachO.LC_DYLD_CHAINED_FIXUPS:
|
||||||
|
var chainedFixupsInfo = ReadObject<MachOLinkEditDataCommand>();
|
||||||
|
ApplyChainedFixups(chainedFixupsInfo);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// There might be other data after the load command so always use the specified total size to step forwards
|
// There might be other data after the load command so always use the specified total size to step forwards
|
||||||
Position = startPos + loadCommand.Size;
|
Position = startPos + loadCommand.Size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Note: Some binaries do not have __mod_init_func, but instead just __init_offset with offsets to the init functions. This check is disabled.
|
||||||
// Must find __mod_init_func
|
// Must find __mod_init_func
|
||||||
if (funcTab == null)
|
//if (funcTab == null)
|
||||||
return false;
|
// return false;
|
||||||
|
|
||||||
// Process relocations
|
// Process relocations
|
||||||
foreach (var section in machoSections) {
|
foreach (var section in machoSections) {
|
||||||
@@ -188,7 +195,7 @@ namespace Il2CppInspector
|
|||||||
|
|
||||||
// TODO: Implement Mach-O relocations
|
// TODO: Implement Mach-O relocations
|
||||||
if (rels.Any()) {
|
if (rels.Any()) {
|
||||||
Console.WriteLine("Mach-O file contains relocations (feature not yet implemented)");
|
AnsiConsole.WriteLine("Mach-O file contains relocations (feature not yet implemented)");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -282,7 +289,7 @@ namespace Il2CppInspector
|
|||||||
: SymbolType.Unknown;
|
: SymbolType.Unknown;
|
||||||
|
|
||||||
if (type == SymbolType.Unknown) {
|
if (type == SymbolType.Unknown) {
|
||||||
Console.WriteLine($"Unknown symbol type: {((int) ntype):x2} {value:x16} " + CxxDemangler.CxxDemangler.Demangle(name));
|
AnsiConsole.WriteLine($"Unknown symbol type: {((int) ntype):x2} {value:x16} {name}");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ignore duplicates
|
// Ignore duplicates
|
||||||
@@ -290,7 +297,82 @@ namespace Il2CppInspector
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override uint[] GetFunctionTable() => ReadArray<TWord>(funcTab.ImageOffset, conv.Int(funcTab.Size) / (Bits / 8)).Select(x => MapVATR(conv.ULong(x)) & 0xffff_fffe).ToArray();
|
private void ApplyChainedFixups(in MachOLinkEditDataCommand info)
|
||||||
|
{
|
||||||
|
var chainedFixupsHeader = ReadVersionedObject<MachODyldChainedFixupsHeader>(info.Offset);
|
||||||
|
if (chainedFixupsHeader.FixupsVersion != 0)
|
||||||
|
{
|
||||||
|
AnsiConsole.WriteLine($"Unsupported chained fixups version: {chainedFixupsHeader.FixupsVersion}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chainedFixupsHeader.ImportsFormat != 1 /* DYLD_CHAINED_IMPORT */)
|
||||||
|
{
|
||||||
|
AnsiConsole.WriteLine($"Unsupported chained fixups import format: {chainedFixupsHeader.ImportsFormat}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//var importsBase = info.Offset + chainedFixupsHeader.ImportsOffset;
|
||||||
|
//var imports = ReadPrimitiveArray<uint>(importsBase,
|
||||||
|
// chainedFixupsHeader.ImportsCount);
|
||||||
|
|
||||||
|
//var symbolsBase = info.Offset + chainedFixupsHeader.SymbolsOffset; // todo: apparently this supports zlib
|
||||||
|
|
||||||
|
var startsBase = info.Offset + chainedFixupsHeader.StartsOffset;
|
||||||
|
var segmentCount = ReadPrimitive<uint>(startsBase);
|
||||||
|
var segmentStartOffsets = ReadPrimitiveArray<uint>(startsBase + 4, segmentCount);
|
||||||
|
|
||||||
|
foreach (var startOffset in segmentStartOffsets)
|
||||||
|
{
|
||||||
|
if (startOffset == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var startsInfo = ReadVersionedObject<MachODyldChainedStartsInSegment>(startsBase + startOffset);
|
||||||
|
if (startsInfo.SegmentOffset == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var pointerFormat = (MachODyldChainedPtr)startsInfo.PointerFormat;
|
||||||
|
|
||||||
|
var pages = ReadPrimitiveArray<ushort>(
|
||||||
|
startsBase + startOffset + MachODyldChainedStartsInSegment.Size(), startsInfo.PageCount);
|
||||||
|
|
||||||
|
for (var i = 0; i < pages.Length; i++)
|
||||||
|
{
|
||||||
|
var page = pages[i];
|
||||||
|
if (page == MachODyldChainedStartsInSegment.DYLD_CHAINED_PTR_START_NONE)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var chainOffset = startsInfo.SegmentOffset + (ulong)(i * startsInfo.PageSize) + page;
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
var currentEntry = ReadVersionedObject<MachODyldChainedPtr64Rebase>((long)chainOffset);
|
||||||
|
|
||||||
|
var fixedValue = 0ul;
|
||||||
|
|
||||||
|
if (!currentEntry.Bind) // todo: bind
|
||||||
|
{
|
||||||
|
fixedValue = pointerFormat switch
|
||||||
|
{
|
||||||
|
MachODyldChainedPtr.DYLD_CHAINED_PTR_64
|
||||||
|
or MachODyldChainedPtr.DYLD_CHAINED_PTR_64_OFFSET
|
||||||
|
=> currentEntry.High8 << 56 | currentEntry.Target,
|
||||||
|
_ => fixedValue
|
||||||
|
};
|
||||||
|
|
||||||
|
Write((long)chainOffset, fixedValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentEntry.Next == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
chainOffset += currentEntry.Next * 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override uint[] GetFunctionTable() => funcTab == null ? [] : ReadArray<TWord>(funcTab.ImageOffset, conv.Int(funcTab.Size) / (Bits / 8)).Select(x => MapVATR(conv.ULong(x)) & 0xffff_fffe).ToArray();
|
||||||
|
|
||||||
public override Dictionary<string, Symbol> GetSymbolTable() => symbolTable;
|
public override Dictionary<string, Symbol> GetSymbolTable() => symbolTable;
|
||||||
|
|
||||||
|
|||||||
@@ -4,13 +4,14 @@
|
|||||||
All rights reserved.
|
All rights reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
using NoisyCowStudios.Bin2Object;
|
||||||
|
using Spectre.Console;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using NoisyCowStudios.Bin2Object;
|
|
||||||
|
|
||||||
namespace Il2CppInspector
|
namespace Il2CppInspector
|
||||||
{
|
{
|
||||||
@@ -102,7 +103,7 @@ namespace Il2CppInspector
|
|||||||
// Unpacking must be done starting here, one byte after the end of the headers
|
// Unpacking must be done starting here, one byte after the end of the headers
|
||||||
// Packed or previously packed with Themida? This is purely for information
|
// Packed or previously packed with Themida? This is purely for information
|
||||||
if (sections.FirstOrDefault(x => x.Name == ".themida") is PESection _)
|
if (sections.FirstOrDefault(x => x.Name == ".themida") is PESection _)
|
||||||
Console.WriteLine("Themida protection detected");
|
AnsiConsole.WriteLine("Themida protection detected");
|
||||||
|
|
||||||
// Packed with anything (including Themida)?
|
// Packed with anything (including Themida)?
|
||||||
mightBePacked = sections.FirstOrDefault(x => x.Name == ".rdata") is null;
|
mightBePacked = sections.FirstOrDefault(x => x.Name == ".rdata") is null;
|
||||||
@@ -114,26 +115,31 @@ namespace Il2CppInspector
|
|||||||
section.Name = wantedSectionTypes[section.Characteristics];
|
section.Name = wantedSectionTypes[section.Characteristics];
|
||||||
|
|
||||||
// Get base of code
|
// Get base of code
|
||||||
GlobalOffset = pe.ImageBase + pe.BaseOfCode - sections.First(x => x.Name == ".text").PointerToRawData;
|
GlobalOffset = pe.ImageBase + pe.BaseOfCode - sections
|
||||||
|
.FirstOrDefault(x => x.Characteristics.HasFlag(PE.IMAGE_SCN_MEM_EXECUTE))?.PointerToRawData ?? 0;
|
||||||
|
|
||||||
// Confirm that .rdata section begins at same place as IAT
|
// Confirm that .rdata section begins at same place as IAT
|
||||||
var rData = sections.First(x => x.Name == ".rdata");
|
var rData = sections.FirstOrDefault(x => x.Name == ".rdata");
|
||||||
mightBePacked |= rData.VirtualAddress != IATStart;
|
mightBePacked |= rData == null || rData.VirtualAddress != IATStart;
|
||||||
|
if (rData != null)
|
||||||
|
{
|
||||||
|
// Calculate start of function pointer table
|
||||||
|
pFuncTable = rData.PointerToRawData + IATSize;
|
||||||
|
|
||||||
// Calculate start of function pointer table
|
// Skip over __guard_check_icall_fptr and __guard_dispatch_icall_fptr if present, then the following zero offset
|
||||||
pFuncTable = rData.PointerToRawData + IATSize;
|
Position = pFuncTable;
|
||||||
|
if (pe is PEOptHeader32)
|
||||||
// Skip over __guard_check_icall_fptr and __guard_dispatch_icall_fptr if present, then the following zero offset
|
{
|
||||||
Position = pFuncTable;
|
while (ReadUInt32() != 0)
|
||||||
if (pe is PEOptHeader32) {
|
pFuncTable += 4;
|
||||||
while (ReadUInt32() != 0)
|
|
||||||
pFuncTable += 4;
|
pFuncTable += 4;
|
||||||
pFuncTable += 4;
|
}
|
||||||
}
|
else
|
||||||
else {
|
{
|
||||||
while (ReadUInt64() != 0)
|
while (ReadUInt64() != 0)
|
||||||
|
pFuncTable += 8;
|
||||||
pFuncTable += 8;
|
pFuncTable += 8;
|
||||||
pFuncTable += 8;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// In the fist go round, we signal that this is at least a valid PE file; we don't try to unpack yet
|
// In the fist go round, we signal that this is at least a valid PE file; we don't try to unpack yet
|
||||||
@@ -191,7 +197,27 @@ namespace Il2CppInspector
|
|||||||
|
|
||||||
return exports.Values;
|
return exports.Values;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override bool TryMapVATR(ulong uiAddr, out uint fileOffset)
|
||||||
|
{
|
||||||
|
if (uiAddr == 0)
|
||||||
|
{
|
||||||
|
fileOffset = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var section = sections.FirstOrDefault(x => uiAddr - pe.ImageBase >= x.VirtualAddress &&
|
||||||
|
uiAddr - pe.ImageBase < x.VirtualAddress + x.SizeOfRawData);
|
||||||
|
if (section == null)
|
||||||
|
{
|
||||||
|
fileOffset = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fileOffset = (uint)(uiAddr - section.VirtualAddress - pe.ImageBase + section.PointerToRawData);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public override uint MapVATR(ulong uiAddr) {
|
public override uint MapVATR(ulong uiAddr) {
|
||||||
if (uiAddr == 0)
|
if (uiAddr == 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -4,11 +4,12 @@
|
|||||||
All rights reserved.
|
All rights reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
using NoisyCowStudios.Bin2Object;
|
||||||
|
using Spectre.Console;
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using NoisyCowStudios.Bin2Object;
|
|
||||||
|
|
||||||
namespace Il2CppInspector
|
namespace Il2CppInspector
|
||||||
{
|
{
|
||||||
@@ -100,7 +101,7 @@ namespace Il2CppInspector
|
|||||||
// Get the entire remaining chunk, or to the end of the file if it doesn't contain the end of the chunk
|
// Get the entire remaining chunk, or to the end of the file if it doesn't contain the end of the chunk
|
||||||
var length = (uint) Math.Min(chunk.Memory.End - memoryNext, source.Length);
|
var length = (uint) Math.Min(chunk.Memory.End - memoryNext, source.Length);
|
||||||
|
|
||||||
Console.WriteLine($"Writing {length:x8} bytes from {Path.GetFileName(file.Name)} +{fileStart:x8} ({memoryNext:x8}) to target {il2cpp.Position:x8}");
|
AnsiConsole.WriteLine($"Writing {length:x8} bytes from {Path.GetFileName(file.Name)} +{fileStart:x8} ({memoryNext:x8}) to target {il2cpp.Position:x8}");
|
||||||
|
|
||||||
// Can't use Stream.CopyTo as it doesn't support length parameter
|
// Can't use Stream.CopyTo as it doesn't support length parameter
|
||||||
var buffer = new byte[length];
|
var buffer = new byte[length];
|
||||||
|
|||||||
@@ -20,7 +20,5 @@ namespace Il2CppInspector
|
|||||||
public ulong VirtualAddress { get; set; }
|
public ulong VirtualAddress { get; set; }
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public SymbolType Type { get; set; }
|
public SymbolType Type { get; set; }
|
||||||
|
|
||||||
public string DemangledName => CxxDemangler.CxxDemangler.Demangle(Name);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,12 +6,13 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
using Il2CppInspector.Next;
|
using Il2CppInspector.Next;
|
||||||
|
using Il2CppInspector.Next.BinaryMetadata;
|
||||||
|
using Il2CppInspector.Next.Metadata;
|
||||||
|
using Spectre.Console;
|
||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using Il2CppInspector.Next.BinaryMetadata;
|
|
||||||
using Il2CppInspector.Next.Metadata;
|
|
||||||
using VersionedSerialization;
|
using VersionedSerialization;
|
||||||
|
|
||||||
namespace Il2CppInspector
|
namespace Il2CppInspector
|
||||||
@@ -191,7 +192,7 @@ namespace Il2CppInspector
|
|||||||
var symbols = Image.GetSymbolTable();
|
var symbols = Image.GetSymbolTable();
|
||||||
|
|
||||||
if (symbols.Any()) {
|
if (symbols.Any()) {
|
||||||
Console.WriteLine($"Symbol table(s) found with {symbols.Count} entries");
|
AnsiConsole.WriteLine($"Symbol table(s) found with {symbols.Count} entries");
|
||||||
|
|
||||||
symbols.TryGetValue("g_CodeRegistration", out var code);
|
symbols.TryGetValue("g_CodeRegistration", out var code);
|
||||||
symbols.TryGetValue("g_MetadataRegistration", out var metadata);
|
symbols.TryGetValue("g_MetadataRegistration", out var metadata);
|
||||||
@@ -202,13 +203,13 @@ namespace Il2CppInspector
|
|||||||
symbols.TryGetValue("_g_MetadataRegistration", out metadata);
|
symbols.TryGetValue("_g_MetadataRegistration", out metadata);
|
||||||
|
|
||||||
if (code != null && metadata != null) {
|
if (code != null && metadata != null) {
|
||||||
Console.WriteLine("Required structures acquired from symbol lookup");
|
AnsiConsole.WriteLine("Required structures acquired from symbol lookup");
|
||||||
return (code.VirtualAddress, metadata.VirtualAddress);
|
return (code.VirtualAddress, metadata.VirtualAddress);
|
||||||
} else {
|
} else {
|
||||||
Console.WriteLine("No matches in symbol table");
|
AnsiConsole.WriteLine("No matches in symbol table");
|
||||||
}
|
}
|
||||||
} else if (symbols != null) {
|
} else if (symbols != null) {
|
||||||
Console.WriteLine("No symbol table present in binary file");
|
AnsiConsole.WriteLine("No symbol table present in binary file");
|
||||||
} else {
|
} else {
|
||||||
Console.WriteLine("Symbol table search not implemented for this binary format");
|
Console.WriteLine("Symbol table search not implemented for this binary format");
|
||||||
}
|
}
|
||||||
@@ -227,12 +228,12 @@ namespace Il2CppInspector
|
|||||||
var (code, metadata) = ConsiderCode(Image, loc);
|
var (code, metadata) = ConsiderCode(Image, loc);
|
||||||
if (code != 0) {
|
if (code != 0) {
|
||||||
RegistrationFunctionPointer = loc + Image.GlobalOffset;
|
RegistrationFunctionPointer = loc + Image.GlobalOffset;
|
||||||
Console.WriteLine("Required structures acquired from code heuristics. Initialization function: 0x{0:X16}", RegistrationFunctionPointer);
|
AnsiConsole.WriteLine("Required structures acquired from code heuristics. Initialization function: 0x{0:X16}", RegistrationFunctionPointer);
|
||||||
return (code, metadata);
|
return (code, metadata);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Console.WriteLine("No matches via code heuristics");
|
AnsiConsole.WriteLine("No matches via code heuristics");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -244,11 +245,11 @@ namespace Il2CppInspector
|
|||||||
|
|
||||||
var (codePtr, metadataPtr) = ImageScan(Metadata);
|
var (codePtr, metadataPtr) = ImageScan(Metadata);
|
||||||
if (codePtr == 0) {
|
if (codePtr == 0) {
|
||||||
Console.WriteLine("No matches via data heuristics");
|
AnsiConsole.WriteLine("No matches via data heuristics");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Console.WriteLine("Required structures acquired from data heuristics");
|
AnsiConsole.WriteLine("Required structures acquired from data heuristics");
|
||||||
return (codePtr, metadataPtr);
|
return (codePtr, metadataPtr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -274,8 +275,8 @@ namespace Il2CppInspector
|
|||||||
|
|
||||||
var pointerSize = Image.Bits == 32 ? 4u : 8u;
|
var pointerSize = Image.Bits == 32 ? 4u : 8u;
|
||||||
|
|
||||||
Console.WriteLine("CodeRegistration struct found at 0x{0:X16} (file offset 0x{1:X8})", Image.Bits == 32 ? codeRegistration & 0xffff_ffff : codeRegistration, Image.MapVATR(codeRegistration));
|
AnsiConsole.WriteLine("CodeRegistration struct found at 0x{0:X16} (file offset 0x{1:X8})", Image.Bits == 32 ? codeRegistration & 0xffff_ffff : codeRegistration, Image.MapVATR(codeRegistration));
|
||||||
Console.WriteLine("MetadataRegistration struct found at 0x{0:X16} (file offset 0x{1:X8})", Image.Bits == 32 ? metadataRegistration & 0xffff_ffff : metadataRegistration, Image.MapVATR(metadataRegistration));
|
AnsiConsole.WriteLine("MetadataRegistration struct found at 0x{0:X16} (file offset 0x{1:X8})", Image.Bits == 32 ? metadataRegistration & 0xffff_ffff : metadataRegistration, Image.MapVATR(metadataRegistration));
|
||||||
|
|
||||||
// Root structures from which we find everything else
|
// Root structures from which we find everything else
|
||||||
CodeRegistration = Image.ReadMappedVersionedObject<Il2CppCodeRegistration>(codeRegistration);
|
CodeRegistration = Image.ReadMappedVersionedObject<Il2CppCodeRegistration>(codeRegistration);
|
||||||
@@ -295,8 +296,11 @@ namespace Il2CppInspector
|
|||||||
*/
|
*/
|
||||||
if ((Metadata != null && Metadata.Types.Length != MetadataRegistration.TypeDefinitionsSizesCount)
|
if ((Metadata != null && Metadata.Types.Length != MetadataRegistration.TypeDefinitionsSizesCount)
|
||||||
|| CodeRegistration.ReversePInvokeWrapperCount > 0x10000
|
|| CodeRegistration.ReversePInvokeWrapperCount > 0x10000
|
||||||
|| CodeRegistration.UnresolvedVirtualCallCount > 0x4000 // >= 22
|
// L-NOTE: These below boundaries have been updated already as some games
|
||||||
|| CodeRegistration.InteropDataCount > 0x1000 // >= 23
|
// have reached these limits during normal use. Maybe we should just remove them
|
||||||
|
// at this point?
|
||||||
|
|| CodeRegistration.UnresolvedVirtualCallCount > 0x8000 // >= 22
|
||||||
|
|| CodeRegistration.InteropDataCount > 0x2000 // >= 23
|
||||||
|| (Image.Version <= MetadataVersions.V241 && CodeRegistration.InvokerPointersCount > CodeRegistration.MethodPointersCount))
|
|| (Image.Version <= MetadataVersions.V241 && CodeRegistration.InvokerPointersCount > CodeRegistration.MethodPointersCount))
|
||||||
throw new NotSupportedException("The detected Il2CppCodeRegistration / Il2CppMetadataRegistration structs do not pass validation. This may mean that their fields have been re-ordered as a form of obfuscation and Il2CppInspector has not been able to restore the original order automatically. Consider re-ordering the fields in Il2CppBinaryClasses.cs and try again.");
|
throw new NotSupportedException("The detected Il2CppCodeRegistration / Il2CppMetadataRegistration structs do not pass validation. This may mean that their fields have been re-ordered as a form of obfuscation and Il2CppInspector has not been able to restore the original order automatically. Consider re-ordering the fields in Il2CppBinaryClasses.cs and try again.");
|
||||||
|
|
||||||
@@ -337,7 +341,15 @@ namespace Il2CppInspector
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Read method invoker pointer indices - one per method
|
// Read method invoker pointer indices - one per method
|
||||||
MethodInvokerIndices.Add(module, Image.ReadMappedPrimitiveArray<int>(module.InvokerIndices, (int) module.MethodPointerCount));
|
try
|
||||||
|
{
|
||||||
|
MethodInvokerIndices.Add(module,
|
||||||
|
Image.ReadMappedPrimitiveArray<int>(module.InvokerIndices, (int)module.MethodPointerCount));
|
||||||
|
}
|
||||||
|
catch (InvalidOperationException)
|
||||||
|
{
|
||||||
|
MethodInvokerIndices.Add(module, [..new int[(int)module.MethodPointerCount]]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -398,12 +410,21 @@ namespace Il2CppInspector
|
|||||||
var type = TypeReferences[i];
|
var type = TypeReferences[i];
|
||||||
if (type.Type.IsTypeDefinitionEnum())
|
if (type.Type.IsTypeDefinitionEnum())
|
||||||
{
|
{
|
||||||
type.Data.Value = (type.Data.Type.PointerValue - baseDefinitionPtr) / definitionSize;
|
if (type.Data.Type.PointerValue >= baseDefinitionPtr)
|
||||||
|
type.Data.Value = (type.Data.Type.PointerValue - baseDefinitionPtr) / definitionSize;
|
||||||
|
|
||||||
|
Debug.Assert(Metadata!.Types.Length > type.Data.KlassIndex);
|
||||||
}
|
}
|
||||||
else if (type.Type.IsGenericParameterEnum())
|
else if (type.Type.IsGenericParameterEnum())
|
||||||
{
|
{
|
||||||
type.Data.Value = (type.Data.Type.PointerValue - baseGenericPtr) / genericParameterSize;
|
if (type.Data.Type.PointerValue >= baseGenericPtr)
|
||||||
|
type.Data.Value = (type.Data.Type.PointerValue - baseGenericPtr) / genericParameterSize;
|
||||||
|
|
||||||
|
Debug.Assert(Metadata!.GenericParameters.Length > type.Data.KlassIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Debug.Assert((long)type.Data.Value >= 0);
|
||||||
|
|
||||||
builder.Add(type);
|
builder.Add(type);
|
||||||
}
|
}
|
||||||
TypeReferences = builder.MoveToImmutable();
|
TypeReferences = builder.MoveToImmutable();
|
||||||
|
|||||||
@@ -5,8 +5,11 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
using Il2CppInspector.Next;
|
using Il2CppInspector.Next;
|
||||||
|
using Il2CppInspector.Next.BinaryMetadata;
|
||||||
|
using Il2CppInspector.Next.Metadata;
|
||||||
using Il2CppInspector.Utils;
|
using Il2CppInspector.Utils;
|
||||||
using NoisyCowStudios.Bin2Object;
|
using NoisyCowStudios.Bin2Object;
|
||||||
|
using Spectre.Console;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
@@ -14,8 +17,6 @@ using System.IO;
|
|||||||
using System.IO.Compression;
|
using System.IO.Compression;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Il2CppInspector.Next.BinaryMetadata;
|
|
||||||
using Il2CppInspector.Next.Metadata;
|
|
||||||
using VersionedSerialization;
|
using VersionedSerialization;
|
||||||
|
|
||||||
namespace Il2CppInspector
|
namespace Il2CppInspector
|
||||||
@@ -84,7 +85,7 @@ namespace Il2CppInspector
|
|||||||
return (0ul, null);
|
return (0ul, null);
|
||||||
|
|
||||||
// Get pointer in binary to default value
|
// Get pointer in binary to default value
|
||||||
var pValue = Metadata.Header.FieldAndParameterDefaultValueDataOffset + dataIndex;
|
var pValue = Metadata.FieldAndParameterDefaultValueDataOffset + dataIndex;
|
||||||
var typeRef = TypeReferences[typeIndex];
|
var typeRef = TypeReferences[typeIndex];
|
||||||
|
|
||||||
// Default value is null
|
// Default value is null
|
||||||
@@ -410,7 +411,7 @@ namespace Il2CppInspector
|
|||||||
if (metadataFile != null) {
|
if (metadataFile != null) {
|
||||||
// Extract the metadata file to memory
|
// Extract the metadata file to memory
|
||||||
if (!silent)
|
if (!silent)
|
||||||
Console.WriteLine($"Extracting metadata from (archive){Path.DirectorySeparatorChar}{metadataFile.FullName}");
|
AnsiConsole.WriteLine($"Extracting metadata from (archive){Path.DirectorySeparatorChar}{metadataFile.FullName}");
|
||||||
|
|
||||||
metadataMemoryStream = new MemoryStream();
|
metadataMemoryStream = new MemoryStream();
|
||||||
using var metadataStream = metadataFile.Open();
|
using var metadataStream = metadataFile.Open();
|
||||||
@@ -428,7 +429,7 @@ namespace Il2CppInspector
|
|||||||
// IPAs will only have one binary (which may or may not be a UB covering multiple architectures)
|
// IPAs will only have one binary (which may or may not be a UB covering multiple architectures)
|
||||||
if (ipaBinaryFolder != null) {
|
if (ipaBinaryFolder != null) {
|
||||||
if (!silent)
|
if (!silent)
|
||||||
Console.WriteLine($"Extracting binary from {zipStreams.First()}{Path.DirectorySeparatorChar}{binaryFiles.First().FullName}");
|
AnsiConsole.WriteLine($"Extracting binary from {zipStreams.First()}{Path.DirectorySeparatorChar}{binaryFiles.First().FullName}");
|
||||||
|
|
||||||
// Extract the binary file or package to memory
|
// Extract the binary file or package to memory
|
||||||
binaryMemoryStream = new MemoryStream();
|
binaryMemoryStream = new MemoryStream();
|
||||||
@@ -531,7 +532,7 @@ namespace Il2CppInspector
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Console.WriteLine("Detected metadata version " + metadata.Version);
|
AnsiConsole.WriteLine("Detected metadata version " + metadata.Version);
|
||||||
|
|
||||||
// Load the il2cpp code file (try all available file formats)
|
// Load the il2cpp code file (try all available file formats)
|
||||||
IFileFormatStream stream;
|
IFileFormatStream stream;
|
||||||
@@ -559,16 +560,16 @@ namespace Il2CppInspector
|
|||||||
|
|
||||||
var processors = new List<Il2CppInspector>();
|
var processors = new List<Il2CppInspector>();
|
||||||
foreach (var image in stream.Images) {
|
foreach (var image in stream.Images) {
|
||||||
Console.WriteLine("Container format: " + image.Format);
|
AnsiConsole.WriteLine("Container format: " + image.Format);
|
||||||
Console.WriteLine("Container endianness: " + ((BinaryObjectStream) image).Endianness);
|
AnsiConsole.WriteLine("Container endianness: " + ((BinaryObjectStream) image).Endianness);
|
||||||
Console.WriteLine("Architecture word size: {0}-bit", image.Bits);
|
AnsiConsole.WriteLine("Architecture word size: {0}-bit", image.Bits);
|
||||||
Console.WriteLine("Instruction set: " + image.Arch);
|
AnsiConsole.WriteLine("Instruction set: " + image.Arch);
|
||||||
Console.WriteLine("Global offset: 0x{0:X16}", image.GlobalOffset);
|
AnsiConsole.WriteLine("Global offset: 0x{0:X16}", image.GlobalOffset);
|
||||||
|
|
||||||
// Architecture-agnostic load attempt
|
// Architecture-agnostic load attempt
|
||||||
try {
|
try {
|
||||||
if (Il2CppBinary.Load(image, metadata, statusCallback) is Il2CppBinary binary) {
|
if (Il2CppBinary.Load(image, metadata, statusCallback) is Il2CppBinary binary) {
|
||||||
Console.WriteLine("IL2CPP binary version " + image.Version);
|
AnsiConsole.WriteLine("IL2CPP binary version " + image.Version);
|
||||||
|
|
||||||
processors.Add(new Il2CppInspector(binary, metadata));
|
processors.Add(new Il2CppInspector(binary, metadata));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -230,7 +230,8 @@ namespace Il2CppInspector
|
|||||||
|
|
||||||
// genericAdjustorThunks was inserted before invokerPointersCount in 24.5 and 27.1
|
// genericAdjustorThunks was inserted before invokerPointersCount in 24.5 and 27.1
|
||||||
// pointer expected if we need to bump version
|
// pointer expected if we need to bump version
|
||||||
if (Image.Version == MetadataVersions.V244 && cr.InvokerPointersCount > 0x50000)
|
if (Image.Version == MetadataVersions.V244 &&
|
||||||
|
(cr.InvokerPointersCount > 0x50000 || cr.ReversePInvokeWrapperCount > cr.ReversePInvokeWrappers))
|
||||||
{
|
{
|
||||||
Image.Version = MetadataVersions.V245;
|
Image.Version = MetadataVersions.V245;
|
||||||
codeRegistration = codeGenEndPtr - (ulong)Il2CppCodeRegistration.Size(Image.Version, Image.Bits == 32);
|
codeRegistration = codeGenEndPtr - (ulong)Il2CppCodeRegistration.Size(Image.Version, Image.Bits == 32);
|
||||||
@@ -276,21 +277,12 @@ namespace Il2CppInspector
|
|||||||
|
|
||||||
vas = FindAllMappedWords(imageBytes, typesLength).Select(a => a - mrSize + ptrSize * 4);
|
vas = FindAllMappedWords(imageBytes, typesLength).Select(a => a - mrSize + ptrSize * 4);
|
||||||
|
|
||||||
// >= 19 && < 27
|
// >= 19
|
||||||
if (Image.Version < MetadataVersions.V270)
|
// Luke: Previously, a check comparing MetadataUsagesCount was used here,
|
||||||
foreach (var va in vas)
|
// but I know of at least one binary where this will break detection.
|
||||||
{
|
// Testing showed that we can just use the same heuristic used for v27+
|
||||||
var mr = Image.ReadMappedVersionedObject<Il2CppMetadataRegistration>(va);
|
// on older versions as well, so we'll just use it for all cases.
|
||||||
if (mr.MetadataUsagesCount == (ulong) metadata.MetadataUsageLists.Length)
|
if (Image.Version >= MetadataVersions.V190)
|
||||||
metadataRegistration = va;
|
|
||||||
}
|
|
||||||
|
|
||||||
// plagiarism. noun - https://www.lexico.com/en/definition/plagiarism
|
|
||||||
// the practice of taking someone else's work or ideas and passing them off as one's own.
|
|
||||||
// Synonyms: copying, piracy, theft, strealing, infringement of copyright
|
|
||||||
|
|
||||||
// >= 27
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
foreach (var va in vas)
|
foreach (var va in vas)
|
||||||
{
|
{
|
||||||
@@ -303,6 +295,7 @@ namespace Il2CppInspector
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (metadataRegistration == 0)
|
if (metadataRegistration == 0)
|
||||||
return (0, 0);
|
return (0, 0);
|
||||||
|
|
||||||
|
|||||||
@@ -5,15 +5,10 @@
|
|||||||
All rights reserved.
|
All rights reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using System.IO;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Linq;
|
|
||||||
using System.Reflection;
|
|
||||||
using Il2CppInspector.Next;
|
using Il2CppInspector.Next;
|
||||||
using Il2CppInspector.Next.Metadata;
|
using Il2CppInspector.Next.Metadata;
|
||||||
using NoisyCowStudios.Bin2Object;
|
|
||||||
using VersionedSerialization;
|
using VersionedSerialization;
|
||||||
|
|
||||||
namespace Il2CppInspector
|
namespace Il2CppInspector
|
||||||
@@ -48,7 +43,15 @@ namespace Il2CppInspector
|
|||||||
public ImmutableArray<uint> VTableMethodIndices { get; set; }
|
public ImmutableArray<uint> VTableMethodIndices { get; set; }
|
||||||
public string[] StringLiterals { get; set; }
|
public string[] StringLiterals { get; set; }
|
||||||
|
|
||||||
public Dictionary<int, string> Strings { get; private set; } = new Dictionary<int, string>();
|
public int FieldAndParameterDefaultValueDataOffset => Version >= MetadataVersions.V380
|
||||||
|
? Header.FieldAndParameterDefaultValueData.Offset
|
||||||
|
: Header.FieldAndParameterDefaultValueDataOffset;
|
||||||
|
|
||||||
|
public int AttributeDataOffset => Version >= MetadataVersions.V380
|
||||||
|
? Header.AttributeData.Offset
|
||||||
|
: Header.AttributeDataOffset;
|
||||||
|
|
||||||
|
public Dictionary<int, string> Strings { get; private set; } = [];
|
||||||
|
|
||||||
// Set if something in the metadata has been modified / decrypted
|
// Set if something in the metadata has been modified / decrypted
|
||||||
public bool IsModified { get; private set; } = false;
|
public bool IsModified { get; private set; } = false;
|
||||||
@@ -92,13 +95,63 @@ namespace Il2CppInspector
|
|||||||
// Set object versioning for Bin2Object from metadata version
|
// Set object versioning for Bin2Object from metadata version
|
||||||
Version = new StructVersion(Header.Version);
|
Version = new StructVersion(Header.Version);
|
||||||
|
|
||||||
if (Version < MetadataVersions.V160 || Version > MetadataVersions.V310) {
|
if (Version < MetadataVersions.V160 || Version > MetadataVersions.V390) {
|
||||||
throw new InvalidOperationException($"The supplied metadata file is not of a supported version ({Header.Version}).");
|
throw new InvalidOperationException($"The supplied metadata file is not of a supported version ({Header.Version}).");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rewind and read metadata header with the correct version settings
|
// Rewind and read metadata header with the correct version settings
|
||||||
Header = ReadVersionedObject<Il2CppGlobalMetadataHeader>(0);
|
Header = ReadVersionedObject<Il2CppGlobalMetadataHeader>(0);
|
||||||
|
|
||||||
|
// Setup the proper index sizes for metadata v38+
|
||||||
|
if (Version >= MetadataVersions.V380)
|
||||||
|
{
|
||||||
|
static int GetIndexSize(int elementCount)
|
||||||
|
{
|
||||||
|
return elementCount switch
|
||||||
|
{
|
||||||
|
<= byte.MaxValue => sizeof(byte),
|
||||||
|
<= ushort.MaxValue => sizeof(ushort),
|
||||||
|
_ => sizeof(int)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
var typeDefinitionIndexSize = GetIndexSize(Header.TypeDefinitions.Count);
|
||||||
|
var genericContainerIndexSize = GetIndexSize(Header.GenericContainers.Count);
|
||||||
|
|
||||||
|
var tag = $"{TypeDefinitionIndex.TagPrefix}{typeDefinitionIndexSize}"
|
||||||
|
+ $"_{GenericContainerIndex.TagPrefix}{genericContainerIndexSize}";
|
||||||
|
|
||||||
|
var tempVersion = new StructVersion(Version.Major, Version.Minor, tag);
|
||||||
|
|
||||||
|
// now we need to derive the size for TypeIndex.
|
||||||
|
// this is normally done through s_Il2CppMetadataRegistration->typesCount, but we don't want to use the binary for this
|
||||||
|
// as we do not have it available at this point.
|
||||||
|
// thankfully, we can just guess the size based off the three available options and the known total size of
|
||||||
|
// a type entry that uses TypeIndex.
|
||||||
|
var expectedEventDefinitionSize = Header.Events.SectionSize / Header.Events.Count;
|
||||||
|
var maxEventDefinitionSize = Il2CppEventDefinition.Size(tempVersion);
|
||||||
|
|
||||||
|
int typeIndexSize;
|
||||||
|
if (expectedEventDefinitionSize == maxEventDefinitionSize)
|
||||||
|
typeIndexSize = sizeof(int);
|
||||||
|
else if (expectedEventDefinitionSize == maxEventDefinitionSize - 2)
|
||||||
|
typeIndexSize = sizeof(ushort);
|
||||||
|
else if (expectedEventDefinitionSize == maxEventDefinitionSize - 3)
|
||||||
|
typeIndexSize = sizeof(byte);
|
||||||
|
else
|
||||||
|
throw new InvalidOperationException("Could not determine TypeIndex size based on the metadata header");
|
||||||
|
|
||||||
|
var fullTag = $"{tag}_{TypeIndex.TagPrefix}{typeIndexSize}";
|
||||||
|
|
||||||
|
if (Version >= MetadataVersions.V390)
|
||||||
|
{
|
||||||
|
var parameterIndexSize = GetIndexSize(Header.Parameters.Count);
|
||||||
|
fullTag += $"_{ParameterIndex.TagPrefix}{parameterIndexSize}";
|
||||||
|
}
|
||||||
|
|
||||||
|
Version = new StructVersion(Version.Major, Version.Minor, fullTag);
|
||||||
|
}
|
||||||
|
|
||||||
// Sanity checking
|
// Sanity checking
|
||||||
// Unity.IL2CPP.MetadataCacheWriter.WriteLibIl2CppMetadata always writes the metadata information in the same order it appears in the header,
|
// Unity.IL2CPP.MetadataCacheWriter.WriteLibIl2CppMetadata always writes the metadata information in the same order it appears in the header,
|
||||||
// with each block always coming directly after the previous block, 4-byte aligned. We can use this to check the integrity of the data and
|
// with each block always coming directly after the previous block, 4-byte aligned. We can use this to check the integrity of the data and
|
||||||
@@ -108,8 +161,11 @@ namespace Il2CppInspector
|
|||||||
// in the header after the sanity and version fields, and since it will always point directly to the first byte after the end of the header,
|
// in the header after the sanity and version fields, and since it will always point directly to the first byte after the end of the header,
|
||||||
// we can use this value to determine the actual header length and therefore narrow down the metadata version to 24.0/24.1 or 24.2.
|
// we can use this value to determine the actual header length and therefore narrow down the metadata version to 24.0/24.1 or 24.2.
|
||||||
|
|
||||||
if (!pluginResult.SkipValidation) {
|
if (!pluginResult.SkipValidation)
|
||||||
var realHeaderLength = Header.StringLiteralOffset;
|
{
|
||||||
|
var realHeaderLength = Version >= MetadataVersions.V380
|
||||||
|
? Header.StringLiterals.Offset
|
||||||
|
: Header.StringLiteralOffset;
|
||||||
|
|
||||||
if (realHeaderLength != Sizeof<Il2CppGlobalMetadataHeader>()) {
|
if (realHeaderLength != Sizeof<Il2CppGlobalMetadataHeader>()) {
|
||||||
if (Version == MetadataVersions.V240) {
|
if (Version == MetadataVersions.V240) {
|
||||||
@@ -122,10 +178,10 @@ namespace Il2CppInspector
|
|||||||
throw new InvalidOperationException("Could not verify the integrity of the metadata file or accurately identify the metadata sub-version");
|
throw new InvalidOperationException("Could not verify the integrity of the metadata file or accurately identify the metadata sub-version");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load all the relevant metadata using offsets provided in the header
|
// Load all the relevant metadata using offsets provided in the header
|
||||||
if (Version >= MetadataVersions.V160)
|
if (Version >= MetadataVersions.V160)
|
||||||
Images = ReadVersionedObjectArray<Il2CppImageDefinition>(Header.ImagesOffset, Header.ImagesSize / Sizeof<Il2CppImageDefinition>());
|
Images = ReadMetadataArray<Il2CppImageDefinition>(Header.ImagesOffset, Header.ImagesSize, Header.Images);
|
||||||
|
|
||||||
// As an additional sanity check, all images in the metadata should have Mono.Cecil.MetadataToken == 1
|
// As an additional sanity check, all images in the metadata should have Mono.Cecil.MetadataToken == 1
|
||||||
// In metadata v24.1, two extra fields were added which will cause the below test to fail.
|
// In metadata v24.1, two extra fields were added which will cause the below test to fail.
|
||||||
@@ -136,28 +192,29 @@ namespace Il2CppInspector
|
|||||||
Version = MetadataVersions.V241;
|
Version = MetadataVersions.V241;
|
||||||
|
|
||||||
// No need to re-read the header, it's the same for both sub-versions
|
// No need to re-read the header, it's the same for both sub-versions
|
||||||
Images = ReadVersionedObjectArray<Il2CppImageDefinition>(Header.ImagesOffset, Header.ImagesSize / Sizeof<Il2CppImageDefinition>());
|
Images = ReadMetadataArray<Il2CppImageDefinition>(Header.ImagesOffset, Header.ImagesSize, Header.Images);
|
||||||
|
|
||||||
if (Images.Any(x => x.Token != 1))
|
if (Images.Any(x => x.Token != 1))
|
||||||
throw new InvalidOperationException("Could not verify the integrity of the metadata file image list");
|
throw new InvalidOperationException("Could not verify the integrity of the metadata file image list");
|
||||||
}
|
}
|
||||||
|
|
||||||
Types = ReadVersionedObjectArray<Il2CppTypeDefinition>(Header.TypeDefinitionsOffset, Header.TypeDefinitionsSize / Sizeof<Il2CppTypeDefinition>());
|
Types = ReadMetadataArray<Il2CppTypeDefinition>(Header.TypeDefinitionsOffset, Header.TypeDefinitionsSize, Header.TypeDefinitions);
|
||||||
Methods = ReadVersionedObjectArray<Il2CppMethodDefinition>(Header.MethodsOffset, Header.MethodsSize / Sizeof<Il2CppMethodDefinition>());
|
Methods = ReadMetadataArray<Il2CppMethodDefinition>(Header.MethodsOffset, Header.MethodsSize, Header.Methods);
|
||||||
Params = ReadVersionedObjectArray<Il2CppParameterDefinition>(Header.ParametersOffset, Header.ParametersSize / Sizeof<Il2CppParameterDefinition>());
|
Params = ReadMetadataArray<Il2CppParameterDefinition>(Header.ParametersOffset, Header.ParametersSize, Header.Parameters);
|
||||||
Fields = ReadVersionedObjectArray<Il2CppFieldDefinition>(Header.FieldsOffset, Header.FieldsSize / Sizeof<Il2CppFieldDefinition>());
|
Fields = ReadMetadataArray<Il2CppFieldDefinition>(Header.FieldsOffset, Header.FieldsSize, Header.Fields);
|
||||||
FieldDefaultValues = ReadVersionedObjectArray<Il2CppFieldDefaultValue>(Header.FieldDefaultValuesOffset, Header.FieldDefaultValuesSize / Sizeof<Il2CppFieldDefaultValue>());
|
FieldDefaultValues = ReadMetadataArray<Il2CppFieldDefaultValue>(Header.FieldDefaultValuesOffset, Header.FieldDefaultValuesSize, Header.FieldDefaultValues);
|
||||||
Properties = ReadVersionedObjectArray<Il2CppPropertyDefinition>(Header.PropertiesOffset, Header.PropertiesSize / Sizeof<Il2CppPropertyDefinition>());
|
Properties = ReadMetadataArray<Il2CppPropertyDefinition>(Header.PropertiesOffset, Header.PropertiesSize, Header.Properties);
|
||||||
Events = ReadVersionedObjectArray<Il2CppEventDefinition>(Header.EventsOffset, Header.EventsSize / Sizeof<Il2CppEventDefinition>());
|
Events = ReadMetadataArray<Il2CppEventDefinition>(Header.EventsOffset, Header.EventsSize, Header.Events);
|
||||||
InterfaceUsageIndices = ReadPrimitiveArray<int>(Header.InterfacesOffset, Header.InterfacesSize / sizeof(int));
|
InterfaceUsageIndices = ReadMetadataPrimitiveArray<int>(Header.InterfacesOffset, Header.InterfacesSize, Header.Interfaces);
|
||||||
NestedTypeIndices = ReadPrimitiveArray<int>(Header.NestedTypesOffset, Header.NestedTypesSize / sizeof(int));
|
NestedTypeIndices = ReadMetadataPrimitiveArray<int>(Header.NestedTypesOffset, Header.NestedTypesSize, Header.NestedTypes);
|
||||||
GenericContainers = ReadVersionedObjectArray<Il2CppGenericContainer>(Header.GenericContainersOffset, Header.GenericContainersSize / Sizeof<Il2CppGenericContainer>());
|
GenericContainers = ReadMetadataArray<Il2CppGenericContainer>(Header.GenericContainersOffset, Header.GenericContainersSize, Header.GenericContainers);
|
||||||
GenericParameters = ReadVersionedObjectArray<Il2CppGenericParameter>(Header.GenericParametersOffset, Header.GenericParametersSize / Sizeof<Il2CppGenericParameter>());
|
GenericParameters = ReadMetadataArray<Il2CppGenericParameter>(Header.GenericParametersOffset, Header.GenericParametersSize, Header.GenericParameters);
|
||||||
GenericConstraintIndices = ReadPrimitiveArray<int>(Header.GenericParameterConstraintsOffset, Header.GenericParameterConstraintsSize / sizeof(int));
|
GenericConstraintIndices = ReadMetadataPrimitiveArray<int>(Header.GenericParameterConstraintsOffset, Header.GenericParameterConstraintsSize, Header.GenericParameterConstraints);
|
||||||
InterfaceOffsets = ReadVersionedObjectArray<Il2CppInterfaceOffsetPair>(Header.InterfaceOffsetsOffset, Header.InterfaceOffsetsSize / Sizeof<Il2CppInterfaceOffsetPair>());
|
InterfaceOffsets = ReadMetadataArray<Il2CppInterfaceOffsetPair>(Header.InterfaceOffsetsOffset, Header.InterfaceOffsetsSize, Header.InterfaceOffsets);
|
||||||
VTableMethodIndices = ReadPrimitiveArray<uint>(Header.VTableMethodsOffset, Header.VTableMethodsSize / sizeof(uint));
|
VTableMethodIndices = ReadMetadataPrimitiveArray<uint>(Header.VTableMethodsOffset, Header.VTableMethodsSize, Header.VtableMethods);
|
||||||
|
|
||||||
if (Version >= MetadataVersions.V160) {
|
if (Version >= MetadataVersions.V160)
|
||||||
|
{
|
||||||
// In v24.4 hashValueIndex was removed from Il2CppAssemblyNameDefinition, which is a field in Il2CppAssemblyDefinition
|
// In v24.4 hashValueIndex was removed from Il2CppAssemblyNameDefinition, which is a field in Il2CppAssemblyDefinition
|
||||||
// The number of images and assemblies should be the same. If they are not, we deduce that we are using v24.4
|
// The number of images and assemblies should be the same. If they are not, we deduce that we are using v24.4
|
||||||
// Note the version comparison matches both 24.2 and 24.3 here since 24.3 is tested for during binary loading
|
// Note the version comparison matches both 24.2 and 24.3 here since 24.3 is tested for during binary loading
|
||||||
@@ -167,32 +224,39 @@ namespace Il2CppInspector
|
|||||||
{
|
{
|
||||||
if (Version == MetadataVersions.V241)
|
if (Version == MetadataVersions.V241)
|
||||||
changedAssemblyDefStruct = true;
|
changedAssemblyDefStruct = true;
|
||||||
|
|
||||||
Version = MetadataVersions.V244;
|
Version = MetadataVersions.V244;
|
||||||
}
|
}
|
||||||
|
|
||||||
Assemblies = ReadVersionedObjectArray<Il2CppAssemblyDefinition>(Header.AssembliesOffset, Images.Length);
|
Assemblies = ReadMetadataArray<Il2CppAssemblyDefinition>(Header.AssembliesOffset, Header.AssembliesSize, Header.Assemblies);
|
||||||
|
|
||||||
if (changedAssemblyDefStruct)
|
if (changedAssemblyDefStruct)
|
||||||
Version = MetadataVersions.V241;
|
Version = MetadataVersions.V241;
|
||||||
|
|
||||||
ParameterDefaultValues = ReadVersionedObjectArray<Il2CppParameterDefaultValue>(Header.ParameterDefaultValuesOffset, Header.ParameterDefaultValuesSize / Sizeof<Il2CppParameterDefaultValue>());
|
ParameterDefaultValues = ReadMetadataArray<Il2CppParameterDefaultValue>(Header.ParameterDefaultValuesOffset, Header.ParameterDefaultValuesSize, Header.ParameterDefaultValues);
|
||||||
}
|
}
|
||||||
if (Version >= MetadataVersions.V190 && Version < MetadataVersions.V270) {
|
|
||||||
MetadataUsageLists = ReadVersionedObjectArray<Il2CppMetadataUsageList>(Header.MetadataUsageListsOffset, Header.MetadataUsageListsCount / Sizeof<Il2CppMetadataUsageList>());
|
if (Version >= MetadataVersions.V190 && Version < MetadataVersions.V270)
|
||||||
MetadataUsagePairs = ReadVersionedObjectArray<Il2CppMetadataUsagePair>(Header.MetadataUsagePairsOffset, Header.MetadataUsagePairsCount / Sizeof<Il2CppMetadataUsagePair>());
|
{
|
||||||
|
MetadataUsageLists = ReadMetadataArray<Il2CppMetadataUsageList>(Header.MetadataUsageListsOffset, Header.MetadataUsageListsCount, default);
|
||||||
|
MetadataUsagePairs = ReadMetadataArray<Il2CppMetadataUsagePair>(Header.MetadataUsagePairsOffset, Header.MetadataUsagePairsCount, default);
|
||||||
}
|
}
|
||||||
if (Version >= MetadataVersions.V190) {
|
|
||||||
FieldRefs = ReadVersionedObjectArray<Il2CppFieldRef>(Header.FieldRefsOffset, Header.FieldRefsSize / Sizeof<Il2CppFieldRef>());
|
if (Version >= MetadataVersions.V190)
|
||||||
|
{
|
||||||
|
FieldRefs = ReadMetadataArray<Il2CppFieldRef>(Header.FieldRefsOffset, Header.FieldRefsSize, Header.FieldRefs);
|
||||||
}
|
}
|
||||||
if (Version >= MetadataVersions.V210 && Version < MetadataVersions.V290) {
|
|
||||||
AttributeTypeIndices = ReadPrimitiveArray<int>(Header.AttributesTypesOffset, Header.AttributesTypesCount / sizeof(int));
|
if (Version >= MetadataVersions.V210 && Version < MetadataVersions.V290)
|
||||||
AttributeTypeRanges = ReadVersionedObjectArray<Il2CppCustomAttributeTypeRange>(Header.AttributesInfoOffset, Header.AttributesInfoCount / Sizeof<Il2CppCustomAttributeTypeRange>());
|
{
|
||||||
|
AttributeTypeIndices = ReadMetadataPrimitiveArray<int>(Header.AttributesTypesOffset, Header.AttributesTypesCount, default);
|
||||||
|
AttributeTypeRanges = ReadMetadataArray<Il2CppCustomAttributeTypeRange>(Header.AttributesInfoOffset, Header.AttributesInfoCount, default);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Version >= MetadataVersions.V290)
|
if (Version >= MetadataVersions.V290)
|
||||||
{
|
{
|
||||||
AttributeDataRanges = ReadVersionedObjectArray<Il2CppCustomAttributeDataRange>(Header.AttributeDataRangeOffset,
|
AttributeDataRanges = ReadMetadataArray<Il2CppCustomAttributeDataRange>(Header.AttributeDataRangeOffset,
|
||||||
Header.AttributeDataRangeSize / Sizeof<Il2CppCustomAttributeDataRange>());
|
Header.AttributeDataRangeSize, Header.AttributeDataRanges);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get all metadata strings
|
// Get all metadata strings
|
||||||
@@ -201,10 +265,17 @@ namespace Il2CppInspector
|
|||||||
Strings = pluginGetStringsResult.Strings;
|
Strings = pluginGetStringsResult.Strings;
|
||||||
|
|
||||||
else {
|
else {
|
||||||
Position = Header.StringOffset;
|
var stringOffset = Version >= MetadataVersions.V380
|
||||||
|
? Header.Strings.Offset
|
||||||
|
: Header.StringOffset;
|
||||||
|
var stringLength = Version >= MetadataVersions.V380
|
||||||
|
? Header.Strings.SectionSize
|
||||||
|
: Header.StringSize;
|
||||||
|
|
||||||
while (Position < Header.StringOffset + Header.StringSize)
|
Position = stringOffset;
|
||||||
Strings.Add((int) Position - Header.StringOffset, ReadNullTerminatedString());
|
|
||||||
|
while (Position < stringOffset + stringLength)
|
||||||
|
Strings.Add((int)Position - stringOffset, ReadNullTerminatedString());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get all string literals
|
// Get all string literals
|
||||||
@@ -212,23 +283,65 @@ namespace Il2CppInspector
|
|||||||
if (pluginGetStringLiteralsResult.IsDataModified)
|
if (pluginGetStringLiteralsResult.IsDataModified)
|
||||||
StringLiterals = pluginGetStringLiteralsResult.StringLiterals.ToArray();
|
StringLiterals = pluginGetStringLiteralsResult.StringLiterals.ToArray();
|
||||||
|
|
||||||
else {
|
else
|
||||||
var stringLiteralList = ReadVersionedObjectArray<Il2CppStringLiteral>(Header.StringLiteralOffset, Header.StringLiteralSize / Sizeof<Il2CppStringLiteral>());
|
{
|
||||||
|
var stringLiteralList = ReadMetadataArray<Il2CppStringLiteral>(Header.StringLiteralOffset,
|
||||||
|
Header.StringLiteralSize, Header.StringLiterals);
|
||||||
|
|
||||||
StringLiterals = new string[stringLiteralList.Length];
|
var dataOffset = Version >= MetadataVersions.V380
|
||||||
for (var i = 0; i < stringLiteralList.Length; i++)
|
? Header.StringLiteralData.Offset
|
||||||
StringLiterals[i] = ReadFixedLengthString(Header.StringLiteralDataOffset + stringLiteralList[i].DataIndex, (int)stringLiteralList[i].Length);
|
: Header.StringLiteralDataOffset;
|
||||||
|
|
||||||
|
if (Version >= MetadataVersions.V350)
|
||||||
|
{
|
||||||
|
var literals = new string[stringLiteralList.Length - 1];
|
||||||
|
for (var i = 0; i < literals.Length; i++)
|
||||||
|
{
|
||||||
|
var currentStringDataIndex = stringLiteralList[i].DataIndex;
|
||||||
|
var nextStringDataIndex = stringLiteralList[i + 1].DataIndex;
|
||||||
|
var stringLength = nextStringDataIndex - currentStringDataIndex;
|
||||||
|
|
||||||
|
literals[i] = ReadFixedLengthString(dataOffset + currentStringDataIndex, stringLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
StringLiterals = literals;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
StringLiterals = new string[stringLiteralList.Length];
|
||||||
|
for (var i = 0; i < stringLiteralList.Length; i++)
|
||||||
|
StringLiterals[i] = ReadFixedLengthString(dataOffset + stringLiteralList[i].DataIndex,
|
||||||
|
(int)stringLiteralList[i].Length);
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Post-processing hook
|
// Post-processing hook
|
||||||
IsModified |= PluginHooks.PostProcessMetadata(this).IsStreamModified;
|
IsModified |= PluginHooks.PostProcessMetadata(this).IsStreamModified;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ImmutableArray<T> ReadMetadataPrimitiveArray<T>(int oldOffset, int oldSize, Il2CppSectionMetadata newMetadata)
|
||||||
|
where T : unmanaged
|
||||||
|
{
|
||||||
|
return Version >= MetadataVersions.V380
|
||||||
|
? ReadPrimitiveArray<T>(newMetadata.Offset, newMetadata.Count)
|
||||||
|
: ReadPrimitiveArray<T>(oldOffset, oldSize / Unsafe.SizeOf<T>());
|
||||||
|
}
|
||||||
|
|
||||||
|
public ImmutableArray<T> ReadMetadataArray<T>(int oldOffset, int oldSize, Il2CppSectionMetadata newMetadata)
|
||||||
|
where T : IReadable, new()
|
||||||
|
{
|
||||||
|
return Version >= MetadataVersions.V380
|
||||||
|
? ReadVersionedObjectArray<T>(newMetadata.Offset, newMetadata.Count)
|
||||||
|
: ReadVersionedObjectArray<T>(oldOffset, oldSize / Sizeof<T>());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save metadata to file, overwriting if necessary
|
// Save metadata to file, overwriting if necessary
|
||||||
public void SaveToFile(string pathname) {
|
public void SaveToFile(string pathname) {
|
||||||
Position = 0;
|
Position = 0;
|
||||||
using (var outFile = new FileStream(pathname, FileMode.Create, FileAccess.Write))
|
using var outFile = new FileStream(pathname, FileMode.Create, FileAccess.Write);
|
||||||
CopyTo(outFile);
|
CopyTo(outFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int Sizeof<T>() where T : IReadable => T.Size(Version, Is32Bit);
|
public int Sizeof<T>() where T : IReadable => T.Size(Version, Is32Bit);
|
||||||
|
|||||||
@@ -41,10 +41,8 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="dnlib" Version="4.4.0" />
|
<PackageReference Include="dnlib" Version="4.4.0" />
|
||||||
<PackageReference Include="McMaster.NETCore.Plugins" Version="1.4.0" />
|
<PackageReference Include="McMaster.NETCore.Plugins" Version="2.0.0" />
|
||||||
<PackageReference Include="CxxDemangler" Version="0.2.4.11">
|
<PackageReference Include="Spectre.Console" Version="0.50.0" />
|
||||||
<NoWarn>NU1605</NoWarn>
|
|
||||||
</PackageReference>
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ using Il2CppInspector.Cpp;
|
|||||||
using Il2CppInspector.Cpp.UnityHeaders;
|
using Il2CppInspector.Cpp.UnityHeaders;
|
||||||
using Il2CppInspector.Next;
|
using Il2CppInspector.Next;
|
||||||
using Il2CppInspector.Reflection;
|
using Il2CppInspector.Reflection;
|
||||||
|
using Spectre.Console;
|
||||||
|
|
||||||
namespace Il2CppInspector.Model
|
namespace Il2CppInspector.Model
|
||||||
{
|
{
|
||||||
@@ -40,6 +41,9 @@ namespace Il2CppInspector.Model
|
|||||||
// The types are ordered to enable the production of code output without forward dependencies
|
// The types are ordered to enable the production of code output without forward dependencies
|
||||||
public List<CppType> DependencyOrderedCppTypes { get; private set; }
|
public List<CppType> DependencyOrderedCppTypes { get; private set; }
|
||||||
|
|
||||||
|
// Required forward definition types for the C++ type definitions
|
||||||
|
public List<CppType> RequiredForwardDefinitions { get; private set; } = [];
|
||||||
|
|
||||||
// Composite mapping of all the .NET methods in the IL2CPP binary
|
// Composite mapping of all the .NET methods in the IL2CPP binary
|
||||||
public MultiKeyDictionary<MethodBase, CppFnPtrType, AppMethod> Methods { get; } = new MultiKeyDictionary<MethodBase, CppFnPtrType, AppMethod>();
|
public MultiKeyDictionary<MethodBase, CppFnPtrType, AppMethod> Methods { get; } = new MultiKeyDictionary<MethodBase, CppFnPtrType, AppMethod>();
|
||||||
|
|
||||||
@@ -149,12 +153,12 @@ namespace Il2CppInspector.Model
|
|||||||
UnityHeaders = unityVersion != null ? UnityHeaders.GetHeadersForVersion(unityVersion) : UnityHeaders.GuessHeadersForBinary(TypeModel.Package.Binary).Last();
|
UnityHeaders = unityVersion != null ? UnityHeaders.GetHeadersForVersion(unityVersion) : UnityHeaders.GuessHeadersForBinary(TypeModel.Package.Binary).Last();
|
||||||
UnityVersion = unityVersion ?? UnityHeaders.VersionRange.Min;
|
UnityVersion = unityVersion ?? UnityHeaders.VersionRange.Min;
|
||||||
|
|
||||||
Console.WriteLine($"Selected Unity version(s) {UnityHeaders.VersionRange} (types: {UnityHeaders.TypeHeaderResource.VersionRange}, APIs: {UnityHeaders.APIHeaderResource.VersionRange})");
|
AnsiConsole.WriteLine($"Selected Unity version(s) {UnityHeaders.VersionRange} (types: {UnityHeaders.TypeHeaderResource.VersionRange}, APIs: {UnityHeaders.APIHeaderResource.VersionRange})");
|
||||||
|
|
||||||
// Check for matching metadata and binary versions
|
// Check for matching metadata and binary versions
|
||||||
if (UnityHeaders.MetadataVersion != Image.Version) {
|
if (UnityHeaders.MetadataVersion != Image.Version) {
|
||||||
Console.WriteLine($"Warning: selected version {UnityVersion} (metadata version {UnityHeaders.MetadataVersion})" +
|
AnsiConsole.WriteLine($"Warning: selected version {UnityVersion} (metadata version {UnityHeaders.MetadataVersion})" +
|
||||||
$" does not match metadata version {Image.Version}.");
|
$" does not match metadata version {Image.Version}.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize declaration generator to process every type in the binary
|
// Initialize declaration generator to process every type in the binary
|
||||||
@@ -236,8 +240,18 @@ namespace Il2CppInspector.Model
|
|||||||
break;
|
break;
|
||||||
case MetadataUsageType.MethodDef or MetadataUsageType.MethodRef:
|
case MetadataUsageType.MethodDef or MetadataUsageType.MethodRef:
|
||||||
var method = TypeModel.GetMetadataUsageMethod(usage);
|
var method = TypeModel.GetMetadataUsageMethod(usage);
|
||||||
|
|
||||||
declarationGenerator.IncludeMethod(method);
|
declarationGenerator.IncludeMethod(method);
|
||||||
AddTypes(declarationGenerator.GenerateRemainingTypeDeclarations());
|
var definitions = declarationGenerator.GenerateRemainingTypeDeclarations();
|
||||||
|
if (definitions == null)
|
||||||
|
{
|
||||||
|
// if we end up here, type generation has failed
|
||||||
|
// todo: this try/catch is a massive hack to sidestep the original issue of generation failing,
|
||||||
|
// todo: this needs to be improved.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
AddTypes(definitions);
|
||||||
|
|
||||||
// Any method here SHOULD already be in the Methods list
|
// Any method here SHOULD already be in the Methods list
|
||||||
// but we have seen one example where this is not the case for a MethodDef
|
// but we have seen one example where this is not the case for a MethodDef
|
||||||
@@ -247,6 +261,7 @@ namespace Il2CppInspector.Model
|
|||||||
Methods.Add(method, fnPtr, new AppMethod(method, fnPtr) { Group = Group });
|
Methods.Add(method, fnPtr, new AppMethod(method, fnPtr) { Group = Group });
|
||||||
}
|
}
|
||||||
Methods[method].MethodInfoPtrAddress = address;
|
Methods[method].MethodInfoPtrAddress = address;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// FieldInfo is used for array initializers.
|
// FieldInfo is used for array initializers.
|
||||||
@@ -294,6 +309,8 @@ namespace Il2CppInspector.Model
|
|||||||
declarationGenerator.IncludeType(type);
|
declarationGenerator.IncludeType(type);
|
||||||
AddTypes(declarationGenerator.GenerateRemainingTypeDeclarations());
|
AddTypes(declarationGenerator.GenerateRemainingTypeDeclarations());
|
||||||
|
|
||||||
|
RequiredForwardDefinitions = declarationGenerator.GenerateRequiredForwardDefinitions();
|
||||||
|
|
||||||
// Restore stdout
|
// Restore stdout
|
||||||
Console.SetOut(stdout);
|
Console.SetOut(stdout);
|
||||||
|
|
||||||
|
|||||||
@@ -77,6 +77,7 @@ public partial record struct Il2CppCodeRegistration
|
|||||||
[NativeInteger]
|
[NativeInteger]
|
||||||
[VersionCondition(EqualTo = "29.0", IncludingTag = "2022"), VersionCondition(EqualTo = "31.0", IncludingTag = "2022")]
|
[VersionCondition(EqualTo = "29.0", IncludingTag = "2022"), VersionCondition(EqualTo = "31.0", IncludingTag = "2022")]
|
||||||
[VersionCondition(EqualTo = "29.0", IncludingTag = "2023"), VersionCondition(EqualTo = "31.0", IncludingTag = "2023")]
|
[VersionCondition(EqualTo = "29.0", IncludingTag = "2023"), VersionCondition(EqualTo = "31.0", IncludingTag = "2023")]
|
||||||
|
[VersionCondition(GreaterThan = "35.0")]
|
||||||
public uint UnresolvedIndirectCallCount; // UnresolvedVirtualCallCount pre 29.1
|
public uint UnresolvedIndirectCallCount; // UnresolvedVirtualCallCount pre 29.1
|
||||||
|
|
||||||
[VersionCondition(GreaterThan = "22.0")]
|
[VersionCondition(GreaterThan = "22.0")]
|
||||||
@@ -84,10 +85,12 @@ public partial record struct Il2CppCodeRegistration
|
|||||||
|
|
||||||
[VersionCondition(EqualTo = "29.0", IncludingTag = "2022"), VersionCondition(EqualTo = "31.0", IncludingTag = "2022")]
|
[VersionCondition(EqualTo = "29.0", IncludingTag = "2022"), VersionCondition(EqualTo = "31.0", IncludingTag = "2022")]
|
||||||
[VersionCondition(EqualTo = "29.0", IncludingTag = "2023"), VersionCondition(EqualTo = "31.0", IncludingTag = "2023")]
|
[VersionCondition(EqualTo = "29.0", IncludingTag = "2023"), VersionCondition(EqualTo = "31.0", IncludingTag = "2023")]
|
||||||
|
[VersionCondition(GreaterThan = "35.0")]
|
||||||
public Pointer<Il2CppMethodPointer> UnresolvedInstanceCallWrappers;
|
public Pointer<Il2CppMethodPointer> UnresolvedInstanceCallWrappers;
|
||||||
|
|
||||||
[VersionCondition(EqualTo = "29.0", IncludingTag = "2022"), VersionCondition(EqualTo = "31.0", IncludingTag = "2022")]
|
[VersionCondition(EqualTo = "29.0", IncludingTag = "2022"), VersionCondition(EqualTo = "31.0", IncludingTag = "2022")]
|
||||||
[VersionCondition(EqualTo = "29.0", IncludingTag = "2023"), VersionCondition(EqualTo = "31.0", IncludingTag = "2023")]
|
[VersionCondition(EqualTo = "29.0", IncludingTag = "2023"), VersionCondition(EqualTo = "31.0", IncludingTag = "2023")]
|
||||||
|
[VersionCondition(GreaterThan = "35.0")]
|
||||||
public Pointer<Il2CppMethodPointer> UnresolvedStaticCallPointers;
|
public Pointer<Il2CppMethodPointer> UnresolvedStaticCallPointers;
|
||||||
|
|
||||||
[NativeInteger]
|
[NativeInteger]
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
using VersionedSerialization;
|
||||||
|
|
||||||
|
namespace Il2CppInspector.Next.Metadata;
|
||||||
|
|
||||||
|
public struct GenericContainerIndex(int value) : IIndexType<GenericContainerIndex>, IReadable, IEquatable<GenericContainerIndex>
|
||||||
|
{
|
||||||
|
public const string TagPrefix = nameof(GenericContainerIndex);
|
||||||
|
|
||||||
|
static string IIndexType<GenericContainerIndex>.TagPrefix => TagPrefix;
|
||||||
|
static StructVersion IIndexType<GenericContainerIndex>.AddedVersion => MetadataVersions.V390;
|
||||||
|
|
||||||
|
private int _value = value;
|
||||||
|
|
||||||
|
public static int Size(in StructVersion version = default, bool is32Bit = false)
|
||||||
|
=> IIndexType<GenericContainerIndex>.IndexSize(version, is32Bit);
|
||||||
|
|
||||||
|
public void Read<TReader>(ref TReader reader, in StructVersion version = default) where TReader : IReader, allows ref struct
|
||||||
|
{
|
||||||
|
_value = IIndexType<GenericContainerIndex>.ReadIndex(ref reader, in version);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Operators + ToString
|
||||||
|
|
||||||
|
public static implicit operator int(GenericContainerIndex idx) => idx._value;
|
||||||
|
public static implicit operator GenericContainerIndex(int idx) => new(idx);
|
||||||
|
|
||||||
|
public static bool operator ==(GenericContainerIndex left, GenericContainerIndex right)
|
||||||
|
=> left._value == right._value;
|
||||||
|
|
||||||
|
public static bool operator !=(GenericContainerIndex left, GenericContainerIndex right)
|
||||||
|
=> !(left == right);
|
||||||
|
|
||||||
|
public readonly override bool Equals(object obj)
|
||||||
|
=> obj is GenericContainerIndex other && Equals(other);
|
||||||
|
|
||||||
|
public readonly bool Equals(GenericContainerIndex other)
|
||||||
|
=> this == other;
|
||||||
|
|
||||||
|
public readonly override int GetHashCode()
|
||||||
|
=> HashCode.Combine(_value);
|
||||||
|
|
||||||
|
public readonly override string ToString() => _value.ToString();
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
55
Il2CppInspector.Common/Next/Metadata/IIndexType.cs
Normal file
55
Il2CppInspector.Common/Next/Metadata/IIndexType.cs
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
using VersionedSerialization;
|
||||||
|
|
||||||
|
namespace Il2CppInspector.Next.Metadata;
|
||||||
|
|
||||||
|
public interface IIndexType<T> where T
|
||||||
|
: IIndexType<T>, allows ref struct
|
||||||
|
{
|
||||||
|
public static abstract string TagPrefix { get; }
|
||||||
|
public static abstract StructVersion AddedVersion { get; }
|
||||||
|
|
||||||
|
private static string TagSize4 => $"{T.TagPrefix}4";
|
||||||
|
private static string TagSize2 => $"{T.TagPrefix}2";
|
||||||
|
private static string TagSize1 => $"{T.TagPrefix}1";
|
||||||
|
|
||||||
|
private static bool HasCustomSize(in StructVersion version)
|
||||||
|
=> version >= T.AddedVersion
|
||||||
|
&& version.Tag != null
|
||||||
|
&& version.Tag.Contains(T.TagPrefix)
|
||||||
|
&& !version.Tag.Contains(TagSize4);
|
||||||
|
|
||||||
|
public static int IndexSize(in StructVersion version = default, bool is32Bit = false)
|
||||||
|
{
|
||||||
|
if (version.Tag != null && HasCustomSize(version))
|
||||||
|
{
|
||||||
|
if (version.Tag.Contains(TagSize2))
|
||||||
|
return sizeof(ushort);
|
||||||
|
|
||||||
|
if (version.Tag.Contains(TagSize1))
|
||||||
|
return sizeof(byte);
|
||||||
|
}
|
||||||
|
|
||||||
|
return sizeof(int);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int ReadIndex<TReader>(ref TReader reader, in StructVersion version = default) where TReader : IReader, allows ref struct
|
||||||
|
{
|
||||||
|
if (version.Tag != null && HasCustomSize(version))
|
||||||
|
{
|
||||||
|
if (version.Tag.Contains(TagSize2))
|
||||||
|
{
|
||||||
|
var value = reader.ReadPrimitive<ushort>();
|
||||||
|
return value == ushort.MaxValue ? -1 : value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (version.Tag.Contains(TagSize1))
|
||||||
|
{
|
||||||
|
var value = reader.ReadPrimitive<byte>();
|
||||||
|
return value == byte.MaxValue ? -1 : value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return reader.ReadPrimitive<int>();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,6 +20,10 @@ public partial record struct Il2CppAssemblyDefinition
|
|||||||
[VersionCondition(GreaterThan = "24.1")]
|
[VersionCondition(GreaterThan = "24.1")]
|
||||||
public uint Token;
|
public uint Token;
|
||||||
|
|
||||||
|
[FieldOffset(20 + 52)]
|
||||||
|
[VersionCondition(GreaterThan = "38.0")]
|
||||||
|
public uint ModuleToken;
|
||||||
|
|
||||||
[FieldOffset(8)]
|
[FieldOffset(8)]
|
||||||
[VersionCondition(LessThan = "24.0")]
|
[VersionCondition(LessThan = "24.0")]
|
||||||
public int CustomAttributeIndex;
|
public int CustomAttributeIndex;
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
namespace Il2CppInspector.Next.Metadata;
|
namespace Il2CppInspector.Next.Metadata;
|
||||||
|
|
||||||
using StringIndex = int;
|
using StringIndex = int;
|
||||||
using TypeIndex = int;
|
|
||||||
using MethodIndex = int;
|
using MethodIndex = int;
|
||||||
using VersionedSerialization.Attributes;
|
using VersionedSerialization.Attributes;
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
namespace Il2CppInspector.Next.Metadata;
|
namespace Il2CppInspector.Next.Metadata;
|
||||||
|
|
||||||
using FieldIndex = int;
|
using FieldIndex = int;
|
||||||
using TypeIndex = int;
|
|
||||||
using DefaultValueDataIndex = int;
|
using DefaultValueDataIndex = int;
|
||||||
|
|
||||||
[VersionedStruct]
|
[VersionedStruct]
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
using VersionedSerialization.Attributes;
|
using VersionedSerialization.Attributes;
|
||||||
using StringIndex = int;
|
using StringIndex = int;
|
||||||
using TypeIndex = int;
|
|
||||||
|
|
||||||
[VersionedStruct]
|
[VersionedStruct]
|
||||||
public partial record struct Il2CppFieldDefinition
|
public partial record struct Il2CppFieldDefinition
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
using VersionedSerialization.Attributes;
|
using VersionedSerialization.Attributes;
|
||||||
using FieldIndex = int;
|
using FieldIndex = int;
|
||||||
using TypeIndex = int;
|
|
||||||
|
|
||||||
[VersionedStruct]
|
[VersionedStruct]
|
||||||
public partial record struct Il2CppFieldMarshaledSize
|
public partial record struct Il2CppFieldMarshaledSize
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
namespace Il2CppInspector.Next.Metadata;
|
namespace Il2CppInspector.Next.Metadata;
|
||||||
|
|
||||||
using FieldIndex = int;
|
using FieldIndex = int;
|
||||||
using TypeIndex = int;
|
|
||||||
|
|
||||||
[VersionedStruct]
|
[VersionedStruct]
|
||||||
public partial record struct Il2CppFieldRef
|
public partial record struct Il2CppFieldRef
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ using VersionedSerialization.Attributes;
|
|||||||
|
|
||||||
namespace Il2CppInspector.Next.Metadata;
|
namespace Il2CppInspector.Next.Metadata;
|
||||||
|
|
||||||
using GenericContainerIndex = int;
|
|
||||||
using StringIndex = int;
|
using StringIndex = int;
|
||||||
using GenericParameterConstraintIndex = short;
|
using GenericParameterConstraintIndex = short;
|
||||||
|
|
||||||
|
|||||||
@@ -29,55 +29,127 @@ public partial record struct Il2CppGlobalMetadataHeader
|
|||||||
{
|
{
|
||||||
public int Sanity { get; private set; }
|
public int Sanity { get; private set; }
|
||||||
public int Version { get; private set; }
|
public int Version { get; private set; }
|
||||||
|
|
||||||
|
[VersionCondition(LessThan = "35.0")]
|
||||||
public int StringLiteralOffset { get; private set; }
|
public int StringLiteralOffset { get; private set; }
|
||||||
|
|
||||||
|
[VersionCondition(LessThan = "35.0")]
|
||||||
public int StringLiteralSize { get; private set; }
|
public int StringLiteralSize { get; private set; }
|
||||||
|
|
||||||
|
[VersionCondition(LessThan = "35.0")]
|
||||||
public int StringLiteralDataOffset { get; private set; }
|
public int StringLiteralDataOffset { get; private set; }
|
||||||
|
|
||||||
|
[VersionCondition(LessThan = "35.0")]
|
||||||
public int StringLiteralDataSize { get; private set; }
|
public int StringLiteralDataSize { get; private set; }
|
||||||
|
|
||||||
|
[VersionCondition(LessThan = "35.0")]
|
||||||
public int StringOffset { get; private set; }
|
public int StringOffset { get; private set; }
|
||||||
|
|
||||||
|
[VersionCondition(LessThan = "35.0")]
|
||||||
public int StringSize { get; private set; }
|
public int StringSize { get; private set; }
|
||||||
|
|
||||||
|
[VersionCondition(LessThan = "35.0")]
|
||||||
public int EventsOffset { get; private set; }
|
public int EventsOffset { get; private set; }
|
||||||
|
|
||||||
|
[VersionCondition(LessThan = "35.0")]
|
||||||
public int EventsSize { get; private set; }
|
public int EventsSize { get; private set; }
|
||||||
|
|
||||||
|
[VersionCondition(LessThan = "35.0")]
|
||||||
public int PropertiesOffset { get; private set; }
|
public int PropertiesOffset { get; private set; }
|
||||||
|
|
||||||
|
[VersionCondition(LessThan = "35.0")]
|
||||||
public int PropertiesSize { get; private set; }
|
public int PropertiesSize { get; private set; }
|
||||||
|
|
||||||
|
[VersionCondition(LessThan = "35.0")]
|
||||||
public int MethodsOffset { get; private set; }
|
public int MethodsOffset { get; private set; }
|
||||||
|
|
||||||
|
[VersionCondition(LessThan = "35.0")]
|
||||||
public int MethodsSize { get; private set; }
|
public int MethodsSize { get; private set; }
|
||||||
|
|
||||||
[VersionCondition(GreaterThan = "16.0")]
|
[VersionCondition(GreaterThan = "16.0", LessThan = "35.0")]
|
||||||
[VersionCondition(EqualTo = "16.0")]
|
[VersionCondition(EqualTo = "16.0")]
|
||||||
public int ParameterDefaultValuesOffset { get; private set; }
|
public int ParameterDefaultValuesOffset { get; private set; }
|
||||||
|
|
||||||
[VersionCondition(GreaterThan = "16.0")]
|
[VersionCondition(GreaterThan = "16.0", LessThan = "35.0")]
|
||||||
[VersionCondition(EqualTo = "16.0")]
|
[VersionCondition(EqualTo = "16.0")]
|
||||||
public int ParameterDefaultValuesSize { get; private set; }
|
public int ParameterDefaultValuesSize { get; private set; }
|
||||||
|
|
||||||
|
[VersionCondition(LessThan = "35.0")]
|
||||||
public int FieldDefaultValuesOffset { get; private set; }
|
public int FieldDefaultValuesOffset { get; private set; }
|
||||||
|
|
||||||
|
[VersionCondition(LessThan = "35.0")]
|
||||||
public int FieldDefaultValuesSize { get; private set; }
|
public int FieldDefaultValuesSize { get; private set; }
|
||||||
|
|
||||||
|
[VersionCondition(LessThan = "35.0")]
|
||||||
public int FieldAndParameterDefaultValueDataOffset { get; private set; }
|
public int FieldAndParameterDefaultValueDataOffset { get; private set; }
|
||||||
|
|
||||||
|
[VersionCondition(LessThan = "35.0")]
|
||||||
public int FieldAndParameterDefaultValueDataSize { get; private set; }
|
public int FieldAndParameterDefaultValueDataSize { get; private set; }
|
||||||
|
|
||||||
[VersionCondition(GreaterThan = "16.0")]
|
[VersionCondition(GreaterThan = "16.0", LessThan = "35.0")]
|
||||||
public int FieldMarshaledSizesOffset { get; private set; }
|
public int FieldMarshaledSizesOffset { get; private set; }
|
||||||
|
|
||||||
[VersionCondition(GreaterThan = "16.0")]
|
[VersionCondition(GreaterThan = "16.0", LessThan = "35.0")]
|
||||||
public int FieldMarshaledSizesSize { get; private set; }
|
public int FieldMarshaledSizesSize { get; private set; }
|
||||||
|
|
||||||
|
[VersionCondition(LessThan = "35.0")]
|
||||||
public int ParametersOffset { get; private set; }
|
public int ParametersOffset { get; private set; }
|
||||||
|
|
||||||
|
[VersionCondition(LessThan = "35.0")]
|
||||||
public int ParametersSize { get; private set; }
|
public int ParametersSize { get; private set; }
|
||||||
|
|
||||||
|
[VersionCondition(LessThan = "35.0")]
|
||||||
public int FieldsOffset { get; private set; }
|
public int FieldsOffset { get; private set; }
|
||||||
|
|
||||||
|
[VersionCondition(LessThan = "35.0")]
|
||||||
public int FieldsSize { get; private set; }
|
public int FieldsSize { get; private set; }
|
||||||
|
|
||||||
|
[VersionCondition(LessThan = "35.0")]
|
||||||
public int GenericParametersOffset { get; private set; }
|
public int GenericParametersOffset { get; private set; }
|
||||||
|
|
||||||
|
[VersionCondition(LessThan = "35.0")]
|
||||||
public int GenericParametersSize { get; private set; }
|
public int GenericParametersSize { get; private set; }
|
||||||
|
|
||||||
|
[VersionCondition(LessThan = "35.0")]
|
||||||
public int GenericParameterConstraintsOffset { get; private set; }
|
public int GenericParameterConstraintsOffset { get; private set; }
|
||||||
|
|
||||||
|
[VersionCondition(LessThan = "35.0")]
|
||||||
public int GenericParameterConstraintsSize { get; private set; }
|
public int GenericParameterConstraintsSize { get; private set; }
|
||||||
|
|
||||||
|
[VersionCondition(LessThan = "35.0")]
|
||||||
public int GenericContainersOffset { get; private set; }
|
public int GenericContainersOffset { get; private set; }
|
||||||
|
|
||||||
|
[VersionCondition(LessThan = "35.0")]
|
||||||
public int GenericContainersSize { get; private set; }
|
public int GenericContainersSize { get; private set; }
|
||||||
|
|
||||||
|
[VersionCondition(LessThan = "35.0")]
|
||||||
public int NestedTypesOffset { get; private set; }
|
public int NestedTypesOffset { get; private set; }
|
||||||
|
|
||||||
|
[VersionCondition(LessThan = "35.0")]
|
||||||
public int NestedTypesSize { get; private set; }
|
public int NestedTypesSize { get; private set; }
|
||||||
|
|
||||||
|
[VersionCondition(LessThan = "35.0")]
|
||||||
public int InterfacesOffset { get; private set; }
|
public int InterfacesOffset { get; private set; }
|
||||||
|
|
||||||
|
[VersionCondition(LessThan = "35.0")]
|
||||||
public int InterfacesSize { get; private set; }
|
public int InterfacesSize { get; private set; }
|
||||||
|
|
||||||
|
[VersionCondition(LessThan = "35.0")]
|
||||||
public int VTableMethodsOffset { get; private set; }
|
public int VTableMethodsOffset { get; private set; }
|
||||||
|
|
||||||
|
[VersionCondition(LessThan = "35.0")]
|
||||||
public int VTableMethodsSize { get; private set; }
|
public int VTableMethodsSize { get; private set; }
|
||||||
|
|
||||||
|
[VersionCondition(LessThan = "35.0")]
|
||||||
public int InterfaceOffsetsOffset { get; private set; }
|
public int InterfaceOffsetsOffset { get; private set; }
|
||||||
|
|
||||||
|
[VersionCondition(LessThan = "35.0")]
|
||||||
public int InterfaceOffsetsSize { get; private set; }
|
public int InterfaceOffsetsSize { get; private set; }
|
||||||
|
|
||||||
|
[VersionCondition(LessThan = "35.0")]
|
||||||
public int TypeDefinitionsOffset { get; private set; }
|
public int TypeDefinitionsOffset { get; private set; }
|
||||||
|
|
||||||
|
[VersionCondition(LessThan = "35.0")]
|
||||||
public int TypeDefinitionsSize { get; private set; }
|
public int TypeDefinitionsSize { get; private set; }
|
||||||
|
|
||||||
[VersionCondition(LessThan = "24.1")]
|
[VersionCondition(LessThan = "24.1")]
|
||||||
@@ -86,16 +158,16 @@ public partial record struct Il2CppGlobalMetadataHeader
|
|||||||
[VersionCondition(LessThan = "24.1")]
|
[VersionCondition(LessThan = "24.1")]
|
||||||
public int RgctxEntriesCount { get; private set; }
|
public int RgctxEntriesCount { get; private set; }
|
||||||
|
|
||||||
[VersionCondition(GreaterThan = "16.0")]
|
[VersionCondition(GreaterThan = "16.0", LessThan = "35.0")]
|
||||||
public int ImagesOffset { get; private set; }
|
public int ImagesOffset { get; private set; }
|
||||||
|
|
||||||
[VersionCondition(GreaterThan = "16.0")]
|
[VersionCondition(GreaterThan = "16.0", LessThan = "35.0")]
|
||||||
public int ImagesSize { get; private set; }
|
public int ImagesSize { get; private set; }
|
||||||
|
|
||||||
[VersionCondition(GreaterThan = "16.0")]
|
[VersionCondition(GreaterThan = "16.0", LessThan = "35.0")]
|
||||||
public int AssembliesOffset { get; private set; }
|
public int AssembliesOffset { get; private set; }
|
||||||
|
|
||||||
[VersionCondition(GreaterThan = "16.0")]
|
[VersionCondition(GreaterThan = "16.0", LessThan = "35.0")]
|
||||||
public int AssembliesSize { get; private set; }
|
public int AssembliesSize { get; private set; }
|
||||||
|
|
||||||
[VersionCondition(GreaterThan = "19.0", LessThan = "24.5")]
|
[VersionCondition(GreaterThan = "19.0", LessThan = "24.5")]
|
||||||
@@ -110,16 +182,16 @@ public partial record struct Il2CppGlobalMetadataHeader
|
|||||||
[VersionCondition(GreaterThan = "19.0", LessThan = "24.5")]
|
[VersionCondition(GreaterThan = "19.0", LessThan = "24.5")]
|
||||||
public int MetadataUsagePairsCount { get; private set; }
|
public int MetadataUsagePairsCount { get; private set; }
|
||||||
|
|
||||||
[VersionCondition(GreaterThan = "19.0")]
|
[VersionCondition(GreaterThan = "19.0", LessThan = "35.0")]
|
||||||
public int FieldRefsOffset { get; private set; }
|
public int FieldRefsOffset { get; private set; }
|
||||||
|
|
||||||
[VersionCondition(GreaterThan = "19.0")]
|
[VersionCondition(GreaterThan = "19.0", LessThan = "35.0")]
|
||||||
public int FieldRefsSize { get; private set; }
|
public int FieldRefsSize { get; private set; }
|
||||||
|
|
||||||
[VersionCondition(GreaterThan = "20.0")]
|
[VersionCondition(GreaterThan = "20.0", LessThan = "35.0")]
|
||||||
public int ReferencedAssembliesOffset { get; private set; }
|
public int ReferencedAssembliesOffset { get; private set; }
|
||||||
|
|
||||||
[VersionCondition(GreaterThan = "20.0")]
|
[VersionCondition(GreaterThan = "20.0", LessThan = "35.0")]
|
||||||
public int ReferencedAssembliesSize { get; private set; }
|
public int ReferencedAssembliesSize { get; private set; }
|
||||||
|
|
||||||
[VersionCondition(GreaterThan = "21.0", LessThan = "27.2")]
|
[VersionCondition(GreaterThan = "21.0", LessThan = "27.2")]
|
||||||
@@ -134,48 +206,143 @@ public partial record struct Il2CppGlobalMetadataHeader
|
|||||||
[VersionCondition(GreaterThan = "21.0", LessThan = "27.2")]
|
[VersionCondition(GreaterThan = "21.0", LessThan = "27.2")]
|
||||||
public int AttributesTypesCount { get; private set; }
|
public int AttributesTypesCount { get; private set; }
|
||||||
|
|
||||||
[VersionCondition(GreaterThan = "29.0")]
|
[VersionCondition(GreaterThan = "29.0", LessThan = "35.0")]
|
||||||
public int AttributeDataOffset { get; private set; }
|
public int AttributeDataOffset { get; private set; }
|
||||||
|
|
||||||
[VersionCondition(GreaterThan = "29.0")]
|
[VersionCondition(GreaterThan = "29.0", LessThan = "35.0")]
|
||||||
public int AttributeDataSize { get; private set; }
|
public int AttributeDataSize { get; private set; }
|
||||||
|
|
||||||
[VersionCondition(GreaterThan = "29.0")]
|
[VersionCondition(GreaterThan = "29.0", LessThan = "35.0")]
|
||||||
public int AttributeDataRangeOffset { get; private set; }
|
public int AttributeDataRangeOffset { get; private set; }
|
||||||
|
|
||||||
[VersionCondition(GreaterThan = "29.0")]
|
[VersionCondition(GreaterThan = "29.0", LessThan = "35.0")]
|
||||||
public int AttributeDataRangeSize { get; private set; }
|
public int AttributeDataRangeSize { get; private set; }
|
||||||
|
|
||||||
[VersionCondition(GreaterThan = "22.0")]
|
[VersionCondition(GreaterThan = "22.0", LessThan = "35.0")]
|
||||||
public int UnresolvedIndirectCallParameterTypesOffset { get; private set; }
|
public int UnresolvedIndirectCallParameterTypesOffset { get; private set; }
|
||||||
|
|
||||||
[VersionCondition(GreaterThan = "22.0")]
|
[VersionCondition(GreaterThan = "22.0", LessThan = "35.0")]
|
||||||
public int UnresolvedIndirectCallParameterTypesSize { get; private set; }
|
public int UnresolvedIndirectCallParameterTypesSize { get; private set; }
|
||||||
|
|
||||||
[VersionCondition(GreaterThan = "22.0")]
|
[VersionCondition(GreaterThan = "22.0", LessThan = "35.0")]
|
||||||
public int UnresolvedIndirectCallParameterRangesOffset { get; private set; }
|
public int UnresolvedIndirectCallParameterRangesOffset { get; private set; }
|
||||||
|
|
||||||
[VersionCondition(GreaterThan = "22.0")]
|
[VersionCondition(GreaterThan = "22.0", LessThan = "35.0")]
|
||||||
public int UnresolvedIndirectCallParameterRangesSize { get; private set; }
|
public int UnresolvedIndirectCallParameterRangesSize { get; private set; }
|
||||||
|
|
||||||
[VersionCondition(GreaterThan = "23.0")]
|
[VersionCondition(GreaterThan = "23.0", LessThan = "35.0")]
|
||||||
public int WindowsRuntimeTypeNamesOffset { get; private set; }
|
public int WindowsRuntimeTypeNamesOffset { get; private set; }
|
||||||
|
|
||||||
[VersionCondition(GreaterThan = "23.0")]
|
[VersionCondition(GreaterThan = "23.0", LessThan = "35.0")]
|
||||||
public int WindowsRuntimeTypeNamesSize { get; private set; }
|
public int WindowsRuntimeTypeNamesSize { get; private set; }
|
||||||
|
|
||||||
[VersionCondition(GreaterThan = "27.0")]
|
[VersionCondition(GreaterThan = "27.0", LessThan = "35.0")]
|
||||||
public int WindowsRuntimeStringsOffset { get; private set; }
|
public int WindowsRuntimeStringsOffset { get; private set; }
|
||||||
|
|
||||||
[VersionCondition(GreaterThan = "27.0")]
|
[VersionCondition(GreaterThan = "27.0", LessThan = "35.0")]
|
||||||
public int WindowsRuntimeStringsSize { get; private set; }
|
public int WindowsRuntimeStringsSize { get; private set; }
|
||||||
|
|
||||||
[VersionCondition(GreaterThan = "24.0")]
|
[VersionCondition(GreaterThan = "24.0", LessThan = "35.0")]
|
||||||
public int ExportedTypeDefinitionsOffset { get; private set; }
|
public int ExportedTypeDefinitionsOffset { get; private set; }
|
||||||
|
|
||||||
[VersionCondition(GreaterThan = "24.0")]
|
[VersionCondition(GreaterThan = "24.0", LessThan = "35.0")]
|
||||||
public int ExportedTypeDefinitionsSize { get; private set; }
|
public int ExportedTypeDefinitionsSize { get; private set; }
|
||||||
|
|
||||||
|
// new, v38 metadata sections
|
||||||
|
|
||||||
|
[VersionCondition(GreaterThan = "38.0")]
|
||||||
|
public Il2CppSectionMetadata StringLiterals { get; private set; }
|
||||||
|
|
||||||
|
[VersionCondition(GreaterThan = "38.0")]
|
||||||
|
public Il2CppSectionMetadata StringLiteralData { get; private set; }
|
||||||
|
|
||||||
|
[VersionCondition(GreaterThan = "38.0")]
|
||||||
|
public Il2CppSectionMetadata Strings { get; private set; }
|
||||||
|
|
||||||
|
[VersionCondition(GreaterThan = "38.0")]
|
||||||
|
public Il2CppSectionMetadata Events { get; private set; }
|
||||||
|
|
||||||
|
[VersionCondition(GreaterThan = "38.0")]
|
||||||
|
public Il2CppSectionMetadata Properties { get; private set; }
|
||||||
|
|
||||||
|
[VersionCondition(GreaterThan = "38.0")]
|
||||||
|
public Il2CppSectionMetadata Methods { get; private set; }
|
||||||
|
|
||||||
|
[VersionCondition(GreaterThan = "38.0")]
|
||||||
|
public Il2CppSectionMetadata ParameterDefaultValues { get; private set; }
|
||||||
|
|
||||||
|
[VersionCondition(GreaterThan = "38.0")]
|
||||||
|
public Il2CppSectionMetadata FieldDefaultValues { get; private set; }
|
||||||
|
|
||||||
|
[VersionCondition(GreaterThan = "38.0")]
|
||||||
|
public Il2CppSectionMetadata FieldAndParameterDefaultValueData { get; private set; }
|
||||||
|
|
||||||
|
[VersionCondition(GreaterThan = "38.0")]
|
||||||
|
public Il2CppSectionMetadata FieldMarshaledSizes { get; private set; }
|
||||||
|
|
||||||
|
[VersionCondition(GreaterThan = "38.0")]
|
||||||
|
public Il2CppSectionMetadata Parameters { get; private set; }
|
||||||
|
|
||||||
|
[VersionCondition(GreaterThan = "38.0")]
|
||||||
|
public Il2CppSectionMetadata Fields { get; private set; }
|
||||||
|
|
||||||
|
[VersionCondition(GreaterThan = "38.0")]
|
||||||
|
public Il2CppSectionMetadata GenericParameters { get; private set; }
|
||||||
|
|
||||||
|
[VersionCondition(GreaterThan = "38.0")]
|
||||||
|
public Il2CppSectionMetadata GenericParameterConstraints { get; private set; }
|
||||||
|
|
||||||
|
[VersionCondition(GreaterThan = "38.0")]
|
||||||
|
public Il2CppSectionMetadata GenericContainers { get; private set; }
|
||||||
|
|
||||||
|
[VersionCondition(GreaterThan = "38.0")]
|
||||||
|
public Il2CppSectionMetadata NestedTypes { get; private set; }
|
||||||
|
|
||||||
|
[VersionCondition(GreaterThan = "38.0")]
|
||||||
|
public Il2CppSectionMetadata Interfaces { get; private set; }
|
||||||
|
|
||||||
|
[VersionCondition(GreaterThan = "38.0")]
|
||||||
|
public Il2CppSectionMetadata VtableMethods { get; private set; }
|
||||||
|
|
||||||
|
[VersionCondition(GreaterThan = "38.0")]
|
||||||
|
public Il2CppSectionMetadata InterfaceOffsets { get; private set; }
|
||||||
|
|
||||||
|
[VersionCondition(GreaterThan = "38.0")]
|
||||||
|
public Il2CppSectionMetadata TypeDefinitions { get; private set; }
|
||||||
|
|
||||||
|
[VersionCondition(GreaterThan = "38.0")]
|
||||||
|
public Il2CppSectionMetadata Images { get; private set; }
|
||||||
|
|
||||||
|
[VersionCondition(GreaterThan = "38.0")]
|
||||||
|
public Il2CppSectionMetadata Assemblies { get; private set; }
|
||||||
|
|
||||||
|
[VersionCondition(GreaterThan = "38.0")]
|
||||||
|
public Il2CppSectionMetadata FieldRefs { get; private set; }
|
||||||
|
|
||||||
|
[VersionCondition(GreaterThan = "38.0")]
|
||||||
|
public Il2CppSectionMetadata ReferencedAssemblies { get; private set; }
|
||||||
|
|
||||||
|
[VersionCondition(GreaterThan = "38.0")]
|
||||||
|
public Il2CppSectionMetadata AttributeData { get; private set; }
|
||||||
|
|
||||||
|
[VersionCondition(GreaterThan = "38.0")]
|
||||||
|
public Il2CppSectionMetadata AttributeDataRanges { get; private set; }
|
||||||
|
|
||||||
|
[VersionCondition(GreaterThan = "38.0")]
|
||||||
|
public Il2CppSectionMetadata UnresolvedIndirectCallParameterTypes { get; private set; }
|
||||||
|
|
||||||
|
[VersionCondition(GreaterThan = "38.0")]
|
||||||
|
public Il2CppSectionMetadata UnresolvedIndirectCallParameterRanges { get; private set; }
|
||||||
|
|
||||||
|
[VersionCondition(GreaterThan = "38.0")]
|
||||||
|
public Il2CppSectionMetadata WindowsRuntimeTypeNames { get; private set; }
|
||||||
|
|
||||||
|
[VersionCondition(GreaterThan = "38.0")]
|
||||||
|
public Il2CppSectionMetadata WindowsRuntimeStrings { get; private set; }
|
||||||
|
|
||||||
|
[VersionCondition(GreaterThan = "38.0")]
|
||||||
|
public Il2CppSectionMetadata ExportedTypeDefinitions { get; private set; }
|
||||||
|
|
||||||
|
|
||||||
public const int ExpectedSanity = unchecked((int)0xFAB11BAF);
|
public const int ExpectedSanity = unchecked((int)0xFAB11BAF);
|
||||||
public readonly bool SanityValid => Sanity == ExpectedSanity;
|
public readonly bool SanityValid => Sanity == ExpectedSanity;
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
using StringIndex = int;
|
using StringIndex = int;
|
||||||
using AssemblyIndex = int;
|
using AssemblyIndex = int;
|
||||||
using TypeDefinitionIndex = int;
|
|
||||||
using MethodIndex = int;
|
using MethodIndex = int;
|
||||||
using CustomAttributeIndex = int;
|
using CustomAttributeIndex = int;
|
||||||
using VersionedSerialization.Attributes;
|
using VersionedSerialization.Attributes;
|
||||||
|
|||||||
@@ -2,8 +2,6 @@
|
|||||||
|
|
||||||
namespace Il2CppInspector.Next.Metadata;
|
namespace Il2CppInspector.Next.Metadata;
|
||||||
|
|
||||||
using TypeIndex = int;
|
|
||||||
|
|
||||||
[VersionedStruct]
|
[VersionedStruct]
|
||||||
public partial record struct Il2CppInterfaceOffsetPair
|
public partial record struct Il2CppInterfaceOffsetPair
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4,10 +4,6 @@ using VersionedSerialization.Attributes;
|
|||||||
namespace Il2CppInspector.Next.Metadata;
|
namespace Il2CppInspector.Next.Metadata;
|
||||||
|
|
||||||
using StringIndex = int;
|
using StringIndex = int;
|
||||||
using TypeDefinitionIndex = int;
|
|
||||||
using TypeIndex = int;
|
|
||||||
using ParameterIndex = int;
|
|
||||||
using GenericContainerIndex = int;
|
|
||||||
|
|
||||||
[VersionedStruct]
|
[VersionedStruct]
|
||||||
public partial record struct Il2CppMethodDefinition
|
public partial record struct Il2CppMethodDefinition
|
||||||
@@ -18,7 +14,7 @@ public partial record struct Il2CppMethodDefinition
|
|||||||
public TypeDefinitionIndex DeclaringType { get; private set; }
|
public TypeDefinitionIndex DeclaringType { get; private set; }
|
||||||
public TypeIndex ReturnType { get; private set; }
|
public TypeIndex ReturnType { get; private set; }
|
||||||
|
|
||||||
[VersionCondition(EqualTo = "31.0")]
|
[VersionCondition(GreaterThan = "31.0")]
|
||||||
public uint ReturnParameterToken { get; private set; }
|
public uint ReturnParameterToken { get; private set; }
|
||||||
|
|
||||||
public ParameterIndex ParameterStart { get; private set; }
|
public ParameterIndex ParameterStart { get; private set; }
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
namespace Il2CppInspector.Next.Metadata;
|
namespace Il2CppInspector.Next.Metadata;
|
||||||
|
|
||||||
using ParameterIndex = int;
|
|
||||||
using TypeIndex = int;
|
|
||||||
using DefaultValueDataIndex = int;
|
using DefaultValueDataIndex = int;
|
||||||
using VersionedSerialization.Attributes;
|
using VersionedSerialization.Attributes;
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
using VersionedSerialization.Attributes;
|
using VersionedSerialization.Attributes;
|
||||||
using StringIndex = int;
|
using StringIndex = int;
|
||||||
using TypeIndex = int;
|
|
||||||
|
|
||||||
[VersionedStruct]
|
[VersionedStruct]
|
||||||
public partial record struct Il2CppParameterDefinition
|
public partial record struct Il2CppParameterDefinition
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
using VersionedSerialization.Attributes;
|
||||||
|
|
||||||
|
namespace Il2CppInspector.Next.Metadata;
|
||||||
|
|
||||||
|
[VersionedStruct]
|
||||||
|
public partial record struct Il2CppSectionMetadata
|
||||||
|
{
|
||||||
|
public int Offset { get; private set; }
|
||||||
|
public int SectionSize { get; private set; }
|
||||||
|
public int Count { get; private set; }
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ using StringLiteralIndex = int;
|
|||||||
[VersionedStruct]
|
[VersionedStruct]
|
||||||
public partial record struct Il2CppStringLiteral
|
public partial record struct Il2CppStringLiteral
|
||||||
{
|
{
|
||||||
|
[VersionCondition(LessThan = "31.0")]
|
||||||
public uint Length { get; private set; }
|
public uint Length { get; private set; }
|
||||||
public StringLiteralIndex DataIndex { get; private set; }
|
public StringLiteralIndex DataIndex { get; private set; }
|
||||||
}
|
}
|
||||||
@@ -1,11 +1,10 @@
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using VersionedSerialization;
|
||||||
using VersionedSerialization.Attributes;
|
using VersionedSerialization.Attributes;
|
||||||
|
|
||||||
namespace Il2CppInspector.Next.Metadata;
|
namespace Il2CppInspector.Next.Metadata;
|
||||||
|
|
||||||
using StringIndex = int;
|
using StringIndex = int;
|
||||||
using TypeIndex = int;
|
|
||||||
using GenericContainerIndex = int;
|
|
||||||
using FieldIndex = int;
|
using FieldIndex = int;
|
||||||
using MethodIndex = int;
|
using MethodIndex = int;
|
||||||
using EventIndex = int;
|
using EventIndex = int;
|
||||||
@@ -17,7 +16,7 @@ using VTableIndex = int;
|
|||||||
[VersionedStruct]
|
[VersionedStruct]
|
||||||
public partial record struct Il2CppTypeDefinition
|
public partial record struct Il2CppTypeDefinition
|
||||||
{
|
{
|
||||||
public const TypeIndex InvalidTypeIndex = -1;
|
public static readonly TypeIndex InvalidTypeIndex = -1;
|
||||||
|
|
||||||
public StringIndex NameIndex { get; private set; }
|
public StringIndex NameIndex { get; private set; }
|
||||||
public StringIndex NamespaceIndex { get; private set; }
|
public StringIndex NamespaceIndex { get; private set; }
|
||||||
@@ -32,6 +31,8 @@ public partial record struct Il2CppTypeDefinition
|
|||||||
|
|
||||||
public TypeIndex DeclaringTypeIndex { get; private set; }
|
public TypeIndex DeclaringTypeIndex { get; private set; }
|
||||||
public TypeIndex ParentIndex { get; private set; }
|
public TypeIndex ParentIndex { get; private set; }
|
||||||
|
|
||||||
|
[VersionCondition(LessThan = "31.0")]
|
||||||
public TypeIndex ElementTypeIndex { get; private set; }
|
public TypeIndex ElementTypeIndex { get; private set; }
|
||||||
|
|
||||||
[VersionCondition(LessThan = "24.1")]
|
[VersionCondition(LessThan = "24.1")]
|
||||||
@@ -80,4 +81,9 @@ public partial record struct Il2CppTypeDefinition
|
|||||||
public uint Token { get; private set; }
|
public uint Token { get; private set; }
|
||||||
|
|
||||||
public readonly bool IsValid => NameIndex != 0;
|
public readonly bool IsValid => NameIndex != 0;
|
||||||
|
|
||||||
|
public int GetEnumElementTypeIndex(StructVersion version)
|
||||||
|
=> version >= MetadataVersions.V350
|
||||||
|
? ParentIndex
|
||||||
|
: ElementTypeIndex;
|
||||||
}
|
}
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
using VersionedSerialization.Attributes;
|
using VersionedSerialization.Attributes;
|
||||||
using StringIndex = int;
|
using StringIndex = int;
|
||||||
using TypeIndex = int;
|
|
||||||
|
|
||||||
[VersionedStruct]
|
[VersionedStruct]
|
||||||
public partial record struct Il2CppWindowsRuntimeTypeNamePair
|
public partial record struct Il2CppWindowsRuntimeTypeNamePair
|
||||||
|
|||||||
46
Il2CppInspector.Common/Next/Metadata/ParameterIndex.cs
Normal file
46
Il2CppInspector.Common/Next/Metadata/ParameterIndex.cs
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
using NoisyCowStudios.Bin2Object;
|
||||||
|
using VersionedSerialization;
|
||||||
|
|
||||||
|
namespace Il2CppInspector.Next.Metadata;
|
||||||
|
|
||||||
|
public struct ParameterIndex(int value) : IIndexType<ParameterIndex>, IReadable, IEquatable<ParameterIndex>
|
||||||
|
{
|
||||||
|
public const string TagPrefix = nameof(ParameterIndex);
|
||||||
|
|
||||||
|
static string IIndexType<ParameterIndex>.TagPrefix => TagPrefix;
|
||||||
|
static StructVersion IIndexType<ParameterIndex>.AddedVersion => MetadataVersions.V390;
|
||||||
|
|
||||||
|
private int _value = value;
|
||||||
|
|
||||||
|
public static int Size(in StructVersion version = default, bool is32Bit = false)
|
||||||
|
=> IIndexType<ParameterIndex>.IndexSize(version, is32Bit);
|
||||||
|
|
||||||
|
public void Read<TReader>(ref TReader reader, in StructVersion version = default) where TReader : IReader, allows ref struct
|
||||||
|
{
|
||||||
|
_value = IIndexType<ParameterIndex>.ReadIndex(ref reader, in version);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Operators + ToString
|
||||||
|
|
||||||
|
public static implicit operator int(ParameterIndex idx) => idx._value;
|
||||||
|
public static implicit operator ParameterIndex(int idx) => new(idx);
|
||||||
|
|
||||||
|
public static bool operator ==(ParameterIndex left, ParameterIndex right)
|
||||||
|
=> left._value == right._value;
|
||||||
|
|
||||||
|
public static bool operator !=(ParameterIndex left, ParameterIndex right)
|
||||||
|
=> !(left == right);
|
||||||
|
|
||||||
|
public readonly override bool Equals(object obj)
|
||||||
|
=> obj is ParameterIndex other && Equals(other);
|
||||||
|
|
||||||
|
public readonly bool Equals(ParameterIndex other)
|
||||||
|
=> this == other;
|
||||||
|
|
||||||
|
public readonly override int GetHashCode()
|
||||||
|
=> HashCode.Combine(_value);
|
||||||
|
|
||||||
|
public readonly override string ToString() => _value.ToString();
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
45
Il2CppInspector.Common/Next/Metadata/TypeDefinitionIndex.cs
Normal file
45
Il2CppInspector.Common/Next/Metadata/TypeDefinitionIndex.cs
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
using VersionedSerialization;
|
||||||
|
|
||||||
|
namespace Il2CppInspector.Next.Metadata;
|
||||||
|
|
||||||
|
public struct TypeDefinitionIndex(int value) : IIndexType<TypeDefinitionIndex>, IReadable, IEquatable<TypeDefinitionIndex>
|
||||||
|
{
|
||||||
|
public const string TagPrefix = nameof(TypeDefinitionIndex);
|
||||||
|
|
||||||
|
static string IIndexType<TypeDefinitionIndex>.TagPrefix => TagPrefix;
|
||||||
|
static StructVersion IIndexType<TypeDefinitionIndex>.AddedVersion => MetadataVersions.V390;
|
||||||
|
|
||||||
|
private int _value = value;
|
||||||
|
|
||||||
|
public static int Size(in StructVersion version = default, bool is32Bit = false)
|
||||||
|
=> IIndexType<TypeDefinitionIndex>.IndexSize(version, is32Bit);
|
||||||
|
|
||||||
|
public void Read<TReader>(ref TReader reader, in StructVersion version = default) where TReader : IReader, allows ref struct
|
||||||
|
{
|
||||||
|
_value = IIndexType<TypeDefinitionIndex>.ReadIndex(ref reader, in version);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Operators + ToString
|
||||||
|
|
||||||
|
public static implicit operator int(TypeDefinitionIndex idx) => idx._value;
|
||||||
|
public static implicit operator TypeDefinitionIndex(int idx) => new(idx);
|
||||||
|
|
||||||
|
public static bool operator ==(TypeDefinitionIndex left, TypeDefinitionIndex right)
|
||||||
|
=> left._value == right._value;
|
||||||
|
|
||||||
|
public static bool operator !=(TypeDefinitionIndex left, TypeDefinitionIndex right)
|
||||||
|
=> !(left == right);
|
||||||
|
|
||||||
|
public readonly override bool Equals(object obj)
|
||||||
|
=> obj is TypeDefinitionIndex other && Equals(other);
|
||||||
|
|
||||||
|
public readonly bool Equals(TypeDefinitionIndex other)
|
||||||
|
=> this == other;
|
||||||
|
|
||||||
|
public readonly override int GetHashCode()
|
||||||
|
=> HashCode.Combine(_value);
|
||||||
|
|
||||||
|
public readonly override string ToString() => _value.ToString();
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
45
Il2CppInspector.Common/Next/Metadata/TypeIndex.cs
Normal file
45
Il2CppInspector.Common/Next/Metadata/TypeIndex.cs
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
using VersionedSerialization;
|
||||||
|
|
||||||
|
namespace Il2CppInspector.Next.Metadata;
|
||||||
|
|
||||||
|
public struct TypeIndex(int value) : IIndexType<TypeIndex>, IReadable, IEquatable<TypeIndex>
|
||||||
|
{
|
||||||
|
public const string TagPrefix = nameof(TypeIndex);
|
||||||
|
|
||||||
|
static string IIndexType<TypeIndex>.TagPrefix => TagPrefix;
|
||||||
|
static StructVersion IIndexType<TypeIndex>.AddedVersion => MetadataVersions.V390;
|
||||||
|
|
||||||
|
private int _value = value;
|
||||||
|
|
||||||
|
public static int Size(in StructVersion version = default, bool is32Bit = false)
|
||||||
|
=> IIndexType<TypeIndex>.IndexSize(version, is32Bit);
|
||||||
|
|
||||||
|
public void Read<TReader>(ref TReader reader, in StructVersion version = default) where TReader : IReader, allows ref struct
|
||||||
|
{
|
||||||
|
_value = IIndexType<TypeIndex>.ReadIndex(ref reader, in version);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Operators + ToString
|
||||||
|
|
||||||
|
public static implicit operator int(TypeIndex idx) => idx._value;
|
||||||
|
public static implicit operator TypeIndex(int idx) => new(idx);
|
||||||
|
|
||||||
|
public static bool operator ==(TypeIndex left, TypeIndex right)
|
||||||
|
=> left._value == right._value;
|
||||||
|
|
||||||
|
public static bool operator !=(TypeIndex left, TypeIndex right)
|
||||||
|
=> !(left == right);
|
||||||
|
|
||||||
|
public readonly override bool Equals(object obj)
|
||||||
|
=> obj is TypeIndex other && Equals(other);
|
||||||
|
|
||||||
|
public readonly bool Equals(TypeIndex other)
|
||||||
|
=> this == other;
|
||||||
|
|
||||||
|
public readonly override int GetHashCode()
|
||||||
|
=> HashCode.Combine(_value);
|
||||||
|
|
||||||
|
public readonly override string ToString() => _value.ToString();
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
@@ -28,4 +28,15 @@ public static class MetadataVersions
|
|||||||
|
|
||||||
// No tag - 29.0/31.0
|
// No tag - 29.0/31.0
|
||||||
public static readonly string Tag2022 = "2022"; // 29.1/31.1
|
public static readonly string Tag2022 = "2022"; // 29.1/31.1
|
||||||
|
|
||||||
|
// Unity 6000.3.0a2
|
||||||
|
public static readonly StructVersion V350 = new(35);
|
||||||
|
|
||||||
|
// Unity 6000.3.0a5
|
||||||
|
public static readonly StructVersion V380 = new(38);
|
||||||
|
// NOTE: This version uses tags to specify the size of TypeIndex, TypeDefinitionIndex, and GenericContainerIndex.
|
||||||
|
|
||||||
|
// Unity 6000.3.0b1
|
||||||
|
public static readonly StructVersion V390 = new(39);
|
||||||
|
// NOTE This version additionally uses a tag to specify the size of ParameterIndex.
|
||||||
}
|
}
|
||||||
@@ -298,6 +298,10 @@ namespace Il2CppInspector.Outputs
|
|||||||
private PropertyDef AddProperty(ModuleDef module, TypeDef mType, PropertyInfo prop) {
|
private PropertyDef AddProperty(ModuleDef module, TypeDef mType, PropertyInfo prop) {
|
||||||
PropertySig s;
|
PropertySig s;
|
||||||
|
|
||||||
|
// Example: ZstdSharp MEM_32Bit which gets inlined using weaving and all accessors removed
|
||||||
|
if (prop.GetMethod == null && prop.SetMethod == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
// Static or instance
|
// Static or instance
|
||||||
if (prop.GetMethod?.IsStatic ?? prop.SetMethod.IsStatic)
|
if (prop.GetMethod?.IsStatic ?? prop.SetMethod.IsStatic)
|
||||||
s = PropertySig.CreateStatic(GetTypeSig(module, prop.PropertyType));
|
s = PropertySig.CreateStatic(GetTypeSig(module, prop.PropertyType));
|
||||||
@@ -430,7 +434,7 @@ namespace Il2CppInspector.Outputs
|
|||||||
if (method.VirtualAddress.HasValue) {
|
if (method.VirtualAddress.HasValue) {
|
||||||
var args = new List<(string,object)> {
|
var args = new List<(string,object)> {
|
||||||
("RVA", (method.VirtualAddress.Value.Start - model.Package.BinaryImage.ImageBase).ToAddressString()),
|
("RVA", (method.VirtualAddress.Value.Start - model.Package.BinaryImage.ImageBase).ToAddressString()),
|
||||||
("Offset", string.Format("0x{0:X}", model.Package.BinaryImage.MapVATR(method.VirtualAddress.Value.Start))),
|
("Offset", $"0x{model.Package.BinaryImage.MapVATR(method.VirtualAddress.Value.Start):X}"),
|
||||||
("VA", method.VirtualAddress.Value.Start.ToAddressString())
|
("VA", method.VirtualAddress.Value.Start.ToAddressString())
|
||||||
};
|
};
|
||||||
if (method.Definition.Slot != ushort.MaxValue)
|
if (method.Definition.Slot != ushort.MaxValue)
|
||||||
@@ -470,7 +474,7 @@ namespace Il2CppInspector.Outputs
|
|||||||
return def.AddAttribute(module, attributeAttribute,
|
return def.AddAttribute(module, attributeAttribute,
|
||||||
("Name", ca.AttributeType.Name),
|
("Name", ca.AttributeType.Name),
|
||||||
("RVA", (ca.VirtualAddress.Start - model.Package.BinaryImage.ImageBase).ToAddressString()),
|
("RVA", (ca.VirtualAddress.Start - model.Package.BinaryImage.ImageBase).ToAddressString()),
|
||||||
("Offset", string.Format("0x{0:X}", model.Package.BinaryImage.MapVATR(ca.VirtualAddress.Start)))
|
("Offset", $"0x{model.Package.BinaryImage.MapVATR(ca.VirtualAddress.Start):X}")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -44,8 +44,13 @@ namespace Il2CppInspector.Outputs
|
|||||||
|
|
||||||
// Write primitive type definitions for when we're not including other headers
|
// Write primitive type definitions for when we're not including other headers
|
||||||
writeCode($"""
|
writeCode($"""
|
||||||
#define IS_LIBCLANG_DECOMPILER (defined(_IDACLANG_) || defined(_BINARYNINJA_))
|
#if defined(_IDACLANG_) || defined(_BINARYNINJA_)
|
||||||
#define IS_DECOMPILER (defined(_GHIDRA_) || defined(_IDA_) || IS_LIBCLANG_DECOMPILER)
|
#define IS_LIBCLANG_DECOMPILER
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(_GHIDRA_) || defined(_IDA_) || defined(IS_LIBCLANG_DECOMPILER)
|
||||||
|
#define IS_DECOMPILER
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(_GHIDRA_) || defined(_IDA_)
|
#if defined(_GHIDRA_) || defined(_IDA_)
|
||||||
typedef unsigned __int8 uint8_t;
|
typedef unsigned __int8 uint8_t;
|
||||||
@@ -58,7 +63,7 @@ namespace Il2CppInspector.Outputs
|
|||||||
typedef __int64 int64_t;
|
typedef __int64 int64_t;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if IS_LIBCLANG_DECOMPILER
|
#if defined(IS_LIBCLANG_DECOMPILER)
|
||||||
typedef unsigned char uint8_t;
|
typedef unsigned char uint8_t;
|
||||||
typedef unsigned short uint16_t;
|
typedef unsigned short uint16_t;
|
||||||
typedef unsigned int uint32_t;
|
typedef unsigned int uint32_t;
|
||||||
@@ -67,21 +72,26 @@ namespace Il2CppInspector.Outputs
|
|||||||
typedef short int16_t;
|
typedef short int16_t;
|
||||||
typedef int int32_t;
|
typedef int int32_t;
|
||||||
typedef long int64_t;
|
typedef long int64_t;
|
||||||
|
|
||||||
|
#ifdef linux
|
||||||
|
#undef linux
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(_GHIDRA_) || IS_LIBCLANG_DECOMPILER
|
#endif
|
||||||
|
|
||||||
|
#if defined(_GHIDRA_) || defined(IS_LIBCLANG_DECOMPILER)
|
||||||
typedef int{_model.Package.BinaryImage.Bits}_t intptr_t;
|
typedef int{_model.Package.BinaryImage.Bits}_t intptr_t;
|
||||||
typedef uint{_model.Package.BinaryImage.Bits}_t uintptr_t;
|
typedef uint{_model.Package.BinaryImage.Bits}_t uintptr_t;
|
||||||
typedef uint{_model.Package.BinaryImage.Bits}_t size_t;
|
typedef uint{_model.Package.BinaryImage.Bits}_t size_t;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if !IS_DECOMPILER
|
#ifndef IS_DECOMPILER
|
||||||
#define _CPLUSPLUS_
|
#define _CPLUSPLUS_
|
||||||
#endif
|
#endif
|
||||||
""");
|
""");
|
||||||
|
|
||||||
if (_useBetterArraySize)
|
if (_useBetterArraySize)
|
||||||
writeCode("#define actual_il2cpp_array_size_t il2cpp_array_size_t");
|
writeCode("#define il2cpp_array_size_t actual_il2cpp_array_size_t");
|
||||||
|
|
||||||
writeSectionHeader("IL2CPP internal types");
|
writeSectionHeader("IL2CPP internal types");
|
||||||
writeCode(_model.UnityHeaders.GetTypeHeaderText(_model.WordSizeBits));
|
writeCode(_model.UnityHeaders.GetTypeHeaderText(_model.WordSizeBits));
|
||||||
@@ -94,9 +104,7 @@ namespace Il2CppInspector.Outputs
|
|||||||
{
|
{
|
||||||
int32_t size;
|
int32_t size;
|
||||||
actual_il2cpp_array_size_t value;
|
actual_il2cpp_array_size_t value;
|
||||||
} better_il2cpp_array_size_t;
|
} il2cpp_array_size_t;
|
||||||
|
|
||||||
#define better_il2cpp_array_size_t il2cpp_array_size_t
|
|
||||||
""");
|
""");
|
||||||
|
|
||||||
if (_model.TargetCompiler == CppCompilerType.MSVC)
|
if (_model.TargetCompiler == CppCompilerType.MSVC)
|
||||||
@@ -115,17 +123,20 @@ namespace Il2CppInspector.Outputs
|
|||||||
}
|
}
|
||||||
|
|
||||||
// C does not support namespaces
|
// C does not support namespaces
|
||||||
writeCode("#if !IS_DECOMPILER");
|
writeCode("#ifndef IS_DECOMPILER");
|
||||||
writeCode("namespace app {");
|
writeCode("namespace app {");
|
||||||
writeCode("#endif");
|
writeCode("#endif");
|
||||||
writeLine("");
|
writeLine("");
|
||||||
|
|
||||||
|
writeForwardDefinitions();
|
||||||
|
|
||||||
|
writeTypesForGroup("Required forward definitions", "required_forward_definitions");
|
||||||
writeTypesForGroup("Application types from method calls", "types_from_methods");
|
writeTypesForGroup("Application types from method calls", "types_from_methods");
|
||||||
writeTypesForGroup("Application types from generic methods", "types_from_generic_methods");
|
writeTypesForGroup("Application types from generic methods", "types_from_generic_methods");
|
||||||
writeTypesForGroup("Application types from usages", "types_from_usages");
|
writeTypesForGroup("Application types from usages", "types_from_usages");
|
||||||
writeTypesForGroup("Application unused value types", "unused_concrete_types");
|
writeTypesForGroup("Application unused value types", "unused_concrete_types");
|
||||||
|
|
||||||
writeCode("#if !IS_DECOMPILER");
|
writeCode("#ifndef IS_DECOMPILER");
|
||||||
writeCode("}");
|
writeCode("}");
|
||||||
writeCode("#endif");
|
writeCode("#endif");
|
||||||
}
|
}
|
||||||
@@ -306,19 +317,32 @@ namespace Il2CppInspector.Outputs
|
|||||||
writeLine("");
|
writeLine("");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void writeTypesForGroup(string header, string group) {
|
private void writeForwardDefinitions()
|
||||||
|
{
|
||||||
|
writeSectionHeader("Required forward definitions");
|
||||||
|
foreach (var cppType in _model.RequiredForwardDefinitions)
|
||||||
|
writeCode(cppType.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeTypesForGroup(string header, string group)
|
||||||
|
{
|
||||||
writeSectionHeader(header);
|
writeSectionHeader(header);
|
||||||
foreach (var cppType in _model.GetDependencyOrderedCppTypeGroup(group))
|
foreach (var cppType in _model.GetDependencyOrderedCppTypeGroup(group))
|
||||||
if (cppType is CppEnumType) {
|
{
|
||||||
|
if (cppType is CppEnumType)
|
||||||
|
{
|
||||||
// Ghidra can't process C++ enum base types
|
// Ghidra can't process C++ enum base types
|
||||||
writeCode("#if defined(_CPLUSPLUS_)");
|
writeCode("#if defined(_CPLUSPLUS_)");
|
||||||
writeCode(cppType.ToString());
|
writeCode(cppType.ToString());
|
||||||
writeCode("#else");
|
writeCode("#else");
|
||||||
writeCode(cppType.ToString("c"));
|
writeCode(cppType.ToString("c"));
|
||||||
writeCode("#endif");
|
writeCode("#endif");
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
writeCode(cppType.ToString());
|
writeCode(cppType.ToString());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void writeCode(string text) {
|
private void writeCode(string text) {
|
||||||
|
|||||||
@@ -217,7 +217,6 @@ namespace Il2CppInspector.Outputs
|
|||||||
foreach (var symbol in symbols) {
|
foreach (var symbol in symbols) {
|
||||||
writeObject(() => {
|
writeObject(() => {
|
||||||
writeName(symbol.VirtualAddress, symbol.Name);
|
writeName(symbol.VirtualAddress, symbol.Name);
|
||||||
writer.WriteString("demangledName", symbol.DemangledName);
|
|
||||||
writer.WriteString("type", symbol.Type.ToString());
|
writer.WriteString("type", symbol.Type.ToString());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,284 +1,330 @@
|
|||||||
from binaryninja import *
|
from binaryninja import (
|
||||||
|
BinaryView,
|
||||||
|
Component,
|
||||||
|
Type,
|
||||||
|
PointerType,
|
||||||
|
TypeParser,
|
||||||
|
Platform,
|
||||||
|
Endianness,
|
||||||
|
ArrayType,
|
||||||
|
BackgroundTaskThread,
|
||||||
|
demangle_gnu3,
|
||||||
|
get_qualified_name,
|
||||||
|
SegmentFlag,
|
||||||
|
SectionSemantics,
|
||||||
|
)
|
||||||
|
from binaryninja.log import log_error
|
||||||
|
|
||||||
#try:
|
# try:
|
||||||
# from typing import TYPE_CHECKING
|
# from typing import TYPE_CHECKING
|
||||||
# if TYPE_CHECKING:
|
# if TYPE_CHECKING:
|
||||||
# from ..shared_base import BaseStatusHandler, BaseDisassemblerInterface, ScriptContext
|
# from ..shared_base import BaseStatusHandler, BaseDisassemblerInterface, ScriptContext
|
||||||
# import json
|
# import json
|
||||||
# import os
|
# import os
|
||||||
# import sys
|
# import sys
|
||||||
# from datetime import datetime
|
# from datetime import datetime
|
||||||
#except:
|
# from typing import Literal
|
||||||
# pass
|
# bv: BinaryView = None # type: ignore
|
||||||
|
# except:
|
||||||
|
# pass
|
||||||
|
|
||||||
CURRENT_PATH = os.path.dirname(os.path.realpath(__file__))
|
CURRENT_PATH = os.path.dirname(os.path.realpath(__file__))
|
||||||
|
|
||||||
|
|
||||||
class BinaryNinjaDisassemblerInterface(BaseDisassemblerInterface):
|
class BinaryNinjaDisassemblerInterface(BaseDisassemblerInterface):
|
||||||
# this is implemented,
|
supports_fake_string_segment: bool = True
|
||||||
# however the write API does not seem to work properly here (possibly a bug),
|
|
||||||
# so this is disabled for now
|
|
||||||
supports_fake_string_segment: bool = False
|
|
||||||
|
|
||||||
_status: BaseStatusHandler
|
_status: BaseStatusHandler
|
||||||
|
|
||||||
_view: BinaryView
|
|
||||||
_undo_id: str
|
|
||||||
_components: dict[str, Component]
|
|
||||||
_type_cache: dict[str, Type]
|
|
||||||
_function_type_cache: dict[str, Type]
|
|
||||||
|
|
||||||
_address_size: int
|
_view: BinaryView
|
||||||
_endianness: Literal["little", "big"]
|
_undo_id: str
|
||||||
|
_components: dict[str, Component]
|
||||||
|
_type_cache: dict[str, Type]
|
||||||
|
_function_type_cache: dict[str, Type]
|
||||||
|
|
||||||
def __init__(self, status: BaseStatusHandler):
|
_address_size: int
|
||||||
self._status = status
|
_endianness: Literal["little", "big"]
|
||||||
|
|
||||||
def _get_or_create_type(self, type: str) -> Type:
|
TYPE_PARSER_OPTIONS = ["--target=x86_64-pc-linux", "-x", "c++", "-D_BINARYNINJA_=1"]
|
||||||
if type.startswith("struct "):
|
|
||||||
type = type[len("struct "):]
|
|
||||||
elif type.startswith("class "):
|
|
||||||
type = type[len("class "):]
|
|
||||||
|
|
||||||
if type in self._type_cache:
|
def __init__(self, status: BaseStatusHandler):
|
||||||
return self._type_cache[type]
|
self._status = status
|
||||||
|
|
||||||
if type.endswith("*"):
|
|
||||||
base_type = self._get_or_create_type(type[:-1].strip())
|
|
||||||
|
|
||||||
parsed = PointerType.create(self._view.arch, base_type) # type: ignore
|
def _get_or_create_type(self, type: str) -> Type:
|
||||||
else:
|
if type.startswith("struct "):
|
||||||
parsed = self._view.get_type_by_name(type)
|
type = type[len("struct ") :]
|
||||||
if parsed is None:
|
elif type.startswith("class "):
|
||||||
parsed, errors = self._view.parse_type_string(type)
|
type = type[len("class ") :]
|
||||||
|
|
||||||
self._type_cache[type] = parsed
|
if type in self._type_cache:
|
||||||
return parsed
|
return self._type_cache[type]
|
||||||
|
|
||||||
def get_script_directory(self) -> str:
|
if type.endswith("*"):
|
||||||
return CURRENT_PATH
|
base_type = self._get_or_create_type(type[:-1].strip())
|
||||||
|
|
||||||
def on_start(self):
|
parsed = PointerType.create(self._view.arch, base_type) # type: ignore
|
||||||
self._view = bv # type: ignore
|
else:
|
||||||
self._undo_id = self._view.begin_undo_actions()
|
parsed = self._view.get_type_by_name(type)
|
||||||
self._view.set_analysis_hold(True)
|
if parsed is None:
|
||||||
self._components = {}
|
parsed, errors = self._view.parse_type_string(type)
|
||||||
self._type_cache = {}
|
|
||||||
self._function_type_cache = {}
|
|
||||||
|
|
||||||
self._address_size = self._view.address_size
|
self._type_cache[type] = parsed
|
||||||
self._endianness = "little" if self._view.endianness == Endianness.LittleEndian else "big"
|
return parsed
|
||||||
|
|
||||||
self._status.update_step("Parsing header")
|
|
||||||
|
|
||||||
with open(os.path.join(self.get_script_directory(), "il2cpp.h"), "r") as f:
|
def _parse_type_source(self, types: str, filename: Union[str, None] = None):
|
||||||
parsed_types, errors = TypeParser.default.parse_types_from_source(
|
parsed_types, errors = TypeParser.default.parse_types_from_source(
|
||||||
f.read(),
|
types,
|
||||||
"il2cpp.h",
|
filename if filename else "types.hpp",
|
||||||
self._view.platform if self._view.platform is not None else Platform["windows-x86_64"],
|
self._view.platform
|
||||||
self._view,
|
if self._view.platform is not None
|
||||||
[
|
else Platform["windows-x86_64"],
|
||||||
"--target=x86_64-pc-linux",
|
self._view,
|
||||||
"-x", "c++",
|
self.TYPE_PARSER_OPTIONS,
|
||||||
"-D_BINARYNINJA_=1"
|
)
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
if parsed_types is None:
|
if parsed_types is None:
|
||||||
log_error("Failed to import header")
|
log_error("Failed to import types.")
|
||||||
log_error(errors)
|
log_error(errors)
|
||||||
return
|
return None
|
||||||
|
|
||||||
self._status.update_step("Importing header types", len(parsed_types.types))
|
return parsed_types
|
||||||
|
|
||||||
def import_progress_func(progress: int, total: int):
|
def get_script_directory(self) -> str:
|
||||||
self._status.update_progress(1)
|
return CURRENT_PATH
|
||||||
return True
|
|
||||||
|
|
||||||
self._view.define_user_types([(x.name, x.type) for x in parsed_types.types], import_progress_func)
|
def on_start(self):
|
||||||
|
self._view = bv # type: ignore
|
||||||
|
self._undo_id = self._view.begin_undo_actions()
|
||||||
|
self._view.set_analysis_hold(True)
|
||||||
|
self._components = {}
|
||||||
|
self._type_cache = {}
|
||||||
|
self._function_type_cache = {}
|
||||||
|
|
||||||
def on_finish(self):
|
self._address_size = self._view.address_size
|
||||||
self._view.commit_undo_actions(self._undo_id)
|
self._endianness = (
|
||||||
self._view.set_analysis_hold(False)
|
"little" if self._view.endianness == Endianness.LittleEndian else "big"
|
||||||
self._view.update_analysis()
|
)
|
||||||
|
|
||||||
def define_function(self, address: int, end: int | None = None):
|
self._status.update_step("Parsing header")
|
||||||
if self._view.get_function_at(address) is not None:
|
|
||||||
return
|
|
||||||
|
|
||||||
self._view.create_user_function(address)
|
|
||||||
|
|
||||||
def define_data_array(self, address: int, type: str, count: int):
|
with open(os.path.join(self.get_script_directory(), "il2cpp.h"), "r") as f:
|
||||||
parsed_type = self._get_or_create_type(type)
|
parsed_types = self._parse_type_source(f.read(), "il2cpp.hpp")
|
||||||
array_type = ArrayType.create(parsed_type, count)
|
if parsed_types is None:
|
||||||
var = self._view.get_data_var_at(address)
|
return
|
||||||
if var is None:
|
|
||||||
self._view.define_user_data_var(address, array_type)
|
|
||||||
else:
|
|
||||||
var.type = array_type
|
|
||||||
|
|
||||||
def set_data_type(self, address: int, type: str):
|
self._status.update_step("Importing header types", len(parsed_types.types))
|
||||||
var = self._view.get_data_var_at(address)
|
|
||||||
dtype = self._get_or_create_type(type)
|
|
||||||
if var is None:
|
|
||||||
self._view.define_user_data_var(address, dtype)
|
|
||||||
else:
|
|
||||||
var.type = dtype
|
|
||||||
|
|
||||||
def set_function_type(self, address: int, type: str):
|
def import_progress_func(progress: int, total: int):
|
||||||
function = self._view.get_function_at(address)
|
self._status.update_progress(1)
|
||||||
if function is None:
|
return True
|
||||||
return
|
|
||||||
|
|
||||||
if type in self._function_type_cache:
|
|
||||||
function.type = self._function_type_cache[type] # type: ignore
|
|
||||||
else:
|
|
||||||
#log_info(f"skipping function type setting for {address}, {type}")
|
|
||||||
#pass
|
|
||||||
function.type = type.replace("this", "`this`")
|
|
||||||
|
|
||||||
def set_data_comment(self, address: int, cmt: str):
|
self._view.define_user_types(
|
||||||
self._view.set_comment_at(address, cmt)
|
[(x.name, x.type) for x in parsed_types.types], import_progress_func
|
||||||
|
)
|
||||||
|
|
||||||
def set_function_comment(self, address: int, cmt: str):
|
def on_finish(self):
|
||||||
function = self._view.get_function_at(address)
|
self._view.commit_undo_actions(self._undo_id)
|
||||||
if function is None:
|
self._view.set_analysis_hold(False)
|
||||||
return
|
self._view.update_analysis()
|
||||||
|
|
||||||
function.comment = cmt
|
def define_function(self, address: int, end: Union[int, None] = None):
|
||||||
|
if self._view.get_function_at(address) is not None:
|
||||||
|
return
|
||||||
|
|
||||||
def set_data_name(self, address: int, name: str):
|
self._view.create_user_function(address)
|
||||||
var = self._view.get_data_var_at(address)
|
|
||||||
if var is None:
|
|
||||||
return
|
|
||||||
|
|
||||||
if name.startswith("_Z"):
|
|
||||||
type, demangled = demangle_gnu3(self._view.arch, name, self._view)
|
|
||||||
var.name = get_qualified_name(demangled)
|
|
||||||
else:
|
|
||||||
var.name = name
|
|
||||||
|
|
||||||
def set_function_name(self, address: int, name: str):
|
def define_data_array(self, address: int, type: str, count: int):
|
||||||
function = self._view.get_function_at(address)
|
parsed_type = self._get_or_create_type(type)
|
||||||
if function is None:
|
array_type = ArrayType.create(parsed_type, count)
|
||||||
return
|
var = self._view.get_data_var_at(address)
|
||||||
|
if var is None:
|
||||||
|
self._view.define_user_data_var(address, array_type)
|
||||||
|
else:
|
||||||
|
var.type = array_type
|
||||||
|
|
||||||
if name.startswith("_Z"):
|
def set_data_type(self, address: int, type: str):
|
||||||
type, demangled = demangle_gnu3(self._view.arch, name, self._view)
|
var = self._view.get_data_var_at(address)
|
||||||
function.name = get_qualified_name(demangled)
|
dtype = self._get_or_create_type(type)
|
||||||
#function.type = type - this does not work due to the generated types not being namespaced. :(
|
if var is None:
|
||||||
else:
|
self._view.define_user_data_var(address, dtype)
|
||||||
function.name = name
|
else:
|
||||||
|
var.type = dtype
|
||||||
|
|
||||||
def add_cross_reference(self, from_address: int, to_address: int):
|
def set_function_type(self, address: int, type: str):
|
||||||
self._view.add_user_data_ref(from_address, to_address)
|
function = self._view.get_function_at(address)
|
||||||
|
if function is None:
|
||||||
|
return
|
||||||
|
|
||||||
def import_c_typedef(self, type_def: str):
|
if type in self._function_type_cache:
|
||||||
self._view.define_user_type(None, type_def)
|
function.type = self._function_type_cache[type] # type: ignore
|
||||||
|
else:
|
||||||
|
# log_info(f"skipping function type setting for {address}, {type}")
|
||||||
|
# pass
|
||||||
|
function.type = type.replace("this", "`this`")
|
||||||
|
|
||||||
# optional
|
def set_data_comment(self, address: int, cmt: str):
|
||||||
def _get_or_create_component(self, name: str):
|
self._view.set_comment_at(address, cmt)
|
||||||
if name in self._components:
|
|
||||||
return self._components[name]
|
|
||||||
|
|
||||||
current = name
|
|
||||||
if current.count("/") != 0:
|
|
||||||
split_idx = current.rindex("/")
|
|
||||||
parent, child = current[:split_idx], current[split_idx:]
|
|
||||||
parent = self._get_or_create_component(name)
|
|
||||||
component = self._view.create_component(child, parent)
|
|
||||||
else:
|
|
||||||
component = self._view.create_component(name)
|
|
||||||
|
|
||||||
self._components[name] = component
|
def set_function_comment(self, address: int, cmt: str):
|
||||||
return component
|
function = self._view.get_function_at(address)
|
||||||
|
if function is None:
|
||||||
|
return
|
||||||
|
|
||||||
def add_function_to_group(self, address: int, group: str):
|
function.comment = cmt
|
||||||
return
|
|
||||||
function = self._view.get_function_at(address)
|
|
||||||
if function is None:
|
|
||||||
return
|
|
||||||
|
|
||||||
self._get_or_create_component(group).add_function(function)
|
|
||||||
|
|
||||||
def cache_function_types(self, signatures: list[str]):
|
def set_data_name(self, address: int, name: str):
|
||||||
function_sigs = set(signatures)
|
var = self._view.get_data_var_at(address)
|
||||||
if len(function_sigs) == 0:
|
if var is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
typestr = ";\n".join(function_sigs).replace("this", "_this") + ";"
|
|
||||||
res = self._view.parse_types_from_string(typestr)
|
|
||||||
for function_sig, function in zip(function_sigs, res.functions.values()): # type: ignore
|
|
||||||
self._function_type_cache[function_sig] = function
|
|
||||||
|
|
||||||
# only required if supports_fake_string_segment == True
|
if name.startswith("_Z"):
|
||||||
def create_fake_segment(self, name: str, size: int) -> int:
|
type, demangled = demangle_gnu3(self._view.arch, name, self._view)
|
||||||
last_end_addr = self._view.mapped_address_ranges[-1].end
|
var.name = get_qualified_name(demangled)
|
||||||
if last_end_addr % 0x1000 != 0:
|
else:
|
||||||
last_end_addr += (0x1000 - (last_end_addr % 0x1000))
|
var.name = name
|
||||||
|
|
||||||
self._view.add_user_segment(last_end_addr, size, 0, 0, SegmentFlag.SegmentContainsData)
|
def set_function_name(self, address: int, name: str):
|
||||||
self._view.add_user_section(name, last_end_addr, size, SectionSemantics.ReadOnlyDataSectionSemantics)
|
function = self._view.get_function_at(address)
|
||||||
return last_end_addr
|
if function is None:
|
||||||
|
return
|
||||||
def write_string(self, address: int, value: str):
|
|
||||||
self._view.write(address, value.encode() + b"\x00")
|
|
||||||
|
|
||||||
def write_address(self, address: int, value: int):
|
if name.startswith("_Z"):
|
||||||
self._view.write(address, value.to_bytes(self._address_size, self._endianness))
|
type, demangled = demangle_gnu3(self._view.arch, name, self._view)
|
||||||
|
function.name = get_qualified_name(demangled)
|
||||||
|
# function.type = type - this does not work due to the generated types not being namespaced. :(
|
||||||
|
else:
|
||||||
|
function.name = name
|
||||||
|
|
||||||
|
def add_cross_reference(self, from_address: int, to_address: int):
|
||||||
|
self._view.add_user_data_ref(from_address, to_address)
|
||||||
|
|
||||||
|
def import_c_typedef(self, type_def: str):
|
||||||
|
self._view.define_user_type(None, type_def)
|
||||||
|
|
||||||
|
# optional
|
||||||
|
def _get_or_create_component(self, name: str):
|
||||||
|
if name in self._components:
|
||||||
|
return self._components[name]
|
||||||
|
|
||||||
|
current = name
|
||||||
|
if current.count("/") != 0:
|
||||||
|
split_idx = current.rindex("/")
|
||||||
|
parent, child = current[:split_idx], current[split_idx:]
|
||||||
|
parent = self._get_or_create_component(name)
|
||||||
|
component = self._view.create_component(child, parent)
|
||||||
|
else:
|
||||||
|
component = self._view.create_component(name)
|
||||||
|
|
||||||
|
self._components[name] = component
|
||||||
|
return component
|
||||||
|
|
||||||
|
def add_function_to_group(self, address: int, group: str):
|
||||||
|
return
|
||||||
|
function = self._view.get_function_at(address)
|
||||||
|
if function is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
self._get_or_create_component(group).add_function(function)
|
||||||
|
|
||||||
|
def cache_function_types(self, signatures: list[str]):
|
||||||
|
function_sigs = set(signatures)
|
||||||
|
if len(function_sigs) == 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
typestr = ";\n".join(function_sigs).replace("this", "_this") + ";"
|
||||||
|
parsed_types = self._parse_type_source(typestr, "cached_types.hpp")
|
||||||
|
if parsed_types is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
# bv.parse_types_from_source returns a dict in the functions field.
|
||||||
|
# TypeParser.parse_types_from_source does not.
|
||||||
|
for function_sig, function in zip(function_sigs, parsed_types.functions):
|
||||||
|
self._function_type_cache[function_sig] = function.type
|
||||||
|
|
||||||
|
# only required if supports_fake_string_segment == True
|
||||||
|
def create_fake_segment(self, name: str, size: int) -> int:
|
||||||
|
last_end_addr = self._view.mapped_address_ranges[-1].end
|
||||||
|
if last_end_addr % 0x1000 != 0:
|
||||||
|
last_end_addr += 0x1000 - (last_end_addr % 0x1000)
|
||||||
|
|
||||||
|
self._view.memory_map.add_memory_region(
|
||||||
|
f"mem_{name}",
|
||||||
|
last_end_addr,
|
||||||
|
bytes(size),
|
||||||
|
SegmentFlag.SegmentContainsData | SegmentFlag.SegmentReadable,
|
||||||
|
)
|
||||||
|
|
||||||
|
self._view.add_user_section(
|
||||||
|
name, last_end_addr, size, SectionSemantics.ReadOnlyDataSectionSemantics
|
||||||
|
)
|
||||||
|
|
||||||
|
return last_end_addr
|
||||||
|
|
||||||
|
def write_string(self, address: int, value: str) -> int:
|
||||||
|
encoded = value.encode() + b"\x00"
|
||||||
|
self._view.write(address, encoded)
|
||||||
|
return len(encoded)
|
||||||
|
|
||||||
|
def write_address(self, address: int, value: int):
|
||||||
|
self._view.write(address, value.to_bytes(self._address_size, self._endianness))
|
||||||
|
|
||||||
|
|
||||||
class BinaryNinjaStatusHandler(BaseStatusHandler):
|
class BinaryNinjaStatusHandler(BaseStatusHandler):
|
||||||
def __init__(self, thread: BackgroundTaskThread):
|
def __init__(self, thread: BackgroundTaskThread):
|
||||||
self.step = "Initializing"
|
self.step = "Initializing"
|
||||||
self.max_items = 0
|
self.max_items = 0
|
||||||
self.current_items = 0
|
self.current_items = 0
|
||||||
self.start_time = datetime.now()
|
self.start_time = datetime.now()
|
||||||
self.step_start_time = self.start_time
|
self.step_start_time = self.start_time
|
||||||
self.last_updated_time = datetime.min
|
self.last_updated_time = datetime.min
|
||||||
self._thread = thread
|
self._thread = thread
|
||||||
|
|
||||||
def initialize(self): pass
|
|
||||||
|
|
||||||
def update(self):
|
def initialize(self):
|
||||||
if self.was_cancelled():
|
pass
|
||||||
raise RuntimeError("Cancelled script.")
|
|
||||||
|
|
||||||
current_time = datetime.now()
|
def update(self):
|
||||||
if 0.5 > (current_time - self.last_updated_time).total_seconds():
|
if self.was_cancelled():
|
||||||
return
|
raise RuntimeError("Cancelled script.")
|
||||||
|
|
||||||
self.last_updated_time = current_time
|
current_time = datetime.now()
|
||||||
|
if 0.5 > (current_time - self.last_updated_time).total_seconds():
|
||||||
|
return
|
||||||
|
|
||||||
step_time = current_time - self.step_start_time
|
self.last_updated_time = current_time
|
||||||
total_time = current_time - self.start_time
|
|
||||||
self._thread.progress = f"Processing IL2CPP metadata: {self.step} ({self.current_items}/{self.max_items}), elapsed: {step_time} ({total_time})"
|
|
||||||
|
|
||||||
def update_step(self, step, max_items = 0):
|
step_time = current_time - self.step_start_time
|
||||||
self.step = step
|
total_time = current_time - self.start_time
|
||||||
self.max_items = max_items
|
self._thread.progress = f"Processing IL2CPP metadata: {self.step} ({self.current_items}/{self.max_items}), elapsed: {step_time} ({total_time})"
|
||||||
self.current_items = 0
|
|
||||||
self.step_start_time = datetime.now()
|
|
||||||
self.last_updated_time = datetime.min
|
|
||||||
self.update()
|
|
||||||
|
|
||||||
def update_progress(self, new_progress = 1):
|
def update_step(self, step, max_items=0):
|
||||||
self.current_items += new_progress
|
self.step = step
|
||||||
self.update()
|
self.max_items = max_items
|
||||||
|
self.current_items = 0
|
||||||
|
self.step_start_time = datetime.now()
|
||||||
|
self.last_updated_time = datetime.min
|
||||||
|
self.update()
|
||||||
|
|
||||||
def was_cancelled(self): return False
|
def update_progress(self, new_progress=1):
|
||||||
|
self.current_items += new_progress
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
def was_cancelled(self):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
pass
|
||||||
|
|
||||||
def close(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
# Entry point
|
# Entry point
|
||||||
class Il2CppTask(BackgroundTaskThread):
|
class Il2CppTask(BackgroundTaskThread):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
BackgroundTaskThread.__init__(self, "Processing IL2CPP metadata...", False)
|
BackgroundTaskThread.__init__(self, "Processing IL2CPP metadata...", False)
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
status = BinaryNinjaStatusHandler(self)
|
status = BinaryNinjaStatusHandler(self)
|
||||||
backend = BinaryNinjaDisassemblerInterface(status)
|
backend = BinaryNinjaDisassemblerInterface(status)
|
||||||
context = ScriptContext(backend, status)
|
context = ScriptContext(backend, status)
|
||||||
context.process()
|
context.process()
|
||||||
|
|
||||||
Il2CppTask().start()
|
|
||||||
|
Il2CppTask().start()
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ from ghidra.program.model.data import ArrayDataType
|
|||||||
from ghidra.program.model.symbol import SourceType
|
from ghidra.program.model.symbol import SourceType
|
||||||
from ghidra.program.model.symbol import RefType
|
from ghidra.program.model.symbol import RefType
|
||||||
from ghidra.app.cmd.label import DemanglerCmd
|
from ghidra.app.cmd.label import DemanglerCmd
|
||||||
|
from ghidra.app.services import DataTypeManagerService
|
||||||
|
from java.lang import Long
|
||||||
|
|
||||||
#try:
|
#try:
|
||||||
# from typing import TYPE_CHECKING
|
# from typing import TYPE_CHECKING
|
||||||
@@ -20,6 +22,9 @@ from ghidra.app.cmd.label import DemanglerCmd
|
|||||||
class GhidraDisassemblerInterface(BaseDisassemblerInterface):
|
class GhidraDisassemblerInterface(BaseDisassemblerInterface):
|
||||||
supports_fake_string_segment = False
|
supports_fake_string_segment = False
|
||||||
|
|
||||||
|
def _to_address(self, value):
|
||||||
|
return toAddr(Long(value))
|
||||||
|
|
||||||
def get_script_directory(self) -> str:
|
def get_script_directory(self) -> str:
|
||||||
return getSourceFile().getParentFile().toString()
|
return getSourceFile().getParentFile().toString()
|
||||||
|
|
||||||
@@ -38,7 +43,7 @@ class GhidraDisassemblerInterface(BaseDisassemblerInterface):
|
|||||||
# Without this, Ghidra may not analyze the binary correctly and you will just waste your time
|
# Without this, Ghidra may not analyze the binary correctly and you will just waste your time
|
||||||
# If 0 doesn't work for you, replace it with the base address from the output of the CLI or GUI
|
# If 0 doesn't work for you, replace it with the base address from the output of the CLI or GUI
|
||||||
if currentProgram.getExecutableFormat().endswith('(ELF)'):
|
if currentProgram.getExecutableFormat().endswith('(ELF)'):
|
||||||
currentProgram.setImageBase(toAddr(0), True)
|
currentProgram.setImageBase(self._to_address(0), True)
|
||||||
|
|
||||||
# Don't trigger decompiler
|
# Don't trigger decompiler
|
||||||
setAnalysisOption(currentProgram, "Call Convention ID", "false")
|
setAnalysisOption(currentProgram, "Call Convention ID", "false")
|
||||||
@@ -46,8 +51,8 @@ class GhidraDisassemblerInterface(BaseDisassemblerInterface):
|
|||||||
def on_finish(self):
|
def on_finish(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def define_function(self, address: int, end: int | None = None):
|
def define_function(self, address: int, end: Union[int, None] = None):
|
||||||
address = toAddr(address)
|
address = self._to_address(address)
|
||||||
# Don't override existing functions
|
# Don't override existing functions
|
||||||
fn = getFunctionAt(address)
|
fn = getFunctionAt(address)
|
||||||
if fn is None:
|
if fn is None:
|
||||||
@@ -60,7 +65,7 @@ class GhidraDisassemblerInterface(BaseDisassemblerInterface):
|
|||||||
|
|
||||||
t = getDataTypes(type)[0]
|
t = getDataTypes(type)[0]
|
||||||
a = ArrayDataType(t, count, t.getLength())
|
a = ArrayDataType(t, count, t.getLength())
|
||||||
address = toAddr(address)
|
address = self._to_address(address)
|
||||||
removeDataAt(address)
|
removeDataAt(address)
|
||||||
createData(address, a)
|
createData(address, a)
|
||||||
|
|
||||||
@@ -70,25 +75,24 @@ class GhidraDisassemblerInterface(BaseDisassemblerInterface):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
t = getDataTypes(type)[0]
|
t = getDataTypes(type)[0]
|
||||||
address = toAddr(address)
|
address = self._to_address(address)
|
||||||
removeDataAt(address)
|
removeDataAt(address)
|
||||||
createData(address, t)
|
createData(address, t)
|
||||||
except:
|
except:
|
||||||
print("Failed to set type: %s" % type)
|
print("Failed to set type: %s" % type)
|
||||||
|
|
||||||
def set_function_type(self, address: int, type: str):
|
def set_function_type(self, address: int, type: str):
|
||||||
make_function(address)
|
typeSig = CParserUtils.parseSignature(DataTypeManagerService@None, currentProgram, type)
|
||||||
typeSig = CParserUtils.parseSignature(None, currentProgram, type)
|
ApplyFunctionSignatureCmd(self._to_address(address), typeSig, SourceType.USER_DEFINED, False, True).applyTo(currentProgram)
|
||||||
ApplyFunctionSignatureCmd(toAddr(address), typeSig, SourceType.USER_DEFINED, False, True).applyTo(currentProgram)
|
|
||||||
|
|
||||||
def set_data_comment(self, address: int, cmt: str):
|
def set_data_comment(self, address: int, cmt: str):
|
||||||
setEOLComment(toAddr(address), cmt)
|
setEOLComment(self._to_address(address), cmt)
|
||||||
|
|
||||||
def set_function_comment(self, address: int, cmt: str):
|
def set_function_comment(self, address: int, cmt: str):
|
||||||
setPlateComment(toAddr(address), cmt)
|
setPlateComment(self._to_address(address), cmt)
|
||||||
|
|
||||||
def set_data_name(self, address: int, name: str):
|
def set_data_name(self, address: int, name: str):
|
||||||
address = toAddr(address)
|
address = self._to_address(address)
|
||||||
|
|
||||||
if len(name) > 2000:
|
if len(name) > 2000:
|
||||||
print("Name length exceeds 2000 characters, skipping (%s)" % name)
|
print("Name length exceeds 2000 characters, skipping (%s)" % name)
|
||||||
@@ -107,7 +111,7 @@ class GhidraDisassemblerInterface(BaseDisassemblerInterface):
|
|||||||
return self.set_data_name(address, name)
|
return self.set_data_name(address, name)
|
||||||
|
|
||||||
def add_cross_reference(self, from_address: int, to_address: int):
|
def add_cross_reference(self, from_address: int, to_address: int):
|
||||||
self.xrefs.addMemoryReference(toAddr(from_address), toAddr(to_address), RefType.DATA, SourceType.USER_DEFINED, 0)
|
self.xrefs.addMemoryReference(self._to_address(from_address), self._to_address(to_address), RefType.DATA, SourceType.USER_DEFINED, 0)
|
||||||
|
|
||||||
def import_c_typedef(self, type_def: str):
|
def import_c_typedef(self, type_def: str):
|
||||||
# Code declarations are not supported in Ghidra
|
# Code declarations are not supported in Ghidra
|
||||||
|
|||||||
@@ -83,8 +83,8 @@ class IDADisassemblerInterface(BaseDisassemblerInterface):
|
|||||||
ida_ida.inf_set_genflags(self._cached_genflags & ~ida_ida.INFFL_AUTO)
|
ida_ida.inf_set_genflags(self._cached_genflags & ~ida_ida.INFFL_AUTO)
|
||||||
|
|
||||||
# Unload type libraries we know to cause issues - like the c++ linux one
|
# Unload type libraries we know to cause issues - like the c++ linux one
|
||||||
PLATFORMS = ["x86", "x64", "arm", "arm64"]
|
PLATFORMS = ["x86", "x64", "arm", "arm64", "win7"]
|
||||||
PROBLEMATIC_TYPELIBS = ["gnulnx"]
|
PROBLEMATIC_TYPELIBS = ["gnulnx", "mssdk64"]
|
||||||
|
|
||||||
for lib in PROBLEMATIC_TYPELIBS:
|
for lib in PROBLEMATIC_TYPELIBS:
|
||||||
for platform in PLATFORMS:
|
for platform in PLATFORMS:
|
||||||
@@ -106,8 +106,8 @@ class IDADisassemblerInterface(BaseDisassemblerInterface):
|
|||||||
ida_typeinf.set_c_macros(original_macros)
|
ida_typeinf.set_c_macros(original_macros)
|
||||||
|
|
||||||
# Skip make_function on Windows GameAssembly.dll files due to them predefining all functions through pdata which makes the method very slow
|
# Skip make_function on Windows GameAssembly.dll files due to them predefining all functions through pdata which makes the method very slow
|
||||||
skip_make_function = ida_segment.get_segm_by_name(".pdata") is not None
|
self._skip_function_creation = ida_segment.get_segm_by_name(".pdata") is not None
|
||||||
if skip_make_function:
|
if self._skip_function_creation:
|
||||||
print(".pdata section found, skipping function boundaries")
|
print(".pdata section found, skipping function boundaries")
|
||||||
|
|
||||||
if FOLDERS_AVAILABLE:
|
if FOLDERS_AVAILABLE:
|
||||||
@@ -118,7 +118,7 @@ class IDADisassemblerInterface(BaseDisassemblerInterface):
|
|||||||
def on_finish(self):
|
def on_finish(self):
|
||||||
ida_ida.inf_set_genflags(self._cached_genflags)
|
ida_ida.inf_set_genflags(self._cached_genflags)
|
||||||
|
|
||||||
def define_function(self, address: int, end: int | None = None):
|
def define_function(self, address: int, end: Union[int, None] = None):
|
||||||
if self._skip_function_creation:
|
if self._skip_function_creation:
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -202,11 +202,12 @@ class IDADisassemblerInterface(BaseDisassemblerInterface):
|
|||||||
|
|
||||||
return start
|
return start
|
||||||
|
|
||||||
def write_string(self, address: int, value: str):
|
def write_string(self, address: int, value: str) -> int:
|
||||||
encoded_string = value.encode() + b'\x00'
|
encoded_string = value.encode() + b'\x00'
|
||||||
string_length = len(encoded_string)
|
string_length = len(encoded_string)
|
||||||
ida_bytes.put_bytes(address, encoded_string)
|
ida_bytes.put_bytes(address, encoded_string)
|
||||||
ida_bytes.create_strlit(address, string_length, ida_nalt.STRTYPE_C)
|
ida_bytes.create_strlit(address, string_length, ida_nalt.STRTYPE_C)
|
||||||
|
return string_length
|
||||||
|
|
||||||
def write_address(self, address: int, value: int):
|
def write_address(self, address: int, value: int):
|
||||||
if self._is_32_bit:
|
if self._is_32_bit:
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
|
#@runtime PyGhidra
|
||||||
|
# ^-- required for ghidra, ignored by all others
|
||||||
|
|
||||||
# Generated script file by Il2CppInspectorRedux - https://github.com/LukeFZ (Original Il2CppInspector by http://www.djkaty.com - https://github.com/djkaty)
|
# Generated script file by Il2CppInspectorRedux - https://github.com/LukeFZ (Original Il2CppInspector by http://www.djkaty.com - https://github.com/djkaty)
|
||||||
# Target Unity version: %TARGET_UNITY_VERSION%
|
# Target Unity version: %TARGET_UNITY_VERSION%
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from typing import Union
|
||||||
import abc
|
import abc
|
||||||
|
|
||||||
class BaseStatusHandler(abc.ABC):
|
class BaseStatusHandler(abc.ABC):
|
||||||
@@ -28,7 +32,7 @@ class BaseDisassemblerInterface(abc.ABC):
|
|||||||
def on_finish(self): pass
|
def on_finish(self): pass
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def define_function(self, address: int, end: int | None = None): pass
|
def define_function(self, address: int, end: Union[int, None] = None): pass
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def define_data_array(self, address: int, type: str, count: int): pass
|
def define_data_array(self, address: int, type: str, count: int): pass
|
||||||
@@ -64,7 +68,7 @@ class BaseDisassemblerInterface(abc.ABC):
|
|||||||
# only required if supports_fake_string_segment == True
|
# only required if supports_fake_string_segment == True
|
||||||
def create_fake_segment(self, name: str, size: int) -> int: return 0
|
def create_fake_segment(self, name: str, size: int) -> int: return 0
|
||||||
|
|
||||||
def write_string(self, address: int, value: str): pass
|
def write_string(self, address: int, value: str) -> int: pass
|
||||||
def write_address(self, address: int, value: int): pass
|
def write_address(self, address: int, value: int): pass
|
||||||
|
|
||||||
class ScriptContext:
|
class ScriptContext:
|
||||||
@@ -108,7 +112,7 @@ class ScriptContext:
|
|||||||
self._backend.set_data_name(addr, definition['name'])
|
self._backend.set_data_name(addr, definition['name'])
|
||||||
self._backend.set_data_comment(addr, definition['string'])
|
self._backend.set_data_comment(addr, definition['string'])
|
||||||
|
|
||||||
def define_field(self, addr: str, name: str, type: str, il_type: str | None = None):
|
def define_field(self, addr: str, name: str, type: str, il_type: Union[str, None] = None):
|
||||||
address = self.from_hex(addr)
|
address = self.from_hex(addr)
|
||||||
self._backend.set_data_type(address, type)
|
self._backend.set_data_type(address, type)
|
||||||
self._backend.set_data_name(address, name)
|
self._backend.set_data_name(address, name)
|
||||||
@@ -191,11 +195,11 @@ class ScriptContext:
|
|||||||
self.define_string(d)
|
self.define_string(d)
|
||||||
|
|
||||||
ref_addr = self.parse_address(d)
|
ref_addr = self.parse_address(d)
|
||||||
self._backend.write_string(current_string_address, d["string"])
|
written_string_length = self._backend.write_string(current_string_address, d["string"])
|
||||||
self._backend.set_data_type(ref_addr, r'const char* const')
|
self._backend.set_data_type(ref_addr, r'const char* const')
|
||||||
self._backend.write_address(ref_addr, current_string_address)
|
self._backend.write_address(ref_addr, current_string_address)
|
||||||
|
|
||||||
current_string_address += len(d["string"]) + 1
|
current_string_address += written_string_length
|
||||||
self._status.update_progress()
|
self._status.update_progress()
|
||||||
else:
|
else:
|
||||||
for d in metadata['stringLiterals']:
|
for d in metadata['stringLiterals']:
|
||||||
@@ -286,5 +290,8 @@ class ScriptContext:
|
|||||||
end_time = datetime.now()
|
end_time = datetime.now()
|
||||||
print(f"Took: {end_time - start_time}")
|
print(f"Took: {end_time - start_time}")
|
||||||
|
|
||||||
except RuntimeError: pass
|
except RuntimeError:
|
||||||
finally: self._status.shutdown()
|
pass
|
||||||
|
|
||||||
|
finally:
|
||||||
|
self._status.shutdown()
|
||||||
@@ -4,6 +4,11 @@
|
|||||||
All rights reserved.
|
All rights reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
using Il2CppInspector.PluginAPI;
|
||||||
|
// This is the ONLY line to update when the API version changes
|
||||||
|
using Il2CppInspector.PluginAPI.V100;
|
||||||
|
using McMaster.NETCore.Plugins;
|
||||||
|
using Spectre.Console;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
@@ -12,11 +17,6 @@ using System.IO;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using McMaster.NETCore.Plugins;
|
|
||||||
using Il2CppInspector.PluginAPI;
|
|
||||||
|
|
||||||
// This is the ONLY line to update when the API version changes
|
|
||||||
using Il2CppInspector.PluginAPI.V100;
|
|
||||||
|
|
||||||
namespace Il2CppInspector
|
namespace Il2CppInspector
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -118,8 +118,8 @@ namespace Il2CppInspector.Reflection
|
|||||||
var range = pkg.Metadata.AttributeDataRanges[customAttributeIndex];
|
var range = pkg.Metadata.AttributeDataRanges[customAttributeIndex];
|
||||||
var next = pkg.Metadata.AttributeDataRanges[customAttributeIndex + 1];
|
var next = pkg.Metadata.AttributeDataRanges[customAttributeIndex + 1];
|
||||||
|
|
||||||
var startOffset = (uint)pkg.Metadata.Header.AttributeDataOffset + range.StartOffset;
|
var startOffset = (uint)pkg.Metadata.AttributeDataOffset + range.StartOffset;
|
||||||
var endOffset = (uint)pkg.Metadata.Header.AttributeDataOffset + next.StartOffset;
|
var endOffset = (uint)pkg.Metadata.AttributeDataOffset + next.StartOffset;
|
||||||
|
|
||||||
var reader = new CustomAttributeDataReader(pkg, asm, pkg.Metadata, startOffset, endOffset);
|
var reader = new CustomAttributeDataReader(pkg, asm, pkg.Metadata, startOffset, endOffset);
|
||||||
if (reader.Count == 0)
|
if (reader.Count == 0)
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ using System.Linq;
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
|
using Il2CppInspector.Next;
|
||||||
using Il2CppInspector.Next.BinaryMetadata;
|
using Il2CppInspector.Next.BinaryMetadata;
|
||||||
using Il2CppInspector.Next.Metadata;
|
using Il2CppInspector.Next.Metadata;
|
||||||
|
|
||||||
@@ -255,7 +256,7 @@ namespace Il2CppInspector.Reflection
|
|||||||
public PropertyInfo GetProperty(string name) => DeclaredProperties.FirstOrDefault(p => p.Name == name);
|
public PropertyInfo GetProperty(string name) => DeclaredProperties.FirstOrDefault(p => p.Name == name);
|
||||||
|
|
||||||
public MethodBase[] GetVTable() {
|
public MethodBase[] GetVTable() {
|
||||||
if (!Definition.IsValid) {
|
if (Definition.IsValid) {
|
||||||
MetadataUsage[] vt = Assembly.Model.Package.GetVTable(Definition);
|
MetadataUsage[] vt = Assembly.Model.Package.GetVTable(Definition);
|
||||||
MethodBase[] res = new MethodBase[vt.Length];
|
MethodBase[] res = new MethodBase[vt.Length];
|
||||||
for (int i = 0; i < vt.Length; i++) {
|
for (int i = 0; i < vt.Length; i++) {
|
||||||
@@ -784,7 +785,9 @@ namespace Il2CppInspector.Reflection
|
|||||||
// Enumerations - bit 1 of bitfield indicates this (also the baseTypeReference will be System.Enum)
|
// Enumerations - bit 1 of bitfield indicates this (also the baseTypeReference will be System.Enum)
|
||||||
if (Definition.Bitfield.EnumType) {
|
if (Definition.Bitfield.EnumType) {
|
||||||
IsEnum = true;
|
IsEnum = true;
|
||||||
enumUnderlyingTypeReference = TypeRef.FromReferenceIndex(Assembly.Model, Definition.ElementTypeIndex);
|
|
||||||
|
var enumUnderlyingTypeIndex = Definition.GetEnumElementTypeIndex(Assembly.Model.Package.Version);
|
||||||
|
enumUnderlyingTypeReference = TypeRef.FromReferenceIndex(Assembly.Model, enumUnderlyingTypeIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pass-by-reference type
|
// Pass-by-reference type
|
||||||
@@ -1190,4 +1193,4 @@ namespace Il2CppInspector.Reflection
|
|||||||
|
|
||||||
public override string ToString() => Name;
|
public override string ToString() => Name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -165,10 +165,9 @@ namespace Il2CppInspector.Reflection
|
|||||||
// Generic type definitions have an invoker index of -1
|
// Generic type definitions have an invoker index of -1
|
||||||
foreach (var method in MethodsByDefinitionIndex) {
|
foreach (var method in MethodsByDefinitionIndex) {
|
||||||
var index = package.GetInvokerIndex(method.DeclaringType.Assembly.ModuleDefinition, method.Definition);
|
var index = package.GetInvokerIndex(method.DeclaringType.Assembly.ModuleDefinition, method.Definition);
|
||||||
if (index != -1) {
|
if (index != -1)
|
||||||
if (MethodInvokers[index] == null)
|
{
|
||||||
MethodInvokers[index] = new MethodInvoker(method, index);
|
MethodInvokers[index] ??= new MethodInvoker(method, index);
|
||||||
|
|
||||||
method.Invoker = MethodInvokers[index];
|
method.Invoker = MethodInvokers[index];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -176,10 +175,11 @@ namespace Il2CppInspector.Reflection
|
|||||||
// Create method invokers sourced from generic method invoker indices
|
// Create method invokers sourced from generic method invoker indices
|
||||||
foreach (var spec in GenericMethods.Keys) {
|
foreach (var spec in GenericMethods.Keys) {
|
||||||
if (package.GenericMethodInvokerIndices.TryGetValue(spec, out var index)) {
|
if (package.GenericMethodInvokerIndices.TryGetValue(spec, out var index)) {
|
||||||
if (MethodInvokers[index] == null)
|
if (index != -1)
|
||||||
MethodInvokers[index] = new MethodInvoker(GenericMethods[spec], index);
|
{
|
||||||
|
MethodInvokers[index] ??= new MethodInvoker(GenericMethods[spec], index);
|
||||||
GenericMethods[spec].Invoker = MethodInvokers[index];
|
GenericMethods[spec].Invoker = MethodInvokers[index];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ using System.Diagnostics;
|
|||||||
using Il2CppInspector.Next;
|
using Il2CppInspector.Next;
|
||||||
using Il2CppInspector.Next.BinaryMetadata;
|
using Il2CppInspector.Next.BinaryMetadata;
|
||||||
using Il2CppInspector.Next.Metadata;
|
using Il2CppInspector.Next.Metadata;
|
||||||
|
using Spectre.Console;
|
||||||
|
|
||||||
namespace Il2CppInspector.Utils;
|
namespace Il2CppInspector.Utils;
|
||||||
|
|
||||||
@@ -122,7 +123,7 @@ public static class BlobReader
|
|||||||
}
|
}
|
||||||
catch (InvalidDataException)
|
catch (InvalidDataException)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"Found invalid compressed int at metadata address 0x{address:x8}. Reading as normal int.");
|
AnsiConsole.WriteLine($"Found invalid compressed int at metadata address 0x{address:x8}. Reading as normal int.");
|
||||||
return blob.ReadInt32(address);
|
return blob.ReadInt32(address);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -142,7 +143,7 @@ public static class BlobReader
|
|||||||
}
|
}
|
||||||
catch (InvalidDataException)
|
catch (InvalidDataException)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"Found invalid compressed uint at metadata address 0x{address:x8}. Reading as normal uint.");
|
AnsiConsole.WriteLine($"Found invalid compressed uint at metadata address 0x{address:x8}. Reading as normal uint.");
|
||||||
return blob.ReadUInt32(address);
|
return blob.ReadUInt32(address);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -163,7 +164,9 @@ public static class BlobReader
|
|||||||
var typeHandle = inspector.TypeReferences[typeIndex].Data.KlassIndex;
|
var typeHandle = inspector.TypeReferences[typeIndex].Data.KlassIndex;
|
||||||
enumType = inspector.TypeDefinitions[typeHandle];
|
enumType = inspector.TypeDefinitions[typeHandle];
|
||||||
|
|
||||||
var elementTypeHandle = inspector.TypeReferences[enumType.ElementTypeIndex].Data.KlassIndex;
|
var elementTypeIndex = enumType.GetEnumElementTypeIndex(inspector.Version);
|
||||||
|
|
||||||
|
var elementTypeHandle = inspector.TypeReferences[elementTypeIndex].Data.KlassIndex;
|
||||||
var elementType = inspector.TypeDefinitions[elementTypeHandle];
|
var elementType = inspector.TypeDefinitions[elementTypeHandle];
|
||||||
typeEnum = inspector.TypeReferences[elementType.ByValTypeIndex].Type;
|
typeEnum = inspector.TypeReferences[elementType.ByValTypeIndex].Type;
|
||||||
}
|
}
|
||||||
|
|||||||
136
Il2CppInspector.Redux.CLI/CliClient.cs
Normal file
136
Il2CppInspector.Redux.CLI/CliClient.cs
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
using System.Threading.Channels;
|
||||||
|
using Il2CppInspector.Redux.FrontendCore;
|
||||||
|
using Microsoft.AspNetCore.SignalR.Client;
|
||||||
|
using Spectre.Console;
|
||||||
|
|
||||||
|
namespace Il2CppInspector.Redux.CLI;
|
||||||
|
|
||||||
|
public class CliClient : IDisposable
|
||||||
|
{
|
||||||
|
public bool ImportCompleted { get; private set; }
|
||||||
|
|
||||||
|
private volatile int _finishedLoadingCount = 0;
|
||||||
|
|
||||||
|
private readonly HubConnection _connection;
|
||||||
|
private readonly List<IDisposable> _commandListeners = [];
|
||||||
|
|
||||||
|
private Channel<string>? _logMessageChannel;
|
||||||
|
|
||||||
|
public CliClient(HubConnection connection)
|
||||||
|
{
|
||||||
|
_connection = connection;
|
||||||
|
|
||||||
|
_commandListeners.Add(_connection.On<string>(nameof(UiClient.ShowLogMessage), ShowLogMessage));
|
||||||
|
_commandListeners.Add(_connection.On(nameof(UiClient.BeginLoading), BeginLoading));
|
||||||
|
_commandListeners.Add(_connection.On(nameof(UiClient.FinishLoading), FinishLoading));
|
||||||
|
_commandListeners.Add(_connection.On<string>(nameof(UiClient.ShowInfoToast), ShowInfoToast));
|
||||||
|
_commandListeners.Add(_connection.On<string>(nameof(UiClient.ShowSuccessToast), ShowSuccessToast));
|
||||||
|
_commandListeners.Add(_connection.On<string>(nameof(UiClient.ShowErrorToast), ShowErrorToast));
|
||||||
|
_commandListeners.Add(_connection.On(nameof(UiClient.OnImportCompleted), OnImportCompleted));
|
||||||
|
}
|
||||||
|
|
||||||
|
public async ValueTask OnUiLaunched(CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
await _connection.InvokeAsync(nameof(Il2CppHub.OnUiLaunched), cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async ValueTask SubmitInputFiles(List<string> inputFiles, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
await _connection.InvokeAsync(nameof(Il2CppHub.SubmitInputFiles), inputFiles, cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async ValueTask QueueExport(string exportTypeId, string outputDirectory, Dictionary<string, string> settings,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
await _connection.InvokeAsync(nameof(Il2CppHub.QueueExport), exportTypeId, outputDirectory, settings, cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async ValueTask StartExport(CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
await _connection.InvokeAsync(nameof(Il2CppHub.StartExport), cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async ValueTask<List<string>> GetPotentialUnityVersions(CancellationToken cancellationToken = default)
|
||||||
|
=> await _connection.InvokeAsync<List<string>>(nameof(Il2CppHub.GetPotentialUnityVersions), cancellationToken);
|
||||||
|
|
||||||
|
public async ValueTask ExportIl2CppFiles(string outputDirectory, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
await _connection.InvokeAsync(nameof(Il2CppHub.ExportIl2CppFiles), outputDirectory, cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async ValueTask<string> GetInspectorVersion(CancellationToken cancellationToken = default)
|
||||||
|
=> await _connection.InvokeAsync<string>(nameof(Il2CppHub.GetInspectorVersion), cancellationToken);
|
||||||
|
|
||||||
|
public async ValueTask WaitForLoadingToFinishAsync(CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
var currentLoadingCount = _finishedLoadingCount;
|
||||||
|
while (_finishedLoadingCount == currentLoadingCount)
|
||||||
|
await Task.Delay(10, cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ShowLogMessage(string message)
|
||||||
|
{
|
||||||
|
if (_logMessageChannel == null)
|
||||||
|
{
|
||||||
|
AnsiConsole.MarkupLine($"[white bold]{message}[/]");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await _logMessageChannel.Writer.WriteAsync(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void BeginLoading()
|
||||||
|
{
|
||||||
|
_logMessageChannel = Channel.CreateUnbounded<string>(new UnboundedChannelOptions
|
||||||
|
{
|
||||||
|
SingleReader = true,
|
||||||
|
SingleWriter = true,
|
||||||
|
AllowSynchronousContinuations = true
|
||||||
|
});
|
||||||
|
|
||||||
|
AnsiConsole.Status()
|
||||||
|
.Spinner(Spinner.Known.Triangle)
|
||||||
|
.StartAsync("Loading", async ctx =>
|
||||||
|
{
|
||||||
|
await foreach (var newLogMessage in _logMessageChannel.Reader.ReadAllAsync())
|
||||||
|
{
|
||||||
|
ctx.Status(newLogMessage);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void FinishLoading()
|
||||||
|
{
|
||||||
|
_logMessageChannel?.Writer.Complete();
|
||||||
|
Interlocked.Increment(ref _finishedLoadingCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ShowInfoToast(string message)
|
||||||
|
{
|
||||||
|
AnsiConsole.MarkupLineInterpolated($"[bold white]INFO: {message}[/]");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ShowSuccessToast(string message)
|
||||||
|
{
|
||||||
|
AnsiConsole.MarkupLineInterpolated($"[bold][green]SUCCESS: [/] [white]{message}[/][/]");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ShowErrorToast(string message)
|
||||||
|
{
|
||||||
|
AnsiConsole.MarkupLineInterpolated($"[bold][red]ERROR: [/] [white]{message}[/][/]");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnImportCompleted()
|
||||||
|
{
|
||||||
|
ImportCompleted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
|
||||||
|
foreach (var listener in _commandListeners)
|
||||||
|
listener.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
38
Il2CppInspector.Redux.CLI/Commands/BaseCommand.cs
Normal file
38
Il2CppInspector.Redux.CLI/Commands/BaseCommand.cs
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
using Il2CppInspector.Redux.FrontendCore;
|
||||||
|
using Microsoft.AspNetCore.SignalR.Client;
|
||||||
|
using Spectre.Console.Cli;
|
||||||
|
|
||||||
|
namespace Il2CppInspector.Redux.CLI.Commands;
|
||||||
|
|
||||||
|
internal abstract class BaseCommand<T>(PortProvider portProvider) : AsyncCommand<T> where T : CommandSettings
|
||||||
|
{
|
||||||
|
private const string HubPath = "/il2cpp"; // TODO: Make this into a shared constant
|
||||||
|
|
||||||
|
private readonly int _serverPort = portProvider.Port;
|
||||||
|
|
||||||
|
protected abstract Task<int> ExecuteAsync(CliClient client, T settings);
|
||||||
|
|
||||||
|
public override async Task<int> ExecuteAsync(CommandContext context, T settings)
|
||||||
|
{
|
||||||
|
var connection = new HubConnectionBuilder().WithUrl($"http://localhost:{_serverPort}{HubPath}")
|
||||||
|
.AddJsonProtocol(options =>
|
||||||
|
{
|
||||||
|
options.PayloadSerializerOptions.TypeInfoResolverChain.Insert(0,
|
||||||
|
FrontendCoreJsonSerializerContext.Default);
|
||||||
|
})
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
await connection.StartAsync();
|
||||||
|
|
||||||
|
int result;
|
||||||
|
using (var client = new CliClient(connection))
|
||||||
|
{
|
||||||
|
await client.OnUiLaunched();
|
||||||
|
result = await ExecuteAsync(client, settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
await connection.StopAsync();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
17
Il2CppInspector.Redux.CLI/Commands/InteractiveCommand.cs
Normal file
17
Il2CppInspector.Redux.CLI/Commands/InteractiveCommand.cs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
using Microsoft.AspNetCore.SignalR.Client;
|
||||||
|
using Spectre.Console;
|
||||||
|
using Spectre.Console.Cli;
|
||||||
|
|
||||||
|
namespace Il2CppInspector.Redux.CLI.Commands;
|
||||||
|
|
||||||
|
internal class InteractiveCommand(PortProvider portProvider) : BaseCommand<InteractiveCommand.Options>(portProvider)
|
||||||
|
{
|
||||||
|
public class Options : CommandSettings;
|
||||||
|
|
||||||
|
protected override async Task<int> ExecuteAsync(CliClient client, Options settings)
|
||||||
|
{
|
||||||
|
await Task.Delay(1000);
|
||||||
|
await AnsiConsole.AskAsync<string>("meow?");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
21
Il2CppInspector.Redux.CLI/Commands/ManualCommand.cs
Normal file
21
Il2CppInspector.Redux.CLI/Commands/ManualCommand.cs
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
using Spectre.Console;
|
||||||
|
using Spectre.Console.Cli;
|
||||||
|
|
||||||
|
namespace Il2CppInspector.Redux.CLI.Commands;
|
||||||
|
|
||||||
|
internal abstract class ManualCommand<T>(PortProvider portProvider) : BaseCommand<T>(portProvider) where T : ManualCommandOptions
|
||||||
|
{
|
||||||
|
public override ValidationResult Validate(CommandContext context, T settings)
|
||||||
|
{
|
||||||
|
foreach (var inputPath in settings.InputPaths)
|
||||||
|
{
|
||||||
|
if (!Path.Exists(inputPath))
|
||||||
|
return ValidationResult.Error($"Provided input path {inputPath} does not exit.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (File.Exists(settings.OutputPath))
|
||||||
|
return ValidationResult.Error("Provided output path already exists as a file.");
|
||||||
|
|
||||||
|
return ValidationResult.Success();
|
||||||
|
}
|
||||||
|
}
|
||||||
15
Il2CppInspector.Redux.CLI/Commands/ManualCommandOptions.cs
Normal file
15
Il2CppInspector.Redux.CLI/Commands/ManualCommandOptions.cs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
using System.ComponentModel;
|
||||||
|
using Spectre.Console.Cli;
|
||||||
|
|
||||||
|
namespace Il2CppInspector.Redux.CLI.Commands;
|
||||||
|
|
||||||
|
internal class ManualCommandOptions : CommandSettings
|
||||||
|
{
|
||||||
|
[CommandArgument(0, "<InputPath>")]
|
||||||
|
[Description("Paths to the input files. Will be subsequently loaded until binary and metadata were found.")]
|
||||||
|
public string[] InputPaths { get; init; } = [];
|
||||||
|
|
||||||
|
[CommandOption("-o|--output")]
|
||||||
|
[Description("Path to the output folder")]
|
||||||
|
public string OutputPath { get; init; } = "";
|
||||||
|
}
|
||||||
172
Il2CppInspector.Redux.CLI/Commands/ProcessCommand.cs
Normal file
172
Il2CppInspector.Redux.CLI/Commands/ProcessCommand.cs
Normal file
@@ -0,0 +1,172 @@
|
|||||||
|
using Il2CppInspector.Cpp;
|
||||||
|
using Il2CppInspector.Redux.FrontendCore.Outputs;
|
||||||
|
using Spectre.Console;
|
||||||
|
using Spectre.Console.Cli;
|
||||||
|
|
||||||
|
namespace Il2CppInspector.Redux.CLI.Commands;
|
||||||
|
|
||||||
|
internal class ProcessCommand(PortProvider portProvider) : ManualCommand<ProcessCommand.Option>(portProvider)
|
||||||
|
{
|
||||||
|
// NOTE: There might be a better option than replicating all available flags here (and in the TS UI).
|
||||||
|
// Investigate this in the future.
|
||||||
|
|
||||||
|
public class Option : ManualCommandOptions
|
||||||
|
{
|
||||||
|
// C++ Scaffolding
|
||||||
|
[CommandOption("--output-cpp-scaffolding")]
|
||||||
|
public bool CppScaffolding { get; init; } = false;
|
||||||
|
|
||||||
|
[CommandOption("--unity-version")]
|
||||||
|
public string? UnityVersion { get; init; }
|
||||||
|
|
||||||
|
[CommandOption("--compiler-type")]
|
||||||
|
public CppCompilerType CompilerType { get; init; } = CppCompilerType.GCC;
|
||||||
|
|
||||||
|
// C# stub
|
||||||
|
[CommandOption("-s|--output-csharp-stub")]
|
||||||
|
public bool CSharpStubs { get; init; } = false;
|
||||||
|
|
||||||
|
[CommandOption("--layout")]
|
||||||
|
public CSharpLayout Layout { get; init; } = CSharpLayout.SingleFile;
|
||||||
|
|
||||||
|
[CommandOption("--flatten-hierarchy")]
|
||||||
|
public bool FlattenHierarchy { get; init; } = false;
|
||||||
|
|
||||||
|
[CommandOption("--sorting-mode")]
|
||||||
|
public TypeSortingMode SortingMode { get; init; } = TypeSortingMode.Alphabetical;
|
||||||
|
|
||||||
|
[CommandOption("--suppress-metadata")]
|
||||||
|
public bool SuppressMetadata { get; init; } = false;
|
||||||
|
|
||||||
|
[CommandOption("--compilable")]
|
||||||
|
public bool MustCompile { get; init; } = false;
|
||||||
|
|
||||||
|
[CommandOption("--separate-assembly-attributes")]
|
||||||
|
public bool SeparateAssemblyAttributes { get; init; } = true;
|
||||||
|
|
||||||
|
// Disassembler metadata
|
||||||
|
[CommandOption("-m|--output-disassembler-metadata")]
|
||||||
|
public bool DisassemblerMetadata { get; init; } = false;
|
||||||
|
|
||||||
|
[CommandOption("--disassembler")]
|
||||||
|
public DisassemblerType Disassembler { get; init; } = DisassemblerType.IDA;
|
||||||
|
|
||||||
|
// Dummy DLL output
|
||||||
|
[CommandOption("-d|--output-dummy-dlls")]
|
||||||
|
public bool DummyDlls { get; init; } = false;
|
||||||
|
|
||||||
|
// Visual Studio solution
|
||||||
|
[CommandOption("--output-vs-solution")]
|
||||||
|
public bool VsSolution { get; init; } = false;
|
||||||
|
|
||||||
|
[CommandOption("--unity-path")]
|
||||||
|
public string? UnityPath { get; init; }
|
||||||
|
|
||||||
|
[CommandOption("--unity-assemblies-path")]
|
||||||
|
public string? UnityAssembliesPath { get; init; }
|
||||||
|
|
||||||
|
[CommandOption("--extract-il2cpp-files")]
|
||||||
|
public string? ExtractIl2CppFilesPath { get; init; }
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task<int> ExecuteAsync(CliClient client, Option settings)
|
||||||
|
{
|
||||||
|
var inspectorVersion = await client.GetInspectorVersion();
|
||||||
|
AnsiConsole.MarkupLineInterpolated($"Using inspector [gray]{inspectorVersion}[/]");
|
||||||
|
|
||||||
|
await client.SubmitInputFiles(settings.InputPaths.ToList());
|
||||||
|
await client.WaitForLoadingToFinishAsync();
|
||||||
|
if (!client.ImportCompleted)
|
||||||
|
{
|
||||||
|
AnsiConsole.MarkupLine("[bold][red]FAILED[/] to load IL2CPP data from the given inputs.[/]");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (settings.ExtractIl2CppFilesPath != null)
|
||||||
|
{
|
||||||
|
await client.ExportIl2CppFiles(settings.ExtractIl2CppFilesPath);
|
||||||
|
await client.WaitForLoadingToFinishAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
var unityVersions = await client.GetPotentialUnityVersions();
|
||||||
|
|
||||||
|
if (settings.CppScaffolding)
|
||||||
|
{
|
||||||
|
var directory = Path.Join(settings.OutputPath, "cpp");
|
||||||
|
await client.QueueExport(CppScaffoldingOutput.Id, directory, new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
["unityversion"] = settings.UnityVersion ?? unityVersions.First(),
|
||||||
|
["compilertype"] = settings.CompilerType.ToString()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (settings.CSharpStubs)
|
||||||
|
{
|
||||||
|
var directory = Path.Join(settings.OutputPath, "cs");
|
||||||
|
await client.QueueExport(CSharpStubOutput.Id, directory, new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
["layout"] = settings.Layout.ToString(),
|
||||||
|
["flattenhierarchy"] = settings.FlattenHierarchy.ToString(),
|
||||||
|
["sortingmode"] = settings.SortingMode.ToString(),
|
||||||
|
["suppressmetadata"] = settings.SuppressMetadata.ToString(),
|
||||||
|
["mustcompile"] = settings.MustCompile.ToString(),
|
||||||
|
["separateassemblyattributes"] = settings.SeparateAssemblyAttributes.ToString()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (settings.DisassemblerMetadata)
|
||||||
|
{
|
||||||
|
await client.QueueExport(DisassemblerMetadataOutput.Id, settings.OutputPath,
|
||||||
|
new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
["disassembler"] = settings.Disassembler.ToString(),
|
||||||
|
["unityversion"] = settings.UnityVersion ?? unityVersions.First()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (settings.DummyDlls)
|
||||||
|
{
|
||||||
|
var directory = Path.Join(settings.OutputPath, "dll");
|
||||||
|
await client.QueueExport(DummyDllOutput.Id, directory, new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
["suppressmetadata"] = settings.SuppressMetadata.ToString()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (settings.VsSolution)
|
||||||
|
{
|
||||||
|
var directory = Path.Join(settings.OutputPath, "vs");
|
||||||
|
await client.QueueExport(VsSolutionOutput.Id, directory, new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
["unitypath"] = settings.UnityPath ?? "",
|
||||||
|
["unityassembliespath"] = settings.UnityAssembliesPath ?? ""
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
await client.StartExport();
|
||||||
|
await client.WaitForLoadingToFinishAsync();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override ValidationResult Validate(CommandContext context, Option settings)
|
||||||
|
{
|
||||||
|
if (settings.UnityPath != null && !Path.Exists(settings.UnityPath))
|
||||||
|
return ValidationResult.Error($"Provided Unity path {settings.UnityPath} does not exist.");
|
||||||
|
|
||||||
|
if (settings.UnityAssembliesPath != null && !Path.Exists(settings.UnityAssembliesPath))
|
||||||
|
return ValidationResult.Error($"Provided Unity assemblies path {settings.UnityAssembliesPath} does not exist.");
|
||||||
|
|
||||||
|
if (settings.ExtractIl2CppFilesPath != null && File.Exists(settings.ExtractIl2CppFilesPath))
|
||||||
|
return ValidationResult.Error(
|
||||||
|
$"Provided extracted IL2CPP files path {settings.ExtractIl2CppFilesPath} already exists as a file.");
|
||||||
|
|
||||||
|
if (settings is
|
||||||
|
{
|
||||||
|
CppScaffolding: false, CSharpStubs: false, DisassemblerMetadata: false, DummyDlls: false,
|
||||||
|
VsSolution: false
|
||||||
|
})
|
||||||
|
return ValidationResult.Error("At least one output format must be specified.");
|
||||||
|
|
||||||
|
return base.Validate(context, settings);
|
||||||
|
}
|
||||||
|
}
|
||||||
21
Il2CppInspector.Redux.CLI/Il2CppInspector.Redux.CLI.csproj
Normal file
21
Il2CppInspector.Redux.CLI/Il2CppInspector.Redux.CLI.csproj
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<InvariantGlobalization>true</InvariantGlobalization>
|
||||||
|
<PublishAot>false</PublishAot>
|
||||||
|
<PublishSingleFile>true</PublishSingleFile>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="9.0.7" />
|
||||||
|
<PackageReference Include="Spectre.Console.Cli" Version="0.50.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Il2CppInspector.Redux.FrontendCore\Il2CppInspector.Redux.FrontendCore.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
6
Il2CppInspector.Redux.CLI/PortProvider.cs
Normal file
6
Il2CppInspector.Redux.CLI/PortProvider.cs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
namespace Il2CppInspector.Redux.CLI;
|
||||||
|
|
||||||
|
internal sealed class PortProvider(int port)
|
||||||
|
{
|
||||||
|
public int Port => port;
|
||||||
|
}
|
||||||
46
Il2CppInspector.Redux.CLI/Program.cs
Normal file
46
Il2CppInspector.Redux.CLI/Program.cs
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
using Il2CppInspector.Redux.CLI;
|
||||||
|
using Il2CppInspector.Redux.CLI.Commands;
|
||||||
|
using Il2CppInspector.Redux.FrontendCore;
|
||||||
|
using Microsoft.AspNetCore.SignalR;
|
||||||
|
using Spectre.Console.Cli;
|
||||||
|
|
||||||
|
var builder = WebApplication.CreateSlimBuilder();
|
||||||
|
|
||||||
|
builder.Services.ConfigureHttpJsonOptions(options =>
|
||||||
|
{
|
||||||
|
options.SerializerOptions.TypeInfoResolverChain.Insert(0, FrontendCoreJsonSerializerContext.Default);
|
||||||
|
});
|
||||||
|
|
||||||
|
builder.Services.Configure<JsonHubProtocolOptions>(options =>
|
||||||
|
{
|
||||||
|
options.PayloadSerializerOptions.TypeInfoResolverChain.Insert(0, FrontendCoreJsonSerializerContext.Default);
|
||||||
|
});
|
||||||
|
|
||||||
|
builder.Services.AddFrontendCore();
|
||||||
|
builder.Logging.ClearProviders();
|
||||||
|
|
||||||
|
var app = builder.Build();
|
||||||
|
|
||||||
|
app.UseCors();
|
||||||
|
|
||||||
|
app.MapFrontendCore();
|
||||||
|
|
||||||
|
await app.StartAsync();
|
||||||
|
|
||||||
|
var serverUrl = app.Urls.First();
|
||||||
|
var port = new Uri(serverUrl).Port;
|
||||||
|
|
||||||
|
var commandServiceProvider = new ServiceCollection();
|
||||||
|
commandServiceProvider.AddSingleton(new PortProvider(port));
|
||||||
|
|
||||||
|
var commandTypeRegistrar = new ServiceTypeRegistrar(commandServiceProvider);
|
||||||
|
var consoleApp = new CommandApp<InteractiveCommand>(commandTypeRegistrar);
|
||||||
|
|
||||||
|
consoleApp.Configure(config =>
|
||||||
|
{
|
||||||
|
config.AddCommand<ProcessCommand>("process")
|
||||||
|
.WithDescription("Processes the provided input data into one or more output formats.");
|
||||||
|
});
|
||||||
|
|
||||||
|
await consoleApp.RunAsync(args);
|
||||||
|
await app.StopAsync();
|
||||||
23
Il2CppInspector.Redux.CLI/Properties/launchSettings.json
Normal file
23
Il2CppInspector.Redux.CLI/Properties/launchSettings.json
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"profiles": {
|
||||||
|
"WSL": {
|
||||||
|
"commandName": "WSL2",
|
||||||
|
"launchUrl": "http://localhost:5118/",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development",
|
||||||
|
"ASPNETCORE_URLS": "http://localhost:5118"
|
||||||
|
},
|
||||||
|
"distributionName": ""
|
||||||
|
},
|
||||||
|
"http": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"commandLineArgs": "process -d --disassembler ghidra M:\\Downloads\\Reversing\\NotYetAnalyzedAPKs\\pokemon_friends\\libil2cpp.so M:\\Downloads\\Reversing\\NotYetAnalyzedAPKs\\pokemon_friends\\global-metadata.dat",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
},
|
||||||
|
"dotnetRunMessages": true,
|
||||||
|
"applicationUrl": "http://localhost:5118"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"$schema": "https://json.schemastore.org/launchsettings.json"
|
||||||
|
}
|
||||||
30
Il2CppInspector.Redux.CLI/ServiceTypeRegistrar.cs
Normal file
30
Il2CppInspector.Redux.CLI/ServiceTypeRegistrar.cs
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
using Spectre.Console.Cli;
|
||||||
|
|
||||||
|
namespace Il2CppInspector.Redux.CLI;
|
||||||
|
|
||||||
|
public class ServiceTypeRegistrar(IServiceCollection serviceCollection) : ITypeRegistrar
|
||||||
|
{
|
||||||
|
private readonly IServiceCollection _serviceCollection = serviceCollection;
|
||||||
|
private ServiceTypeResolver? _resolver;
|
||||||
|
|
||||||
|
public void Register(Type service, Type implementation)
|
||||||
|
{
|
||||||
|
_serviceCollection.AddSingleton(service, implementation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RegisterInstance(Type service, object implementation)
|
||||||
|
{
|
||||||
|
_serviceCollection.AddSingleton(service, implementation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RegisterLazy(Type service, Func<object> factory)
|
||||||
|
{
|
||||||
|
_serviceCollection.AddSingleton(service, _ => factory());
|
||||||
|
}
|
||||||
|
|
||||||
|
public ITypeResolver Build()
|
||||||
|
{
|
||||||
|
_resolver ??= new ServiceTypeResolver(_serviceCollection.BuildServiceProvider());
|
||||||
|
return _resolver;
|
||||||
|
}
|
||||||
|
}
|
||||||
13
Il2CppInspector.Redux.CLI/ServiceTypeResolver.cs
Normal file
13
Il2CppInspector.Redux.CLI/ServiceTypeResolver.cs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
using Spectre.Console.Cli;
|
||||||
|
|
||||||
|
namespace Il2CppInspector.Redux.CLI;
|
||||||
|
|
||||||
|
public class ServiceTypeResolver(IServiceProvider serviceProvider) : ITypeResolver
|
||||||
|
{
|
||||||
|
private readonly IServiceProvider _serviceProvider = serviceProvider;
|
||||||
|
|
||||||
|
public object? Resolve(Type? type)
|
||||||
|
=> type == null
|
||||||
|
? null
|
||||||
|
: _serviceProvider.GetService(type);
|
||||||
|
}
|
||||||
8
Il2CppInspector.Redux.CLI/appsettings.Development.json
Normal file
8
Il2CppInspector.Redux.CLI/appsettings.Development.json
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft.AspNetCore": "Warning"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
9
Il2CppInspector.Redux.CLI/appsettings.json
Normal file
9
Il2CppInspector.Redux.CLI/appsettings.json
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft.AspNetCore": "Warning"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"AllowedHosts": "*"
|
||||||
|
}
|
||||||
54
Il2CppInspector.Redux.FrontendCore/Extensions.cs
Normal file
54
Il2CppInspector.Redux.FrontendCore/Extensions.cs
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace Il2CppInspector.Redux.FrontendCore;
|
||||||
|
|
||||||
|
public static class Extensions
|
||||||
|
{
|
||||||
|
internal static bool GetAsBooleanOrDefault(this Dictionary<string, string> dict, string key, bool defaultValue)
|
||||||
|
{
|
||||||
|
if (dict.TryGetValue(key, out var value) && bool.TryParse(value, out var boolResult))
|
||||||
|
return boolResult;
|
||||||
|
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static T GetAsEnumOrDefault<T>(this Dictionary<string, string> dict, string key, T defaultValue)
|
||||||
|
where T : struct, Enum
|
||||||
|
{
|
||||||
|
if (dict.TryGetValue(key, out var value) && Enum.TryParse<T>(value, true, out var enumResult))
|
||||||
|
return enumResult;
|
||||||
|
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static string? GetAssemblyVersion(this Assembly assembly)
|
||||||
|
=> assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion;
|
||||||
|
|
||||||
|
public static WebApplication MapFrontendCore(this WebApplication app)
|
||||||
|
{
|
||||||
|
app.MapHub<Il2CppHub>("/il2cpp");
|
||||||
|
return app;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IServiceCollection AddFrontendCore(this IServiceCollection services)
|
||||||
|
{
|
||||||
|
services.AddSignalR(config =>
|
||||||
|
{
|
||||||
|
#if DEBUG
|
||||||
|
config.EnableDetailedErrors = true;
|
||||||
|
#endif
|
||||||
|
});
|
||||||
|
|
||||||
|
return services.AddCors(options =>
|
||||||
|
{
|
||||||
|
options.AddDefaultPolicy(policy =>
|
||||||
|
{
|
||||||
|
policy.SetIsOriginAllowed(origin =>
|
||||||
|
origin.StartsWith("http://localhost") || origin.StartsWith("http://tauri.localhost"))
|
||||||
|
.AllowAnyHeader()
|
||||||
|
.WithMethods("GET", "POST")
|
||||||
|
.AllowCredentials();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Il2CppInspector.Redux.FrontendCore;
|
||||||
|
|
||||||
|
[JsonSerializable(typeof(string))]
|
||||||
|
[JsonSerializable(typeof(List<string>))]
|
||||||
|
[JsonSerializable(typeof(Dictionary<string, string>))]
|
||||||
|
public partial class FrontendCoreJsonSerializerContext : JsonSerializerContext;
|
||||||
58
Il2CppInspector.Redux.FrontendCore/Il2CppHub.cs
Normal file
58
Il2CppInspector.Redux.FrontendCore/Il2CppHub.cs
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
using Microsoft.AspNetCore.SignalR;
|
||||||
|
|
||||||
|
namespace Il2CppInspector.Redux.FrontendCore;
|
||||||
|
|
||||||
|
public class Il2CppHub : Hub
|
||||||
|
{
|
||||||
|
private const string ContextKey = "context";
|
||||||
|
|
||||||
|
private UiContext State
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (!Context.Items.TryGetValue(ContextKey, out var context)
|
||||||
|
|| context is not UiContext ctx)
|
||||||
|
{
|
||||||
|
Context.Items[ContextKey] = ctx = new UiContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private UiClient Client => new(Clients.Caller);
|
||||||
|
|
||||||
|
public async Task OnUiLaunched()
|
||||||
|
{
|
||||||
|
await State.Initialize(Client);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SubmitInputFiles(List<string> inputFiles)
|
||||||
|
{
|
||||||
|
await State.LoadInputFiles(Client, inputFiles);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task QueueExport(string exportTypeId, string outputDirectory, Dictionary<string, string> settings)
|
||||||
|
{
|
||||||
|
await State.QueueExport(Client, exportTypeId, outputDirectory, settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task StartExport()
|
||||||
|
{
|
||||||
|
await State.StartExport(Client);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<string>> GetPotentialUnityVersions()
|
||||||
|
{
|
||||||
|
return await State.GetPotentialUnityVersions();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task ExportIl2CppFiles(string outputDirectory)
|
||||||
|
{
|
||||||
|
await State.ExportIl2CppFiles(Client, outputDirectory);
|
||||||
|
}
|
||||||
|
public async Task<string> GetInspectorVersion()
|
||||||
|
{
|
||||||
|
return await UiContext.GetInspectorVersion();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<InvariantGlobalization>true</InvariantGlobalization>
|
||||||
|
<OutputType>Library</OutputType>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Bin2Object\Bin2Object\Bin2Object.csproj" />
|
||||||
|
<ProjectReference Include="..\Il2CppInspector.Common\Il2CppInspector.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
23
Il2CppInspector.Redux.FrontendCore/LoadingSession.cs
Normal file
23
Il2CppInspector.Redux.FrontendCore/LoadingSession.cs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
namespace Il2CppInspector.Redux.FrontendCore;
|
||||||
|
|
||||||
|
public class LoadingSession : IAsyncDisposable
|
||||||
|
{
|
||||||
|
private readonly UiClient _client;
|
||||||
|
|
||||||
|
private LoadingSession(UiClient client)
|
||||||
|
{
|
||||||
|
_client = client;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<LoadingSession> Start(UiClient client)
|
||||||
|
{
|
||||||
|
await client.BeginLoading();
|
||||||
|
return new LoadingSession(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async ValueTask DisposeAsync()
|
||||||
|
{
|
||||||
|
await _client.FinishLoading();
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
10
Il2CppInspector.Redux.FrontendCore/Outputs/CSharpLayout.cs
Normal file
10
Il2CppInspector.Redux.FrontendCore/Outputs/CSharpLayout.cs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
namespace Il2CppInspector.Redux.FrontendCore.Outputs;
|
||||||
|
|
||||||
|
public enum CSharpLayout
|
||||||
|
{
|
||||||
|
SingleFile,
|
||||||
|
Namespace,
|
||||||
|
Assembly,
|
||||||
|
Class,
|
||||||
|
Tree
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
using Il2CppInspector.Model;
|
||||||
|
using Il2CppInspector.Outputs;
|
||||||
|
|
||||||
|
namespace Il2CppInspector.Redux.FrontendCore.Outputs;
|
||||||
|
|
||||||
|
public class CSharpStubOutput : IOutputFormatProvider
|
||||||
|
{
|
||||||
|
public static string Id => "cs";
|
||||||
|
|
||||||
|
private class Settings(Dictionary<string, string> settings)
|
||||||
|
{
|
||||||
|
public readonly CSharpLayout Layout = settings.GetAsEnumOrDefault("layout", CSharpLayout.SingleFile);
|
||||||
|
public readonly bool FlattenHierarchy = settings.GetAsBooleanOrDefault("flattenhierarchy", false);
|
||||||
|
public readonly TypeSortingMode SortingMode = settings.GetAsEnumOrDefault("sortingmode", TypeSortingMode.Alphabetical);
|
||||||
|
public readonly bool SuppressMetadata = settings.GetAsBooleanOrDefault("suppressmetadata", false);
|
||||||
|
public readonly bool MustCompile = settings.GetAsBooleanOrDefault("mustcompile", false);
|
||||||
|
public readonly bool SeperateAssemblyAttributes = settings.GetAsBooleanOrDefault("seperateassemblyattributes", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Export(AppModel model, UiClient client, string outputPath, Dictionary<string, string> settingsDict)
|
||||||
|
{
|
||||||
|
var settings = new Settings(settingsDict);
|
||||||
|
|
||||||
|
var writer = new CSharpCodeStubs(model.TypeModel)
|
||||||
|
{
|
||||||
|
SuppressMetadata = settings.SuppressMetadata,
|
||||||
|
MustCompile = settings.MustCompile
|
||||||
|
};
|
||||||
|
|
||||||
|
await client.ShowLogMessage("Writing C# type definitions");
|
||||||
|
|
||||||
|
var outputPathFile = Path.Join(outputPath, "il2cpp.cs");
|
||||||
|
|
||||||
|
switch (settings.Layout, settings.SortingMode)
|
||||||
|
{
|
||||||
|
case (CSharpLayout.SingleFile, TypeSortingMode.TypeDefinitionIndex):
|
||||||
|
writer.WriteSingleFile(outputPathFile, info => info.Index);
|
||||||
|
break;
|
||||||
|
case (CSharpLayout.SingleFile, TypeSortingMode.Alphabetical):
|
||||||
|
writer.WriteSingleFile(outputPathFile, info => info.Name);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case (CSharpLayout.Namespace, TypeSortingMode.TypeDefinitionIndex):
|
||||||
|
writer.WriteFilesByNamespace(outputPath, info => info.Index, settings.FlattenHierarchy);
|
||||||
|
break;
|
||||||
|
case (CSharpLayout.Namespace, TypeSortingMode.Alphabetical):
|
||||||
|
writer.WriteFilesByNamespace(outputPath, info => info.Name, settings.FlattenHierarchy);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case (CSharpLayout.Assembly, TypeSortingMode.TypeDefinitionIndex):
|
||||||
|
writer.WriteFilesByAssembly(outputPath, info => info.Index, settings.SeperateAssemblyAttributes);
|
||||||
|
break;
|
||||||
|
case (CSharpLayout.Assembly, TypeSortingMode.Alphabetical):
|
||||||
|
writer.WriteFilesByAssembly(outputPath, info => info.Name, settings.SeperateAssemblyAttributes);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case (CSharpLayout.Class, _):
|
||||||
|
writer.WriteFilesByClass(outputPath, settings.FlattenHierarchy);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case (CSharpLayout.Tree, _):
|
||||||
|
writer.WriteFilesByClassTree(outputPath, settings.SeperateAssemblyAttributes);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
using Il2CppInspector.Cpp;
|
||||||
|
using Il2CppInspector.Cpp.UnityHeaders;
|
||||||
|
using Il2CppInspector.Model;
|
||||||
|
using Il2CppInspector.Outputs;
|
||||||
|
|
||||||
|
namespace Il2CppInspector.Redux.FrontendCore.Outputs;
|
||||||
|
|
||||||
|
public class CppScaffoldingOutput : IOutputFormatProvider
|
||||||
|
{
|
||||||
|
public static string Id => "cppscaffolding";
|
||||||
|
|
||||||
|
private class Settings(Dictionary<string, string> settings)
|
||||||
|
{
|
||||||
|
public readonly string UnityVersion = settings.GetValueOrDefault("unityversion", "");
|
||||||
|
public readonly CppCompilerType Compiler = settings.GetAsEnumOrDefault("compiler", CppCompilerType.GCC);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Export(AppModel model, UiClient client, string outputPath, Dictionary<string, string> settingsDict)
|
||||||
|
{
|
||||||
|
var settings = new Settings(settingsDict);
|
||||||
|
|
||||||
|
await client.ShowLogMessage($"Building application model for Unity {settings.UnityVersion}/{settings.Compiler}");
|
||||||
|
model.Build(new UnityVersion(settings.UnityVersion), settings.Compiler);
|
||||||
|
|
||||||
|
await client.ShowLogMessage("Generating C++ scaffolding");
|
||||||
|
var scaffolding = new CppScaffolding(model);
|
||||||
|
|
||||||
|
await client.ShowLogMessage("Writing C++ scaffolding");
|
||||||
|
scaffolding.Write(outputPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
using Il2CppInspector.Cpp;
|
||||||
|
using Il2CppInspector.Cpp.UnityHeaders;
|
||||||
|
using Il2CppInspector.Model;
|
||||||
|
using Il2CppInspector.Outputs;
|
||||||
|
|
||||||
|
namespace Il2CppInspector.Redux.FrontendCore.Outputs;
|
||||||
|
|
||||||
|
public class DisassemblerMetadataOutput : IOutputFormatProvider
|
||||||
|
{
|
||||||
|
public static string Id => "disassemblermetadata";
|
||||||
|
|
||||||
|
private class Settings(Dictionary<string, string> dict)
|
||||||
|
{
|
||||||
|
public readonly DisassemblerType Disassembler = dict.GetAsEnumOrDefault("disassembler", DisassemblerType.IDA);
|
||||||
|
public readonly string UnityVersion = dict.GetValueOrDefault("unityversion", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Export(AppModel model, UiClient client, string outputPath, Dictionary<string, string> settingsDict)
|
||||||
|
{
|
||||||
|
var settings = new Settings(settingsDict);
|
||||||
|
|
||||||
|
await client.ShowLogMessage($"Building application model for Unity {settings.UnityVersion}/{CppCompilerType.GCC}");
|
||||||
|
model.Build(new UnityVersion(settings.UnityVersion), CppCompilerType.GCC);
|
||||||
|
|
||||||
|
var headerPath = Path.Join(outputPath, "il2cpp.h");
|
||||||
|
{
|
||||||
|
await client.ShowLogMessage("Generating C++ types");
|
||||||
|
var cppScaffolding = new CppScaffolding(model, useBetterArraySize: true);
|
||||||
|
|
||||||
|
await client.ShowLogMessage("Writing C++ types");
|
||||||
|
cppScaffolding.WriteTypes(headerPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
var metadataPath = Path.Join(outputPath, "il2cpp.json");
|
||||||
|
{
|
||||||
|
await client.ShowLogMessage("Generating disassembler metadata");
|
||||||
|
var jsonMetadata = new JSONMetadata(model);
|
||||||
|
|
||||||
|
await client.ShowLogMessage("Writing disassembler metadata");
|
||||||
|
jsonMetadata.Write(metadataPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (settings.Disassembler != DisassemblerType.None)
|
||||||
|
{
|
||||||
|
var scriptPath = Path.Join(outputPath, "il2cpp.py");
|
||||||
|
await client.ShowLogMessage($"Generating python script for {settings.Disassembler}");
|
||||||
|
var script = new PythonScript(model);
|
||||||
|
|
||||||
|
await client.ShowLogMessage($"Writing python script for {settings.Disassembler}");
|
||||||
|
script.WriteScriptToFile(scriptPath, settings.Disassembler.ToString(), headerPath, metadataPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
namespace Il2CppInspector.Redux.FrontendCore.Outputs;
|
||||||
|
|
||||||
|
public enum DisassemblerType
|
||||||
|
{
|
||||||
|
IDA,
|
||||||
|
Ghidra,
|
||||||
|
BinaryNinja,
|
||||||
|
None
|
||||||
|
}
|
||||||
27
Il2CppInspector.Redux.FrontendCore/Outputs/DummyDllOutput.cs
Normal file
27
Il2CppInspector.Redux.FrontendCore/Outputs/DummyDllOutput.cs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
using Il2CppInspector.Model;
|
||||||
|
using Il2CppInspector.Outputs;
|
||||||
|
|
||||||
|
namespace Il2CppInspector.Redux.FrontendCore.Outputs;
|
||||||
|
|
||||||
|
public class DummyDllOutput : IOutputFormatProvider
|
||||||
|
{
|
||||||
|
public static string Id => "dummydlls";
|
||||||
|
|
||||||
|
private class Settings(Dictionary<string, string> dict)
|
||||||
|
{
|
||||||
|
public readonly bool SuppressMetadata = dict.GetAsBooleanOrDefault("suppressmetadata", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Export(AppModel model, UiClient client, string outputPath, Dictionary<string, string> settingsDict)
|
||||||
|
{
|
||||||
|
var outputSettings = new Settings(settingsDict);
|
||||||
|
|
||||||
|
await client.ShowLogMessage("Generating .NET dummy assemblies");
|
||||||
|
var shims = new AssemblyShims(model.TypeModel)
|
||||||
|
{
|
||||||
|
SuppressMetadata = outputSettings.SuppressMetadata
|
||||||
|
};
|
||||||
|
|
||||||
|
shims.Write(outputPath, client.EventHandler);
|
||||||
|
}
|
||||||
|
}
|
||||||
14
Il2CppInspector.Redux.FrontendCore/Outputs/IOutputFormat.cs
Normal file
14
Il2CppInspector.Redux.FrontendCore/Outputs/IOutputFormat.cs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
using Il2CppInspector.Model;
|
||||||
|
|
||||||
|
namespace Il2CppInspector.Redux.FrontendCore.Outputs;
|
||||||
|
|
||||||
|
public interface IOutputFormat
|
||||||
|
{
|
||||||
|
public Task Export(AppModel model, UiClient client, string outputPath,
|
||||||
|
Dictionary<string, string> settingsDict);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IOutputFormatProvider : IOutputFormat
|
||||||
|
{
|
||||||
|
public static abstract string Id { get; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
namespace Il2CppInspector.Redux.FrontendCore.Outputs;
|
||||||
|
|
||||||
|
public static class OutputFormatRegistry
|
||||||
|
{
|
||||||
|
public static IEnumerable<string> AvailableOutputFormats => OutputFormats.Keys;
|
||||||
|
|
||||||
|
private static readonly Dictionary<string, IOutputFormat> OutputFormats = [];
|
||||||
|
|
||||||
|
public static void RegisterOutputFormat<T>() where T : IOutputFormatProvider, new()
|
||||||
|
{
|
||||||
|
if (OutputFormats.ContainsKey(T.Id))
|
||||||
|
throw new InvalidOperationException("An output format with this id was already registered.");
|
||||||
|
|
||||||
|
OutputFormats[T.Id] = new T();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IOutputFormat GetOutputFormat(string id)
|
||||||
|
{
|
||||||
|
if (!OutputFormats.TryGetValue(id, out var format))
|
||||||
|
throw new ArgumentException($"Failed to find output format for id {id}", nameof(id));
|
||||||
|
|
||||||
|
return format;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void RegisterBuiltinOutputFormats()
|
||||||
|
{
|
||||||
|
RegisterOutputFormat<CSharpStubOutput>();
|
||||||
|
RegisterOutputFormat<VsSolutionOutput>();
|
||||||
|
RegisterOutputFormat<DummyDllOutput>();
|
||||||
|
RegisterOutputFormat<DisassemblerMetadataOutput>();
|
||||||
|
RegisterOutputFormat<CppScaffoldingOutput>();
|
||||||
|
}
|
||||||
|
|
||||||
|
static OutputFormatRegistry()
|
||||||
|
{
|
||||||
|
RegisterBuiltinOutputFormats();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
namespace Il2CppInspector.Redux.FrontendCore.Outputs;
|
||||||
|
|
||||||
|
public enum TypeSortingMode
|
||||||
|
{
|
||||||
|
Alphabetical,
|
||||||
|
TypeDefinitionIndex
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
using Il2CppInspector.Model;
|
||||||
|
using Il2CppInspector.Outputs;
|
||||||
|
|
||||||
|
namespace Il2CppInspector.Redux.FrontendCore.Outputs;
|
||||||
|
|
||||||
|
public class VsSolutionOutput : IOutputFormatProvider
|
||||||
|
{
|
||||||
|
public static string Id => "vssolution";
|
||||||
|
|
||||||
|
private class Settings(Dictionary<string, string> settings)
|
||||||
|
{
|
||||||
|
public readonly string UnityPath = settings.GetValueOrDefault("unitypath", "");
|
||||||
|
public readonly string UnityAssembliesPath = settings.GetValueOrDefault("unityassembliespath", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Export(AppModel model, UiClient client, string outputPath, Dictionary<string, string> settingsDict)
|
||||||
|
{
|
||||||
|
var settings = new Settings(settingsDict);
|
||||||
|
|
||||||
|
var writer = new CSharpCodeStubs(model.TypeModel)
|
||||||
|
{
|
||||||
|
MustCompile = true,
|
||||||
|
SuppressMetadata = true
|
||||||
|
};
|
||||||
|
|
||||||
|
await client.ShowLogMessage("Writing Visual Studio solution");
|
||||||
|
writer.WriteSolution(outputPath, settings.UnityPath, settings.UnityAssembliesPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
54
Il2CppInspector.Redux.FrontendCore/PathHeuristics.cs
Normal file
54
Il2CppInspector.Redux.FrontendCore/PathHeuristics.cs
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
namespace Il2CppInspector.Redux.FrontendCore;
|
||||||
|
|
||||||
|
public static class PathHeuristics
|
||||||
|
{
|
||||||
|
private static readonly string[] AllowedMetadataExtensionComponents =
|
||||||
|
[
|
||||||
|
"dat", "dec"
|
||||||
|
];
|
||||||
|
|
||||||
|
private static readonly string[] AllowedMetadataNameComponents =
|
||||||
|
[
|
||||||
|
"metadata"
|
||||||
|
];
|
||||||
|
|
||||||
|
private static readonly string[] AllowedBinaryPathComponents =
|
||||||
|
[
|
||||||
|
"GameAssembly",
|
||||||
|
"il2cpp",
|
||||||
|
"UnityFramework"
|
||||||
|
];
|
||||||
|
|
||||||
|
private static readonly string[] AllowedBinaryExtensionComponents =
|
||||||
|
[
|
||||||
|
"dll", "so", "exe", "bin", "prx", "sprx", "dylib"
|
||||||
|
];
|
||||||
|
|
||||||
|
public static bool IsMetadataPath(string path)
|
||||||
|
{
|
||||||
|
var extension = Path.GetExtension(path);
|
||||||
|
if (AllowedMetadataExtensionComponents.Any(extension.Contains))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
var filename = Path.GetFileNameWithoutExtension(path);
|
||||||
|
if (AllowedMetadataNameComponents.Any(filename.Contains))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsBinaryPath(string path)
|
||||||
|
{
|
||||||
|
var extension = Path.GetExtension(path);
|
||||||
|
|
||||||
|
// empty to allow macho binaries which do not have an extension
|
||||||
|
if (extension == "" || AllowedBinaryExtensionComponents.Any(extension.Contains))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
var filename = Path.GetFileNameWithoutExtension(path);
|
||||||
|
if (AllowedBinaryPathComponents.Any(filename.Contains))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"profiles": {
|
||||||
|
"Il2CppInspector.Redux.FrontendCore": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"launchBrowser": false,
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
},
|
||||||
|
"applicationUrl": "https://localhost:43298;http://localhost:43299"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
44
Il2CppInspector.Redux.FrontendCore/UiClient.cs
Normal file
44
Il2CppInspector.Redux.FrontendCore/UiClient.cs
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
using Microsoft.AspNetCore.SignalR;
|
||||||
|
|
||||||
|
namespace Il2CppInspector.Redux.FrontendCore;
|
||||||
|
|
||||||
|
public class UiClient(ISingleClientProxy client)
|
||||||
|
{
|
||||||
|
private EventHandler<string>? _handler;
|
||||||
|
|
||||||
|
public EventHandler<string> EventHandler
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
_handler ??= (_, status) =>
|
||||||
|
{
|
||||||
|
#pragma warning disable CS4014
|
||||||
|
ShowLogMessage(status);
|
||||||
|
#pragma warning restore CS4014
|
||||||
|
};
|
||||||
|
|
||||||
|
return _handler;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task ShowLogMessage(string message, CancellationToken cancellationToken = default)
|
||||||
|
=> await client.SendAsync(nameof(ShowLogMessage), message, cancellationToken);
|
||||||
|
|
||||||
|
public async Task BeginLoading(CancellationToken cancellationToken = default)
|
||||||
|
=> await client.SendAsync(nameof(BeginLoading), cancellationToken);
|
||||||
|
|
||||||
|
public async Task FinishLoading(CancellationToken cancellationToken = default)
|
||||||
|
=> await client.SendAsync(nameof(FinishLoading), cancellationToken);
|
||||||
|
|
||||||
|
public async Task ShowInfoToast(string message, CancellationToken cancellationToken = default)
|
||||||
|
=> await client.SendAsync(nameof(ShowInfoToast), message, cancellationToken);
|
||||||
|
|
||||||
|
public async Task ShowSuccessToast(string message, CancellationToken cancellationToken = default)
|
||||||
|
=> await client.SendAsync(nameof(ShowSuccessToast), message, cancellationToken);
|
||||||
|
|
||||||
|
public async Task ShowErrorToast(string message, CancellationToken cancellationToken = default)
|
||||||
|
=> await client.SendAsync(nameof(ShowErrorToast), message, cancellationToken);
|
||||||
|
|
||||||
|
public async Task OnImportCompleted(CancellationToken cancellationToken = default)
|
||||||
|
=> await client.SendAsync(nameof(OnImportCompleted), cancellationToken);
|
||||||
|
}
|
||||||
251
Il2CppInspector.Redux.FrontendCore/UiContext.cs
Normal file
251
Il2CppInspector.Redux.FrontendCore/UiContext.cs
Normal file
@@ -0,0 +1,251 @@
|
|||||||
|
using System.Diagnostics;
|
||||||
|
using Il2CppInspector.Cpp.UnityHeaders;
|
||||||
|
using Il2CppInspector.Model;
|
||||||
|
using Il2CppInspector.Redux.FrontendCore.Outputs;
|
||||||
|
using Il2CppInspector.Reflection;
|
||||||
|
using Inspector = Il2CppInspector.Il2CppInspector;
|
||||||
|
|
||||||
|
namespace Il2CppInspector.Redux.FrontendCore;
|
||||||
|
|
||||||
|
public class UiContext
|
||||||
|
{
|
||||||
|
private const string BugReportSuffix =
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
If you believe this is a bug in Il2CppInspectorRedux, please use the CLI version to generate the complete output and paste it when filing a bug report.
|
||||||
|
Do not send a screenshot of this error!
|
||||||
|
""";
|
||||||
|
|
||||||
|
private Metadata? _metadata;
|
||||||
|
private IFileFormatStream? _binary;
|
||||||
|
private readonly List<AppModel> _appModels = [];
|
||||||
|
private readonly List<UnityHeaders> _potentialUnityVersions = [];
|
||||||
|
|
||||||
|
private readonly LoadOptions _loadOptions = new();
|
||||||
|
|
||||||
|
private readonly List<(string FormatId, string OutputDirectory, Dictionary<string, string> Settings)> _queuedExports = [];
|
||||||
|
|
||||||
|
private async Task<bool> TryLoadMetadataFromStream(UiClient client, MemoryStream stream)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_metadata = Metadata.FromStream(stream, client.EventHandler);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
await client.ShowErrorToast($"{e.Message}{BugReportSuffix}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<bool> TryLoadBinaryFromStream(UiClient client, MemoryStream stream)
|
||||||
|
{
|
||||||
|
await client.ShowLogMessage("Processing binary");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var file = FileFormatStream.Load(stream, _loadOptions, client.EventHandler)
|
||||||
|
?? throw new InvalidOperationException("Failed to determine binary file format.");
|
||||||
|
|
||||||
|
if (file.NumImages == 0)
|
||||||
|
throw new InvalidOperationException("Failed to find any binary images in the file");
|
||||||
|
|
||||||
|
_binary = file;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
await client.ShowErrorToast($"{e.Message}{BugReportSuffix}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<bool> TryInitializeInspector(UiClient client)
|
||||||
|
{
|
||||||
|
Debug.Assert(_binary != null);
|
||||||
|
Debug.Assert(_metadata != null);
|
||||||
|
|
||||||
|
_appModels.Clear();
|
||||||
|
|
||||||
|
var inspectors = Inspector.LoadFromStream(_binary, _metadata, client.EventHandler);
|
||||||
|
|
||||||
|
if (inspectors.Count == 0)
|
||||||
|
{
|
||||||
|
await client.ShowErrorToast(
|
||||||
|
"""
|
||||||
|
Failed to auto-detect any IL2CPP binary images in the provided files.
|
||||||
|
This may mean the binary file is packed, encrypted or obfuscated, that the file
|
||||||
|
is not an IL2CPP image or that Il2CppInspector was not able to automatically find the required data.
|
||||||
|
Please check the binary file in a disassembler to ensure that it is an unencrypted IL2CPP binary before submitting a bug report!
|
||||||
|
""");
|
||||||
|
|
||||||
|
_binary = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var inspector in inspectors)
|
||||||
|
{
|
||||||
|
await client.ShowLogMessage(
|
||||||
|
$"Building .NET type model for {inspector.BinaryImage.Format}/{inspector.BinaryImage.Arch} image");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var typeModel = new TypeModel(inspector);
|
||||||
|
|
||||||
|
// Just create the app model, do not initialize it - this is done lazily depending on the exports
|
||||||
|
_appModels.Add(new AppModel(typeModel, makeDefaultBuild: false));
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
await client.ShowErrorToast($"Failed to build type model: {e.Message}{BugReportSuffix}");
|
||||||
|
|
||||||
|
// Clear out failed metadata and binary so subsequent loads do not use any stale data.
|
||||||
|
_metadata = null;
|
||||||
|
_binary = null;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_potentialUnityVersions.Clear();
|
||||||
|
_potentialUnityVersions.AddRange(UnityHeaders.GuessHeadersForBinary(_appModels[0].Package.Binary));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Initialize(UiClient client, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
await client.ShowSuccessToast("SignalR initialized!", cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task LoadInputFiles(UiClient client, List<string> inputFiles,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
await using (await LoadingSession.Start(client))
|
||||||
|
{
|
||||||
|
var streams = Inspector.GetStreamsFromPackage(inputFiles);
|
||||||
|
if (streams != null)
|
||||||
|
{
|
||||||
|
// The input files contained a package that provides the metadata and binary.
|
||||||
|
// Use these instead of parsing all files individually.
|
||||||
|
if (!await TryLoadMetadataFromStream(client, streams.Value.Metadata))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!await TryLoadBinaryFromStream(client, streams.Value.Binary))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach (var inputFile in inputFiles)
|
||||||
|
{
|
||||||
|
if (_metadata != null && _binary != null)
|
||||||
|
break;
|
||||||
|
|
||||||
|
await client.ShowLogMessage($"Processing {inputFile}", cancellationToken);
|
||||||
|
|
||||||
|
var data = await File.ReadAllBytesAsync(inputFile, cancellationToken);
|
||||||
|
var stream = new MemoryStream(data);
|
||||||
|
|
||||||
|
if ( _metadata == null && PathHeuristics.IsMetadataPath(inputFile))
|
||||||
|
{
|
||||||
|
if (await TryLoadMetadataFromStream(client, stream))
|
||||||
|
{
|
||||||
|
await client.ShowSuccessToast($"Loaded metadata (v{_metadata!.Version}) from {inputFile}", cancellationToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (_binary == null && PathHeuristics.IsBinaryPath(inputFile))
|
||||||
|
{
|
||||||
|
stream.Position = 0;
|
||||||
|
_loadOptions.BinaryFilePath = inputFile;
|
||||||
|
|
||||||
|
if (await TryLoadBinaryFromStream(client, stream))
|
||||||
|
{
|
||||||
|
await client.ShowSuccessToast($"Loaded binary from {inputFile}", cancellationToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_metadata != null && _binary != null)
|
||||||
|
{
|
||||||
|
if (await TryInitializeInspector(client))
|
||||||
|
{
|
||||||
|
await client.ShowSuccessToast($"Successfully loaded IL2CPP (v{_appModels[0].Package.Version}) data!", cancellationToken);
|
||||||
|
await client.OnImportCompleted(cancellationToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task QueueExport(UiClient client, string exportFormatId, string outputDirectory,
|
||||||
|
Dictionary<string, string> settings, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
_queuedExports.Add((exportFormatId, outputDirectory, settings));
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task StartExport(UiClient client, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
// todo: support different app model selection (when loading packages)
|
||||||
|
Debug.Assert(_appModels.Count > 0);
|
||||||
|
|
||||||
|
await using (await LoadingSession.Start(client))
|
||||||
|
{
|
||||||
|
var model = _appModels[0];
|
||||||
|
|
||||||
|
foreach (var (formatId, outputDirectory, settings) in _queuedExports)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var outputFormat = OutputFormatRegistry.GetOutputFormat(formatId);
|
||||||
|
await outputFormat.Export(model, client, outputDirectory, settings);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await client.ShowErrorToast($"Export for format {formatId} failed: {ex}",
|
||||||
|
cancellationToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_queuedExports.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
await client.ShowSuccessToast("Export finished", cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<List<string>> GetPotentialUnityVersions()
|
||||||
|
{
|
||||||
|
return Task.FromResult(_potentialUnityVersions.Select(x => x.VersionRange.Min.ToString()).ToList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task ExportIl2CppFiles(UiClient client, string outputDirectory, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
Debug.Assert(_appModels.Count > 0);
|
||||||
|
var pkg = _appModels[0].Package;
|
||||||
|
|
||||||
|
await using (await LoadingSession.Start(client))
|
||||||
|
{
|
||||||
|
await Task.Run(async () =>
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(outputDirectory);
|
||||||
|
|
||||||
|
await client.ShowLogMessage("Extracting IL2CPP binary", cancellationToken);
|
||||||
|
pkg.SaveBinaryToFile(Path.Join(outputDirectory, pkg.BinaryImage.DefaultFilename));
|
||||||
|
|
||||||
|
await client.ShowLogMessage("Extracting IL2CPP metadata", cancellationToken);
|
||||||
|
pkg.SaveMetadataToFile(Path.Join(outputDirectory, "global-metadata.dat"));
|
||||||
|
|
||||||
|
await client.ShowSuccessToast("Successfully extracted IL2CPP files.", cancellationToken);
|
||||||
|
}, cancellationToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Task<string> GetInspectorVersion()
|
||||||
|
{
|
||||||
|
return Task.FromResult(typeof(UiContext).Assembly.GetAssemblyVersion() ?? "<unknown>");
|
||||||
|
}
|
||||||
|
}
|
||||||
10
Il2CppInspector.Redux.GUI.UI/.gitignore
vendored
Normal file
10
Il2CppInspector.Redux.GUI.UI/.gitignore
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
.DS_Store
|
||||||
|
node_modules
|
||||||
|
/build
|
||||||
|
/.svelte-kit
|
||||||
|
/package
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
!.env.example
|
||||||
|
vite.config.js.timestamp-*
|
||||||
|
vite.config.ts.timestamp-*
|
||||||
4
Il2CppInspector.Redux.GUI.UI/.prettierrc
Normal file
4
Il2CppInspector.Redux.GUI.UI/.prettierrc
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"plugins": ["prettier-plugin-svelte", "prettier-plugin-tailwindcss"],
|
||||||
|
"tabWidth": 4
|
||||||
|
}
|
||||||
17
Il2CppInspector.Redux.GUI.UI/components.json
Normal file
17
Il2CppInspector.Redux.GUI.UI/components.json
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://next.shadcn-svelte.com/schema.json",
|
||||||
|
"style": "new-york",
|
||||||
|
"tailwind": {
|
||||||
|
"config": "tailwind.config.ts",
|
||||||
|
"css": "src\\app.css",
|
||||||
|
"baseColor": "stone"
|
||||||
|
},
|
||||||
|
"aliases": {
|
||||||
|
"components": "$lib/components",
|
||||||
|
"utils": "$lib/utils",
|
||||||
|
"ui": "$lib/components/ui",
|
||||||
|
"hooks": "$lib/hooks"
|
||||||
|
},
|
||||||
|
"typescript": true,
|
||||||
|
"registry": "https://next.shadcn-svelte.com/registry"
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user