Skip to content

Commit 0c11680

Browse files
krulcijs6pak
authored andcommitted
Implemented runtime module dump to support obscure game assembly
1 parent 70c9e1f commit 0c11680

1 file changed

Lines changed: 117 additions & 1 deletion

File tree

Il2CppInterop.Runtime/MemoryUtils.cs

Lines changed: 117 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Diagnostics;
4+
using System.IO;
45
using System.Linq;
56
using System.Runtime.InteropServices;
7+
using System.Text;
68
using Il2CppInterop.Common;
79
using Il2CppInterop.Common.XrefScans;
810
using Microsoft.Extensions.Logging;
911

1012
namespace Il2CppInterop.Runtime;
1113

12-
internal class MemoryUtils
14+
public class MemoryUtils
1315
{
1416
[DllImport("kernel32.dll", SetLastError = true)]
1517
internal static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, [Out] byte[] lpBuffer, int dwSize, out IntPtr lpNumberOfBytesRead);
@@ -94,6 +96,120 @@ public static void SetModuleRegions(List<MEMORY_BASIC_INFORMATION> protectedRegi
9496
}
9597
}
9698

99+
public static void RuntimeModuleDump(ILogger logger, out byte[] il2cppBytes, out byte[] metadataBytes, byte[] metadataSignatureToScan, byte[] magicToFix, int metadataSignatureOffset = 252)
100+
{
101+
Process process = Process.GetCurrentProcess();
102+
var module = process
103+
.Modules.OfType<ProcessModule>()
104+
.Single((x) => x.ModuleName is "GameAssembly.dll" or "GameAssembly.so" or "UserAssembly.dll"); ;
105+
if (module.ModuleName == null)
106+
{
107+
logger.LogError("GameAssembly.dll or GameAssembly.so or UserAssembly.dll not found");
108+
il2cppBytes = [];
109+
metadataBytes = [];
110+
return;
111+
}
112+
var moduleBytes = new byte[module.ModuleMemorySize];
113+
GetModuleRegions(module, out var protectedRegions);
114+
SetModuleRegions(protectedRegions, PAGE_EXECUTE_READWRITE);
115+
if (!ReadProcessMemory(process.Handle, module.BaseAddress, moduleBytes, module.ModuleMemorySize, out _))
116+
{
117+
logger.LogError("Failed to read process memory");
118+
il2cppBytes = [];
119+
metadataBytes = [];
120+
return;
121+
}
122+
SetModuleRegions(protectedRegions);
123+
using (var stream = new MemoryStream(moduleBytes))
124+
using (var reader = new BinaryReader(stream))
125+
using (var writer = new BinaryWriter(stream))
126+
{
127+
// Parse the PE header to get the section headers
128+
stream.Position = 0x3C;
129+
var peHeaderOffset = reader.ReadInt32();
130+
logger.LogDebug("peHeaderOffset: {peHeaderOffset}", peHeaderOffset);
131+
stream.Position = peHeaderOffset + 6;
132+
var numberOfSections = reader.ReadUInt16();
133+
var timeDateStame = reader.ReadUInt32();
134+
var pointerToSymbolTable = reader.ReadUInt32();
135+
var numberOfSymbols = reader.ReadUInt32();
136+
var sizeOfOptionalHeader = reader.ReadUInt16();
137+
var characteristics = reader.ReadUInt16();
138+
var section0StartPosition = (int)stream.Position + sizeOfOptionalHeader;
139+
140+
// Update each section header's PointerToRawData and SizeOfRawData fields
141+
for (var i = 0; i < numberOfSections; i++)
142+
{
143+
logger.LogDebug("numberOfSections: {numberOfSections}", numberOfSections);
144+
stream.Position = section0StartPosition + (i * 40);
145+
logger.LogDebug("stream.Position: {stream.Position}", stream.Position);
146+
var sectionNameBytes = reader.ReadBytes(8);
147+
var sectionName = Encoding.ASCII.GetString(sectionNameBytes).TrimEnd('\0');
148+
logger.LogDebug("sectionName: {sectionName}", sectionName);
149+
var virtualSize = reader.ReadUInt32();
150+
logger.LogDebug("VirtualSize: {virtualSize:X} stream.Position: {stream.Position}", virtualSize, stream.Position);
151+
var virtualAddress = reader.ReadUInt32();
152+
logger.LogDebug("VirtualAddress: {virtualAddress:X} stream.Position: {stream.Position}", virtualAddress, stream.Position);
153+
writer.Write(virtualSize);
154+
logger.LogDebug("Replacing SizeOfRawData with VirtualSize value of {virtualSize:X} stream.Position: {stream.Position}", virtualSize, stream.Position);
155+
writer.Write(virtualAddress);
156+
logger.LogDebug("Replacing SizeOfRawData with VirtualSize value of {virtualAddress:X} stream.Position: {stream.Position}", virtualAddress, stream.Position);
157+
}
158+
}
159+
160+
logger.LogDebug("Processed {module.ModuleName}", module.ModuleName);
161+
il2cppBytes = moduleBytes;
162+
163+
var byteArray = moduleBytes;
164+
165+
// search for pattern in the byte array
166+
var index = Array.IndexOf(byteArray, (byte)metadataSignatureToScan[0]);
167+
while (index >= 0 && index <= byteArray.Length - metadataSignatureToScan.Length)
168+
{
169+
if (byteArray.Skip(index).Take(metadataSignatureToScan.Length).SequenceEqual(metadataSignatureToScan))
170+
{
171+
// pattern found, trim everything before it
172+
var trimmedArray = new byte[byteArray.Length - index + metadataSignatureOffset];
173+
// copy the metadata bytes
174+
Array.Copy(byteArray, index - metadataSignatureOffset, trimmedArray, 0, trimmedArray.Length);
175+
// this is required for il2cppdumper to work
176+
if (magicToFix.Length >= 0)
177+
Array.Copy(magicToFix, 0, trimmedArray, 0, magicToFix.Length);
178+
179+
byteArray = trimmedArray;
180+
break;
181+
}
182+
index = Array.IndexOf(byteArray, (byte)metadataSignatureToScan[0], index + 1);
183+
}
184+
185+
logger.LogDebug("Processed global-metadata.dat");
186+
metadataBytes = byteArray;
187+
return;
188+
}
189+
190+
public static void ValidateMetadata(ILogger logger, string metadataPath, byte[] il2cppBytes, ref byte[] metadataBytes)
191+
{
192+
//metadataBytes will equal il2cppBytes if the search pattern did not match.
193+
//In this case, global-metadata.dat is not embedded in GameAssembly.dll and most likely at the default path.
194+
if (il2cppBytes == metadataBytes)
195+
{
196+
logger.LogWarning("global-metadata.dat is not embedded in GameAssembly.dll.");
197+
if (File.Exists(metadataPath))
198+
{
199+
logger.LogWarning("Found global-metadata.dat at the default path, using it instead.");
200+
metadataBytes = File.ReadAllBytes(metadataPath);
201+
}
202+
else
203+
{
204+
logger.LogWarning("global-meatadata.dat is not found at the default location. " +
205+
"It may be hidden somewhere else. " +
206+
"\n Input the file path: (Example: C:\\Users\\_\\{YourGame}\\fake-global-metadata-name.fakeExtension", null);
207+
metadataPath = Path.Combine(Console.ReadLine() ?? string.Empty);
208+
metadataBytes = File.ReadAllBytes(metadataPath);
209+
}
210+
}
211+
}
212+
97213
public const uint PAGE_EXECUTE_READWRITE = 0x40;
98214

99215
public struct SignatureDefinition

0 commit comments

Comments
 (0)