From 8e33e589393476287b75bd069e58cd9777c5679d Mon Sep 17 00:00:00 2001 From: kitbyte Date: Sat, 4 Jan 2025 17:53:37 +0200 Subject: [PATCH] Moved to electron. Beta desktop app 0.0.1 --- README.md | 30 ++++--- appicon.ico | Bin 0 -> 105213 bytes index.html | 13 +++ index.js | 44 +++++++++- memoryScanner.js | 7 ++ package.json | 14 ++- renderer.js | 33 ++++++- styles.css | 40 ++++++++- unlocker.js | 220 +++++++++++++++++------------------------------ updater.js | 105 ++++++++++++++++++++++ 10 files changed, 345 insertions(+), 161 deletions(-) create mode 100644 appicon.ico create mode 100644 updater.js diff --git a/README.md b/README.md index 771c9d1..d45cbf8 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Yes, this is safe. This script is open-source, and you can check the code yourse With this patch you will be able to use the latest version together with Pro. -## 👀 What features will be available? +## 💫 What features will be available? ✅ Unlimited usage time
✅ No ads
@@ -21,21 +21,25 @@ With this patch you will be able to use the latest version together with Pro. ❌ Connect phone
❌ Hotkeys (hotkey functionality breaks after patch for unknown reason) -## ❓ How to use? +## 👀 How to use? -1. **Clone the Repository (or just download source code)** - ``` bash - git clone https://github.com/k1tbyte/Wemod-patcher.git - cd Wemod-patcher - ``` -2. **Run the script** - ``` bash - bun install/npm install/yarn install/whatever you use - node ./unlocker.js - ``` -3. **Follow the instructions** +1. Go to [Releases](https://github.com/k1tbyte/Wemod-Patcher/releases) page. +2. Download latest version +3. Run and click the patch --- + +## ❓ Q&A + +- 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 + +--- + ## 📜 License This project is licensed under the Apache-2.0 - see the [LICENSE](LICENSE.txt) file for details. diff --git a/appicon.ico b/appicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..caf644717c787e1ddcc27c941bba853242e1e0d4 GIT binary patch literal 105213 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
~VooV7Iw6PS4xmpH~|_JXK{Q>UgD`KL*6 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 literal 0 HcmV?d00001 diff --git a/index.html b/index.html index 7d0c8cf..12f0086 100644 --- a/index.html +++ b/index.html @@ -28,6 +28,19 @@
Waiting for action...
+ +
+
+ + +
+ + + + +
diff --git a/index.js b/index.js index f37e483..9061c19 100644 --- a/index.js +++ b/index.js @@ -1,6 +1,13 @@ const { app, BrowserWindow, ipcMain, dialog } = require('electron'); const path = require('path'); const fs = require("fs"); +const Unlocker = require("./unlocker"); +const GitHubUpdater = require("./updater"); + +const packageJsonPath = path.join(__dirname, 'package.json'); +const packageData = require(packageJsonPath) +const updater = new GitHubUpdater(packageData.author, packageData.name); + function createWindow() { const win = new BrowserWindow({ @@ -20,6 +27,12 @@ function createWindow() { const checkWeModPath = (root) => fs.existsSync(path.join(root, 'WeMod.exe')) && fs.existsSync(path.join(root, 'resources/app.asar')) +function log(message, type = 'info') { + BrowserWindow.getAllWindows().forEach(win => { + win.webContents.send('log', { message, type }); + }); +} + app.whenReady().then(createWindow); app.on('window-all-closed', () => { @@ -44,6 +57,11 @@ ipcMain.handle('select-file', async () => { return null; }); +ipcMain.handle('apply-patch', async (event, path) => { + const unlocker = new Unlocker(path, (e) => log(e)) + await unlocker.start() +}) + ipcMain.handle('resolve-default-path', async () => { const defaultDir = path.join(process.env.LOCALAPPDATA || path.join(process.env.HOME || process.env.USERPROFILE, 'AppData', 'Local'), 'WeMod'); @@ -89,4 +107,28 @@ ipcMain.handle('start-patch', async (event, filePath) => { message: error.message }; } -}); \ No newline at end of file +}); + +ipcMain.handle("get-current-version", () => { + return packageData.version +}) + + +ipcMain.handle("check-updates", async () => { + return await updater.checkForUpdates() +}) + +ipcMain.on('open-link', () => { + require('electron').shell.openExternal("https://github.com/k1tbyte/Wemod-Patcher") +}) + +ipcMain.on("apply-update", async (event, source) => { + try { + log("Downloading update ...") + const path = await updater.downloadUpdate(source) + log("Preparation") + updater.applyUpdate(path) + } catch (err) { + log(err, "error") + } +}) \ No newline at end of file diff --git a/memoryScanner.js b/memoryScanner.js index 01ce815..0f25fed 100644 --- a/memoryScanner.js +++ b/memoryScanner.js @@ -55,6 +55,13 @@ async function patchBySignature(filePath, functionSignature, patchBytes, patchOf if (matchIndex !== -1) { const functionStartPosition = filePosition + matchIndex; + const checkBuffer = Buffer.alloc(patchBytes.length); + await fileHandle.read(checkBuffer, 0, patchBytes.length, functionStartPosition + patchOffset); + + if (Buffer.compare(checkBuffer, Buffer.from(patchBytes)) === 0) { + return 0; // Memory already patched + } + // Go to patch position await fileHandle.write(Buffer.from(patchBytes), 0, patchBytes.length, functionStartPosition + patchOffset); diff --git a/package.json b/package.json index 5088206..456a807 100644 --- a/package.json +++ b/package.json @@ -1,13 +1,13 @@ { "name": "wemod-patcher", - "version": "1.0.0", + "version": "0.0.1", "main": "index.js", "scripts": { "start": "electron .", "build": "electron-builder --win portable" }, "keywords": [], - "author": "kitbyte", + "author": "k1tbyte", "license": "Apache-2.0", "description": "", "dependencies": { @@ -23,14 +23,22 @@ "directories": { "output": "dist" }, - "asar": true, "win": { + "icon": "./appicon.ico", "target": [{ "target": "portable", "arch": ["x64"] }], "artifactName": "${productName}.exe" }, + "nsis": { + "oneClick": false, + "perMachine": false, + "allowToChangeInstallationDirectory": true, + "installerIcon": "./appicon.ico", + "uninstallerIcon": "./appicon.ico", + "installerHeaderIcon": "./appicon.ico" + }, "compression": "maximum", "removePackageScripts": true, "removePackageKeywords": true, diff --git a/renderer.js b/renderer.js index 31b5411..5e3aed4 100644 --- a/renderer.js +++ b/renderer.js @@ -5,13 +5,22 @@ class PatcherUI { this.selectedPath = ''; this.initializeElements(); this.bindEvents(); + + ipcRenderer.on('log', (event, { message, type }) => { + this.addLog(message, type); + }); } initializeElements() { this.filePathInput = document.getElementById('file-path'); this.browseBtn = document.getElementById('browse-btn'); this.patchBtn = document.getElementById('patch-btn'); + this.updateBtn = document.getElementById('updateBtn'); + this.versionLabel = document.getElementById('version-label'); this.logSection = document.querySelector('.log-section'); + document.getElementById(`sourceBtn`).addEventListener('click', () => { + ipcRenderer.send("open-link") + }) } bindEvents() { @@ -58,8 +67,11 @@ class PatcherUI { this.addLog('Starting patch process...', 'info'); try { - const result = await ipcRenderer.invoke('start-patch', this.selectedPath); - this.addLog(result.message, result.success ? 'success' : 'error'); + await ipcRenderer.invoke( + 'apply-patch', + this.selectedPath + ); + this.addLog("Success", 'success'); } catch (error) { this.addLog('Patch failed: ' + error.message, 'error'); } finally { @@ -68,12 +80,29 @@ class PatcherUI { } resolveDefault() { + + ipcRenderer.invoke("get-current-version").then((v) => + this.versionLabel.textContent = `Current version: ${v}`); + ipcRenderer.invoke("resolve-default-path").then(path => { this.addLog(path ? 'The WeMod folder has been found!' : "WeMod folder was not found. You need to specify the path manually", path ? "success" : "warning" ); this.setPath(path) }) + + ipcRenderer.invoke("check-updates").then(result => { + if(!result) { + return; + } + + this.updateBtn.className = "" + this.updateBtn.textContent = `Update to ${result.version}` + this.updateBtn.addEventListener('click', () => { + this.updateBtn.className = "hidden" + ipcRenderer.send("apply-update", result); + }); + }) } } diff --git a/styles.css b/styles.css index f4cfe48..71d997a 100644 --- a/styles.css +++ b/styles.css @@ -15,7 +15,7 @@ body { .container { background: #21223d; - padding: 24px; + padding: 24px 24px 15px; border: 1px solid #1e2039; height: 100%; width: 100%; @@ -29,7 +29,6 @@ body { height: 100%; justify-content: center; flex-direction: column; - gap: 20px; } .header { @@ -55,6 +54,7 @@ body { .path-section { background: #26254b; padding: 16px; + margin-top: 20px; border-radius: 6px; border: 1px solid #1e2039; } @@ -97,6 +97,7 @@ body { } .patch-btn { + margin: 15px 0; background: #6d67fd; color: #f8f6f6; border: none; @@ -149,4 +150,39 @@ body { .info { color: #6d67fd; +} + +#sourceBtn { + cursor: pointer; +} + +.footer { + margin-top: 10px; + display: flex; + align-items: center; + justify-content: space-between; +} + +.footer span { + font-size: 12px; +} + +.hidden { + display: none; +} + +#updateBtn { + background: springgreen; + color: #1e2039; + font-weight: bold; + border: none; + cursor: pointer; + border-radius: 10px; + margin-left: 10px; + padding: 10px 25px; + transition: all 0.1s; +} + +#updateBtn:hover { + scale: 105% } \ No newline at end of file diff --git a/unlocker.js b/unlocker.js index 5914a83..dc01156 100644 --- a/unlocker.js +++ b/unlocker.js @@ -1,13 +1,13 @@ const path = require('path'); const fs = require('fs'); -const readline = require('readline'); const asar = require('asar'); +const { execSync } = require("child_process"); const patchBySignature = require("./memoryScanner"); const regex = /getUserAccount\(\)\{.*?return\s+this\.#\w+\.fetch\(\{.*?}\).*?}/g; const asarPatch = "getUserAccount(){return this.#.fetch({endpoint:\"/v3/account\",method:\"GET\",name:\"/v3/account\",collectMetrics:0}).then(response=>{response.subscription={period:\"yearly\",state:\"active\"};response.flags=78;return response;})}" -const signature = "E8 ?? ?? ?? ?? 85 C0 75 ?? F6 C3 01 74 ?? 48 89 F9 E8 ?? ?? ?? ??" +const signature = "E8 ?? ?? ?? ?? ?? C0 75 ?? F6 C3 01 74 ?? 48 89 F9 E8 ?? ?? ?? ??" const patchBytes = [0x31] const patchOffset = 0x5 // ... @@ -16,157 +16,97 @@ const patchOffset = 0x5 // call near ptr funk_1445527E0 // ... -console.log("WeMod unlocker by K1tbyte") -let defaultDir = path.join(process.env.LOCALAPPDATA || path.join(process.env.HOME || process.env.USERPROFILE, 'AppData', 'Local'), 'WeMod'); -let appDir = null +class Unlocker { -const rl = readline.createInterface({ - input: process.stdin, - output: process.stdout -}); - -const read = (title, onSubmit, onExit) => { - rl.question(title, (answer) => { - try { - onSubmit(answer) - } finally { - rl.close() - onExit?.() - } - }); -} - -const checkWeModPaths = (dir) => { - return fs.existsSync(dir) && fs.existsSync(path.join(dir, 'WeMod.exe')) && fs.existsSync(path.join(dir, 'resources')) -} - -function getFetchFieldName(code) { - const match = code.match(/this\.#([a-zA-Z_$][0-9a-zA-Z_$]*)\.fetch/); - return match ? match[1] : null; -} - -const patchAsar = unpackedPath => { - let items = fs.readdirSync(unpackedPath,{ withFileTypes: true }) - items = items.filter(item => !item.isDirectory() && /^app-\w+/.test(item.name)); - if(items.length === 0) { - console.log(" - No app bundle found") - return; + constructor(appDir, logger) { + this.appDir = appDir; + this.logger = logger; } - let asarPatchApplied = false; - for (const item of items) { - const data = fs.readFileSync(path.join(unpackedPath, item.name), { encoding: 'utf8'}) + #getFetchFieldName(code) { + const match = code.match(/this\.#([a-zA-Z_$][0-9a-zA-Z_$]*)\.fetch/); + return match ? match[1] : null; + } - const matches = data.match(regex) - if(!matches) { - continue; - } - if(matches.length > 1) { - console.error(" - Multiple target functions found. Looks like the version is not supported") - throw new Error("Multiple target functions found"); + #patchAsar (unpackedPath) { + let items = fs.readdirSync(unpackedPath,{ withFileTypes: true }) + items = items.filter(item => !item.isDirectory() && /^app-\w+/.test(item.name)); + if(items.length === 0) { + throw new Error(" - No app bundle found"); } - const fetchFieldName = getFetchFieldName(matches[0]); - if(!fetchFieldName) { - console.error(" - Fetch field name not found") - throw new Error("Fetch field name not found"); - } + let asarPatchApplied = false; + for (const item of items) { + const data = fs.readFileSync(path.join(unpackedPath, item.name), { encoding: 'utf8'}) - const patch = asarPatch.replace(//g, fetchFieldName) - - console.log(" - Found target function in: ", item.name) - console.log(" - Patching asar...") - fs.writeFileSync(path.join(unpackedPath, item.name), data.replace(regex, patch), {encoding: 'utf8'}) - console.log(" - Patch applied") - asarPatchApplied = true; - break; - } - - if(!asarPatchApplied) { - throw new Error("Failed to apply patch"); - } -} - -const patchPE = async () => { - console.log(" - Patching PE...") - const pePath = path.join(appDir, 'WeMod.exe') - const procStart = await patchBySignature(pePath, signature, patchBytes, patchOffset) - if(procStart === -1) { - console.log(" - Signature not found or already patched") - return; - } - console.log(" - Patch saved") -} - -const start = async () => { - console.log(" - Extracting asar...") - try { - const asarPath = path.join(appDir, 'resources', 'app.asar') - asar.extractAll(asarPath, path.join(appDir, 'resources', 'app.asar.unpacked')) - } catch(e) { - console.error("Failed to extract asar", e) - return; - } - - const unpackedPath = path.join(appDir, 'resources', 'app.asar.unpacked') - - fs.renameSync(path.join(appDir, 'resources', 'app.asar'), path.join(appDir, 'resources', 'app.asar.backup')) - console.log(" - Backup saved") - patchAsar(unpackedPath) - - await asar.createPackageWithOptions(unpackedPath, path.join(appDir, 'resources', 'app.asar'), - { unpack: path.join(unpackedPath,"static/unpacked/**") }) - console.log(" - Patch saved") - - await patchPE() - console.log("Done!") - process.exit(0) -} - -const prepare = async () => { - console.log(" - Path: ", appDir || "Not found") - - if (!appDir) { - read("WeMod directory not found. Enter the path manually: ", (answer) => { - if (checkWeModPaths(answer)) { - appDir = answer; - return; + const matches = data.match(regex) + if(!matches) { + continue; + } + if(matches.length > 1) { + throw new Error(" - Multiple target functions found. Looks like the version is not supported"); } - console.log("Invalid path") - }, prepare); - return; - } - read("Continue? (y/n): ", (answer) => { - if(answer.toLowerCase() === 'y') { - start() - } - }) -} + const fetchFieldName = this.#getFetchFieldName(matches[0]); + if(!fetchFieldName) { + throw new Error(" - Fetch field name not found"); + } -if(fs.existsSync(defaultDir)) { - const items = fs.readdirSync(defaultDir, { withFileTypes: true }); - const appFolders = items - .filter(item => item.isDirectory() && /^app-\w+/.test(item.name)) - .map(item => { - const folderPath = path.join(defaultDir, item.name); - const stats = fs.statSync(folderPath); - return { - name: item.name, - path: folderPath, - mtime: stats.mtime, - }; - }); + const patch = asarPatch.replace(//g, fetchFieldName) - appFolders.sort((a, b) => b.mtime - a.mtime); - for(const folder of appFolders) { - if(checkWeModPaths(folder.path)) { - appDir = folder.path; + this.logger(" - Found target function in: " + item.name) + this.logger(" - Patching asar...") + fs.writeFileSync(path.join(unpackedPath, item.name), data.replace(regex, patch), {encoding: 'utf8'}) + this.logger(" - Patch applied") + asarPatchApplied = true; break; } + + if(!asarPatchApplied) { + throw new Error("Failed to apply patch"); + } + } + + + async #patchPE () { + this.logger(" - Patching PE...") + const pePath = path.join(this.appDir, 'WeMod.exe') + const procStart = await patchBySignature(pePath, signature, patchBytes, patchOffset) + if(procStart === -1) { + throw new Error(" - Signature not found or already patched") + } + + this.logger(procStart === 0 ? " - PE already patched" : " - Patch saved") + } + + async start () { + this.logger(" - Extracting asar...") + const asarPath = path.join(this.appDir, 'resources', 'app.asar') + const unpackedPath = path.join(this.appDir, 'resources', 'app.asar.unpacked') + const backupPath = path.join(this.appDir, 'resources', 'app.asar.backup') + + if(fs.existsSync(backupPath)) { + this.logger(" - Backup already exists") + } else { + execSync(`copy "${asarPath}" "${backupPath}"`, { encoding: "utf-8" }); + this.logger(" - Backup saved") + } + + try { + asar.extractAll(asarPath, unpackedPath) + } catch(e) { + throw new Error("Failed to extract asar: " + e) + } + + this.#patchAsar(unpackedPath) + + await asar.createPackageWithOptions(unpackedPath, asarPath, + { unpack: path.join(unpackedPath,"static/unpacked/**") }) + this.logger(" - Patch saved") + + await this.#patchPE() } } -(async () => { - await prepare(); -})(); +module.exports = Unlocker; + diff --git a/updater.js b/updater.js new file mode 100644 index 0000000..87a380f --- /dev/null +++ b/updater.js @@ -0,0 +1,105 @@ +const { app } = require("electron"); +const fs = require("fs"); +const path = require("path"); +const { exec } = require("child_process"); + +class GitHubUpdater { + constructor(owner, repo, currentVersion) { + this.owner = owner; + this.repo = repo; + this.currentVersion = currentVersion || app.getVersion(); + this.apiUrl = `https://api.github.com/repos/${owner}/${repo}/releases/latest`; + this.downloadDir = path.join(app.getPath("temp"), "update"); + } + + async checkForUpdates() { + const response = await fetch(this.apiUrl, { + headers: { "User-Agent": "GitHub-Updater" }, + }); + if (!response.ok) { + throw new Error(`An error occurred while checking for an update: ${response.statusText}`); + } + + const release = await response.json(); + const latestVersion = release.tag_name; + const assets = release.assets; + + if (this.currentVersion === latestVersion) { + return null; + } + + const asset = assets.find(a => a.name.endsWith(".exe")); + if (!asset) { + throw new Error("Unable to find files to update"); + } + + return { + version: latestVersion, + url: asset.browser_download_url, + name: asset.name, + }; + } + + async downloadUpdate(updateInfo) { + const response = await fetch(updateInfo.url); + if (!response.ok) { + throw new Error(`Error downloading file: ${response.statusText}`); + } + + if (!fs.existsSync(this.downloadDir)) { + fs.mkdirSync(this.downloadDir, { recursive: true }); + } + + const filePath = path.join(this.downloadDir, updateInfo.name); + const fileStream = fs.createWriteStream(filePath); + + await new Promise((resolve, reject) => { + const downloadStream = response.body.getReader(); + + const pump = async () => { + try { + while (true) { + const { done, value } = await downloadStream.read(); + if (done) { + fileStream.end(); + resolve(); + break; + } + fileStream.write(value); + } + } catch (error) { + reject(error); + } + }; + + fileStream.on("error", (error) => { + reject(new Error(`File write error: ${error.message}`)); + }); + + pump(); + }); + + return filePath; + } + + async applyUpdate(filePath) { + try { + const currentExecutable = process.env.PORTABLE_EXECUTABLE_FILE; + const updateScript = `Start-Sleep -Seconds 3; Copy-Item -Path '${filePath}' -Destination '${currentExecutable}' -Force; Remove-Item -Path '${filePath}' -Force; Start-Sleep -Seconds 2; Start-Process -FilePath '${currentExecutable}';`; + + exec(`start /b "" powershell -WindowStyle Hidden -Command "${updateScript}"`, { + windowsHide: true, + stdio: 'ignore' + }); + + setTimeout(() => { + app.quit(); + }, 1000); + + } catch (error) { + throw new Error(`Update failed: ${error.message}`); + } + } +} + +module.exports = GitHubUpdater;