diff --git a/Patcher/.gitignore b/Patcher/.gitignore
new file mode 100644
index 0000000..9c9348a
--- /dev/null
+++ b/Patcher/.gitignore
@@ -0,0 +1,334 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
+
+# User-specific files
+*.rsuser
+*.suo
+# *.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+
+# Visual Studio 2015/2017 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# Visual Studio 2017 auto generated files
+Generated\ Files/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUNIT
+*.VisualState.xml
+TestResult.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# Benchmark Results
+BenchmarkDotNet.Artifacts/
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+
+# StyleCop
+StyleCopReport.xml
+
+# Files built by Visual Studio
+*_i.c
+*_p.c
+*_h.h
+*.ilk
+*.meta
+*.obj
+*.iobj
+*.pch
+*.pdb
+*.ipdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*_wpftmp.csproj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# Visual Studio Trace Files
+*.e2e
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# JustCode is a .NET coding add-in
+.JustCode
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# AxoCover is a Code Coverage Tool
+.axoCover/*
+!.axoCover/settings.json
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# Note: Comment the next line if you want to checkin your web deploy settings,
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# The packages folder can be ignored because of Package Restore
+**/[Pp]ackages/*
+# except build/, which is used as an MSBuild target.
+!**/[Pp]ackages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/[Pp]ackages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+*.appx
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+orleans.codegen.cs
+
+# Including strong name files can present a security risk
+# (https://github.com/github/gitignore/pull/2483#issue-259490424)
+#*.snk
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+ServiceFabricBackup/
+*.rptproj.bak
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+*.rptproj.rsuser
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# JetBrains Rider
+.idea/
+*.sln.iml
+
+# CodeRush personal settings
+.cr/personal
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Tabs Studio
+*.tss
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
+
+# OpenCover UI analysis results
+OpenCover/
+
+# Azure Stream Analytics local run output
+ASALocalRun/
+
+# MSBuild Binary and Structured Log
+*.binlog
+
+# NVidia Nsight GPU debugger configuration file
+*.nvuser
+
+# MFractors (Xamarin productivity tool) working folder
+.mfractor/
+
+# Local History for Visual Studio
+.localhistory/
diff --git a/Patcher/AutoPatcher/App.config b/Patcher/AutoPatcher/App.config
new file mode 100644
index 0000000..88fa402
--- /dev/null
+++ b/Patcher/AutoPatcher/App.config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Patcher/AutoPatcher/ArmDisassembler.cs b/Patcher/AutoPatcher/ArmDisassembler.cs
new file mode 100644
index 0000000..5c5be96
--- /dev/null
+++ b/Patcher/AutoPatcher/ArmDisassembler.cs
@@ -0,0 +1,301 @@
+// Copyright (c) 2018, Rene Lergner - wpinternals.net - @Heathcliff74xda
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Gee.External.Capstone;
+using Gee.External.Capstone.Arm;
+//using PeNet;
+
+namespace Patcher
+{
+ public static class ArmDisassembler
+ {
+ public static AnalyzedFile Analyze(string FilePath, string AsmPath = null)
+ {
+ PeFile File = new(FilePath);
+
+ SortedList AnalyzedCode = new(0x1000000); // Default capacity of 0x100000 was not enough for analyzing ntoskrnl.exe
+
+ if ((AsmPath != null) && System.IO.File.Exists(AsmPath))
+ {
+ using StreamReader Reader = new(AsmPath);
+ while (Reader.Peek() >= 0)
+ {
+ ArmInstruction Instruction = new(Reader.ReadLine());
+ AnalyzedCode.Add(Instruction.Address, Instruction);
+ }
+ }
+ else
+ {
+ CapstoneArmDisassembler Disassembler = CapstoneDisassembler.CreateArmDisassembler(ArmDisassembleMode.Thumb);
+
+ // Initially use a Dictionary and sort it afterwards. For analyzing ntoskrnl.exe this is about 60 times faster than using a SortedList from the start.
+ // Default capacity of 0x100000 was not enough for analyzing ntoskrnl.exe
+ Dictionary TempCode = new(0x1000000);
+
+ // Analyze from entrypoint
+ Analyze(Disassembler, File.Sections, TempCode, (UInt32)(File.ImageBase + File.EntryPoint));
+
+ // Analyze from exports
+ foreach (FunctionDescriptor Function in File.Exports)
+ Analyze(Disassembler, File.Sections, TempCode, (UInt32)Function.VirtualAddress);
+
+ // Analyze from imports
+ foreach (FunctionDescriptor Function in File.Imports)
+ Analyze(Disassembler, File.Sections, TempCode, (UInt32)Function.VirtualAddress);
+
+ // Analyze from runtime-functions
+ foreach (FunctionDescriptor Function in File.RuntimeFunctions)
+ Analyze(Disassembler, File.Sections, TempCode, (UInt32)Function.VirtualAddress);
+
+ // Sort the instructions.
+ // SortedList is used, because it can be indexed by value (not only by key).
+ List Keys = TempCode.Keys.ToList();
+ Keys.Sort();
+ foreach (UInt32 Key in Keys)
+ AnalyzedCode.Add(Key, TempCode[Key]);
+
+ if (AsmPath != null)
+ {
+ System.IO.Directory.CreateDirectory(System.IO.Path.GetDirectoryName(AsmPath));
+
+ using StreamWriter Writer = new(AsmPath, false);
+ for (int i = 0; i < AnalyzedCode.Count; i++)
+ {
+ Writer.WriteLine(AnalyzedCode.Values[i].ToString());
+ }
+ }
+ }
+
+ return new AnalyzedFile() { File = File, Code = AnalyzedCode };
+ }
+
+ public static void Analyze(CapstoneArmDisassembler Disassembler, List Sections, Dictionary AnalyzedCode, UInt32 VirtualAddress)
+ {
+ VirtualAddress -= (VirtualAddress % 2);
+ List AddressesToAnalyze = new();
+ AddressesToAnalyze.Add(VirtualAddress);
+ Section CurrentSection = null;
+
+ while (AddressesToAnalyze.Count > 0)
+ {
+ UInt32 CurrentAddress = AddressesToAnalyze[0];
+ if ((CurrentSection == null) || (CurrentAddress < CurrentSection.VirtualAddress) || (CurrentAddress > (CurrentSection.VirtualAddress + CurrentSection.VirtualSize)))
+ {
+ CurrentSection = Sections.Find(s => (CurrentAddress >= s.VirtualAddress) && (CurrentAddress < (s.VirtualAddress + s.VirtualSize)) && s.IsCode);
+ if (CurrentSection == null)
+ {
+ // throw new Exception("Address 0x" + CurrentAddress.ToString("X8") + " is not inside boundaries of code-sections");
+ // Probably jumped to this address because data was disassembled as if it were code. Ignore this.
+ // return;
+ AddressesToAnalyze.RemoveAt(0);
+ continue;
+ }
+ }
+
+ if (AnalyzedCode.ContainsKey(CurrentAddress))
+ {
+ // return;
+ AddressesToAnalyze.RemoveAt(0);
+ continue;
+ }
+
+ Gee.External.Capstone.Arm.ArmInstruction[] NewInstructions = Disassembler.Disassemble(CurrentSection.Buffer.Skip((int)CurrentAddress - (int)CurrentSection.VirtualAddress).ToArray(), CurrentAddress);
+ if (NewInstructions.Any())
+ {
+ UInt32 StartAddress = (UInt32)NewInstructions.First().Address;
+ UInt32 EndAddress = (UInt32)NewInstructions.Last().Address;
+
+ ArmInstruction PreviousInstruction = null;
+ foreach (Gee.External.Capstone.Arm.ArmInstruction DisassemblerInstruction in NewInstructions)
+ {
+ // ArmInstruction Instruction = new ArmInstruction(DisassemblerInstruction);
+ ArmInstruction Instruction = new()
+ {
+ Address = (UInt32)DisassemblerInstruction.Address,
+ Bytes = DisassemblerInstruction.Bytes,
+ Mnemonic = DisassemblerInstruction.Mnemonic,
+ Operand = DisassemblerInstruction.Operand.Replace("sb", "r9").Replace("sl", "r10").Replace("fp", "r11").Replace("ip", "r12")
+ };
+
+ if (AnalyzedCode.ContainsKey((UInt32)Instruction.Address))
+ break;
+
+ // Merge movw + movt into one command
+ // movw r3, #0x6010 + movt r3, #0x1000 = mov r3, #0x10006010
+ UInt32 HighPart, LowPart;
+ string HighString, LowString;
+ if ((PreviousInstruction?.Mnemonic == "movt") && (Instruction.Mnemonic == "movw") && (PreviousInstruction.Operand.Split(new char[] { ',' })[0] == Instruction.Operand.Split(new char[] { ',' })[0]))
+ {
+ byte[] Combined = new byte[8];
+ System.Buffer.BlockCopy(PreviousInstruction.Bytes, 0, Combined, 0, 4);
+ System.Buffer.BlockCopy(Instruction.Bytes, 0, Combined, 4, 4);
+ PreviousInstruction.Bytes = Combined;
+ PreviousInstruction.Mnemonic = "mov";
+
+ HighString = PreviousInstruction.Operand[(PreviousInstruction.Operand.IndexOf('#') + 1)..];
+ HighPart = (HighString.Length >= 2) && (HighString.Substring(0, 2) == "0x")
+ ? UInt32.Parse(HighString[2..], System.Globalization.NumberStyles.HexNumber)
+ : UInt32.Parse(HighString);
+ LowString = Instruction.Operand[(Instruction.Operand.IndexOf('#') + 1)..];
+ LowPart = (LowString.Length >= 2) && (LowString.Substring(0, 2) == "0x")
+ ? UInt32.Parse(LowString[2..], System.Globalization.NumberStyles.HexNumber)
+ : UInt32.Parse(LowString);
+ PreviousInstruction.Operand = string.Concat(PreviousInstruction.Operand.AsSpan(0, PreviousInstruction.Operand.IndexOf('#') + 1), "0x", ((HighPart << 16) + LowPart).ToString("X8"));
+ continue;
+ }
+ if ((PreviousInstruction?.Mnemonic == "movw") && (Instruction.Mnemonic == "movt") && (PreviousInstruction.Operand.Split(new char[] { ',' })[0] == Instruction.Operand.Split(new char[] { ',' })[0]))
+ {
+ byte[] Combined = new byte[8];
+ System.Buffer.BlockCopy(PreviousInstruction.Bytes, 0, Combined, 0, 4);
+ System.Buffer.BlockCopy(Instruction.Bytes, 0, Combined, 4, 4);
+ PreviousInstruction.Bytes = Combined;
+ PreviousInstruction.Mnemonic = "mov";
+
+ HighString = Instruction.Operand[(Instruction.Operand.IndexOf('#') + 1)..];
+ HighPart = (HighString.Length >= 2) && (HighString.Substring(0, 2) == "0x")
+ ? UInt32.Parse(HighString[2..], System.Globalization.NumberStyles.HexNumber)
+ : UInt32.Parse(HighString);
+ LowString = PreviousInstruction.Operand[(PreviousInstruction.Operand.IndexOf('#') + 1)..];
+ LowPart = (LowString.Length >= 2) && (LowString.Substring(0, 2) == "0x")
+ ? UInt32.Parse(LowString[2..], System.Globalization.NumberStyles.HexNumber)
+ : UInt32.Parse(LowString);
+ PreviousInstruction.Operand = string.Concat(PreviousInstruction.Operand.AsSpan(0, PreviousInstruction.Operand.IndexOf('#') + 1), "0x", ((HighPart << 16) + LowPart).ToString("X8"));
+ continue;
+ }
+
+ AnalyzedCode.Add((UInt32)Instruction.Address, Instruction);
+
+ int IndexOfIndirectConstant = Instruction.Operand.IndexOf("[pc, #0x");
+ if (IndexOfIndirectConstant >= 0)
+ {
+ int IndexOfEnd = Instruction.Operand.IndexOf("]", IndexOfIndirectConstant);
+ string PCOffsetString = Instruction.Operand.Substring(IndexOfIndirectConstant + 8, IndexOfEnd - IndexOfIndirectConstant - 8);
+ UInt32 PCOffset = UInt32.Parse(PCOffsetString, System.Globalization.NumberStyles.HexNumber);
+ UInt32 PC = (UInt32)Instruction.Address + 4;
+ UInt32 PCforIndirect = PC - (PC % 4);
+ UInt32 VirtualAddressOfIndirectConstant = PCforIndirect + PCOffset;
+
+ // If the address is outside the range of the section, then this is probably data which is compiled as code.
+ // In this case we will ignore this and not do this part of the analysis.
+ if ((VirtualAddressOfIndirectConstant >= CurrentSection.VirtualAddress) && (VirtualAddressOfIndirectConstant < (CurrentSection.VirtualAddress + CurrentSection.VirtualSize)))
+ {
+ UInt32 RawOffsetOfIndirectConstant = VirtualAddressOfIndirectConstant - CurrentSection.VirtualAddress;
+ UInt32 IndirectConstant = BitConverter.ToUInt32(CurrentSection.Buffer, (int)RawOffsetOfIndirectConstant);
+ Instruction.Operand = Instruction.Operand.Substring(0, IndexOfIndirectConstant) + "#0x" + IndirectConstant.ToString("x8") + Instruction.Operand[(IndexOfEnd + 1)..];
+ }
+ }
+
+ if (JumpCommands.Contains(Instruction.Mnemonic))
+ {
+ UInt32 NewAddress = UInt32.Parse(Instruction.Operand[(Instruction.Operand.IndexOf("#0x") + 3)..], System.Globalization.NumberStyles.HexNumber);
+ NewAddress -= (NewAddress % 2);
+ if (((NewAddress < StartAddress) || (NewAddress > EndAddress)) && !AddressesToAnalyze.Any(a => a == NewAddress))
+ AddressesToAnalyze.Add(NewAddress);
+ }
+
+ PreviousInstruction = Instruction;
+ }
+ }
+
+ AddressesToAnalyze.RemoveAt(0);
+ }
+ }
+
+ public static string[] JumpCommands = new string[]
+ {
+ "b", "b.w", "bl", "bl.w", "beq", "beq.w", "bne", "bne.w", "bhs", "bhs.w", "blo", "blo.w",
+ "bmi", "bmi.w", "bpl", "bpl.w", "bvs", "bvs.w", "bvc", "bvc.w", "bhi", "bhi.w", "bls", "bls.w",
+ "bge", "bge.w", "blt", "blt.w", "bgt", "bgt.w", "ble", "ble.w", "bal", "bal.w", "cbnz", "cbz"
+ };
+
+ public static string[] ConditionalJumpInstructions = new string[]
+ {
+ "beq", "beq.w", "bne", "bne.w", "bhs", "bhs.w", "blo", "blo.w", "bmi", "bmi.w",
+ "bpl", "bpl.w", "bvs", "bvs.w", "bvc", "bvc.w", "bhi", "bhi.w", "bls", "bls.w",
+ "bge", "bge.w", "blt", "blt.w", "bgt", "bgt.w", "ble", "ble.w", "bal", "bal.w", "cbnz", "cbz"
+ };
+
+ public static string WriteCode(SortedDictionary AnalyzedCode)
+ {
+ StringBuilder Code = new(1000);
+
+ foreach (var Instruction in AnalyzedCode)
+ {
+ Code.AppendFormat("{0:X}: \t {1} \t {2}\r\n", Instruction.Value.Address, Instruction.Value.Mnemonic, Instruction.Value.Operand);
+ }
+
+ return Code.ToString();
+ }
+ }
+
+ public class ArmInstruction
+ {
+ public UInt32 Address;
+ public byte[] Bytes;
+ public string Mnemonic;
+ public string Operand;
+
+ public ArmInstruction()
+ {
+ }
+
+ public ArmInstruction(string Assembly)
+ {
+ Address = UInt32.Parse(Assembly.Substring(0, 8), System.Globalization.NumberStyles.HexNumber);
+ string Hex = Assembly.Substring(12, 24).Trim();
+ Bytes = new byte[(Hex.Length + 1) / 3];
+ for (int i = 0; i < Bytes.Length; i++)
+ Bytes[i] = byte.Parse(Hex.Substring(i * 3, 2), System.Globalization.NumberStyles.HexNumber);
+ Mnemonic = Assembly.Substring(39, 16).Trim();
+ Operand = Assembly[55..];
+ }
+
+ public override string ToString()
+ {
+ StringBuilder Result = new();
+
+ Result.Append(Address.ToString("X8")); // 0
+ Result.Append(" ");
+ for (int i = 0; i < Bytes.Length; i++) // 12
+ {
+ Result.Append(Bytes[i].ToString("X2"));
+ Result.Append(' ');
+ }
+ Result.Append(new String(' ', (8 - Bytes.Length) * 3));
+ Result.Append(" ");
+ Result.Append(Mnemonic.PadRight(16)); // 39
+ Result.Append(Operand); // 55
+
+ return Result.ToString();
+ }
+ }
+
+ public class AnalyzedFile
+ {
+ public PeFile File;
+ public SortedList Code;
+ }
+}
diff --git a/Patcher/AutoPatcher/AutoPatcher.csproj b/Patcher/AutoPatcher/AutoPatcher.csproj
new file mode 100644
index 0000000..210c5f2
--- /dev/null
+++ b/Patcher/AutoPatcher/AutoPatcher.csproj
@@ -0,0 +1,56 @@
+
+
+ net5.0-windows
+ WinExe
+ Patcher
+ false
+ true
+
+
+ bin\x86\Debug\
+ MinimumRecommendedRules.ruleset
+
+
+ bin\x86\Release\
+ MinimumRecommendedRules.ruleset
+
+
+ bin\x64\Debug\
+ MinimumRecommendedRules.ruleset
+
+
+ bin\x64\Release\
+ MinimumRecommendedRules.ruleset
+
+
+
+ ArmCompiler.cs
+
+
+ ByteOperations.cs
+
+
+ HelperClasses.cs
+
+
+ ObjectFileParser.cs
+
+
+ PatchEngine.cs
+
+
+ PeFile.cs
+
+
+ LICENSE
+
+
+ Always
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Patcher/AutoPatcher/AutoPatcher.csproj.user b/Patcher/AutoPatcher/AutoPatcher.csproj.user
new file mode 100644
index 0000000..6696a72
--- /dev/null
+++ b/Patcher/AutoPatcher/AutoPatcher.csproj.user
@@ -0,0 +1,9 @@
+
+
+
+
+
+ Form
+
+
+
\ No newline at end of file
diff --git a/Patcher/AutoPatcher/BootUnllockAndRootAccessPatchScript.pds b/Patcher/AutoPatcher/BootUnllockAndRootAccessPatchScript.pds
new file mode 100644
index 0000000..f2d6e75
--- /dev/null
+++ b/Patcher/AutoPatcher/BootUnllockAndRootAccessPatchScript.pds
@@ -0,0 +1,698 @@
+// Copyright (c) 2018, Rene Lergner - wpinternals.net - @Heathcliff74xda
+//
+// Patch Definition Script for Boot Unlock and Root Access on Windows Mobile
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+
+PatchDefinition Name="RootAccess-MainOS" VersionFrom="EFIESP\Windows\System32\Boot\mobilestartup.efi"
+
+ PatchFile Path="Windows\System32\sspisrv.dll"
+
+ JumpToImport "RpcImpersonateClient"
+ JumpToReference
+ FindPreviousInstruction "PUSH.W"
+ CreateLabel "CheckLowboxAccess" // Optional here
+ PatchCode
+ MOVS R1, #1
+ STR R1, [R0]
+ MOVS R0, #0
+ BX LR
+ EndPatch
+ PatchChecksum
+
+ PatchFile Path="Windows\System32\NtlmShared.dll"
+
+ JumpToExport "MsvpPasswordValidate"
+ PatchCode
+ MOVS R0, #1
+ BX LR
+ EndPatch
+ PatchChecksum
+
+ PatchFile Path="Windows\System32\pacmanserver.dll"
+
+ FindFirstUnicode "GetMaxCountForDeployedApp"
+ JumpToReference
+ FindPreviousInstruction "PUSH.W"
+ PatchCode
+ LDR R1, =0x7FFFFFFF
+ STR R1, [R0]
+ MOVS R0, #0
+ BX LR
+ EndPatch
+ PatchChecksum
+
+ PatchFile Path="Windows\System32\mscoree.dll"
+
+ JumpToImport "GetModuleFileNameW"
+ JumpToReference
+ FindPreviousInstruction "PUSH.W"
+ CreateLabel "CompareWithWhiteList" // Optional here
+ PatchCode
+ MOVS R0, #0
+ BX LR
+ EndPatch
+ PatchChecksum
+
+ PatchFile Path="Windows\System32\DeploymentExt.dll"
+
+ FindFirstUnicode "MaxUnsignedApp"
+ JumpToReference
+ FindValue 0x800413A0
+ FindPreviousConditionalJump
+ MakeJumpUnconditional
+ PatchChecksum
+
+ PatchFile Path="Windows\System32\ntoskrnl.exe"
+
+ // Fase 1: find all kernel-functions
+
+ JumpToExport "SeAccessCheckWithHint"
+ CreateLabel "SeAccessCheckWithHint"
+
+ FindFunctionCall R0 = "ADD R0, SP, #0x7C" R1 = "MOV R1, R?"
+ JumpToTarget
+ CreateLabel "SepFilterToDiscretionary"
+
+ JumpToReference R0 = "ADDS R0, R?, #0xD0"
+ FindPreviousInstruction "PUSH"
+ FindPreviousInstruction "PUSH"
+ CreateLabel "SeAccessCheckByType"
+
+ FindFunctionCall R0 = "ADDS R0, R?, #0xF8" R1 = "MOV R1, R?" R2 = "LDR R2, [R?,#0x28]" R3 = "MOV R3, R?"
+ JumpToTarget
+ CreateLabel "SepConstrainByMandatory"
+
+ JumpBack // to SeAccessCheckByType
+ JumpBack // to SepFilterToDiscretionary
+
+ JumpToReference R1 = "LDR R1, [R?,#8]"
+ FindPreviousInstruction "PUSH"
+ CreateLabel "SepCommonAccessCheckEx"
+
+ FindFunctionCall Result = "STR R0, [SP,#0xD4]"
+ JumpToTarget
+ CreateLabel "SepAccessCheckEx"
+
+ JumpBack // to SepCommonAccessCheckEx
+ JumpBack // to SepFilterToDiscretionary
+
+ JumpToReference R0 = "ADDS R0, R?, #0x130"
+ FindPreviousInstruction "PUSH"
+ FindPreviousInstruction "PUSH"
+ CreateLabel "SepAccessCheckAndAuditAlarm"
+
+ FindFunctionCall R0 = "LDR R0, [R?,#0x130]" R1 = "MOV R1, R?" R2 = "LDR R2, [R?,#0x50]" R3 = "MOV R3, R?"
+ JumpToTarget
+ CreateLabel "SepConstrainByConstraintMask"
+ FindNextConditionalJump
+ JumpToTarget
+ CreateLabel "SepConstrainByConstraintMask_FunctionChunk01"
+
+ JumpBack // to SepConstrainByConstraintMask
+ JumpBack // to SepAccessCheckAndAuditAlarm
+ JumpBack // to SepFilterToDiscretionary
+ JumpBack // to SeAccessCheckWithHint
+
+ FindFunctionCall R0 = "ADD R0, SP, #0x88" R1 = "MOV R1, R?"
+ JumpToTarget
+ CreateLabel "SepMandatoryToDiscretionary"
+ JumpBack
+
+ FindFunctionCall Result = "STR R0, [SP,#0x70]"
+ JumpToTarget
+ CreateLabel "SepAccessCheck"
+
+ JumpToExport "SePrivilegeCheck"
+ FindFunctionCall
+ JumpToTarget
+ CreateLabel "SepPrivilegeCheck"
+
+ JumpToExport "SeSinglePrivilegeCheck"
+ CreateLabel "SeSinglePrivilegeCheck"
+
+ JumpToExport "ObReferenceObjectByHandleWithTag"
+ CreateLabel "ObReferenceObjectByHandleWithTag"
+
+ // Fase 2: patches
+
+ JumpToLabel "SeAccessCheckByType"
+
+ // Patch 1:
+ FindNextValue 0xC0000022
+ FindPreviousConditionalJump
+ FindPreviousConditionalJump
+ FindPreviousConditionalJump
+ FindPreviousConditionalJump
+ MakeJumpUnconditional
+ FindNextValue 0xC0000022
+
+ // Patch 2:
+ FindNextValue 0xC0000022
+ FindStore
+ FindPreviousConditionalJump
+ MakeJumpUnconditional
+
+ // Patch 3:
+ FindNextValue 0xC0000022
+ FindPreviousConditionalJump
+ MakeJumpUnconditional // This jump is right above the value 0xC0000022. After patch the pointer is back on that value.
+ // FindNextValue 0xC0000022
+
+ // Patch 4:
+ FindNextValue 0xC0000022
+ FindPreviousConditionalJump
+ MakeJumpUnconditional // This jump is right above the value 0xC0000022. After patch the pointer is back on that value.
+ // FindNextValue 0xC0000022
+
+ // Patch 5:
+ FindNextValue 0xC0000022
+ FindNextInstruction "BNE"
+ JumpToTarget
+ CreateLabel "TargetPatch5"
+ JumpBack
+ FindPreviousInstruction "BEQ"
+ PatchCode
+ B TargetPatch5
+ EndPatch
+
+ // Patch 6:
+ FindNextValue 0xC0000022
+ FindNextConditionalJump
+ MakeJumpUnconditional
+
+ // Patch 7:
+ FindNextValue 0xC0000022
+ FindStore
+ FindPreviousConditionalJump
+ MakeJumpUnconditional
+
+ // Patch 8:
+ FindNextValue 0xC0000022
+ JumpToReference
+ ClearInstruction
+ JumpBack
+
+ // Patch 9:
+ FindNextValue 0xC0000022
+ JumpToReference
+ ClearInstruction
+ JumpBack
+
+ JumpToLabel "SepAccessCheckAndAuditAlarm"
+
+ // Patch 10:
+ FindNextValue 0xC0000022
+ FindPreviousConditionalJump
+ MakeJumpUnconditional
+ FindNextValue 0xC0000022
+
+ // Patch 11:
+ FindNextValue 0xC0000022
+ FindStore
+ CreateLabel "Patch11"
+ FindNextConditionalJump
+ JumpToTarget
+ CreateLabel "TargetPatch11"
+ JumpToLabel "Patch11"
+ PatchCode
+ B TargetPatch11
+ EndPatch
+
+ // Patch 12:
+ FindNextValue 0xC0000022
+ PatchCode
+ MOV.W R2, #0
+ EndPatch
+
+ JumpToLabel "SepCommonAccessCheckEx"
+
+ // Patch 13:
+ FindNextInstruction "TST"
+ FindNextInstruction "TST"
+ FindPreviousConditionalJump
+ ClearInstruction
+
+ JumpToLabel "SeAccessCheckWithHint"
+
+ // Patch 14:
+ FindNextInstruction "BEQ"
+ MakeJumpUnconditional
+
+ JumpToLabel "SeSinglePrivilegeCheck"
+
+ // Patch 15:
+ PatchCode
+ MOVS R0, #1
+ BX LR
+ EndPatch
+
+ JumpToLabel "ObReferenceObjectByHandleWithTag"
+
+ FindFunctionCall
+ JumpToTarget
+ CreateLabel "ObpReferenceObjectByHandleWithTag"
+ FindInstructionPattern "LDR R?, [R?,#0x74]; CMP R?, #0; BNE ?" InstructionIndex = 2
+ JumpToTarget
+
+ // Patch 16:
+ FindNextConditionalJump
+ MakeJumpUnconditional // This jump is right above the value 0xC0000022. After patch the pointer is on the error-value.
+
+ // Patch 17:
+ JumpToReference
+ ClearInstruction
+ JumpBack
+ JumpBack
+
+ // Patch 18:
+ FindNextValue 0xC0000022
+ JumpToReference
+ ClearInstruction
+
+ JumpToLabel "SepPrivilegeCheck"
+
+ // Patch 19:
+ PatchCode
+ MOVS R0, #1
+ BX LR
+ EndPatch
+
+ JumpToLabel "SepMandatoryToDiscretionary"
+
+ // Patch 20:
+ PatchCode
+ MOVS R0, #0
+ BX LR
+ EndPatch
+
+ JumpToLabel "SepAccessCheckEx"
+
+ // Patch 21:
+ FindNextValue 0x2000000
+ CreateLabel "Patch21"
+ FindNextInstruction "B"
+ JumpToTarget
+ CreateLabel "TargetPatch21"
+ JumpToLabel "Patch21"
+ PatchCode
+ B TargetPatch21
+ EndPatch
+ FindNextValue 0xC0000022
+
+ // Patch 22:
+ FindNextValue 0xC0000022
+ FindPreviousConditionalJump
+ MakeJumpUnconditional // This jump is right above the value 0xC0000022. After patch the pointer is back on that value.
+ // FindNextValue 0xC0000022
+
+ // Patch 23:
+ JumpToReference 0
+ ClearInstruction
+ JumpBack
+
+ // Patch 24:
+ JumpToReference 1
+ ClearInstruction
+ JumpBack
+
+ // Patch 25:
+ JumpToReference 2
+ ClearInstruction
+ JumpBack
+
+ // Patch 26:
+ FindNextValue 0xC0000022
+ FindPreviousConditionalJump
+ MakeJumpUnconditional
+ FindNextValue 0xC0000022
+
+ // Patch 27:
+ FindNextValue 0xC0000022
+ FindPreviousConditionalJump
+ MakeJumpUnconditional
+ FindNextValue 0xC0000022
+
+ // Patch 28:
+ JumpToReference
+ ClearInstruction
+
+ JumpToLabel "SepAccessCheck"
+
+ // Patch 29:
+ FindFunctionCall R0 = "LDR R0, [SP,#0x28]"
+ JumpToTarget
+ CreateLabel "SepNormalAccessCheck"
+ JumpBack
+ FindNextInstruction "TST"
+ FindNextConditionalJump
+ ClearInstruction
+
+ // Patch 30:
+ FindFunctionCall R0 = "MOV R0, R?" R1 = "MOV R1, R?" R2 = "MOV R2, R?" R3 = "LDR R3, [SP,#0x38]"
+ JumpToTarget
+ CreateLabel "SepMaximumAccessCheck"
+ JumpBack
+ FindNextConditionalJump
+ ClearInstruction
+
+ // Patch 31:
+ FindNextConditionalJump
+ ClearInstruction
+
+ // Patch 32:
+ FindNextValue 0xC0000022
+ JumpToReference 1
+ ClearInstruction
+ JumpBack
+
+ // Patch 33:
+ JumpToReference 2
+ ClearInstruction
+ JumpBack
+
+ // Patch 34:
+ FindNextValue 0xC0000022
+ FindPreviousInstruction "MOVS"
+ FindPreviousInstruction "MOVS"
+ JumpToReference
+ ClearInstruction
+ JumpBack
+ FindNextValue 0xC0000022
+
+ // Patch 35:
+ JumpToReference CodePattern = "BEQ"
+ ClearInstruction
+ JumpBack
+
+ // Patch 36:
+ JumpToReference CodePattern = "MOVS; B"
+ FindPreviousInstruction "B"
+ JumpToTarget
+ CreateLabel "TargetPatch36"
+ JumpBack
+ FindPreviousInstruction "CMP"
+ PatchCode
+ B.W TargetPatch36
+ EndPatch
+ JumpBack
+
+ // Patch 37:
+ JumpToReference CodePattern = "STR; B"
+ FindPreviousConditionalJump
+ MakeJumpUnconditional
+
+ // Patch 38:
+ // Stay in function-chunk. Error-code is between previous two patches.
+ FindPreviousValue 0xC0000022
+ FindPreviousConditionalJump
+ MakeJumpUnconditional
+
+ JumpToLabel "SepConstrainByMandatory"
+
+ // Patch 39:
+ FindNextInstruction "BNE"
+ JumpToTarget
+ FindNextInstruction "CBNZ"
+ JumpToTarget
+ CreateLabel "TargetPatch39"
+ JumpBack
+ FindPreviousInstruction "BEQ"
+ PatchCode
+ B TargetPatch39
+ EndPatch
+ JumpBack
+
+ // Patch 40:
+ FindNextInstruction "B"
+ JumpToTarget
+ FindNextInstruction "CBNZ"
+ JumpToTarget
+ CreateLabel "TargetPatch40"
+ JumpBack
+ FindPreviousInstruction "BEQ"
+ PatchCode
+ B TargetPatch40
+ EndPatch
+
+ JumpToLabel "SepFilterToDiscretionary"
+
+ // Patch 41:
+ PatchCode
+ MOVS R0, #0
+ BX LR
+ EndPatch
+
+ JumpToLabel "SepConstrainByConstraintMask_FunctionChunk01"
+
+ // Patch 42:
+ FindNextInstruction "TST"
+ FindNextInstruction "CBNZ"
+ JumpToTarget
+ CreateLabel "TargetPatch42"
+ JumpBack
+ FindPreviousInstruction "BEQ"
+ PatchCode
+ B TargetPatch42
+ EndPatch
+
+ // Patch 43:
+ FindNextInstruction "TST"
+ FindNextInstruction "CBNZ"
+ JumpToTarget
+ CreateLabel "TargetPatch43"
+ JumpBack
+ FindPreviousInstruction "BEQ"
+ FindPreviousInstruction "BEQ" // This one is actually not necessary. Kept here for consistency.
+ PatchCode
+ B TargetPatch43
+ EndPatch
+
+ PatchChecksum
+
+PatchDefinition Name="SecureBootHack-MainOS" VersionFrom="EFIESP\Windows\System32\Boot\mobilestartup.efi"
+
+ PatchFile Path="Windows\System32\BOOT\winload.efi"
+
+ FindFirstAscii "1.3.6.1.4.1.311.61.4.1"
+ JumpToReference
+ FindPreviousInstruction "PUSH.W"
+ CreateLabel "ImgpValidateImageHash"
+ PatchCode
+ MOVS R0, #0
+ BX LR
+ EndPatch
+ PatchChecksum
+
+ PatchFile Path="Windows\System32\ci.dll"
+
+ JumpToImport "PsGetProcessSignatureLevel"
+ JumpToReference
+ CreateLabel "PsGetProcessSignatureLevelWrapper"
+ JumpToReference
+ FindPreviousInstruction "PUSH.W"
+ CreateLabel "CipReportAndReprieveUMCIFailure"
+ FindNextInstruction "TST.W"
+ FindNextConditionalJump
+ MakeJumpUnconditional "BNE" // BNE -> B, BEQ -> NOP
+ PatchChecksum
+
+PatchDefinition Name="SecureBootHack-V1-EFIESP" VersionFrom="EFIESP\Windows\System32\Boot\mobilestartup.efi" RelativePath="EFIESP" RelativeOutputPath="SecureBootHack-V1"
+
+ PatchFile Path="Windows\System32\boot\mobilestartup.efi" // Symbols taken from pdb from version 10.0.10586.107
+
+ FindFirstAscii "1.3.6.1.4.1.311.61.4.1"
+ JumpToReference
+ FindPreviousInstruction "PUSH.W"
+ CreateLabel "ImgpValidateImageHash"
+ PatchCode
+ MOVS R0, #0
+ BX LR
+ EndPatch
+ FindFirstUnicode "BootDebugPolicyApplied"
+ JumpToReference
+ FindPreviousInstruction "PUSH.W"
+ CreateLabel "ApplyBootDebugPolicy"
+ PatchCode // This patch is for the new unlock for Lumia Spec A
+ MOVS R0, #0
+ BX LR
+ EndPatch
+ PatchChecksum
+
+ PatchFile Path="efi\boot\bootarm.efi"
+
+ FindFirstAscii "1.3.6.1.4.1.311.61.4.1"
+ JumpToReference
+ FindPreviousInstruction "PUSH.W"
+ CreateLabel "ImgpValidateImageHash"
+ PatchCode
+ MOVS R0, #0
+ BX LR
+ EndPatch
+ PatchChecksum
+
+PatchDefinition Name="SecureBootHack-V2-EFIESP" VersionFrom="EFIESP\Windows\System32\Boot\mobilestartup.efi" RelativePath="EFIESP"
+
+ PatchFile Path="Windows\System32\boot\mobilestartup.efi"
+
+ FindFirstAscii "MZ"
+ CreateLabel "ImageBase"
+ FindFirstAscii "1.3.6.1.4.1.311.61.4.1"
+ JumpToReference
+ FindPreviousInstruction "PUSH.W"
+ CreateLabel "ImgpValidateImageHash"
+ PatchCode
+ MOVS R0, #0
+ BX LR
+ EndPatch
+ FindFirstUnicode "BootDebugPolicyApplied"
+ JumpToReference
+ FindPreviousInstruction "PUSH.W"
+ CreateLabel "ApplyBootDebugPolicy"
+ PatchCode
+ MOVS R0, #0
+ BX LR
+ EndPatch
+ CreateLabel "EnterMassStorageModeShellCode" // Use the left-over space of the ApplyBootDebugPolicy-function to insert shell-code later on
+ FindFirstUnicode "MassStorageFlag"
+ CreateLabel "MassStorageName"
+ PatchUnicode "Heathcliff74MSM"
+ FindFirstBytes "41 E5 C1 A0 CE 73 7F 46 88 EC D4 4F 92 34 50 4A"
+ CreateLabel "MassStorageGuid"
+ JumpToLabel "MassStorageName"
+ JumpToReference
+ FindNextInstruction "BL"
+ JumpToTarget
+ CreateLabel "EfiGetVariableVolatile"
+ FindValue 2
+ FindNextConditionalJump
+ MakeJumpUnconditional "BEQ"
+ FindFirstUnicode "\Windows\System32\boot\ui\boot.ums.waiting.bmpx"
+ JumpToReference
+ FindPreviousInstruction "PUSH.W"
+ CreateLabel "EnterMassStorageMode"
+ JumpToReference
+ PatchCode
+ B.W EnterMassStorageModeShellCode
+ EndPatch
+ CreateLabel ReturnFromMassStorageMode
+ FindFirstValue 0x26000145
+ IfNotFoundGo PatchForSetErrorDone
+ FindPreviousInstruction "PUSH.W"
+ CreateLabel "SetError"
+ PatchCode
+ MOVS R0, #1
+ BX LR
+ EndPatch
+ PatchForSetErrorDone:
+ FindFirstUnicode "DeviceIDVersion"
+ JumpToReference
+ FindNextInstruction "BL"
+ JumpToTarget
+ CreateLabel "EfiSetVariable"
+ FindFirstAscii "charge: DisplayPowerState protocol successfully loaded"
+ JumpToReference
+ FindPreviousInstruction "PUSH.W"
+ CreateLabel "InitGraphicsSubsystem"
+ FindNextInstruction "BL"
+ JumpToTarget
+ CreateLabel "BlpArchQueryCurrentContextType"
+ JumpBack
+ FindNextInstruction "BL"
+ FindNextInstruction "BL"
+ FindNextInstruction "BL"
+ JumpToTarget
+ CreateLabel "BlpArchSwitchContext"
+ JumpBack
+ FindNextInstruction "LDR"
+ JumpToTarget
+ CreateLabel "EfiBS"
+ JumpToLabel "EnterMassStorageModeShellCode"
+ PatchCode
+ MOV R0, PC
+ LDR R1, =(ApplyBootDebugPolicy - ImageBase + 8) // Subtract (Offset of shell-code + 4)
+ SUB R0, R0, R1 // R0 = relocated base of mobilestartup.efi
+ PUSH {R4-R6}
+ SUB SP, SP, #4
+ MOV R4, R0 // R4 = relocated base of mobilestartup.efi
+
+ LDR R3, =(MassStorageName - ImageBase) // Offset of NV var name (which is patched to "Heathcliff74MSM")
+ ADD R0, R4, R3
+ LDR R3, =(MassStorageGuid - ImageBase) // Offset of NV var Guid
+ ADD R1, R4, R3
+ MOVS R2, #3 // Non-volatile, boot-services
+ MOVS R3, #0 // Data-size
+ STR R3, [SP] // Pointer to data-buffer = NULL
+ LDR R6, =(EfiSetVariable - ImageBase + 1) // Offset of SetVariable + 1
+ ADD R5, R4, R6
+ BLX R5 // EfiSetVariable -> Delete variable
+
+ LDR R1, =(BlpArchQueryCurrentContextType - ImageBase + 1) // Offset to first thread-function + 1
+ ADD R5, R4, R1
+ BLX R5
+ MOV R6, R0
+ CMP R6, #1
+ BEQ ContextSwitchDone1
+ MOVS R0, #1
+ LDR R1, =(BlpArchSwitchContext - ImageBase + 1) // Offset to second thread-function + 1
+ ADD R5, R4, R1
+ BLX R5
+ ContextSwitchDone1:
+
+ LDR R0, =(EfiBS - ImageBase) // Offset of pointer to BootServices function-table
+ ADD R1, R4, R0 // R1 = pointer to pointer to BootServices function-table
+ LDR R1, [R1] // R1 = pointer to BootServices function-table
+ LDR.W R5, [R1,#0xAC] // LocateProtocol
+ ADR R0, VarServicesGuid // This is relative, no need to relocate
+ MOVS R1, #0
+ MOV R2, SP
+ BLX R5 // LocateProtocol - pVarServices in [SP]
+ LDR R5, [SP] // R5 = Pointer to VariableServices interface
+ LDR R5, [R5,#4] // R5 = pointer to FlushVariableNV()
+ CMP R5, #0
+ BNE PointerFound
+ LDR R5, [SP] // R5 = Pointer to VariableServices interface
+ LDR R5, [R5,#8] // R5 = pointer to FlushVariableNV()
+ PointerFound:
+ BLX R5 // FlushVariableNV()
+
+ CMP R6, #1
+ BEQ ContextSwitchDone2
+ MOV R0, R6
+ LDR R1, =(BlpArchSwitchContext - ImageBase + 1) // Offset to second thread-function + 1
+ ADD R5, R4, R1
+ BLX R5
+ ContextSwitchDone2:
+
+ LDR R6, =(EnterMassStorageMode - ImageBase + 1) // Offset of EnterMassStorageMode + 1
+ ADD R5, R4, R6
+ BLX R5 // EnterMassStorageMode
+
+ LDR R6, =(ReturnFromMassStorageMode - ImageBase + 1) // Offset of return address + 1
+ ADD R0, R4, R6
+ ADD SP, SP, #4
+ POP {R4-R6}
+ BX R0
+
+ VarServicesGuid:
+ DCD 0xf9085b9d
+ DCW 0x9304, 0x40fb
+ DCB 0x8f, 0xe0, 0x4a, 0xee, 0x3b, 0x1a, 0x78, 0x4b
+ EndPatch
+ PatchChecksum
diff --git a/Patcher/AutoPatcher/FolderSelectDialog.cs b/Patcher/AutoPatcher/FolderSelectDialog.cs
new file mode 100644
index 0000000..01aeb3e
--- /dev/null
+++ b/Patcher/AutoPatcher/FolderSelectDialog.cs
@@ -0,0 +1,120 @@
+// This class was found online.
+// Original author is probably: Swizzy
+// https://github.com/ttgxdinger/Random/blob/master/CPUKey%20Checker/CPUKey%20Checker/FolderSelectDialog.cs
+
+using System;
+using System.Windows.Forms;
+
+namespace WPinternals
+{
+ ///
+ /// Wraps System.Windows.Forms.OpenFileDialog to make it present
+ /// a vista-style dialog.
+ ///
+ public class FolderSelectDialog
+ {
+ // Wrapped dialog
+ private readonly OpenFileDialog ofd = null;
+
+ ///
+ /// Default constructor
+ ///
+ public FolderSelectDialog()
+ {
+ ofd = new OpenFileDialog
+ {
+ Filter = "Folders|\n",
+ AddExtension = false,
+ CheckFileExists = false,
+ DereferenceLinks = true,
+ Multiselect = false
+ };
+ }
+
+ #region Properties
+
+ ///
+ /// Gets/Sets the initial folder to be selected. A null value selects the current directory.
+ ///
+ public string InitialDirectory
+ {
+ get { return ofd.InitialDirectory; }
+ set { ofd.InitialDirectory = string.IsNullOrEmpty(value) ? Environment.CurrentDirectory : value; }
+ }
+
+ ///
+ /// Gets/Sets the title to show in the dialog
+ ///
+ public string Title
+ {
+ get { return ofd.Title; }
+ set { ofd.Title = value ?? "Select a folder"; }
+ }
+
+ ///
+ /// Gets the selected folder
+ ///
+ public string FileName
+ {
+ get { return ofd.FileName; }
+ }
+
+ #endregion
+
+ #region Methods
+
+ ///
+ /// Shows the dialog
+ ///
+ /// True if the user presses OK else false
+ public bool ShowDialog()
+ {
+ return ShowDialog(IntPtr.Zero);
+ }
+
+ ///
+ /// Shows the dialog
+ ///
+ /// Handle of the control to be parent
+ /// True if the user presses OK else false
+ public bool ShowDialog(IntPtr hWndOwner)
+ {
+ var fbd = new FolderBrowserDialog
+ {
+ Description = this.Title,
+ SelectedPath = this.InitialDirectory,
+ ShowNewFolderButton = false
+ };
+ if (fbd.ShowDialog(new WindowWrapper(hWndOwner)) != DialogResult.OK)
+ {
+ return false;
+ }
+
+ ofd.FileName = fbd.SelectedPath;
+
+ return true;
+ }
+
+ #endregion
+ }
+
+ ///
+ /// Creates IWin32Window around an IntPtr
+ ///
+ public class WindowWrapper : IWin32Window
+ {
+ ///
+ /// Constructor
+ ///
+ /// Handle to wrap
+ public WindowWrapper(IntPtr handle)
+ {
+ Handle = handle;
+ }
+
+ ///
+ /// Original ptr
+ ///
+ public IntPtr Handle { get; }
+ }
+}
diff --git a/Patcher/AutoPatcher/MainForm.Designer.cs b/Patcher/AutoPatcher/MainForm.Designer.cs
new file mode 100644
index 0000000..1c19cd8
--- /dev/null
+++ b/Patcher/AutoPatcher/MainForm.Designer.cs
@@ -0,0 +1,423 @@
+namespace Patcher
+{
+ partial class MainForm
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ this.label1 = new System.Windows.Forms.Label();
+ this.txtVisualStudioPath = new System.Windows.Forms.TextBox();
+ this.FolderBrowserDialog = new System.Windows.Forms.FolderBrowserDialog();
+ this.OpenFileDialog = new System.Windows.Forms.OpenFileDialog();
+ this.SaveFileDialog = new System.Windows.Forms.SaveFileDialog();
+ this.cmdVisualStudioPath = new System.Windows.Forms.Button();
+ this.cmdInputFolder = new System.Windows.Forms.Button();
+ this.txtInputFolder = new System.Windows.Forms.TextBox();
+ this.label2 = new System.Windows.Forms.Label();
+ this.cmdOutputFolder = new System.Windows.Forms.Button();
+ this.txtOutputFolder = new System.Windows.Forms.TextBox();
+ this.label3 = new System.Windows.Forms.Label();
+ this.cmdPatchDefinitionsFile = new System.Windows.Forms.Button();
+ this.txtPatchDefinitionsFile = new System.Windows.Forms.TextBox();
+ this.label4 = new System.Windows.Forms.Label();
+ this.txtConsole = new System.Windows.Forms.TextBox();
+ this.label9 = new System.Windows.Forms.Label();
+ this.cmdCompile = new System.Windows.Forms.Button();
+ this.cmdPatch = new System.Windows.Forms.Button();
+ this.cmdScriptFile = new System.Windows.Forms.Button();
+ this.txtScriptFile = new System.Windows.Forms.TextBox();
+ this.label5 = new System.Windows.Forms.Label();
+ this.cmdBackupFolder = new System.Windows.Forms.Button();
+ this.txtBackupFolder = new System.Windows.Forms.TextBox();
+ this.label6 = new System.Windows.Forms.Label();
+ this.label7 = new System.Windows.Forms.Label();
+ this.CapstoneLink = new System.Windows.Forms.LinkLabel();
+ this.label8 = new System.Windows.Forms.Label();
+ this.CapstoneNetLink = new System.Windows.Forms.LinkLabel();
+ this.label10 = new System.Windows.Forms.Label();
+ this.SuspendLayout();
+ //
+ // label1
+ //
+ this.label1.AutoSize = true;
+ this.label1.Location = new System.Drawing.Point(15, 13);
+ this.label1.Name = "label1";
+ this.label1.Size = new System.Drawing.Size(191, 13);
+ this.label1.TabIndex = 0;
+ this.label1.Text = "Path to Visual Studio with ARM32 SDK";
+ //
+ // txtVisualStudioPath
+ //
+ this.txtVisualStudioPath.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.txtVisualStudioPath.Location = new System.Drawing.Point(18, 29);
+ this.txtVisualStudioPath.Name = "txtVisualStudioPath";
+ this.txtVisualStudioPath.Size = new System.Drawing.Size(665, 20);
+ this.txtVisualStudioPath.TabIndex = 1;
+ //
+ // OpenFileDialog
+ //
+ this.OpenFileDialog.FileName = "openFileDialog1";
+ //
+ // cmdVisualStudioPath
+ //
+ this.cmdVisualStudioPath.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.cmdVisualStudioPath.Font = new System.Drawing.Font("Arial", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.cmdVisualStudioPath.Location = new System.Drawing.Point(689, 28);
+ this.cmdVisualStudioPath.Name = "cmdVisualStudioPath";
+ this.cmdVisualStudioPath.Size = new System.Drawing.Size(35, 22);
+ this.cmdVisualStudioPath.TabIndex = 2;
+ this.cmdVisualStudioPath.Text = "...";
+ this.cmdVisualStudioPath.UseVisualStyleBackColor = true;
+ this.cmdVisualStudioPath.Click += new System.EventHandler(this.cmdVisualStudioPath_Click);
+ //
+ // cmdInputFolder
+ //
+ this.cmdInputFolder.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.cmdInputFolder.Font = new System.Drawing.Font("Arial", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.cmdInputFolder.Location = new System.Drawing.Point(689, 224);
+ this.cmdInputFolder.Name = "cmdInputFolder";
+ this.cmdInputFolder.Size = new System.Drawing.Size(35, 22);
+ this.cmdInputFolder.TabIndex = 8;
+ this.cmdInputFolder.Text = "...";
+ this.cmdInputFolder.UseVisualStyleBackColor = true;
+ this.cmdInputFolder.Click += new System.EventHandler(this.cmdInputFolder_Click);
+ //
+ // txtInputFolder
+ //
+ this.txtInputFolder.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.txtInputFolder.Location = new System.Drawing.Point(18, 225);
+ this.txtInputFolder.Name = "txtInputFolder";
+ this.txtInputFolder.Size = new System.Drawing.Size(665, 20);
+ this.txtInputFolder.TabIndex = 7;
+ //
+ // label2
+ //
+ this.label2.AutoSize = true;
+ this.label2.Location = new System.Drawing.Point(15, 209);
+ this.label2.Name = "label2";
+ this.label2.Size = new System.Drawing.Size(71, 13);
+ this.label2.TabIndex = 12;
+ this.label2.Text = "Input location";
+ //
+ // cmdOutputFolder
+ //
+ this.cmdOutputFolder.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.cmdOutputFolder.Font = new System.Drawing.Font("Arial", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.cmdOutputFolder.Location = new System.Drawing.Point(689, 322);
+ this.cmdOutputFolder.Name = "cmdOutputFolder";
+ this.cmdOutputFolder.Size = new System.Drawing.Size(35, 22);
+ this.cmdOutputFolder.TabIndex = 12;
+ this.cmdOutputFolder.Text = "...";
+ this.cmdOutputFolder.UseVisualStyleBackColor = true;
+ this.cmdOutputFolder.Click += new System.EventHandler(this.cmdOutputFolder_Click);
+ //
+ // txtOutputFolder
+ //
+ this.txtOutputFolder.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.txtOutputFolder.Location = new System.Drawing.Point(18, 323);
+ this.txtOutputFolder.Name = "txtOutputFolder";
+ this.txtOutputFolder.Size = new System.Drawing.Size(665, 20);
+ this.txtOutputFolder.TabIndex = 11;
+ //
+ // label3
+ //
+ this.label3.AutoSize = true;
+ this.label3.Location = new System.Drawing.Point(15, 307);
+ this.label3.Name = "label3";
+ this.label3.Size = new System.Drawing.Size(125, 13);
+ this.label3.TabIndex = 15;
+ this.label3.Text = "Output location (optional)";
+ //
+ // cmdPatchDefinitionsFile
+ //
+ this.cmdPatchDefinitionsFile.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.cmdPatchDefinitionsFile.Font = new System.Drawing.Font("Arial", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.cmdPatchDefinitionsFile.Location = new System.Drawing.Point(689, 91);
+ this.cmdPatchDefinitionsFile.Name = "cmdPatchDefinitionsFile";
+ this.cmdPatchDefinitionsFile.Size = new System.Drawing.Size(35, 22);
+ this.cmdPatchDefinitionsFile.TabIndex = 4;
+ this.cmdPatchDefinitionsFile.Text = "...";
+ this.cmdPatchDefinitionsFile.UseVisualStyleBackColor = true;
+ this.cmdPatchDefinitionsFile.Click += new System.EventHandler(this.cmdPatchDefinitionsFile_Click);
+ //
+ // txtPatchDefinitionsFile
+ //
+ this.txtPatchDefinitionsFile.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.txtPatchDefinitionsFile.Location = new System.Drawing.Point(18, 92);
+ this.txtPatchDefinitionsFile.Name = "txtPatchDefinitionsFile";
+ this.txtPatchDefinitionsFile.Size = new System.Drawing.Size(665, 20);
+ this.txtPatchDefinitionsFile.TabIndex = 3;
+ this.txtPatchDefinitionsFile.Leave += new System.EventHandler(this.txtPatchDefinitionsFile_Leave);
+ //
+ // label4
+ //
+ this.label4.AutoSize = true;
+ this.label4.Location = new System.Drawing.Point(15, 76);
+ this.label4.Name = "label4";
+ this.label4.Size = new System.Drawing.Size(117, 13);
+ this.label4.TabIndex = 3;
+ this.label4.Text = "Patch defintions xml-file";
+ //
+ // txtConsole
+ //
+ this.txtConsole.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+ | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.txtConsole.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.txtConsole.Location = new System.Drawing.Point(18, 383);
+ this.txtConsole.Multiline = true;
+ this.txtConsole.Name = "txtConsole";
+ this.txtConsole.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
+ this.txtConsole.Size = new System.Drawing.Size(706, 392);
+ this.txtConsole.TabIndex = 13;
+ //
+ // label9
+ //
+ this.label9.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.label9.AutoSize = true;
+ this.label9.Location = new System.Drawing.Point(15, 367);
+ this.label9.Name = "label9";
+ this.label9.Size = new System.Drawing.Size(78, 13);
+ this.label9.TabIndex = 24;
+ this.label9.Text = "Console output";
+ //
+ // cmdCompile
+ //
+ this.cmdCompile.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+ this.cmdCompile.Location = new System.Drawing.Point(470, 792);
+ this.cmdCompile.Name = "cmdCompile";
+ this.cmdCompile.Size = new System.Drawing.Size(120, 33);
+ this.cmdCompile.TabIndex = 14;
+ this.cmdCompile.Text = "Compile";
+ this.cmdCompile.UseVisualStyleBackColor = true;
+ this.cmdCompile.Click += new System.EventHandler(this.cmdCompile_Click);
+ //
+ // cmdPatch
+ //
+ this.cmdPatch.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+ this.cmdPatch.Location = new System.Drawing.Point(604, 792);
+ this.cmdPatch.Name = "cmdPatch";
+ this.cmdPatch.Size = new System.Drawing.Size(120, 33);
+ this.cmdPatch.TabIndex = 15;
+ this.cmdPatch.Text = "Patch";
+ this.cmdPatch.UseVisualStyleBackColor = true;
+ this.cmdPatch.Click += new System.EventHandler(this.cmdPatch_Click);
+ //
+ // cmdScriptFile
+ //
+ this.cmdScriptFile.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.cmdScriptFile.Font = new System.Drawing.Font("Arial", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.cmdScriptFile.Location = new System.Drawing.Point(689, 155);
+ this.cmdScriptFile.Name = "cmdScriptFile";
+ this.cmdScriptFile.Size = new System.Drawing.Size(35, 22);
+ this.cmdScriptFile.TabIndex = 6;
+ this.cmdScriptFile.Text = "...";
+ this.cmdScriptFile.UseVisualStyleBackColor = true;
+ this.cmdScriptFile.Click += new System.EventHandler(this.cmdScriptFile_Click);
+ //
+ // txtScriptFile
+ //
+ this.txtScriptFile.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.txtScriptFile.Location = new System.Drawing.Point(18, 156);
+ this.txtScriptFile.Name = "txtScriptFile";
+ this.txtScriptFile.Size = new System.Drawing.Size(665, 20);
+ this.txtScriptFile.TabIndex = 5;
+ //
+ // label5
+ //
+ this.label5.AutoSize = true;
+ this.label5.Location = new System.Drawing.Point(15, 140);
+ this.label5.Name = "label5";
+ this.label5.Size = new System.Drawing.Size(50, 13);
+ this.label5.TabIndex = 28;
+ this.label5.Text = "Script-file";
+ //
+ // cmdBackupFolder
+ //
+ this.cmdBackupFolder.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.cmdBackupFolder.Font = new System.Drawing.Font("Arial", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.cmdBackupFolder.Location = new System.Drawing.Point(689, 273);
+ this.cmdBackupFolder.Name = "cmdBackupFolder";
+ this.cmdBackupFolder.Size = new System.Drawing.Size(35, 22);
+ this.cmdBackupFolder.TabIndex = 10;
+ this.cmdBackupFolder.Text = "...";
+ this.cmdBackupFolder.UseVisualStyleBackColor = true;
+ this.cmdBackupFolder.Click += new System.EventHandler(this.cmdBackupFolder_Click);
+ //
+ // txtBackupFolder
+ //
+ this.txtBackupFolder.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.txtBackupFolder.Location = new System.Drawing.Point(18, 274);
+ this.txtBackupFolder.Name = "txtBackupFolder";
+ this.txtBackupFolder.Size = new System.Drawing.Size(665, 20);
+ this.txtBackupFolder.TabIndex = 9;
+ //
+ // label6
+ //
+ this.label6.AutoSize = true;
+ this.label6.Location = new System.Drawing.Point(15, 258);
+ this.label6.Name = "label6";
+ this.label6.Size = new System.Drawing.Size(130, 13);
+ this.label6.TabIndex = 31;
+ this.label6.Text = "Backup location (optional)";
+ //
+ // label7
+ //
+ this.label7.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+ this.label7.AutoSize = true;
+ this.label7.Location = new System.Drawing.Point(20, 807);
+ this.label7.Name = "label7";
+ this.label7.Size = new System.Drawing.Size(66, 13);
+ this.label7.TabIndex = 32;
+ this.label7.Text = "Powered by ";
+ //
+ // CapstoneLink
+ //
+ this.CapstoneLink.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+ this.CapstoneLink.AutoSize = true;
+ this.CapstoneLink.Location = new System.Drawing.Point(80, 807);
+ this.CapstoneLink.Name = "CapstoneLink";
+ this.CapstoneLink.Size = new System.Drawing.Size(52, 13);
+ this.CapstoneLink.TabIndex = 33;
+ this.CapstoneLink.TabStop = true;
+ this.CapstoneLink.Text = "Capstone";
+ this.CapstoneLink.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.CapstoneLink_LinkClicked);
+ //
+ // label8
+ //
+ this.label8.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+ this.label8.AutoSize = true;
+ this.label8.Location = new System.Drawing.Point(129, 807);
+ this.label8.Name = "label8";
+ this.label8.Size = new System.Drawing.Size(28, 13);
+ this.label8.TabIndex = 34;
+ this.label8.Text = "and ";
+ //
+ // CapstoneNetLink
+ //
+ this.CapstoneNetLink.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+ this.CapstoneNetLink.AutoSize = true;
+ this.CapstoneNetLink.Location = new System.Drawing.Point(151, 807);
+ this.CapstoneNetLink.Name = "CapstoneNetLink";
+ this.CapstoneNetLink.Size = new System.Drawing.Size(77, 13);
+ this.CapstoneNetLink.TabIndex = 35;
+ this.CapstoneNetLink.TabStop = true;
+ this.CapstoneNetLink.Text = "Capstone.NET";
+ this.CapstoneNetLink.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.CapstoneNetLink_LinkClicked);
+ //
+ // label10
+ //
+ this.label10.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+ this.label10.AutoSize = true;
+ this.label10.Location = new System.Drawing.Point(225, 807);
+ this.label10.Name = "label10";
+ this.label10.Size = new System.Drawing.Size(42, 13);
+ this.label10.TabIndex = 36;
+ this.label10.Text = "libraries";
+ //
+ // MainForm
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.ClientSize = new System.Drawing.Size(742, 840);
+ this.Controls.Add(this.label10);
+ this.Controls.Add(this.CapstoneNetLink);
+ this.Controls.Add(this.label8);
+ this.Controls.Add(this.CapstoneLink);
+ this.Controls.Add(this.label7);
+ this.Controls.Add(this.cmdBackupFolder);
+ this.Controls.Add(this.txtBackupFolder);
+ this.Controls.Add(this.label6);
+ this.Controls.Add(this.cmdScriptFile);
+ this.Controls.Add(this.txtScriptFile);
+ this.Controls.Add(this.label5);
+ this.Controls.Add(this.cmdPatch);
+ this.Controls.Add(this.cmdCompile);
+ this.Controls.Add(this.txtConsole);
+ this.Controls.Add(this.label9);
+ this.Controls.Add(this.cmdPatchDefinitionsFile);
+ this.Controls.Add(this.txtPatchDefinitionsFile);
+ this.Controls.Add(this.label4);
+ this.Controls.Add(this.cmdOutputFolder);
+ this.Controls.Add(this.txtOutputFolder);
+ this.Controls.Add(this.label3);
+ this.Controls.Add(this.cmdInputFolder);
+ this.Controls.Add(this.txtInputFolder);
+ this.Controls.Add(this.label2);
+ this.Controls.Add(this.cmdVisualStudioPath);
+ this.Controls.Add(this.txtVisualStudioPath);
+ this.Controls.Add(this.label1);
+ this.Name = "MainForm";
+ this.Text = "ARM Auto-patcher by Rene Lergner";
+ this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.MainForm_FormClosed);
+ this.Load += new System.EventHandler(this.MainForm_Load);
+ this.ResumeLayout(false);
+ this.PerformLayout();
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.Label label1;
+ private System.Windows.Forms.TextBox txtVisualStudioPath;
+ private System.Windows.Forms.FolderBrowserDialog FolderBrowserDialog;
+ private System.Windows.Forms.OpenFileDialog OpenFileDialog;
+ private System.Windows.Forms.SaveFileDialog SaveFileDialog;
+ private System.Windows.Forms.Button cmdVisualStudioPath;
+ private System.Windows.Forms.Button cmdInputFolder;
+ private System.Windows.Forms.TextBox txtInputFolder;
+ private System.Windows.Forms.Label label2;
+ private System.Windows.Forms.Button cmdOutputFolder;
+ private System.Windows.Forms.TextBox txtOutputFolder;
+ private System.Windows.Forms.Label label3;
+ private System.Windows.Forms.Button cmdPatchDefinitionsFile;
+ private System.Windows.Forms.TextBox txtPatchDefinitionsFile;
+ private System.Windows.Forms.Label label4;
+ private System.Windows.Forms.TextBox txtConsole;
+ private System.Windows.Forms.Label label9;
+ private System.Windows.Forms.Button cmdCompile;
+ private System.Windows.Forms.Button cmdPatch;
+ private System.Windows.Forms.Button cmdScriptFile;
+ private System.Windows.Forms.TextBox txtScriptFile;
+ private System.Windows.Forms.Label label5;
+ private System.Windows.Forms.Button cmdBackupFolder;
+ private System.Windows.Forms.TextBox txtBackupFolder;
+ private System.Windows.Forms.Label label6;
+ private System.Windows.Forms.Label label7;
+ private System.Windows.Forms.LinkLabel CapstoneLink;
+ private System.Windows.Forms.Label label8;
+ private System.Windows.Forms.LinkLabel CapstoneNetLink;
+ private System.Windows.Forms.Label label10;
+ }
+}
+
diff --git a/Patcher/AutoPatcher/MainForm.cs b/Patcher/AutoPatcher/MainForm.cs
new file mode 100644
index 0000000..f5515c1
--- /dev/null
+++ b/Patcher/AutoPatcher/MainForm.cs
@@ -0,0 +1,371 @@
+// Copyright (c) 2018, Rene Lergner - wpinternals.net - @Heathcliff74xda
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+
+using Microsoft.Win32;
+using System;
+using System.Collections.Generic;
+using System.Data;
+using System.IO;
+using System.Linq;
+using System.Windows.Forms;
+using WPinternals;
+
+namespace Patcher
+{
+ public partial class MainForm : Form
+ {
+ private PatchEngine PatchEngine = null;
+
+ public MainForm()
+ {
+ InitializeComponent();
+ }
+
+ private void MainForm_Load(object sender, EventArgs e)
+ {
+ LoadPaths();
+ CenterToScreen();
+ }
+
+ private void LoadPaths()
+ {
+ RegistryKey Key = Registry.CurrentUser.OpenSubKey(@"Software\Patcher", true) ?? Registry.CurrentUser.CreateSubKey(@"Software\Patcher");
+
+ txtVisualStudioPath.Text = (string)Key.GetValue("VisualStudioPath", "");
+ if (txtVisualStudioPath.Text.Length == 0)
+ txtVisualStudioPath.Text = FindVisualStudioPath();
+
+ txtPatchDefinitionsFile.Text = (string)Key.GetValue("PatchDefinitionsFilePath", "");
+ txtScriptFile.Text = (string)Key.GetValue("ScriptFilePath", "");
+ txtInputFolder.Text = (string)Key.GetValue("InputFolderPath", "");
+ txtOutputFolder.Text = (string)Key.GetValue("OutputFolderPath", "");
+ txtBackupFolder.Text = (string)Key.GetValue("BackupFolderPath", "");
+
+ LoadPatchDefinitions();
+ }
+
+ public static string[] FindMSVCBinaryPaths(string s)
+ {
+ string LegacyPath = Path.Combine(s, @"VC\bin");
+ if (Directory.Exists(LegacyPath))
+ {
+ return new string[] { LegacyPath };
+ }
+
+ if (Directory.Exists(Path.Combine(s, @"VC\Tools\MSVC")))
+ {
+ IEnumerable MSVCs = Directory.EnumerateDirectories(Path.Combine(s, @"VC\Tools\MSVC"));
+ IEnumerable Bins = MSVCs.Select(s => Path.Combine(s, "bin")).Where(s => Directory.Exists(s));
+ return Bins.ToArray();
+ }
+
+ return Array.Empty();
+ }
+
+ public static string FindArmAsmPath(string s)
+ {
+ foreach (string MSVCBin in FindMSVCBinaryPaths(s))
+ {
+ string path1 = Path.Combine(MSVCBin, "x86_arm");
+ string path2 = Path.Combine(MSVCBin, @"Hostx86\arm");
+
+ if (File.Exists(Path.Combine(path1, "armasm.exe")))
+ {
+ return path1;
+ }
+
+ if (File.Exists(Path.Combine(path2, "armasm.exe")))
+ {
+ return path2;
+ }
+ }
+
+ return "";
+ }
+
+ private static string FindVisualStudioPath()
+ {
+ IEnumerable MainX86VSDirectories = Directory.EnumerateDirectories(@"C:\Program Files (x86)\", "Microsoft Visual Studio*");
+ IEnumerable MainX64VSDirectories = Directory.EnumerateDirectories(@"C:\Program Files\", "Microsoft Visual Studio*");
+
+ IEnumerable MainVSDirectories = MainX86VSDirectories.Union(MainX64VSDirectories);
+
+ IEnumerable SubMainVSDirectories = MainVSDirectories.SelectMany(s => Directory.EnumerateDirectories(s));
+ IEnumerable SubSubMainVSDirectories = SubMainVSDirectories.SelectMany(s => Directory.EnumerateDirectories(s));
+ IEnumerable Directories = MainVSDirectories.Union(SubMainVSDirectories).Union(SubSubMainVSDirectories);
+
+ string attempt1 = Directories.Where(s => FindArmAsmPath(s) != "").OrderByDescending(s => File.GetCreationTime(Path.Combine(s, @"VC\bin\x86_arm\armasm.exe"))).FirstOrDefault() ?? "";
+
+ if (attempt1 != "")
+ return attempt1;
+
+ return Directories.Where(s => Directory.Exists(Path.Combine(s, @"VC\Tools\MSVC"))).Select(s => Path.Combine(s, @"VC\Tools\MSVC")).SelectMany(s => Directory.EnumerateDirectories(s)).Where(s => File.Exists(Path.Combine(s, @"bin\Hostx86\arm\armasm.exe"))).OrderByDescending(s => File.GetCreationTime(Path.Combine(s, @"bin\Hostx86\arm\armasm.exe"))).FirstOrDefault() ?? "";
+ }
+
+ private void StorePaths()
+ {
+ RegistryKey Key = Registry.CurrentUser.OpenSubKey(@"Software\Patcher", true) ?? Registry.CurrentUser.CreateSubKey(@"Software\Patcher");
+
+ string VisualStudioPath = txtVisualStudioPath.Text.Trim();
+ if (VisualStudioPath.Length == 0)
+ {
+ if (Key.GetValue("VisualStudioPath") != null)
+ Key.DeleteValue("VisualStudioPath");
+ }
+ else
+ {
+ Key.SetValue("VisualStudioPath", VisualStudioPath);
+ }
+
+ string PatchDefinitionsFilePath = txtPatchDefinitionsFile.Text.Trim();
+ if (PatchDefinitionsFilePath.Length == 0)
+ {
+ if (Key.GetValue("PatchDefinitionsFilePath") != null)
+ Key.DeleteValue("PatchDefinitionsFilePath");
+ }
+ else
+ {
+ Key.SetValue("PatchDefinitionsFilePath", PatchDefinitionsFilePath);
+ }
+
+ string ScriptFilePath = txtScriptFile.Text.Trim();
+ if (ScriptFilePath.Length == 0)
+ {
+ if (Key.GetValue("ScriptFilePath") != null)
+ Key.DeleteValue("ScriptFilePath");
+ }
+ else
+ {
+ Key.SetValue("ScriptFilePath", ScriptFilePath);
+ }
+
+ string InputFolderPath = txtInputFolder.Text.Trim();
+ if (InputFolderPath.Length == 0)
+ {
+ if (Key.GetValue("InputFolderPath") != null)
+ Key.DeleteValue("InputFolderPath");
+ }
+ else
+ {
+ Key.SetValue("InputFolderPath", InputFolderPath);
+ }
+
+ string OutputFolderPath = txtOutputFolder.Text.Trim();
+ if (OutputFolderPath.Length == 0)
+ {
+ if (Key.GetValue("OutputFolderPath") != null)
+ Key.DeleteValue("OutputFolderPath");
+ }
+ else
+ {
+ Key.SetValue("OutputFolderPath", OutputFolderPath);
+ }
+
+ string BackupFolderPath = txtBackupFolder.Text.Trim();
+ if (BackupFolderPath.Length == 0)
+ {
+ if (Key.GetValue("BackupFolderPath") != null)
+ Key.DeleteValue("BackupFolderPath");
+ }
+ else
+ {
+ Key.SetValue("BackupFolderPath", BackupFolderPath);
+ }
+ }
+
+ private bool LoadingPatchDefinitions = false;
+
+ private void LoadPatchDefinitions()
+ {
+ if (LoadingPatchDefinitions)
+ return;
+ LoadingPatchDefinitions = true;
+
+ try
+ {
+ string Definitions = File.ReadAllText(txtPatchDefinitionsFile.Text);
+ PatchEngine = new PatchEngine(Definitions);
+ }
+ catch
+ {
+ PatchEngine = new PatchEngine();
+ }
+
+ LoadingPatchDefinitions = false;
+ }
+
+ private void cmdVisualStudioPath_Click(object sender, EventArgs e)
+ {
+ FolderBrowserDialog.SelectedPath = txtVisualStudioPath.Text;
+ FolderBrowserDialog.Description = "Select path to Visual Studio with ARM32 SDK";
+ System.Windows.Forms.DialogResult Result = FolderBrowserDialog.ShowDialog();
+ if (Result == System.Windows.Forms.DialogResult.OK)
+ txtVisualStudioPath.Text = FolderBrowserDialog.SelectedPath;
+ }
+
+ private void cmdPatchDefinitionsFile_Click(object sender, EventArgs e)
+ {
+ OpenFileDialog.CheckFileExists = false;
+ OpenFileDialog.DefaultExt = "xml";
+ try
+ {
+ OpenFileDialog.FileName = Path.GetFileName(txtPatchDefinitionsFile.Text);
+ OpenFileDialog.InitialDirectory = Path.GetDirectoryName(txtPatchDefinitionsFile.Text);
+ }
+ catch { }
+ OpenFileDialog.Multiselect = false;
+ OpenFileDialog.Title = "Open patch-definitions file";
+ System.Windows.Forms.DialogResult Result = OpenFileDialog.ShowDialog();
+ if (Result == System.Windows.Forms.DialogResult.OK)
+ {
+ txtPatchDefinitionsFile.Text = OpenFileDialog.FileName;
+ WindowsFormsSynchronizationContext.Current.Post(s => LoadPatchDefinitions(), null);
+ }
+ }
+
+ private void txtPatchDefinitionsFile_Leave(object sender, EventArgs e)
+ {
+ WindowsFormsSynchronizationContext.Current.Post(s => LoadPatchDefinitions(), null);
+ }
+
+ private void MainForm_FormClosed(object sender, FormClosedEventArgs e)
+ {
+ StorePaths();
+ }
+
+ private void cmdInputFolder_Click(object sender, EventArgs e)
+ {
+ FolderSelectDialog Dialog = new();
+ Dialog.Title = "Select input location";
+ Dialog.InitialDirectory = txtInputFolder.Text;
+ try
+ {
+ Dialog.InitialDirectory = txtInputFolder.Text;
+ }
+ catch { }
+ bool Result = Dialog.ShowDialog();
+ if (Result)
+ {
+ txtInputFolder.Text = Dialog.FileName;
+ txtOutputFolder.Text = "";
+ }
+ }
+
+ private void cmdOutputFolder_Click(object sender, EventArgs e)
+ {
+ FolderSelectDialog Dialog = new();
+ Dialog.Title = "Select output location";
+ Dialog.InitialDirectory = txtOutputFolder.Text;
+ try
+ {
+ Dialog.InitialDirectory = txtOutputFolder.Text;
+ }
+ catch { }
+ bool Result = Dialog.ShowDialog();
+ if (Result)
+ {
+ txtOutputFolder.Text = Dialog.FileName;
+ }
+ }
+
+ private void cmdScriptFile_Click(object sender, EventArgs e)
+ {
+ OpenFileDialog.CheckFileExists = true;
+ OpenFileDialog.DefaultExt = "pds";
+ try
+ {
+ OpenFileDialog.FileName = Path.GetFileName(txtScriptFile.Text);
+ OpenFileDialog.InitialDirectory = Path.GetDirectoryName(txtScriptFile.Text);
+ }
+ catch { }
+ OpenFileDialog.Multiselect = false;
+ OpenFileDialog.Title = "Open patch-definition-script-file";
+ System.Windows.Forms.DialogResult Result = OpenFileDialog.ShowDialog();
+ if (Result == System.Windows.Forms.DialogResult.OK)
+ {
+ txtScriptFile.Text = OpenFileDialog.FileName;
+ }
+ }
+
+ private void cmdCompile_Click(object sender, EventArgs e)
+ {
+ ClearLog();
+ StorePaths();
+ ScriptEngine.ExecuteScript(txtVisualStudioPath.Text.Trim(), txtScriptFile.Text.Trim(), txtInputFolder.Text.Trim(), PatchEngine: PatchEngine, WriteLog: WriteLog);
+ }
+
+ private void cmdPatch_Click(object sender, EventArgs e)
+ {
+ ClearLog();
+ StorePaths();
+ ScriptEngine.ExecuteScript(txtVisualStudioPath.Text.Trim(), txtScriptFile.Text.Trim(), txtInputFolder.Text.Trim(), PatchEngine, txtOutputFolder.Text.Trim(), txtBackupFolder.Text.Trim().Length == 0 ? null : txtBackupFolder.Text.Trim(), WriteLog);
+
+ PatchEngine.WriteDefinitions(txtPatchDefinitionsFile.Text);
+ WriteLog("Patch-definitions written to: " + txtPatchDefinitionsFile.Text);
+ }
+
+ private void ClearLog()
+ {
+ txtConsole.Clear();
+ }
+
+ private void WriteLog(string Line)
+ {
+ if (txtConsole.InvokeRequired)
+ {
+ txtConsole.Invoke((MethodInvoker)delegate { WriteLog(Line); });
+ }
+ else
+ {
+ txtConsole.AppendText(Line + Environment.NewLine);
+ txtConsole.Select(txtConsole.Text.Length, 0);
+ txtConsole.ScrollToCaret();
+ System.Diagnostics.Debug.WriteLine(Line);
+ }
+ }
+
+ private void cmdBackupFolder_Click(object sender, EventArgs e)
+ {
+ FolderSelectDialog Dialog = new();
+ Dialog.Title = "Select backup location";
+ Dialog.InitialDirectory = txtBackupFolder.Text;
+ try
+ {
+ Dialog.InitialDirectory = txtBackupFolder.Text;
+ }
+ catch { }
+ bool Result = Dialog.ShowDialog();
+ if (Result)
+ {
+ txtBackupFolder.Text = Dialog.FileName;
+ }
+ }
+
+ private void CapstoneLink_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
+ {
+ System.Diagnostics.Process.Start("https://github.com/aquynh/capstone/blob/master/LICENSE.TXT");
+ }
+
+ private void CapstoneNetLink_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
+ {
+ System.Diagnostics.Process.Start("https://github.com/9ee1/Capstone.NET/blob/master/LICENSE");
+ }
+ }
+}
diff --git a/Patcher/AutoPatcher/MainForm.resx b/Patcher/AutoPatcher/MainForm.resx
new file mode 100644
index 0000000..28d85c9
--- /dev/null
+++ b/Patcher/AutoPatcher/MainForm.resx
@@ -0,0 +1,129 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ 17, 17
+
+
+ 182, 17
+
+
+ 317, 17
+
+
\ No newline at end of file
diff --git a/Patcher/AutoPatcher/Program.cs b/Patcher/AutoPatcher/Program.cs
new file mode 100644
index 0000000..9e8a06d
--- /dev/null
+++ b/Patcher/AutoPatcher/Program.cs
@@ -0,0 +1,39 @@
+// Copyright (c) 2018, Rene Lergner - wpinternals.net - @Heathcliff74xda
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+
+using System;
+using System.Windows.Forms;
+
+namespace Patcher
+{
+ internal static class Program
+ {
+ ///
+ /// The main entry point for the application.
+ ///
+ [STAThread]
+ private static void Main()
+ {
+ Application.EnableVisualStyles();
+ Application.SetCompatibleTextRenderingDefault(false);
+ Application.Run(new MainForm());
+ }
+ }
+}
diff --git a/Patcher/AutoPatcher/Properties/AssemblyInfo.cs b/Patcher/AutoPatcher/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..903ac15
--- /dev/null
+++ b/Patcher/AutoPatcher/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("AutoPatcher")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("AutoPatcher")]
+[assembly: AssemblyCopyright("Copyright © 2018")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("95cf9509-c1c4-40f5-a60e-9d93ea6f438c")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/Patcher/AutoPatcher/Properties/Resources.Designer.cs b/Patcher/AutoPatcher/Properties/Resources.Designer.cs
new file mode 100644
index 0000000..e88730f
--- /dev/null
+++ b/Patcher/AutoPatcher/Properties/Resources.Designer.cs
@@ -0,0 +1,63 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace Patcher.Properties {
+ using System;
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Resources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources() {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Patcher.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+ }
+}
diff --git a/Patcher/AutoPatcher/Properties/Resources.resx b/Patcher/AutoPatcher/Properties/Resources.resx
new file mode 100644
index 0000000..af7dbeb
--- /dev/null
+++ b/Patcher/AutoPatcher/Properties/Resources.resx
@@ -0,0 +1,117 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/Patcher/AutoPatcher/Properties/Settings.Designer.cs b/Patcher/AutoPatcher/Properties/Settings.Designer.cs
new file mode 100644
index 0000000..717a487
--- /dev/null
+++ b/Patcher/AutoPatcher/Properties/Settings.Designer.cs
@@ -0,0 +1,26 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace Patcher.Properties {
+
+
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.8.0.0")]
+ internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
+
+ private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+ public static Settings Default {
+ get {
+ return defaultInstance;
+ }
+ }
+ }
+}
diff --git a/Patcher/AutoPatcher/Properties/Settings.settings b/Patcher/AutoPatcher/Properties/Settings.settings
new file mode 100644
index 0000000..3964565
--- /dev/null
+++ b/Patcher/AutoPatcher/Properties/Settings.settings
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/Patcher/AutoPatcher/ScriptEngine.cs b/Patcher/AutoPatcher/ScriptEngine.cs
new file mode 100644
index 0000000..117e03c
--- /dev/null
+++ b/Patcher/AutoPatcher/ScriptEngine.cs
@@ -0,0 +1,1976 @@
+// Copyright (c) 2018, Rene Lergner - wpinternals.net - @Heathcliff74xda
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Security.Cryptography;
+using System.Text;
+using System.Threading.Tasks;
+using WPinternals;
+
+namespace Patcher
+{
+ public static class ScriptEngine
+ {
+ private static List ScriptCode;
+ private static int Pointer;
+ private static Action WriteLog;
+ private static string InputFolderPath;
+ private static string OutputFolderPath;
+ private static string BackupFolderPath;
+ private static string RelativePath;
+ private static string RelativeOutputPath;
+ private static string PathToVisualStudio;
+ private static string PatchDefinitionName;
+ private static string PatchDefinitionVersion;
+ private static AnalyzedFile AnalyzedFile;
+ private static byte[] FileBuffer;
+ private static string FilePath;
+ private static TargetFile FilePatchCollection;
+ private static PatchEngine PatchEngine;
+ private static UInt32 CurrentVirtualAddressTarget;
+ private static bool FindSuccess;
+ private static List Labels;
+ private static List JumpHistory;
+
+ private static readonly char[] Operators = new char[] { '!', '@', '#', '$', '%', '^', '&', '*', '-', '+', '=', '|', '/', '?', '<', '>' };
+ private static readonly char[] Separators = new char[] { ',' };
+ private static readonly char[] Brackets = new char[] { '[', ']', '(', ')', '{', '}' };
+
+ internal static void ExecuteScript(string PathToVisualStudio, string ScriptFilePath, string InputFolderPath, PatchEngine PatchEngine = null, string OutputFolderPath = null, string BackupFolderPath = null, Action WriteLog = null)
+ {
+ try
+ {
+ ScriptEngine.WriteLog = WriteLog ?? ((s) => { });
+
+ ScriptCode = new List();
+ JumpHistory = new List();
+ Pointer = 0;
+ PatchDefinitionName = null;
+ PatchDefinitionVersion = null;
+ AnalyzedFile = null;
+
+ ScriptEngine.InputFolderPath = InputFolderPath;
+ ScriptEngine.OutputFolderPath = OutputFolderPath?.Length == 0 ? null : OutputFolderPath;
+ ScriptEngine.BackupFolderPath = BackupFolderPath?.Length == 0 ? null : BackupFolderPath;
+ ScriptEngine.PathToVisualStudio = PathToVisualStudio;
+ ScriptEngine.PatchEngine = PatchEngine;
+
+ string[] ScriptCodeLines = File.ReadAllText(ScriptFilePath).Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None);
+
+ CodeLine CurrentLine = new();
+ bool InCode = false;
+ for (int i = 0; i < ScriptCodeLines.Length; i++)
+ {
+ string Line = RemoveComment(ScriptCodeLines[i]).Trim(new char[] { ' ', '\t' });
+ if (Line.Length > 0)
+ {
+ string Command = Line.Split(new char[] { ' ', '\t' })[0];
+ if (string.Equals(Command, "PatchCode", StringComparison.CurrentCultureIgnoreCase))
+ {
+ InCode = true;
+ CurrentLine.Code = Line;
+ }
+ else if ((string.Equals(Command, "EndCode", StringComparison.CurrentCultureIgnoreCase)) || (string.Equals(Command, "EndPatch", StringComparison.CurrentCultureIgnoreCase)))
+ {
+ InCode = false;
+ ScriptCode.Add(CurrentLine);
+ CurrentLine = new CodeLine();
+ }
+ else if (InCode)
+ {
+ CurrentLine.PatchCode += Line + Environment.NewLine;
+ }
+ else if (Line.EndsWith(":"))
+ {
+ if (CurrentLine.Label?.Length > 0)
+ throw new ScriptParserException("Two labels at the same location");
+
+ string Label = Line.TrimEnd(new char[] { ' ', ':' });
+ if (Label.Contains(' '))
+ throw new ScriptParserException("No spaces allowed in label");
+
+ CurrentLine.Label = Label;
+ }
+ else
+ {
+ CurrentLine.Code = Line;
+ ScriptCode.Add(CurrentLine);
+ CurrentLine = new CodeLine();
+ }
+ }
+ }
+
+ do
+ {
+ CurrentLine = ScriptCode[Pointer];
+ Pointer++;
+ ExecuteCode(CurrentLine);
+ }
+ while (Pointer < ScriptCode.Count);
+
+ CloseFile();
+
+ WriteLog("Script finished!");
+ }
+ catch (ScriptParserException Ex)
+ {
+ WriteLog("Script parser error: " + Ex.Message);
+ }
+ catch (Exception Ex)
+ {
+ WriteLog("Script execution error: " + Ex.Message);
+ }
+ }
+
+ private static string RemoveComment(string Line)
+ {
+ int q;
+ bool InString;
+ int p;
+ do
+ {
+ InString = false;
+ p = Line.IndexOf("//");
+ if (p >= 0)
+ {
+ q = 0;
+ do
+ {
+ q = Line.IndexOf("\"", q);
+ if ((q >= 0) && (q < p))
+ {
+ if ((q == 0) || (Line[q - 1] != '\\'))
+ InString = !InString;
+ q++;
+ }
+ }
+ while ((q >= 0) && (q < p));
+
+ if (!InString)
+ {
+ return Line.Substring(0, p);
+ }
+
+ p++;
+ }
+ }
+ while (p >= 0);
+
+ return Line;
+ }
+
+ private static void ExecuteCode(CodeLine Line)
+ {
+ List Tokens = Tokenizer(Line.Code);
+ ParseTokens(Tokens, out string Command, out List> Params);
+
+ // Invoke method
+ MethodInfo Method = Array.Find(typeof(ScriptEngine).GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static), m => string.Equals(m.Name, Command, StringComparison.CurrentCultureIgnoreCase));
+
+ if (Method == null)
+ throw new ScriptParserException("Unrecognized command: " + Command);
+
+ ParameterInfo[] ParamInfos = Method.GetParameters();
+ object[] ParamObjects = new object[ParamInfos.Length];
+ for (int i = 0; i < ParamInfos.Length; i++)
+ {
+ if (ParamInfos[i].HasDefaultValue)
+ ParamObjects[i] = ParamInfos[i].DefaultValue;
+ else ParamObjects[i] = ParamInfos[i].ParameterType.IsValueType ? Activator.CreateInstance(ParamInfos[i].ParameterType) : null;
+ }
+ for (int i = 0; i < Params.Count; i++)
+ {
+ int ParamIndex;
+ if (Params[i].Item1 == null)
+ {
+ ParamIndex = i < ParamObjects.Length ? i : throw new ScriptParserException("Wrong number of parameters for command: " + Command);
+ }
+ else
+ {
+ ParameterInfo ParamInfo = Array.Find(ParamInfos, p => string.Equals(p.Name, Params[i].Item1, StringComparison.CurrentCultureIgnoreCase));
+ ParamIndex = ParamInfo != null
+ ? ParamInfo.Position
+ : throw new ScriptParserException("Unrecognized parameters " + Params[i].Item1 + " for command: " + Command);
+ }
+
+ if ((Params[i].Item3 == TokenType.Text) && (ParamInfos[ParamIndex].ParameterType == typeof(string)))
+ {
+ ParamObjects[ParamIndex] = Params[i].Item2;
+ }
+ else if ((Params[i].Item3 == TokenType.Number) && ((ParamInfos[ParamIndex].ParameterType == typeof(int)) || (ParamInfos[ParamIndex].ParameterType == typeof(uint))))
+ {
+ if (Params[i].Item2.StartsWith("0x", StringComparison.OrdinalIgnoreCase))
+ {
+ // Hex
+ UInt32 Value = Convert.ToUInt32(Params[i].Item2[2..], 16);
+ ParamObjects[ParamIndex] = ParamInfos[ParamIndex].ParameterType == typeof(uint) ? Value : (object)(int)Value;
+ }
+ else
+ {
+ if (Params[i].Item2.StartsWith("-"))
+ {
+ Int32 IntValue = Int32.Parse(Params[i].Item2);
+ ParamObjects[ParamIndex] = ParamInfos[ParamIndex].ParameterType == typeof(uint) ? (uint)IntValue : (object)IntValue;
+ }
+ else
+ {
+ UInt32 UIntValue = UInt32.Parse(Params[i].Item2);
+ ParamObjects[ParamIndex] = ParamInfos[ParamIndex].ParameterType == typeof(uint) ? UIntValue : (object)(int)UIntValue;
+ }
+ }
+ }
+ else
+ {
+ throw new ScriptParserException("Wrong parametertype for parameter " + ParamInfos[ParamIndex].Name + " of command " + Command);
+ }
+ }
+
+ if (Method.Name == "PatchCode")
+ ParamObjects[^1] = Line.PatchCode;
+
+ try
+ {
+ Method.Invoke(null, ParamObjects);
+ }
+ catch (ScriptExecutionException)
+ {
+ throw;
+ }
+ catch (Exception Ex)
+ {
+ Exception Ex2 = Ex;
+ if (Ex2.InnerException != null)
+ Ex2 = Ex2.InnerException;
+ if (Ex2 is ScriptExecutionException)
+ throw Ex2;
+ throw new ScriptExecutionException(Ex2.Message, Ex2);
+ }
+ }
+
+ private static List Tokenizer(string Line)
+ {
+ // Name: Test_123
+ // Text: Test "Dit is een \"test\"..."
+ // Number: -12 0xA1 +334
+ // Operator: !@#$%^&*-+=|/?<> (combination of multiple characters possible)
+ // Separator: ,
+ // Bracket: []{}()
+
+ List Tokens = new();
+ int p = 0;
+
+ while (p < Line.Length)
+ {
+ if (char.IsWhiteSpace(Line[p]))
+ {
+ p++;
+ continue;
+ }
+
+ if (Line[p] == '\"')
+ {
+ int q = Line.IndexOf('\"', p + 1);
+ if (q == -1)
+ {
+ Tokens.Add(new Token() { Type = TokenType.Text, Text = Line[(p + 1)..] });
+ p = Line.Length;
+ }
+ else
+ {
+ Tokens.Add(new Token() { Type = TokenType.Text, Text = Line.Substring(p + 1, q - p - 1) });
+ p = q + 1;
+ }
+ continue;
+ }
+
+ if (char.IsLetter(Line[p]) || (Line[p] == '_'))
+ {
+ int q = p + 1;
+ while (q < Line.Length)
+ {
+ if (char.IsLetterOrDigit(Line[q]) || (Line[q] == '_') || (Line[q] == '.'))
+ q++;
+ else
+ break;
+ }
+
+ if ((q == Line.Length) || char.IsWhiteSpace(Line[q]) || Separators.Contains(Line[q]) || Brackets.Contains(Line[q]) || Operators.Contains(Line[q]))
+ {
+ Tokens.Add(new Token() { Type = TokenType.Text, Text = Line[p..q] });
+ p = q;
+ continue;
+ }
+ }
+
+ // Int
+ if ((((Line[p] == '+') || (Line[p] == '-')) && (p < Line.Length) && char.IsNumber(Line[p + 1])) || char.IsNumber(Line[p]))
+ {
+ int q = p + 1;
+ while (q < Line.Length)
+ {
+ if (char.IsDigit(Line[q]))
+ q++;
+ else
+ break;
+ }
+
+ if ((q == Line.Length) || char.IsWhiteSpace(Line[q]) || Separators.Contains(Line[q]) || Brackets.Contains(Line[q]) || Operators.Contains(Line[q]))
+ {
+ Tokens.Add(new Token() { Type = TokenType.Number, Text = Line[p..q], Value = (UInt32)Int32.Parse(Line[p..q]) });
+ p = q;
+ continue;
+ }
+ }
+
+ // Hex
+ if (((Line.Length - p) >= 3) && (string.Equals(Line.Substring(p, 2), "0x", StringComparison.CurrentCultureIgnoreCase)))
+ {
+ int q = p + 2;
+ while (q < Line.Length)
+ {
+ char CurrentChar = Line[q];
+ if (char.IsDigit(CurrentChar) || ((CurrentChar >= 'a') && (CurrentChar <= 'f')) || ((CurrentChar >= 'A') && (CurrentChar <= 'F')))
+ q++;
+ else
+ break;
+ }
+
+ if ((q == Line.Length) || char.IsWhiteSpace(Line[q]) || Separators.Contains(Line[q]) || Brackets.Contains(Line[q]) || Operators.Contains(Line[q]))
+ {
+ Tokens.Add(new Token() { Type = TokenType.Number, Text = Line[p..q], Value = UInt32.Parse(Line.Substring(p + 2, q - p - 2), System.Globalization.NumberStyles.HexNumber) });
+ p = q;
+ continue;
+ }
+ }
+
+ // Operators
+ if (Operators.Contains(Line[p]))
+ {
+ int q = p + 1;
+ while (q < Line.Length)
+ {
+ if (Operators.Contains(Line[q]))
+ q++;
+ else
+ break;
+ }
+
+ Tokens.Add(new Token() { Type = TokenType.Operator, Text = Line[p..q] });
+ p = q;
+ continue;
+ }
+
+ // Brackets
+ if (Brackets.Contains(Line[p]))
+ {
+ Tokens.Add(new Token() { Type = TokenType.Bracket, Text = Line.Substring(p, 1) });
+ p++;
+ continue;
+ }
+
+ // Separators
+ if (Separators.Contains(Line[p]))
+ {
+ Tokens.Add(new Token() { Type = TokenType.Separator, Text = Line.Substring(p, 1) });
+ p++;
+ continue;
+ }
+
+ throw new ScriptParserException("Syntax error in line: " + Line);
+ }
+
+ return Tokens;
+ }
+
+ private static List ArmThumbTokenizer(string Line)
+ {
+ List Tokens;
+ try
+ {
+ Tokens = Tokenizer(Line.ToLower().Replace("#", ""));
+ }
+ catch
+ {
+ Tokens = new List();
+ }
+
+ for (int i = 0; i < (Tokens.Count - 1); i++)
+ {
+ if ((Tokens[i].Text == "r") && (Tokens[i + 1].Text == "?"))
+ {
+ Tokens[i].Text = "r?";
+ Tokens.RemoveAt(i + 1);
+ }
+ }
+
+ return Tokens;
+ }
+
+ private static void ParseTokens(List Tokens, out string Command, out List> Params)
+ {
+ Command = null;
+ Params = new List>();
+
+ if ((Tokens[0].Type == TokenType.Text) && (!IsName(Tokens[0].Text)))
+ throw new ScriptParserException("Invalid command");
+
+ Command = Tokens[0].Text;
+ Tokens.RemoveAt(0);
+
+ if (Tokens.Count > 0)
+ {
+ if ((Tokens[0].Text == "(") && (Tokens[^1].Text == ")"))
+ {
+ Tokens.RemoveAt(0);
+ Tokens.RemoveAt(Tokens.Count - 1);
+ }
+
+ // Parse params
+ bool GotNamedParam = false;
+ while (Tokens.Count > 0)
+ {
+ if ((Tokens.Count >= 3) && (Tokens[0].Type == TokenType.Text) && IsName(Tokens[0].Text) && (Tokens[1].Text == "=") && ((Tokens[2].Type == TokenType.Text) || (Tokens[2].Type == TokenType.Number)))
+ {
+ Params.Add(new Tuple(Tokens[0].Text, Tokens[2].Text, Tokens[2].Type));
+ GotNamedParam = true;
+ Tokens.RemoveRange(0, 3);
+ }
+ else if ((Tokens[0].Type == TokenType.Text) || (Tokens[0].Type == TokenType.Number))
+ {
+ if (GotNamedParam)
+ throw new ScriptParserException("Named parameter cannot preceed an unnamed parameter");
+
+ Params.Add(new Tuple(null, Tokens[0].Text, Tokens[0].Type));
+ Tokens.RemoveAt(0);
+ }
+ else
+ {
+ throw new ScriptParserException("Syntax error");
+ }
+
+ if ((Tokens.Count > 0) && (Tokens[0].Text == ","))
+ Tokens.RemoveAt(0);
+ }
+ }
+ }
+
+ private static bool IsName(string Token)
+ {
+ if (Token.Length == 0)
+ return false;
+ for (int i = 0; i < Token.Length; i++)
+ {
+ if (!(char.IsLetter(Token[i]) || (Token[i] == '_') || (Token[i] == '.') || ((i > 0) && char.IsNumber(Token[i]))))
+ return false;
+ }
+ return true;
+ }
+
+ private static void PatchDefinition(string Name, string VersionFrom, string Version, string RelativePath, string RelativeOutputPath)
+ {
+ CloseFile();
+
+ PatchDefinitionName = Name;
+ ScriptEngine.RelativePath = RelativePath;
+ ScriptEngine.RelativeOutputPath = RelativeOutputPath;
+ if (Version != null)
+ {
+ PatchDefinitionVersion = Version;
+ }
+ else if (VersionFrom != null)
+ {
+ string FullPath = System.IO.Path.Combine(InputFolderPath, VersionFrom);
+ PeFile File = new(FullPath);
+ Version ProductVersion = File.GetProductVersion();
+ PatchDefinitionVersion = ProductVersion.Major.ToString() + "." + ProductVersion.Minor.ToString() + "." + ProductVersion.Build.ToString() + "." + ProductVersion.Revision.ToString();
+ }
+ else
+ {
+ throw new ScriptExecutionException("Patch definition version is mandatory");
+ }
+ WriteLog("PatchDefinition: " + PatchDefinitionName);
+ WriteLog("Version: " + PatchDefinitionVersion);
+ }
+
+ private static void PatchFile(string Path)
+ {
+ if (PatchDefinitionName == null)
+ throw new ScriptExecutionException("PatchDefinition not defined");
+
+ CloseFile();
+ string FullPath = System.IO.Path.Combine(InputFolderPath, RelativePath ?? "", Path);
+
+ string AsmFilePath = BackupFolderPath == null
+ ? System.IO.Path.Combine(InputFolderPath, RelativePath ?? "", Path)
+ : System.IO.Path.Combine(BackupFolderPath, RelativePath ?? "", Path).Replace("%VERSION%", PatchDefinitionVersion, StringComparison.OrdinalIgnoreCase);
+ AsmFilePath = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(AsmFilePath), System.IO.Path.GetFileNameWithoutExtension(AsmFilePath) + ".asm");
+
+ bool AsmFileExists = File.Exists(AsmFilePath);
+ if (AsmFileExists)
+ WriteLog("Loading file: " + AsmFilePath);
+ else
+ WriteLog("Analyzing file: " + FullPath);
+
+ AnalyzedFile = ArmDisassembler.Analyze(FullPath, AsmFilePath);
+ FilePath = Path;
+ FileBuffer = AnalyzedFile.File.Buffer;
+
+ if (!AsmFileExists)
+ {
+ WriteLog("Writing file: " + AsmFilePath);
+ WriteLog("Analysis done");
+ }
+
+ if (BackupFolderPath != null)
+ {
+ FullPath = System.IO.Path.Combine(BackupFolderPath, RelativePath ?? "", Path).Replace("%VERSION%", PatchDefinitionVersion, StringComparison.OrdinalIgnoreCase);
+
+ WriteLog("Create backup to: " + FullPath);
+ File.WriteAllBytes(FullPath, FileBuffer);
+ }
+
+ CurrentVirtualAddressTarget = (UInt32)AnalyzedFile.File.ImageBase;
+
+ PatchDefinition PatchDefinition = PatchEngine.PatchDefinitions.Find(d => string.Equals(d.Name, PatchDefinitionName, StringComparison.CurrentCultureIgnoreCase));
+ if (PatchDefinition == null)
+ {
+ PatchDefinition = new PatchDefinition
+ {
+ Name = PatchDefinitionName
+ };
+ PatchEngine.PatchDefinitions.Add(PatchDefinition);
+ }
+ TargetVersion TargetVersion = PatchDefinition.TargetVersions.Find(v => string.Equals(v.Description, PatchDefinitionVersion, StringComparison.CurrentCultureIgnoreCase));
+ if (TargetVersion == null)
+ {
+ TargetVersion = new TargetVersion
+ {
+ Description = PatchDefinitionVersion
+ };
+ PatchDefinition.TargetVersions.Add(TargetVersion);
+ }
+ TargetFile TargetFile = TargetVersion.TargetFiles.Find(f => (f.Path != null) && (string.Equals(f.Path.TrimStart(new char[] { '\\' }), Path.TrimStart(new char[] { '\\' }), StringComparison.CurrentCultureIgnoreCase)));
+ if (TargetFile != null)
+ TargetVersion.TargetFiles.Remove(TargetFile); // Remove any old patches for this file
+ TargetFile = new TargetFile();
+ TargetVersion.TargetFiles.Add(TargetFile);
+ TargetFile.Path = Path.TrimStart(new char[] { '\\' });
+ SHA1Managed SHA = new();
+ TargetFile.HashOriginal = SHA.ComputeHash(AnalyzedFile.File.Buffer);
+ FilePatchCollection = TargetFile;
+
+ Labels = new List();
+ }
+
+ private static void CloseFile()
+ {
+ if (AnalyzedFile != null)
+ {
+ // Update hash in patch definition
+ SHA1Managed SHA = new();
+ FilePatchCollection.HashPatched = SHA.ComputeHash(FileBuffer);
+ WriteLog("New hash for patched file: " + Converter.ConvertHexToString(FilePatchCollection.HashPatched, ""));
+
+ // Write patched file
+ if (OutputFolderPath != null)
+ {
+ string FullPath = System.IO.Path.Combine(OutputFolderPath, RelativeOutputPath ?? "", RelativePath ?? "", FilePath).Replace("%VERSION%", PatchDefinitionVersion, StringComparison.OrdinalIgnoreCase);
+
+ System.IO.Directory.CreateDirectory(System.IO.Path.GetDirectoryName(FullPath));
+
+ WriteLog("Writing patched file: " + FullPath);
+
+ File.WriteAllBytes(FullPath, FileBuffer);
+ }
+
+ AnalyzedFile = null;
+ }
+ }
+
+ private static void PatchAtRawOffset(byte[] Bytes, UInt32 RawOffset)
+ {
+ // Read original bytes
+ byte[] Original = new byte[Bytes.Length];
+ System.Buffer.BlockCopy(FileBuffer, (int)RawOffset, Original, 0, Bytes.Length);
+
+ // Patch bytes in buffer
+ System.Buffer.BlockCopy(Bytes, 0, FileBuffer, (int)RawOffset, Bytes.Length);
+
+ // Add patch to defintions (original and patched bytes)
+ Patch CurrentPatch = FilePatchCollection.Patches.Find(p => p.Address == RawOffset);
+ if (CurrentPatch == null)
+ {
+ CurrentPatch = new Patch
+ {
+ Address = RawOffset,
+ OriginalBytes = Original
+ };
+ FilePatchCollection.Patches.Add(CurrentPatch);
+ }
+ CurrentPatch.PatchedBytes = Bytes;
+
+ WriteLog("Patched file at raw offset: 0x" + RawOffset.ToString("X8"));
+ WriteLog(" Original bytes: " + Converter.ConvertHexToString(Original, " "));
+ WriteLog(" Patched bytes: " + Converter.ConvertHexToString(Bytes, " "));
+ }
+
+ private static void PatchAtVirtualAddress(byte[] Bytes, UInt32 VirtualAddress)
+ {
+ PatchAtRawOffset(Bytes, AnalyzedFile.File.ConvertVirtualAddressToRawOffset(CurrentVirtualAddressTarget));
+ }
+
+ private static void FindFirstAscii(string SearchString)
+ {
+ CurrentVirtualAddressTarget = (UInt32)AnalyzedFile.File.ImageBase;
+ WriteLog("Set search start point to virtual address: 0x" + CurrentVirtualAddressTarget.ToString("X8"));
+ FindNextAscii(SearchString);
+ }
+
+ private static void FindNextAscii(string SearchString)
+ {
+ if (AnalyzedFile == null)
+ throw new ScriptExecutionException("PatchFile not defined");
+
+ WriteLog("Looking for ascii string: " + SearchString);
+ UInt32? FindIndex = ByteOperations.FindAscii(FileBuffer, AnalyzedFile.File.ConvertVirtualAddressToRawOffset(CurrentVirtualAddressTarget), SearchString);
+ FindSuccess = FindIndex != null;
+ if (FindIndex != null)
+ {
+ CurrentVirtualAddressTarget = AnalyzedFile.File.ConvertRawOffsetToVirtualAddress((UInt32)FindIndex);
+ WriteLog("Ascii string found at virtual address: 0x" + CurrentVirtualAddressTarget.ToString("X8"));
+ }
+ else
+ {
+ WriteLog("String not found");
+ }
+ }
+
+ private static void FindAscii(string SearchString)
+ {
+ FindNextAscii(SearchString);
+ }
+
+ private static void FindFirstUnicode(string SearchString)
+ {
+ CurrentVirtualAddressTarget = (UInt32)AnalyzedFile.File.ImageBase;
+ WriteLog("Set search start point to virtual address: 0x" + CurrentVirtualAddressTarget.ToString("X8"));
+ FindNextUnicode(SearchString);
+ }
+
+ private static void FindNextUnicode(string SearchString)
+ {
+ if (AnalyzedFile == null)
+ throw new ScriptExecutionException("PatchFile not defined");
+
+ WriteLog("Looking for unicode string: " + SearchString);
+ UInt32? FindIndex = ByteOperations.FindUnicode(FileBuffer, AnalyzedFile.File.ConvertVirtualAddressToRawOffset(CurrentVirtualAddressTarget), SearchString);
+ FindSuccess = FindIndex != null;
+ if (FindIndex != null)
+ {
+ CurrentVirtualAddressTarget = AnalyzedFile.File.ConvertRawOffsetToVirtualAddress((UInt32)FindIndex);
+ WriteLog("Unicode string found at virtual address: 0x" + CurrentVirtualAddressTarget.ToString("X8"));
+ }
+ else
+ {
+ WriteLog("String not found");
+ }
+ }
+
+ private static void FindUnicode(string SearchString)
+ {
+ FindNextUnicode(SearchString);
+ }
+
+ private static void FindFirstBytes(string SearchString)
+ {
+ CurrentVirtualAddressTarget = (UInt32)AnalyzedFile.File.ImageBase;
+ WriteLog("Set search start point to virtual address: 0x" + CurrentVirtualAddressTarget.ToString("X8"));
+ FindNextBytes(SearchString);
+ }
+
+ private static void FindNextBytes(string SearchString)
+ {
+ if (AnalyzedFile == null)
+ throw new ScriptExecutionException("PatchFile not defined");
+
+ WriteLog("Looking for bytes: " + SearchString);
+ byte[] Bytes = GetBytesFromString(SearchString);
+ UInt32? FindIndex = ByteOperations.FindPattern(FileBuffer, AnalyzedFile.File.ConvertVirtualAddressToRawOffset(CurrentVirtualAddressTarget), null, Bytes, null, null);
+ FindSuccess = FindIndex != null;
+ if (FindIndex != null)
+ {
+ CurrentVirtualAddressTarget = AnalyzedFile.File.ConvertRawOffsetToVirtualAddress((UInt32)FindIndex);
+ WriteLog("Binary search pattern found at virtual address: 0x" + CurrentVirtualAddressTarget.ToString("X8"));
+ }
+ else
+ {
+ WriteLog("Binary search pattern not found");
+ }
+ }
+
+ private static void FindBytes(string SearchString)
+ {
+ FindNextBytes(SearchString);
+ }
+
+ private static byte[] GetBytesFromString(string Bytes)
+ {
+ Bytes = Bytes.ToUpper().Replace("0X", "");
+ for (int i = Bytes.Length -1 ; i >= 0; i--)
+ {
+ char CurrentChar = Bytes[i];
+ if (((CurrentChar < '0') || (CurrentChar > '9')) && ((CurrentChar < 'A') || (CurrentChar > 'F')))
+ Bytes = Bytes.Substring(0, i) + Bytes[(i + 1)..];
+ }
+ if ((Bytes.Length % 2) > 0)
+ throw new ScriptExecutionException("Not a valid binary search string: " + Bytes);
+ byte[] Result = new byte[Bytes.Length / 2];
+ for (int i = 0; i < (Bytes.Length / 2); i++)
+ Result[i] = byte.Parse(Bytes.Substring(i * 2, 2), System.Globalization.NumberStyles.HexNumber);
+ return Result;
+ }
+
+ private static void JumpToReference(int ReferenceIndex, string CodePattern, string R0, string R1, string R2, string R3, string Result)
+ {
+ if (AnalyzedFile == null)
+ throw new ScriptExecutionException("PatchFile not defined");
+
+ WriteLog("Looking for reference " + (ReferenceIndex == 0 ? "" : "with index " + ReferenceIndex.ToString() + " ") + "to virtual address: 0x" + CurrentVirtualAddressTarget.ToString("X8"));
+ int FoundIndex = -1;
+ for (int i = 0; i < AnalyzedFile.Code.Count; i++)
+ {
+ if ((AnalyzedFile.Code.Values[i].Operand.Contains("#0x" + CurrentVirtualAddressTarget.ToString("X8"), StringComparison.OrdinalIgnoreCase)) ||
+ (AnalyzedFile.Code.Values[i].Operand.Contains("#0x" + CurrentVirtualAddressTarget.ToString("X"), StringComparison.OrdinalIgnoreCase)))
+ {
+ // Reference found. Now check criteria.
+ bool Match = false;
+
+ if (CodePattern != null)
+ {
+ string[] PatternLines = CodePattern.Split(new char[] { ';' });
+ if (i < (PatternLines.Length - 1))
+ continue;
+ for (int j = 0; j < PatternLines.Length; j++)
+ {
+ int k = i - PatternLines.Length + j + 1;
+ Match = MatchPattern(AnalyzedFile.Code.Values[k], PatternLines[j]);
+ if (!Match)
+ break;
+ }
+ if (!Match)
+ continue;
+ }
+
+ if (R0 != null)
+ {
+ const string Register = "r0";
+ string Pattern = R0;
+ Match = false;
+ int j = i - 1;
+ do
+ {
+ List Tokens = ArmThumbTokenizer(AnalyzedFile.Code.Values[j].Operand);
+ if ((Tokens.Count > 0) && (Tokens[0].Text == Register))
+ {
+ Match = MatchPattern(AnalyzedFile.Code.Values[j], Pattern);
+ break;
+ }
+ j--;
+ }
+ while (j >= 0);
+ if (!Match)
+ continue;
+ }
+
+ if (R1 != null)
+ {
+ const string Register = "r1";
+ string Pattern = R1;
+ Match = false;
+ int j = i - 1;
+ do
+ {
+ List Tokens = ArmThumbTokenizer(AnalyzedFile.Code.Values[j].Operand);
+ if ((Tokens.Count > 0) && (Tokens[0].Text == Register))
+ {
+ Match = MatchPattern(AnalyzedFile.Code.Values[j], Pattern);
+ break;
+ }
+ j--;
+ }
+ while (j >= 0);
+ if (!Match)
+ continue;
+ }
+
+ if (R2 != null)
+ {
+ const string Register = "r2";
+ string Pattern = R2;
+ Match = false;
+ int j = i - 1;
+ do
+ {
+ List Tokens = ArmThumbTokenizer(AnalyzedFile.Code.Values[j].Operand);
+ if ((Tokens.Count > 0) && (Tokens[0].Text == Register))
+ {
+ Match = MatchPattern(AnalyzedFile.Code.Values[j], Pattern);
+ break;
+ }
+ j--;
+ }
+ while (j >= 0);
+ if (!Match)
+ continue;
+ }
+
+ if (R3 != null)
+ {
+ const string Register = "r3";
+ string Pattern = R3;
+ Match = false;
+ int j = i - 1;
+ do
+ {
+ List Tokens = ArmThumbTokenizer(AnalyzedFile.Code.Values[j].Operand);
+ if ((Tokens.Count > 0) && (Tokens[0].Text == Register))
+ {
+ Match = MatchPattern(AnalyzedFile.Code.Values[j], Pattern);
+ break;
+ }
+ j--;
+ }
+ while (j >= 0);
+ if (!Match)
+ continue;
+ }
+
+ if (Result != null)
+ {
+ const string Register = "r0";
+ string Pattern = Result;
+ Match = false;
+ int j = i + 1;
+ do
+ {
+ List Tokens = ArmThumbTokenizer(AnalyzedFile.Code.Values[j].Operand);
+ if ((Tokens.Count > 0) && Tokens.Any(t => t.Text == Register))
+ {
+ Match = MatchPattern(AnalyzedFile.Code.Values[j], Pattern);
+ break;
+ }
+ j++;
+ }
+ while (j < AnalyzedFile.Code.Count);
+ if (!Match)
+ continue;
+ }
+
+ FoundIndex++;
+ if (FoundIndex == ReferenceIndex)
+ {
+ JumpHistory.Add(CurrentVirtualAddressTarget);
+ CurrentVirtualAddressTarget = AnalyzedFile.Code.Values[i].Address;
+ WriteLog("Found reference in code at virtual address: 0x" + CurrentVirtualAddressTarget.ToString("X8"));
+ FindSuccess = true;
+ return;
+ }
+ }
+ }
+
+ // throw new ScriptExecutionException("Reference not found");
+ FindSuccess = false;
+ }
+
+ private static bool MatchPattern(ArmInstruction Instruction, string Pattern)
+ {
+ List PatternTokens = ArmThumbTokenizer(Pattern);
+ return MatchPattern(Instruction, PatternTokens);
+ }
+
+ private static bool MatchPattern(ArmInstruction Instruction, List PatternTokens)
+ {
+ List InstructionTokens = null;
+ try
+ {
+ InstructionTokens = ArmThumbTokenizer(Instruction.Mnemonic + " " + Instruction.Operand);
+ }
+ catch { }
+ if (InstructionTokens == null)
+ return false;
+
+ // Sanity check
+ if ((InstructionTokens.Count == 0) || (PatternTokens.Count == 0))
+ return false;
+
+ // Complete wildcard pattern
+ if (PatternTokens[0].Text == "?")
+ return true;
+
+ // instruction must match
+ if ((InstructionTokens[0].Text != PatternTokens[0].Text) && (InstructionTokens[0].Text != (PatternTokens[0].Text + ".w")))
+ return false;
+
+ if ((PatternTokens.Count == 1) || ((PatternTokens.Count == 2) && (PatternTokens[1].Text == "?")))
+ return true;
+
+ for (int i = 1; i < InstructionTokens.Count; i++)
+ {
+ if (PatternTokens[i].Text == "?")
+ continue;
+
+ if ((PatternTokens[i].Text == "r?") && InstructionTokens[i].Text.StartsWith("r"))
+ continue;
+
+ if (PatternTokens[i].Text == InstructionTokens[i].Text)
+ continue;
+
+ if ((PatternTokens[i].Type == TokenType.Number) && (InstructionTokens[i].Type == TokenType.Number) && (PatternTokens[i].Value == InstructionTokens[i].Value))
+ continue;
+
+ return false;
+ }
+
+ return true;
+ }
+
+ private static void FindFirstInstructionPattern(string Pattern, int InstructionIndex)
+ {
+ CurrentVirtualAddressTarget = (UInt32)AnalyzedFile.File.ImageBase;
+ WriteLog("Set search start point to virtual address: 0x" + CurrentVirtualAddressTarget.ToString("X8"));
+ FindNextInstructionPattern(Pattern, InstructionIndex);
+ }
+
+ private static void FindNextInstructionPattern(string Pattern, int InstructionIndex)
+ {
+ if (AnalyzedFile == null)
+ throw new ScriptExecutionException("PatchFile not defined");
+
+ WriteLog("Looking for instruction-pattern");
+
+ List> PatternTokens = new();
+ string[] PatternInstructions = Pattern.Split(new char[] { ';' });
+ for (int i = 0; i < PatternInstructions.Length; i++)
+ PatternTokens.Add(ArmThumbTokenizer(PatternInstructions[i]));
+
+ for (int i = AnalyzedFile.Code.IndexOfKey(CurrentVirtualAddressTarget) + 1; i < AnalyzedFile.Code.Count; i++)
+ {
+ bool IsMatch = true;
+
+ for (int j = 0; j < PatternTokens.Count; j++)
+ {
+ if (!MatchPattern(AnalyzedFile.Code.Values[i + j], PatternTokens[j]))
+ {
+ IsMatch = false;
+ break;
+ }
+ }
+
+ if (IsMatch)
+ {
+ FindSuccess = true;
+ CurrentVirtualAddressTarget = AnalyzedFile.Code.Values[i + InstructionIndex].Address;
+ WriteLog("Found instruction-pattern at virtual address: 0x" + CurrentVirtualAddressTarget.ToString("X8"));
+ return;
+ }
+ }
+
+ FindSuccess = false;
+ WriteLog("Instruction-pattern not found");
+ }
+
+ private static void FindPreviousInstructionPattern(string Pattern, int InstructionIndex)
+ {
+ if (AnalyzedFile == null)
+ throw new ScriptExecutionException("PatchFile not defined");
+
+ WriteLog("Looking for instruction-pattern");
+
+ List> PatternTokens = new();
+ string[] PatternInstructions = Pattern.Split(new char[] { ';' });
+ for (int i = 0; i < PatternInstructions.Length; i++)
+ PatternTokens.Add(ArmThumbTokenizer(PatternInstructions[i]));
+
+ for (int i = AnalyzedFile.Code.IndexOfKey(CurrentVirtualAddressTarget) - 1; i >= 0; i--)
+ {
+ bool IsMatch = true;
+
+ for (int j = 0; j < PatternTokens.Count; j++)
+ {
+ if (!MatchPattern(AnalyzedFile.Code.Values[i + j], PatternTokens[j]))
+ {
+ IsMatch = false;
+ break;
+ }
+ }
+
+ if (IsMatch)
+ {
+ FindSuccess = true;
+ CurrentVirtualAddressTarget = AnalyzedFile.Code.Values[i + InstructionIndex].Address;
+ WriteLog("Found instruction-pattern at virtual address: 0x" + CurrentVirtualAddressTarget.ToString("X8"));
+ return;
+ }
+ }
+
+ FindSuccess = false;
+ WriteLog("Instruction-pattern not found");
+ }
+
+ private static void FindInstructionPattern(string Pattern, int InstructionIndex)
+ {
+ FindNextInstructionPattern(Pattern, InstructionIndex);
+ }
+
+ private static void FindFirstValue(UInt32 Value)
+ {
+ CurrentVirtualAddressTarget = (UInt32)AnalyzedFile.File.ImageBase;
+ WriteLog("Set search start point to virtual address: 0x" + CurrentVirtualAddressTarget.ToString("X8"));
+ FindNextValue(Value);
+ }
+
+ private static void FindPreviousValue(UInt32 Value)
+ {
+ if (AnalyzedFile == null)
+ throw new ScriptExecutionException("PatchFile not defined");
+
+ bool GotValue;
+ UInt32 FoundValue = 0;
+ WriteLog("Looking for previous value: 0x" + Value.ToString("X8"));
+ for (int i = AnalyzedFile.Code.IndexOfKey(CurrentVirtualAddressTarget) - 1; i >= 0; i--)
+ {
+ GotValue = false;
+
+ int Index = AnalyzedFile.Code.Values[i].Operand.IndexOf("#0x");
+ if (Index >= 0)
+ GotValue = UInt32.TryParse(AnalyzedFile.Code.Values[i].Operand[(Index + 3)..].TrimEnd(new char[] { ']' }), System.Globalization.NumberStyles.HexNumber, null, out FoundValue);
+ if (!GotValue)
+ {
+ Index = AnalyzedFile.Code.Values[i].Operand.IndexOf("#");
+ if (Index >= 0)
+ GotValue = UInt32.TryParse(AnalyzedFile.Code.Values[i].Operand[(Index + 1)..].TrimEnd(new char[] { ']' }), out FoundValue);
+ }
+ if (GotValue && (FoundValue == Value))
+ {
+ FindSuccess = true;
+ CurrentVirtualAddressTarget = AnalyzedFile.Code.Values[i].Address;
+ WriteLog("Found value in code at virtual address: 0x" + CurrentVirtualAddressTarget.ToString("X8"));
+ return;
+ }
+ }
+
+ FindSuccess = false;
+ WriteLog("Value not found");
+ }
+
+ private static void FindNextValue(UInt32 Value)
+ {
+ if (AnalyzedFile == null)
+ throw new ScriptExecutionException("PatchFile not defined");
+
+ bool GotValue;
+ UInt32 FoundValue = 0;
+ WriteLog("Looking for value: 0x" + Value.ToString("X8"));
+ for (int i = AnalyzedFile.Code.IndexOfKey(CurrentVirtualAddressTarget) + 1; i < AnalyzedFile.Code.Count; i++)
+ {
+ GotValue = false;
+
+ int Index = AnalyzedFile.Code.Values[i].Operand.IndexOf("#0x");
+ if (Index >= 0)
+ GotValue = UInt32.TryParse(AnalyzedFile.Code.Values[i].Operand[(Index + 3)..].TrimEnd(new char[] { ']' }), System.Globalization.NumberStyles.HexNumber, null, out FoundValue);
+ if (!GotValue)
+ {
+ Index = AnalyzedFile.Code.Values[i].Operand.IndexOf("#");
+ if (Index >= 0)
+ GotValue = UInt32.TryParse(AnalyzedFile.Code.Values[i].Operand[(Index + 1)..].TrimEnd(new char[] { ']' }), out FoundValue);
+ }
+ if (GotValue && (FoundValue == Value))
+ {
+ FindSuccess = true;
+ CurrentVirtualAddressTarget = AnalyzedFile.Code.Values[i].Address;
+ WriteLog("Found value in code at virtual address: 0x" + CurrentVirtualAddressTarget.ToString("X8"));
+ return;
+ }
+ }
+
+ FindSuccess = false;
+ WriteLog("Value not found");
+ }
+
+ private static void FindValue(UInt32 Value)
+ {
+ FindNextValue(Value);
+ }
+
+ private static void FindPreviousConditionalJump()
+ {
+ if (AnalyzedFile == null)
+ throw new ScriptExecutionException("PatchFile not defined");
+
+ WriteLog("Looking for previous conditional jump");
+ for (int i = AnalyzedFile.Code.IndexOfKey(CurrentVirtualAddressTarget) - 1; i >= 0; i--)
+ {
+ if (ArmDisassembler.ConditionalJumpInstructions.Contains(AnalyzedFile.Code.Values[i].Mnemonic))
+ {
+ FindSuccess = true;
+ CurrentVirtualAddressTarget = AnalyzedFile.Code.Values[i].Address;
+ WriteLog("Found conditional jump at virtual address: 0x" + CurrentVirtualAddressTarget.ToString("X8"));
+ WriteLog(" " + AnalyzedFile.Code.Values[i].Mnemonic + " " + AnalyzedFile.Code.Values[i].Operand);
+ return;
+ }
+ }
+
+ FindSuccess = false;
+ WriteLog("Conditional jump not found");
+ }
+
+ private static void FindNextConditionalJump()
+ {
+ if (AnalyzedFile == null)
+ throw new ScriptExecutionException("PatchFile not defined");
+
+ WriteLog("Looking for next conditional jump");
+ for (int i = AnalyzedFile.Code.IndexOfKey(CurrentVirtualAddressTarget) + 1; i < AnalyzedFile.Code.Count; i++)
+ {
+ if (ArmDisassembler.ConditionalJumpInstructions.Contains(AnalyzedFile.Code.Values[i].Mnemonic))
+ {
+ FindSuccess = true;
+ CurrentVirtualAddressTarget = AnalyzedFile.Code.Values[i].Address;
+ WriteLog("Found conditional jump at virtual address: 0x" + CurrentVirtualAddressTarget.ToString("X8"));
+ WriteLog(" " + AnalyzedFile.Code.Values[i].Mnemonic + " " + AnalyzedFile.Code.Values[i].Operand);
+ return;
+ }
+ }
+
+ FindSuccess = false;
+ WriteLog("Conditional jump not found");
+ }
+
+ private static void MakeJumpUnconditional(string Instruction)
+ {
+ if (AnalyzedFile == null)
+ throw new ScriptExecutionException("PatchFile not defined");
+
+ ArmInstruction CurrentInstruction = AnalyzedFile.Code[CurrentVirtualAddressTarget];
+ if (ArmDisassembler.ConditionalJumpInstructions.Contains(CurrentInstruction.Mnemonic))
+ {
+ if ((Instruction == null) || (string.Equals(Instruction, CurrentInstruction.Mnemonic, StringComparison.CurrentCultureIgnoreCase)) || (string.Equals(Instruction + ".w", CurrentInstruction.Mnemonic, StringComparison.CurrentCultureIgnoreCase)))
+ {
+ WriteLog("Making instruction unconditional at virtual address: 0x" + CurrentVirtualAddressTarget.ToString("X8"));
+ string AddressString = CurrentInstruction.Operand[(CurrentInstruction.Operand.IndexOf("#0x") + 3)..];
+ UInt32 Address = Convert.ToUInt32(AddressString, 16);
+ string NewInstruction = CurrentInstruction.Mnemonic.EndsWith(".w") ? "b.w" : "b";
+ WriteLog(" Original: " + CurrentInstruction.Mnemonic + " " + CurrentInstruction.Operand);
+ WriteLog(" Patch: " + NewInstruction + " #0x" + AddressString);
+ string NewCode = NewInstruction + " 0x" + AddressString;
+ byte[] CompiledCode = Compile(NewCode);
+ PatchAtVirtualAddress(CompiledCode, CurrentVirtualAddressTarget);
+ CurrentVirtualAddressTarget += (UInt32)CompiledCode.Length;
+ }
+ else
+ {
+ WriteLog("Looking for conditional jump: " + Instruction);
+ WriteLog("Instead this conditional jump was found: " + CurrentInstruction.Mnemonic + " " + CurrentInstruction.Operand);
+ WriteLog("Instead of making the jump unconditional, the jump will be cleared");
+ string NewInstruction = (CurrentInstruction.Bytes.Length == 2) ? "nop" : "nop.w";
+ WriteLog("Patch: " + NewInstruction);
+ byte[] CompiledCode = Compile(NewInstruction);
+ PatchAtVirtualAddress(CompiledCode, CurrentVirtualAddressTarget);
+ CurrentVirtualAddressTarget += (UInt32)CompiledCode.Length;
+ }
+ }
+ else
+ {
+ throw new ScriptExecutionException("Instruction cannot be made unconditional because it isn't a jump-instruction");
+ }
+ }
+
+ private static void PatchChecksum()
+ {
+ if (AnalyzedFile == null)
+ throw new ScriptExecutionException("PatchFile not defined");
+
+ WriteLog("Calculating new checksum for file");
+ UInt32 ChecksumOffset = AnalyzedFile.File.GetChecksumOffset();
+ UInt32 Checksum = AnalyzedFile.File.CalculateChecksum();
+ byte[] ChecksumBytes = new byte[4];
+ ByteOperations.WriteUInt32(ChecksumBytes, 0, Checksum);
+ PatchAtRawOffset(ChecksumBytes, ChecksumOffset);
+ }
+
+ private static void PatchCode(string CodeType, string AsmCode)
+ {
+ if (AnalyzedFile == null)
+ throw new ScriptExecutionException("PatchFile not defined");
+
+ Patcher.CodeType PatcherCodeType = Patcher.CodeType.Thumb2;
+ if (CodeType != null)
+ {
+ PatcherCodeType = CodeType.ToLower() switch
+ {
+ "arm" => Patcher.CodeType.ARM,
+ "thumb" => Patcher.CodeType.Thumb,
+ "thumb2" => Patcher.CodeType.Thumb2,
+ _ => throw new ScriptExecutionException("Invalid Assembly Type"),
+ };
+ }
+
+ WriteLog("Compiling new code at virtual address: 0x" + CurrentVirtualAddressTarget.ToString("X8"));
+ byte[] CompiledCode = Compile(PatcherCodeType, AsmCode);
+ PatchAtVirtualAddress(CompiledCode, CurrentVirtualAddressTarget);
+
+ CurrentVirtualAddressTarget += (UInt32)CompiledCode.Length;
+ if (!AnalyzedFile.Code.ContainsKey(CurrentVirtualAddressTarget))
+ CurrentVirtualAddressTarget += 2;
+ if (!AnalyzedFile.Code.ContainsKey(CurrentVirtualAddressTarget))
+ {
+ for (int i = 0; i < AnalyzedFile.Code.Count; i++)
+ {
+ if (AnalyzedFile.Code.Values[i].Address > CurrentVirtualAddressTarget)
+ {
+ CurrentVirtualAddressTarget = AnalyzedFile.Code.Values[i].Address;
+ break;
+ }
+ }
+ }
+ }
+
+ private static void JumpToImport(string FunctionName)
+ {
+ if (AnalyzedFile == null)
+ throw new ScriptExecutionException("PatchFile not defined");
+
+ FunctionDescriptor Import = AnalyzedFile.File.Imports.Find(i => string.Equals(i.Name, FunctionName, StringComparison.CurrentCultureIgnoreCase));
+ if (Import == null)
+ {
+ throw new ScriptExecutionException("Import not found: " + FunctionName);
+ }
+ else
+ {
+ WriteLog("Import " + FunctionName + " found at: 0x" + Import.VirtualAddress.ToString("X8"));
+ JumpHistory.Add(CurrentVirtualAddressTarget);
+ CurrentVirtualAddressTarget = Import.VirtualAddress;
+ }
+ }
+
+ private static void JumpToExport(string FunctionName)
+ {
+ if (AnalyzedFile == null)
+ throw new ScriptExecutionException("PatchFile not defined");
+
+ FunctionDescriptor Export = AnalyzedFile.File.Exports.Find(i => string.Equals(i.Name, FunctionName, StringComparison.CurrentCultureIgnoreCase));
+ if (Export == null)
+ {
+ throw new ScriptExecutionException("Export not found: " + FunctionName);
+ }
+ else
+ {
+ WriteLog("Export " + FunctionName + " found at: 0x" + Export.VirtualAddress.ToString("X8"));
+ JumpHistory.Add(CurrentVirtualAddressTarget);
+ CurrentVirtualAddressTarget = Export.VirtualAddress;
+ }
+ }
+
+ private static void FindPreviousInstruction(string Instruction)
+ {
+ if (AnalyzedFile == null)
+ throw new ScriptExecutionException("PatchFile not defined");
+
+ WriteLog("Looking for previous instruction: " + Instruction);
+ for (int i = AnalyzedFile.Code.IndexOfKey(CurrentVirtualAddressTarget) - 1; i >= 0; i--)
+ {
+ if ((string.Equals(Instruction, AnalyzedFile.Code.Values[i].Mnemonic, StringComparison.CurrentCultureIgnoreCase)) || (string.Equals(Instruction + ".W", AnalyzedFile.Code.Values[i].Mnemonic, StringComparison.CurrentCultureIgnoreCase)))
+ {
+ FindSuccess = true;
+ CurrentVirtualAddressTarget = AnalyzedFile.Code.Values[i].Address;
+ WriteLog("Found instruction at virtual address: 0x" + CurrentVirtualAddressTarget.ToString("X8"));
+ return;
+ }
+ }
+
+ FindSuccess = false;
+ WriteLog("Instruction not found");
+ }
+
+ private static void FindNextInstruction(string Instruction)
+ {
+ if (AnalyzedFile == null)
+ throw new ScriptExecutionException("PatchFile not defined");
+
+ WriteLog("Looking for instruction: " + Instruction);
+ for (int i = AnalyzedFile.Code.IndexOfKey(CurrentVirtualAddressTarget) + 1; i < AnalyzedFile.Code.Count; i++)
+ {
+ if ((string.Equals(Instruction, AnalyzedFile.Code.Values[i].Mnemonic, StringComparison.CurrentCultureIgnoreCase)) || (string.Equals(Instruction + ".W", AnalyzedFile.Code.Values[i].Mnemonic, StringComparison.CurrentCultureIgnoreCase)))
+ {
+ FindSuccess = true;
+ CurrentVirtualAddressTarget = AnalyzedFile.Code.Values[i].Address;
+ WriteLog("Found instruction at virtual address: 0x" + CurrentVirtualAddressTarget.ToString("X8"));
+ return;
+ }
+ }
+
+ FindSuccess = false;
+ WriteLog("Instruction not found");
+ }
+
+ private static void FindInstruction(string Instruction)
+ {
+ FindNextInstruction(Instruction);
+ }
+
+ private static void CreateLabel(string Label)
+ {
+ if (AnalyzedFile == null)
+ throw new ScriptExecutionException("PatchFile not defined");
+
+ if (Labels.Any(l => string.Equals(l.Name, Label, StringComparison.CurrentCultureIgnoreCase)))
+ throw new ScriptExecutionException("Label already exists: " + Label);
+
+ Labels.Add(new FunctionDescriptor() { Name = Label, VirtualAddress = CurrentVirtualAddressTarget });
+ WriteLog("Label created: " + Label + " = 0x" + CurrentVirtualAddressTarget.ToString("X8"));
+ }
+
+ private static string GetLabelsForAsm()
+ {
+ string Result = "";
+ foreach (FunctionDescriptor Label in Labels)
+ {
+ Result += Label.Name + " EQU 0x" + Label.VirtualAddress.ToString("X8") + Environment.NewLine;
+ }
+ return Result;
+ }
+
+ private static void CompareInstructionGo(string Instruction, string Label)
+ {
+ if (AnalyzedFile == null)
+ throw new ScriptExecutionException("PatchFile not defined");
+
+ string CurrentInstruction = AnalyzedFile.Code[CurrentVirtualAddressTarget].Mnemonic;
+ WriteLog("Current instruction: " + CurrentInstruction);
+ if ((string.Equals(CurrentInstruction, Instruction, StringComparison.CurrentCultureIgnoreCase)) || (string.Equals(CurrentInstruction, Instruction + ".w", StringComparison.CurrentCultureIgnoreCase)))
+ Go(Label);
+ }
+
+ private static void ClearInstruction()
+ {
+ if (AnalyzedFile == null)
+ throw new ScriptExecutionException("PatchFile not defined");
+
+ ArmInstruction CurrentInstruction = AnalyzedFile.Code[CurrentVirtualAddressTarget];
+ WriteLog("clearing instruction at virtual address: 0x" + CurrentVirtualAddressTarget.ToString("X8"));
+ string Instruction = (CurrentInstruction.Bytes.Length == 2) ? "nop" : "nop.w";
+ WriteLog(" Original: " + CurrentInstruction.Mnemonic + " " + CurrentInstruction.Operand);
+ WriteLog(" Patch: " + Instruction);
+ byte[] CompiledCode = Compile(Instruction);
+ PatchAtVirtualAddress(CompiledCode, CurrentVirtualAddressTarget);
+ CurrentVirtualAddressTarget += (UInt32)CompiledCode.Length;
+ }
+
+ private static byte[] Compile(string AsmCode)
+ {
+ return Compile(CodeType.Thumb2, AsmCode);
+ }
+
+ private static byte[] Compile(CodeType Type, string AsmCode)
+ {
+ byte[] Result = ArmCompiler.Compile(PathToVisualStudio, CurrentVirtualAddressTarget, Type, GetLabelsForAsm() + AsmCode);
+ if (Result == null)
+ throw new ScriptExecutionException("ARM compiler output: " + ArmCompiler.Output);
+ else
+ return Result;
+ }
+
+ private static void Go(string Label)
+ {
+ if (AnalyzedFile == null)
+ throw new ScriptExecutionException("PatchFile not defined");
+
+ CodeLine NewLine = ScriptCode.Find(l => string.Equals(l.Label, Label, StringComparison.CurrentCultureIgnoreCase));
+ if (NewLine == null)
+ {
+ throw new ScriptExecutionException("Label " + Label + " not found");
+ }
+ else
+ {
+ Pointer = ScriptCode.IndexOf(NewLine);
+ WriteLog("Go to label: " + Label);
+ }
+ }
+
+ private static void PatchUnicode(string Text)
+ {
+ if (AnalyzedFile == null)
+ throw new ScriptExecutionException("PatchFile not defined");
+
+ WriteLog("Patching zero-terminated unicode string: " + Text);
+ if (!Text.EndsWith("\0"))
+ Text += "\0";
+ byte[] Bytes = Encoding.Unicode.GetBytes(Text);
+ PatchAtVirtualAddress(Bytes, CurrentVirtualAddressTarget);
+ CurrentVirtualAddressTarget += (UInt32)Bytes.Length;
+ }
+
+ private static void PatchAscii(string Text)
+ {
+ if (AnalyzedFile == null)
+ throw new ScriptExecutionException("PatchFile not defined");
+
+ WriteLog("Patching zero-terminated ascii string: " + Text);
+ if (!Text.EndsWith("\0"))
+ Text += "\0";
+ byte[] Bytes = Encoding.ASCII.GetBytes(Text);
+ PatchAtVirtualAddress(Bytes, CurrentVirtualAddressTarget);
+ CurrentVirtualAddressTarget += (UInt32)Bytes.Length;
+ }
+
+ private static void JumpToTarget()
+ {
+ if (AnalyzedFile == null)
+ throw new ScriptExecutionException("PatchFile not defined");
+
+ ArmInstruction CurrentInstruction = AnalyzedFile.Code[CurrentVirtualAddressTarget];
+ string AddressString = CurrentInstruction.Operand[(CurrentInstruction.Operand.IndexOf("#0x") + 3)..];
+ if (UInt32.TryParse(AddressString, System.Globalization.NumberStyles.HexNumber, null, out uint Address))
+ {
+ if (AnalyzedFile.File.GetSectionForVirtualAddress(Address) == null)
+ throw new ScriptExecutionException("Target at virtual address 0x" + Address.ToString("X8") + " is invalid");
+ WriteLog("Jumping to target: 0x" + Address.ToString("X8"));
+ JumpHistory.Add(CurrentVirtualAddressTarget);
+ CurrentVirtualAddressTarget = Address;
+ }
+ else
+ {
+ throw new ScriptExecutionException("Could not jump to target: " + CurrentInstruction.Operand);
+ }
+ }
+
+ private static void JumpToLabel(string Label)
+ {
+ if (AnalyzedFile == null)
+ throw new ScriptExecutionException("PatchFile not defined");
+
+ FunctionDescriptor FoundLabel = Labels.Find(l => string.Equals(l.Name, Label, StringComparison.CurrentCultureIgnoreCase));
+ if (FoundLabel == null)
+ throw new ScriptExecutionException("Label not found: " + Label);
+
+ WriteLog("Jumping to label: " + Label);
+ WriteLog("New virtual address: 0x" + FoundLabel.VirtualAddress.ToString("X8"));
+ JumpHistory.Add(CurrentVirtualAddressTarget);
+ CurrentVirtualAddressTarget = FoundLabel.VirtualAddress;
+ }
+
+ private static void IfFoundGo(string Label)
+ {
+ if (AnalyzedFile == null)
+ throw new ScriptExecutionException("PatchFile not defined");
+
+ if (FindSuccess)
+ {
+ FunctionDescriptor FoundLabel = Labels.Find(l => string.Equals(l.Name, Label, StringComparison.CurrentCultureIgnoreCase));
+ if (FoundLabel == null)
+ throw new ScriptExecutionException("Label not found: " + Label);
+
+ WriteLog("Condition was found, jumping to label: " + Label);
+ WriteLog("New virtual address: 0x" + FoundLabel.VirtualAddress.ToString("X8"));
+ CurrentVirtualAddressTarget = FoundLabel.VirtualAddress;
+ }
+ }
+
+ private static void IfNotFoundGo(string Label)
+ {
+ if (AnalyzedFile == null)
+ throw new ScriptExecutionException("PatchFile not defined");
+
+ if (!FindSuccess)
+ {
+ FunctionDescriptor FoundLabel = Labels.Find(l => string.Equals(l.Name, Label, StringComparison.CurrentCultureIgnoreCase));
+ if (FoundLabel == null)
+ throw new ScriptExecutionException("Label not found: " + Label);
+
+ WriteLog("Condition was not found, jumping to label: " + Label);
+ WriteLog("New virtual address: 0x" + FoundLabel.VirtualAddress.ToString("X8"));
+ CurrentVirtualAddressTarget = FoundLabel.VirtualAddress;
+ }
+ }
+
+ private static void IfFoundThrowError(string Message)
+ {
+ if (AnalyzedFile == null)
+ throw new ScriptExecutionException("PatchFile not defined");
+
+ if (FindSuccess)
+ {
+ if (Message == null)
+ throw new ScriptExecutionException("Script execution error");
+ else
+ throw new ScriptExecutionException(Message);
+ }
+ }
+
+ private static void IfNotFoundThrowError(string Message)
+ {
+ if (AnalyzedFile == null)
+ throw new ScriptExecutionException("PatchFile not defined");
+
+ if (!FindSuccess)
+ {
+ if (Message == null)
+ throw new ScriptExecutionException("Script execution error");
+ else
+ throw new ScriptExecutionException(Message);
+ }
+ }
+
+ private static void JumpBack()
+ {
+ if (AnalyzedFile == null)
+ throw new ScriptExecutionException("PatchFile not defined");
+
+ if (JumpHistory.Count == 0)
+ throw new ScriptExecutionException("No jump history, can't jump back");
+
+ CurrentVirtualAddressTarget = JumpHistory.Last();
+ JumpHistory.RemoveAt(JumpHistory.Count - 1);
+ WriteLog("Jumping back to: 0x" + CurrentVirtualAddressTarget.ToString("X8"));
+ }
+
+ private static void FindStore()
+ {
+ if (AnalyzedFile == null)
+ throw new ScriptExecutionException("PatchFile not defined");
+
+ string Operand = AnalyzedFile.Code[CurrentVirtualAddressTarget].Operand;
+ if (Operand.Contains(','))
+ {
+ string Register = Operand.Substring(0, Operand.IndexOf(',')).Trim();
+ if (Register.StartsWith("r"))
+ {
+ WriteLog("Looking for instruction where " + Register + " is being stored");
+
+ for (int i = AnalyzedFile.Code.IndexOfKey(CurrentVirtualAddressTarget) + 1; i < AnalyzedFile.Code.Count; i++)
+ {
+ if ((string.Equals("str", AnalyzedFile.Code.Values[i].Mnemonic, StringComparison.CurrentCultureIgnoreCase)) || (string.Equals("str.w", AnalyzedFile.Code.Values[i].Mnemonic, StringComparison.CurrentCultureIgnoreCase)))
+ {
+ Operand = AnalyzedFile.Code.Values[i].Operand;
+ if (Operand.Contains(','))
+ {
+ if (Register == Operand.Substring(0, Operand.IndexOf(',')).Trim())
+ {
+ FindSuccess = true;
+ CurrentVirtualAddressTarget = AnalyzedFile.Code.Values[i].Address;
+ WriteLog("Found instruction at virtual address: 0x" + CurrentVirtualAddressTarget.ToString("X8"));
+ return;
+ }
+ }
+ }
+ }
+
+ FindSuccess = false;
+ WriteLog("Instruction not found");
+ }
+ else
+ {
+ throw new ScriptExecutionException("Could not locate register to search for");
+ }
+ }
+ else
+ {
+ throw new ScriptExecutionException("Could not locate register to search for");
+ }
+ }
+
+ private static void FindFunctionCall(string R0, string R1, string R2, string R3, string Result)
+ {
+ FindNextFunctionCall(R0, R1, R2, R3, Result);
+ }
+
+ private static void FindNextFunctionCall(string R0, string R1, string R2, string R3, string Result)
+ {
+ if (AnalyzedFile == null)
+ throw new ScriptExecutionException("PatchFile not defined");
+
+ WriteLog("Looking for function call");
+ for (int i = AnalyzedFile.Code.IndexOfKey(CurrentVirtualAddressTarget) + 1; i < AnalyzedFile.Code.Count; i++)
+ {
+ if (AnalyzedFile.Code.Values[i].Mnemonic == "bl")
+ {
+ // Function call found. Now check criteria.
+ bool Match;
+
+ if (R0 != null)
+ {
+ const string Register = "r0";
+ string Pattern = R0;
+ Match = false;
+ int j = i - 1;
+ do
+ {
+ List Tokens = ArmThumbTokenizer(AnalyzedFile.Code.Values[j].Operand);
+ if ((Tokens.Count > 0) && (Tokens[0].Text == Register))
+ {
+ Match = MatchPattern(AnalyzedFile.Code.Values[j], Pattern);
+ break;
+ }
+ j--;
+ }
+ while (j >= 0);
+ if (!Match)
+ continue;
+ }
+
+ if (R1 != null)
+ {
+ const string Register = "r1";
+ string Pattern = R1;
+ Match = false;
+ int j = i - 1;
+ do
+ {
+ List Tokens = ArmThumbTokenizer(AnalyzedFile.Code.Values[j].Operand);
+ if ((Tokens.Count > 0) && (Tokens[0].Text == Register))
+ {
+ Match = MatchPattern(AnalyzedFile.Code.Values[j], Pattern);
+ break;
+ }
+ j--;
+ }
+ while (j >= 0);
+ if (!Match)
+ continue;
+ }
+
+ if (R2 != null)
+ {
+ const string Register = "r2";
+ string Pattern = R2;
+ Match = false;
+ int j = i - 1;
+ do
+ {
+ List Tokens = ArmThumbTokenizer(AnalyzedFile.Code.Values[j].Operand);
+ if ((Tokens.Count > 0) && (Tokens[0].Text == Register))
+ {
+ Match = MatchPattern(AnalyzedFile.Code.Values[j], Pattern);
+ break;
+ }
+ j--;
+ }
+ while (j >= 0);
+ if (!Match)
+ continue;
+ }
+
+ if (R3 != null)
+ {
+ const string Register = "r3";
+ string Pattern = R3;
+ Match = false;
+ int j = i - 1;
+ do
+ {
+ List Tokens = ArmThumbTokenizer(AnalyzedFile.Code.Values[j].Operand);
+ if ((Tokens.Count > 0) && (Tokens[0].Text == Register))
+ {
+ Match = MatchPattern(AnalyzedFile.Code.Values[j], Pattern);
+ break;
+ }
+ j--;
+ }
+ while (j >= 0);
+ if (!Match)
+ continue;
+ }
+
+ if (Result != null)
+ {
+ const string Register = "r0";
+ string Pattern = Result;
+ Match = false;
+ int j = i + 1;
+ do
+ {
+ List Tokens = ArmThumbTokenizer(AnalyzedFile.Code.Values[j].Operand);
+ if ((Tokens.Count > 0) && Tokens.Any(t => t.Text == Register))
+ {
+ Match = MatchPattern(AnalyzedFile.Code.Values[j], Pattern);
+ break;
+ }
+ j++;
+ }
+ while (j < AnalyzedFile.Code.Count);
+ if (!Match)
+ continue;
+ }
+
+ CurrentVirtualAddressTarget = AnalyzedFile.Code.Values[i].Address;
+ WriteLog("Found function-call in code at virtual address: 0x" + CurrentVirtualAddressTarget.ToString("X8"));
+ FindSuccess = true;
+ return;
+ }
+ }
+
+ // throw new ScriptExecutionException("Reference not found");
+ FindSuccess = false;
+ }
+
+ private static void FindPreviousFunctionCall(string R0, string R1, string R2, string R3, string Result)
+ {
+ if (AnalyzedFile == null)
+ throw new ScriptExecutionException("PatchFile not defined");
+
+ WriteLog("Looking for function call");
+ for (int i = AnalyzedFile.Code.IndexOfKey(CurrentVirtualAddressTarget) - 1; i >= 0; i--)
+ {
+ if (AnalyzedFile.Code.Values[i].Mnemonic == "bl")
+ {
+ // Function call found. Now check criteria.
+ bool Match;
+
+ if (R0 != null)
+ {
+ const string Register = "r0";
+ string Pattern = R0;
+ Match = false;
+ int j = i - 1;
+ do
+ {
+ List Tokens = ArmThumbTokenizer(AnalyzedFile.Code.Values[j].Operand);
+ if ((Tokens.Count > 0) && (Tokens[0].Text == Register))
+ {
+ Match = MatchPattern(AnalyzedFile.Code.Values[j], Pattern);
+ break;
+ }
+ j--;
+ }
+ while (j >= 0);
+ if (!Match)
+ continue;
+ }
+
+ if (R1 != null)
+ {
+ const string Register = "r1";
+ string Pattern = R1;
+ Match = false;
+ int j = i - 1;
+ do
+ {
+ List Tokens = ArmThumbTokenizer(AnalyzedFile.Code.Values[j].Operand);
+ if ((Tokens.Count > 0) && (Tokens[0].Text == Register))
+ {
+ Match = MatchPattern(AnalyzedFile.Code.Values[j], Pattern);
+ break;
+ }
+ j--;
+ }
+ while (j >= 0);
+ if (!Match)
+ continue;
+ }
+
+ if (R2 != null)
+ {
+ const string Register = "r2";
+ string Pattern = R2;
+ Match = false;
+ int j = i - 1;
+ do
+ {
+ List Tokens = ArmThumbTokenizer(AnalyzedFile.Code.Values[j].Operand);
+ if ((Tokens.Count > 0) && (Tokens[0].Text == Register))
+ {
+ Match = MatchPattern(AnalyzedFile.Code.Values[j], Pattern);
+ break;
+ }
+ j--;
+ }
+ while (j >= 0);
+ if (!Match)
+ continue;
+ }
+
+ if (R3 != null)
+ {
+ const string Register = "r3";
+ string Pattern = R3;
+ Match = false;
+ int j = i - 1;
+ do
+ {
+ List Tokens = ArmThumbTokenizer(AnalyzedFile.Code.Values[j].Operand);
+ if ((Tokens.Count > 0) && (Tokens[0].Text == Register))
+ {
+ Match = MatchPattern(AnalyzedFile.Code.Values[j], Pattern);
+ break;
+ }
+ j--;
+ }
+ while (j >= 0);
+ if (!Match)
+ continue;
+ }
+
+ if (Result != null)
+ {
+ const string Register = "r0";
+ string Pattern = Result;
+ Match = false;
+ int j = i + 1;
+ do
+ {
+ List Tokens = ArmThumbTokenizer(AnalyzedFile.Code.Values[j].Operand);
+ if ((Tokens.Count > 0) && Tokens.Any(t => t.Text == Register))
+ {
+ Match = MatchPattern(AnalyzedFile.Code.Values[j], Pattern);
+ break;
+ }
+ j++;
+ }
+ while (j < AnalyzedFile.Code.Count);
+ if (!Match)
+ continue;
+ }
+
+ CurrentVirtualAddressTarget = AnalyzedFile.Code.Values[i].Address;
+ WriteLog("Found function-call in code at virtual address: 0x" + CurrentVirtualAddressTarget.ToString("X8"));
+ FindSuccess = true;
+ return;
+ }
+ }
+
+ // throw new ScriptExecutionException("Reference not found");
+ FindSuccess = false;
+ }
+ }
+
+ public class CodeLine
+ {
+ public string Label;
+ public string Code;
+ public string PatchCode;
+ }
+
+ public enum TokenType
+ {
+ Bracket,
+ Separator,
+ Operator,
+ Text,
+ Number
+ }
+
+ public class Token
+ {
+ public TokenType Type;
+ public string Text;
+ public UInt32 Value;
+ }
+
+ public class ScriptParserException: Exception
+ {
+ public ScriptParserException(string Message): base(Message)
+ {
+ }
+
+ public ScriptParserException() : base()
+ {
+ }
+
+ public ScriptParserException(string message, Exception innerException) : base(message, innerException)
+ {
+ }
+ }
+
+ public class ScriptExecutionException: Exception
+ {
+ public ScriptExecutionException(string Message) : base(Message)
+ {
+ }
+
+ public ScriptExecutionException(string Message, Exception InnerException) : base(Message, InnerException)
+ {
+ }
+
+ public ScriptExecutionException() : base()
+ {
+ }
+ }
+
+ ///
+ /// Extension method by: Oleg Zarevennyi
+ /// https://stackoverflow.com/a/45756981
+ ///
+ static public class StringExtensions
+ {
+ public static string Replace(this string str, string oldValue, string @newValue, StringComparison comparisonType)
+ {
+ if (str == null)
+ {
+ throw new ArgumentNullException(nameof(str));
+ }
+ if (str.Length == 0)
+ {
+ return str;
+ }
+ if (oldValue == null)
+ {
+ throw new ArgumentNullException(nameof(oldValue));
+ }
+ if (oldValue.Length == 0)
+ {
+ throw new ArgumentException("String cannot be of zero length.");
+ }
+
+ StringBuilder resultStringBuilder = new(str.Length);
+ bool isReplacementNullOrEmpty = string.IsNullOrEmpty(@newValue);
+
+ const int valueNotFound = -1;
+ int foundAt;
+ int startSearchFromIndex = 0;
+ while ((foundAt = str.IndexOf(oldValue, startSearchFromIndex, comparisonType)) != valueNotFound)
+ {
+ int @charsUntilReplacment = foundAt - startSearchFromIndex;
+ bool isNothingToAppend = @charsUntilReplacment == 0;
+ if (!isNothingToAppend)
+ {
+ resultStringBuilder.Append(str, startSearchFromIndex, @charsUntilReplacment);
+ }
+ if (!isReplacementNullOrEmpty)
+ {
+ resultStringBuilder.Append(@newValue);
+ }
+ startSearchFromIndex = foundAt + oldValue.Length;
+ if (startSearchFromIndex == str.Length)
+ {
+ return resultStringBuilder.ToString();
+ }
+ }
+
+ int @charsUntilStringEnd = str.Length - startSearchFromIndex;
+ resultStringBuilder.Append(str, startSearchFromIndex, @charsUntilStringEnd);
+
+ return resultStringBuilder.ToString();
+ }
+ }
+}
diff --git a/Patcher/Patcher.sln b/Patcher/Patcher.sln
new file mode 100644
index 0000000..e3dfc6b
--- /dev/null
+++ b/Patcher/Patcher.sln
@@ -0,0 +1,81 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 15
+VisualStudioVersion = 15.0.28010.2041
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Patcher", "Patcher\Patcher.csproj", "{B67C62AE-86C4-4C18-99AB-4E94A3E09D36}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoPatcher", "AutoPatcher\AutoPatcher.csproj", "{95CF9509-C1C4-40F5-A60E-9D93EA6F438C}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ .NET45Debug|Any CPU = .NET45Debug|Any CPU
+ .NET45Debug|x64 = .NET45Debug|x64
+ .NET45Debug|x86 = .NET45Debug|x86
+ .NET45Release|Any CPU = .NET45Release|Any CPU
+ .NET45Release|x64 = .NET45Release|x64
+ .NET45Release|x86 = .NET45Release|x86
+ Debug|Any CPU = Debug|Any CPU
+ Debug|x64 = Debug|x64
+ Debug|x86 = Debug|x86
+ Release|Any CPU = Release|Any CPU
+ Release|x64 = Release|x64
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {B67C62AE-86C4-4C18-99AB-4E94A3E09D36}..NET45Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B67C62AE-86C4-4C18-99AB-4E94A3E09D36}..NET45Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B67C62AE-86C4-4C18-99AB-4E94A3E09D36}..NET45Debug|x64.ActiveCfg = Debug|x64
+ {B67C62AE-86C4-4C18-99AB-4E94A3E09D36}..NET45Debug|x64.Build.0 = Debug|x64
+ {B67C62AE-86C4-4C18-99AB-4E94A3E09D36}..NET45Debug|x86.ActiveCfg = Debug|x86
+ {B67C62AE-86C4-4C18-99AB-4E94A3E09D36}..NET45Debug|x86.Build.0 = Debug|x86
+ {B67C62AE-86C4-4C18-99AB-4E94A3E09D36}..NET45Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B67C62AE-86C4-4C18-99AB-4E94A3E09D36}..NET45Release|Any CPU.Build.0 = Release|Any CPU
+ {B67C62AE-86C4-4C18-99AB-4E94A3E09D36}..NET45Release|x64.ActiveCfg = Release|x64
+ {B67C62AE-86C4-4C18-99AB-4E94A3E09D36}..NET45Release|x64.Build.0 = Release|x64
+ {B67C62AE-86C4-4C18-99AB-4E94A3E09D36}..NET45Release|x86.ActiveCfg = Release|x86
+ {B67C62AE-86C4-4C18-99AB-4E94A3E09D36}..NET45Release|x86.Build.0 = Release|x86
+ {B67C62AE-86C4-4C18-99AB-4E94A3E09D36}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B67C62AE-86C4-4C18-99AB-4E94A3E09D36}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B67C62AE-86C4-4C18-99AB-4E94A3E09D36}.Debug|x64.ActiveCfg = Debug|x64
+ {B67C62AE-86C4-4C18-99AB-4E94A3E09D36}.Debug|x64.Build.0 = Debug|x64
+ {B67C62AE-86C4-4C18-99AB-4E94A3E09D36}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {B67C62AE-86C4-4C18-99AB-4E94A3E09D36}.Debug|x86.Build.0 = Debug|Any CPU
+ {B67C62AE-86C4-4C18-99AB-4E94A3E09D36}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B67C62AE-86C4-4C18-99AB-4E94A3E09D36}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B67C62AE-86C4-4C18-99AB-4E94A3E09D36}.Release|x64.ActiveCfg = Release|x64
+ {B67C62AE-86C4-4C18-99AB-4E94A3E09D36}.Release|x64.Build.0 = Release|x64
+ {B67C62AE-86C4-4C18-99AB-4E94A3E09D36}.Release|x86.ActiveCfg = Release|Any CPU
+ {B67C62AE-86C4-4C18-99AB-4E94A3E09D36}.Release|x86.Build.0 = Release|Any CPU
+ {95CF9509-C1C4-40F5-A60E-9D93EA6F438C}..NET45Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {95CF9509-C1C4-40F5-A60E-9D93EA6F438C}..NET45Debug|Any CPU.Build.0 = Debug|Any CPU
+ {95CF9509-C1C4-40F5-A60E-9D93EA6F438C}..NET45Debug|x64.ActiveCfg = Debug|x64
+ {95CF9509-C1C4-40F5-A60E-9D93EA6F438C}..NET45Debug|x64.Build.0 = Debug|x64
+ {95CF9509-C1C4-40F5-A60E-9D93EA6F438C}..NET45Debug|x86.ActiveCfg = Debug|x86
+ {95CF9509-C1C4-40F5-A60E-9D93EA6F438C}..NET45Debug|x86.Build.0 = Debug|x86
+ {95CF9509-C1C4-40F5-A60E-9D93EA6F438C}..NET45Release|Any CPU.ActiveCfg = Release|Any CPU
+ {95CF9509-C1C4-40F5-A60E-9D93EA6F438C}..NET45Release|Any CPU.Build.0 = Release|Any CPU
+ {95CF9509-C1C4-40F5-A60E-9D93EA6F438C}..NET45Release|x64.ActiveCfg = Release|x64
+ {95CF9509-C1C4-40F5-A60E-9D93EA6F438C}..NET45Release|x64.Build.0 = Release|x64
+ {95CF9509-C1C4-40F5-A60E-9D93EA6F438C}..NET45Release|x86.ActiveCfg = Release|x86
+ {95CF9509-C1C4-40F5-A60E-9D93EA6F438C}..NET45Release|x86.Build.0 = Release|x86
+ {95CF9509-C1C4-40F5-A60E-9D93EA6F438C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {95CF9509-C1C4-40F5-A60E-9D93EA6F438C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {95CF9509-C1C4-40F5-A60E-9D93EA6F438C}.Debug|x64.ActiveCfg = Debug|x64
+ {95CF9509-C1C4-40F5-A60E-9D93EA6F438C}.Debug|x64.Build.0 = Debug|x64
+ {95CF9509-C1C4-40F5-A60E-9D93EA6F438C}.Debug|x86.ActiveCfg = Debug|x86
+ {95CF9509-C1C4-40F5-A60E-9D93EA6F438C}.Debug|x86.Build.0 = Debug|x86
+ {95CF9509-C1C4-40F5-A60E-9D93EA6F438C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {95CF9509-C1C4-40F5-A60E-9D93EA6F438C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {95CF9509-C1C4-40F5-A60E-9D93EA6F438C}.Release|x64.ActiveCfg = Release|x64
+ {95CF9509-C1C4-40F5-A60E-9D93EA6F438C}.Release|x64.Build.0 = Release|x64
+ {95CF9509-C1C4-40F5-A60E-9D93EA6F438C}.Release|x86.ActiveCfg = Release|x86
+ {95CF9509-C1C4-40F5-A60E-9D93EA6F438C}.Release|x86.Build.0 = Release|x86
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {6763D5C5-7BCE-437C-9FD0-67BDD8D79FD2}
+ EndGlobalSection
+EndGlobal
diff --git a/Patcher/Patcher/App.config b/Patcher/Patcher/App.config
new file mode 100644
index 0000000..8e15646
--- /dev/null
+++ b/Patcher/Patcher/App.config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Patcher/Patcher/ArmCompiler.cs b/Patcher/Patcher/ArmCompiler.cs
new file mode 100644
index 0000000..408ee64
--- /dev/null
+++ b/Patcher/Patcher/ArmCompiler.cs
@@ -0,0 +1,229 @@
+// Copyright (c) 2018, Rene Lergner - wpinternals.net - @Heathcliff74xda
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Text;
+
+namespace Patcher
+{
+ public enum CodeType
+ {
+ ARM,
+ Thumb,
+ Thumb2
+ }
+
+ public static class ArmCompiler
+ {
+ public static Int32 LastErrorCode;
+ public static string Output;
+
+ public static byte[] Compile(string PathToVisualStudio, UInt32 Origin, CodeType CodeType, string ArmCodeFragment)
+ {
+ if (PathToVisualStudio.Length == 0)
+ {
+ Output = "ARM SDK is missing.";
+ return null;
+ }
+
+ string AssemblyFilePath = Path.GetTempFileName();
+ string ObjectFilePathTmp = Path.GetTempFileName();
+ string ObjectFilePath = ObjectFilePathTmp.Replace(".tmp", ".obj");
+ File.Move(ObjectFilePathTmp, ObjectFilePath);
+
+ string CodeTypeDirective = null;
+ switch (CodeType)
+ {
+ case Patcher.CodeType.ARM:
+ CodeTypeDirective = "CODE32";
+ break;
+ case Patcher.CodeType.Thumb:
+ CodeTypeDirective = "CODE16";
+ break;
+ case Patcher.CodeType.Thumb2:
+ CodeTypeDirective = "THUMB";
+ break;
+ }
+
+ string FullAssemblyCode =
+ " AREA ARM_AREA, CODE, READONLY" + Environment.NewLine +
+ " " + CodeTypeDirective + Environment.NewLine;
+
+ string ProcessedAssembly = ProcessArmCodeFragment(ArmCodeFragment, Origin, out uint Padding);
+
+ if (Padding > 0)
+ FullAssemblyCode += " SPACE " + Padding.ToString() + Environment.NewLine;
+
+ FullAssemblyCode +=
+ "start" + Environment.NewLine +
+ ProcessedAssembly;
+
+ FullAssemblyCode += " end" + Environment.NewLine;
+
+ File.WriteAllText(AssemblyFilePath, FullAssemblyCode);
+
+ string ArmAsmPath = MainForm.FindArmAsmPath(PathToVisualStudio);
+ string BinPath = MainForm.FindMSVCBinaryPaths(PathToVisualStudio).FirstOrDefault() ?? "";
+ ProcessStartInfo psi = new(Path.Combine(ArmAsmPath, "armasm.exe"));
+ psi.EnvironmentVariables["PATH"] += ";" + Path.Combine(PathToVisualStudio, @"Common7\IDE\");
+ psi.EnvironmentVariables["PATH"] += ";" + Path.Combine(PathToVisualStudio, @"Common7\Tools\");
+ psi.EnvironmentVariables["PATH"] += ";" + BinPath;
+ psi.EnvironmentVariables["PATH"] += ";" + ArmAsmPath;
+ psi.UseShellExecute = false;
+ psi.RedirectStandardOutput = true;
+ psi.CreateNoWindow = true;
+ psi.Arguments = "-g \"" + AssemblyFilePath + "\" \"" + ObjectFilePath + "\"";
+ Process ArmAsmProcess = Process.Start(psi);
+ ArmAsmProcess.WaitForExit();
+ LastErrorCode = ArmAsmProcess.ExitCode;
+ if (ArmAsmProcess.ExitCode != 0)
+ {
+ using StreamReader reader = ArmAsmProcess.StandardOutput;
+ Output = reader.ReadToEnd();
+
+ string[] Lines = Output.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None);
+
+ Output = "";
+
+ foreach (string Line in Lines)
+ {
+ string Out = Line.Trim();
+ if (Out.Length == 0) continue;
+ if (Out.StartsWith("Microsoft (R) ARM Macro Assembler")) continue;
+ if (Out.StartsWith("Copyright (C) Microsoft Corporation")) continue;
+ if (Out.StartsWith(AssemblyFilePath))
+ {
+ Out = Out[AssemblyFilePath.Length..];
+ int P = Out.IndexOf(':');
+ Out = Out[(P + 1)..].Trim();
+ }
+ Output += Out + Environment.NewLine;
+ }
+ }
+
+ byte[] Result = null;
+
+ if (LastErrorCode == 0)
+ Result = COFF.ObjectFileParser.ParseObjectFile(ObjectFilePath).SectionHeaders.First(h => h.Name == "ARM_AREA").RawData;
+
+ File.Delete(AssemblyFilePath);
+ File.Delete(ObjectFilePath);
+
+ if ((Result != null) && (Padding > 0))
+ {
+ byte[] RemovedPadding = new byte[Result.Length - Padding];
+ Buffer.BlockCopy(Result, (int)Padding, RemovedPadding, 0, RemovedPadding.Length);
+ Result = RemovedPadding;
+ }
+
+ return Result;
+ }
+
+ private static string ProcessArmCodeFragment(string ArmCodeFragment, UInt32 Origin, out UInt32 Padding)
+ {
+ string[] BranchOpcodes = new string[] { "B", "BEQ", "BNE", "BCS", "BHS", "BCC", "BLO", "BMI", "BPL", "BVS", "BVC", "BHI", "BLS", "BGE", "BLT", "BGT", "BLE", "BAL"};
+
+ StringBuilder Result = new(1000);
+ Padding = 0;
+
+ string[] Lines = ArmCodeFragment.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None);
+ List> Labels = new();
+
+ foreach (string Line in Lines)
+ {
+ string Code = Line;
+ if (Line.Contains(':'))
+ {
+ string Label = Line.Substring(0, Line.IndexOf(':'));
+ Label = Label.Trim();
+ if (Label.Length > 0)
+ Result.AppendLine(Label);
+ Code = Line[(Line.IndexOf(':') + 1)..];
+ }
+
+ int EquPos = Code.IndexOf("EQU", StringComparison.OrdinalIgnoreCase);
+ if ((EquPos > 0) && (EquPos > 0) && (EquPos < (Code.Length - 3)))
+ {
+ if (new char[] { '\t', ' ' }.Contains(Line[EquPos - 1]) &&
+ new char[] { '\t', ' ' }.Contains(Line[EquPos + 3]))
+ {
+ Result.AppendLine(Line.Trim());
+ Labels.Add(new Tuple(Line.Substring(0, EquPos).Trim(), Line[(EquPos + 3)..].Trim()));
+ continue;
+ }
+ }
+
+ Code = Code.Trim();
+
+ bool IsAbsoluteAddress = false;
+
+ int OpcodeLength = Code.IndexOfAny(new char [] { '\t', ' ', '.' });
+ if (OpcodeLength > 0)
+ {
+ string Opcode = Code.Substring(0, OpcodeLength).ToUpper();
+
+ string PossibleAddress = null;
+
+ if (Opcode == "LDR")
+ {
+ PossibleAddress = Code[(Code.IndexOf(',') + 1)..].Trim();
+ }
+ else if (BranchOpcodes.Contains(Opcode))
+ {
+ PossibleAddress = Code[(Code.IndexOfAny(new char[] { '\t', ' ' }) + 1)..].Trim();
+ }
+
+ if (PossibleAddress != null)
+ {
+ foreach (Tuple Label in Labels)
+ {
+ if (string.Equals(Label.Item1, PossibleAddress, StringComparison.CurrentCultureIgnoreCase))
+ {
+ PossibleAddress = Label.Item2;
+ break;
+ }
+ }
+ if (PossibleAddress.StartsWith("0x"))
+ PossibleAddress = PossibleAddress[2..];
+ IsAbsoluteAddress = UInt32.TryParse(PossibleAddress, NumberStyles.HexNumber, CultureInfo.CurrentCulture, out uint ParsedValue);
+ if (IsAbsoluteAddress && (ParsedValue < Origin))
+ Padding = Math.Max(Padding, Origin - ParsedValue);
+ }
+ }
+
+ Result.Append(' ');
+ Result.Append(Code);
+ if (IsAbsoluteAddress)
+ {
+ Result.Append(" + start - 0x");
+ Result.Append(Origin.ToString("X8"));
+ }
+ Result.Append(Environment.NewLine);
+ }
+
+ return Result.ToString();
+ }
+ }
+}
diff --git a/Patcher/Patcher/ByteOperations.cs b/Patcher/Patcher/ByteOperations.cs
new file mode 100644
index 0000000..e1700c9
--- /dev/null
+++ b/Patcher/Patcher/ByteOperations.cs
@@ -0,0 +1,382 @@
+// Copyright (c) 2018, Rene Lergner - wpinternals.net - @Heathcliff74xda
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+
+using System;
+using System.IO;
+
+namespace WPinternals
+{
+ internal static class ByteOperations
+ {
+ internal static string ReadAsciiString(byte[] ByteArray, UInt32 Offset)
+ {
+ UInt32 Length = 0;
+ while (((Offset + Length) < ByteArray.Length) && (ByteArray[Offset + Length] != 0))
+ Length++;
+ byte[] Bytes = new byte[Length];
+ Buffer.BlockCopy(ByteArray, (int)Offset, Bytes, 0, (int)Length);
+ return System.Text.Encoding.ASCII.GetString(Bytes);
+ }
+
+ internal static string ReadAsciiString(byte[] ByteArray, UInt32 Offset, UInt32 Length)
+ {
+ byte[] Bytes = new byte[Length];
+ Buffer.BlockCopy(ByteArray, (int)Offset, Bytes, 0, (int)Length);
+ return System.Text.Encoding.ASCII.GetString(Bytes);
+ }
+
+ internal static string ReadUnicodeString(byte[] ByteArray, UInt32 Offset, UInt32 Length)
+ {
+ byte[] Bytes = new byte[Length];
+ Buffer.BlockCopy(ByteArray, (int)Offset, Bytes, 0, (int)Length);
+ return System.Text.Encoding.Unicode.GetString(Bytes);
+ }
+
+ internal static void WriteUnicodeString(byte[] ByteArray, UInt32 Offset, string Text, UInt32? MaxBufferLength = null)
+ {
+ if (MaxBufferLength != null)
+ Array.Clear(ByteArray, (int)Offset, (int)MaxBufferLength);
+
+ byte[] TextBytes = System.Text.UnicodeEncoding.Unicode.GetBytes(Text);
+ int WriteLength = TextBytes.Length;
+ if (WriteLength > MaxBufferLength)
+ WriteLength = (int)MaxBufferLength;
+
+ Buffer.BlockCopy(TextBytes, 0, ByteArray, (int)Offset, WriteLength);
+ }
+
+ internal static UInt32 ReadUInt32(byte[] ByteArray, UInt32 Offset)
+ {
+ // Assume CPU and FFU are both Little Endian
+ return BitConverter.ToUInt32(ByteArray, (int)Offset);
+ }
+
+ internal static void WriteUInt32(byte[] ByteArray, UInt32 Offset, UInt32 Value)
+ {
+ System.Buffer.BlockCopy(BitConverter.GetBytes(Value), 0, ByteArray, (int)Offset, 4);
+ }
+
+ internal static Int32 ReadInt32(byte[] ByteArray, UInt32 Offset)
+ {
+ // Assume CPU and FFU are both Little Endian
+ return BitConverter.ToInt32(ByteArray, (int)Offset);
+ }
+
+ internal static void WriteInt32(byte[] ByteArray, UInt32 Offset, Int32 Value)
+ {
+ System.Buffer.BlockCopy(BitConverter.GetBytes(Value), 0, ByteArray, (int)Offset, 4);
+ }
+
+ internal static UInt16 ReadUInt16(byte[] ByteArray, UInt32 Offset)
+ {
+ // Assume CPU and FFU are both Little Endian
+ return BitConverter.ToUInt16(ByteArray, (int)Offset);
+ }
+
+ internal static void WriteUInt16(byte[] ByteArray, UInt32 Offset, UInt16 Value)
+ {
+ System.Buffer.BlockCopy(BitConverter.GetBytes(Value), 0, ByteArray, (int)Offset, 2);
+ }
+
+ internal static Int16 ReadInt16(byte[] ByteArray, UInt32 Offset)
+ {
+ // Assume CPU and FFU are both Little Endian
+ return BitConverter.ToInt16(ByteArray, (int)Offset);
+ }
+
+ internal static void WriteInt16(byte[] ByteArray, UInt32 Offset, Int16 Value)
+ {
+ System.Buffer.BlockCopy(BitConverter.GetBytes(Value), 0, ByteArray, (int)Offset, 2);
+ }
+
+ internal static byte ReadUInt8(byte[] ByteArray, UInt32 Offset)
+ {
+ return ByteArray[Offset];
+ }
+
+ internal static void WriteUInt8(byte[] ByteArray, UInt32 Offset, byte Value)
+ {
+ ByteArray[Offset] = Value;
+ }
+
+ internal static UInt32 ReadUInt24(byte[] ByteArray, UInt32 Offset)
+ {
+ return (UInt32)(ByteArray[Offset] + (ByteArray[Offset + 1] << 8) + (ByteArray[Offset + 2] << 16));
+ }
+
+ internal static void WriteUInt24(byte[] ByteArray, UInt32 Offset, UInt32 Value)
+ {
+ System.Buffer.BlockCopy(BitConverter.GetBytes(Value), 0, ByteArray, (int)Offset, 3);
+ }
+
+ internal static UInt64 ReadUInt64(byte[] ByteArray, UInt32 Offset)
+ {
+ // Assume CPU and FFU are both Little Endian
+ return BitConverter.ToUInt64(ByteArray, (int)Offset);
+ }
+
+ internal static void WriteUInt64(byte[] ByteArray, UInt32 Offset, UInt64 Value)
+ {
+ System.Buffer.BlockCopy(BitConverter.GetBytes(Value), 0, ByteArray, (int)Offset, 8);
+ }
+
+ internal static Guid ReadGuid(byte[] ByteArray, UInt32 Offset)
+ {
+ byte[] GuidBuffer = new byte[0x10];
+ Buffer.BlockCopy(ByteArray, (int)Offset, GuidBuffer, 0, 0x10);
+ return new Guid(GuidBuffer);
+ }
+
+ internal static void WriteGuid(byte[] ByteArray, UInt32 Offset, Guid Value)
+ {
+ Buffer.BlockCopy(Value.ToByteArray(), 0, ByteArray, (int)Offset, 0x10);
+ }
+
+ internal static UInt32 Align(UInt32 Base, UInt32 Offset, UInt32 Alignment)
+ {
+ if (((Offset - Base) % Alignment) == 0)
+ return Offset;
+ else
+ return ((((Offset - Base) / Alignment) + 1) * Alignment) + Base;
+ }
+
+ internal static UInt32? FindPatternInFile(string FileName, byte[] Pattern, byte[] Mask, out byte[] OutPattern)
+ {
+ // The mask is optional.
+ // In the mask 0x00 means the value must match, and 0xFF means that this position is a wildcard.
+
+ UInt32? Result = null;
+
+ FileStream Stream = new(FileName, FileMode.Open, FileAccess.Read);
+
+ byte[] Buffer = new byte[0x10000 + Pattern.Length - 1];
+ UInt32 BytesInBuffer = 0;
+ UInt32 BytesRead;
+ UInt32 SearchPositionFile = 0;
+ UInt32 SearchPositionBuffer = 0;
+ UInt32 BufferFileOffset = 0; // Offset in file where data from buffer is located.
+ int i;
+
+ OutPattern = null;
+
+ while (SearchPositionFile <= (Stream.Length - Pattern.Length))
+ {
+ if ((SearchPositionBuffer + Pattern.Length) > BytesInBuffer)
+ {
+ // Need to read next chunk
+ if ((BytesInBuffer - SearchPositionBuffer) > 0)
+ {
+ System.Buffer.BlockCopy(Buffer, (int)SearchPositionBuffer, Buffer, 0, (int)(BytesInBuffer - SearchPositionBuffer));
+ }
+ uint BufferReadPosition = BytesInBuffer - SearchPositionBuffer;
+ BytesInBuffer -= SearchPositionBuffer;
+ BufferFileOffset += SearchPositionBuffer;
+ SearchPositionBuffer = 0;
+
+ BytesRead = (UInt32)Stream.Read(Buffer, (int)BufferReadPosition, Buffer.Length - (int)BufferReadPosition);
+ BytesInBuffer += BytesRead;
+ }
+
+ bool Match = true;
+ for (i = 0; i < Pattern.Length; i++)
+ {
+ if (Buffer[SearchPositionBuffer + i] != Pattern[i])
+ {
+ if ((Mask == null) || (Mask[i] == 0))
+ {
+ Match = false;
+ break;
+ }
+ }
+ }
+
+ if (Match)
+ {
+ Result = SearchPositionFile;
+
+ OutPattern = new byte[Pattern.Length];
+ System.Buffer.BlockCopy(Buffer, (int)SearchPositionBuffer, OutPattern, 0, Pattern.Length);
+ break;
+ }
+
+ SearchPositionBuffer++;
+ SearchPositionFile++;
+ }
+
+ Stream.Close();
+
+ return Result;
+ }
+
+ internal static UInt32? FindAscii(byte[] SourceBuffer, string Pattern)
+ {
+ return FindPattern(SourceBuffer, System.Text.ASCIIEncoding.ASCII.GetBytes((string)Pattern), null, null);
+ }
+
+ internal static UInt32? FindAscii(byte[] SourceBuffer, uint SourceOffset, string Pattern)
+ {
+ return FindPattern(SourceBuffer, SourceOffset, null, System.Text.ASCIIEncoding.ASCII.GetBytes((string)Pattern), null, null);
+ }
+
+ internal static UInt32? FindUnicode(byte[] SourceBuffer, string Pattern)
+ {
+ return FindPattern(SourceBuffer, System.Text.UnicodeEncoding.Unicode.GetBytes((string)Pattern), null, null);
+ }
+
+ internal static UInt32? FindUnicode(byte[] SourceBuffer, uint SourceOffset, string Pattern)
+ {
+ return FindPattern(SourceBuffer, SourceOffset, null, System.Text.UnicodeEncoding.Unicode.GetBytes((string)Pattern), null, null);
+ }
+
+ internal static UInt32? FindUint(byte[] SourceBuffer, UInt32 Pattern)
+ {
+ return FindPattern(SourceBuffer, BitConverter.GetBytes((UInt32)Pattern), null, null);
+ }
+
+ internal static UInt32? FindPattern(byte[] SourceBuffer, byte[] Pattern, byte[] Mask, byte[] OutPattern)
+ {
+ return FindPattern(SourceBuffer, 0, null, Pattern, Mask, OutPattern);
+ }
+
+ internal static UInt32? FindPattern(byte[] SourceBuffer, uint SourceOffset, uint? SourceSize, byte[] Pattern, byte[] Mask, byte[] OutPattern)
+ {
+ // The mask is optional.
+ // In the mask 0x00 means the value must match, and 0xFF means that this position is a wildcard.
+
+ UInt32? Result = null;
+
+ UInt32 SearchPosition = SourceOffset;
+ int i;
+
+ while ((SearchPosition <= (SourceBuffer.Length - Pattern.Length)) && ((SourceSize == null) || (SearchPosition <= (SourceOffset + SourceSize - Pattern.Length))))
+ {
+ bool Match = true;
+ for (i = 0; i < Pattern.Length; i++)
+ {
+ if (SourceBuffer[SearchPosition + i] != Pattern[i])
+ {
+ if ((Mask == null) || (Mask[i] == 0))
+ {
+ Match = false;
+ break;
+ }
+ }
+ }
+
+ if (Match)
+ {
+ Result = SearchPosition;
+
+ if (OutPattern != null)
+ System.Buffer.BlockCopy(SourceBuffer, (int)SearchPosition, OutPattern, 0, Pattern.Length);
+ break;
+ }
+
+ SearchPosition++;
+ }
+
+ return Result;
+ }
+
+ internal static byte CalculateChecksum8(byte[] Buffer, UInt32 Offset, UInt32 Size)
+ {
+ byte Checksum = 0;
+
+ for (UInt32 i = Offset; i < (Offset + Size); i++)
+ Checksum += Buffer[i];
+
+ return (byte)(0x100 - Checksum);
+ }
+
+ internal static UInt16 CalculateChecksum16(byte[] Buffer, UInt32 Offset, UInt32 Size)
+ {
+ UInt16 Checksum = 0;
+
+ for (UInt32 i = Offset; i < (Offset + Size - 1); i += 2)
+ Checksum += BitConverter.ToUInt16(Buffer, (int)i);
+
+ return (UInt16)(0x10000 - Checksum);
+ }
+
+ private static readonly UInt32[] CRC32Table = new UInt32[] {
+ 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F,
+ 0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
+ 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2,
+ 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
+ 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9,
+ 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
+ 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C,
+ 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
+ 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423,
+ 0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
+ 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, 0x01DB7106,
+ 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
+ 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D,
+ 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
+ 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950,
+ 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
+ 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7,
+ 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
+ 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA,
+ 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
+ 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81,
+ 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
+ 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84,
+ 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
+ 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB,
+ 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
+ 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E,
+ 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
+ 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55,
+ 0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
+ 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28,
+ 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
+ 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F,
+ 0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
+ 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242,
+ 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
+ 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69,
+ 0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
+ 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC,
+ 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
+ 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693,
+ 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
+ 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
+ };
+
+ internal static UInt32 CRC32(byte[] Input, UInt32 Offset, UInt32 Length)
+ {
+ if ((Input == null) || ((Offset + Length) > Input.Length))
+ throw new ArgumentException();
+
+ unchecked
+ {
+ uint crc = (uint)(((uint)0) ^ (-1));
+ for (var i = Offset; i < (Offset + Length); i++)
+ {
+ crc = (crc >> 8) ^ CRC32Table[ (crc ^ Input[i]) & 0xFF ];
+ }
+ crc = (uint)(crc ^ (-1));
+
+ return crc;
+ }
+ }
+ }
+}
diff --git a/Patcher/Patcher/HelperClasses.cs b/Patcher/Patcher/HelperClasses.cs
new file mode 100644
index 0000000..7d53634
--- /dev/null
+++ b/Patcher/Patcher/HelperClasses.cs
@@ -0,0 +1,66 @@
+// Copyright (c) 2018, Rene Lergner - wpinternals.net - @Heathcliff74xda
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+
+using System;
+using System.Text;
+
+namespace WPinternals
+{
+ public static class Converter
+ {
+ public static string ConvertHexToString(byte[] Bytes, string Separator)
+ {
+ StringBuilder s = new(1000);
+ for (int i = Bytes.GetLowerBound(0); i <= Bytes.GetUpperBound(0); i++)
+ {
+ if (i != Bytes.GetLowerBound(0))
+ s.Append(Separator);
+ s.Append(Bytes[i].ToString("X2"));
+ }
+ return s.ToString();
+ }
+
+ public static byte[] ConvertStringToHex(string HexString)
+ {
+ if (HexString.Length % 2 == 1)
+ throw new Exception("The binary key cannot have an odd number of digits");
+
+ byte[] arr = new byte[HexString.Length >> 1];
+
+ for (int i = 0; i < (HexString.Length >> 1); ++i)
+ {
+ arr[i] = (byte)((GetHexVal(HexString[i << 1]) << 4) + GetHexVal(HexString[(i << 1) + 1]));
+ }
+
+ return arr;
+ }
+
+ public static int GetHexVal(char hex)
+ {
+ int val = (int)hex;
+ //For uppercase A-F letters:
+ //return val - (val < 58 ? 48 : 55);
+ //For lowercase a-f letters:
+ //return val - (val < 58 ? 48 : 87);
+ //Or the two combined, but a bit slower:
+ return val - (val < 58 ? 48 : (val < 97 ? 55 : 87));
+ }
+ }
+}
diff --git a/Patcher/Patcher/LICENSE b/Patcher/Patcher/LICENSE
new file mode 100644
index 0000000..f879c1c
--- /dev/null
+++ b/Patcher/Patcher/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2018, Rene Lergner - wpinternals.net - @Heathcliff74xda
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/Patcher/Patcher/MainForm.Designer.cs b/Patcher/Patcher/MainForm.Designer.cs
new file mode 100644
index 0000000..fc529b5
--- /dev/null
+++ b/Patcher/Patcher/MainForm.Designer.cs
@@ -0,0 +1,426 @@
+namespace Patcher
+{
+ partial class MainForm
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ this.label1 = new System.Windows.Forms.Label();
+ this.txtVisualStudioPath = new System.Windows.Forms.TextBox();
+ this.FolderBrowserDialog = new System.Windows.Forms.FolderBrowserDialog();
+ this.OpenFileDialog = new System.Windows.Forms.OpenFileDialog();
+ this.SaveFileDialog = new System.Windows.Forms.SaveFileDialog();
+ this.cmdVisualStudioPath = new System.Windows.Forms.Button();
+ this.cmdInputFile = new System.Windows.Forms.Button();
+ this.txtInputFile = new System.Windows.Forms.TextBox();
+ this.label2 = new System.Windows.Forms.Label();
+ this.cmdOutputFile = new System.Windows.Forms.Button();
+ this.txtOutputFile = new System.Windows.Forms.TextBox();
+ this.label3 = new System.Windows.Forms.Label();
+ this.cmdPatchDefinitionsFile = new System.Windows.Forms.Button();
+ this.txtPatchDefinitionsFile = new System.Windows.Forms.TextBox();
+ this.label4 = new System.Windows.Forms.Label();
+ this.label5 = new System.Windows.Forms.Label();
+ this.cmbPatchDefinitionName = new System.Windows.Forms.ComboBox();
+ this.cmbTargetVersion = new System.Windows.Forms.ComboBox();
+ this.label6 = new System.Windows.Forms.Label();
+ this.cmbTargetPath = new System.Windows.Forms.ComboBox();
+ this.label7 = new System.Windows.Forms.Label();
+ this.label8 = new System.Windows.Forms.Label();
+ this.txtAssemblyCode = new System.Windows.Forms.TextBox();
+ this.txtCompiledOpcodes = new System.Windows.Forms.TextBox();
+ this.label9 = new System.Windows.Forms.Label();
+ this.txtVirtualOffset = new System.Windows.Forms.TextBox();
+ this.label10 = new System.Windows.Forms.Label();
+ this.cmbCodeType = new System.Windows.Forms.ComboBox();
+ this.label11 = new System.Windows.Forms.Label();
+ this.cmdCompile = new System.Windows.Forms.Button();
+ this.cmdPatch = new System.Windows.Forms.Button();
+ this.SuspendLayout();
+ //
+ // label1
+ //
+ this.label1.AutoSize = true;
+ this.label1.Location = new System.Drawing.Point(15, 13);
+ this.label1.Name = "label1";
+ this.label1.Size = new System.Drawing.Size(191, 13);
+ this.label1.TabIndex = 0;
+ this.label1.Text = "Path to Visual Studio with ARM32 SDK";
+ //
+ // txtVisualStudioPath
+ //
+ this.txtVisualStudioPath.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.txtVisualStudioPath.Location = new System.Drawing.Point(18, 29);
+ this.txtVisualStudioPath.Name = "txtVisualStudioPath";
+ this.txtVisualStudioPath.Size = new System.Drawing.Size(665, 20);
+ this.txtVisualStudioPath.TabIndex = 1;
+ //
+ // OpenFileDialog
+ //
+ this.OpenFileDialog.FileName = "openFileDialog1";
+ //
+ // cmdVisualStudioPath
+ //
+ this.cmdVisualStudioPath.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.cmdVisualStudioPath.Font = new System.Drawing.Font("Arial", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.cmdVisualStudioPath.Location = new System.Drawing.Point(689, 28);
+ this.cmdVisualStudioPath.Name = "cmdVisualStudioPath";
+ this.cmdVisualStudioPath.Size = new System.Drawing.Size(35, 22);
+ this.cmdVisualStudioPath.TabIndex = 2;
+ this.cmdVisualStudioPath.Text = "...";
+ this.cmdVisualStudioPath.UseVisualStyleBackColor = true;
+ this.cmdVisualStudioPath.Click += new System.EventHandler(this.cmdVisualStudioPath_Click);
+ //
+ // cmdInputFile
+ //
+ this.cmdInputFile.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.cmdInputFile.Font = new System.Drawing.Font("Arial", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.cmdInputFile.Location = new System.Drawing.Point(689, 301);
+ this.cmdInputFile.Name = "cmdInputFile";
+ this.cmdInputFile.Size = new System.Drawing.Size(35, 22);
+ this.cmdInputFile.TabIndex = 14;
+ this.cmdInputFile.Text = "...";
+ this.cmdInputFile.UseVisualStyleBackColor = true;
+ this.cmdInputFile.Click += new System.EventHandler(this.cmdInputFile_Click);
+ //
+ // txtInputFile
+ //
+ this.txtInputFile.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.txtInputFile.Location = new System.Drawing.Point(18, 302);
+ this.txtInputFile.Name = "txtInputFile";
+ this.txtInputFile.Size = new System.Drawing.Size(665, 20);
+ this.txtInputFile.TabIndex = 13;
+ //
+ // label2
+ //
+ this.label2.AutoSize = true;
+ this.label2.Location = new System.Drawing.Point(15, 286);
+ this.label2.Name = "label2";
+ this.label2.Size = new System.Drawing.Size(47, 13);
+ this.label2.TabIndex = 12;
+ this.label2.Text = "Input file";
+ //
+ // cmdOutputFile
+ //
+ this.cmdOutputFile.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.cmdOutputFile.Font = new System.Drawing.Font("Arial", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.cmdOutputFile.Location = new System.Drawing.Point(689, 349);
+ this.cmdOutputFile.Name = "cmdOutputFile";
+ this.cmdOutputFile.Size = new System.Drawing.Size(35, 22);
+ this.cmdOutputFile.TabIndex = 17;
+ this.cmdOutputFile.Text = "...";
+ this.cmdOutputFile.UseVisualStyleBackColor = true;
+ this.cmdOutputFile.Click += new System.EventHandler(this.cmdOutputFile_Click);
+ //
+ // txtOutputFile
+ //
+ this.txtOutputFile.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.txtOutputFile.Location = new System.Drawing.Point(18, 350);
+ this.txtOutputFile.Name = "txtOutputFile";
+ this.txtOutputFile.Size = new System.Drawing.Size(665, 20);
+ this.txtOutputFile.TabIndex = 16;
+ //
+ // label3
+ //
+ this.label3.AutoSize = true;
+ this.label3.Location = new System.Drawing.Point(15, 334);
+ this.label3.Name = "label3";
+ this.label3.Size = new System.Drawing.Size(101, 13);
+ this.label3.TabIndex = 15;
+ this.label3.Text = "Output file (optional)";
+ //
+ // cmdPatchDefinitionsFile
+ //
+ this.cmdPatchDefinitionsFile.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.cmdPatchDefinitionsFile.Font = new System.Drawing.Font("Arial", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.cmdPatchDefinitionsFile.Location = new System.Drawing.Point(689, 91);
+ this.cmdPatchDefinitionsFile.Name = "cmdPatchDefinitionsFile";
+ this.cmdPatchDefinitionsFile.Size = new System.Drawing.Size(35, 22);
+ this.cmdPatchDefinitionsFile.TabIndex = 5;
+ this.cmdPatchDefinitionsFile.Text = "...";
+ this.cmdPatchDefinitionsFile.UseVisualStyleBackColor = true;
+ this.cmdPatchDefinitionsFile.Click += new System.EventHandler(this.cmdPatchDefinitionsFile_Click);
+ //
+ // txtPatchDefinitionsFile
+ //
+ this.txtPatchDefinitionsFile.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.txtPatchDefinitionsFile.Location = new System.Drawing.Point(18, 92);
+ this.txtPatchDefinitionsFile.Name = "txtPatchDefinitionsFile";
+ this.txtPatchDefinitionsFile.Size = new System.Drawing.Size(665, 20);
+ this.txtPatchDefinitionsFile.TabIndex = 4;
+ this.txtPatchDefinitionsFile.Leave += new System.EventHandler(this.txtPatchDefinitionsFile_Leave);
+ //
+ // label4
+ //
+ this.label4.AutoSize = true;
+ this.label4.Location = new System.Drawing.Point(15, 76);
+ this.label4.Name = "label4";
+ this.label4.Size = new System.Drawing.Size(117, 13);
+ this.label4.TabIndex = 3;
+ this.label4.Text = "Patch defintions xml-file";
+ //
+ // label5
+ //
+ this.label5.AutoSize = true;
+ this.label5.Location = new System.Drawing.Point(15, 124);
+ this.label5.Name = "label5";
+ this.label5.Size = new System.Drawing.Size(107, 13);
+ this.label5.TabIndex = 6;
+ this.label5.Text = "Patch defintion name";
+ //
+ // cmbPatchDefinitionName
+ //
+ this.cmbPatchDefinitionName.FormattingEnabled = true;
+ this.cmbPatchDefinitionName.Location = new System.Drawing.Point(18, 140);
+ this.cmbPatchDefinitionName.Name = "cmbPatchDefinitionName";
+ this.cmbPatchDefinitionName.Size = new System.Drawing.Size(302, 21);
+ this.cmbPatchDefinitionName.TabIndex = 7;
+ this.cmbPatchDefinitionName.SelectedValueChanged += new System.EventHandler(this.cmbPatchDefinitionName_SelectedValueChanged);
+ this.cmbPatchDefinitionName.Leave += new System.EventHandler(this.cmbPatchDefinitionName_Leave);
+ //
+ // cmbTargetVersion
+ //
+ this.cmbTargetVersion.FormattingEnabled = true;
+ this.cmbTargetVersion.Location = new System.Drawing.Point(18, 189);
+ this.cmbTargetVersion.Name = "cmbTargetVersion";
+ this.cmbTargetVersion.Size = new System.Drawing.Size(302, 21);
+ this.cmbTargetVersion.TabIndex = 9;
+ this.cmbTargetVersion.SelectedValueChanged += new System.EventHandler(this.cmbTargetVersion_SelectedValueChanged);
+ this.cmbTargetVersion.Leave += new System.EventHandler(this.cmbTargetVersion_Leave);
+ //
+ // label6
+ //
+ this.label6.AutoSize = true;
+ this.label6.Location = new System.Drawing.Point(15, 173);
+ this.label6.Name = "label6";
+ this.label6.Size = new System.Drawing.Size(129, 13);
+ this.label6.TabIndex = 8;
+ this.label6.Text = "Target version description";
+ //
+ // cmbTargetPath
+ //
+ this.cmbTargetPath.FormattingEnabled = true;
+ this.cmbTargetPath.Location = new System.Drawing.Point(18, 238);
+ this.cmbTargetPath.Name = "cmbTargetPath";
+ this.cmbTargetPath.Size = new System.Drawing.Size(302, 21);
+ this.cmbTargetPath.TabIndex = 11;
+ //
+ // label7
+ //
+ this.label7.AutoSize = true;
+ this.label7.Location = new System.Drawing.Point(15, 222);
+ this.label7.Name = "label7";
+ this.label7.Size = new System.Drawing.Size(269, 13);
+ this.label7.TabIndex = 10;
+ this.label7.Text = "Folder for target file relative to Patch Defintion rootfolder";
+ //
+ // label8
+ //
+ this.label8.AutoSize = true;
+ this.label8.Location = new System.Drawing.Point(15, 497);
+ this.label8.Name = "label8";
+ this.label8.Size = new System.Drawing.Size(365, 13);
+ this.label8.TabIndex = 22;
+ this.label8.Text = "ARM32 Patch / Shell assembly code (labels need to be followed by a colon)";
+ //
+ // txtAssemblyCode
+ //
+ this.txtAssemblyCode.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+ | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.txtAssemblyCode.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.txtAssemblyCode.Location = new System.Drawing.Point(18, 513);
+ this.txtAssemblyCode.Multiline = true;
+ this.txtAssemblyCode.Name = "txtAssemblyCode";
+ this.txtAssemblyCode.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
+ this.txtAssemblyCode.Size = new System.Drawing.Size(706, 161);
+ this.txtAssemblyCode.TabIndex = 23;
+ //
+ // txtCompiledOpcodes
+ //
+ this.txtCompiledOpcodes.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.txtCompiledOpcodes.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.txtCompiledOpcodes.Location = new System.Drawing.Point(18, 713);
+ this.txtCompiledOpcodes.Multiline = true;
+ this.txtCompiledOpcodes.Name = "txtCompiledOpcodes";
+ this.txtCompiledOpcodes.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
+ this.txtCompiledOpcodes.Size = new System.Drawing.Size(706, 59);
+ this.txtCompiledOpcodes.TabIndex = 25;
+ //
+ // label9
+ //
+ this.label9.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.label9.AutoSize = true;
+ this.label9.Location = new System.Drawing.Point(15, 697);
+ this.label9.Name = "label9";
+ this.label9.Size = new System.Drawing.Size(94, 13);
+ this.label9.TabIndex = 24;
+ this.label9.Text = "Compiled opcodes";
+ //
+ // txtVirtualOffset
+ //
+ this.txtVirtualOffset.Location = new System.Drawing.Point(18, 416);
+ this.txtVirtualOffset.Name = "txtVirtualOffset";
+ this.txtVirtualOffset.Size = new System.Drawing.Size(302, 20);
+ this.txtVirtualOffset.TabIndex = 19;
+ //
+ // label10
+ //
+ this.label10.AutoSize = true;
+ this.label10.Location = new System.Drawing.Point(15, 400);
+ this.label10.Name = "label10";
+ this.label10.Size = new System.Drawing.Size(309, 13);
+ this.label10.TabIndex = 18;
+ this.label10.Text = "Virtual offset (hex) (leave empty for only recalculating checksum)";
+ //
+ // cmbCodeType
+ //
+ this.cmbCodeType.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.cmbCodeType.FormattingEnabled = true;
+ this.cmbCodeType.Items.AddRange(new object[] {
+ "ARM",
+ "Thumb",
+ "Thumb2"});
+ this.cmbCodeType.Location = new System.Drawing.Point(18, 464);
+ this.cmbCodeType.Name = "cmbCodeType";
+ this.cmbCodeType.Size = new System.Drawing.Size(302, 21);
+ this.cmbCodeType.TabIndex = 21;
+ //
+ // label11
+ //
+ this.label11.AutoSize = true;
+ this.label11.Location = new System.Drawing.Point(15, 448);
+ this.label11.Name = "label11";
+ this.label11.Size = new System.Drawing.Size(55, 13);
+ this.label11.TabIndex = 20;
+ this.label11.Text = "Code type";
+ //
+ // cmdCompile
+ //
+ this.cmdCompile.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+ this.cmdCompile.Location = new System.Drawing.Point(470, 792);
+ this.cmdCompile.Name = "cmdCompile";
+ this.cmdCompile.Size = new System.Drawing.Size(120, 33);
+ this.cmdCompile.TabIndex = 26;
+ this.cmdCompile.Text = "Compile";
+ this.cmdCompile.UseVisualStyleBackColor = true;
+ this.cmdCompile.Click += new System.EventHandler(this.cmdCompile_Click);
+ //
+ // cmdPatch
+ //
+ this.cmdPatch.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+ this.cmdPatch.Location = new System.Drawing.Point(604, 792);
+ this.cmdPatch.Name = "cmdPatch";
+ this.cmdPatch.Size = new System.Drawing.Size(120, 33);
+ this.cmdPatch.TabIndex = 27;
+ this.cmdPatch.Text = "Patch";
+ this.cmdPatch.UseVisualStyleBackColor = true;
+ this.cmdPatch.Click += new System.EventHandler(this.cmdPatch_Click);
+ //
+ // MainForm
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.ClientSize = new System.Drawing.Size(742, 840);
+ this.Controls.Add(this.cmdPatch);
+ this.Controls.Add(this.cmdCompile);
+ this.Controls.Add(this.cmbCodeType);
+ this.Controls.Add(this.label11);
+ this.Controls.Add(this.txtVirtualOffset);
+ this.Controls.Add(this.label10);
+ this.Controls.Add(this.txtCompiledOpcodes);
+ this.Controls.Add(this.label9);
+ this.Controls.Add(this.txtAssemblyCode);
+ this.Controls.Add(this.label8);
+ this.Controls.Add(this.cmbTargetPath);
+ this.Controls.Add(this.label7);
+ this.Controls.Add(this.cmbTargetVersion);
+ this.Controls.Add(this.label6);
+ this.Controls.Add(this.cmbPatchDefinitionName);
+ this.Controls.Add(this.label5);
+ this.Controls.Add(this.cmdPatchDefinitionsFile);
+ this.Controls.Add(this.txtPatchDefinitionsFile);
+ this.Controls.Add(this.label4);
+ this.Controls.Add(this.cmdOutputFile);
+ this.Controls.Add(this.txtOutputFile);
+ this.Controls.Add(this.label3);
+ this.Controls.Add(this.cmdInputFile);
+ this.Controls.Add(this.txtInputFile);
+ this.Controls.Add(this.label2);
+ this.Controls.Add(this.cmdVisualStudioPath);
+ this.Controls.Add(this.txtVisualStudioPath);
+ this.Controls.Add(this.label1);
+ this.Name = "MainForm";
+ this.Text = "ARM patcher by Rene Lergner";
+ this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.MainForm_FormClosed);
+ this.Load += new System.EventHandler(this.MainForm_Load);
+ this.ResumeLayout(false);
+ this.PerformLayout();
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.Label label1;
+ private System.Windows.Forms.TextBox txtVisualStudioPath;
+ private System.Windows.Forms.FolderBrowserDialog FolderBrowserDialog;
+ private System.Windows.Forms.OpenFileDialog OpenFileDialog;
+ private System.Windows.Forms.SaveFileDialog SaveFileDialog;
+ private System.Windows.Forms.Button cmdVisualStudioPath;
+ private System.Windows.Forms.Button cmdInputFile;
+ private System.Windows.Forms.TextBox txtInputFile;
+ private System.Windows.Forms.Label label2;
+ private System.Windows.Forms.Button cmdOutputFile;
+ private System.Windows.Forms.TextBox txtOutputFile;
+ private System.Windows.Forms.Label label3;
+ private System.Windows.Forms.Button cmdPatchDefinitionsFile;
+ private System.Windows.Forms.TextBox txtPatchDefinitionsFile;
+ private System.Windows.Forms.Label label4;
+ private System.Windows.Forms.Label label5;
+ private System.Windows.Forms.ComboBox cmbPatchDefinitionName;
+ private System.Windows.Forms.ComboBox cmbTargetVersion;
+ private System.Windows.Forms.Label label6;
+ private System.Windows.Forms.ComboBox cmbTargetPath;
+ private System.Windows.Forms.Label label7;
+ private System.Windows.Forms.Label label8;
+ private System.Windows.Forms.TextBox txtAssemblyCode;
+ private System.Windows.Forms.TextBox txtCompiledOpcodes;
+ private System.Windows.Forms.Label label9;
+ private System.Windows.Forms.TextBox txtVirtualOffset;
+ private System.Windows.Forms.Label label10;
+ private System.Windows.Forms.ComboBox cmbCodeType;
+ private System.Windows.Forms.Label label11;
+ private System.Windows.Forms.Button cmdCompile;
+ private System.Windows.Forms.Button cmdPatch;
+ }
+}
+
diff --git a/Patcher/Patcher/MainForm.cs b/Patcher/Patcher/MainForm.cs
new file mode 100644
index 0000000..df0e7f2
--- /dev/null
+++ b/Patcher/Patcher/MainForm.cs
@@ -0,0 +1,410 @@
+// Copyright (c) 2018, Rene Lergner - wpinternals.net - @Heathcliff74xda
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+
+using Microsoft.Win32;
+using System;
+using System.Collections.Generic;
+using System.Data;
+using System.IO;
+using System.Linq;
+using System.Windows.Forms;
+using WPinternals;
+
+namespace Patcher
+{
+ public partial class MainForm : Form
+ {
+ public MainForm()
+ {
+ InitializeComponent();
+ }
+
+ private void MainForm_Load(object sender, EventArgs e)
+ {
+ cmbCodeType.SelectedItem = "Thumb2";
+ LoadPaths();
+ CenterToScreen();
+ }
+
+ private void cmdCompile_Click(object sender, EventArgs e)
+ {
+ string VirtualOffsetString = txtVirtualOffset.Text.Trim();
+ if (VirtualOffsetString.StartsWith("0x", StringComparison.OrdinalIgnoreCase))
+ VirtualOffsetString = VirtualOffsetString[2..];
+
+ CodeType CodeType = Patcher.CodeType.Thumb2;
+ byte[] CompiledCode = null;
+ if (UInt32.TryParse(VirtualOffsetString, System.Globalization.NumberStyles.HexNumber, null, out uint VirtualOffset))
+ {
+ CodeType = (CodeType)Enum.Parse(typeof(Patcher.CodeType), cmbCodeType.SelectedItem.ToString());
+ CompiledCode = ArmCompiler.Compile(txtVisualStudioPath.Text, VirtualOffset, CodeType, txtAssemblyCode.Text);
+ }
+ if ((VirtualOffset != 0) && (CompiledCode == null))
+ txtCompiledOpcodes.Text = ArmCompiler.Output;
+ else if (CompiledCode != null)
+ txtCompiledOpcodes.Text = Converter.ConvertHexToString(CompiledCode, " ");
+ }
+
+ private void LoadPaths()
+ {
+ RegistryKey Key = Registry.CurrentUser.OpenSubKey(@"Software\Patcher", true) ?? Registry.CurrentUser.CreateSubKey(@"Software\Patcher");
+
+ txtVisualStudioPath.Text = (string)Key.GetValue("VisualStudioPath", "");
+ if (txtVisualStudioPath.Text.Length == 0)
+ txtVisualStudioPath.Text = FindVisualStudioPath();
+
+ txtPatchDefinitionsFile.Text = (string)Key.GetValue("PatchDefinitionsFilePath", "");
+ cmbPatchDefinitionName.Text = (string)Key.GetValue("PatchDefinitionName", "");
+ cmbTargetVersion.Text = (string)Key.GetValue("TargetVersion", "");
+ cmbTargetPath.Text = (string)Key.GetValue("TargetFilePath", "");
+ txtInputFile.Text = (string)Key.GetValue("InputFilePath", "");
+ txtOutputFile.Text = (string)Key.GetValue("OutputFilePath", "");
+
+ LoadPatchDefinitions();
+ }
+
+ public static string[] FindMSVCBinaryPaths(string s)
+ {
+ string LegacyPath = Path.Combine(s, @"VC\bin");
+ if (Directory.Exists(LegacyPath))
+ {
+ return new string[] { LegacyPath };
+ }
+
+ if (Directory.Exists(Path.Combine(s, @"VC\Tools\MSVC")))
+ {
+ IEnumerable MSVCs = Directory.EnumerateDirectories(Path.Combine(s, @"VC\Tools\MSVC"));
+ IEnumerable Bins = MSVCs.Select(s => Path.Combine(s, "bin")).Where(s => Directory.Exists(s));
+ return Bins.ToArray();
+ }
+
+ return Array.Empty();
+ }
+
+ public static string FindArmAsmPath(string s)
+ {
+ foreach (string MSVCBin in FindMSVCBinaryPaths(s))
+ {
+ string path1 = Path.Combine(MSVCBin, "x86_arm");
+ string path2 = Path.Combine(MSVCBin, @"Hostx86\arm");
+
+ if (File.Exists(Path.Combine(path1, "armasm.exe")))
+ {
+ return path1;
+ }
+
+ if (File.Exists(Path.Combine(path2, "armasm.exe")))
+ {
+ return path2;
+ }
+ }
+
+ return "";
+ }
+
+ private static string FindVisualStudioPath()
+ {
+ IEnumerable MainX86VSDirectories = Directory.EnumerateDirectories(@"C:\Program Files (x86)\", "Microsoft Visual Studio*");
+ IEnumerable MainX64VSDirectories = Directory.EnumerateDirectories(@"C:\Program Files\", "Microsoft Visual Studio*");
+
+ IEnumerable MainVSDirectories = MainX86VSDirectories.Union(MainX64VSDirectories);
+
+ IEnumerable SubMainVSDirectories = MainVSDirectories.SelectMany(s => Directory.EnumerateDirectories(s));
+ IEnumerable SubSubMainVSDirectories = SubMainVSDirectories.SelectMany(s => Directory.EnumerateDirectories(s));
+ IEnumerable Directories = MainVSDirectories.Union(SubMainVSDirectories).Union(SubSubMainVSDirectories);
+
+ string attempt1 = Directories.Where(s => FindArmAsmPath(s) != "").OrderByDescending(s => File.GetCreationTime(Path.Combine(s, @"VC\bin\x86_arm\armasm.exe"))).FirstOrDefault() ?? "";
+
+ if (attempt1 != "")
+ return attempt1;
+
+ return Directories.Where(s => Directory.Exists(Path.Combine(s, @"VC\Tools\MSVC"))).Select(s => Path.Combine(s, @"VC\Tools\MSVC")).SelectMany(s => Directory.EnumerateDirectories(s)).Where(s => File.Exists(Path.Combine(s, @"bin\Hostx86\arm\armasm.exe"))).OrderByDescending(s => File.GetCreationTime(Path.Combine(s, @"bin\Hostx86\arm\armasm.exe"))).FirstOrDefault() ?? "";
+ }
+
+ private void StorePaths()
+ {
+ RegistryKey Key = Registry.CurrentUser.OpenSubKey(@"Software\Patcher", true) ?? Registry.CurrentUser.CreateSubKey(@"Software\Patcher");
+
+ string VisualStudioPath = txtVisualStudioPath.Text.Trim();
+ if (VisualStudioPath.Length == 0)
+ {
+ if (Key.GetValue("VisualStudioPath") != null)
+ Key.DeleteValue("VisualStudioPath");
+ }
+ else
+ {
+ Key.SetValue("VisualStudioPath", VisualStudioPath);
+ }
+
+ string PatchDefinitionsFilePath = txtPatchDefinitionsFile.Text.Trim();
+ if (PatchDefinitionsFilePath.Length == 0)
+ {
+ if (Key.GetValue("PatchDefinitionsFilePath") != null)
+ Key.DeleteValue("PatchDefinitionsFilePath");
+ }
+ else
+ {
+ Key.SetValue("PatchDefinitionsFilePath", PatchDefinitionsFilePath);
+ }
+
+ string PatchDefinitionName = cmbPatchDefinitionName.Text.Trim();
+ if (PatchDefinitionName.Length == 0)
+ {
+ if (Key.GetValue("PatchDefinitionName") != null)
+ Key.DeleteValue("PatchDefinitionName");
+ }
+ else
+ {
+ Key.SetValue("PatchDefinitionName", PatchDefinitionName);
+ }
+
+ string TargetVersion = cmbTargetVersion.Text.Trim();
+ if (TargetVersion.Length == 0)
+ {
+ if (Key.GetValue("TargetVersion") != null)
+ Key.DeleteValue("TargetVersion");
+ }
+ else
+ {
+ Key.SetValue("TargetVersion", TargetVersion);
+ }
+
+ string TargetFilePath = cmbTargetPath.Text.Trim();
+ if (TargetFilePath.Length == 0)
+ {
+ if (Key.GetValue("TargetFilePath") != null)
+ Key.DeleteValue("TargetFilePath");
+ }
+ else
+ {
+ Key.SetValue("TargetFilePath", TargetFilePath);
+ }
+
+ string InputFilePath = txtInputFile.Text.Trim();
+ if (InputFilePath.Length == 0)
+ {
+ if (Key.GetValue("InputFilePath") != null)
+ Key.DeleteValue("InputFilePath");
+ }
+ else
+ {
+ Key.SetValue("InputFilePath", InputFilePath);
+ }
+
+ string OutputFilePath = txtOutputFile.Text.Trim();
+ if (OutputFilePath.Length == 0)
+ {
+ if (Key.GetValue("OutputFilePath") != null)
+ Key.DeleteValue("OutputFilePath");
+ }
+ else
+ {
+ Key.SetValue("OutputFilePath", OutputFilePath);
+ }
+ }
+
+ private bool LoadingPatchDefinitions = false;
+
+ private void LoadPatchDefinitions()
+ {
+ if (LoadingPatchDefinitions)
+ return;
+ LoadingPatchDefinitions = true;
+
+ string PatchDefinitionName = cmbPatchDefinitionName.Text;
+ string TargetVersion = cmbTargetVersion.Text;
+ string TargetPath = cmbTargetPath.Text;
+
+ cmbPatchDefinitionName.SelectedIndex = -1;
+ cmbTargetVersion.SelectedIndex = -1;
+ cmbTargetPath.SelectedIndex = -1;
+
+ cmbPatchDefinitionName.Items.Clear();
+ cmbTargetVersion.Items.Clear();
+ cmbTargetPath.Items.Clear();
+
+ cmbPatchDefinitionName.Text = PatchDefinitionName;
+ cmbTargetVersion.Text = TargetVersion;
+ cmbTargetPath.Text = TargetPath;
+
+ try
+ {
+ string Definitions = File.ReadAllText(txtPatchDefinitionsFile.Text);
+ PatchEngine Engine = new(Definitions);
+ Engine.PatchDefinitions.Where(d => !string.IsNullOrEmpty(d.Name)).Select(d => d.Name).Distinct().ToList().ForEach(n => cmbPatchDefinitionName.Items.Add(n));
+ PatchDefinition Definition = null;
+ if (cmbPatchDefinitionName.Text.Trim().Length > 0)
+ Definition = Engine.PatchDefinitions.Find(d => string.Equals(d.Name, cmbPatchDefinitionName.Text.Trim(), StringComparison.CurrentCultureIgnoreCase));
+ if (Definition != null)
+ {
+ Definition.TargetVersions.Where(v => !string.IsNullOrEmpty(v.Description)).Select(v => v.Description).Distinct().ToList().ForEach(d => cmbTargetVersion.Items.Add(d));
+ TargetVersion Version = null;
+ if (cmbTargetVersion.Text.Trim().Length > 0)
+ Version = Definition.TargetVersions.Find(v => string.Equals(v.Description, cmbTargetVersion.Text.Trim(), StringComparison.CurrentCultureIgnoreCase));
+ if (Version != null)
+ {
+ Version.TargetFiles.Where(f => !string.IsNullOrEmpty(f.Path)).Select(f => Path.GetDirectoryName(f.Path)).Distinct().ToList().ForEach(f => cmbTargetPath.Items.Add(f));
+ }
+ }
+ }
+ catch { }
+
+ LoadingPatchDefinitions = false;
+ }
+
+ private void cmdVisualStudioPath_Click(object sender, EventArgs e)
+ {
+ FolderBrowserDialog.SelectedPath = txtVisualStudioPath.Text;
+ FolderBrowserDialog.Description = "Select path to Visual Studio with ARM32 SDK";
+ System.Windows.Forms.DialogResult Result = FolderBrowserDialog.ShowDialog();
+ if (Result == System.Windows.Forms.DialogResult.OK)
+ txtVisualStudioPath.Text = FolderBrowserDialog.SelectedPath;
+ }
+
+ private void cmdPatchDefinitionsFile_Click(object sender, EventArgs e)
+ {
+ OpenFileDialog.CheckFileExists = false;
+ OpenFileDialog.DefaultExt = "xml";
+ try
+ {
+ OpenFileDialog.FileName = Path.GetFileName(txtPatchDefinitionsFile.Text);
+ OpenFileDialog.InitialDirectory = Path.GetDirectoryName(txtPatchDefinitionsFile.Text);
+ }
+ catch { }
+ OpenFileDialog.Multiselect = false;
+ OpenFileDialog.Title = "Open patch-definitions file";
+ System.Windows.Forms.DialogResult Result = OpenFileDialog.ShowDialog();
+ if (Result == System.Windows.Forms.DialogResult.OK)
+ {
+ txtPatchDefinitionsFile.Text = OpenFileDialog.FileName;
+ WindowsFormsSynchronizationContext.Current.Post(s => LoadPatchDefinitions(), null);
+ }
+ }
+
+ private void txtPatchDefinitionsFile_Leave(object sender, EventArgs e)
+ {
+ WindowsFormsSynchronizationContext.Current.Post(s => LoadPatchDefinitions(), null);
+ }
+
+ private void MainForm_FormClosed(object sender, FormClosedEventArgs e)
+ {
+ StorePaths();
+ }
+
+ private void cmbPatchDefinitionName_SelectedValueChanged(object sender, EventArgs e)
+ {
+ WindowsFormsSynchronizationContext.Current.Post(s => LoadPatchDefinitions(), null);
+ }
+
+ private void cmbPatchDefinitionName_Leave(object sender, EventArgs e)
+ {
+ WindowsFormsSynchronizationContext.Current.Post(s => LoadPatchDefinitions(), null);
+ }
+
+ private void cmbTargetVersion_SelectedValueChanged(object sender, EventArgs e)
+ {
+ WindowsFormsSynchronizationContext.Current.Post(s => LoadPatchDefinitions(), null);
+ }
+
+ private void cmbTargetVersion_Leave(object sender, EventArgs e)
+ {
+ WindowsFormsSynchronizationContext.Current.Post(s => LoadPatchDefinitions(), null);
+ }
+
+ private void cmdInputFile_Click(object sender, EventArgs e)
+ {
+ OpenFileDialog.CheckFileExists = true;
+ OpenFileDialog.DefaultExt = "";
+ try
+ {
+ OpenFileDialog.FileName = Path.GetFileName(txtInputFile.Text);
+ OpenFileDialog.InitialDirectory = Path.GetDirectoryName(txtInputFile.Text);
+ }
+ catch { }
+ OpenFileDialog.Multiselect = false;
+ OpenFileDialog.Title = "Open input file";
+ System.Windows.Forms.DialogResult Result = OpenFileDialog.ShowDialog();
+ if (Result == System.Windows.Forms.DialogResult.OK)
+ {
+ txtInputFile.Text = OpenFileDialog.FileName;
+ txtOutputFile.Text = "";
+ }
+ }
+
+ private void cmdOutputFile_Click(object sender, EventArgs e)
+ {
+ SaveFileDialog.CheckFileExists = false;
+ SaveFileDialog.DefaultExt = "";
+ try
+ {
+ SaveFileDialog.FileName = Path.GetFileName(txtInputFile.Text);
+ SaveFileDialog.InitialDirectory = Path.GetDirectoryName(txtOutputFile.Text);
+ }
+ catch { }
+ SaveFileDialog.Title = "Open input file";
+ System.Windows.Forms.DialogResult Result = SaveFileDialog.ShowDialog();
+ if (Result == System.Windows.Forms.DialogResult.OK)
+ txtOutputFile.Text = SaveFileDialog.FileName;
+ }
+
+ private void cmdPatch_Click(object sender, EventArgs e)
+ {
+ string VirtualOffsetString = txtVirtualOffset.Text.Trim();
+ if (VirtualOffsetString.StartsWith("0x", StringComparison.OrdinalIgnoreCase))
+ VirtualOffsetString = VirtualOffsetString[2..];
+ CodeType CodeType = Patcher.CodeType.Thumb2;
+ byte[] CompiledCode = null;
+ if (UInt32.TryParse(VirtualOffsetString, System.Globalization.NumberStyles.HexNumber, null, out uint VirtualAddress))
+ {
+ CodeType = (CodeType)Enum.Parse(typeof(Patcher.CodeType), cmbCodeType.SelectedItem.ToString());
+ CompiledCode = ArmCompiler.Compile(txtVisualStudioPath.Text, VirtualAddress, CodeType, txtAssemblyCode.Text);
+ }
+ if ((VirtualAddress != 0) && (CompiledCode == null))
+ {
+ txtCompiledOpcodes.Text = ArmCompiler.Output;
+ }
+ else
+ {
+ if (CompiledCode != null)
+ txtCompiledOpcodes.Text = Converter.ConvertHexToString(CompiledCode, " ");
+
+ string TargetFilePath = Path.Combine(cmbTargetPath.Text, Path.GetFileName(txtInputFile.Text));
+ if (TargetFilePath.StartsWith(@"\"))
+ TargetFilePath = TargetFilePath[1..];
+
+ try
+ {
+ MainPatcher.AddPatch(txtInputFile.Text, (txtOutputFile.Text.Trim().Length > 0) ? txtOutputFile.Text : null, cmbPatchDefinitionName.Text, cmbTargetVersion.Text, TargetFilePath, txtVisualStudioPath.Text, VirtualAddress, CodeType, txtAssemblyCode.Text, txtPatchDefinitionsFile.Text);
+ }
+ catch (ArgumentOutOfRangeException)
+ {
+ txtCompiledOpcodes.Text = "BAD VIRTUAL OFFSET";
+ }
+ catch
+ {
+ txtCompiledOpcodes.Text = "UNKNOWN ERROR";
+ }
+
+ txtVirtualOffset.Focus();
+ txtVirtualOffset.SelectAll();
+ }
+ }
+ }
+}
diff --git a/Patcher/Patcher/MainForm.resx b/Patcher/Patcher/MainForm.resx
new file mode 100644
index 0000000..28d85c9
--- /dev/null
+++ b/Patcher/Patcher/MainForm.resx
@@ -0,0 +1,129 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ 17, 17
+
+
+ 182, 17
+
+
+ 317, 17
+
+
\ No newline at end of file
diff --git a/Patcher/Patcher/MainPatcher.cs b/Patcher/Patcher/MainPatcher.cs
new file mode 100644
index 0000000..4ffde52
--- /dev/null
+++ b/Patcher/Patcher/MainPatcher.cs
@@ -0,0 +1,189 @@
+// Copyright (c) 2018, Rene Lergner - wpinternals.net - @Heathcliff74xda
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+
+using System;
+using System.IO;
+using System.Linq;
+using System.Security.Cryptography;
+using WPinternals;
+
+namespace Patcher
+{
+ public static class MainPatcher
+ {
+ ///
+ /// TargetFilePath is relative to the root of the PatchDefinition
+ /// OutputFilePath can be null
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static void AddPatch(string InputFilePath, string OutputFilePath, string PatchDefinitionName, string TargetVersionDescription, string TargetFilePath, string PathToVisualStudioWithWP8SDK, UInt32 VirtualAddress, CodeType CodeType, string ArmCodeFragment, string PatchDefintionsXmlPath)
+ {
+ SHA1Managed SHA = new();
+
+ // Compile ARM code
+ byte[] CompiledCode = null;
+ if (VirtualAddress != 0)
+ CompiledCode = ArmCompiler.Compile(PathToVisualStudioWithWP8SDK, VirtualAddress, CodeType, ArmCodeFragment);
+
+ // Read original binary
+ byte[] Binary = File.ReadAllBytes(InputFilePath);
+
+ // Backup original checksum
+ UInt32 ChecksumOffset = GetChecksumOffset(Binary);
+ UInt32 OriginalChecksum = ByteOperations.ReadUInt32(Binary, ChecksumOffset);
+
+ // Determine Raw Offset
+ PeFile PeFile = new(Binary);
+ UInt32 RawOffset = 0;
+ if (VirtualAddress != 0)
+ RawOffset = PeFile.ConvertVirtualAddressToRawOffset(VirtualAddress);
+
+ // Add or replace patch
+ string PatchDefintionsXml = File.ReadAllText(PatchDefintionsXmlPath);
+ PatchEngine PatchEngine = new(PatchDefintionsXml);
+ PatchDefinition PatchDefinition = PatchEngine.PatchDefinitions.Find(d => string.Equals(d.Name, PatchDefinitionName, StringComparison.CurrentCultureIgnoreCase));
+ if (PatchDefinition == null)
+ {
+ PatchDefinition = new PatchDefinition
+ {
+ Name = PatchDefinitionName
+ };
+ PatchEngine.PatchDefinitions.Add(PatchDefinition);
+ }
+ TargetVersion TargetVersion = PatchDefinition.TargetVersions.Find(v => string.Equals(v.Description, TargetVersionDescription, StringComparison.CurrentCultureIgnoreCase));
+ if (TargetVersion == null)
+ {
+ TargetVersion = new TargetVersion
+ {
+ Description = TargetVersionDescription
+ };
+ PatchDefinition.TargetVersions.Add(TargetVersion);
+ }
+ TargetFile TargetFile = TargetVersion.TargetFiles.Find(f => (f.Path != null) && (string.Equals(f.Path.TrimStart(new char[] { '\\' }), TargetFilePath.TrimStart(new char[] { '\\' }), StringComparison.CurrentCultureIgnoreCase)));
+ if (TargetFile == null)
+ {
+ TargetFile = new TargetFile();
+ TargetVersion.TargetFiles.Add(TargetFile);
+ }
+ TargetFile.Path = TargetFilePath;
+ TargetFile.HashOriginal = SHA.ComputeHash(Binary);
+ Patch Patch;
+ if (VirtualAddress != 0)
+ {
+ Patch = TargetFile.Patches.Find(p => p.Address == RawOffset);
+ if (Patch == null)
+ {
+ Patch = new Patch
+ {
+ Address = RawOffset
+ };
+ TargetFile.Patches.Add(Patch);
+ }
+ Patch.OriginalBytes = new byte[CompiledCode.Length];
+ Buffer.BlockCopy(Binary, (int)RawOffset, Patch.OriginalBytes, 0, CompiledCode.Length);
+ Patch.PatchedBytes = CompiledCode;
+ }
+
+ // Apply all patches
+ foreach (Patch CurrentPatch in TargetFile.Patches)
+ {
+ Buffer.BlockCopy(CurrentPatch.PatchedBytes, 0, Binary, (int)CurrentPatch.Address, CurrentPatch.PatchedBytes.Length);
+ }
+
+ // Calculate checksum
+ // This also modifies the binary
+ // Original checksum is already backed up
+ UInt32 Checksum = CalculateChecksum(Binary);
+
+ // Add or replace checksum patch
+ Patch = TargetFile.Patches.Find(p => p.Address == ChecksumOffset);
+ if (Patch == null)
+ {
+ Patch = new Patch
+ {
+ Address = ChecksumOffset
+ };
+ TargetFile.Patches.Add(Patch);
+ }
+ Patch.OriginalBytes = new byte[4];
+ ByteOperations.WriteUInt32(Patch.OriginalBytes, 0, OriginalChecksum);
+ Patch.PatchedBytes = new byte[4];
+ ByteOperations.WriteUInt32(Patch.PatchedBytes, 0, Checksum);
+
+ // Calculate hash for patched target file
+ TargetFile.HashPatched = SHA.ComputeHash(Binary);
+
+ // Write patched file
+ if (OutputFilePath != null)
+ File.WriteAllBytes(OutputFilePath, Binary);
+
+ // Write PatchDefintions
+ PatchEngine.WriteDefinitions(PatchDefintionsXmlPath);
+ }
+
+ private static UInt32 CalculateChecksum(byte[] PEFile)
+ {
+ UInt32 Checksum = 0;
+ UInt32 Hi;
+
+ // Clear file checksum
+ ByteOperations.WriteUInt32(PEFile, GetChecksumOffset(PEFile), 0);
+
+ for (UInt32 i = 0; i < ((UInt32)PEFile.Length & 0xfffffffe); i += 2)
+ {
+ Checksum += ByteOperations.ReadUInt16(PEFile, i);
+ Hi = Checksum >> 16;
+ if (Hi != 0)
+ {
+ Checksum = Hi + (Checksum & 0xFFFF);
+ }
+ }
+ if ((PEFile.Length % 2) != 0)
+ {
+ Checksum += (UInt32)ByteOperations.ReadUInt8(PEFile, (UInt32)PEFile.Length - 1);
+ Hi = Checksum >> 16;
+ if (Hi != 0)
+ {
+ Checksum = Hi + (Checksum & 0xFFFF);
+ }
+ }
+ Checksum += (UInt32)PEFile.Length;
+
+ // Write file checksum
+ ByteOperations.WriteUInt32(PEFile, GetChecksumOffset(PEFile), Checksum);
+
+ return Checksum;
+ }
+
+ private static UInt32 GetChecksumOffset(byte[] PEFile)
+ {
+ return ByteOperations.ReadUInt32(PEFile, 0x3C) + +0x58;
+ }
+ }
+}
diff --git a/Patcher/Patcher/ObjectFileParser.cs b/Patcher/Patcher/ObjectFileParser.cs
new file mode 100644
index 0000000..ac5057d
--- /dev/null
+++ b/Patcher/Patcher/ObjectFileParser.cs
@@ -0,0 +1,819 @@
+// Copyright (c) 2018, Rene Lergner - wpinternals.net - @Heathcliff74xda
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+
+// This code is based on Get-ObjDump from Matthew Graeber (@mattifestation)
+
+using System;
+using System.IO;
+using System.Text;
+using System.Linq;
+
+namespace COFF
+{
+ public enum Machine : ushort
+ {
+ UNKNOWN = 0,
+ ///
+ /// Intel 386.
+ ///
+ I386 = 0x014C,
+ ///
+ /// MIPS little-endian =0x160 big-endian
+ ///
+ R3000 = 0x0162,
+ ///
+ /// MIPS little-endian
+ ///
+ R4000 = 0x0166,
+ ///
+ /// MIPS little-endian
+ ///
+ R10000 = 0x0168,
+ ///
+ /// MIPS little-endian WCE v2
+ ///
+ WCEMIPSV2 = 0x0169,
+ ///
+ /// Alpha_AXP
+ ///
+ ALPHA = 0x0184,
+ ///
+ /// SH3 little-endian
+ ///
+ SH3 = 0x01A2,
+ SH3DSP = 0x01A3,
+ ///
+ /// SH3E little-endian
+ ///
+ SH3E = 0x01A4,
+ ///
+ /// SH4 little-endian
+ ///
+ SH4 = 0x01A6,
+ ///
+ /// SH5
+ ///
+ SH5 = 0x01A8,
+ ///
+ /// ARM Little-Endian
+ ///
+ ARM = 0x01C0,
+ THUMB = 0x01C2,
+ ///
+ /// ARM Thumb-2 Little-Endian
+ ///
+ ARMV7 = 0x01C4,
+ AM33 = 0x01D3,
+ ///
+ /// IBM PowerPC Little-Endian
+ ///
+ POWERPC = 0x01F0,
+ POWERPCFP = 0x01F1,
+ ///
+ /// Intel 64
+ ///
+ IA64 = 0x0200,
+ ///
+ /// MIPS
+ ///
+ MIPS16 = 0x0266,
+ ///
+ /// ALPHA64
+ ///
+ ALPHA64 = 0x0284,
+ ///
+ /// MIPS
+ ///
+ MIPSFPU = 0x0366,
+ ///
+ /// MIPS
+ ///
+ MIPSFPU16 = 0x0466,
+ AXP64 = ALPHA64,
+ ///
+ /// Infineon
+ ///
+ TRICORE = 0x0520,
+ CEF = 0x0CEF,
+ ///
+ /// EFI public byte Code
+ ///
+ EBC = 0x0EBC,
+ ///
+ /// AMD64 (K8)
+ ///
+ AMD64 = 0x8664,
+ ///
+ /// M32R little-endian
+ ///
+ M32R = 0x9041,
+ ///
+ /// ARMv8 in 64-bit mode
+ ///
+ ARM64 = 0xAA64,
+ CEE = 0xC0EE
+ }
+
+ [Flags]
+ public enum CoffHeaderCharacteristics : ushort
+ {
+ ///
+ /// Relocation info stripped from file.
+ ///
+ RELOCS_STRIPPED = 0x0001,
+ ///
+ /// File is executable (i.e. no unresolved external references).
+ ///
+ EXECUTABLE_IMAGE = 0x0002,
+ ///
+ /// Line nunbers stripped from file.
+ ///
+ LINE_NUMS_STRIPPED = 0x0004,
+ ///
+ /// Local symbols stripped from file.
+ ///
+ LOCAL_SYMS_STRIPPED = 0x0008,
+ ///
+ /// Agressively trim working set
+ ///
+ AGGRESIVE_WS_TRIM = 0x0010,
+ ///
+ /// App can handle >2gb addresses
+ ///
+ LARGE_ADDRESS_AWARE = 0x0020,
+ ///
+ /// public bytes of machine public ushort are reversed.
+ ///
+ REVERSED_LO = 0x0080,
+ ///
+ /// 32 bit public ushort machine.
+ ///
+ BIT32_MACHINE = 0x0100,
+ ///
+ /// Debugging info stripped from file in .DBG file
+ ///
+ DEBUG_STRIPPED = 0x0200,
+ ///
+ /// If Image is on removable media =copy and run from the swap file.
+ ///
+ REMOVABLE_RUN_FROM_SWAP = 0x0400,
+ ///
+ /// If Image is on Net =copy and run from the swap file.
+ ///
+ NET_RUN_FROM_SWAP = 0x0800,
+ ///
+ /// System File.
+ ///
+ SYSTEM = 0x1000,
+ ///
+ /// File is a DLL.
+ ///
+ DLL = 0x2000,
+ ///
+ /// File should only be run on a UP machine
+ ///
+ UP_SYSTEM_ONLY = 0x4000,
+ ///
+ /// public bytes of machine public ushort are reversed.
+ ///
+ REVERSED_HI = 0x8000
+ }
+
+ public class HEADER
+ {
+ public Machine Machine;
+ public ushort NumberOfSections;
+ public DateTime TimeDateStamp;
+ public uint PointerToSymbolTable;
+ public uint NumberOfSymbols;
+ public ushort SizeOfOptionalHeader;
+ public CoffHeaderCharacteristics Characteristics;
+
+ public HEADER(BinaryReader br)
+ {
+ this.Machine = (Machine)br.ReadUInt16();
+ this.NumberOfSections = br.ReadUInt16();
+ this.TimeDateStamp = new DateTime(1970, 1, 1, 0, 0, 0).AddSeconds(br.ReadUInt32());
+ this.PointerToSymbolTable = br.ReadUInt32();
+ this.NumberOfSymbols = br.ReadUInt32();
+ this.SizeOfOptionalHeader = br.ReadUInt16();
+ this.Characteristics = (CoffHeaderCharacteristics)br.ReadUInt16();
+ }
+ }
+
+ [Flags]
+ public enum SectionHeaderCharacteristics : uint
+ {
+ ///
+ /// Reserved.
+ ///
+ TYPE_NO_PAD = 0x00000008,
+ ///
+ /// Section contains code.
+ ///
+ CNT_CODE = 0x00000020,
+ ///
+ /// Section contains initialized data.
+ ///
+ CNT_INITIALIZED_DATA = 0x00000040,
+ ///
+ /// Section contains uninitialized data.
+ ///
+ CNT_UNINITIALIZED_DATA = 0x00000080,
+ ///
+ /// Section contains comments or some other type of information.
+ ///
+ LNK_INFO = 0x00000200,
+ ///
+ /// Section contents will not become part of image.
+ ///
+ LNK_REMOVE = 0x00000800,
+ ///
+ /// Section contents comdat.
+ ///
+ LNK_COMDAT = 0x00001000,
+ ///
+ /// Reset speculative exceptions handling bits in the TLB entries for this section.
+ ///
+ NO_DEFER_SPEC_EXC = 0x00004000,
+ ///
+ /// Section content can be accessed relative to GP
+ ///
+ GPREL = 0x00008000,
+ MEM_FARDATA = 0x00008000,
+ MEM_PURGEABLE = 0x00020000,
+ MEM_16BIT = 0x00020000,
+ MEM_LOCKED = 0x00040000,
+ MEM_PRELOAD = 0x00080000,
+ ALIGN_1BYTES = 0x00100000,
+ ALIGN_2BYTES = 0x00200000,
+ ALIGN_4BYTES = 0x00300000,
+ ALIGN_8BYTES = 0x00400000,
+ ///
+ /// Default alignment if no others are specified.
+ ///
+ ALIGN_16BYTES = 0x00500000,
+ ALIGN_32BYTES = 0x00600000,
+ ALIGN_64BYTES = 0x00700000,
+ ALIGN_128BYTES = 0x00800000,
+ ALIGN_256BYTES = 0x00900000,
+ ALIGN_512BYTES = 0x00A00000,
+ ALIGN_1024BYTES = 0x00B00000,
+ ALIGN_2048BYTES = 0x00C00000,
+ ALIGN_4096BYTES = 0x00D00000,
+ ALIGN_8192BYTES = 0x00E00000,
+ ALIGN_MASK = 0x00F00000,
+ ///
+ /// Section contains extended relocations.
+ ///
+ LNK_NRELOC_OVFL = 0x01000000,
+ ///
+ /// Section can be discarded.
+ ///
+ MEM_DISCARDABLE = 0x02000000,
+ ///
+ /// Section is not cachable.
+ ///
+ MEM_NOT_CACHED = 0x04000000,
+ ///
+ /// Section is not pageable.
+ ///
+ MEM_NOT_PAGED = 0x08000000,
+ ///
+ /// Section is shareable.
+ ///
+ MEM_SHARED = 0x10000000,
+ ///
+ /// Section is executable.
+ ///
+ MEM_EXECUTE = 0x20000000,
+ ///
+ /// Section is readable.
+ ///
+ MEM_READ = 0x40000000,
+ ///
+ /// Section is writeable.
+ ///
+ MEM_WRITE = 0x80000000
+ }
+
+ public enum AMD64RelocationType : ushort
+ {
+ ABSOLUTE,
+ ADDR64,
+ ADDR32,
+ ADDR32NB,
+ REL32,
+ REL32_1,
+ REL32_2,
+ REL32_3,
+ REL32_4,
+ REL32_5,
+ SECTION,
+ SECREL,
+ SECREL7,
+ TOKEN,
+ SREL32,
+ PAIR,
+ SSPAN32
+ }
+
+ public enum ARMRelocationType : ushort
+ {
+ ABSOLUTE,
+ ADDR32,
+ ADDR32NB,
+ BRANCH24,
+ BRANCH11,
+ TOKEN,
+ BLX24 = 0x08,
+ BLX11 = 0x09,
+ SECTION = 0x0E,
+ SECREL = 0x0F,
+ MOV32A = 0x10,
+ MOV32T = 0x11,
+ BRANCH20T = 0x12,
+ BRANCH24T = 0x14,
+ BLX23T = 0x15
+ }
+
+ public enum ARMv8RelocationType : ushort
+ {
+ ABSOLUTE,
+ ADDR32,
+ ADDR32NB,
+ BRANCH26,
+ PAGEBASE_REL21,
+ REL21,
+ PAGEOFFSET_12A,
+ PAGEOFFSET_12L,
+ SECREL,
+ SECREL_LOW12A,
+ SECREL_HIGH12A,
+ SECREL_LOW12L,
+ TOKEN,
+ SECTION,
+ ADDR64
+ }
+
+ public enum X86RelocationType : ushort
+ {
+ ABSOLUTE,
+ DIR16,
+ DIR32 = 0x06,
+ DIR32NB = 0x07,
+ SEG12 = 0x09,
+ SECTION = 0x0A,
+ SECREL = 0x0B,
+ TOKEN = 0x0C,
+ SECREL7 = 0x0D,
+ REL32 = 0x14
+ }
+
+ public class RelocationEntry
+ {
+ public uint VirtualAddress;
+ public uint SymbolTableIndex;
+ public Enum Type;
+ public string Name;
+
+ public RelocationEntry(BinaryReader br)
+ {
+ this.VirtualAddress = br.ReadUInt32();
+ this.SymbolTableIndex = br.ReadUInt32();
+ // Default to X86RelocationType. This will be changed once the processor type is determined
+ this.Type = (X86RelocationType)br.ReadUInt16();
+ }
+ }
+
+ public class SECTION_HEADER
+ {
+ public string Name;
+ public uint PhysicalAddress;
+ public uint VirtualSize;
+ public uint VirtualAddress;
+ public uint SizeOfRawData;
+ public uint PointerToRawData;
+ public uint PointerToRelocations;
+ public uint PointerToLinenumbers;
+ public ushort NumberOfRelocations;
+ public ushort NumberOfLinenumbers;
+ public SectionHeaderCharacteristics Characteristics;
+ public Byte[] RawData;
+ public RelocationEntry[] Relocations;
+
+ public SECTION_HEADER(BinaryReader br)
+ {
+ this.Name = Encoding.UTF8.GetString(br.ReadBytes(8)).Split((Char)0)[0];
+ this.PhysicalAddress = br.ReadUInt32();
+ this.VirtualSize = this.PhysicalAddress;
+ this.VirtualAddress = br.ReadUInt32();
+ this.SizeOfRawData = br.ReadUInt32();
+ this.PointerToRawData = br.ReadUInt32();
+ this.PointerToRelocations = br.ReadUInt32();
+ this.PointerToLinenumbers = br.ReadUInt32();
+ this.NumberOfRelocations = br.ReadUInt16();
+ this.NumberOfLinenumbers = br.ReadUInt16();
+ this.Characteristics = (SectionHeaderCharacteristics)br.ReadUInt32();
+ }
+ }
+
+ public enum SectionNumber : short
+ {
+ UNDEFINED,
+ ABSOLUTE = -1,
+ DEBUG = -2
+ }
+
+ [Flags]
+ public enum TypeClass : short
+ {
+ TYPE_NULL,
+ TYPE_VOID,
+ TYPE_CHAR,
+ TYPE_SHORT,
+ TYPE_INT,
+ TYPE_LONG,
+ TYPE_FLOAT,
+ TYPE_DOUBLE,
+ TYPE_STRUCT,
+ TYPE_UNION,
+ TYPE_ENUM,
+ TYPE_MOE,
+ TYPE_BYTE,
+ TYPE_WORD,
+ TYPE_UINT,
+ TYPE_DWORD,
+ DTYPE_POINTER = 0x100,
+ DTYPE_FUNCTION = 0x200,
+ DTYPE_ARRAY = 0x300,
+ ///
+ /// Technically, this is defined as 0 in the MSB
+ ///
+ DTYPE_NULL = 0x400
+ }
+
+ public enum StorageClass : byte
+ {
+ NULL,
+ AUTOMATIC,
+ EXTERNAL,
+ STATIC,
+ REGISTER,
+ EXTERNAL_DEF,
+ LABEL,
+ UNDEFINED_LABEL,
+ MEMBER_OF_STRUCT,
+ ARGUMENT,
+ STRUCT_TAG,
+ MEMBER_OF_UNION,
+ UNION_TAG,
+ TYPE_DEFINITION,
+ ENUM_TAG,
+ MEMBER_OF_ENUM,
+ REGISTER_PARAM,
+ BIT_FIELD,
+ BLOCK = 0x64,
+ FUNCTION = 0x65,
+ END_OF_STRUCT = 0x66,
+ FILE = 0x67,
+ SECTION = 0x68,
+ WEAK_EXTERNAL = 0x69,
+ CLR_TOKEN = 0x6B,
+ END_OF_FUNCTION = 0xFF
+ }
+
+ public class SYMBOL_TABLE
+ {
+ public string Name;
+ public uint Value;
+ public SectionNumber SectionNumber;
+ public TypeClass Type;
+ public StorageClass StorageClass;
+ public byte NumberOfAuxSymbols;
+ public Object AuxSymbols;
+ private readonly Byte[] NameArray;
+
+ public SYMBOL_TABLE(BinaryReader br)
+ {
+ this.NameArray = br.ReadBytes(8);
+
+ if (this.NameArray[0] == 0 && this.NameArray[1] == 0 && this.NameArray[2] == 0 && this.NameArray[3] == 0)
+ {
+ // Per specification, if the high DWORD is 0, then then low DWORD is an index into the string table
+ this.Name = "/" + BitConverter.ToInt32(NameArray, 4).ToString();
+ }
+ else
+ {
+ this.Name = Encoding.UTF8.GetString(NameArray).Trim((char)0);
+ }
+
+ this.Value = br.ReadUInt32();
+ this.SectionNumber = (SectionNumber)br.ReadInt16();
+ this.Type = (TypeClass)br.ReadInt16();
+ if ((((int)this.Type) & 0xff00) == 0) { this.Type = (TypeClass)Enum.Parse(typeof(TypeClass), ((int)this.Type | 0x400).ToString()); }
+ this.StorageClass = (StorageClass)br.ReadByte();
+ this.NumberOfAuxSymbols = br.ReadByte();
+ }
+ }
+
+ public class SECTION_DEFINITION
+ {
+ public uint Length;
+ public ushort NumberOfRelocations;
+ public ushort NumberOfLinenumbers;
+ public uint CheckSum;
+ public ushort Number;
+ public byte Selection;
+
+ public SECTION_DEFINITION(BinaryReader br)
+ {
+ this.Length = br.ReadUInt32();
+ this.NumberOfRelocations = br.ReadUInt16();
+ this.NumberOfLinenumbers = br.ReadUInt16();
+ this.CheckSum = br.ReadUInt32();
+ this.Number = br.ReadUInt16();
+ this.Selection = br.ReadByte();
+ br.ReadBytes(3);
+ }
+ }
+
+ public class ParsedObjectFile
+ {
+ public HEADER CoffHeader;
+ public SECTION_HEADER[] SectionHeaders;
+ public SYMBOL_TABLE[] SymbolTable;
+ }
+
+ public static class ObjectFileParser
+ {
+ public static ParsedObjectFile ParseObjectFile(string ObjectFilePath)
+ {
+ ParsedObjectFile Result = null;
+
+ // Fixed structure sizes
+ const int SizeofCOFFFileHeader = 20;
+ const int SizeofSectionHeader = 40;
+ const int SizeofSymbolTableEntry = 18;
+ const int SizeofRelocationEntry = 10;
+
+ // Open the object file for reading
+ using (FileStream FileStream = System.IO.File.OpenRead(ObjectFilePath))
+ {
+ long FileLength = FileStream.Length;
+
+ if (FileLength < SizeofCOFFFileHeader)
+ {
+ // You cannot parse the COFF header if the file is not big enough to contain a COFF header.
+ throw new Exception("ObjectFile is too small to store a COFF header.");
+ }
+
+ // Open a BinaryReader object for the object file
+ using BinaryReader BinaryReader = new(FileStream);
+ // Parse the COFF header
+ COFF.HEADER CoffHeader = new(BinaryReader);
+
+ if (CoffHeader.SizeOfOptionalHeader != 0)
+ {
+ // Per the PECOFF specification, an object file does not have an optional header
+ throw new Exception("Coff header indicates the existence of an optional header. An object file cannot have an optional header.");
+ }
+
+ if (CoffHeader.PointerToSymbolTable == 0)
+ {
+ throw new Exception("An object file is supposed to have a symbol table.");
+ }
+
+ if (FileLength < ((CoffHeader.NumberOfSections * SizeofSectionHeader) + SizeofCOFFFileHeader))
+ {
+ // The object file isn't big enough to store the number of sections present.
+ throw new Exception("ObjectFile is too small to store section header data.");
+ }
+
+ // A string collection used to store section header names. This collection is referenced while
+ // parsing the symbol table entries whose name is the same as the section header. In this case,
+ // the symbol entry will have a particular auxiliary symbol table entry.
+ System.Collections.Specialized.StringCollection SectionHeaderNames = new();
+
+ // Correlate the processor type to the relocation type. There are more relocation type defined
+ // in the PECOFF specification, but I don't expect those to be present. In that case, relocation
+ // entries default to X86RelocationType.
+ COFF.SECTION_HEADER[] SectionHeaders = new COFF.SECTION_HEADER[CoffHeader.NumberOfSections];
+
+ // Parse section headers
+ for (int i = 0; i < CoffHeader.NumberOfSections; i++)
+ {
+ SectionHeaders[i] = new COFF.SECTION_HEADER(BinaryReader);
+
+ // Add the section name to the string collection. This will be referenced during symbol table parsing.
+ SectionHeaderNames.Add(SectionHeaders[i].Name);
+
+ // Save the current filestream position. We are about to jump out of place.
+ long SavedFilePosition = FileStream.Position;
+
+ // Check to see if the raw data points beyond the actual file size
+ if ((SectionHeaders[i].PointerToRawData + SectionHeaders[i].SizeOfRawData) > FileLength)
+ {
+ throw new Exception("Section header's raw data exceeds the size of the object file.");
+ }
+ else
+ {
+ // Read the raw data into a byte array
+ FileStream.Seek(SectionHeaders[i].PointerToRawData, SeekOrigin.Begin);
+ SectionHeaders[i].RawData = BinaryReader.ReadBytes((int)SectionHeaders[i].SizeOfRawData);
+ }
+
+ // Check to see if the section has a relocation table
+ if ((SectionHeaders[i].PointerToRelocations != 0) && (SectionHeaders[i].NumberOfRelocations != 0))
+ {
+ // Check to see if the relocation entries point beyond the actual file size
+ if ((SectionHeaders[i].PointerToRelocations + (SizeofRelocationEntry * SectionHeaders[i].NumberOfRelocations)) > FileLength)
+ {
+ throw new Exception("(SectionHeaders[i].Name) section header's relocation entries exceeds the soze of the object file.");
+ }
+
+ FileStream.Seek(SectionHeaders[i].PointerToRelocations, SeekOrigin.Begin);
+
+ COFF.RelocationEntry[] Relocations = new COFF.RelocationEntry[SectionHeaders[i].NumberOfRelocations];
+
+ for (int j = 0; j < SectionHeaders[i].NumberOfRelocations; j++)
+ {
+ Relocations[j] = new COFF.RelocationEntry(BinaryReader);
+ // Cast the relocation as its respective type
+ switch (CoffHeader.Machine)
+ {
+ case Machine.I386:
+ Relocations[j].Type = (COFF.X86RelocationType)Relocations[j].Type;
+ break;
+ case Machine.AMD64:
+ Relocations[j].Type = (COFF.AMD64RelocationType)Relocations[j].Type;
+ break;
+ case Machine.ARMV7:
+ Relocations[j].Type = (COFF.ARMRelocationType)Relocations[j].Type;
+ break;
+ case Machine.ARM64:
+ Relocations[j].Type = (COFF.ARMv8RelocationType)Relocations[j].Type;
+ break;
+ }
+ }
+
+ // Add the relocation table entry to the section header
+ SectionHeaders[i].Relocations = Relocations;
+ }
+
+ // Restore the original filestream pointer
+ FileStream.Seek(SavedFilePosition, SeekOrigin.Begin);
+ }
+
+ // Retrieve the contents of the COFF string table
+ long SymTableSize = CoffHeader.NumberOfSymbols * SizeofSymbolTableEntry;
+ long StringTableOffset = CoffHeader.PointerToSymbolTable + SymTableSize;
+
+ if (StringTableOffset > FileLength)
+ {
+ throw new Exception("The string table points beyond the end of the file.");
+ }
+
+ FileStream.Seek(StringTableOffset, SeekOrigin.Begin);
+ UInt32 StringTableLength = BinaryReader.ReadUInt32();
+
+ if (StringTableLength > FileLength)
+ {
+ throw new Exception("The string table's length exceeds the length of the file.");
+ }
+
+ string StringTable = System.Text.Encoding.UTF8.GetString(BinaryReader.ReadBytes((int)StringTableLength));
+
+ COFF.SYMBOL_TABLE[] RawSymbolTable = new COFF.SYMBOL_TABLE[CoffHeader.NumberOfSymbols];
+
+ // Retrieve the symbol table
+ if (FileLength < StringTableOffset)
+ {
+ throw new Exception("Symbol table is larger than the file size.");
+ }
+
+ FileStream.Seek(CoffHeader.PointerToSymbolTable, SeekOrigin.Begin);
+ int NumberofRegularSymbols = 0;
+
+ /*
+ Go through each symbol table looking for auxiliary symbols to parse
+
+ Currently supported auxiliary symbol table entry formats:
+ 1) .file
+ 2) Entry names that match the name of a section header
+ */
+
+ for (int i = 0; i < CoffHeader.NumberOfSymbols; i++)
+ {
+ // Parse the symbol tables regardless of whether they are normal or auxiliary symbols
+ RawSymbolTable[i] = new COFF.SYMBOL_TABLE(BinaryReader);
+
+ if (RawSymbolTable[i].NumberOfAuxSymbols == 0)
+ {
+ // This symbol table entry has no auxiliary symbols
+ NumberofRegularSymbols++;
+ }
+ else if (RawSymbolTable[i].Name == ".file")
+ {
+ long TempPosition = FileStream.Position; // Save filestream position
+ // Retrieve the file name
+ RawSymbolTable[i].AuxSymbols = System.Text.Encoding.UTF8.GetString(BinaryReader.ReadBytes(RawSymbolTable[i].NumberOfAuxSymbols * SizeofSymbolTableEntry)).TrimEnd((Char)0);
+ FileStream.Seek(TempPosition, SeekOrigin.Begin); // Restore filestream position
+ }
+ else if (SectionHeaderNames.Contains(RawSymbolTable[i].Name))
+ {
+ long TempPosition = FileStream.Position; // Save filestream position
+ RawSymbolTable[i].AuxSymbols = new COFF.SECTION_DEFINITION(BinaryReader);
+ FileStream.Seek(TempPosition, SeekOrigin.Begin); // Restore filestream position
+ }
+ }
+
+ // Create an array of symbol table entries without auxiliary table entries
+ COFF.SYMBOL_TABLE[] SymbolTable = new COFF.SYMBOL_TABLE[NumberofRegularSymbols];
+ int k = 0;
+
+ for (int i = 0; i < CoffHeader.NumberOfSymbols; i++)
+ {
+ SymbolTable[k] = RawSymbolTable[i]; // FYI, the first symbol table entry will never be an aux symbol
+ k++;
+
+ // Skip over the auxiliary symbols
+ if (RawSymbolTable[i].NumberOfAuxSymbols != 0)
+ {
+ i += RawSymbolTable[i].NumberOfAuxSymbols;
+ }
+ }
+
+ // Fix the section names if any of them point to the COFF string table
+ for (int i = 0; i < CoffHeader.NumberOfSections; i++)
+ {
+ if (SectionHeaders[i].Name?.IndexOf('/') == 0)
+ {
+ string StringTableIndexString = SectionHeaders[i].Name[1..];
+
+ if (int.TryParse(StringTableIndexString, out int StringTableIndex))
+ {
+ StringTableIndex -= 4;
+
+ if (StringTableIndex > (StringTableLength + 4))
+ {
+ throw new Exception("String table entry exceeds the bounds of the object file.");
+ }
+
+ int Length = StringTable.IndexOf((Char)0, StringTableIndex);
+ SectionHeaders[i].Name = StringTable.Substring(StringTableIndex, Length);
+ }
+ }
+ }
+
+ // Fix the symbol table names
+ for (int i = 0; i < SymbolTable.Length; i++)
+ {
+ if (SymbolTable[i].Name?.IndexOf('/') == 0)
+ {
+ string StringTableIndexString = SymbolTable[i].Name[1..];
+
+ if (int.TryParse(StringTableIndexString, out int StringTableIndex))
+ {
+ StringTableIndex -= 4;
+ int Length = StringTable.IndexOf((Char)0, StringTableIndex) - StringTableIndex;
+ SymbolTable[i].Name = StringTable.Substring(StringTableIndex, Length);
+ }
+ }
+ }
+
+ // Apply symbol names to the relocation entries
+ // SectionHeaders | Where-Object { _.Relocations } | % {
+ // _.Relocations | % { _.Name = RawSymbolTable[_.SymbolTableIndex].Name }
+ // }
+ SectionHeaders.Where(h => h.Relocations != null).ToList().ForEach(h => h.Relocations.ToList().ForEach(r => r.Name = RawSymbolTable[r.SymbolTableIndex].Name));
+
+ Result = new ParsedObjectFile
+ {
+ CoffHeader = CoffHeader,
+ SectionHeaders = SectionHeaders,
+ SymbolTable = SymbolTable
+ };
+ }
+
+ return Result;
+ }
+ }
+}
diff --git a/Patcher/Patcher/PatchEngine.cs b/Patcher/Patcher/PatchEngine.cs
new file mode 100644
index 0000000..9a97964
--- /dev/null
+++ b/Patcher/Patcher/PatchEngine.cs
@@ -0,0 +1,261 @@
+// Copyright (c) 2018, Rene Lergner - wpinternals.net - @Heathcliff74xda
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Xml;
+using System.Xml.Serialization;
+
+namespace WPinternals
+{
+ internal class PatchEngine
+ {
+ internal List PatchDefinitions = new();
+ internal readonly List TargetRedirections = new();
+
+ internal PatchEngine() { }
+
+ internal PatchEngine(string PatchDefinitionsXmlString)
+ {
+ XmlSerializer x = new(PatchDefinitions.GetType(), null, Array.Empty(), new XmlRootAttribute("PatchDefinitions"), "");
+ MemoryStream s = new(System.Text.Encoding.ASCII.GetBytes(PatchDefinitionsXmlString));
+ PatchDefinitions = (List)x.Deserialize(s);
+ }
+
+ internal void WriteDefinitions(string FilePath)
+ {
+ XmlSerializer x = new(PatchDefinitions.GetType(), null, Array.Empty(), new XmlRootAttribute("PatchDefinitions"), "");
+
+ XmlSerializerNamespaces ns = new();
+ ns.Add("", "");
+
+ System.IO.StreamWriter FileWriter = new(FilePath);
+ XmlWriter XmlWriter = XmlWriter.Create(FileWriter, new XmlWriterSettings() { OmitXmlDeclaration = true, Indent = true, NewLineHandling = NewLineHandling.Entitize });
+
+ FileWriter.WriteLine("");
+ FileWriter.WriteLine("");
+ FileWriter.WriteLine("");
+ FileWriter.WriteLine("");
+
+ x.Serialize(XmlWriter, PatchDefinitions, ns);
+
+ FileWriter.Close();
+ }
+
+ private string _TargetPath = null;
+ internal string TargetPath
+ {
+ get
+ {
+ return _TargetPath;
+ }
+ set
+ {
+ _TargetPath = value.TrimEnd(new char[] { '\\' });
+ }
+ }
+ }
+
+ internal class TargetRedirection
+ {
+ private string _RelativePath;
+ private string _TargetPath;
+
+ internal TargetRedirection(string RelativePath, string TargetPath)
+ {
+ this.RelativePath = RelativePath;
+ this.TargetPath = TargetPath;
+ }
+
+ internal string RelativePath
+ {
+ get
+ {
+ return _RelativePath;
+ }
+ set
+ {
+ _RelativePath = value.TrimStart(new char[] { '\\' }).TrimEnd(new char[] { '\\' });
+ }
+ }
+
+ internal string TargetPath
+ {
+ get
+ {
+ return _TargetPath;
+ }
+ set
+ {
+ _TargetPath = value.TrimEnd(new char[] { '\\' });
+ }
+ }
+ }
+
+ ///
+ /// Must be public to be serializable
+ ///
+ public class PatchDefinition
+ {
+ [XmlAttribute]
+ public string Name;
+
+ public List TargetVersions = new();
+ }
+
+ ///
+ /// Must be public to be serializable
+ ///
+ public class TargetVersion
+ {
+ [XmlAttribute]
+ public string Description;
+
+ public List TargetFiles = new();
+ }
+
+ ///
+ /// Must be public to be serializable
+ ///
+ public class TargetFile
+ {
+ private string _Path;
+ [XmlAttribute]
+ public string Path
+ {
+ get
+ {
+ return _Path;
+ }
+ set
+ {
+ _Path = value.TrimStart(new char[] { '\\' });
+ }
+ }
+
+ [XmlIgnore]
+ public byte[] HashOriginal;
+ [XmlAttribute("HashOriginal")]
+ public string HashOriginalAsString
+ {
+ get
+ {
+ return Converter.ConvertHexToString(HashOriginal, "");
+ }
+ set
+ {
+ HashOriginal = Converter.ConvertStringToHex(value);
+ }
+ }
+
+ [XmlIgnore]
+ public byte[] HashPatched;
+ [XmlAttribute("HashPatched")]
+ public string HashPatchedAsString
+ {
+ get
+ {
+ return Converter.ConvertHexToString(HashPatched, "");
+ }
+ set
+ {
+ HashPatched = Converter.ConvertStringToHex(value);
+ }
+ }
+
+ public List Patches = new();
+ public List Obsolete = new();
+ }
+
+ ///
+ /// Must be public to be serializable
+ ///
+ public class Patch
+ {
+ [XmlIgnore]
+ public UInt32 Address;
+ [XmlAttribute("Address")]
+ public string AddressAsString
+ {
+ get
+ {
+ return "0x" + Address.ToString("X8");
+ }
+ set
+ {
+ string NewValue = value;
+ if (NewValue.StartsWith("0x", StringComparison.OrdinalIgnoreCase))
+ NewValue = NewValue[2..];
+ Address = Convert.ToUInt32(NewValue, 16);
+ }
+ }
+
+ [XmlIgnore]
+ public byte[] OriginalBytes;
+ [XmlAttribute("OriginalBytes")]
+ public string OriginalBytesAsString
+ {
+ get
+ {
+ return Converter.ConvertHexToString(OriginalBytes, "");
+ }
+ set
+ {
+ OriginalBytes = Converter.ConvertStringToHex(value);
+ }
+ }
+
+ [XmlIgnore]
+ public byte[] PatchedBytes;
+ [XmlAttribute("PatchedBytes")]
+ public string PatchedBytesAsString
+ {
+ get
+ {
+ return Converter.ConvertHexToString(PatchedBytes, "");
+ }
+ set
+ {
+ PatchedBytes = Converter.ConvertStringToHex(value);
+ }
+ }
+ }
+}
diff --git a/Patcher/Patcher/Patcher.csproj b/Patcher/Patcher/Patcher.csproj
new file mode 100644
index 0000000..7f9acf5
--- /dev/null
+++ b/Patcher/Patcher/Patcher.csproj
@@ -0,0 +1,28 @@
+
+
+ net5.0-windows
+ WinExe
+ false
+ true
+
+
+ bin\x86\Debug\
+ MinimumRecommendedRules.ruleset
+
+
+ bin\x86\Release\
+ MinimumRecommendedRules.ruleset
+
+
+ bin\x64\Debug\
+ MinimumRecommendedRules.ruleset
+
+
+ bin\x64\Release\
+ MinimumRecommendedRules.ruleset
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Patcher/Patcher/Patcher.csproj.user b/Patcher/Patcher/Patcher.csproj.user
new file mode 100644
index 0000000..6696a72
--- /dev/null
+++ b/Patcher/Patcher/Patcher.csproj.user
@@ -0,0 +1,9 @@
+
+
+
+
+
+ Form
+
+
+
\ No newline at end of file
diff --git a/Patcher/Patcher/PeFile.cs b/Patcher/Patcher/PeFile.cs
new file mode 100644
index 0000000..85d0a6a
--- /dev/null
+++ b/Patcher/Patcher/PeFile.cs
@@ -0,0 +1,818 @@
+// Copyright (c) 2018, Rene Lergner - wpinternals.net - @Heathcliff74xda
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+
+// Explanation of PE header here:
+// https://msdn.microsoft.com/en-us/library/ms809762.aspx
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Runtime.InteropServices;
+using System.Linq;
+using WPinternals;
+
+namespace Patcher
+{
+ #region Structs
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct IMAGE_DOS_HEADER
+ {
+ public UInt16 e_magic;
+ public UInt16 e_cblp;
+ public UInt16 e_cp;
+ public UInt16 e_crlc;
+ public UInt16 e_cparhdr;
+ public UInt16 e_minalloc;
+ public UInt16 e_maxalloc;
+ public UInt16 e_ss;
+ public UInt16 e_sp;
+ public UInt16 e_csum;
+ public UInt16 e_ip;
+ public UInt16 e_cs;
+ public UInt16 e_lfarlc;
+ public UInt16 e_ovno;
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
+ public UInt16[] e_res1;
+ public UInt16 e_oemid;
+ public UInt16 e_oeminfo;
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
+ public UInt16[] e_res2;
+ public UInt32 e_lfanew;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct IMAGE_NT_HEADERS
+ {
+ public UInt32 Signature;
+ public IMAGE_FILE_HEADER FileHeader;
+ public IMAGE_OPTIONAL_HEADER32 OptionalHeader32;
+ public IMAGE_OPTIONAL_HEADER64 OptionalHeader64;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct IMAGE_FILE_HEADER
+ {
+ public UInt16 Machine;
+ public UInt16 NumberOfSections;
+ public UInt32 TimeDateStamp;
+ public UInt32 PointerToSymbolTable;
+ public UInt32 NumberOfSymbols;
+ public UInt16 SizeOfOptionalHeader;
+ public UInt16 Characteristics;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct IMAGE_OPTIONAL_HEADER32
+ {
+ public UInt16 Magic;
+ public Byte MajorLinkerVersion;
+ public Byte MinorLinkerVersion;
+ public UInt32 SizeOfCode;
+ public UInt32 SizeOfInitializedData;
+ public UInt32 SizeOfUninitializedData;
+ public UInt32 AddressOfEntryPoint;
+ public UInt32 BaseOfCode;
+ public UInt32 BaseOfData;
+ public UInt32 ImageBase;
+ public UInt32 SectionAlignment;
+ public UInt32 FileAlignment;
+ public UInt16 MajorOperatingSystemVersion;
+ public UInt16 MinorOperatingSystemVersion;
+ public UInt16 MajorImageVersion;
+ public UInt16 MinorImageVersion;
+ public UInt16 MajorSubsystemVersion;
+ public UInt16 MinorSubsystemVersion;
+ public UInt32 Win32VersionValue;
+ public UInt32 SizeOfImage;
+ public UInt32 SizeOfHeaders;
+ public UInt32 CheckSum;
+ public UInt16 Subsystem;
+ public UInt16 DllCharacteristics;
+ public UInt32 SizeOfStackReserve;
+ public UInt32 SizeOfStackCommit;
+ public UInt32 SizeOfHeapReserve;
+ public UInt32 SizeOfHeapCommit;
+ public UInt32 LoaderFlags;
+ public UInt32 NumberOfRvaAndSizes;
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
+ public IMAGE_DATA_DIRECTORY[] DataDirectory;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct IMAGE_OPTIONAL_HEADER64
+ {
+ public UInt16 Magic;
+ public Byte MajorLinkerVersion;
+ public Byte MinorLinkerVersion;
+ public UInt32 SizeOfCode;
+ public UInt32 SizeOfInitializedData;
+ public UInt32 SizeOfUninitializedData;
+ public UInt32 AddressOfEntryPoint;
+ public UInt32 BaseOfCode;
+ public UInt64 ImageBase;
+ public UInt32 SectionAlignment;
+ public UInt32 FileAlignment;
+ public UInt16 MajorOperatingSystemVersion;
+ public UInt16 MinorOperatingSystemVersion;
+ public UInt16 MajorImageVersion;
+ public UInt16 MinorImageVersion;
+ public UInt16 MajorSubsystemVersion;
+ public UInt16 MinorSubsystemVersion;
+ public UInt32 Win32VersionValue;
+ public UInt32 SizeOfImage;
+ public UInt32 SizeOfHeaders;
+ public UInt32 CheckSum;
+ public UInt16 Subsystem;
+ public UInt16 DllCharacteristics;
+ public UInt64 SizeOfStackReserve;
+ public UInt64 SizeOfStackCommit;
+ public UInt64 SizeOfHeapReserve;
+ public UInt64 SizeOfHeapCommit;
+ public UInt32 LoaderFlags;
+ public UInt32 NumberOfRvaAndSizes;
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
+ public IMAGE_DATA_DIRECTORY[] DataDirectory;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct IMAGE_DATA_DIRECTORY
+ {
+ public UInt32 VirtualAddress;
+ public UInt32 Size;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct IMAGE_SECTION_HEADER
+ {
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8)]
+ public string Name;
+ public Misc Misc;
+ public UInt32 VirtualAddress;
+ public UInt32 SizeOfRawData;
+ public UInt32 PointerToRawData;
+ public UInt32 PointerToRelocations;
+ public UInt32 PointerToLinenumbers;
+ public UInt16 NumberOfRelocations;
+ public UInt16 NumberOfLinenumbers;
+ public UInt32 Characteristics;
+ }
+
+ [StructLayout(LayoutKind.Explicit)]
+ public struct Misc
+ {
+ [FieldOffset(0)]
+ public UInt32 PhysicalAddress;
+ [FieldOffset(0)]
+ public UInt32 VirtualSize;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct IMAGE_EXPORT_DIRECTORY
+ {
+ public UInt32 Characteristics;
+ public UInt32 TimeDateStamp;
+ public UInt16 MajorVersion;
+ public UInt16 MinorVersion;
+ public UInt32 Name;
+ public UInt32 Base;
+ public UInt32 NumberOfFunctions;
+ public UInt32 NumberOfNames;
+ ///
+ /// RVA from base of image
+ ///
+ public UInt32 AddressOfFunctions;
+ ///
+ /// RVA from base of image
+ ///
+ public UInt32 AddressOfNames;
+ ///
+ /// RVA from base of image
+ ///
+ public UInt32 AddressOfNameOrdinals;
+ }
+
+ [StructLayout(LayoutKind.Explicit)]
+ public struct IMAGE_IMPORT_DESCRIPTOR
+ {
+ #region union
+ ///
+ /// CSharp doesnt really support unions, but they can be emulated by a field offset 0
+ ///
+ [FieldOffset(0)]
+ public uint Characteristics; // 0 for terminating null import descriptor
+ ///
+ /// RVA to original unbound IAT (PIMAGE_THUNK_DATA)
+ ///
+ [FieldOffset(0)]
+ public uint OriginalFirstThunk;
+ #endregion
+
+ [FieldOffset(4)]
+ public uint TimeDateStamp;
+ [FieldOffset(8)]
+ public uint ForwarderChain;
+ [FieldOffset(12)]
+ public uint Name;
+ [FieldOffset(16)]
+ public uint FirstThunk;
+ }
+
+ public struct RUNTIME_FUNCTION_64
+ {
+ public UInt64 RVAofBeginAddress;
+ public UInt64 RVAofEndAddress;
+ public UInt64 RVAofUnwindData;
+ }
+
+ public struct RUNTIME_FUNCTION_32
+ {
+ public UInt32 RVAofBeginAddress;
+ public UInt32 RVAofUnwindData;
+ }
+
+ public static class Constants
+ {
+ public static class SectionFlags
+ {
+ public const UInt32 IMAGE_SCN_CNT_CODE = 0x00000020;
+ }
+ }
+
+ internal enum ResourceType
+ {
+ ///
+ /// Accelerator table.
+ ///
+ RT_ACCELERATOR = 9,
+ ///
+ /// Animated cursor.
+ ///
+ RT_ANICURSOR = 21,
+ ///
+ /// Animated icon.
+ ///
+ RT_ANIICON = 22,
+ ///
+ /// Bitmap resource.
+ ///
+ RT_BITMAP = 2,
+ ///
+ /// Hardware-dependent cursor resource.
+ ///
+ RT_CURSOR = 1,
+ ///
+ /// Dialog box.
+ ///
+ RT_DIALOG = 5,
+ ///
+ /// Allows
+ ///
+ RT_DLGINCLUDE = 17,
+ ///
+ /// Font resource.
+ ///
+ RT_FONT = 8,
+ ///
+ /// Font directory resource.
+ ///
+ RT_FONTDIR = 7,
+ ///
+ /// Hardware-independent cursor resource.
+ ///
+ RT_GROUP_CURSOR = RT_CURSOR + 11,
+ ///
+ /// Hardware-independent icon resource.
+ ///
+ RT_GROUP_ICON = RT_ICON + 11,
+ ///
+ /// HTML resource.
+ ///
+ RT_HTML = 23,
+ ///
+ /// Hardware-dependent icon resource.
+ ///
+ RT_ICON = 3,
+ ///
+ /// Side-by-Side Assembly Manifest.
+ ///
+ RT_MANIFEST = 24,
+ ///
+ /// Menu resource.
+ ///
+ RT_MENU = 4,
+ ///
+ /// Message-table entry.
+ ///
+ RT_MESSAGETABLE = 11,
+ ///
+ /// Plug and Play resource.
+ ///
+ RT_PLUGPLAY = 19,
+ ///
+ /// Application-defined resource (raw data).
+ ///
+ RT_RCDATA = 10,
+ ///
+ /// String-table entry.
+ ///
+ RT_STRING = 6,
+ ///
+ /// Version resource.
+ ///
+ RT_VERSION = 16,
+ ///
+ /// VXD
+ ///
+ RT_VXD = 20,
+ RT_DLGINIT = 240,
+ RT_TOOLBAR = 241
+ };
+
+ #endregion
+
+ public class PeFile
+ {
+ #region Fields
+
+ public readonly IMAGE_DOS_HEADER DosHeader;
+ public IMAGE_NT_HEADERS NtHeaders;
+ private readonly IList _sectionHeaders = new List();
+
+ public List Sections = new();
+ public List Exports = new();
+ public List Imports = new();
+ public List RuntimeFunctions = new();
+ public byte[] Buffer;
+ public UInt64 ImageBase;
+ public UInt64 EntryPoint;
+ public UInt64 ExportDirectoryVirtualOffset;
+ public UInt64 ImportDirectoryVirtualOffset;
+ public UInt64 RuntimeDirectoryVirtualOffset;
+ public UInt32 RuntimeDirectorySize;
+
+ #endregion
+
+ public PeFile(string Path): this(File.ReadAllBytes(Path))
+ {
+ }
+
+ public PeFile(byte[] Buffer)
+ {
+ int P = 0;
+
+ this.Buffer = Buffer;
+
+ // Read MS-DOS header section
+ DosHeader = MarshalBytesTo(Buffer, P);
+
+ // MS-DOS magic number should read 'MZ'
+ if (DosHeader.e_magic != 0x5a4d)
+ {
+ throw new InvalidOperationException("File is not a portable executable.");
+ }
+
+ // Read NT Headers
+ P = (int)DosHeader.e_lfanew;
+ NtHeaders.Signature = MarshalBytesTo(Buffer, P);
+
+ // Make sure we have 'PE' in the pe signature
+ if (NtHeaders.Signature != 0x4550)
+ {
+ throw new InvalidOperationException("Invalid portable executable signature in NT header.");
+ }
+
+ P += sizeof(UInt32);
+ NtHeaders.FileHeader = MarshalBytesTo(Buffer, P);
+
+ // Read optional headers
+ P += Marshal.SizeOf(typeof(IMAGE_FILE_HEADER));
+ if (Is32bitAssembly())
+ {
+ Load32bitOptionalHeaders(Buffer, P);
+ ImageBase = NtHeaders.OptionalHeader32.ImageBase;
+ EntryPoint = NtHeaders.OptionalHeader32.AddressOfEntryPoint;
+ ExportDirectoryVirtualOffset = NtHeaders.OptionalHeader32.DataDirectory[0].VirtualAddress;
+ ImportDirectoryVirtualOffset = NtHeaders.OptionalHeader32.DataDirectory[1].VirtualAddress;
+ RuntimeDirectoryVirtualOffset = NtHeaders.OptionalHeader32.DataDirectory[3].VirtualAddress;
+ RuntimeDirectorySize = NtHeaders.OptionalHeader32.DataDirectory[3].Size;
+ }
+ else
+ {
+ Load64bitOptionalHeaders(Buffer, P);
+ ImageBase = NtHeaders.OptionalHeader64.ImageBase;
+ EntryPoint = NtHeaders.OptionalHeader64.AddressOfEntryPoint;
+ ExportDirectoryVirtualOffset = NtHeaders.OptionalHeader64.DataDirectory[0].VirtualAddress;
+ ImportDirectoryVirtualOffset = NtHeaders.OptionalHeader64.DataDirectory[1].VirtualAddress;
+ RuntimeDirectoryVirtualOffset = NtHeaders.OptionalHeader64.DataDirectory[3].VirtualAddress;
+ RuntimeDirectorySize = NtHeaders.OptionalHeader64.DataDirectory[3].Size;
+ }
+
+ // Read Sections
+ _sectionHeaders.ToList().ForEach(s =>
+ {
+ byte[] RawCode = new byte[s.SizeOfRawData];
+ System.Buffer.BlockCopy(Buffer, (int)s.PointerToRawData, RawCode, 0, (int)s.SizeOfRawData);
+
+ Sections.Add(new Section { Header = s, Buffer = RawCode, VirtualAddress = s.VirtualAddress + (UInt32)ImageBase, VirtualSize = s.Misc.VirtualSize, IsCode = (s.Characteristics & (uint)Constants.SectionFlags.IMAGE_SCN_CNT_CODE) != 0 });
+ });
+
+ // Read Exports
+ // TODO: Proper support for 64-bit files
+ if (ExportDirectoryVirtualOffset != 0)
+ {
+ IMAGE_EXPORT_DIRECTORY ExportDirectory = MarshalBytesTo(Buffer, (int)ConvertVirtualOffsetToRawOffset((uint)ExportDirectoryVirtualOffset));
+ if (ExportDirectory.AddressOfNames != 0)
+ {
+ Section ExportsSection = GetSectionForVirtualAddress((uint)(ImageBase + ExportDirectory.AddressOfNames));
+ UInt32 OffsetNames = (UInt32)(ImageBase + ExportDirectory.AddressOfNames - ExportsSection.VirtualAddress);
+ UInt32 OffsetOrdinals = (UInt32)(ImageBase + ExportDirectory.AddressOfNameOrdinals - ExportsSection.VirtualAddress);
+ UInt32 OffsetFunctions = (UInt32)(ImageBase + ExportDirectory.AddressOfFunctions - ExportsSection.VirtualAddress);
+ string[] ExportNames = new string[ExportDirectory.NumberOfNames];
+ UInt16[] Ordinals = new UInt16[ExportDirectory.NumberOfNames];
+ UInt32[] VirtualAddresses = new UInt32[ExportDirectory.NumberOfFunctions];
+ for (int i = 0; i < ExportDirectory.NumberOfNames; i++)
+ {
+ UInt32 NamesRVA = ByteOperations.ReadUInt32(ExportsSection.Buffer, (UInt32)(OffsetNames + (i * sizeof(UInt32))));
+ UInt32 NameOffset = (UInt32)(NamesRVA + ImageBase - ExportsSection.VirtualAddress);
+ ExportNames[i] = ByteOperations.ReadAsciiString(ExportsSection.Buffer, NameOffset);
+
+ Ordinals[i] = ByteOperations.ReadUInt16(ExportsSection.Buffer, (UInt32)(OffsetOrdinals + (i * sizeof(UInt16))));
+ }
+ for (int i = 0; i < ExportDirectory.NumberOfFunctions; i++)
+ {
+ VirtualAddresses[i] = ByteOperations.ReadUInt32(ExportsSection.Buffer, (UInt32)(OffsetFunctions + (i * sizeof(UInt32))));
+ VirtualAddresses[i] -= VirtualAddresses[i] % 2; // Round down for Thumb2
+ }
+ for (int i = 0; i < ExportDirectory.NumberOfNames; i++)
+ {
+ Exports.Add(new FunctionDescriptor() { Name = ExportNames[i], VirtualAddress = (UInt32)(ImageBase + VirtualAddresses[Ordinals[i]]) });
+ }
+ }
+ }
+
+ // Read Imports
+ // TODO: Proper support for 64-bit files
+ if (ImportDirectoryVirtualOffset != 0)
+ {
+ Section ImportsSection = GetSectionForVirtualAddress((uint)(ImageBase + ImportDirectoryVirtualOffset));
+ IMAGE_IMPORT_DESCRIPTOR ImportDirectory;
+ do
+ {
+ ImportDirectory = MarshalBytesTo(ImportsSection.Buffer, (int)(ImportDirectoryVirtualOffset - (ImportsSection.VirtualAddress - ImageBase)));
+ if (ImportDirectory.OriginalFirstThunk != 0)
+ {
+ // ImportDirectory.OriginalFirstThunk is the VirtualOffset to an array of VirtualOffsets. They point to a struct with a word-value, followed by a zero-terminated ascii-string, which is the name of the import.
+ // ImportDirectory.FirstThunk points to an array pointers which is the actual import table.
+ UInt32 NameArrayOffset = ImportDirectory.OriginalFirstThunk - (ImportsSection.VirtualAddress - (UInt32)ImageBase);
+ UInt32 NameOffset;
+ int i = 0;
+ do
+ {
+ NameOffset = ByteOperations.ReadUInt32(ImportsSection.Buffer, NameArrayOffset);
+ if ((NameOffset < (ImportsSection.VirtualAddress - ImageBase)) || (NameOffset >= (ImportsSection.VirtualAddress + ImportsSection.VirtualSize - ImageBase)))
+ NameOffset = 0; // ImportDirectory.OriginalFirstThunk seems to contain Characteristics, not an offset to an array.
+ NameArrayOffset += sizeof(UInt32);
+ if (NameOffset != 0)
+ {
+ string Name = ByteOperations.ReadAsciiString(ImportsSection.Buffer, NameOffset + 2 - (ImportsSection.VirtualAddress - (UInt32)ImageBase));
+ Imports.Add(new FunctionDescriptor() { Name = Name, VirtualAddress = ImportDirectory.FirstThunk + (UInt32)ImageBase + (UInt32)(i * sizeof(UInt32)) });
+ i++;
+ }
+ }
+ while (NameOffset != 0);
+
+ ImportDirectoryVirtualOffset += (UInt64)Marshal.SizeOf(typeof(IMAGE_IMPORT_DESCRIPTOR));
+ }
+ }
+ while (ImportDirectory.OriginalFirstThunk != 0);
+ }
+
+ // Read Runtime functions
+ // TODO: Proper support for 64-bit files
+ if (RuntimeDirectoryVirtualOffset != 0)
+ {
+ Section RuntimeSection = GetSectionForVirtualAddress((uint)(ImageBase + RuntimeDirectoryVirtualOffset));
+ RUNTIME_FUNCTION_32 RuntimeFunction;
+ for (int i = 0; i < (RuntimeDirectorySize / Marshal.SizeOf(typeof(RUNTIME_FUNCTION_32))); i++)
+ {
+ RuntimeFunction = MarshalBytesTo(RuntimeSection.Buffer, (int)(RuntimeDirectoryVirtualOffset - (RuntimeSection.VirtualAddress - ImageBase)) + (i * Marshal.SizeOf(typeof(RUNTIME_FUNCTION_32))));
+ RuntimeFunctions.Add(new FunctionDescriptor() { Name = null, VirtualAddress = (UInt32)(RuntimeFunction.RVAofBeginAddress + ImageBase) });
+ }
+ }
+ }
+
+ public UInt32 GetChecksumOffset()
+ {
+ return ByteOperations.ReadUInt32(Buffer, 0x3C) + +0x58;
+ }
+
+ internal UInt32 CalculateChecksum()
+ {
+ UInt32 Checksum = 0;
+ UInt32 Hi;
+
+ // Clear file checksum
+ // ByteOperations.WriteUInt32(PEFile, GetChecksumOffset(), 0);
+ UInt32 ChecksumOffset = GetChecksumOffset();
+
+ for (UInt32 i = 0; i < ((UInt32)Buffer.Length & 0xfffffffe); i += 2)
+ {
+ if ((i < ChecksumOffset) || (i >= (ChecksumOffset + 4)))
+ Checksum += ByteOperations.ReadUInt16(Buffer, i);
+
+ Hi = Checksum >> 16;
+ if (Hi != 0)
+ {
+ Checksum = Hi + (Checksum & 0xFFFF);
+ }
+ }
+ if ((Buffer.Length % 2) != 0)
+ {
+ Checksum += (UInt32)ByteOperations.ReadUInt8(Buffer, (UInt32)Buffer.Length - 1);
+ Hi = Checksum >> 16;
+ if (Hi != 0)
+ {
+ Checksum = Hi + (Checksum & 0xFFFF);
+ }
+ }
+ Checksum += (UInt32)Buffer.Length;
+
+ // Write file checksum
+ // ByteOperations.WriteUInt32(Buffer, GetChecksumOffset(), Checksum);
+
+ return Checksum;
+ }
+
+ public IMAGE_DOS_HEADER GetDOSHeader()
+ {
+ return DosHeader;
+ }
+
+ public UInt32 GetPESignature()
+ {
+ return NtHeaders.Signature;
+ }
+
+ public IMAGE_FILE_HEADER GetFileHeader()
+ {
+ return NtHeaders.FileHeader;
+ }
+
+ public IMAGE_OPTIONAL_HEADER32 GetOptionalHeaders32()
+ {
+ return NtHeaders.OptionalHeader32;
+ }
+
+ public IMAGE_OPTIONAL_HEADER64 GetOptionalHeaders64()
+ {
+ return NtHeaders.OptionalHeader64;
+ }
+
+ public IList GetSectionHeaders()
+ {
+ return _sectionHeaders;
+ }
+
+ public bool Is32bitAssembly()
+ {
+ return (NtHeaders.FileHeader.Characteristics & 0x0100) == 0x0100;
+ }
+
+ private void Load64bitOptionalHeaders(byte[] Buffer, int Offset)
+ {
+ NtHeaders.OptionalHeader64 = MarshalBytesTo(Buffer, Offset);
+
+ // Should have 10 data directories
+ if (NtHeaders.OptionalHeader64.NumberOfRvaAndSizes != 0x10)
+ {
+ throw new InvalidOperationException("Invalid number of data directories in NT header");
+ }
+ Offset += Marshal.SizeOf(typeof(IMAGE_OPTIONAL_HEADER64));
+
+ for (int i = 0; i < NtHeaders.FileHeader.NumberOfSections; i++)
+ {
+ _sectionHeaders.Add(MarshalBytesTo(Buffer, Offset));
+ Offset += Marshal.SizeOf(typeof(IMAGE_SECTION_HEADER));
+ }
+ }
+
+ private void Load32bitOptionalHeaders(byte[] Buffer, int Offset)
+ {
+ NtHeaders.OptionalHeader32 = MarshalBytesTo(Buffer, Offset);
+
+ // Should have 10 data directories
+ if (NtHeaders.OptionalHeader32.NumberOfRvaAndSizes != 0x10)
+ {
+ throw new InvalidOperationException("Invalid number of data directories in NT header");
+ }
+ Offset += Marshal.SizeOf(typeof(IMAGE_OPTIONAL_HEADER32));
+
+ for (int i = 0; i < NtHeaders.FileHeader.NumberOfSections; i++)
+ {
+ _sectionHeaders.Add(MarshalBytesTo(Buffer, Offset));
+ Offset += Marshal.SizeOf(typeof(IMAGE_SECTION_HEADER));
+ }
+ }
+
+ public UInt32 ConvertVirtualOffsetToRawOffset(UInt32 VirtualOffset)
+ {
+ // TODO: Add 64-bit support
+ if (VirtualOffset < (Sections.OrderBy(s => s.VirtualAddress).First().VirtualAddress - GetOptionalHeaders32().ImageBase))
+ return VirtualOffset;
+
+ IMAGE_SECTION_HEADER? SectionHeaderSelection = _sectionHeaders.FirstOrDefault(h => (h.VirtualAddress <= VirtualOffset) && ((h.VirtualAddress + h.SizeOfRawData) > VirtualOffset));
+ if (SectionHeaderSelection == null)
+ throw new ArgumentOutOfRangeException();
+
+ IMAGE_SECTION_HEADER SectionHeader = (IMAGE_SECTION_HEADER)SectionHeaderSelection;
+
+ if (string.IsNullOrEmpty(SectionHeader.Name) || (SectionHeader.SizeOfRawData == 0))
+ throw new ArgumentOutOfRangeException();
+
+ return SectionHeader.PointerToRawData + (VirtualOffset - SectionHeader.VirtualAddress);
+ }
+
+ public UInt32 ConvertVirtualAddressToRawOffset(UInt32 VirtualAddress)
+ {
+ return ConvertVirtualOffsetToRawOffset((UInt32)(VirtualAddress - ImageBase));
+ }
+
+ internal uint ConvertRawOffsetToVirtualAddress(uint RawOffset)
+ {
+ // TODO: Add 64-bit support
+ if (RawOffset < Sections.OrderBy(s => s.VirtualAddress).First().Header.PointerToRawData)
+ return RawOffset + GetOptionalHeaders32().ImageBase;
+
+ IMAGE_SECTION_HEADER? SectionHeaderSelection = _sectionHeaders.FirstOrDefault(h => (h.PointerToRawData <= RawOffset) && ((h.PointerToRawData + h.SizeOfRawData) > RawOffset));
+ if (SectionHeaderSelection == null)
+ throw new ArgumentOutOfRangeException();
+
+ IMAGE_SECTION_HEADER SectionHeader = (IMAGE_SECTION_HEADER)SectionHeaderSelection;
+
+ if (string.IsNullOrEmpty(SectionHeader.Name) || (SectionHeader.SizeOfRawData == 0))
+ throw new ArgumentOutOfRangeException();
+
+ return RawOffset - SectionHeader.PointerToRawData + SectionHeader.VirtualAddress + GetOptionalHeaders32().ImageBase;
+ }
+
+ public Section GetSectionForVirtualAddress(UInt32 VirtualAddress)
+ {
+ return Sections.Find(s => (VirtualAddress >= s.VirtualAddress) && (VirtualAddress < (s.VirtualAddress + s.VirtualSize)));
+ }
+
+ private static T MarshalBytesTo(BinaryReader reader)
+ {
+ // Unmanaged data
+ byte[] bytes = reader.ReadBytes(Marshal.SizeOf(typeof(T)));
+
+ // Create a pointer to the unmanaged data pinned in memory to be accessed by unmanaged code
+ GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
+
+ // Use our previously created pointer to unmanaged data and marshal to the specified type
+ T theStructure = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
+
+ // Deallocate pointer
+ handle.Free();
+
+ return theStructure;
+ }
+
+ private static T MarshalBytesTo(byte[] Binary, int Offset)
+ {
+ // Unmanaged data
+ byte[] bytes = new byte[Marshal.SizeOf(typeof(T))];
+ System.Buffer.BlockCopy(Binary, Offset, bytes, 0, Marshal.SizeOf(typeof(T)));
+
+ // Create a pointer to the unmanaged data pinned in memory to be accessed by unmanaged code
+ GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
+
+ // Use our previously created pointer to unmanaged data and marshal to the specified type
+ T theStructure = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
+
+ // Deallocate pointer
+ handle.Free();
+
+ return theStructure;
+ }
+
+ internal byte[] GetResource(int[] Index)
+ {
+ UInt32 PEPointer = ByteOperations.ReadUInt32(Buffer, 0x3C);
+ UInt16 OptionalHeaderSize = ByteOperations.ReadUInt16(Buffer, PEPointer + 0x14);
+ UInt32 SectionTablePointer = PEPointer + 0x18 + OptionalHeaderSize;
+ UInt16 SectionCount = ByteOperations.ReadUInt16(Buffer, PEPointer + 0x06);
+ UInt32? ResourceSectionEntryPointer = null;
+ for (int i = 0; i < SectionCount; i++)
+ {
+ string SectionName = ByteOperations.ReadAsciiString(Buffer, (UInt32)(SectionTablePointer + (i * 0x28)), 8);
+ int e = SectionName.IndexOf('\0');
+ if (e >= 0)
+ SectionName = SectionName.Substring(0, e);
+ if (SectionName == ".rsrc")
+ {
+ ResourceSectionEntryPointer = (UInt32)(SectionTablePointer + (i * 0x28));
+ break;
+ }
+ }
+ if (ResourceSectionEntryPointer == null)
+ throw new Exception("Resource-section not found");
+ UInt32 ResourceRawSize = ByteOperations.ReadUInt32(Buffer, (UInt32)ResourceSectionEntryPointer + 0x10);
+ UInt32 ResourceRawPointer = ByteOperations.ReadUInt32(Buffer, (UInt32)ResourceSectionEntryPointer + 0x14);
+ UInt32 ResourceVirtualPointer = ByteOperations.ReadUInt32(Buffer, (UInt32)ResourceSectionEntryPointer + 0x0C);
+
+ UInt32 p = ResourceRawPointer;
+ for (int i = 0; i < Index.Length; i++)
+ {
+ UInt16 ResourceNamedEntryCount = ByteOperations.ReadUInt16(Buffer, p + 0x0c);
+ UInt16 ResourceIdEntryCount = ByteOperations.ReadUInt16(Buffer, p + 0x0e);
+ for (int j = ResourceNamedEntryCount; j < ResourceNamedEntryCount + ResourceIdEntryCount; j++)
+ {
+ UInt32 ResourceID = ByteOperations.ReadUInt32(Buffer, (UInt32)(p + 0x10 + (j * 8)));
+ UInt32 NextPointer = ByteOperations.ReadUInt32(Buffer, (UInt32)(p + 0x10 + (j * 8) + 4));
+ if (ResourceID == (UInt32)Index[i])
+ {
+ // Check high bit
+ if ((NextPointer & 0x80000000) == 0 != (i == (Index.Length - 1)))
+ throw new Exception("Bad resource path");
+
+ p = ResourceRawPointer + (NextPointer & 0x7fffffff);
+ break;
+ }
+ }
+ }
+
+ UInt32 ResourceValuePointer = ByteOperations.ReadUInt32(Buffer, p) - ResourceVirtualPointer + ResourceRawPointer;
+ UInt32 ResourceValueSize = ByteOperations.ReadUInt32(Buffer, p + 4);
+
+ byte[] ResourceValue = new byte[ResourceValueSize];
+ Array.Copy(Buffer, ResourceValuePointer, ResourceValue, 0, ResourceValueSize);
+
+ return ResourceValue;
+ }
+
+ internal Version GetFileVersion()
+ {
+ byte[] version = GetResource(new int[] { (int)ResourceType.RT_VERSION, 1, 1033 });
+
+ // RT_VERSION format:
+ // https://msdn.microsoft.com/en-us/library/windows/desktop/ms647001(v=vs.85).aspx
+ // https://msdn.microsoft.com/en-us/library/windows/desktop/ms646997(v=vs.85).aspx
+ const UInt32 FixedFileInfoPointer = 0x28;
+ UInt16 Major = ByteOperations.ReadUInt16(version, FixedFileInfoPointer + 0x0A);
+ UInt16 Minor = ByteOperations.ReadUInt16(version, FixedFileInfoPointer + 0x08);
+ UInt16 Build = ByteOperations.ReadUInt16(version, FixedFileInfoPointer + 0x0E);
+ UInt16 Revision = ByteOperations.ReadUInt16(version, FixedFileInfoPointer + 0x0C);
+
+ return new Version(Major, Minor, Build, Revision);
+ }
+
+ internal Version GetProductVersion()
+ {
+ byte[] version = GetResource(new int[] { (int)ResourceType.RT_VERSION, 1, 1033 });
+
+ // RT_VERSION format:
+ // https://msdn.microsoft.com/en-us/library/windows/desktop/ms647001(v=vs.85).aspx
+ // https://msdn.microsoft.com/en-us/library/windows/desktop/ms646997(v=vs.85).aspx
+ const UInt32 FixedFileInfoPointer = 0x28;
+ UInt16 Major = ByteOperations.ReadUInt16(version, FixedFileInfoPointer + 0x12);
+ UInt16 Minor = ByteOperations.ReadUInt16(version, FixedFileInfoPointer + 0x10);
+ UInt16 Build = ByteOperations.ReadUInt16(version, FixedFileInfoPointer + 0x16);
+ UInt16 Revision = ByteOperations.ReadUInt16(version, FixedFileInfoPointer + 0x14);
+
+ return new Version(Major, Minor, Build, Revision);
+ }
+ }
+
+ public class Section
+ {
+ public IMAGE_SECTION_HEADER Header;
+ public byte[] Buffer;
+ public UInt32 VirtualAddress;
+ public UInt32 VirtualSize;
+ public bool IsCode;
+ }
+
+ public class FunctionDescriptor
+ {
+ public string Name;
+ public UInt32 VirtualAddress;
+ }
+}
diff --git a/Patcher/Patcher/Program.cs b/Patcher/Patcher/Program.cs
new file mode 100644
index 0000000..9e8a06d
--- /dev/null
+++ b/Patcher/Patcher/Program.cs
@@ -0,0 +1,39 @@
+// Copyright (c) 2018, Rene Lergner - wpinternals.net - @Heathcliff74xda
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+
+using System;
+using System.Windows.Forms;
+
+namespace Patcher
+{
+ internal static class Program
+ {
+ ///
+ /// The main entry point for the application.
+ ///
+ [STAThread]
+ private static void Main()
+ {
+ Application.EnableVisualStyles();
+ Application.SetCompatibleTextRenderingDefault(false);
+ Application.Run(new MainForm());
+ }
+ }
+}
diff --git a/Patcher/Patcher/Properties/AssemblyInfo.cs b/Patcher/Patcher/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..3ef8a58
--- /dev/null
+++ b/Patcher/Patcher/Properties/AssemblyInfo.cs
@@ -0,0 +1,35 @@
+using System.Reflection;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("ARM Patcher")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("WPinternals.net")]
+[assembly: AssemblyProduct("ARM Patcher")]
+[assembly: AssemblyCopyright("Copyright © 2018 by Rene Lergner")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("b9aebca3-bcf8-4e4a-bf0e-5e764867be07")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/Patcher/Patcher/Properties/Resources.Designer.cs b/Patcher/Patcher/Properties/Resources.Designer.cs
new file mode 100644
index 0000000..0187c74
--- /dev/null
+++ b/Patcher/Patcher/Properties/Resources.Designer.cs
@@ -0,0 +1,71 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace Patcher.Properties
+{
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Resources
+ {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources()
+ {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager
+ {
+ get
+ {
+ if ((resourceMan == null))
+ {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Patcher.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture
+ {
+ get
+ {
+ return resourceCulture;
+ }
+ set
+ {
+ resourceCulture = value;
+ }
+ }
+ }
+}
diff --git a/Patcher/Patcher/Properties/Resources.resx b/Patcher/Patcher/Properties/Resources.resx
new file mode 100644
index 0000000..af7dbeb
--- /dev/null
+++ b/Patcher/Patcher/Properties/Resources.resx
@@ -0,0 +1,117 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/Patcher/Patcher/Properties/Settings.Designer.cs b/Patcher/Patcher/Properties/Settings.Designer.cs
new file mode 100644
index 0000000..ddbc4e1
--- /dev/null
+++ b/Patcher/Patcher/Properties/Settings.Designer.cs
@@ -0,0 +1,30 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace Patcher.Properties
+{
+
+
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
+ internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
+ {
+
+ private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+ public static Settings Default
+ {
+ get
+ {
+ return defaultInstance;
+ }
+ }
+ }
+}
diff --git a/Patcher/Patcher/Properties/Settings.settings b/Patcher/Patcher/Properties/Settings.settings
new file mode 100644
index 0000000..3964565
--- /dev/null
+++ b/Patcher/Patcher/Properties/Settings.settings
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/Patcher/nuget.config b/Patcher/nuget.config
new file mode 100644
index 0000000..efb6ffe
--- /dev/null
+++ b/Patcher/nuget.config
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+