@kakuzu_aon/apkz 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,410 @@
1
+ // ────────────[ KAKUZU ]────────────────────────────
2
+ // | Discord : kakuzu_aon
3
+ // | Telegram : kakuzu_aon
4
+ // | Github : kakuzu-aon
5
+ // | File : extract.js
6
+ // | License : MIT License © 2026 Kakuzu
7
+ // | Brief : Advanced APK extraction command
8
+ // ────────────────★─────────────────────────────────
9
+
10
+ const { Command } = require('commander');
11
+ const chalk = require('chalk').default;
12
+ const { default: ora } = require('ora');
13
+ const fs = require('fs-extra');
14
+ const path = require('path');
15
+ const AdmZip = require('adm-zip');
16
+ const cliProgress = require('cli-progress');
17
+ const ManifestParser = require('../utils/manifest-parser');
18
+ const NetworkAnalyzer = require('../utils/network-analyzer');
19
+ const VulnerabilityScanner = require('../utils/vulnerability-scanner');
20
+
21
+ const extractCommand = new Command('extract')
22
+ .description('Professional APK extraction with comprehensive analysis and reporting')
23
+ .argument('<apk-file>', 'APK file to extract')
24
+ .option('-o, --output <dir>', 'Output directory (default: ./extracted)', './extracted')
25
+ .option('-f, --force', 'Overwrite existing output directory')
26
+ .option('--no-resources', 'Skip resource extraction')
27
+ .option('--no-dex', 'Skip DEX file extraction')
28
+ .option('--no-native', 'Skip native library extraction')
29
+ .option('--no-assets', 'Skip assets extraction')
30
+ .option('--no-manifest', 'Skip manifest extraction')
31
+ .option('--no-meta', 'Skip metadata extraction')
32
+ .option('--analyze', 'Perform deep analysis during extraction')
33
+ .option('--vulnerability', 'Perform vulnerability scanning')
34
+ .option('--obfuscation', 'Perform obfuscation analysis')
35
+ .option('--network', 'Perform network analysis')
36
+ .option('--strings', 'Extract all strings from smali files')
37
+ .option('--signatures', 'Extract code signatures and metadata')
38
+ .option('--permissions', 'Extract permission analysis')
39
+ .option('--components', 'Extract component analysis')
40
+ .option('--framework', 'Detect development frameworks')
41
+ .option('--resources', 'Extract resource analysis')
42
+ .option('--native', 'Extract native library analysis')
43
+ .option('--parallel <num>', 'Parallel extraction threads', '4')
44
+ .option('--timeout <ms>', 'Extraction timeout in milliseconds', '300000')
45
+ .option('--progress', 'Show detailed progress information')
46
+ .option('--validate', 'Validate extracted files')
47
+ .option('--compress', 'Compress extracted files')
48
+ .option('--backup', 'Create backup of original APK')
49
+ .option('--export <file>', 'Export extraction report')
50
+ .option('--format <format>', 'Report format (json, html, csv)', 'json')
51
+ .option('--dry-run', 'Show extraction plan without executing')
52
+ .option('--template <name>', 'Use extraction template')
53
+ .option('--log-level <level>', 'Logging level (debug, info, warn, error)', 'info')
54
+ .action(async (apkFile, options) => {
55
+ let spinner;
56
+ try {
57
+ if (!fs.existsSync(apkFile)) {
58
+ console.error(chalk.red(`🔴 Error: APK file not found: ${apkFile}`));
59
+ process.exit(1);
60
+ }
61
+
62
+ const outputDir = path.resolve(options.output);
63
+
64
+ if (fs.existsSync(outputDir) && !options.force) {
65
+ console.error(chalk.red(`🔴 Error: Output directory exists: ${outputDir}`));
66
+ console.error(chalk.yellow('💡 Use --force to overwrite'));
67
+ process.exit(1);
68
+ }
69
+
70
+ spinner = ora('🔧 Starting advanced extraction...').start();
71
+
72
+ await performAdvancedExtraction(apkFile, outputDir, options);
73
+
74
+ spinner.succeed('Advanced extraction completed!');
75
+
76
+ } catch (error) {
77
+ if (spinner) spinner.fail('Extraction failed');
78
+ console.error(chalk.red('🔴 Error:'), error.message);
79
+ process.exit(1);
80
+ }
81
+ });
82
+
83
+ async function performAdvancedExtraction(apkPath, outputDir, options) {
84
+ const zip = new AdmZip(apkPath);
85
+ const entries = zip.getEntries();
86
+
87
+ // Create output directory
88
+ await fs.ensureDir(outputDir);
89
+
90
+ // Multi-progress bars
91
+ const multiProgress = new cliProgress.MultiBar({
92
+ format: chalk.cyan('📦 {bar}') + ' | {percentage}% | {value}/{total} {file}',
93
+ barCompleteChar: '█',
94
+ barIncompleteChar: '░',
95
+ hideCursor: true
96
+ }, cliProgress.Presets.shades_grey);
97
+
98
+ const extractionProgress = multiProgress.create(entries.length, 0, { file: 'Extracting files...' });
99
+ const analysisProgress = multiProgress.create(100, 0, { file: 'Analysis...' });
100
+
101
+ // Extract files
102
+ let processed = 0;
103
+ const extractedFiles = {
104
+ total: entries.length,
105
+ dex: 0,
106
+ resources: 0,
107
+ native: 0,
108
+ assets: 0,
109
+ manifest: 0,
110
+ certificates: 0,
111
+ other: 0
112
+ };
113
+
114
+ for (const entry of entries) {
115
+ const entryPath = path.join(outputDir, entry.entryName);
116
+
117
+ try {
118
+ // Skip based on options
119
+ if (!options.dex && entry.entryName.endsWith('.dex')) {
120
+ processed++;
121
+ extractionProgress.update(processed);
122
+ continue;
123
+ }
124
+
125
+ if (!options.resources && entry.entryName === 'resources.arsc') {
126
+ processed++;
127
+ extractionProgress.update(processed);
128
+ continue;
129
+ }
130
+
131
+ if (!options.native && entry.entryName.startsWith('lib/')) {
132
+ processed++;
133
+ extractionProgress.update(processed);
134
+ continue;
135
+ }
136
+
137
+ if (entry.isDirectory) {
138
+ await fs.ensureDir(entryPath);
139
+ } else {
140
+ await fs.ensureDir(path.dirname(entryPath));
141
+ await fs.writeFile(entryPath, entry.getData());
142
+
143
+ // Categorize files
144
+ if (entry.entryName.endsWith('.dex')) {
145
+ extractedFiles.dex++;
146
+ } else if (entry.entryName === 'resources.arsc') {
147
+ extractedFiles.resources++;
148
+ } else if (entry.entryName.startsWith('lib/')) {
149
+ extractedFiles.native++;
150
+ } else if (entry.entryName.startsWith('assets/')) {
151
+ extractedFiles.assets++;
152
+ } else if (entry.entryName === 'AndroidManifest.xml') {
153
+ extractedFiles.manifest++;
154
+ } else if (entry.entryName.startsWith('META-INF/')) {
155
+ extractedFiles.certificates++;
156
+ } else {
157
+ extractedFiles.other++;
158
+ }
159
+ }
160
+ } catch (error) {
161
+ console.warn(chalk.yellow(`⚠️ Warning: Could not extract ${entry.entryName}: ${error.message}`));
162
+ }
163
+
164
+ processed++;
165
+ extractionProgress.update(processed, { file: entry.entryName });
166
+ }
167
+
168
+ multiProgress.stop();
169
+
170
+ // Display extraction summary
171
+ console.log(chalk.bold('\n📊 Extraction Summary:'));
172
+ console.log(chalk.gray(` • Total files: ${extractedFiles.total}`));
173
+ console.log(chalk.cyan(` • DEX files: ${extractedFiles.dex}`));
174
+ console.log(chalk.blue(` • Resources: ${extractedFiles.resources}`));
175
+ console.log(chalk.magenta(` • Native libs: ${extractedFiles.native}`));
176
+ console.log(chalk.green(` • Assets: ${extractedFiles.assets}`));
177
+ console.log(chalk.yellow(` • Certificates: ${extractedFiles.certificates}`));
178
+ console.log(chalk.gray(` • Other files: ${extractedFiles.other}`));
179
+
180
+ // Perform analysis if requested
181
+ if (options.analyze || options.vulnerability || options.network) {
182
+ await performPostExtractionAnalysis(outputDir, options, analysisProgress);
183
+ }
184
+
185
+ // Extract strings if requested
186
+ if (options.strings) {
187
+ await extractAllStrings(outputDir);
188
+ }
189
+
190
+ // Extract signatures if requested
191
+ if (options.signatures) {
192
+ await extractSignatures(outputDir);
193
+ }
194
+
195
+ // Save extraction metadata
196
+ const metadata = {
197
+ timestamp: new Date().toISOString(),
198
+ source_apk: path.resolve(apkPath),
199
+ extracted_files: extractedFiles,
200
+ options: options,
201
+ analysis: {}
202
+ };
203
+
204
+ await fs.writeJson(path.join(outputDir, 'extraction_metadata.json'), metadata, { spaces: 2 });
205
+
206
+ console.log(chalk.green(`✅ Advanced extraction completed: ${outputDir}`));
207
+ }
208
+
209
+ async function performPostExtractionAnalysis(outputDir, options, progress) {
210
+ progress.update(0, { file: 'Starting analysis...' });
211
+
212
+ const analysis = {};
213
+
214
+ // Manifest analysis
215
+ if (fs.existsSync(path.join(outputDir, 'AndroidManifest.xml'))) {
216
+ progress.update(25, { file: 'Analyzing manifest...' });
217
+ try {
218
+ const manifestParser = new ManifestParser();
219
+ analysis.manifest = await manifestParser.parseManifest(outputDir);
220
+ } catch (error) {
221
+ console.warn(chalk.yellow('⚠️ Manifest analysis failed:', error.message));
222
+ }
223
+ }
224
+
225
+ // Network analysis
226
+ if (options.network) {
227
+ progress.update(50, { file: 'Analyzing network...' });
228
+ try {
229
+ const networkAnalyzer = new NetworkAnalyzer();
230
+ analysis.network = await networkAnalyzer.analyzeNetwork(outputDir);
231
+ } catch (error) {
232
+ console.warn(chalk.yellow('⚠️ Network analysis failed:', error.message));
233
+ }
234
+ }
235
+
236
+ // Vulnerability scanning
237
+ if (options.vulnerability) {
238
+ progress.update(75, { file: 'Scanning for vulnerabilities...' });
239
+ try {
240
+ const vulnScanner = new VulnerabilityScanner();
241
+ analysis.vulnerability = await vulnScanner.scanAPK(outputDir);
242
+ } catch (error) {
243
+ console.warn(chalk.yellow('⚠️ Vulnerability scanning failed:', error.message));
244
+ }
245
+ }
246
+
247
+ progress.update(100, { file: 'Analysis completed!' });
248
+
249
+ // Save analysis results
250
+ await fs.writeJson(path.join(outputDir, 'analysis_results.json'), analysis, { spaces: 2 });
251
+
252
+ // Display analysis summary
253
+ if (analysis.manifest) {
254
+ console.log(chalk.bold('\n📱 Application Information:'));
255
+ console.log(chalk.cyan(` • Package: ${analysis.manifest.package || 'Unknown'}`));
256
+ console.log(chalk.green(` • Version: ${analysis.manifest.versionName || 'Unknown'} (${analysis.manifest.versionCode || 'Unknown'})`));
257
+ console.log(chalk.blue(` • Min SDK: ${analysis.manifest.minSdk || 'Unknown'}`));
258
+ console.log(chalk.blue(` • Target SDK: ${analysis.manifest.targetSdk || 'Unknown'}`));
259
+
260
+ if (analysis.manifest.application?.debuggable === 'true') {
261
+ console.log(chalk.red(' ⚠️ Debug mode enabled'));
262
+ }
263
+ }
264
+
265
+ if (analysis.network) {
266
+ console.log(chalk.bold('\n🌐 Network Analysis:'));
267
+ console.log(chalk.cyan(` • URLs found: ${analysis.network.summary.totalUrls}`));
268
+ console.log(chalk.yellow(` • API keys: ${analysis.network.summary.apiKeysFound}`));
269
+ console.log(chalk.red(` • Risk level: ${analysis.network.security.riskLevel}`));
270
+ }
271
+
272
+ if (analysis.vulnerability) {
273
+ console.log(chalk.bold('\n🔒 Security Scan:'));
274
+ console.log(chalk.red(` • Risk Score: ${analysis.vulnerability.summary.riskScore}/100`));
275
+ console.log(chalk.red(` • Total vulnerabilities: ${analysis.vulnerability.summary.total}`));
276
+ console.log(chalk.red(` • Critical: ${analysis.vulnerability.summary.critical}`));
277
+ console.log(chalk.yellow(` • High: ${analysis.vulnerability.summary.high}`));
278
+ }
279
+ }
280
+
281
+ async function extractAllStrings(decodedPath) {
282
+ const SmaliEditor = require('../utils/smali-editor');
283
+ const spinner = ora('🔤 Extracting strings...').start();
284
+
285
+ try {
286
+ const smaliEditor = new SmaliEditor();
287
+ const strings = await smaliEditor.extractStrings(decodedPath, {
288
+ minLength: 3,
289
+ maxLength: 200,
290
+ includeUrls: true,
291
+ includeEmails: true
292
+ });
293
+
294
+ spinner.succeed(`Extracted ${strings.length} strings`);
295
+
296
+ // Save strings
297
+ const stringsPath = path.join(decodedPath, 'extracted_strings.json');
298
+ await fs.writeJson(stringsPath, strings, { spaces: 2 });
299
+
300
+ // Show sample
301
+ console.log(chalk.bold('\n🔤 Sample Strings:'));
302
+ strings.slice(0, 10).forEach((str, index) => {
303
+ console.log(chalk.gray(` ${index + 1}. "${str.value}" (${path.basename(str.file)})`));
304
+ });
305
+
306
+ if (strings.length > 10) {
307
+ console.log(chalk.gray(` ... and ${strings.length - 10} more strings`));
308
+ }
309
+
310
+ } catch (error) {
311
+ spinner.fail('String extraction failed');
312
+ console.error(chalk.red('🔴 Error:'), error.message);
313
+ }
314
+ }
315
+
316
+ async function extractSignatures(decodedPath) {
317
+ const spinner = ora('🔍 Extracting signatures...').start();
318
+
319
+ try {
320
+ const signatures = {
321
+ certificates: [],
322
+ manifest: {},
323
+ native_libs: [],
324
+ file_hashes: {}
325
+ };
326
+
327
+ // Extract certificates
328
+ const metaInfDir = path.join(decodedPath, 'META-INF');
329
+ if (fs.existsSync(metaInfDir)) {
330
+ const certFiles = await fs.readdir(metaInfDir);
331
+ for (const file of certFiles) {
332
+ if (file.endsWith('.RSA') || file.endsWith('.DSA') || file.endsWith('.SF')) {
333
+ const filePath = path.join(metaInfDir, file);
334
+ const stats = await fs.stat(filePath);
335
+ signatures.certificates.push({
336
+ name: file,
337
+ size: stats.size,
338
+ path: path.relative(decodedPath, filePath)
339
+ });
340
+ }
341
+ }
342
+ }
343
+
344
+ // Extract manifest info
345
+ const manifestPath = path.join(decodedPath, 'AndroidManifest.xml');
346
+ if (fs.existsSync(manifestPath)) {
347
+ const stats = await fs.stat(manifestPath);
348
+ signatures.manifest = {
349
+ size: stats.size,
350
+ modified: stats.mtime,
351
+ path: path.relative(decodedPath, manifestPath)
352
+ };
353
+ }
354
+
355
+ // Extract native library signatures
356
+ const libDir = path.join(decodedPath, 'lib');
357
+ if (fs.existsSync(libDir)) {
358
+ const archDirs = await fs.readdir(libDir);
359
+ for (const arch of archDirs) {
360
+ const archPath = path.join(libDir, arch);
361
+ if (fs.statSync(archPath).isDirectory()) {
362
+ const soFiles = await fs.readdir(archPath);
363
+ for (const file of soFiles) {
364
+ if (file.endsWith('.so')) {
365
+ const filePath = path.join(archPath, file);
366
+ const stats = await fs.stat(filePath);
367
+ signatures.native_libs.push({
368
+ name: file,
369
+ architecture: arch,
370
+ size: stats.size,
371
+ path: path.relative(decodedPath, filePath)
372
+ });
373
+ }
374
+ }
375
+ }
376
+ }
377
+ }
378
+
379
+ // Calculate file hashes for important files
380
+ const crypto = require('crypto');
381
+ const importantFiles = ['AndroidManifest.xml', 'classes.dex', 'resources.arsc'];
382
+
383
+ for (const file of importantFiles) {
384
+ const filePath = path.join(decodedPath, file);
385
+ if (fs.existsSync(filePath)) {
386
+ const content = await fs.readFile(filePath);
387
+ const hash = crypto.createHash('sha256').update(content).digest('hex');
388
+ signatures.file_hashes[file] = hash;
389
+ }
390
+ }
391
+
392
+ spinner.succeed('Signatures extracted');
393
+
394
+ // Save signatures
395
+ const signaturesPath = path.join(decodedPath, 'signatures.json');
396
+ await fs.writeJson(signaturesPath, signatures, { spaces: 2 });
397
+
398
+ // Display summary
399
+ console.log(chalk.bold('\n🔍 Signature Summary:'));
400
+ console.log(chalk.cyan(` • Certificates: ${signatures.certificates.length}`));
401
+ console.log(chalk.magenta(` • Native libraries: ${signatures.native_libs.length}`));
402
+ console.log(chalk.blue(` • File hashes: ${Object.keys(signatures.file_hashes).length}`));
403
+
404
+ } catch (error) {
405
+ spinner.fail('Signature extraction failed');
406
+ console.error(chalk.red('🔴 Error:'), error.message);
407
+ }
408
+ }
409
+
410
+ module.exports = extractCommand;