@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.
- package/README.md +392 -0
- package/package.json +53 -0
- package/src/commands/analyze.js +261 -0
- package/src/commands/batch.js +549 -0
- package/src/commands/build.js +134 -0
- package/src/commands/clean.js +159 -0
- package/src/commands/compile.js +285 -0
- package/src/commands/config.js +343 -0
- package/src/commands/decode.js +133 -0
- package/src/commands/decompile.js +444 -0
- package/src/commands/diff.js +334 -0
- package/src/commands/extract.js +410 -0
- package/src/commands/info.js +886 -0
- package/src/commands/install.js +258 -0
- package/src/commands/modify-enhanced.js +1077 -0
- package/src/commands/modify.js +375 -0
- package/src/commands/monitor.js +421 -0
- package/src/commands/plugin.js +239 -0
- package/src/commands/sign.js +169 -0
- package/src/commands/vulnerability-scan.js +404 -0
- package/src/commands/web.js +97 -0
- package/src/index.js +139 -0
- package/src/utils/config.js +492 -0
- package/src/utils/config.json +118 -0
- package/src/utils/icon-manager.js +544 -0
- package/src/utils/manifest-parser.js +506 -0
- package/src/utils/network-analyzer.js +461 -0
- package/src/utils/obfuscation-detector.js +819 -0
- package/src/utils/plugin-system.js +390 -0
- package/src/utils/smali-editor.js +480 -0
- package/src/utils/vulnerability-scanner.js +838 -0
- package/src/web/public/index.html +1017 -0
- package/src/web/web-server.js +587 -0
- package/test_files/test.js +131 -0
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
// ────────────[ KAKUZU ]────────────────────────────
|
|
2
|
+
// | Discord : kakuzu_aon
|
|
3
|
+
// | Telegram : kakuzu_aon
|
|
4
|
+
// | Github : kakuzu-aon
|
|
5
|
+
// | File : sign.js
|
|
6
|
+
// | License : MIT License © 2026 Kakuzu
|
|
7
|
+
// | Brief : APK signing command implementation
|
|
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 { exec } = require('child_process');
|
|
16
|
+
const util = require('util');
|
|
17
|
+
const execPromise = util.promisify(exec);
|
|
18
|
+
|
|
19
|
+
const signCommand = new Command('sign')
|
|
20
|
+
.description('Sign APK with keystore')
|
|
21
|
+
.argument('<apk-file>', 'APK file to sign')
|
|
22
|
+
.option('-k, --keystore <path>', 'Path to keystore file')
|
|
23
|
+
.option('-a, --alias <alias>', 'Keystore alias')
|
|
24
|
+
.option('-p, --password <password>', 'Keystore password')
|
|
25
|
+
.option('-o, --output <file>', 'Output signed APK file')
|
|
26
|
+
.option('--debug', 'Create debug keystore and sign')
|
|
27
|
+
.action(async (apkFile, options) => {
|
|
28
|
+
let spinner;
|
|
29
|
+
try {
|
|
30
|
+
if (!fs.existsSync(apkFile)) {
|
|
31
|
+
console.error(chalk.red(`🔴 Error: APK file not found: ${apkFile}`));
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
spinner = ora('🔐 Signing APK...').start();
|
|
36
|
+
|
|
37
|
+
await signAPK(apkFile, options);
|
|
38
|
+
|
|
39
|
+
spinner.succeed('APK signed successfully!');
|
|
40
|
+
|
|
41
|
+
} catch (error) {
|
|
42
|
+
if (spinner) spinner.fail('Signing failed');
|
|
43
|
+
console.error(chalk.red('🔴 Error:'), error.message);
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
async function signAPK(apkPath, options) {
|
|
49
|
+
const resolvedApkPath = path.resolve(apkPath);
|
|
50
|
+
let signedApkPath;
|
|
51
|
+
|
|
52
|
+
if (options.output) {
|
|
53
|
+
signedApkPath = path.resolve(options.output);
|
|
54
|
+
} else {
|
|
55
|
+
const parsed = path.parse(apkPath);
|
|
56
|
+
signedApkPath = path.join(parsed.dir, `${parsed.name}-signed${parsed.ext}`);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (options.debug) {
|
|
60
|
+
// Create debug keystore and sign
|
|
61
|
+
await signWithDebugKeystore(resolvedApkPath, signedApkPath);
|
|
62
|
+
} else {
|
|
63
|
+
// Sign with provided keystore
|
|
64
|
+
await signWithKeystore(resolvedApkPath, signedApkPath, options);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
console.log(chalk.green(`✅ Signed APK saved to: ${signedApkPath}`));
|
|
68
|
+
|
|
69
|
+
// Verify signature
|
|
70
|
+
await verifySignature(signedApkPath);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async function signWithDebugKeystore(apkPath, outputPath) {
|
|
74
|
+
console.log(chalk.blue('🔧 Creating debug keystore...'));
|
|
75
|
+
|
|
76
|
+
const debugKeystore = path.join(path.dirname(apkPath), 'debug.keystore');
|
|
77
|
+
|
|
78
|
+
// Create debug keystore if it doesn't exist
|
|
79
|
+
if (!fs.existsSync(debugKeystore)) {
|
|
80
|
+
try {
|
|
81
|
+
await execPromise(`keytool -genkey -v -keystore "${debugKeystore}" -storepass android -alias androiddebugkey -keypass android -keyalg RSA -keysize 2048 -validity 10000 -dname "CN=Android Debug,O=Android,C=US"`);
|
|
82
|
+
console.log(chalk.green('✅ Debug keystore created'));
|
|
83
|
+
} catch (error) {
|
|
84
|
+
throw new Error(`Failed to create debug keystore: ${error.message}`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Sign with debug keystore
|
|
89
|
+
try {
|
|
90
|
+
await execPromise(`jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore "${debugKeystore}" -storepass android -keypass android "${apkPath}" androiddebugkey`);
|
|
91
|
+
|
|
92
|
+
// Copy to output path if different
|
|
93
|
+
if (outputPath !== apkPath) {
|
|
94
|
+
await fs.copy(apkPath, outputPath);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
console.log(chalk.green('✅ APK signed with debug keystore'));
|
|
98
|
+
} catch (error) {
|
|
99
|
+
throw new Error(`Failed to sign APK: ${error.message}`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async function signWithKeystore(apkPath, outputPath, options) {
|
|
104
|
+
if (!options.keystore) {
|
|
105
|
+
throw new Error('Keystore path is required for signing');
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (!fs.existsSync(options.keystore)) {
|
|
109
|
+
throw new Error(`Keystore not found: ${options.keystore}`);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (!options.alias) {
|
|
113
|
+
throw new Error('Keystore alias is required');
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const keystorePath = path.resolve(options.keystore);
|
|
117
|
+
|
|
118
|
+
try {
|
|
119
|
+
let signCommand = `jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore "${keystorePath}"`;
|
|
120
|
+
|
|
121
|
+
if (options.password) {
|
|
122
|
+
signCommand += ` -storepass "${options.password}"`;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
signCommand += ` "${apkPath}" "${options.alias}"`;
|
|
126
|
+
|
|
127
|
+
await execPromise(signCommand);
|
|
128
|
+
|
|
129
|
+
// Copy to output path if different
|
|
130
|
+
if (outputPath !== apkPath) {
|
|
131
|
+
await fs.copy(apkPath, outputPath);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
console.log(chalk.green(`✅ APK signed with keystore: ${options.keystore}`));
|
|
135
|
+
} catch (error) {
|
|
136
|
+
throw new Error(`Failed to sign APK: ${error.message}`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
async function verifySignature(apkPath) {
|
|
141
|
+
try {
|
|
142
|
+
console.log(chalk.blue('🔍 Verifying signature...'));
|
|
143
|
+
|
|
144
|
+
const { stdout } = await execPromise(`jarsigner -verify -verbose "${apkPath}"`);
|
|
145
|
+
|
|
146
|
+
if (stdout.includes('jar verified')) {
|
|
147
|
+
console.log(chalk.green('✅ APK signature verified successfully'));
|
|
148
|
+
} else {
|
|
149
|
+
console.log(chalk.yellow('⚠️ APK signature verification returned warnings'));
|
|
150
|
+
console.log(chalk.gray(stdout));
|
|
151
|
+
}
|
|
152
|
+
} catch (error) {
|
|
153
|
+
console.log(chalk.yellow('⚠️ Could not verify signature:'));
|
|
154
|
+
console.log(chalk.gray(error.message));
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Check if required tools are available
|
|
159
|
+
async function checkSigningTools() {
|
|
160
|
+
try {
|
|
161
|
+
await execPromise('jarsigner -help');
|
|
162
|
+
await execPromise('keytool -help');
|
|
163
|
+
return true;
|
|
164
|
+
} catch (error) {
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
module.exports = signCommand;
|
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
// ────────────[ KAKUZU ]────────────────────────────
|
|
2
|
+
// | Discord : kakuzu_aon
|
|
3
|
+
// | Telegram : kakuzu_aon
|
|
4
|
+
// | Github : kakuzu-aon
|
|
5
|
+
// | File : vulnerability-scan.js
|
|
6
|
+
// | License : MIT License © 2026 Kakuzu
|
|
7
|
+
// | Brief : APK vulnerability scanning 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 VulnerabilityScanner = require('../utils/vulnerability-scanner');
|
|
16
|
+
const ObfuscationDetector = require('../utils/obfuscation-detector');
|
|
17
|
+
|
|
18
|
+
const vulnScanCommand = new Command('vuln-scan')
|
|
19
|
+
.description('Comprehensive vulnerability and security scan')
|
|
20
|
+
.argument('<apk-file>', 'APK file to scan')
|
|
21
|
+
.option('-d, --decode-dir <dir>', 'Use existing decoded directory')
|
|
22
|
+
.option('-o, --output <file>', 'Save scan report to file')
|
|
23
|
+
.option('-f, --format <format>', 'Report format (json, html, csv)', 'json')
|
|
24
|
+
.option('--obfuscation', 'Include obfuscation analysis')
|
|
25
|
+
.option('--deep', 'Perform deep analysis (slower)')
|
|
26
|
+
.option('--severity <level>', 'Minimum severity level (low, medium, high, critical)', 'low')
|
|
27
|
+
.action(async (apkFile, options) => {
|
|
28
|
+
let spinner;
|
|
29
|
+
try {
|
|
30
|
+
if (!fs.existsSync(apkFile)) {
|
|
31
|
+
console.error(chalk.red(`🔴 Error: APK file not found: ${apkFile}`));
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
spinner = ora('🔍 Starting comprehensive security scan...').start();
|
|
36
|
+
|
|
37
|
+
await performVulnerabilityScan(apkFile, options);
|
|
38
|
+
|
|
39
|
+
spinner.succeed('Security scan completed!');
|
|
40
|
+
|
|
41
|
+
} catch (error) {
|
|
42
|
+
if (spinner) spinner.fail('Security scan failed');
|
|
43
|
+
console.error(chalk.red('🔴 Error:'), error.message);
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
async function performVulnerabilityScan(apkPath, options) {
|
|
49
|
+
let decodedPath;
|
|
50
|
+
const tempDir = path.join(path.dirname(apkPath), 'apkz_vuln_' + Date.now());
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
// Use existing decoded directory or decode APK
|
|
54
|
+
if (options.decodeDir && fs.existsSync(options.decodeDir)) {
|
|
55
|
+
decodedPath = path.resolve(options.decodeDir);
|
|
56
|
+
console.log(chalk.blue(`📁 Using existing decoded directory: ${decodedPath}`));
|
|
57
|
+
} else {
|
|
58
|
+
decodedPath = path.join(tempDir, 'decoded');
|
|
59
|
+
await decodeAPK(apkPath, decodedPath);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Initialize scanners
|
|
63
|
+
const vulnScanner = new VulnerabilityScanner();
|
|
64
|
+
const obfDetector = new ObfuscationDetector();
|
|
65
|
+
|
|
66
|
+
// Perform vulnerability scan
|
|
67
|
+
const vulnSpinner = ora('🔍 Scanning for vulnerabilities...').start();
|
|
68
|
+
const vulnResults = await vulnScanner.scanAPK(decodedPath, options);
|
|
69
|
+
vulnSpinner.succeed('Vulnerability scan completed!');
|
|
70
|
+
|
|
71
|
+
// Perform obfuscation analysis if requested
|
|
72
|
+
let obfResults = null;
|
|
73
|
+
if (options.obfuscation) {
|
|
74
|
+
const obfSpinner = ora('🔍 Analyzing code obfuscation...').start();
|
|
75
|
+
obfResults = await obfDetector.analyzeObfuscation(decodedPath);
|
|
76
|
+
obfSpinner.succeed('Obfuscation analysis completed!');
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Display results
|
|
80
|
+
displayScanResults(vulnResults, obfResults, options);
|
|
81
|
+
|
|
82
|
+
// Save report if requested
|
|
83
|
+
if (options.output) {
|
|
84
|
+
const reportData = obfResults ?
|
|
85
|
+
{ vulnerabilities: vulnResults, obfuscation: obfResults } :
|
|
86
|
+
vulnResults;
|
|
87
|
+
|
|
88
|
+
await saveReport(reportData, options.output, options.format);
|
|
89
|
+
console.log(chalk.green(`💾 Report saved to: ${options.output}`));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Show risk assessment
|
|
93
|
+
showRiskAssessment(vulnResults, obfResults);
|
|
94
|
+
|
|
95
|
+
} finally {
|
|
96
|
+
// Cleanup temp directory if we created one
|
|
97
|
+
if (!options.decodeDir) {
|
|
98
|
+
await fs.remove(tempDir);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function displayScanResults(vulnResults, obfResults, options) {
|
|
104
|
+
console.log(chalk.bold('\n🔒 Security Scan Results'));
|
|
105
|
+
console.log(chalk.gray('─'.repeat(50)));
|
|
106
|
+
|
|
107
|
+
// Vulnerability Summary
|
|
108
|
+
console.log(chalk.bold('\n📊 Vulnerability Summary:'));
|
|
109
|
+
const vulnSummary = vulnResults.summary;
|
|
110
|
+
|
|
111
|
+
// Risk score visualization
|
|
112
|
+
const riskScore = vulnSummary.riskScore;
|
|
113
|
+
let riskColor = chalk.green;
|
|
114
|
+
if (riskScore >= 70) riskColor = chalk.red;
|
|
115
|
+
else if (riskScore >= 40) riskColor = chalk.yellow;
|
|
116
|
+
|
|
117
|
+
console.log(`Risk Score: ${riskColor(`${riskScore}/100`)}`);
|
|
118
|
+
|
|
119
|
+
// Severity breakdown
|
|
120
|
+
const severities = ['critical', 'high', 'medium', 'low'];
|
|
121
|
+
severities.forEach(severity => {
|
|
122
|
+
const count = vulnSummary[severity];
|
|
123
|
+
if (count > 0) {
|
|
124
|
+
const color = getSeverityColor(severity);
|
|
125
|
+
console.log(`${color(`${severity.toUpperCase()}: ${count}`)}`);
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
// Top vulnerabilities
|
|
130
|
+
if (vulnSummary.total > 0) {
|
|
131
|
+
console.log(chalk.bold('\n🚨 Top Vulnerabilities:'));
|
|
132
|
+
const allVulns = [
|
|
133
|
+
...vulnResults.vulnerabilities.critical,
|
|
134
|
+
...vulnResults.vulnerabilities.high,
|
|
135
|
+
...vulnResults.vulnerabilities.medium
|
|
136
|
+
].slice(0, 5);
|
|
137
|
+
|
|
138
|
+
allVulns.forEach((vuln, index) => {
|
|
139
|
+
const severity = vuln.severity.toUpperCase();
|
|
140
|
+
const color = getSeverityColor(vuln.severity);
|
|
141
|
+
console.log(`${index + 1}. ${color(`[${severity}]`)} ${vuln.name}`);
|
|
142
|
+
console.log(chalk.gray(` File: ${vuln.file}`));
|
|
143
|
+
console.log(chalk.gray(` CVSS: ${vuln.cvss}`));
|
|
144
|
+
console.log(chalk.gray(` ${vuln.description.substring(0, 100)}${vuln.description.length > 100 ? '...' : ''}`));
|
|
145
|
+
console.log('');
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Obfuscation results
|
|
150
|
+
if (obfResults) {
|
|
151
|
+
console.log(chalk.bold('\n🔍 Obfuscation Analysis:'));
|
|
152
|
+
const obfLevel = obfResults.obfuscationLevel;
|
|
153
|
+
const obfScore = obfResults.overallScore;
|
|
154
|
+
|
|
155
|
+
let obfColor = chalk.green;
|
|
156
|
+
if (obfLevel === 'none') obfColor = chalk.red;
|
|
157
|
+
else if (obfLevel === 'light') obfColor = chalk.yellow;
|
|
158
|
+
else if (obfLevel === 'heavy') obfColor = chalk.green;
|
|
159
|
+
|
|
160
|
+
console.log(`Obfuscation Level: ${obfColor(obfLevel.toUpperCase())}`);
|
|
161
|
+
console.log(`Obfuscation Score: ${obfScore}%`);
|
|
162
|
+
console.log(`Confidence: ${obfResults.confidence}%`);
|
|
163
|
+
|
|
164
|
+
if (obfResults.antiTampering.techniques.length > 0) {
|
|
165
|
+
console.log(chalk.blue('\n🛡️ Anti-Tampering Techniques:'));
|
|
166
|
+
obfResults.antiTampering.techniques.forEach(technique => {
|
|
167
|
+
console.log(chalk.gray(` • ${technique}`));
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Compliance mapping
|
|
173
|
+
if (vulnResults.compliance.owaspTop10.length > 0) {
|
|
174
|
+
console.log(chalk.bold('\n📋 OWASP Top 10 Mapping:'));
|
|
175
|
+
vulnResults.compliance.owaspTop10.forEach(item => {
|
|
176
|
+
console.log(chalk.gray(` • ${item}`));
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function showRiskAssessment(vulnResults, obfResults) {
|
|
182
|
+
console.log(chalk.bold('\n🎯 Risk Assessment:'));
|
|
183
|
+
|
|
184
|
+
const vulnScore = vulnResults.summary.riskScore;
|
|
185
|
+
const obfScore = obfResults ? obfResults.overallScore : 0;
|
|
186
|
+
|
|
187
|
+
// Overall risk assessment
|
|
188
|
+
let overallRisk = 'low';
|
|
189
|
+
let riskColor = chalk.green;
|
|
190
|
+
|
|
191
|
+
if (vulnScore >= 70 || (vulnScore >= 50 && obfScore < 50)) {
|
|
192
|
+
overallRisk = 'critical';
|
|
193
|
+
riskColor = chalk.red;
|
|
194
|
+
} else if (vulnScore >= 40 || (vulnScore >= 20 && obfScore < 70)) {
|
|
195
|
+
overallRisk = 'high';
|
|
196
|
+
riskColor = chalk.red;
|
|
197
|
+
} else if (vulnScore >= 20 || obfScore < 50) {
|
|
198
|
+
overallRisk = 'medium';
|
|
199
|
+
riskColor = chalk.yellow;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
console.log(`Overall Risk: ${riskColor(overallRisk.toUpperCase())}`);
|
|
203
|
+
|
|
204
|
+
// Recommendations summary
|
|
205
|
+
if (vulnResults.recommendations.length > 0) {
|
|
206
|
+
console.log(chalk.bold('\n💡 Key Recommendations:'));
|
|
207
|
+
vulnResults.recommendations.slice(0, 3).forEach(rec => {
|
|
208
|
+
console.log(chalk.blue(`• ${rec.title}`));
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Action items
|
|
213
|
+
console.log(chalk.bold('\n⚡ Immediate Actions Required:'));
|
|
214
|
+
const criticalVulns = vulnResults.vulnerabilities.critical.length + vulnResults.vulnerabilities.high.length;
|
|
215
|
+
|
|
216
|
+
if (criticalVulns > 0) {
|
|
217
|
+
console.log(chalk.red(`🚨 Address ${criticalVulns} critical/high vulnerabilities immediately`));
|
|
218
|
+
} else {
|
|
219
|
+
console.log(chalk.green('✅ No critical vulnerabilities found'));
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (obfResults && obfResults.obfuscationLevel === 'none') {
|
|
223
|
+
console.log(chalk.yellow('⚠️ Implement code obfuscation to protect against reverse engineering'));
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (obfResults && obfResults.antiTampering.techniques.length === 0) {
|
|
227
|
+
console.log(chalk.yellow('⚠️ Add anti-tampering protection for production builds'));
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function getSeverityColor(severity) {
|
|
232
|
+
switch (severity) {
|
|
233
|
+
case 'critical': return chalk.red;
|
|
234
|
+
case 'high': return chalk.red;
|
|
235
|
+
case 'medium': return chalk.yellow;
|
|
236
|
+
case 'low': return chalk.blue;
|
|
237
|
+
default: return chalk.gray;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
async function saveReport(data, outputPath, format) {
|
|
242
|
+
await fs.ensureDir(path.dirname(outputPath));
|
|
243
|
+
|
|
244
|
+
let content;
|
|
245
|
+
switch (format.toLowerCase()) {
|
|
246
|
+
case 'html':
|
|
247
|
+
content = generateHTMLReport(data);
|
|
248
|
+
break;
|
|
249
|
+
case 'csv':
|
|
250
|
+
content = generateCSVReport(data);
|
|
251
|
+
break;
|
|
252
|
+
default:
|
|
253
|
+
content = JSON.stringify(data, null, 2);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
await fs.writeFile(outputPath, content);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function generateHTMLReport(data) {
|
|
260
|
+
const vulnResults = data.vulnerabilities || data;
|
|
261
|
+
const obfResults = data.obfuscation;
|
|
262
|
+
|
|
263
|
+
return `
|
|
264
|
+
<!DOCTYPE html>
|
|
265
|
+
<html>
|
|
266
|
+
<head>
|
|
267
|
+
<title>APKZ Security Report</title>
|
|
268
|
+
<style>
|
|
269
|
+
body { font-family: Arial, sans-serif; margin: 20px; background: #f5f5f5; }
|
|
270
|
+
.container { max-width: 1200px; margin: 0 auto; background: white; padding: 30px; border-radius: 10px; box-shadow: 0 4px 6px rgba(0,0,0,0.1); }
|
|
271
|
+
.header { text-align: center; border-bottom: 2px solid #007bff; padding-bottom: 20px; margin-bottom: 30px; }
|
|
272
|
+
.risk-score { font-size: 48px; font-weight: bold; text-align: center; margin: 20px 0; }
|
|
273
|
+
.critical { color: #dc3545; }
|
|
274
|
+
.high { color: #fd7e14; }
|
|
275
|
+
.medium { color: #ffc107; }
|
|
276
|
+
.low { color: #28a745; }
|
|
277
|
+
.summary { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 20px; margin: 30px 0; }
|
|
278
|
+
.metric { background: #f8f9fa; padding: 20px; border-radius: 8px; text-align: center; }
|
|
279
|
+
.vulnerability { background: white; margin: 15px 0; padding: 20px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); border-left: 5px solid #007bff; }
|
|
280
|
+
.critical { border-left-color: #dc3545; }
|
|
281
|
+
.high { border-left-color: #fd7e14; }
|
|
282
|
+
.medium { border-left-color: #ffc107; }
|
|
283
|
+
.low { border-left-color: #28a745; }
|
|
284
|
+
.recommendation { background: #e7f3ff; padding: 15px; border-radius: 5px; margin: 10px 0; }
|
|
285
|
+
</style>
|
|
286
|
+
</head>
|
|
287
|
+
<body>
|
|
288
|
+
<div class="container">
|
|
289
|
+
<div class="header">
|
|
290
|
+
<h1>🔒 APKZ Security Report</h1>
|
|
291
|
+
<p>Generated: ${new Date().toISOString()}</p>
|
|
292
|
+
</div>
|
|
293
|
+
|
|
294
|
+
<div class="risk-score ${getRiskLevel(vulnResults.summary.riskScore)}">
|
|
295
|
+
Risk Score: ${vulnResults.summary.riskScore}/100
|
|
296
|
+
</div>
|
|
297
|
+
|
|
298
|
+
<div class="summary">
|
|
299
|
+
<div class="metric">
|
|
300
|
+
<h3>Critical</h3>
|
|
301
|
+
<p>${vulnResults.summary.critical}</p>
|
|
302
|
+
</div>
|
|
303
|
+
<div class="metric">
|
|
304
|
+
<h3>High</h3>
|
|
305
|
+
<p>${vulnResults.summary.high}</p>
|
|
306
|
+
</div>
|
|
307
|
+
<div class="metric">
|
|
308
|
+
<h3>Medium</h3>
|
|
309
|
+
<p>${vulnResults.summary.medium}</p>
|
|
310
|
+
</div>
|
|
311
|
+
<div class="metric">
|
|
312
|
+
<h3>Low</h3>
|
|
313
|
+
<p>${vulnResults.summary.low}</p>
|
|
314
|
+
</div>
|
|
315
|
+
</div>
|
|
316
|
+
|
|
317
|
+
<h2>🚨 Vulnerabilities</h2>
|
|
318
|
+
${generateVulnerabilitiesHTML(vulnResults.vulnerabilities)}
|
|
319
|
+
|
|
320
|
+
${obfResults ? `
|
|
321
|
+
<h2>🔍 Obfuscation Analysis</h2>
|
|
322
|
+
<div class="metric">
|
|
323
|
+
<h3>Obfuscation Level</h3>
|
|
324
|
+
<p>${obfResults.obfuscationLevel.toUpperCase()}</p>
|
|
325
|
+
</div>
|
|
326
|
+
<div class="metric">
|
|
327
|
+
<h3>Score</h3>
|
|
328
|
+
<p>${obfResults.overallScore}%</p>
|
|
329
|
+
</div>
|
|
330
|
+
` : ''}
|
|
331
|
+
|
|
332
|
+
<h2>💡 Recommendations</h2>
|
|
333
|
+
${generateRecommendationsHTML(vulnResults.recommendations)}
|
|
334
|
+
</div>
|
|
335
|
+
</body>
|
|
336
|
+
</html>`;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
function getRiskLevel(score) {
|
|
340
|
+
if (score >= 70) return 'critical';
|
|
341
|
+
if (score >= 40) return 'high';
|
|
342
|
+
if (score >= 20) return 'medium';
|
|
343
|
+
return 'low';
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
function generateVulnerabilitiesHTML(vulnerabilities) {
|
|
347
|
+
let html = '';
|
|
348
|
+
|
|
349
|
+
for (const [severity, vulns] of Object.entries(vulnerabilities)) {
|
|
350
|
+
if (vulns.length === 0) continue;
|
|
351
|
+
|
|
352
|
+
for (const vuln of vulns) {
|
|
353
|
+
html += `
|
|
354
|
+
<div class="vulnerability ${severity}">
|
|
355
|
+
<h3>${vuln.name}</h3>
|
|
356
|
+
<p><strong>Severity:</strong> ${severity.toUpperCase()}</p>
|
|
357
|
+
<p><strong>CVSS:</strong> ${vuln.cvss}</p>
|
|
358
|
+
<p><strong>File:</strong> ${vuln.file}</p>
|
|
359
|
+
<p><strong>Description:</strong> ${vuln.description}</p>
|
|
360
|
+
${vuln.evidence ? `<p><strong>Evidence:</strong> <code>${vuln.evidence.join(', ')}</code></p>` : ''}
|
|
361
|
+
</div>`;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
return html;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
function generateRecommendationsHTML(recommendations) {
|
|
369
|
+
let html = '';
|
|
370
|
+
|
|
371
|
+
for (const rec of recommendations) {
|
|
372
|
+
html += `
|
|
373
|
+
<div class="recommendation">
|
|
374
|
+
<h3>${rec.title}</h3>
|
|
375
|
+
<p>${rec.description}</p>
|
|
376
|
+
<ul>
|
|
377
|
+
${rec.actions.map(action => `<li>${action}</li>`).join('')}
|
|
378
|
+
</ul>
|
|
379
|
+
</div>`;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
return html;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
function generateCSVReport(data) {
|
|
386
|
+
const vulnResults = data.vulnerabilities || data;
|
|
387
|
+
let csv = 'Severity,ID,Name,File,CVSS,Description\n';
|
|
388
|
+
|
|
389
|
+
for (const [severity, vulns] of Object.entries(vulnResults.vulnerabilities)) {
|
|
390
|
+
for (const vuln of vulns) {
|
|
391
|
+
csv += `${severity},${vuln.id},"${vuln.name}","${vuln.file}",${vuln.cvss},"${vuln.description}"\n`;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
return csv;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
async function decodeAPK(apkPath, outputDir) {
|
|
399
|
+
const AdmZip = require('adm-zip');
|
|
400
|
+
const zip = new AdmZip(apkPath);
|
|
401
|
+
zip.extractAllTo(outputDir, true);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
module.exports = vulnScanCommand;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
// ────────────[ KAKUZU ]────────────────────────────
|
|
2
|
+
// | Discord : kakuzu_aon
|
|
3
|
+
// | Telegram : kakuzu_aon
|
|
4
|
+
// | Github : kakuzu-aon
|
|
5
|
+
// | File : web.js
|
|
6
|
+
// | License : MIT License © 2026 Kakuzu
|
|
7
|
+
// | Brief : Web interface command
|
|
8
|
+
// ────────────────★─────────────────────────────────
|
|
9
|
+
|
|
10
|
+
const { Command } = require('commander');
|
|
11
|
+
const chalk = require('chalk').default;
|
|
12
|
+
const { default: ora } = require('ora');
|
|
13
|
+
const WebServer = require('../web/web-server');
|
|
14
|
+
|
|
15
|
+
const webCommand = new Command('web')
|
|
16
|
+
.description('Start web interface for APK analysis')
|
|
17
|
+
.option('-p, --port <number>', 'Port for web server', '3000')
|
|
18
|
+
.option('--host <address>', 'Host address', 'localhost')
|
|
19
|
+
.option('--no-open', 'Do not open browser automatically')
|
|
20
|
+
.action(async (options) => {
|
|
21
|
+
try {
|
|
22
|
+
await startWebInterface(options);
|
|
23
|
+
} catch (error) {
|
|
24
|
+
console.error(chalk.red('🔴 Error:'), error.message);
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
async function startWebInterface(options) {
|
|
30
|
+
const port = parseInt(options.port) || 3000;
|
|
31
|
+
const host = options.host || 'localhost';
|
|
32
|
+
|
|
33
|
+
console.log(chalk.blue('🌐 Starting APKZ Web Interface...'));
|
|
34
|
+
|
|
35
|
+
const webServer = new WebServer(port);
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
await webServer.start();
|
|
39
|
+
|
|
40
|
+
// Display startup information
|
|
41
|
+
console.log(chalk.green('\n✅ Web server started successfully!'));
|
|
42
|
+
console.log(chalk.blue(`🌐 Local: http://${host}:${port}`));
|
|
43
|
+
console.log(chalk.blue(`🌐 Network: http://0.0.0.0:${port}`));
|
|
44
|
+
|
|
45
|
+
console.log(chalk.bold('\n📋 Available Features:'));
|
|
46
|
+
console.log(chalk.gray(' • Upload and analyze APK files'));
|
|
47
|
+
console.log(chalk.gray(' • Vulnerability scanning'));
|
|
48
|
+
console.log(chalk.gray(' • Obfuscation analysis'));
|
|
49
|
+
console.log(chalk.gray(' • Network analysis'));
|
|
50
|
+
console.log(chalk.gray(' • Real-time progress tracking'));
|
|
51
|
+
console.log(chalk.gray(' • Download reports (JSON, HTML, CSV)'));
|
|
52
|
+
console.log(chalk.gray(' • Batch processing support'));
|
|
53
|
+
|
|
54
|
+
console.log(chalk.bold('\n🎮 API Endpoints:'));
|
|
55
|
+
console.log(chalk.gray(' • POST /api/upload - Upload APK'));
|
|
56
|
+
console.log(chalk.gray(' • POST /api/analyze - Start analysis'));
|
|
57
|
+
console.log(chalk.gray(' • GET /api/results/:jobId - Get results'));
|
|
58
|
+
console.log(chalk.gray(' • GET /api/jobs - List all jobs'));
|
|
59
|
+
console.log(chalk.gray(' • GET /api/download/:jobId/:format - Download report'));
|
|
60
|
+
|
|
61
|
+
// Open browser if not disabled
|
|
62
|
+
if (options.open !== false) {
|
|
63
|
+
const open = require('open');
|
|
64
|
+
try {
|
|
65
|
+
await open(`http://${host}:${port}`);
|
|
66
|
+
console.log(chalk.green('\n🌍 Browser opened automatically'));
|
|
67
|
+
} catch (error) {
|
|
68
|
+
console.log(chalk.yellow('\n⚠️ Could not open browser automatically'));
|
|
69
|
+
console.log(chalk.blue(` Please open: http://${host}:${port}`));
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
console.log(chalk.bold('\n🛑 Press Ctrl+C to stop the server'));
|
|
74
|
+
|
|
75
|
+
// Handle graceful shutdown
|
|
76
|
+
process.on('SIGINT', async () => {
|
|
77
|
+
console.log(chalk.yellow('\n🛑 Shutting down web server...'));
|
|
78
|
+
await webServer.stop();
|
|
79
|
+
console.log(chalk.green('✅ Web server stopped'));
|
|
80
|
+
process.exit(0);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// Keep process running
|
|
84
|
+
await new Promise(() => {});
|
|
85
|
+
|
|
86
|
+
} catch (error) {
|
|
87
|
+
if (error.code === 'EADDRINUSE') {
|
|
88
|
+
console.error(chalk.red(`🔴 Port ${port} is already in use`));
|
|
89
|
+
console.error(chalk.yellow(`💡 Try a different port: apkz web -p ${port + 1}`));
|
|
90
|
+
} else {
|
|
91
|
+
console.error(chalk.red('🔴 Failed to start web server:'), error.message);
|
|
92
|
+
}
|
|
93
|
+
throw error;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
module.exports = webCommand;
|