Compare commits

...

7 Commits

14 changed files with 238 additions and 31 deletions
+30
View File
@@ -0,0 +1,30 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: k1tbyte
---
**WeMod version**: X.X.X
**Patcher version**: X.X.X
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Additional context**
Add any other context about the problem here.
+20
View File
@@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
+111
View File
@@ -0,0 +1,111 @@
# Contributing to WeMod Patcher
Thank you for your interest in the WeMod Patcher project! This document provides guidelines for contributing to the project.
## Table of Contents
- [Development Environment Setup](#development-environment-setup)
- [Bug Reports](#bug-reports)
- [Feature Suggestions](#feature-suggestions)
- [Creating a Pull Request](#creating-a-pull-request)
- [Code Style](#code-style)
- [Testing](#testing)
- [License](#license)
## Code of Conduct
By participating in this project, you commit to maintaining respectful interactions with all community members. Any form of insults, harassment, or other unacceptable behavior will not be tolerated.
## Project Structure
The project consists of the following main components:
- **WeModPatcher** - Main project containing the patcher logic and user interface
- **AsarSharp** - Library for working with ASAR archives (used for unpacking and modifying WeMod files)
- **Core** - Core of the patcher, including static and dynamic patching
- **Models** - Data models used in the project
- **View** - User interface components
## Development Environment Setup
1. Clone the repository:
```
git clone https://github.com/k1tbyte/Wemod-Patcher.git
```
2. Open the solution `Wemod-Patcher.sln` in Visual Studio or JetBrains Rider.
3. Restore NuGet packages.
4. Build the project.
## Bug Reports
If you've found a bug, please create an Issue with a detailed description:
- WeMod Patcher version
- WeMod version where the problem occurred
- Detailed steps to reproduce the bug
- Expected and actual behavior
- Screenshots or error logs (if available)
## Feature Suggestions
Suggestions for new features or improvements are welcome! Create an Issue describing your idea, explaining:
- What problem the proposed improvement solves
- How you envision implementing this feature
- Potential alternatives you've considered
## Creating a Pull Request
1. Fork the repository.
2. Create a branch with a descriptive name:
```
git checkout -b feature/feature-name
```
or
```
git checkout -b fix/fix-name
```
3. Make the necessary changes and commit with clear, descriptive messages.
4. Ensure your code follows the project's style.
5. Push the branch to your fork:
```
git push origin your-branch-name
```
6. Create a Pull Request to the main repository.
7. In the Pull Request description, explain the changes made and why they're necessary.
## Code Style
- Use C# naming conventions:
- PascalCase for class, method, and property names
- camelCase for local variables and parameters
- _camelCase for private fields
- Add comments for complex code sections or patching methods
- Follow SOLID and DRY principles
## Testing
Before submitting a Pull Request, ensure that:
1. Your code compiles without errors
2. You've manually tested the functionality
3. The patch works with the current version of WeMod
4. Changes don't break existing functionality
## License
By contributing, you agree that your contributions will be licensed under the [Apache License 2.0](LICENSE.md).
---
Thank you for contributing to the WeMod Patcher project!
+26 -6
View File
@@ -1,4 +1,8 @@
<div align="center">
![logo](./assets/icon.svg)
---
<h1>WeMod Patcher</h1>
</div>
@@ -15,11 +19,14 @@ With this patch you will be able to use the latest version together with Pro.
## 💫 What features will be available?
✅ Unlimited usage time <br/>
No ads <br/>
Disabling automatic updates (optional) <br/>
✅ Automatic patching of new WeMod versions <br/>
✅ AI Game guides <br/>
✅ Saving mods <br/>
✅ Exclusive to pro subscription customization for hacks <br/>
✅ Hotkeys (hotkey functionality is broken after static patching for unknown reason) <br/>
❌ Connect phone <br/>
❌ Hotkeys (hotkey functionality breaks after patch for unknown reason)
## 👀 How to use?
@@ -34,15 +41,28 @@ With this patch you will be able to use the latest version together with Pro.
- I applied the patch but when I inject I get stuck on 'Loading mods...'.
- Just close WeMod and try again
- During the game, some hacks are enabled without my input
- This is a bug after the patch, you have to turn off hotkeys in WeMod settings
- Why is the patch executable file size so large? It seems to me that you want to harm my system.
- The thing is that the application is written in Electron, so it also puts chromium, nodejs and some libraries in the exe. Maybe Electron is a temporary solution and in the future I will consider another option
- This is a bug after the static patch, you have to turn off hotkeys in WeMod settings
- VirusTotal claims that this program is a malware/trojan.
- Perhaps the patcher does have the same signatures as malware (virtual memory patching). But this is a false positive, you can look at the source code or even build the patcher yourself.
- Does this application transfer any data to the Internet from my computer?
- The short answer is NO. This application does not need access to the Internet. The most it does is download updates if you want it to.
- What makes this application better than other patchers?
- All actions related to patches are performed on your computer. No files of unknown origin will be downloaded.
---
## 🖼️ Screenshots
![1](./assets/screenshots/app1.png)
![2](./assets/screenshots/app2.png)
---
## 📜 License
This project is licensed under the Apache-2.0 - see the [LICENSE](LICENSE.txt) file for details.
This project is licensed under the Apache-2.0 - see the [LICENSE](LICENSE.md) file for details.
---
## ❤️ Support
[![ko-fi](https://www.ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/kitbyte)
---
[![Star History Chart](https://api.star-history.com/svg?repos=k1tbyte/Wemod-Patcher&type=Date)](https://www.star-history.com/#k1tbyte/Wemod-Patcher&Date)
+3
View File
@@ -10,6 +10,9 @@ namespace WeModPatcher
public const string Owner = "k1tbyte";
public static readonly string RepositoryUrl = $"https://github.com/{Owner}/{RepoName}";
public static readonly Version Version;
public static readonly string WeModBrandName = "Wand";
public static readonly string WeModExeName = "Wand.exe";
public static readonly string[] WeModRootFolders = { "WeMod", "Wand" };
// cmp dword ptr [rdx], 0
// jnz loc_XXXXXXXX
+30 -12
View File
@@ -43,21 +43,39 @@ namespace WeModPatcher.Core
}
var debugEvent = new Imports.DEBUG_EVENT();
var processIds = new List<uint>();
while (Imports.WaitForDebugEvent(ref debugEvent, 1000))
var processIds = new Dictionary<uint, bool>();
while (Imports.WaitForDebugEvent(ref debugEvent, uint.MaxValue))
{
uint continueStatus = Imports.DBG_CONTINUE;
var code = debugEvent.dwDebugEventCode;
// Console.WriteLine("Debug event code: " + code);
if (code == Imports.CREATE_PROCESS_DEBUG_EVENT)
{
processIds.Add(debugEvent.dwProcessId);
// Console.WriteLine("Spawning process: " + debugEvent.dwProcessId);
processIds.Add(debugEvent.dwProcessId, false);
}
else if (code == Imports.EXIT_PROCESS_DEBUG_EVENT)
{
processIds.Remove(debugEvent.dwProcessId);
if(processIds.Count == 0)
{
break;
}
}
else if (code == Imports.EXCEPTION_DEBUG_EVENT)
{
var exceptionInfo = Imports.MapUnmanagedStructure<Imports.EXCEPTION_DEBUG_INFO>(debugEvent.Union);
// pass the exception to the process
continueStatus = Imports.DBG_EXCEPTION_NOT_HANDLED;
if (exceptionInfo.ExceptionRecord.ExceptionCode == Imports.EXCEPTION_BREAKPOINT)
var exceptionInfo = Imports.MapUnmanagedStructure<Imports.EXCEPTION_DEBUG_INFO>(debugEvent.Union);
// Console.WriteLine("Exception code: " + exceptionInfo.ExceptionRecord.ExceptionCode);
if (exceptionInfo.ExceptionRecord.ExceptionCode == Imports.EXCEPTION_BREAKPOINT &&
processIds.TryGetValue(debugEvent.dwProcessId, out var wasPatched) && !wasPatched)
{
var process = Process.GetProcessById((int)debugEvent.dwProcessId);
// Console.WriteLine("Scanning process: " + process.ProcessName + " " + process.Id);
var address = MemoryUtils.ScanVirtualMemory(
process.Handle,
process.Modules[0].BaseAddress,
@@ -67,7 +85,7 @@ namespace WeModPatcher.Core
if (address != IntPtr.Zero)
{
MemoryUtils.SafeWriteVirtualMemory(
processIds[debugEvent.dwProcessId] = MemoryUtils.SafeWriteVirtualMemory(
process.Handle,
address + Constants.ExePatchSignature.Offset,
Constants.ExePatchSignature.PatchBytes
@@ -87,12 +105,12 @@ namespace WeModPatcher.Core
}
}
Imports.ContinueDebugEvent(debugEvent.dwProcessId, debugEvent.dwThreadId, Imports.DBG_CONTINUE);
Imports.ContinueDebugEvent(debugEvent.dwProcessId, debugEvent.dwThreadId, continueStatus);
}
foreach (var processId in processIds)
foreach (var entry in processIds)
{
Imports.DebugActiveProcessStop(processId);
Imports.DebugActiveProcessStop(entry.Key);
}
Imports.CloseHandle(processInfo.hProcess);
@@ -119,7 +137,7 @@ namespace WeModPatcher.Core
new StaticPatcher(latestPath, logger, config).Patch();
}
new RuntimePatcher(Path.Combine(latestPath, "WeMod.exe"))
new RuntimePatcher(Path.Combine(latestPath, Constants.WeModExeName))
.StartProcess();
}
@@ -127,7 +145,7 @@ namespace WeModPatcher.Core
public static void KillWeMod()
{
Process[] processes = Process.GetProcessesByName("WeMod");
Process[] processes = Process.GetProcessesByName(Constants.WeModBrandName);
for (int i = 0; processes.Length > i || i < 5; i++)
{
foreach (var process in processes)
@@ -142,7 +160,7 @@ namespace WeModPatcher.Core
}
}
processes = Process.GetProcessesByName("WeMod");
processes = Process.GetProcessesByName(Constants.WeModBrandName);
Thread.Sleep(250);
}
+2 -2
View File
@@ -64,7 +64,7 @@ namespace WeModPatcher.Core
_asarPath = Path.Combine(weModRootFolder, "resources", "app.asar");
_unpackedPath = Path.Combine(weModRootFolder, "resources", "app.asar.unpacked");
_backupPath = Path.Combine(weModRootFolder, "resources", "app.asar.backup");
_exePath = Path.Combine(_weModRootFolder, "WeMod.exe");
_exePath = Path.Combine(_weModRootFolder, Constants.WeModExeName);
}
private static string GetFetchFieldName(string targetFunction)
@@ -161,7 +161,7 @@ namespace WeModPatcher.Core
CheckPathExists = true,
AddExtension = true,
SupportMultiDottedExtensions = false,
FileName = "WeMod",
FileName = Constants.WeModBrandName,
};
if(fileDialog.ShowDialog() != DialogResult.OK)
+2 -2
View File
@@ -51,5 +51,5 @@ using System.Windows;
// 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")]
[assembly: AssemblyVersion("1.0.3.0")]
[assembly: AssemblyFileVersion("1.0.3.0")]
+9 -7
View File
@@ -11,7 +11,7 @@ namespace WeModPatcher.Utils
{
try
{
return File.Exists(Path.Combine(root, "WeMod.exe")) &&
return File.Exists(Path.Combine(root, Constants.WeModExeName)) &&
File.Exists(Path.Combine(root, "resources", "app.asar"));
}
catch
@@ -23,15 +23,17 @@ namespace WeModPatcher.Utils
public static string FindWeModDirectory()
{
string localAppDataPath = Environment.GetEnvironmentVariable("LOCALAPPDATA");
string defaultDir = Path.Combine(localAppDataPath ?? "", "WeMod");
if (!Directory.Exists(defaultDir))
foreach (var folder in Constants.WeModRootFolders)
{
return null;
var weModDir = Path.Combine(localAppDataPath ?? "", folder);
if(Directory.Exists(weModDir))
{
return FindLatestWeMod(weModDir);
}
}
return FindLatestWeMod(defaultDir);
return null;
}
public static string Base64Decode(string base64EncodedData)
+2
View File
@@ -247,10 +247,12 @@ namespace WeModPatcher.Utils.Win32
public const uint DEBUG_PROCESS = 0x00000001;
public const uint DBG_CONTINUE = 0x00010002;
public const uint CREATE_PROCESS_DEBUG_EVENT = 3;
public const uint EXIT_PROCESS_DEBUG_EVENT = 5;
public const uint EXCEPTION_DEBUG_EVENT = 1;
public const uint LOAD_DLL_DEBUG_EVENT = 6;
public const uint OUTPUT_DEBUG_STRING_EVENT = 8;
public const uint EXCEPTION_BREAKPOINT = 0x80000003;
public const uint DBG_EXCEPTION_NOT_HANDLED = 0x80010001;
// Constants for VirtualProtectex
public const uint PAGE_EXECUTE_READWRITE = 0x40;
+2 -2
View File
@@ -119,7 +119,7 @@ namespace WeModPatcher.View.MainWindow
// 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"),
var restoreExeResult = MemoryUtils.PatchFile( Path.Combine( WeModPath, Constants.WeModExeName),
Constants.ExePatchSignature, Constants.ExePatchSignature.OriginalBytes);
if (restoreExeResult == -1)
{
@@ -129,7 +129,7 @@ namespace WeModPatcher.View.MainWindow
{
Log(restoreExeResult == 0 ?
"Signature exe is original, does not require restoration"
: "WeMod.exe restored successfully", ELogType.Success);
: $"{Constants.WeModExeName} restored successfully", ELogType.Success);
}
}
catch
+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0,0,256,256" width="170px" height="170px" fill-rule="nonzero"><g transform="translate(17.92,17.92) scale(0.86,0.86)"><g fill="#27272a" fill-rule="nonzero" stroke="none" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="10" stroke-dasharray="" stroke-dashoffset="0" font-family="none" font-weight="none" font-size="none" text-anchor="none" style="mix-blend-mode: normal"><path d="M53.5814,276.83721c-41.10026,0 -74.4186,-33.31834 -74.4186,-74.4186v-148.83721c0,-41.10026 33.31834,-74.4186 74.4186,-74.4186h148.83721c41.10026,0 74.4186,33.31834 74.4186,74.4186v148.83721c0,41.10026 -33.31834,74.4186 -74.4186,74.4186z" id="shape"></path></g><g fill="#ffffff" fill-rule="nonzero" stroke="none" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="10" stroke-dasharray="" stroke-dashoffset="0" font-family="none" font-weight="none" font-size="none" text-anchor="none" style="mix-blend-mode: normal"><g transform="scale(5.12,5.12)"><path d="M47.845,22.185l-20.03,-20.03c-1.543,-1.543 -4.046,-1.553 -5.729,0.002l-19.931,20.028c-1.542,1.542 -1.554,4.045 0,5.727l19.934,19.934c0.772,0.772 1.785,1.16 2.816,1.16c1.026,0 2.07,-0.385 2.91,-1.16l19.933,-19.934c1.605,-1.605 1.648,-4.175 0.097,-5.727zM18,27c-1.105,0 -2,-0.895 -2,-2c0,-1.105 0.895,-2 2,-2c1.105,0 2,0.895 2,2c0,1.105 -0.895,2 -2,2zM25,34c-1.105,0 -2,-0.895 -2,-2c0,-1.105 0.895,-2 2,-2c1.105,0 2,0.895 2,2c0,1.105 -0.895,2 -2,2zM25,20c-1.105,0 -2,-0.895 -2,-2c0,-1.105 0.895,-2 2,-2c1.105,0 2,0.895 2,2c0,1.105 -0.895,2 -2,2zM32,27c-1.105,0 -2,-0.895 -2,-2c0,-1.105 0.895,-2 2,-2c1.105,0 2,0.895 2,2c0,1.105 -0.895,2 -2,2z"></path></g></g></g></svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB