Update to 134
This commit is contained in:
commit
e42d7f9c47
|
|
@ -0,0 +1,199 @@
|
|||
const fs = require("fs");
|
||||
const https = require("https");
|
||||
|
||||
const { parse: parseHtml } = require('node-html-parser');
|
||||
const { parseScript } = require('esprima');
|
||||
const { exec, execSync } = require("child_process");
|
||||
|
||||
const FLYFF_URL = "https://universe.flyff.com/play";
|
||||
const OUTPUT_DIR = "./out/";
|
||||
|
||||
const { edit } = require("@webassemblyjs/wasm-edit");
|
||||
|
||||
const IMPORT_NAMESPACE = "flyff";
|
||||
|
||||
if (!fs.existsSync(OUTPUT_DIR))
|
||||
fs.mkdirSync(OUTPUT_DIR);
|
||||
|
||||
download(FLYFF_URL, main);
|
||||
|
||||
function main(html) {
|
||||
const root = parseHtml(html);
|
||||
|
||||
|
||||
const launcherScript = (() => {
|
||||
for(const script of root.querySelectorAll("script[type='text/javascript']")) {
|
||||
if(script.innerText.indexOf("FilemapVersion") > 0) {
|
||||
return script;
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
||||
const launcherJs = launcherScript.innerText;
|
||||
if(launcherJs.length == 0) {
|
||||
console.error("Unable to get launcher javascript.");
|
||||
}
|
||||
|
||||
const version = (() => {
|
||||
// Figure out which FilemapVersion the client currently uses.
|
||||
|
||||
// HACKY:
|
||||
// - The JS that's downloaded is written for use in HTML.
|
||||
// - The fact that it crashes very fast is abused.
|
||||
try {
|
||||
eval(launcherJs);
|
||||
} catch (e) { }
|
||||
|
||||
return FilemapVersion;
|
||||
})();
|
||||
|
||||
if (!fs.existsSync(OUTPUT_DIR + version))
|
||||
fs.mkdirSync(OUTPUT_DIR + version);
|
||||
|
||||
// Write the html out for debugging, if something has changed.try {
|
||||
fs.writeFileSync(OUTPUT_DIR + version + "/index.html", html);
|
||||
|
||||
console.log("Detected version: " + version);
|
||||
|
||||
const binaryUrl = "https://gcpcdn-universe.flyff.com/client/program/web/main-wasm32.wasm?" + version;
|
||||
const emscriptenUrl = "https://gcpcdn-universe.flyff.com/client/program/web/main-wasm32.js?" + version;
|
||||
|
||||
const binaryPath = OUTPUT_DIR + version + "/client-original.wasm";
|
||||
const file = fs.createWriteStream(binaryPath);
|
||||
https.get(binaryUrl, function (response) {
|
||||
response.pipe(file);
|
||||
|
||||
// after download completed close filestream
|
||||
file.on("finish", () => {
|
||||
file.close();
|
||||
|
||||
console.log("Binary: downloaded.");
|
||||
|
||||
download(emscriptenUrl, (js) => {
|
||||
const path = OUTPUT_DIR + version + "/client-original.js";
|
||||
fs.writeFileSync(path, js);
|
||||
console.log("Javascript Environment: downloaded.");
|
||||
|
||||
const importFnMap = new Map();
|
||||
const exportFnMap = new Map();
|
||||
|
||||
const handleVariableDeclarator = (node, meta) => {
|
||||
if(node.id.name === "wasmImports") {
|
||||
// Find minified import names.
|
||||
for(const property of node.init.properties) {
|
||||
const obfuscated = property.key.value;
|
||||
const nice = property.value.name;
|
||||
|
||||
importFnMap.set(obfuscated, nice);
|
||||
}
|
||||
} else {
|
||||
// Find minified export names.
|
||||
if(node.init && node.init.type === "AssignmentExpression" &&
|
||||
node.init.right && node.init.right.type === "FunctionExpression") {
|
||||
const nice = node.id.name;
|
||||
|
||||
// What a mess...
|
||||
const initExpression = node.init.right;
|
||||
const returnStatement = initExpression.body.body[0];
|
||||
const callee = returnStatement.argument.callee;
|
||||
const computedMemberExpression = callee.object.right.right;
|
||||
const obfuscated = computedMemberExpression.property.value;
|
||||
|
||||
exportFnMap.set(obfuscated, nice);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log("Javascript Environment: Finding minified function map...");
|
||||
parseScript(js, null, (node, meta) => {
|
||||
if(node.type === "VariableDeclarator")
|
||||
return handleVariableDeclarator(node, meta);
|
||||
|
||||
if(node.type === "ExpressionStatement") {
|
||||
// Find minified memory and table names.
|
||||
if((node.expression.left?.name === "wasmTable" ||
|
||||
node.expression.left?.name === "wasmMemory") &&
|
||||
node.expression.right?.object?.object?.name === "Module") {
|
||||
const obfuscated = node.expression.right.property.value;
|
||||
const jsName = node.expression.left.name;
|
||||
const nice = jsName === "wasmMemory" ? "memory" :
|
||||
jsName === "wasmTable" ? "table" : "";
|
||||
|
||||
exportFnMap.set(obfuscated, nice);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Use this to identify what you are looking for.
|
||||
// parseScript("var wasmTable; wasmTable = other['kc'];", null, (node, meta) => {
|
||||
// console.log(node);
|
||||
// });
|
||||
|
||||
const jsonObj = {
|
||||
// Sort 'm
|
||||
imports: Object.fromEntries(new Map([...importFnMap.entries()].sort())),
|
||||
exports: Object.fromEntries(new Map([...exportFnMap.entries()].sort())),
|
||||
};
|
||||
|
||||
fs.writeFileSync(OUTPUT_DIR + version + "/functions.json", JSON.stringify(jsonObj, null, 4));
|
||||
// console.log(fnMap);
|
||||
console.log("Identified " + importFnMap.size + " imported, and " + exportFnMap.size + " exported symbols!");
|
||||
|
||||
// console.log("Loading binary from " + binaryPath);
|
||||
// const binary = fs.readFileSync(binaryPath);
|
||||
// console.log("Loaded binary.");
|
||||
// const visitors = {
|
||||
// ModuleImport({ node }) {
|
||||
// console.log("Renaming " + node.module + "::" + node.name + " to " + IMPORT_NAMESPACE + "::" + fnMap[node.name]);
|
||||
// // console.log("MODULE IMPORT: ", node);
|
||||
// node.module = "flyff";
|
||||
// node.name = fnMap[node.name];
|
||||
// }
|
||||
// };
|
||||
|
||||
// console.log("Fixing imports (this may take a while...)");
|
||||
// const newBinary = edit(binary, visitors);
|
||||
// console.log("Fixed imports, writing new binary...");
|
||||
// console.log(typeof(newBinary), newBinary);
|
||||
// fs.writeFileSync(binaryPath, Buffer.from(newBinary));
|
||||
// console.log("Done writing new binary.");
|
||||
|
||||
// console.log("Binary: Starting decompilation...");
|
||||
|
||||
// const outputPath = OUTPUT_DIR + version + "/client.c";
|
||||
// execSync("wasm2c " + binaryPath + " -o " + outputPath);
|
||||
// console.log("Binary: Decompilation finished.");
|
||||
|
||||
// Tag our header file so we know what version of the filemap was when decompiled.
|
||||
// let data = fs.readFileSync(OUTPUT_DIR + version + "/client.h", 'utf8');
|
||||
// data = "/* Automagically edited by fugg-fetch for FilemapVersion " + version + " */\n\n" + data;
|
||||
// fs.writeFileSync(OUTPUT_DIR + version + "/client.h", data);
|
||||
|
||||
// console.log("Binary: wrote FilemapVersion to file.");
|
||||
|
||||
process.exit();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function download(url, cb) {
|
||||
https.get(url, (res) => {
|
||||
console.log("Got response: " + res.statusCode);
|
||||
|
||||
let body = "";
|
||||
res.on("data", function (chunk) {
|
||||
body += chunk;
|
||||
});
|
||||
|
||||
res.on('end', function () {
|
||||
cb(body);
|
||||
});
|
||||
|
||||
}).on('error', function (e) {
|
||||
console.log("Unable to retrieve webpage at '" + FLYFF_URL + "': " + e.message);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"name": "fugg-fetch",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"start": "node index.js",
|
||||
"deob": "synchrony deobfuscate ./out/client_47.js"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@webassemblyjs/wasm-edit": "^1.11.1",
|
||||
"deobfuscator": "^2.3.0",
|
||||
"esprima": "^4.0.1",
|
||||
"node-fetch": "^3.2.9",
|
||||
"node-html-parser": "^5.3.3"
|
||||
}
|
||||
}
|
||||
Reference in New Issue