forked from hackf5/unityspy
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathAssemblyImageFactory.cs
More file actions
206 lines (186 loc) · 9.44 KB
/
AssemblyImageFactory.cs
File metadata and controls
206 lines (186 loc) · 9.44 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
namespace HackF5.UnitySpy
{
using System;
using System.IO;
using System.Runtime.InteropServices;
using HackF5.UnitySpy.Detail;
using HackF5.UnitySpy.Offsets;
using HackF5.UnitySpy.ProcessFacade;
using HackF5.UnitySpy.Util;
using JetBrains.Annotations;
/// <summary>
/// A factory that creates <see cref="IAssemblyImage"/> instances that provides access into a Unity application's
/// managed memory.
/// SEE: https://github.com/Unity-Technologies/mono.
/// </summary>
[PublicAPI]
public static class AssemblyImageFactory
{
public static IAssemblyImage Create([NotNull] UnityProcessFacade process, string assemblyName = "Assembly-CSharp")
{
if (process == null)
{
throw new ArgumentNullException("process parameter cannot be null");
}
var monoModule = process.GetMonoModule();
IntPtr rootDomainFunctionAddress;
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
rootDomainFunctionAddress = GetRootDomainFunctionAddressMachOFormat(monoModule);
}
else
{
var moduleDump = process.ReadModule(monoModule);
rootDomainFunctionAddress = AssemblyImageFactory.GetRootDomainFunctionAddressPEFormat(moduleDump, monoModule, process.Is64Bits);
}
return AssemblyImageFactory.GetAssemblyImage(process, assemblyName, rootDomainFunctionAddress);
}
private static AssemblyImage GetAssemblyImage(UnityProcessFacade process, string name, IntPtr rootDomainFunctionAddress)
{
IntPtr domain;
if (process.Is64Bits)
{
int ripPlusOffsetOffset;
int ripValueOffset;
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
// Offsets taken by decompiling the 64 bits version of libmonobdwgc-2.0.dylib
//
// push rbp
// mov rbp,rsp
// mov rax, [rip + 0x4250ba]
// pop rbp
// ret
//
// These five lines in Hex translate to
// 55
// 4889E5
// 488B05 BA5042 00
// 5D
// C3
//
// So wee need to offset the first seven bytes to get to the relative offset we need to add to rip
// rootDomainFunctionAddress + 7
//
// rip has the current value of the rootDoaminAddress plus the 4 bytes of the first two instructions
// plus the 7 bytes of the rip + offset instruction (mov rax, [rip + 0x4250ba]).
// then we need to add this offsets to get the domain starting address
ripPlusOffsetOffset = 7;
ripValueOffset = 11;
}
else
{
// Offsets taken by decompiling the 64 bits version of mono-2.0-bdwgc.dll
//
// mov rax, [rip + 0x46ad39]
// ret
//
// These two lines in Hex translate to
// 488B05 39AD46 00
// C3
//
// So wee need to offset the first three bytes to get to the relative offset we need to add to rip
// rootDomainFunctionAddress + 3
//
// rip has the current value of the rootDoaminAddress plus the 7 bytes of the first instruction (mov rax, [rip + 0x46ad39])
// then we need to add this offsets to get the domain starting address
ripPlusOffsetOffset = 3;
ripValueOffset = 7;
}
var offset = process.ReadInt32(rootDomainFunctionAddress + ripPlusOffsetOffset) + ripValueOffset;
//// pointer to struct of type _MonoDomain
domain = process.ReadPtr(rootDomainFunctionAddress + offset);
}
else
{
var domainAddress = process.ReadPtr(rootDomainFunctionAddress + 1);
//// pointer to struct of type _MonoDomain
domain = process.ReadPtr(domainAddress);
}
//// pointer to array of structs of type _MonoAssembly
var assemblyArrayAddress = process.ReadPtr(domain + process.MonoLibraryOffsets.ReferencedAssemblies);
for (var assemblyAddress = assemblyArrayAddress;
assemblyAddress != IntPtr.Zero;
assemblyAddress = process.ReadPtr(assemblyAddress + process.SizeOfPtr))
{
var assembly = process.ReadPtr(assemblyAddress);
var assemblyNameAddress = process.ReadPtr(assembly + (process.SizeOfPtr * 2));
var assemblyName = process.ReadAsciiString(assemblyNameAddress);
if (assemblyName == name)
{
return new AssemblyImage(process, process.ReadPtr(assembly + process.MonoLibraryOffsets.AssemblyImage));
}
}
throw new InvalidOperationException($"Unable to find assembly '{name}'");
}
private static IntPtr GetRootDomainFunctionAddressPEFormat(byte[] moduleDump, ModuleInfo monoModuleInfo, bool is64Bits)
{
// offsets taken from https://docs.microsoft.com/en-us/windows/desktop/Debug/pe-format
// ReSharper disable once CommentTypo
var startIndex = moduleDump.ToInt32(PEFormatOffsets.Signature); // lfanew
var exportDirectoryIndex = startIndex + PEFormatOffsets.GetExportDirectoryIndex(is64Bits);
var exportDirectory = moduleDump.ToInt32(exportDirectoryIndex);
var numberOfFunctions = moduleDump.ToInt32(exportDirectory + PEFormatOffsets.NumberOfFunctions);
var functionAddressArrayIndex = moduleDump.ToInt32(exportDirectory + PEFormatOffsets.FunctionAddressArrayIndex);
var functionNameArrayIndex = moduleDump.ToInt32(exportDirectory + PEFormatOffsets.FunctionNameArrayIndex);
var rootDomainFunctionAddress = IntPtr.Zero;
for (var functionIndex = 0;
functionIndex < numberOfFunctions * PEFormatOffsets.FunctionEntrySize;
functionIndex += PEFormatOffsets.FunctionEntrySize)
{
var functionNameIndex = moduleDump.ToInt32(functionNameArrayIndex + functionIndex);
var functionName = moduleDump.ToAsciiString(functionNameIndex);
if (functionName == "mono_get_root_domain")
{
rootDomainFunctionAddress = monoModuleInfo.BaseAddress
+ moduleDump.ToInt32(functionAddressArrayIndex + functionIndex);
break;
}
}
if (rootDomainFunctionAddress == IntPtr.Zero)
{
throw new InvalidOperationException("Failed to find mono_get_root_domain function.");
}
return rootDomainFunctionAddress;
}
private static IntPtr GetRootDomainFunctionAddressMachOFormat(ModuleInfo monoModuleInfo)
{
var rootDomainFunctionAddress = IntPtr.Zero;
byte[] moduleFromPath = File.ReadAllBytes(monoModuleInfo.Path);
int numberOfCommands = moduleFromPath.ToInt32(MachOFormatOffsets.NumberOfCommands);
int offsetToNextCommand = MachOFormatOffsets.LoadCommands;
for (int i = 0; i < numberOfCommands; i++)
{
// Check if load command is LC_SYMTAB
if (moduleFromPath.ToInt32(offsetToNextCommand) == 2)
{
int symbolTableOffset = moduleFromPath.ToInt32(offsetToNextCommand + MachOFormatOffsets.SymbolTableOffset);
int numberOfSymbols = moduleFromPath.ToInt32(offsetToNextCommand + MachOFormatOffsets.NumberOfSymbols);
int stringTableOffset = moduleFromPath.ToInt32(offsetToNextCommand + MachOFormatOffsets.StringTableOffset);
for (int j = 0; j < numberOfSymbols; j++)
{
int symbolNameOffset = moduleFromPath.ToInt32(symbolTableOffset + (j * MachOFormatOffsets.SizeOfNListItem));
var symbolName = moduleFromPath.ToAsciiString(stringTableOffset + symbolNameOffset);
if (symbolName == "_mono_get_root_domain")
{
rootDomainFunctionAddress = monoModuleInfo.BaseAddress
+ moduleFromPath.ToInt32(symbolTableOffset + (j * MachOFormatOffsets.SizeOfNListItem)
+ MachOFormatOffsets.NListValue);
break;
}
}
break;
}
else
{
offsetToNextCommand += moduleFromPath.ToInt32(offsetToNextCommand + MachOFormatOffsets.CommandSize);
}
}
if (rootDomainFunctionAddress == IntPtr.Zero)
{
throw new InvalidOperationException("Failed to find mono_get_root_domain function.");
}
return rootDomainFunctionAddress;
}
}
}