Skip to content

Commit af8fb61

Browse files
committed
Unity 2019 fix WIP
1 parent 8784088 commit af8fb61

6 files changed

Lines changed: 199 additions & 23 deletions

File tree

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Diagnostics;
4+
using System.IO;
5+
using System.Linq;
6+
using System.Text;
7+
8+
namespace Il2CppInterop.Runtime.Injection
9+
{
10+
internal class GameManagersAssemblyListFile : IAssemblyListFile
11+
{
12+
private List<string> _assemblies = new List<string>();
13+
private string originalFile;
14+
private string newFile;
15+
16+
public bool IsTargetFile(string originalFilePath)
17+
{
18+
return originalFilePath.Contains("globalgamemanagers");
19+
}
20+
21+
public void Setup(string originalFilePath)
22+
{
23+
if (originalFile != null) return;
24+
originalFile = originalFilePath;
25+
}
26+
27+
public void AddAssembly(string name)
28+
{
29+
_assemblies.Add(name);
30+
}
31+
32+
public string GetOrCreateNewFile()
33+
{
34+
if (newFile != null) return newFile;
35+
36+
newFile = Path.GetTempFileName();
37+
CreateModifiedFile();
38+
return newFile;
39+
}
40+
41+
private void CreateModifiedFile()
42+
{
43+
using var outputStream = File.Open(newFile, FileMode.Create);
44+
using var outputWriter = new BinaryWriter(outputStream, Encoding.ASCII, false);
45+
using var inputStream = File.Open(originalFile, FileMode.Open);
46+
using var inputReader = new BinaryReader(inputStream, Encoding.ASCII, false);
47+
48+
// Assembly list always starts with UnityEngine.dll
49+
var startPos = SeekFirstName(inputStream, inputReader);
50+
if (startPos == -1)
51+
{
52+
throw new Exception("Failed to find start of assembly list in globalgamemanagers file!");
53+
}
54+
55+
inputStream.Position = 0;
56+
startPos -= 8;
57+
58+
for (var i = 0; i < startPos; i++)
59+
{
60+
outputWriter.Write(inputReader.ReadByte());
61+
}
62+
63+
var assemblyCount = inputReader.ReadInt32();
64+
List<string> newAssemblyList = new List<string>(assemblyCount + _assemblies.Count);
65+
List<int> newAssemblyTypes = new List<int>(assemblyCount + _assemblies.Count);
66+
for (var i = 0; i < assemblyCount; i++)
67+
{
68+
newAssemblyList.Add(ReadString(inputReader));
69+
}
70+
71+
assemblyCount = inputReader.ReadInt32();
72+
for (var i = 0; i < assemblyCount; i++)
73+
{
74+
newAssemblyTypes.Add(inputReader.ReadInt32());
75+
}
76+
77+
newAssemblyList.AddRange(_assemblies);
78+
newAssemblyTypes.AddRange(_assemblies.Select(_ => 16));
79+
80+
outputWriter.Write(newAssemblyList.Count);
81+
foreach (var assemblyName in newAssemblyList)
82+
{
83+
WriteString(outputWriter, assemblyName);
84+
}
85+
86+
outputWriter.Write(newAssemblyTypes.Count);
87+
foreach (var assemblyType in newAssemblyTypes)
88+
{
89+
outputWriter.Write(assemblyType);
90+
}
91+
92+
while (inputStream.Position < inputStream.Length)
93+
{
94+
outputWriter.Write(inputReader.ReadByte());
95+
}
96+
}
97+
98+
private static void WriteString(BinaryWriter outputWriter, string @string)
99+
{
100+
outputWriter.Write(@string.Length);
101+
var paddedLenth = (int)(Math.Ceiling(@string.Length / 4f) * 4f);
102+
for (int i = 0; i < paddedLenth; i++)
103+
{
104+
if (i < @string.Length)
105+
outputWriter.Write(@string[i]);
106+
else
107+
outputWriter.Write((byte)0);
108+
}
109+
}
110+
111+
private static string ReadString(BinaryReader inputReader)
112+
{
113+
var length = inputReader.ReadInt32();
114+
var paddedLenth = (int)(Math.Ceiling(length / 4f) * 4f);
115+
StringBuilder sb = new StringBuilder(length);
116+
for (var j = 0; j < paddedLenth; j++)
117+
{
118+
var c = inputReader.ReadChar();
119+
if (j < length)
120+
sb.Append(c);
121+
}
122+
123+
return sb.ToString();
124+
}
125+
126+
private static long SeekFirstName(FileStream inputStream, BinaryReader inputReader)
127+
{
128+
while (inputStream.Position < inputStream.Length)
129+
{
130+
var currentPos = inputStream.Position;
131+
var firstChar = inputReader.ReadChar();
132+
if (firstChar != 'U') continue;
133+
134+
var nextString = new string(inputReader.ReadChars(14));
135+
if (!nextString.Equals("nityEngine.dll")) continue;
136+
137+
return currentPos;
138+
}
139+
140+
return -1;
141+
}
142+
}
143+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
namespace Il2CppInterop.Runtime.Injection
2+
{
3+
internal interface IAssemblyListFile
4+
{
5+
public bool IsTargetFile(string originalFilePath);
6+
public void Setup(string originalFilePath);
7+
public void AddAssembly(string name);
8+
public string GetOrCreateNewFile();
9+
}
10+
}

Il2CppInterop.Runtime/Injection/AssemblyListFile.cs renamed to Il2CppInterop.Runtime/Injection/AssemblyList/JSONAssemblyListFile.cs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,28 +4,35 @@
44

55
namespace Il2CppInterop.Runtime.Injection
66
{
7-
public class AssemblyListFile
7+
internal class JSONAssemblyListFile : IAssemblyListFile
88
{
9-
private readonly JsonNode node;
10-
private readonly JsonArray names;
11-
private readonly JsonArray types;
9+
private JsonNode node;
10+
private JsonArray names;
11+
private JsonArray types;
1212

1313
private string newFile;
1414

15-
public AssemblyListFile(string originalFilePath)
15+
public void Setup(string originalFilePath)
1616
{
17+
if (node != null) return;
18+
1719
node = JsonNode.Parse(File.ReadAllText(originalFilePath));
1820
names = node["names"].AsArray();
1921
types = node["types"].AsArray();
2022
}
2123

24+
public bool IsTargetFile(string originalFilePath)
25+
{
26+
return originalFilePath.Contains("ScriptingAssemblies.json");
27+
}
28+
2229
public void AddAssembly(string name)
2330
{
2431
names.Add(name);
2532
types.Add(16);
2633
}
2734

28-
public string GetTmpFile()
35+
public string GetOrCreateNewFile()
2936
{
3037
if (!string.IsNullOrEmpty(newFile)) return newFile;
3138

Il2CppInterop.Runtime/Injection/Hooks/Assembly_Load_Hook.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,12 @@ internal unsafe class Assembly_Load_Hook : Hook<Assembly_Load_Hook.MethodDelegat
1919
private Il2CppAssembly* Hook(IntPtr name)
2020
{
2121
Il2CppAssembly* assembly = Original(name);
22+
InjectorHelpers.UnpatchIATHooks();
23+
var assemblyName = Marshal.PtrToStringAnsi(name);
2224

25+
Logger.Instance.LogInformation($"Assembly::Load {assemblyName}");
2326
if (assembly == null)
2427
{
25-
var assemblyName = Marshal.PtrToStringAnsi(name);
2628
if (InjectorHelpers.TryGetInjectedImage(assemblyName, out var ptr))
2729
{
2830
var image = UnityVersionHandler.Wrap((Il2CppImage*)ptr);

Il2CppInterop.Runtime/Injection/InjectorHelpers.cs

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ internal static unsafe class InjectorHelpers
3535
internal static IntPtr Il2CppHandle = NativeLibrary.Load("GameAssembly", typeof(InjectorHelpers).Assembly, null);
3636
internal static IntPtr UnityPlayerHandle = NativeLibrary.Load("UnityPlayer", typeof(InjectorHelpers).Assembly, null);
3737
internal static AssemblyIATHooker UnityPlayerIATHooker;
38-
internal static string NewAssemblyListFile;
38+
internal static IAssemblyListFile AssemblyListFile;
3939

4040
internal static readonly Dictionary<Type, OpCode> StIndOpcodes = new()
4141
{
@@ -124,8 +124,8 @@ internal static void Setup()
124124
FromNameHook.ApplyHook();
125125
RunFinalizerPatch.ApplyHook();
126126

127-
AssemblyGetLoadedAssemblyHook.ApplyHook();
128-
AppDomainGetAssembliesHook.ApplyHook();
127+
//AssemblyGetLoadedAssemblyHook.ApplyHook();
128+
//AppDomainGetAssembliesHook.ApplyHook();
129129
}
130130

131131
[StructLayout(LayoutKind.Sequential)]
@@ -160,21 +160,19 @@ public static extern IntPtr CreateFile(
160160
private static int GetFileAttributesExDetour(IntPtr lpFileName, int fInfoLevelId, IntPtr lpFileInformation)
161161
{
162162
var filePath = Marshal.PtrToStringUni(lpFileName);
163+
filePath = filePath.Replace(@"\\?\", "");
163164

164-
if (filePath.Contains("ScriptingAssemblies.json"))
165+
if (AssemblyListFile.IsTargetFile(filePath))
165166
{
166-
filePath = filePath.Replace(@"\\?\", "");
167-
168-
var assemblyList = new AssemblyListFile(filePath);
169-
167+
AssemblyListFile.Setup(filePath);
170168
foreach (var assemblyName in InjectedImages.Keys)
171169
{
172-
assemblyList.AddAssembly(assemblyName);
170+
AssemblyListFile.AddAssembly(assemblyName);
173171
}
174172

175-
NewAssemblyListFile = assemblyList.GetTmpFile();
176-
Logger.Instance.LogInformation($"Forcing unity to read assembly list from {NewAssemblyListFile}");
177-
var newlpFileName = Marshal.StringToHGlobalUni(NewAssemblyListFile);
173+
var newFile = AssemblyListFile.GetOrCreateNewFile();
174+
Logger.Instance.LogInformation($"Forcing unity to read assembly list from {newFile}");
175+
var newlpFileName = Marshal.StringToHGlobalUni(newFile);
178176

179177
var result = GetFileAttributesEx(newlpFileName, fInfoLevelId, lpFileInformation);
180178
Marshal.FreeHGlobal(newlpFileName);
@@ -197,22 +195,24 @@ private static int ReadFileDetour(IntPtr handle, IntPtr bytes, uint numBytesToRe
197195
if (res != 0)
198196
{
199197
var filePath = sb.ToString();
200-
if (filePath.Contains("ScriptingAssemblies.json"))
198+
if (AssemblyListFile.IsTargetFile(filePath))
201199
{
202-
IntPtr newHandle = CreateFile(NewAssemblyListFile, FileAccess.Read, FileShare.Read, IntPtr.Zero, FileMode.Open, FileAttributes.Normal, IntPtr.Zero);
203-
UnpatchIATHooks();
200+
IntPtr newHandle = CreateFile(AssemblyListFile.GetOrCreateNewFile(), FileAccess.Read, FileShare.Read, IntPtr.Zero, FileMode.Open, FileAttributes.Normal, IntPtr.Zero);
204201
return ReadFile(newHandle, bytes, numBytesToRead, numBytesRead, overlapped);
205202
}
206203
}
207204

208205
return ReadFile(handle, bytes, numBytesToRead, numBytesRead, overlapped);
209206
}
210207

211-
private static void UnpatchIATHooks()
208+
internal static void UnpatchIATHooks()
212209
{
210+
if (UnityPlayerIATHooker == null) return;
211+
213212
Logger.Instance.LogInformation("Unpatching UnityPlayer IAT hooks");
214213
UnityPlayerIATHooker.UnpatchIATHook("KERNEL32.dll", "ReadFile");
215214
UnityPlayerIATHooker.UnpatchIATHook("KERNEL32.dll", "GetFileAttributesExW");
215+
UnityPlayerIATHooker = null;
216216
}
217217

218218
// Setup before unity loads assembly list
@@ -231,6 +231,8 @@ internal static void EarlySetup()
231231
}
232232
}
233233

234+
AssemblyListFile = UnityVersionHandler.GetAssemblyListFile();
235+
234236
UnityPlayerIATHooker = new AssemblyIATHooker(UnityPlayerHandle);
235237
UnityPlayerIATHooker.CreateIATHook("KERNEL32.dll", "ReadFile", thunk =>
236238
{

Il2CppInterop.Runtime/Runtime/UnityVersionHandler.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.Reflection;
55
using Il2CppInterop.Common;
66
using Il2CppInterop.Common.Extensions;
7+
using Il2CppInterop.Runtime.Injection;
78
using Il2CppInterop.Runtime.Runtime.VersionSpecific.Assembly;
89
using Il2CppInterop.Runtime.Runtime.VersionSpecific.AssemblyName;
910
using Il2CppInterop.Runtime.Runtime.VersionSpecific.Class;
@@ -79,6 +80,8 @@ static UnityVersionHandler()
7980
public static bool HasShimForGetMethod { get; private set; }
8081
public static bool IsMetadataV29OrHigher { get; private set; }
8182

83+
public static bool HasScriptAssembliesFile { get; private set; }
84+
8285
// Version since which extra_arg is set to invoke_multicast, necessitating constructor calls
8386
public static bool MustUseDelegateConstructor => IsMetadataV29OrHigher;
8487

@@ -98,6 +101,7 @@ internal static void RecalculateHandlers()
98101

99102
HasGetMethodFromReflection = unityVersion > new Version(2018, 1, 0);
100103
IsMetadataV29OrHigher = unityVersion >= new Version(2021, 2, 0);
104+
HasScriptAssembliesFile = unityVersion >= new Version(2020, 0, 0);
101105

102106
HasShimForGetMethod = unityVersion >= new Version(2020, 3, 41) || IsMetadataV29OrHigher;
103107

@@ -129,6 +133,14 @@ private static Type[] GetAllTypesSafe()
129133
return typeof(UnityVersionHandler).Assembly.GetTypesSafe();
130134
}
131135

136+
internal static IAssemblyListFile GetAssemblyListFile()
137+
{
138+
if (HasScriptAssembliesFile)
139+
return new JSONAssemblyListFile();
140+
141+
return new GameManagersAssemblyListFile();
142+
}
143+
132144
//Assemblies
133145
public static INativeAssemblyStruct NewAssembly()
134146
{

0 commit comments

Comments
 (0)