274 lines
8.4 KiB
Diff
274 lines
8.4 KiB
Diff
diff --git a/node_modules/apk_unpack/apk_unpack.js b/node_modules/apk_unpack/apk_unpack.js
|
|
index e05cc3f..8cb2ed4 100644
|
|
--- a/node_modules/apk_unpack/apk_unpack.js
|
|
+++ b/node_modules/apk_unpack/apk_unpack.js
|
|
@@ -1,132 +1,83 @@
|
|
-// APK Asset unpack - uses apktool.
|
|
-// helper for cli.js
|
|
-var fs = require('fs'),
|
|
- lineReader = require('line-reader'),
|
|
- java = require('java'),
|
|
- path = require('path'),
|
|
- _last = { dir:'' },
|
|
- cwd = process.cwd(),
|
|
- _config = {
|
|
- apk : '',
|
|
- java : true,
|
|
- dir : ''
|
|
- };
|
|
-
|
|
-// capture console out
|
|
-/*var access = fs.createWriteStream(cwd + path.sep + 'apk_unpack.log');
|
|
-process.stdout.write = process.stderr.write = access.write.bind(access);*/
|
|
-
|
|
-// add classes for JADX and apktool
|
|
-java.classpath.pushDir(__dirname+path.sep+'java/jadx/');
|
|
-java.classpath.pushDir(__dirname+path.sep+'java/');
|
|
-java.options.push('-Xrs'); // reduce signal os signals
|
|
-//
|
|
-
|
|
-var classes = {
|
|
- ApkDecoder : java.import('brut.androlib.ApkDecoder'),
|
|
- libResources : java.import('brut.androlib.res.AndrolibResources'),
|
|
- ExtFile : java.import('brut.directory.ExtFile'),
|
|
- File : java.import('java.io.File'),
|
|
- //jadx
|
|
- _jadx : java.import('jadx.api.JadxDecompiler'),
|
|
- _jadx_args : java.import('jadx.cli.JadxCLIArgs'),
|
|
- _javalog : java.import('java.util.logging.LogManager'),
|
|
- _jadx_javaclass : java.import('jadx.api.JavaClass'),
|
|
- _jadx_classnode : java.import('jadx.core.dex.nodes.ClassNode'),
|
|
+const fs = require('fs');
|
|
+const path = require('path');
|
|
+const { execSync } = require('child_process');
|
|
+const yauzl = require('yauzl');
|
|
+const cheerio = require('cheerio');
|
|
+
|
|
+const cwd = process.cwd();
|
|
+const _config = {
|
|
+ apk: '',
|
|
+ dir: ''
|
|
};
|
|
+const _last = { dir: '' };
|
|
+
|
|
+
|
|
+const APKTOOL_JAR = path.join(__dirname, 'java', 'apktool_2.4.1.jar');
|
|
+const JADX_CLI_JAR = path.join(__dirname, 'java', 'jadx', 'jadx-cli.jar');
|
|
|
|
-var init = function(config) {
|
|
+var init = function (config) {
|
|
for (var i in config) {
|
|
_config[i] = config[i];
|
|
}
|
|
- // prepare apk and outputdir location
|
|
- var _apk = _config.apk, _outd = _config.dir;
|
|
- if (_config.apk.charAt(0)!=path.sep && _config.apk.charAt(0)!='~') {
|
|
- _config.full_apk = path.join(cwd,_config.apk);
|
|
+ if (_config.apk.charAt(0) !== path.sep && _config.apk.charAt(0) !== '~') {
|
|
+ _config.full_apk = path.join(cwd, _config.apk);
|
|
}
|
|
- if (_config.dir.charAt(_config.dir.length-1)!=path.sep) {
|
|
- // if outputdir doesn't end in / or path separator, add it.
|
|
- _config.full_dir = _config.dir+''+path.sep;
|
|
+ if (_config.dir.charAt(_config.dir.length - 1) !== path.sep) {
|
|
+ _config.full_dir = _config.dir + '' + path.sep;
|
|
}
|
|
- // turn standard java logger off
|
|
- classes._javalog.getLogManagerSync().resetSync();
|
|
};
|
|
|
|
-var extractAPK = function(cb) {
|
|
+var extractAPK = function (cb) {
|
|
if (fileExists(_config.apk)) {
|
|
_last.dir = _config.full_dir;
|
|
- var decoder = new classes.ApkDecoder();
|
|
- var _apkfile = new classes.File(_config.full_apk);
|
|
- decoder.setForceDeleteSync(true);
|
|
- decoder.setDecodeResourcesSync(java.newShort(256)); // exclude resources
|
|
- decoder.setOutDirSync(new classes.File(_config.full_dir));
|
|
- decoder.setApkFileSync(_apkfile);
|
|
- decoder.decodeSync();
|
|
- // try to decrypt manifest
|
|
- // get Resources Table
|
|
- var _res = decoder.getResTableSync();
|
|
- var _ext = new classes.ExtFile(_apkfile);
|
|
- var _lib = new classes.libResources();
|
|
- _lib.decodeManifest(_res, _ext, new classes.File(_config.full_dir), function() {
|
|
+
|
|
+
|
|
+
|
|
+
|
|
+ try {
|
|
+ const cmd = `java -jar "${APKTOOL_JAR}" d -f -o "${_config.full_dir}" "${_config.full_apk}"`;
|
|
+ execSync(cmd, { stdio: 'inherit' });
|
|
cb(true);
|
|
- });
|
|
+ } catch (err) {
|
|
+ console.error('Apktool extraction failed:', err);
|
|
+ cb(false);
|
|
+ }
|
|
} else {
|
|
cb(false);
|
|
}
|
|
};
|
|
|
|
-var decompile = function(onReady) {
|
|
- // extract classes.dex into output
|
|
- extract_dex(function() {
|
|
- // decrypt dex into src, using jadx
|
|
- var _dir = new classes.File(_last.dir + 'src' + path.sep);
|
|
- var _dex = new classes.File(_last.dir + 'classes.dex');
|
|
- var _dexs = java.newInstanceSync("java.util.ArrayList");
|
|
- _dexs.addSync(_dex);
|
|
- var _args = new classes._jadx_args();
|
|
- _args.setOutputDir(_dir);
|
|
- var jadx = new classes._jadx(_args);
|
|
- jadx.setOutputDir(_dir);
|
|
- //get sources manually, to avoid threads
|
|
- /*jadx.loadFiles(_dexs, function() {
|
|
- console.log('requesting classes:');
|
|
- var _j_classes = jadx.getClassesSync();
|
|
- console.log('got classes:',_j_classes);
|
|
- for (var _o in _j_classes) {
|
|
- //var _cl_node = new classes._jadx_classnode();
|
|
- var cls = new classes._jadx_javaclass(_j_classes[_o], jadx);
|
|
- console.log(cls);
|
|
- //.decompile(function(){
|
|
- // console.log('decompiling class:'+_o);
|
|
- //});
|
|
- }
|
|
- });*/
|
|
- /**/
|
|
- jadx.loadFiles(_dexs, function() {
|
|
- jadx.save(function() {
|
|
- onReady();
|
|
- });
|
|
- });/**/
|
|
+var decompile = function (onReady) {
|
|
+ extract_dex(function () {
|
|
+ var srcDir = path.join(_last.dir, 'src');
|
|
+ var dexFile = path.join(_last.dir, 'classes.dex');
|
|
+
|
|
+
|
|
+
|
|
+ try {
|
|
+ const cmd = `java -jar "${JADX_CLI_JAR}" -d "${srcDir}" "${dexFile}"`;
|
|
+ execSync(cmd, { stdio: 'inherit' });
|
|
+ onReady();
|
|
+ } catch (err) {
|
|
+ console.error('JADX decompilation failed:', err);
|
|
+ onReady();
|
|
+ }
|
|
});
|
|
};
|
|
|
|
-var extract_dex = function(onReady) {
|
|
- // extract classes.dex from apk into outputdir.
|
|
- var yauzl = require('yauzl');
|
|
- yauzl.open(_config.apk, { lazyEntries: false }, function(err,zipfile) {
|
|
+var extract_dex = function (onReady) {
|
|
+ yauzl.open(_config.apk, { lazyEntries: false }, function (err, zipfile) {
|
|
if (err) throw err;
|
|
- // zipfile.readEntry();
|
|
- zipfile.on('entry', function(entry) {
|
|
+ zipfile.on('entry', function (entry) {
|
|
if (!/\/$/.test(entry.fileName)) {
|
|
- // file
|
|
- zipfile.openReadStream(entry, function(err2, readStream) {
|
|
- //if (err2) throw err2;
|
|
- if (entry.fileName=='classes.dex') {
|
|
- readStream.pipe(fs.createWriteStream(_last.dir + entry.fileName));
|
|
+ zipfile.openReadStream(entry, function (err2, readStream) {
|
|
+ if (entry.fileName === 'classes.dex') {
|
|
+ const writeStream = fs.createWriteStream(path.join(_last.dir, entry.fileName));
|
|
+ readStream.pipe(writeStream);
|
|
zipfile.close();
|
|
- setTimeout(function(){
|
|
- onReady();
|
|
- }, 350); // delay for zip to release lock on dex
|
|
+ writeStream.on('finish', function () {
|
|
+ setTimeout(onReady, 350);
|
|
+ });
|
|
}
|
|
});
|
|
}
|
|
@@ -134,51 +85,42 @@ var extract_dex = function(onReady) {
|
|
});
|
|
};
|
|
|
|
-var info = function(callback) {
|
|
- // to be called after 'extractAPK', gets info about APK from decoded AndroidManifest.xml
|
|
- var cheerio = require('cheerio');
|
|
- var reply = {};
|
|
- var _manifest = _last.dir + 'AndroidManifest.xml';
|
|
+var info = function (callback) {
|
|
+ var _manifest = path.join(_last.dir, 'AndroidManifest.xml');
|
|
if (fileExists(_manifest)) {
|
|
- fs.readFile(_manifest, function(err,_data) {
|
|
- if (err) callback(true,{});
|
|
- var $ = cheerio.load(_data, { xmlMode:true });
|
|
- reply['package'] = $('manifest[package]').attr('package');
|
|
- reply['versionCode'] = $('manifest').attr('android\:versionCode');
|
|
- reply['versionName'] = $('manifest').attr('android\:versionName');
|
|
- reply['appName'] = $('manifest application').attr('android\:label');
|
|
- reply['_dir'] = _last.dir;
|
|
+ fs.readFile(_manifest, function (err, _data) {
|
|
+ if (err) return callback(true, {});
|
|
+ var $ = cheerio.load(_data, { xmlMode: true });
|
|
+ var reply = {
|
|
+ 'package': $('manifest[package]').attr('package'),
|
|
+ 'versionCode': $('manifest').attr('android:versionCode'),
|
|
+ 'versionName': $('manifest').attr('android:versionName'),
|
|
+ 'appName': $('manifest application').attr('android:label'),
|
|
+ '_dir': _last.dir
|
|
+ };
|
|
callback(false, reply);
|
|
});
|
|
} else {
|
|
console.log('apk_unpack -> run \'extract\' method before \'info\'.');
|
|
- callback(true, {})
|
|
+ callback(true, {});
|
|
+ }
|
|
+};
|
|
+
|
|
+var fileExists = function (filePath) {
|
|
+ try {
|
|
+ return fs.statSync(filePath).isFile();
|
|
+ } catch (err) {
|
|
+ return false;
|
|
}
|
|
};
|
|
|
|
-var fileExists = function(filePath)
|
|
-{
|
|
- try
|
|
- {
|
|
- return fs.statSync(filePath).isFile();
|
|
- }
|
|
- catch (err)
|
|
- {
|
|
- return false;
|
|
- }
|
|
-}
|
|
-
|
|
-var dirExists = function(filePath)
|
|
-{
|
|
- try
|
|
- {
|
|
- return fs.statSync(filePath).isDirectory();
|
|
- }
|
|
- catch (err)
|
|
- {
|
|
- return false;
|
|
- }
|
|
-}
|
|
+var dirExists = function (filePath) {
|
|
+ try {
|
|
+ return fs.statSync(filePath).isDirectory();
|
|
+ } catch (err) {
|
|
+ return false;
|
|
+ }
|
|
+};
|
|
|
|
exports.init = init;
|
|
exports.extract = extractAPK;
|