mirror of
https://github.com/araxiaonline/WoW-Launcher.git
synced 2026-06-13 01:22:21 -04:00
Add registry patch for -launcherlogin usage.
Some code cleanups.
This commit is contained in:
@@ -15,6 +15,7 @@
|
||||
<FileVersion>9.0.0.0</FileVersion>
|
||||
<Copyright>Arctium</Copyright>
|
||||
<Platforms>x64</Platforms>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
@@ -32,12 +33,6 @@
|
||||
<PackageReference Include="Microsoft.DotNet.ILCompiler" Version="7.0.0-*" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="IO\" />
|
||||
<Folder Include="Constants\" />
|
||||
<Folder Include="Misc\" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<TrimmerRootAssembly Include="mscorlib" />
|
||||
<TrimmerRootAssembly Include="System.Runtime" />
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) Arctium.
|
||||
// Licensed under the MIT license. See LICENSE file in the proje root for full license information.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using static Arctium.WoW.Launcher.Misc.NativeWindows;
|
||||
|
||||
@@ -7,24 +7,39 @@ namespace Arctium.WoW.Launcher.IO;
|
||||
|
||||
class WinMemory
|
||||
{
|
||||
public byte[] Data { get; private set; }
|
||||
public byte[] Data { get; set; }
|
||||
|
||||
public nint ProcessHandle { get; }
|
||||
public nint BaseAddress { get; }
|
||||
|
||||
ProcessBasicInformation peb;
|
||||
|
||||
public WinMemory(nint processHandle)
|
||||
public WinMemory(ProcessInformation processInformation, FileInfo fileInfo)
|
||||
{
|
||||
ProcessHandle = processHandle;
|
||||
ProcessHandle = processInformation.ProcessHandle;
|
||||
|
||||
if (processHandle == 0)
|
||||
if (processInformation.ProcessHandle == 0)
|
||||
throw new InvalidOperationException("No valid process found.");
|
||||
|
||||
BaseAddress = ReadImageBaseFromPEB(processHandle);
|
||||
BaseAddress = ReadImageBaseFromPEB(processInformation.ProcessHandle);
|
||||
|
||||
if (BaseAddress == 0)
|
||||
throw new InvalidOperationException("Error while reading PEB data.");
|
||||
|
||||
Data = Read(BaseAddress, (int)fileInfo.Length);
|
||||
}
|
||||
|
||||
public void RefreshMemoryData(int size)
|
||||
{
|
||||
// Reset previous memory data.
|
||||
Data = Array.Empty<byte>();
|
||||
|
||||
while (Data?.Length == 0)
|
||||
{
|
||||
Console.WriteLine("Refreshing client data...");
|
||||
|
||||
Data = Read(BaseAddress, size);
|
||||
}
|
||||
}
|
||||
|
||||
public nint Read(nint address)
|
||||
@@ -88,7 +103,7 @@ class WinMemory
|
||||
return (int)length;
|
||||
}
|
||||
|
||||
public void Write(nint address, byte[] data, MemProtection newProtection = MemProtection.ExecuteReadWrite)
|
||||
public void Write(nint address, byte[] data, MemProtection newProtection = MemProtection.ReadWrite)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -106,7 +121,39 @@ class WinMemory
|
||||
}
|
||||
}
|
||||
|
||||
public void Write(long address, byte[] data, MemProtection newProtection = MemProtection.ExecuteReadWrite) => Write((nint)address, data, newProtection);
|
||||
public void Write(long address, byte[] data, MemProtection newProtection = MemProtection.ReadWrite) => Write((nint)address, data, newProtection);
|
||||
|
||||
public Task PatchMemory(short[] pattern, byte[] patch, string patchName)
|
||||
{
|
||||
Console.WriteLine($"[{patchName}] Patching...");
|
||||
|
||||
long patchOffset = Data.FindPattern(pattern, BaseAddress);
|
||||
|
||||
// No result for the given pattern.
|
||||
if (patchOffset == 0)
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Yellow;
|
||||
|
||||
Console.WriteLine($"[{patchName}] No result found.");
|
||||
Console.WriteLine("Press any key to continue...");
|
||||
Console.ReadKey();
|
||||
}
|
||||
|
||||
while (Read(patchOffset, patch.Length)?.SequenceEqual(patch) == false)
|
||||
Write(patchOffset, patch);
|
||||
|
||||
Console.Write($"[{patchName}]");
|
||||
|
||||
Console.ForegroundColor = ConsoleColor.Green;
|
||||
|
||||
Console.WriteLine(" Done.");
|
||||
|
||||
Console.ForegroundColor = ConsoleColor.Gray;
|
||||
|
||||
Console.WriteLine();
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public bool RemapAndPatch(nint viewAddress, int viewSize, Dictionary<string, (long, byte[])> patches)
|
||||
{
|
||||
@@ -170,10 +217,8 @@ class WinMemory
|
||||
// Unmap them writeable view, it's not longer needed.
|
||||
NtUnmapViewOfSection(ProcessHandle, viewBase2);
|
||||
|
||||
var mbi = new MemoryBasicInformation();
|
||||
|
||||
// Check if the allocation protections is the right one.
|
||||
if (VirtualQueryEx(ProcessHandle, BaseAddress, out mbi, mbi.Size) != 0 && mbi.AllocationProtect == MemProtection.ExecuteRead)
|
||||
if (VirtualQueryEx(ProcessHandle, BaseAddress, out MemoryBasicInformation mbi, MemoryBasicInformation.Size) != 0 && mbi.AllocationProtect == MemProtection.ExecuteRead)
|
||||
{
|
||||
// Also check if we can change the page protection.
|
||||
if (!VirtualProtectEx(ProcessHandle, BaseAddress, 0x4000, (uint)MemProtection.ReadWrite, out var oldProtect))
|
||||
@@ -198,9 +243,7 @@ class WinMemory
|
||||
|
||||
public bool RemapAndPatch(Dictionary<string, (long, byte[])> patches)
|
||||
{
|
||||
var mbi = new MemoryBasicInformation();
|
||||
|
||||
if (VirtualQueryEx(ProcessHandle, BaseAddress, out mbi, mbi.Size) != 0)
|
||||
if (VirtualQueryEx(ProcessHandle, BaseAddress, out MemoryBasicInformation mbi, MemoryBasicInformation.Size) != 0)
|
||||
return RemapAndPatch(mbi.BaseAddress, (int)mbi.RegionSize, patches);
|
||||
|
||||
return false;
|
||||
|
||||
@@ -70,6 +70,8 @@ static class Extensions
|
||||
|
||||
} while ((matchList.Count < maxMatches || match < maxOffset) && match != 0);
|
||||
|
||||
return matchList;
|
||||
}
|
||||
return matchList;
|
||||
}
|
||||
|
||||
public static short[] ToPattern(this string data) => Encoding.UTF8.GetBytes(data).Select(b => (short)b).ToArray();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) Arctium.
|
||||
// Licensed under the MIT license. See LICENSE file in the proje root for full license information.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
namespace Arctium.WoW.Launcher.Patches;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) Arctium.
|
||||
// Licensed under the MIT license. See LICENSE file in the proje root for full license information.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
namespace Arctium.WoW.Launcher.Patches;
|
||||
|
||||
@@ -9,4 +9,7 @@ static class Windows
|
||||
public static byte[] CertBundle = { 0x90, 0x90 };
|
||||
public static byte[] CertCommonName = { 0xB0, 0x01 };
|
||||
public static byte[] ShortJump = { 0xEB };
|
||||
|
||||
// Registry entry used for -launcherlogin.
|
||||
public static byte[] LauncherLogin = Encoding.UTF8.GetBytes(@"Software\Custom Game Server Dev\Battle.net\Launch Options\");
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
// Copyright (c) Arctium.
|
||||
// Licensed under the MIT license. See LICENSE file in the proje root for full license information.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
namespace Arctium.WoW.Launcher.Patterns;
|
||||
|
||||
static class Common
|
||||
{
|
||||
public static short[] CertBundle = Encoding.UTF8.GetBytes("{\"Created\":").Select(b => (short)b).ToArray();
|
||||
public static short[] CertBundle = "{\"Created\":".ToPattern();
|
||||
public static short[] ConnectToModulus = { 0x91, 0xD5, 0x9B, 0xB7, 0xD4, 0xE1, 0x83, 0xA5 };
|
||||
public static short[] SignatureModulus = { 0x35, 0xFF, 0x17, 0xE7, 0x33, 0xC4, 0xD3, 0xD4 };
|
||||
public static short[] ChangeProtocolModulus = { 0x71, 0xFD, 0xFA, 0x60, 0x14, 0x0D, 0xF2, 0x05 };
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) Arctium.
|
||||
// Licensed under the MIT license. See LICENSE file in the proje root for full license information.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
namespace Arctium.WoW.Launcher.Patterns;
|
||||
|
||||
@@ -18,4 +18,7 @@ static class Windows
|
||||
public static short[] CertCommonName = { 0x80, 0xF9, 0x2A, 0x75, -1, 0x32, 0xC0, 0x48, 0x8B };
|
||||
public static short[] CertSignatureMagic = { 0x3B, 0x0D, -1, -1, -1, -1, 0x0F, 0x85, -1, -1, -1, -1, 0x48, 0x8D, 0x15, -1, -1, -1, -1, 0x48, 0x8D, -1, -1, -1, -1, 0x00, 0x00, 0xE8, -1, -1, -1, -1, 0x48 };
|
||||
public static short[] CertSignature = { 0x74, -1, 0x4C, 0x8B, -1, 0x08, 0x48, 0x8B, -1, 0x48, 0x8B, -1, 0x49, 0x81, -1, 0xFC };
|
||||
|
||||
// Registry entry used for -launcherlogin.
|
||||
public static short[] LauncherLogin = @"Software\Blizzard Entertainment\Battle.net\Launch Options\".ToPattern();
|
||||
}
|
||||
|
||||
187
src/Program.cs
187
src/Program.cs
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) Arctium.
|
||||
// Licensed under the MIT license. See LICENSE file in the proje root for full license information.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using static Arctium.WoW.Launcher.Misc.Helpers;
|
||||
|
||||
@@ -12,8 +12,6 @@ class Program
|
||||
static bool keepCache = false;
|
||||
static string consoleArgs = string.Empty;
|
||||
|
||||
static Dictionary<string, (long, byte[])> patches = new();
|
||||
|
||||
static void Main(string[] args)
|
||||
{
|
||||
PrintHeader("WoW Client Launcher");
|
||||
@@ -125,121 +123,28 @@ class Program
|
||||
|
||||
// On some systems we have to launch the game with the application name used.
|
||||
if (!createSuccess)
|
||||
createSuccess = NativeWindows.CreateProcess(appPath, $"- {consoleArgs}", 0, 0, false, 4U, 0, null, ref startupInfo, out processInfo);
|
||||
createSuccess = NativeWindows.CreateProcess(appPath, $" {consoleArgs}", 0, 0, false, 4U, 0, null, ref startupInfo, out processInfo);
|
||||
|
||||
// Start process with suspend flags.
|
||||
if (createSuccess)
|
||||
{
|
||||
var memory = new WinMemory(processInfo.ProcessHandle);
|
||||
var appFileInfo = new FileInfo(appPath);
|
||||
var memory = new WinMemory(processInfo, appFileInfo);
|
||||
|
||||
// Get cert bundle offset from file.
|
||||
var bundleOffset = File.ReadAllBytes(appPath).FindPattern(Patterns.Common.CertBundle, memory.BaseAddress).ToNint();
|
||||
var sectionOffset = memory.Read(bundleOffset, 0x10000).FindPattern(Patterns.Common.CertBundle);
|
||||
byte[] certBundleData = Convert.FromBase64String(Patches.Common.CertBundleData);
|
||||
|
||||
bundleOffset = (bundleOffset + sectionOffset).ToNint();
|
||||
// Build the version URL from the game binary build.
|
||||
int wowBuild = GetVersionValueFromClient(appPath, 0);
|
||||
byte[] versionPatch = Patches.Common.GetVersionUrl(wowBuild);
|
||||
|
||||
var certBundleData = Convert.FromBase64String(Patches.Common.CertBundleData);
|
||||
var originalBundleLength = memory.ReadDataLength(bundleOffset, "NGIS");
|
||||
|
||||
if (originalBundleLength == -1)
|
||||
{
|
||||
Console.WriteLine("Can't get cert bundle data length.");
|
||||
|
||||
WaitAndExit(5000);
|
||||
}
|
||||
|
||||
Console.WriteLine("Patching cert bundle data...");
|
||||
|
||||
// Zero the original cert bundle data.
|
||||
while (memory.Read(bundleOffset, 1)?[0] != 0)
|
||||
memory.Write(bundleOffset, new byte[originalBundleLength + 4 + 256]);
|
||||
|
||||
// Be sure that the modulus is written before the client is initialized.
|
||||
while (memory.Read(bundleOffset, 1)?[0] != certBundleData[0])
|
||||
memory.Write(bundleOffset, certBundleData);
|
||||
|
||||
Console.WriteLine("Done");
|
||||
Console.WriteLine("Patching Signature modulus...");
|
||||
|
||||
// Get ConnectTo RSA modulus offset from file.
|
||||
var modulusOffset = File.ReadAllBytes(appPath).FindPattern(Patterns.Common.SignatureModulus, memory.BaseAddress).ToNint();
|
||||
sectionOffset = memory.Read(modulusOffset, 0x10000).FindPattern(Patterns.Common.SignatureModulus);
|
||||
|
||||
modulusOffset = (modulusOffset + sectionOffset).ToNint();
|
||||
|
||||
// Be sure that the modulus is written before the client is initialized.
|
||||
while (memory.Read(modulusOffset, 1)?[0] != Patches.Common.SignatureModulus[0])
|
||||
memory.Write(modulusOffset, Patches.Common.SignatureModulus);
|
||||
|
||||
Console.WriteLine("Done");
|
||||
Console.WriteLine("Patching ConnectTo modulus...");
|
||||
|
||||
// Get ConnectTo RSA modulus offset from file.
|
||||
modulusOffset = File.ReadAllBytes(appPath).FindPattern(Patterns.Common.ConnectToModulus, memory.BaseAddress).ToNint();
|
||||
sectionOffset = memory.Read(modulusOffset, 0x10000).FindPattern(Patterns.Common.ConnectToModulus);
|
||||
|
||||
modulusOffset = (modulusOffset + sectionOffset).ToNint();
|
||||
|
||||
// Be sure that the modulus is written before the client is initialized.
|
||||
while (memory.Read(modulusOffset, 1)?[0] != Patches.Common.Modulus[0])
|
||||
memory.Write(modulusOffset, Patches.Common.Modulus);
|
||||
|
||||
Console.WriteLine("Done");
|
||||
Console.WriteLine("Patching ChangeProtocol modulus...");
|
||||
|
||||
// Get ChangeProtocol RSA modulus offset from file.
|
||||
modulusOffset = File.ReadAllBytes(appPath).FindPattern(Patterns.Common.ChangeProtocolModulus, memory.BaseAddress).ToNint();
|
||||
sectionOffset = memory.Read(modulusOffset, 0x10000).FindPattern(Patterns.Common.ChangeProtocolModulus);
|
||||
|
||||
modulusOffset = (modulusOffset + sectionOffset).ToNint();
|
||||
|
||||
// Be sure that the modulus is written before the client is initialized.
|
||||
while (memory.Read(modulusOffset, 1)?[0] != Patches.Common.Modulus[0])
|
||||
memory.Write(modulusOffset, Patches.Common.Modulus);
|
||||
|
||||
Console.WriteLine("Done");
|
||||
Console.WriteLine("Patching portal...");
|
||||
|
||||
// Portal patch
|
||||
nint portalOffset = 0;
|
||||
|
||||
// Get portal offset from file.
|
||||
portalOffset = File.ReadAllBytes(appPath).FindPattern(Patterns.Common.Portal, memory.BaseAddress).ToNint();
|
||||
sectionOffset = memory.Read(portalOffset, 0x2000).FindPattern(Patterns.Common.Portal);
|
||||
|
||||
portalOffset = (portalOffset + sectionOffset).ToNint();
|
||||
|
||||
// Be sure that the portal is written before the client is initialized.
|
||||
while (memory.Read(portalOffset, 1)?[0] != Patches.Common.Portal[0])
|
||||
memory.Write(portalOffset, Patches.Common.Portal);
|
||||
|
||||
Console.WriteLine("Done");
|
||||
Console.WriteLine("Patching version url...");
|
||||
|
||||
// Version patch
|
||||
nint versionOffset = 0;
|
||||
|
||||
// Get version offset from file.
|
||||
versionOffset = File.ReadAllBytes(appPath).FindPattern(Patterns.Common.VersionUrl, memory.BaseAddress).ToNint();
|
||||
|
||||
if (versionOffset == 0)
|
||||
{
|
||||
Console.WriteLine($"Can't find {nameof(versionOffset)}. Custom version URL is used!!!");
|
||||
Console.WriteLine("Done.");
|
||||
}
|
||||
else
|
||||
{
|
||||
sectionOffset = memory.Read(versionOffset, 0x2000).FindPattern(Patterns.Common.VersionUrl);
|
||||
versionOffset = (versionOffset + sectionOffset).ToNint();
|
||||
|
||||
var wowBuild = GetVersionValueFromClient(appPath, 0);
|
||||
var versionPatch = Patches.Common.GetVersionUrl(wowBuild);
|
||||
|
||||
while (memory.Read(versionOffset, 1)?[0] != versionPatch[0])
|
||||
memory.Write(versionOffset, versionPatch);
|
||||
|
||||
Console.WriteLine("Done");
|
||||
}
|
||||
// Wait for all direct memory patch tasks to complete,
|
||||
Task.WaitAll(memory.PatchMemory(Patterns.Common.CertBundle, certBundleData, "Certificate Bundle"),
|
||||
memory.PatchMemory(Patterns.Common.SignatureModulus, Patches.Common.SignatureModulus, "Certificate Signature Modulus"),
|
||||
memory.PatchMemory(Patterns.Common.ConnectToModulus, Patches.Common.Modulus, "ConnectTo Modulus"),
|
||||
memory.PatchMemory(Patterns.Common.ChangeProtocolModulus, Patches.Common.Modulus, "ChangeProtocol (GameCrypt) Modulus"),
|
||||
memory.PatchMemory(Patterns.Common.Portal, Patches.Common.Portal, "Login Portal"),
|
||||
memory.PatchMemory(Patterns.Common.VersionUrl, versionPatch, "Version URL"),
|
||||
memory.PatchMemory(Patterns.Windows.LauncherLogin, Patches.Windows.LauncherLogin, "Launcher Login Registry"));
|
||||
|
||||
// Resume the process to initialize it.
|
||||
NativeWindows.NtResumeProcess(processInfo.ProcessHandle);
|
||||
@@ -247,26 +152,20 @@ class Program
|
||||
var mbi = new MemoryBasicInformation();
|
||||
|
||||
// Wait for the memory region to be initialized.
|
||||
while (NativeWindows.VirtualQueryEx(processInfo.ProcessHandle, memory.BaseAddress, out mbi, mbi.Size) == 0 || (int)mbi.RegionSize <= 0x1000)
|
||||
while (NativeWindows.VirtualQueryEx(processInfo.ProcessHandle, memory.BaseAddress, out mbi, MemoryBasicInformation.Size) == 0 || mbi.RegionSize <= 0x1000)
|
||||
{ }
|
||||
|
||||
if (mbi.BaseAddress != 0)
|
||||
{
|
||||
var binary = Array.Empty<byte>();
|
||||
var patches = new Dictionary<string, (long Address, byte[] Data)>();
|
||||
|
||||
PrepareAntiCrash(memory, binary, ref mbi, ref processInfo);
|
||||
PrepareAntiCrash(memory, patches, ref mbi, ref processInfo);
|
||||
|
||||
// Recheck binary data.
|
||||
while (binary?.Length == 0)
|
||||
{
|
||||
Console.WriteLine("Waiting for client data...");
|
||||
|
||||
binary = memory.Read(mbi.BaseAddress, (int)mbi.RegionSize);
|
||||
}
|
||||
memory.RefreshMemoryData((int)mbi.RegionSize);
|
||||
|
||||
// Get patch locations.
|
||||
var certBundleOffset = binary.FindPattern(Patterns.Windows.CertBundle);
|
||||
var certCommonNameOffset = binary.FindPattern(Patterns.Windows.CertCommonName);
|
||||
var certBundleOffset = memory.Data.FindPattern(Patterns.Windows.CertBundle);
|
||||
var certCommonNameOffset = memory.Data.FindPattern(Patterns.Windows.CertCommonName);
|
||||
|
||||
if (certBundleOffset == 0 || certCommonNameOffset == 0)
|
||||
{
|
||||
@@ -322,7 +221,7 @@ class Program
|
||||
}
|
||||
}
|
||||
|
||||
static void PrepareAntiCrash(WinMemory memory, byte[] binary, ref MemoryBasicInformation mbi, ref ProcessInformation processInfo)
|
||||
static void PrepareAntiCrash(WinMemory memory, Dictionary<string, (long, byte[])> patches, ref MemoryBasicInformation mbi, ref ProcessInformation processInfo)
|
||||
{
|
||||
// Wait for client initialization.
|
||||
var initOffset = memory?.Read(mbi.BaseAddress, (int)mbi.RegionSize)?.FindPattern(Patterns.Windows.Init) ?? 0;
|
||||
@@ -338,35 +237,29 @@ class Program
|
||||
|
||||
while (memory?.Read(initOffset + memory.BaseAddress, 1)?[0] == null ||
|
||||
memory?.Read(initOffset + memory.BaseAddress, 1)?[0] == 0)
|
||||
binary = memory.Read(mbi.BaseAddress, (int)mbi.RegionSize);
|
||||
memory.Data = memory.Read(mbi.BaseAddress, (int)mbi.RegionSize);
|
||||
|
||||
// Recheck binary data.
|
||||
while (binary?.Length == 0)
|
||||
{
|
||||
Console.WriteLine("Waiting for client data...");
|
||||
|
||||
binary = memory.Read(mbi.BaseAddress, (int)mbi.RegionSize);
|
||||
}
|
||||
memory.RefreshMemoryData((int)mbi.RegionSize);
|
||||
|
||||
// Suspend the process and handle the patches.
|
||||
NativeWindows.NtSuspendProcess(processInfo.ProcessHandle);
|
||||
|
||||
// Get Integrity check locations
|
||||
var integrityOffsets = binary.FindPattern(Patterns.Windows.Integrity, int.MaxValue, (int)mbi.RegionSize).ToArray();
|
||||
var integrityOffsets = memory.Data.FindPattern(Patterns.Windows.Integrity, int.MaxValue, (int)mbi.RegionSize).ToArray();
|
||||
|
||||
// Encrypt integrity offsets and patches and add them to the patch list.
|
||||
for (var i = 0; i < integrityOffsets.Length; i++)
|
||||
patches[$"Integrity{i}"] = (integrityOffsets[i], Patches.Windows.Integrity);
|
||||
|
||||
// Get Integrity check locations
|
||||
var integrityOffsets2 = binary.FindPattern(Patterns.Windows.Integrity2, int.MaxValue, (int)mbi.RegionSize).ToArray();
|
||||
var integrityOffsets2 = memory.Data.FindPattern(Patterns.Windows.Integrity2, int.MaxValue, (int)mbi.RegionSize).ToArray();
|
||||
|
||||
// Encrypt integrity offsets and patches and add them to the patch list.
|
||||
for (var i = 0; i < integrityOffsets2.Length; i++)
|
||||
patches[$"Integrity{integrityOffsets.Length + i}"] = (integrityOffsets2[i], Patches.Windows.Integrity);
|
||||
|
||||
// Get Remap check locations.
|
||||
var remapOffsets = binary.FindPattern(Patterns.Windows.Remap, int.MaxValue, (int)mbi.RegionSize);
|
||||
var remapOffsets = memory.Data.FindPattern(Patterns.Windows.Remap, int.MaxValue, (int)mbi.RegionSize);
|
||||
var lastAddress = 0;
|
||||
|
||||
foreach (var a in remapOffsets)
|
||||
@@ -375,7 +268,7 @@ class Program
|
||||
var instructionEnd = (int)a + 4 + 6;
|
||||
var instructions = new byte[6];
|
||||
|
||||
Buffer.BlockCopy(binary, instructionStart, instructions, 0, 6);
|
||||
Buffer.BlockCopy(memory.Data, instructionStart, instructions, 0, 6);
|
||||
|
||||
// Skip unconditional jumps.
|
||||
if (memory.IsUnconditionalJump(instructions))
|
||||
@@ -394,32 +287,32 @@ class Program
|
||||
var tempPatches = new ConcurrentDictionary<string, (long, byte[])>();
|
||||
|
||||
// Find all references of real code parts inside the remap check functions.
|
||||
Parallel.For(lastAddress, binary.Length, i =>
|
||||
Parallel.For(lastAddress, memory.Data.Length, i =>
|
||||
{
|
||||
if (memory.IsJump(binary, i))
|
||||
if (memory.IsJump(memory.Data, i))
|
||||
{
|
||||
var jumpOperand = BitConverter.ToInt32(binary, i + 2);
|
||||
var jumpOperand = BitConverter.ToInt32(memory.Data, i + 2);
|
||||
var jumpSize = (int)jumpToValue - i - 6;
|
||||
|
||||
if (jumpOperand == jumpSize)
|
||||
{
|
||||
// Add 1 because we patch the instruction start.
|
||||
// This results in a shorter overall instruction length.
|
||||
var jumpBytes = new byte[] { 0xE9 }.Concat(BitConverter.GetBytes(jumpSize + 1)).ToArray();
|
||||
// Add 1 because we patch the instruction start.
|
||||
// This results in a shorter overall instruction length.
|
||||
var jumpBytes = new byte[] { 0xE9 }.Concat(BitConverter.GetBytes(jumpSize + 1)).ToArray();
|
||||
|
||||
tempPatches.TryAdd($"Jump{i}", (i, jumpBytes));
|
||||
}
|
||||
}
|
||||
else if (memory.IsShortJump(binary, i))
|
||||
else if (memory.IsShortJump(memory.Data, i))
|
||||
{
|
||||
var jumpOperand = binary[i + 1];
|
||||
var jumpOperand = memory.Data[i + 1];
|
||||
var jumpSize = (int)jumpToValue - i - 2;
|
||||
|
||||
if (jumpOperand == jumpSize)
|
||||
{
|
||||
// Check for 0x48 here. This is an indicator for the test instructions.
|
||||
// Might need some better checks or future updates.
|
||||
if (binary[i - 3] == 0x48)
|
||||
// Check for 0x48 here. This is an indicator for the test instructions.
|
||||
// Might need some better checks or future updates.
|
||||
if (memory.Data[i - 3] == 0x48)
|
||||
{
|
||||
var iBytes = BitConverter.GetBytes(i);
|
||||
var jumpBytes = new byte[] { 0xEB };
|
||||
|
||||
@@ -14,5 +14,5 @@ struct MemoryBasicInformation
|
||||
public MemProtection Protect;
|
||||
public MemType Type;
|
||||
|
||||
public int Size => Marshal.SizeOf(typeof(MemoryBasicInformation));
|
||||
public static int Size => Marshal.SizeOf(typeof(MemoryBasicInformation));
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) Arctium.
|
||||
// Licensed under the MIT license. See LICENSE file in the proje root for full license information.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
global using Arctium.WoW.Launcher.Constants;
|
||||
global using Arctium.WoW.Launcher.IO;
|
||||
|
||||
Reference in New Issue
Block a user