added runtime patcher, added MemoryUtils, fixes

This commit is contained in:
kitbyte
2025-03-24 14:51:58 +02:00
parent 123307aed7
commit d3ff13a528
33 changed files with 1294 additions and 287 deletions
+20
View File
@@ -0,0 +1,20 @@
<UserControl x:Class="WeModPatcher.View.Controls.InfoItem"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WeModPatcher.View.Controls"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Viewbox Width="20" Height="20" VerticalAlignment="Top">
<Path Fill="{Binding IconColor}" Data="{Binding IconData}"/>
</Viewbox>
<TextBlock Grid.Column="1" VerticalAlignment="Center" Margin="5 0 5 0" TextWrapping="Wrap"
FontSize="12" Text="{Binding Text}"/>
</Grid>
</UserControl>
@@ -0,0 +1,42 @@
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace WeModPatcher.View.Controls
{
public partial class InfoItem : UserControl
{
public static readonly DependencyProperty IconDataProperty =
DependencyProperty.Register(nameof(IconData), typeof(Geometry), typeof(InfoItem));
public static readonly DependencyProperty IconColorProperty =
DependencyProperty.Register(nameof(IconColor), typeof(Brush), typeof(InfoItem));
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register(nameof(Text), typeof(string), typeof(InfoItem));
public Geometry IconData
{
get => (Geometry)GetValue(IconDataProperty);
set => SetValue(IconDataProperty, value);
}
public Brush IconColor
{
get => (Brush)GetValue(IconColorProperty);
set => SetValue(IconColorProperty, value);
}
public string Text
{
get => (string)GetValue(TextProperty);
set => SetValue(TextProperty, value);
}
public InfoItem()
{
InitializeComponent();
this.DataContext = this;
}
}
}
+5 -3
View File
@@ -37,9 +37,11 @@
</Button.Style>
</Button>
<TextBlock x:Name="Title" Text="This is title" Foreground="{DynamicResource Foreground}"
HorizontalAlignment="Left" FontWeight="Bold" FontSize="16"
VerticalAlignment="Bottom"/>
<StackPanel Grid.Row="0" x:Name="TitleContainer" Orientation="Horizontal">
<TextBlock x:Name="Title" Text="This is title" Foreground="{DynamicResource Foreground}"
HorizontalAlignment="Left" FontWeight="Bold" FontSize="16"
VerticalAlignment="Bottom"/>
</StackPanel>
<ContentPresenter x:Name="Presenter" Margin="0 20 0 0"
Content="{Binding PopupContent}" Grid.Row="2"/>
@@ -65,6 +65,10 @@ namespace WeModPatcher.View.Controls
Visibility = Visibility.Collapsed;
Closed?.Invoke();
if (PopupContent is IDisposable disposable)
{
disposable.Dispose();
}
PopupContent = null;
Closed = null;
OpenedSemaphore.Release();
+3 -3
View File
@@ -88,14 +88,14 @@
</Grid.RowDefinitions>
<Grid Margin="10" Cursor="Hand" Background="Transparent">
<Grid.InputBindings>
<MouseBinding Gesture="LeftClick" Command="{Binding SetFolderPathCommand}" />
</Grid.InputBindings>
<TextBox Style="{StaticResource TitledTextBox}"
Uid="Folder path" IsReadOnly="True"
Text="{Binding WeModPath}"
VerticalAlignment="Center" Tag="Folder not found">
</TextBox>
<Grid.InputBindings>
<MouseBinding Gesture="LeftClick" Command="{Binding SetFolderPathCommand}" />
</Grid.InputBindings>
</Grid>
@@ -11,16 +11,19 @@ namespace WeModPatcher.View.MainWindow
public partial class MainWindow
{
public static MainWindow Instance;
public readonly MainWindowVm ViewModel;
public MainWindow()
{
InitializeComponent();
this.DataContext = new MainWindowVm(this);
this.ViewModel = new MainWindowVm(this);
this.DataContext = ViewModel;
VersionLabel.Text = Constants.Version.ToString();
Instance = this;
}
public void OpenPopup(object content, string title = null)
public void OpenPopup(FrameworkElement content, string title = null)
{
this.PopupHost.PopupContent = content;
PopupHost.Title.Text = title;
+39 -51
View File
@@ -2,12 +2,15 @@
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Windows.Threading;
using AsarSharp;
using WeModPatcher.ReactiveCore;
using WeModPatcher.Core;
using WeModPatcher.Models;
using WeModPatcher.ReactiveUICore;
using WeModPatcher.Utils;
using WeModPatcher.View.Popups;
using Application = System.Windows.Application;
@@ -18,7 +21,7 @@ namespace WeModPatcher.View.MainWindow
public class MainWindowVm : ObservableObject
{
private readonly MainWindow _view;
public ObservableCollection<LogEntry> LogList { get; } = new ObservableCollection<LogEntry>();
public ObservableCollection<LogEntry> LogList { get; set; } = new ObservableCollection<LogEntry>();
private static Updater _updater = new Updater();
private string _weModPath;
@@ -71,50 +74,6 @@ namespace WeModPatcher.View.MainWindow
public RelayCommand RestoreBackupCommand { get; }
public AsyncRelayCommand UpdateCommand { get; }
private static bool CheckWeModPath(string root)
{
try
{
return File.Exists(Path.Combine(root, "WeMod.exe")) &&
File.Exists(Path.Combine(root, "resources", "app.asar"));
}
catch
{
return false;
}
}
public string FindWeModDirectory()
{
string localAppDataPath = Environment.GetEnvironmentVariable("LOCALAPPDATA");
string defaultDir = Path.Combine(localAppDataPath ?? "", "WeMod");
if (!Directory.Exists(defaultDir))
{
return null;
}
var appFolders = Directory.EnumerateDirectories(defaultDir)
.Select(folderPath => new DirectoryInfo(folderPath))
.Where(dirInfo => Regex.IsMatch(dirInfo.Name, @"^app-\w+"))
.Select(dirInfo => new
{
Name = dirInfo.Name,
Path = dirInfo.FullName,
LastModified = dirInfo.LastWriteTime
})
.OrderByDescending(item => item.LastModified)
.ToList();
return (
from folder
in appFolders
where CheckWeModPath(folder.Path)
select folder.Path
).FirstOrDefault();
}
private void OnFolderPathSelection(object obj)
{
using (var dialog = new FolderBrowserDialog())
@@ -127,7 +86,7 @@ namespace WeModPatcher.View.MainWindow
string selectedPath = dialog.SelectedPath;
string fileName = Path.GetFileName(selectedPath);
if (CheckWeModPath(selectedPath))
if (Extensions.CheckWeModPath(selectedPath))
{
WeModPath = selectedPath;
return;
@@ -143,6 +102,7 @@ namespace WeModPatcher.View.MainWindow
private void OnBackupRestoring(object param)
{
var backupPath = Path.Combine(WeModPath, "resources", "app.asar.backup");
if (!File.Exists(backupPath))
{
@@ -155,6 +115,22 @@ namespace WeModPatcher.View.MainWindow
using (File.Open(backupPath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
{
}
// This shit doesn't look at the hash and verify() always returns true
//using X509Certificate2 cert = new X509Certificate2(X509Certificate.CreateFromSignedFile(filePath));
var restoreExeResult = MemoryUtils.PatchFile( Path.Combine( WeModPath, "WeMod.exe"),
Constants.ExePatchSignature, Constants.ExePatchSignature.OriginalBytes);
if (restoreExeResult == -1)
{
Log("Failed to restore the backup. Please close the WeMod and try again.", ELogType.Error);
}
else
{
Log(restoreExeResult == 0 ?
"Signature exe is original, does not require restoration"
: "WeMod.exe restored successfully", ELogType.Success);
}
}
catch
{
@@ -177,12 +153,24 @@ namespace WeModPatcher.View.MainWindow
return;
}
MainWindow.Instance.OpenPopup(new PatchVectorsPopup(async config =>
MainWindow.Instance.OpenPopup(new PatchVectorsPopup( async config =>
{
MainWindow.Instance.ClosePopup();
IsPatchEnabled = false;
await Task.Run(() => new Patcher(WeModPath, Log, config).Patch());
IsPatchEnabled = true;
await Task.Run(() =>
{
try
{
new StaticPatcher(WeModPath, Log, config).Patch();
AlreadyPatched = true;
}
catch (Exception e)
{
Log($"Failed to patch: {e.Message}", ELogType.Error);
IsPatchEnabled = true;
}
});
}), "What are we gonna patch?");
}
@@ -229,7 +217,7 @@ namespace WeModPatcher.View.MainWindow
RestoreBackupCommand = new RelayCommand(OnBackupRestoring);
UpdateCommand = new AsyncRelayCommand(OnUpdate);
WeModPath = FindWeModDirectory();
WeModPath = Extensions.FindWeModDirectory();
if (WeModPath == null)
{
Log("WeMod directory not found.", ELogType.Error);
+111 -16
View File
@@ -4,28 +4,123 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WeModPatcher.View.Popups"
xmlns:controls="clr-namespace:WeModPatcher.View.Controls"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
d:DesignHeight="Auto" d:DesignWidth="Auto"
Background="{DynamicResource Background}"
Foreground="{DynamicResource MutedForeground}"
FontWeight="Medium"
FontSize="13">
<Grid Margin="0 0 5 0">
<Grid.RowDefinitions>
<RowDefinition Height="27"/>
<RowDefinition Height="27"/>
<RowDefinition Height="27"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" VerticalAlignment="Center" Text="Activate WeMod Pro"/>
<CheckBox Grid.Row="0" x:Name="ActivateProBox" HorizontalAlignment="Right" VerticalAlignment="Center" IsChecked="True"/>
<UserControl.Resources>
<Button x:Key="BackButton" Click="BackClicked" VerticalAlignment="Bottom" Padding="3"
Margin="0 0 15 0"
Width="35" Height="23" Style="{StaticResource IconButton}"
Tag="{StaticResource ArrowLeft}"/>
</UserControl.Resources>
<Grid>
<Grid x:Name="PatchVectors" Visibility="Visible" Margin="0 0 5 0">
<Grid.RowDefinitions>
<RowDefinition Height="27"/>
<RowDefinition Height="27"/>
<RowDefinition Height="27"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" VerticalAlignment="Center" Text="Activate WeMod Pro"/>
<CheckBox Grid.Row="0" x:Name="ActivateProBox" HorizontalAlignment="Right" VerticalAlignment="Center" IsChecked="True"/>
<TextBlock Grid.Row="1" VerticalAlignment="Center" Text="Disable telemetry"/>
<CheckBox Grid.Row="1" x:Name="DisableTelemetryBox" HorizontalAlignment="Right" VerticalAlignment="Center"/>
<TextBlock Grid.Row="1" VerticalAlignment="Center" Text="Disable telemetry"/>
<CheckBox Grid.Row="1" x:Name="DisableTelemetryBox" HorizontalAlignment="Right" VerticalAlignment="Center"/>
<TextBlock Grid.Row="2" VerticalAlignment="Center" Text="Disable updates"/>
<CheckBox Grid.Row="2" x:Name="DisableUpdateBox" HorizontalAlignment="Right" VerticalAlignment="Center"/>
<TextBlock Grid.Row="2" VerticalAlignment="Center" Text="Disable updates"/>
<CheckBox Grid.Row="2" x:Name="DisableUpdateBox" HorizontalAlignment="Right" VerticalAlignment="Center"/>
<Button Grid.Row="3" Padding="0 5 0 5" Margin="0 15 0 0" Content="Continue"
Click="ButtonBase_OnClick"/>
<Button Grid.Row="3" Padding="0 5 0 5" Margin="0 15 0 0" Content="Continue"
Click="NextClicked"/>
</Grid>
<Grid x:Name="PatchMethod" Visibility="Collapsed" Width="650">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="250"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<Grid>
<Border Background="{DynamicResource Muted}" HorizontalAlignment="Right" Width="2"
CornerRadius="10"/>
<StackPanel>
<TextBlock FontSize="16" Text="Static" Foreground="{DynamicResource Foreground}" HorizontalAlignment="Center" Margin="0 0 0 10"/>
<controls:InfoItem
IconColor="SpringGreen"
IconData="{StaticResource CheckDecagram}"
Text="Starting WeMod without this program" />
<controls:InfoItem
Margin="0 15 0 0"
IconColor="PaleVioletRed"
IconData="{StaticResource AlertDecagram}"
Text="Violation of WeMod digital signature (possibly marked by antiviruses, anti-cheats)" />
<controls:InfoItem
Margin="0 10 0 0"
IconColor="PaleVioletRed"
IconData="{StaticResource AlertDecagram}"
Text="Auto-patching after WeMod updates is not available" />
<controls:InfoItem
Margin="0 10 0 0"
IconColor="PaleVioletRed"
IconData="{StaticResource AlertDecagram}"
Text="Hotkeys will be broken" />
</StackPanel>
</Grid>
<Grid Grid.Row="0" Grid.Column="1">
<StackPanel Margin="10 0 0 0">
<TextBlock FontSize="16" Text="Runtime" Foreground="{DynamicResource Foreground}"
HorizontalAlignment="Center" Margin="-10 0 0 10"/>
<controls:InfoItem
IconColor="SpringGreen"
IconData="{StaticResource CheckDecagram}"
Text="Hotkeys still work" />
<controls:InfoItem Margin="0 10 0 0"
IconColor="SpringGreen"
IconData="{StaticResource CheckDecagram}"
Text="Does not break the digital signature (does not make changes to the original .exe)" />
<controls:InfoItem Margin="0 10 0 0"
IconColor="SpringGreen"
IconData="{StaticResource CheckDecagram}"
Text="Automatically applies patches to new versions (referring to your current selection)" />
<controls:InfoItem
Margin="0 10 0 0"
IconColor="Yellow"
IconData="{StaticResource AlertDecagram}"
Text="The WeMod startup process is controlled by the patcher. (Don't worry, you will no longer see this window. You will run WeMod as usual but using the shortcut that will be created after choosing this method). So you will want to keep this program. Make sure it's in a safe directory (not Temp, Downloads, etc)." />
<controls:InfoItem Margin="0 10 0 0"
IconColor="PaleVioletRed"
IconData="{StaticResource AlertDecagram}"
Text="Running WeMod directly through official WeMod.exe is not possible until you restore patch backup" />
</StackPanel>
</Grid>
<Button Grid.Column="0" Grid.Row="1" VerticalAlignment="Bottom" HorizontalAlignment="Center"
Padding="10 5" Margin="0 15 0 0"
Click="OnStaticSelected"
Content="Use static"/>
<Button Grid.Column="1" Grid.Row="1" VerticalAlignment="Bottom" HorizontalAlignment="Center"
Padding="10 5" Margin="0 15 0 0"
Click="OnRuntimeSelected"
Content="Use runtime"/>
</Grid>
</Grid>
</UserControl>
@@ -3,22 +3,42 @@ using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using WeModPatcher.Models;
using WeModPatcher.View.Controls;
namespace WeModPatcher.View.Popups
{
public partial class PatchVectorsPopup : UserControl
public partial class PatchVectorsPopup : UserControl, IDisposable
{
private readonly Action<HashSet<EPatchType>> _onApply;
private readonly Action<PatchConfig> _onApply;
private readonly StackPanel _popupTitleContainer;
private string _originalTitle;
private readonly TextBlock _titleTextBlock;
public PatchVectorsPopup(Action<HashSet<EPatchType>> onApply)
public PatchVectorsPopup(Action<PatchConfig> onApply)
{
_onApply = onApply;
InitializeComponent();
_popupTitleContainer = MainWindow.MainWindow.Instance.PopupHost.TitleContainer;
_titleTextBlock = _popupTitleContainer.Children[0] as TextBlock;
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
private void BackClicked(object sender, RoutedEventArgs e)
{
if(ActivateProBox.IsChecked != true && DisableUpdateBox.IsChecked != true && DisableTelemetryBox.IsChecked != true)
Dispose();
PatchMethod.Visibility = Visibility.Collapsed;
PatchVectors.Visibility = Visibility.Visible;
}
private void OnRuntimeSelected(object sender, RoutedEventArgs e)
=> RaiseCallback(EPatchProcessMethod.Runtime);
private void OnStaticSelected(object sender, RoutedEventArgs e)
=> RaiseCallback(EPatchProcessMethod.Static);
private void RaiseCallback(EPatchProcessMethod method)
{
if (ActivateProBox.IsChecked != true && DisableUpdateBox.IsChecked != true &&
DisableTelemetryBox.IsChecked != true)
{
return;
}
@@ -28,13 +48,35 @@ namespace WeModPatcher.View.Popups
{
result.Add(EPatchType.ActivatePro);
}
if (DisableUpdateBox.IsChecked == true)
{
result.Add(EPatchType.DisableUpdates);
}
_onApply(result);
_onApply(new PatchConfig
{
PatchTypes = result,
PatchMethod = method
});
}
private void NextClicked(object sender, RoutedEventArgs e)
{
_popupTitleContainer.Children.Insert(0, FindResource("BackButton") as Button);
_originalTitle = _titleTextBlock.Text;
_titleTextBlock.Text = "Patch method";
PatchMethod.Visibility = Visibility.Visible;
PatchVectors.Visibility = Visibility.Collapsed;
}
public void Dispose()
{
if (PatchVectors.Visibility == Visibility.Collapsed)
{
_popupTitleContainer.Children.RemoveAt(0);
_titleTextBlock.Text = _originalTitle;
}
}
}
}