Skip to content

Commit

Permalink
Make KFA thunk detection a little more intelligent.
Browse files Browse the repository at this point in the history
  • Loading branch information
Sam Byass committed Sep 19, 2021
1 parent 813b471 commit d94b694
Show file tree
Hide file tree
Showing 6 changed files with 65 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ public override void PostProcess(MethodAnalysis<T> analysis)
if (nameBase.Contains("`"))
nameBase = nameBase[..nameBase.IndexOf("`", StringComparison.Ordinal)];

if (nameBase.Contains("<"))
//Compiler-generated IEnumerator instances
nameBase = "enumeratorInstance";

if (nameBase.EndsWith("[]"))
nameBase = nameBase[..^2] + "Array";

Expand Down
25 changes: 16 additions & 9 deletions Cpp2IL.Core/Arm64KeyFunctionAddresses.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public Arm64KeyFunctionAddresses()
_allInstructions = disassembler.Disassemble(LibCpp2IlMain.Binary.GetEntirePrimaryExecutableSection(), (long)LibCpp2IlMain.Binary.GetVirtualAddressOfPrimaryExecutableSection()).ToList();
}

protected override ulong FindThunkFunction(ulong addr, uint maxBytesBack = 0, params ulong[] addressesToIgnore)
protected override IEnumerable<ulong> FindAllThunkFunctions(ulong addr, uint maxBytesBack = 0, params ulong[] addressesToIgnore)
{
var allBranchesToAddr = _allInstructions.Where(i => i.Mnemonic == "b")
.Where(i => i.Details.Operands[0].IsImmediate() && i.Details.Operands[0].Immediate == (long)addr)
Expand All @@ -46,21 +46,25 @@ protected override ulong FindThunkFunction(ulong addr, uint maxBytesBack = 0, pa

if(addressesToIgnore.Contains((ulong) prevInstruction.Address))
continue;

if(prevInstruction.Mnemonic is "b" or "bl")
return (ulong)potentialBranch.Address - (ulong)(backtrack * 4);

if(prevInstruction.Mnemonic is "ret")
return (ulong)potentialBranch.Address - (ulong)(backtrack * 4);

if (prevInstruction.Mnemonic is "b" or "bl")
{
yield return (ulong)potentialBranch.Address - (ulong)(backtrack * 4);
break;
}

if (prevInstruction.Mnemonic is "ret")
{
yield return (ulong)potentialBranch.Address - (ulong)(backtrack * 4);
break;
}
}

//We're working in the .text section here so we have few symbols, so there's no point looking for the previous one.

backtrack++;
} while (backtrack * 4 < maxBytesBack);
}

return 0;
}

protected override ulong FindFunctionThisIsAThunkOf(ulong thunkPtr, bool prioritiseCall = false)
Expand All @@ -82,5 +86,8 @@ protected override ulong FindFunctionThisIsAThunkOf(ulong thunkPtr, bool priorit

return 0;
}

protected override int GetCallerCount(ulong toWhere) => _allInstructions.Where(i => i.Mnemonic is "b" or "bl")
.Count(i => i.Details.Operands[0].IsImmediate() && i.Details.Operands[0].Immediate == (long)toWhere);
}
}
25 changes: 20 additions & 5 deletions Cpp2IL.Core/BaseKeyFunctionAddresses.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Diagnostics.CodeAnalysis;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Iced.Intel;
using LibCpp2IL;
Expand Down Expand Up @@ -57,7 +58,19 @@ public void Find()
if (il2cpp_vm_object_new != 0)
{
Logger.Verbose("\t\tLooking for il2cpp_codegen_object_new as a thunk of vm::Object::New...");
il2cpp_codegen_object_new = FindThunkFunction(il2cpp_vm_object_new);

var potentialThunks = FindAllThunkFunctions(il2cpp_vm_object_new, 10);

//Sort by caller count in ascending order
var list = potentialThunks.Select(ptr => (ptr, count: GetCallerCount(ptr))).ToList();
list.SortByExtractedKey(pair => pair.count);

//Sort in descending order - most called first
list.Reverse();

//Take first as the target
il2cpp_codegen_object_new = list.FirstOrDefault().ptr;

Logger.VerboseNewline($"Found at 0x{il2cpp_codegen_object_new:X}");
}

Expand Down Expand Up @@ -124,7 +137,7 @@ public void Find()
if (il2cpp_vm_exception_raise != 0)
{
Logger.Verbose("\t\tMapping il2cpp::vm::Exception::Raise to il2cpp_codegen_raise_exception...");
il2cpp_codegen_raise_exception = FindThunkFunction(il2cpp_vm_exception_raise, 4, il2cpp_raise_exception);
il2cpp_codegen_raise_exception = FindAllThunkFunctions(il2cpp_vm_exception_raise, 4, il2cpp_raise_exception).FirstOrDefault();
Logger.VerboseNewline($"Found at 0x{il2cpp_codegen_raise_exception:X}");
}

Expand Down Expand Up @@ -155,7 +168,7 @@ public void Find()
if (il2cpp_vm_array_new_specific != 0)
{
Logger.Verbose("\t\tLooking for SzArrayNew as a thunk function proxying Array::NewSpecific...");
SzArrayNew = FindThunkFunction(il2cpp_vm_array_new_specific, 4, il2cpp_array_new_specific);
SzArrayNew = FindAllThunkFunctions(il2cpp_vm_array_new_specific, 4, il2cpp_array_new_specific).FirstOrDefault();
Logger.VerboseNewline($"Found at 0x{SzArrayNew:X}");
}
}
Expand Down Expand Up @@ -198,7 +211,7 @@ protected void TryGetInitMetadataFromException()
/// <param name="maxBytesBack">The maximum number of bytes to go back from any branching instructions to find the actual start of the thunk function.</param>
/// <param name="addressesToIgnore">A list of function addresses which this function must not return</param>
/// <returns>The address of the first function in the file which thunks addr, starts within maxBytesBack bytes of the branch, and is not contained within addressesToIgnore, else 0 if none can be found.</returns>
protected abstract ulong FindThunkFunction(ulong addr, uint maxBytesBack = 0, params ulong[] addressesToIgnore);
protected abstract IEnumerable<ulong> FindAllThunkFunctions(ulong addr, uint maxBytesBack = 0, params ulong[] addressesToIgnore);

/// <summary>
/// Given a function at thunkPtr, return the address of the function that said function exists only to call.
Expand All @@ -208,5 +221,7 @@ protected void TryGetInitMetadataFromException()
/// <param name="prioritiseCall">True to prioritise "call" statements - conditional flow transfer - over "jump" statements - unconditional flow transfer. False for the inverse.</param>
/// <returns>The address of the thunked function, if it can be found, else 0</returns>
protected abstract ulong FindFunctionThisIsAThunkOf(ulong thunkPtr, bool prioritiseCall = false);

protected abstract int GetCallerCount(ulong toWhere);
}
}
24 changes: 18 additions & 6 deletions Cpp2IL.Core/X86KeyFunctionAddresses.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Iced.Intel;
using LibCpp2IL;
Expand All @@ -8,13 +9,13 @@ namespace Cpp2IL.Core
{
public class X86KeyFunctionAddresses : BaseKeyFunctionAddresses
{
protected override ulong FindThunkFunction(ulong addr, uint maxBytesBack = 0, params ulong[] addressesToIgnore)
protected override IEnumerable<ulong> FindAllThunkFunctions(ulong addr, uint maxBytesBack = 0, params ulong[] addressesToIgnore)
{
//Disassemble .text
var allInstructions = ((PE) LibCpp2IlMain.Binary!).DisassembleTextSection();

//Find all jumps to the target address
var matchingJmps = allInstructions.Where(i => i.Mnemonic == Mnemonic.Jmp || i.Mnemonic == Mnemonic.Call && i.NearBranchTarget == addr).ToList();
var matchingJmps = allInstructions.Where(i => i.Mnemonic is Mnemonic.Jmp or Mnemonic.Call && i.NearBranchTarget == addr).ToList();

foreach (var matchingJmp in matchingJmps)
{
Expand All @@ -32,7 +33,8 @@ protected override ulong FindThunkFunction(ulong addr, uint maxBytesBack = 0, pa
//Double-cc = thunk
if (previousByte == 0xCC && nextByte == 0xCC)
{
return matchingJmp.IP;
yield return matchingJmp.IP;
continue;
}

if (nextByte == 0xCC && maxBytesBack > 0)
Expand All @@ -44,12 +46,13 @@ protected override ulong FindThunkFunction(ulong addr, uint maxBytesBack = 0, pa
break;

if (LibCpp2IlMain.Binary!.GetByteAtRawAddress(offsetInPe - backtrack) == 0xCC)
return matchingJmp.IP - (backtrack - 1);
{
yield return matchingJmp.IP - (backtrack - 1);
break;
}
}
}
}

return 0;
}

protected override ulong FindFunctionThisIsAThunkOf(ulong thunkPtr, bool prioritiseCall = false)
Expand All @@ -74,5 +77,14 @@ protected override ulong FindFunctionThisIsAThunkOf(ulong thunkPtr, bool priorit
return 0;
}
}

protected override int GetCallerCount(ulong toWhere)
{
//Disassemble .text
var allInstructions = ((PE) LibCpp2IlMain.Binary!).DisassembleTextSection();

//Find all jumps to the target address
return allInstructions.Count(i => i.Mnemonic == Mnemonic.Jmp || i.Mnemonic == Mnemonic.Call && i.NearBranchTarget == toWhere);
}
}
}
4 changes: 4 additions & 0 deletions Cpp2IL/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,10 @@ private static void ResolvePathsFromCommandLine(string gamePath, string? inputEx

args.Valid = true;
}
else
{
throw new SoftException($"Could not find a valid unity game at {gamePath}");
}
}

private static Cpp2IlRuntimeArgs GetRuntimeOptionsFromCommandLine(string[] commandLine)
Expand Down
3 changes: 3 additions & 0 deletions LibCpp2IL/Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ public static bool TryAdd<TKey, TValue>(this Dictionary<TKey, TValue> dictionary
return true;
}

/// <summary>
/// Sorts in ascending order using the provided key function
/// </summary>
public static void SortByExtractedKey<T, K>(this List<T> list, Func<T, K> keyObtainer) where K : IComparable<K>
{
list.Sort((a, b) =>
Expand Down

0 comments on commit d94b694

Please sign in to comment.