AppModel: Add initial handling of methods (AppMethod)
This commit is contained in:
35
Il2CppInspector.Common/Model/AppMethod.cs
Normal file
35
Il2CppInspector.Common/Model/AppMethod.cs
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2020 Katy Coe - http://www.djkaty.com - https://github.com/djkaty
|
||||||
|
|
||||||
|
All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using Il2CppInspector.Cpp;
|
||||||
|
using Il2CppInspector.Reflection;
|
||||||
|
|
||||||
|
namespace Il2CppInspector.Model
|
||||||
|
{
|
||||||
|
// Class that represents a composite IL/C++ method
|
||||||
|
public class AppMethod
|
||||||
|
{
|
||||||
|
// The corresponding C++ function pointer type
|
||||||
|
public CppFnPtrType CppFnPtrType { get; internal set; }
|
||||||
|
|
||||||
|
// The corresponding .NET method
|
||||||
|
public MethodBase Method { get; internal set; }
|
||||||
|
|
||||||
|
// The VA of the MethodInfo* (VA of the pointer to the MethodInfo) object which defines this method
|
||||||
|
public ulong MethodInfoPtrAddress { get; internal set; }
|
||||||
|
|
||||||
|
// The VA of the method code itself, or 0 if unknown/not compiled
|
||||||
|
public ulong MethodCodeAddress => Method.VirtualAddress?.Start ?? 0;
|
||||||
|
|
||||||
|
public AppMethod(MethodBase method, CppFnPtrType cppMethod, ulong methodInfoPtr = 0xffffffff_ffffffff) {
|
||||||
|
Method = method;
|
||||||
|
CppFnPtrType = cppMethod;
|
||||||
|
MethodInfoPtrAddress = methodInfoPtr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString() => CppFnPtrType.ToSignatureString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@ using System;
|
|||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Aron.Weiler;
|
||||||
using Il2CppInspector.Cpp;
|
using Il2CppInspector.Cpp;
|
||||||
using Il2CppInspector.Cpp.UnityHeaders;
|
using Il2CppInspector.Cpp.UnityHeaders;
|
||||||
using Il2CppInspector.Reflection;
|
using Il2CppInspector.Reflection;
|
||||||
@@ -32,22 +33,6 @@ namespace Il2CppInspector.Model
|
|||||||
public ulong VirtualAddress { get; internal set; }
|
public ulong VirtualAddress { get; internal set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Class that represents a composite IL/C++ method
|
|
||||||
public class AppMethod
|
|
||||||
{
|
|
||||||
// The corresponding C++ function pointer type
|
|
||||||
public CppFnPtrType CppFnPtrType { get; internal set; }
|
|
||||||
|
|
||||||
// The corresponding .NET method
|
|
||||||
public MethodBase ILMethod { get; internal set; }
|
|
||||||
|
|
||||||
// The VA of the MethodInfo* (VA of the pointer to the MethodInfo) object which defines this method
|
|
||||||
public ulong MethodInfoPtrAddress { get; internal set; }
|
|
||||||
|
|
||||||
// The VA of the method code itself, or 0 if unknown/not compiled
|
|
||||||
public ulong MethodCodeAddress => ILMethod.VirtualAddress?.Start ?? 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Class that represents the entire structure of the IL2CPP binary realized as C++ types and code,
|
// Class that represents the entire structure of the IL2CPP binary realized as C++ types and code,
|
||||||
// correlated with .NET types where applicable. Primarily designed to enable automated static analysis of disassembly code.
|
// correlated with .NET types where applicable. Primarily designed to enable automated static analysis of disassembly code.
|
||||||
public class AppModel : IEnumerable<CppType>
|
public class AppModel : IEnumerable<CppType>
|
||||||
@@ -71,6 +56,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> DependencyOrderedTypes { get; private set; }
|
public List<CppType> DependencyOrderedTypes { get; private set; }
|
||||||
|
|
||||||
|
// Composite mapping of all the .NET methods in the IL2CPP binary
|
||||||
|
public MultiKeyDictionary<MethodBase, CppFnPtrType, AppMethod> Methods = new MultiKeyDictionary<MethodBase, CppFnPtrType, AppMethod>();
|
||||||
|
|
||||||
// The .NET type model for the application
|
// The .NET type model for the application
|
||||||
public TypeModel ILModel { get; }
|
public TypeModel ILModel { get; }
|
||||||
|
|
||||||
@@ -143,6 +131,9 @@ namespace Il2CppInspector.Model
|
|||||||
foreach (var method in ILModel.MethodsByDefinitionIndex.Where(m => m.VirtualAddress.HasValue)) {
|
foreach (var method in ILModel.MethodsByDefinitionIndex.Where(m => m.VirtualAddress.HasValue)) {
|
||||||
declarationGenerator.IncludeMethod(method);
|
declarationGenerator.IncludeMethod(method);
|
||||||
DependencyOrderedTypes.AddRange(declarationGenerator.GenerateRemainingTypeDeclarations());
|
DependencyOrderedTypes.AddRange(declarationGenerator.GenerateRemainingTypeDeclarations());
|
||||||
|
|
||||||
|
var fnPtr = declarationGenerator.GenerateMethodDeclaration(method);
|
||||||
|
Methods.Add(method, fnPtr, new AppMethod(method, fnPtr));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add generic methods to C++ type model
|
// Add generic methods to C++ type model
|
||||||
@@ -151,6 +142,9 @@ namespace Il2CppInspector.Model
|
|||||||
foreach (var method in ILModel.GenericMethods.Values.Where(m => m.VirtualAddress.HasValue)) {
|
foreach (var method in ILModel.GenericMethods.Values.Where(m => m.VirtualAddress.HasValue)) {
|
||||||
declarationGenerator.IncludeMethod(method);
|
declarationGenerator.IncludeMethod(method);
|
||||||
DependencyOrderedTypes.AddRange(declarationGenerator.GenerateRemainingTypeDeclarations());
|
DependencyOrderedTypes.AddRange(declarationGenerator.GenerateRemainingTypeDeclarations());
|
||||||
|
|
||||||
|
var fnPtr = declarationGenerator.GenerateMethodDeclaration(method);
|
||||||
|
Methods.Add(method, fnPtr, new AppMethod(method, fnPtr));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add metadata usage types to C++ type model
|
// Add metadata usage types to C++ type model
|
||||||
@@ -159,6 +153,8 @@ namespace Il2CppInspector.Model
|
|||||||
|
|
||||||
if (Package.MetadataUsages != null)
|
if (Package.MetadataUsages != null)
|
||||||
foreach (var usage in Package.MetadataUsages) {
|
foreach (var usage in Package.MetadataUsages) {
|
||||||
|
var address = usage.VirtualAddress;
|
||||||
|
|
||||||
switch (usage.Type) {
|
switch (usage.Type) {
|
||||||
case MetadataUsageType.Type:
|
case MetadataUsageType.Type:
|
||||||
case MetadataUsageType.TypeInfo:
|
case MetadataUsageType.TypeInfo:
|
||||||
@@ -171,6 +167,8 @@ namespace Il2CppInspector.Model
|
|||||||
var method = ILModel.GetMetadataUsageMethod(usage);
|
var method = ILModel.GetMetadataUsageMethod(usage);
|
||||||
declarationGenerator.IncludeMethod(method);
|
declarationGenerator.IncludeMethod(method);
|
||||||
DependencyOrderedTypes.AddRange(declarationGenerator.GenerateRemainingTypeDeclarations());
|
DependencyOrderedTypes.AddRange(declarationGenerator.GenerateRemainingTypeDeclarations());
|
||||||
|
|
||||||
|
Methods[method].MethodInfoPtrAddress = address;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
327
Il2CppInspector.Common/MultiKeyDictionary.cs
Normal file
327
Il2CppInspector.Common/MultiKeyDictionary.cs
Normal file
@@ -0,0 +1,327 @@
|
|||||||
|
// *************************************************
|
||||||
|
// Created by Aron Weiler
|
||||||
|
// Feel free to use this code in any way you like,
|
||||||
|
// just don't blame me when your coworkers think you're awesome.
|
||||||
|
// Comments? Email aronweiler@gmail.com
|
||||||
|
// Revision 1.6
|
||||||
|
// Revised locking strategy based on the some bugs found with the existing lock objects.
|
||||||
|
// Possible deadlock and race conditions resolved by swapping out the two lock objects for a ReaderWriterLockSlim.
|
||||||
|
// Performance takes a very small hit, but correctness is guaranteed.
|
||||||
|
// *************************************************
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace Aron.Weiler
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Multi-Key Dictionary Class
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="K">Primary Key Type</typeparam>
|
||||||
|
/// <typeparam name="L">Sub Key Type</typeparam>
|
||||||
|
/// <typeparam name="V">Value Type</typeparam>
|
||||||
|
public class MultiKeyDictionary<K, L, V>
|
||||||
|
{
|
||||||
|
internal readonly Dictionary<K, V> baseDictionary = new Dictionary<K, V>();
|
||||||
|
internal readonly Dictionary<L, K> subDictionary = new Dictionary<L, K>();
|
||||||
|
internal readonly Dictionary<K, L> primaryToSubkeyMapping = new Dictionary<K, L>();
|
||||||
|
|
||||||
|
ReaderWriterLockSlim readerWriterLock = new ReaderWriterLockSlim();
|
||||||
|
|
||||||
|
public V this[L subKey]
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
V item;
|
||||||
|
if (TryGetValue(subKey, out item))
|
||||||
|
return item;
|
||||||
|
|
||||||
|
throw new KeyNotFoundException("sub key not found: " + subKey.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public V this[K primaryKey]
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
V item;
|
||||||
|
if (TryGetValue(primaryKey, out item))
|
||||||
|
return item;
|
||||||
|
|
||||||
|
throw new KeyNotFoundException("primary key not found: " + primaryKey.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Associate(L subKey, K primaryKey)
|
||||||
|
{
|
||||||
|
readerWriterLock.EnterUpgradeableReadLock();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!baseDictionary.ContainsKey(primaryKey))
|
||||||
|
throw new KeyNotFoundException(string.Format("The base dictionary does not contain the key '{0}'", primaryKey));
|
||||||
|
|
||||||
|
if (primaryToSubkeyMapping.ContainsKey(primaryKey)) // Remove the old mapping first
|
||||||
|
{
|
||||||
|
readerWriterLock.EnterWriteLock();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (subDictionary.ContainsKey(primaryToSubkeyMapping[primaryKey]))
|
||||||
|
{
|
||||||
|
subDictionary.Remove(primaryToSubkeyMapping[primaryKey]);
|
||||||
|
}
|
||||||
|
|
||||||
|
primaryToSubkeyMapping.Remove(primaryKey);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
readerWriterLock.ExitWriteLock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
subDictionary[subKey] = primaryKey;
|
||||||
|
primaryToSubkeyMapping[primaryKey] = subKey;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
readerWriterLock.ExitUpgradeableReadLock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetValue(L subKey, out V val)
|
||||||
|
{
|
||||||
|
val = default(V);
|
||||||
|
|
||||||
|
K primaryKey;
|
||||||
|
|
||||||
|
readerWriterLock.EnterReadLock();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (subDictionary.TryGetValue(subKey, out primaryKey))
|
||||||
|
{
|
||||||
|
return baseDictionary.TryGetValue(primaryKey, out val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
readerWriterLock.ExitReadLock();
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetValue(K primaryKey, out V val)
|
||||||
|
{
|
||||||
|
readerWriterLock.EnterReadLock();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return baseDictionary.TryGetValue(primaryKey, out val);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
readerWriterLock.ExitReadLock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ContainsKey(L subKey)
|
||||||
|
{
|
||||||
|
V val;
|
||||||
|
|
||||||
|
return TryGetValue(subKey, out val);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ContainsKey(K primaryKey)
|
||||||
|
{
|
||||||
|
V val;
|
||||||
|
|
||||||
|
return TryGetValue(primaryKey, out val);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Remove(K primaryKey)
|
||||||
|
{
|
||||||
|
readerWriterLock.EnterWriteLock();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (primaryToSubkeyMapping.ContainsKey(primaryKey))
|
||||||
|
{
|
||||||
|
if (subDictionary.ContainsKey(primaryToSubkeyMapping[primaryKey]))
|
||||||
|
{
|
||||||
|
subDictionary.Remove(primaryToSubkeyMapping[primaryKey]);
|
||||||
|
}
|
||||||
|
|
||||||
|
primaryToSubkeyMapping.Remove(primaryKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
baseDictionary.Remove(primaryKey);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
readerWriterLock.ExitWriteLock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Remove(L subKey)
|
||||||
|
{
|
||||||
|
readerWriterLock.EnterWriteLock();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
baseDictionary.Remove(subDictionary[subKey]);
|
||||||
|
|
||||||
|
primaryToSubkeyMapping.Remove(subDictionary[subKey]);
|
||||||
|
|
||||||
|
subDictionary.Remove(subKey);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
readerWriterLock.ExitWriteLock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Add(K primaryKey, V val)
|
||||||
|
{
|
||||||
|
readerWriterLock.EnterWriteLock();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
baseDictionary.Add(primaryKey, val);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
readerWriterLock.ExitWriteLock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Add(K primaryKey, L subKey, V val)
|
||||||
|
{
|
||||||
|
Add(primaryKey, val);
|
||||||
|
|
||||||
|
Associate(subKey, primaryKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
public V[] CloneValues()
|
||||||
|
{
|
||||||
|
readerWriterLock.EnterReadLock();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
V[] values = new V[baseDictionary.Values.Count];
|
||||||
|
|
||||||
|
baseDictionary.Values.CopyTo(values, 0);
|
||||||
|
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
readerWriterLock.ExitReadLock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<V> Values
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
readerWriterLock.EnterReadLock();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return baseDictionary.Values.ToList();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
readerWriterLock.ExitReadLock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public K[] ClonePrimaryKeys()
|
||||||
|
{
|
||||||
|
readerWriterLock.EnterReadLock();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
K[] values = new K[baseDictionary.Keys.Count];
|
||||||
|
|
||||||
|
baseDictionary.Keys.CopyTo(values, 0);
|
||||||
|
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
readerWriterLock.ExitReadLock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public L[] CloneSubKeys()
|
||||||
|
{
|
||||||
|
readerWriterLock.EnterReadLock();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
L[] values = new L[subDictionary.Keys.Count];
|
||||||
|
|
||||||
|
subDictionary.Keys.CopyTo(values, 0);
|
||||||
|
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
readerWriterLock.ExitReadLock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
readerWriterLock.EnterWriteLock();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
baseDictionary.Clear();
|
||||||
|
|
||||||
|
subDictionary.Clear();
|
||||||
|
|
||||||
|
primaryToSubkeyMapping.Clear();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
readerWriterLock.ExitWriteLock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Count
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
readerWriterLock.EnterReadLock();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return baseDictionary.Count;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
readerWriterLock.ExitReadLock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerator<KeyValuePair<K, V>> GetEnumerator()
|
||||||
|
{
|
||||||
|
readerWriterLock.EnterReadLock();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return baseDictionary.GetEnumerator();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
readerWriterLock.ExitReadLock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -289,6 +289,8 @@ Thanks to the following major contributors!
|
|||||||
- nneonneo - https://github.com/nneonneo (huge overhaul of generics and script generation)
|
- nneonneo - https://github.com/nneonneo (huge overhaul of generics and script generation)
|
||||||
- carterbush - https://github.com/carterbush (IDA script generation)
|
- carterbush - https://github.com/carterbush (IDA script generation)
|
||||||
|
|
||||||
|
This project uses [MultiKeyDictionary](https://www.codeproject.com/Articles/32894/C-Multi-key-Generic-Dictionary) by Aron Weiler.
|
||||||
|
|
||||||
Thanks to the following individuals whose code and research helped me develop this tool:
|
Thanks to the following individuals whose code and research helped me develop this tool:
|
||||||
|
|
||||||
- Perfare - https://github.com/Perfare/Il2CppDumper
|
- Perfare - https://github.com/Perfare/Il2CppDumper
|
||||||
|
|||||||
Reference in New Issue
Block a user