@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,118 @@
1
+ {
2
+ "version": "1.0.0",
3
+ "general": {
4
+ "logLevel": "info",
5
+ "outputDir": "./output",
6
+ "tempDir": "./temp",
7
+ "maxFileSize": 104857600,
8
+ "parallel": 4,
9
+ "timeout": 300000,
10
+ "autoCleanup": true,
11
+ "showBanner": true
12
+ },
13
+ "security": {
14
+ "defaultSeverity": "medium",
15
+ "scanNativeLibs": true,
16
+ "scanResources": true,
17
+ "scanManifest": true,
18
+ "checkKnownVulns": true,
19
+ "generateCVSS": true,
20
+ "complianceChecks": ["owasp-top10", "mobile-top10"],
21
+ "excludePatterns": [
22
+ "schemas.android.com",
23
+ "android:layout",
24
+ "android:drawable",
25
+ "android:color"
26
+ ]
27
+ },
28
+ "web": {
29
+ "port": 3000,
30
+ "host": "localhost",
31
+ "cors": true,
32
+ "maxUploadSize": 104857600,
33
+ "sessionTimeout": 3600000,
34
+ "enableAuth": false,
35
+ "authSecret": "apkz-secret-key",
36
+ "enableWebhook": false,
37
+ "webhookUrl": ""
38
+ },
39
+ "batch": {
40
+ "parallel": 4,
41
+ "recursive": true,
42
+ "includeSubdirs": true,
43
+ "filter": "*.apk",
44
+ "autoAnalyze": true,
45
+ "generateSummary": true,
46
+ "saveIndividualReports": true,
47
+ "cleanupTempFiles": true
48
+ },
49
+ "monitoring": {
50
+ "interval": 5000,
51
+ "autoAnalyze": true,
52
+ "enableWebhook": false,
53
+ "webhookUrl": "",
54
+ "logEvents": true,
55
+ "maxEvents": 1000,
56
+ "eventRetention": 86400000
57
+ },
58
+ "reports": {
59
+ "defaultFormat": "json",
60
+ "includeScreenshots": false,
61
+ "includeStrings": true,
62
+ "includeNetwork": true,
63
+ "includeObfuscation": true,
64
+ "template": "default",
65
+ "customTemplates": {},
66
+ "exportFormats": ["json", "html", "csv"],
67
+ "compression": false,
68
+ "encryption": false
69
+ },
70
+ "modification": {
71
+ "autoBackup": true,
72
+ "backupDir": "./backups",
73
+ "preserveOriginal": true,
74
+ "validateAfterModification": true,
75
+ "autoSign": false,
76
+ "debugKeystore": {
77
+ "path": "./debug.keystore",
78
+ "alias": "androiddebugkey",
79
+ "password": "android",
80
+ "keyPassword": "android"
81
+ }
82
+ },
83
+ "analysis": {
84
+ "deepScan": false,
85
+ "extractStrings": true,
86
+ "analyzeNetwork": true,
87
+ "detectObfuscation": true,
88
+ "scanNativeLibs": true,
89
+ "analyzeResources": true,
90
+ "extractSignatures": true,
91
+ "generateReport": true
92
+ },
93
+ "plugins": {
94
+ "enabled": true,
95
+ "autoLoad": true,
96
+ "pluginDir": "./plugins",
97
+ "maxPlugins": 50,
98
+ "allowRemote": false,
99
+ "trustedSources": []
100
+ },
101
+ "ui": {
102
+ "theme": "default",
103
+ "colors": true,
104
+ "progressBars": true,
105
+ "animations": true,
106
+ "compact": false,
107
+ "showTimestamps": false,
108
+ "showMemoryUsage": false
109
+ },
110
+ "performance": {
111
+ "cacheResults": true,
112
+ "cacheSize": 104857600,
113
+ "compressionLevel": 6,
114
+ "memoryLimit": 1073741824,
115
+ "cpuLimit": 80,
116
+ "enableProfiling": false
117
+ }
118
+ }
@@ -0,0 +1,544 @@
1
+ // ────────────[ KAKUZU ]────────────────────────────
2
+ // | Discord : kakuzu_aon
3
+ // | Telegram : kakuzu_aon
4
+ // | Github : kakuzu-aon
5
+ // | File : icon-manager.js
6
+ // | License : MIT License © 2026 Kakuzu
7
+ // | Brief : APK icon replacement utilities
8
+ // ────────────────★─────────────────────────────────
9
+
10
+ const fs = require('fs-extra');
11
+ const path = require('path');
12
+ const sharp = require('sharp');
13
+
14
+ class IconManager {
15
+ constructor() {
16
+ this.iconSizes = {
17
+ // Android launcher icon sizes
18
+ mdpi: 48,
19
+ hdpi: 72,
20
+ xhdpi: 96,
21
+ xxhdpi: 144,
22
+ xxxhdpi: 192,
23
+ // Adaptive icon sizes
24
+ adaptive_mdpi: 80,
25
+ adaptive_hdpi: 120,
26
+ adaptive_xhdpi: 160,
27
+ adaptive_xxhdpi: 240,
28
+ adaptive_xxxhdpi: 320,
29
+ // Notification icons
30
+ notification_mdpi: 24,
31
+ notification_hdpi: 36,
32
+ notification_xhdpi: 48,
33
+ notification_xxhdpi: 72,
34
+ notification_xxxhdpi: 96
35
+ };
36
+ }
37
+
38
+ /**
39
+ * Replace app icons in decoded APK
40
+ */
41
+ async replaceIcons(decodedPath, iconPath, options = {}) {
42
+ const results = {
43
+ replaced: [],
44
+ errors: [],
45
+ generated: []
46
+ };
47
+
48
+ try {
49
+ // Validate input icon
50
+ if (!fs.existsSync(iconPath)) {
51
+ throw new Error(`Icon file not found: ${iconPath}`);
52
+ }
53
+
54
+ // Check if icon is valid image
55
+ const iconMetadata = await sharp(iconPath).metadata();
56
+ if (!['png', 'jpeg', 'jpg', 'webp'].includes(iconMetadata.format)) {
57
+ throw new Error(`Unsupported icon format: ${iconMetadata.format}`);
58
+ }
59
+
60
+ const resDir = path.join(decodedPath, 'res');
61
+ if (!fs.existsSync(resDir)) {
62
+ throw new Error('Resources directory not found in decoded APK');
63
+ }
64
+
65
+ // Generate different sizes
66
+ const tempDir = path.join(decodedPath, '.temp_icons');
67
+ await fs.ensureDir(tempDir);
68
+
69
+ try {
70
+ // Generate all required sizes
71
+ await this.generateIconSizes(iconPath, tempDir, options);
72
+
73
+ // Replace launcher icons
74
+ await this.replaceLauncherIcons(resDir, tempDir, results);
75
+
76
+ // Replace adaptive icons
77
+ await this.replaceAdaptiveIcons(resDir, tempDir, results);
78
+
79
+ // Replace notification icons if requested
80
+ if (options.includeNotifications) {
81
+ await this.replaceNotificationIcons(resDir, tempDir, results);
82
+ }
83
+
84
+ // Update manifest if requested
85
+ if (options.updateManifest) {
86
+ await this.updateManifestIcons(decodedPath, iconPath, results);
87
+ }
88
+
89
+ } finally {
90
+ // Clean up temp directory
91
+ await fs.remove(tempDir);
92
+ }
93
+
94
+ } catch (error) {
95
+ results.errors.push({
96
+ error: error.message,
97
+ step: 'icon_replacement'
98
+ });
99
+ }
100
+
101
+ return results;
102
+ }
103
+
104
+ /**
105
+ * Generate icon sizes from source image
106
+ */
107
+ async generateIconSizes(sourcePath, outputDir, options = {}) {
108
+ const { format = 'png', quality = 90, adaptive = true } = options;
109
+
110
+ for (const [sizeName, size] of Object.entries(this.iconSizes)) {
111
+ try {
112
+ let outputPath = path.join(outputDir, `${sizeName}.${format}`);
113
+
114
+ const resizeOptions = {
115
+ width: size,
116
+ height: size,
117
+ fit: 'cover',
118
+ position: 'center'
119
+ };
120
+
121
+ // For adaptive icons, add padding
122
+ if (sizeName.startsWith('adaptive_')) {
123
+ const adaptiveSize = parseInt(sizeName.split('_')[1]);
124
+ const canvasSize = this.iconSizes[`adaptive_${sizeName.split('_')[1]}`];
125
+
126
+ await sharp(sourcePath)
127
+ .resize(adaptiveSize * 0.8, adaptiveSize * 0.8, { fit: 'cover', position: 'center' })
128
+ .extend({
129
+ top: adaptiveSize * 0.1,
130
+ bottom: adaptiveSize * 0.1,
131
+ left: adaptiveSize * 0.1,
132
+ right: adaptiveSize * 0.1,
133
+ background: { r: 0, g: 0, b: 0, alpha: 0 }
134
+ })
135
+ .toFile(outputPath);
136
+ } else {
137
+ await sharp(sourcePath)
138
+ .resize(resizeOptions)
139
+ .toFormat(format, { quality })
140
+ .toFile(outputPath);
141
+ }
142
+
143
+ } catch (error) {
144
+ console.warn(`Warning: Could not generate ${sizeName}: ${error.message}`);
145
+ }
146
+ }
147
+ }
148
+
149
+ /**
150
+ * Replace launcher icons
151
+ */
152
+ async replaceLauncherIcons(resDir, tempDir, results) {
153
+ const launcherDirs = [
154
+ 'mipmap-mdpi', 'mipmap-hdpi', 'mipmap-xhdpi', 'mipmap-xxhdpi', 'mipmap-xxxhdpi',
155
+ 'drawable-mdpi', 'drawable-hdpi', 'drawable-xhdpi', 'drawable-xxhdpi', 'drawable-xxxhdpi'
156
+ ];
157
+
158
+ const iconNames = ['ic_launcher.png', 'ic_launcher.webp', 'icon.png', 'icon.webp'];
159
+
160
+ for (const dir of launcherDirs) {
161
+ const dirPath = path.join(resDir, dir);
162
+ if (!fs.existsSync(dirPath)) continue;
163
+
164
+ const density = dir.split('-')[1];
165
+ const sizeName = density;
166
+
167
+ for (const iconName of iconNames) {
168
+ const iconPath = path.join(dirPath, iconName);
169
+ const sourceIcon = path.join(tempDir, `${sizeName}.png`);
170
+
171
+ if (fs.existsSync(iconPath) && fs.existsSync(sourceIcon)) {
172
+ try {
173
+ await fs.copy(sourceIcon, iconPath);
174
+ results.replaced.push({
175
+ type: 'launcher',
176
+ file: path.relative(resDir, iconPath),
177
+ size: this.iconSizes[sizeName]
178
+ });
179
+ } catch (error) {
180
+ results.errors.push({
181
+ error: error.message,
182
+ file: path.relative(resDir, iconPath)
183
+ });
184
+ }
185
+ }
186
+ }
187
+ }
188
+ }
189
+
190
+ /**
191
+ * Replace adaptive icons
192
+ */
193
+ async replaceAdaptiveIcons(resDir, tempDir, results) {
194
+ const adaptiveDirs = [
195
+ 'mipmap-mdpi-v26', 'mipmap-hdpi-v26', 'mipmap-xhdpi-v26',
196
+ 'mipmap-xxhdpi-v26', 'mipmap-xxxhdpi-v26'
197
+ ];
198
+
199
+ for (const dir of adaptiveDirs) {
200
+ const dirPath = path.join(resDir, dir);
201
+ if (!fs.existsSync(dirPath)) continue;
202
+
203
+ const density = dir.split('-')[1];
204
+ const sizeName = `adaptive_${density}`;
205
+
206
+ // Replace foreground icons
207
+ const foregroundIcon = path.join(dirPath, 'ic_launcher_foreground.png');
208
+ const sourceForeground = path.join(tempDir, `${sizeName}.png`);
209
+
210
+ if (fs.existsSync(foregroundIcon) && fs.existsSync(sourceForeground)) {
211
+ try {
212
+ await fs.copy(sourceForeground, foregroundIcon);
213
+ results.replaced.push({
214
+ type: 'adaptive_foreground',
215
+ file: path.relative(resDir, foregroundIcon),
216
+ size: this.iconSizes[sizeName]
217
+ });
218
+ } catch (error) {
219
+ results.errors.push({
220
+ error: error.message,
221
+ file: path.relative(resDir, foregroundIcon)
222
+ });
223
+ }
224
+ }
225
+ }
226
+ }
227
+
228
+ /**
229
+ * Replace notification icons
230
+ */
231
+ async replaceNotificationIcons(resDir, tempDir, results) {
232
+ const notificationDirs = [
233
+ 'drawable-mdpi', 'drawable-hdpi', 'drawable-xhdpi',
234
+ 'drawable-xxhdpi', 'drawable-xxxhdpi'
235
+ ];
236
+
237
+ for (const dir of notificationDirs) {
238
+ const dirPath = path.join(resDir, dir);
239
+ if (!fs.existsSync(dirPath)) continue;
240
+
241
+ const density = dir.split('-')[1];
242
+ const sizeName = `notification_${density}`;
243
+
244
+ const notificationIcon = path.join(dirPath, 'ic_notification.png');
245
+ const sourceIcon = path.join(tempDir, `${sizeName}.png`);
246
+
247
+ if (fs.existsSync(notificationIcon) && fs.existsSync(sourceIcon)) {
248
+ try {
249
+ await fs.copy(sourceIcon, notificationIcon);
250
+ results.replaced.push({
251
+ type: 'notification',
252
+ file: path.relative(resDir, notificationIcon),
253
+ size: this.iconSizes[sizeName]
254
+ });
255
+ } catch (error) {
256
+ results.errors.push({
257
+ error: error.message,
258
+ file: path.relative(resDir, notificationIcon)
259
+ });
260
+ }
261
+ }
262
+ }
263
+ }
264
+
265
+ /**
266
+ * Update manifest to reference new icons
267
+ */
268
+ async updateManifestIcons(decodedPath, iconPath, results) {
269
+ const manifestPath = path.join(decodedPath, 'AndroidManifest.xml');
270
+
271
+ if (!fs.existsSync(manifestPath)) {
272
+ return;
273
+ }
274
+
275
+ try {
276
+ let manifestContent = await fs.readFile(manifestPath, 'utf8');
277
+
278
+ // Update application icon if needed
279
+ if (manifestContent.includes('android:icon')) {
280
+ // This is a simplified approach - in reality, manifest might be binary
281
+ console.log('Manifest icon references detected - manual update may be required');
282
+ results.replaced.push({
283
+ type: 'manifest_note',
284
+ file: 'AndroidManifest.xml',
285
+ message: 'Manual icon reference update may be needed'
286
+ });
287
+ }
288
+
289
+ } catch (error) {
290
+ results.errors.push({
291
+ error: error.message,
292
+ file: 'AndroidManifest.xml'
293
+ });
294
+ }
295
+ }
296
+
297
+ /**
298
+ * Extract existing icons from APK
299
+ */
300
+ async extractIcons(decodedPath, outputDir) {
301
+ const results = {
302
+ extracted: [],
303
+ errors: []
304
+ };
305
+
306
+ const resDir = path.join(decodedPath, 'res');
307
+ if (!fs.existsSync(resDir)) {
308
+ results.errors.push('Resources directory not found');
309
+ return results;
310
+ }
311
+
312
+ await fs.ensureDir(outputDir);
313
+
314
+ // Find all icon files
315
+ const iconPatterns = [
316
+ '**/ic_launcher.*',
317
+ '**/ic_launcher_foreground.*',
318
+ '**/ic_launcher_background.*',
319
+ '**/icon.*',
320
+ '**/ic_notification.*'
321
+ ];
322
+
323
+ for (const pattern of iconPatterns) {
324
+ try {
325
+ const files = await this.findFiles(resDir, pattern);
326
+
327
+ for (const file of files) {
328
+ const relativePath = path.relative(resDir, file);
329
+ const outputPath = path.join(outputDir, relativePath.replace(/[\/\\]/g, '_'));
330
+
331
+ await fs.ensureDir(path.dirname(outputPath));
332
+ await fs.copy(file, outputPath);
333
+
334
+ results.extracted.push({
335
+ source: relativePath,
336
+ output: path.basename(outputPath)
337
+ });
338
+ }
339
+ } catch (error) {
340
+ results.errors.push({
341
+ error: error.message,
342
+ pattern
343
+ });
344
+ }
345
+ }
346
+
347
+ return results;
348
+ }
349
+
350
+ /**
351
+ * Create adaptive icon from single image
352
+ */
353
+ async createAdaptiveIcon(iconPath, outputPath, options = {}) {
354
+ const {
355
+ backgroundColor = '#FFFFFF',
356
+ size = 108,
357
+ padding = 0.2
358
+ } = options;
359
+
360
+ try {
361
+ const iconSize = Math.floor(size * (1 - padding));
362
+
363
+ await sharp(iconPath)
364
+ .resize(iconSize, iconSize, { fit: 'cover', position: 'center' })
365
+ .extend({
366
+ top: size * padding / 2,
367
+ bottom: size * padding / 2,
368
+ left: size * padding / 2,
369
+ right: size * padding / 2,
370
+ background: backgroundColor
371
+ })
372
+ .toFile(outputPath);
373
+
374
+ return { success: true, outputPath };
375
+
376
+ } catch (error) {
377
+ return { success: false, error: error.message };
378
+ }
379
+ }
380
+
381
+ /**
382
+ * Validate icon file
383
+ */
384
+ async validateIcon(iconPath) {
385
+ try {
386
+ if (!fs.existsSync(iconPath)) {
387
+ return { valid: false, error: 'File not found' };
388
+ }
389
+
390
+ const metadata = await sharp(iconPath).metadata();
391
+
392
+ if (!['png', 'jpeg', 'jpg', 'webp'].includes(metadata.format)) {
393
+ return { valid: false, error: `Unsupported format: ${metadata.format}` };
394
+ }
395
+
396
+ if (metadata.width < 48 || metadata.height < 48) {
397
+ return { valid: false, error: 'Icon too small (minimum 48x48)' };
398
+ }
399
+
400
+ if (metadata.width > 2048 || metadata.height > 2048) {
401
+ return { valid: false, error: 'Icon too large (maximum 2048x2048)' };
402
+ }
403
+
404
+ if (Math.abs(metadata.width - metadata.height) > metadata.width * 0.1) {
405
+ return { valid: false, error: 'Icon should be roughly square' };
406
+ }
407
+
408
+ return {
409
+ valid: true,
410
+ metadata: {
411
+ format: metadata.format,
412
+ width: metadata.width,
413
+ height: metadata.height,
414
+ size: metadata.size
415
+ }
416
+ };
417
+
418
+ } catch (error) {
419
+ return { valid: false, error: error.message };
420
+ }
421
+ }
422
+
423
+ /**
424
+ * Find files matching pattern
425
+ */
426
+ async findFiles(dir, pattern) {
427
+ const files = [];
428
+ const items = await fs.readdir(dir);
429
+
430
+ for (const item of items) {
431
+ const fullPath = path.join(dir, item);
432
+ const stat = await fs.stat(fullPath);
433
+
434
+ if (stat.isDirectory()) {
435
+ const subFiles = await this.findFiles(fullPath, pattern);
436
+ files.push(...subFiles);
437
+ } else {
438
+ // Simple pattern matching
439
+ if (this.matchesPattern(item, pattern)) {
440
+ files.push(fullPath);
441
+ }
442
+ }
443
+ }
444
+
445
+ return files;
446
+ }
447
+
448
+ /**
449
+ * Simple pattern matching
450
+ */
451
+ matchesPattern(filename, pattern) {
452
+ const regex = new RegExp(
453
+ pattern.replace(/\*\*/g, '.*').replace(/\*/g, '[^/]*').replace(/\?/g, '[^/]'),
454
+ 'i'
455
+ );
456
+ return regex.test(filename);
457
+ }
458
+
459
+ /**
460
+ * Get icon information from decoded APK
461
+ */
462
+ async getIconInfo(decodedPath) {
463
+ const info = {
464
+ launcherIcons: [],
465
+ adaptiveIcons: [],
466
+ notificationIcons: [],
467
+ totalIcons: 0
468
+ };
469
+
470
+ const resDir = path.join(decodedPath, 'res');
471
+ if (!fs.existsSync(resDir)) {
472
+ return info;
473
+ }
474
+
475
+ // Scan for icon files
476
+ const items = await fs.readdir(resDir, { withFileTypes: true });
477
+
478
+ for (const item of items) {
479
+ if (!item.isDirectory()) continue;
480
+
481
+ const dirPath = path.join(resDir, item.name);
482
+ const files = await fs.readdir(dirPath);
483
+
484
+ for (const file of files) {
485
+ if (this.isIconFile(file)) {
486
+ const filePath = path.join(dirPath, file);
487
+ const stat = await fs.stat(filePath);
488
+
489
+ const iconInfo = {
490
+ path: path.relative(resDir, filePath),
491
+ size: stat.size,
492
+ type: this.getIconType(file),
493
+ density: this.getDensityFromDir(item.name)
494
+ };
495
+
496
+ if (iconInfo.type === 'launcher') {
497
+ info.launcherIcons.push(iconInfo);
498
+ } else if (iconInfo.type === 'adaptive') {
499
+ info.adaptiveIcons.push(iconInfo);
500
+ } else if (iconInfo.type === 'notification') {
501
+ info.notificationIcons.push(iconInfo);
502
+ }
503
+
504
+ info.totalIcons++;
505
+ }
506
+ }
507
+ }
508
+
509
+ return info;
510
+ }
511
+
512
+ /**
513
+ * Check if file is an icon
514
+ */
515
+ isIconFile(filename) {
516
+ const iconNames = [
517
+ 'ic_launcher', 'ic_launcher_foreground', 'ic_launcher_background',
518
+ 'icon', 'ic_notification', 'ic_stat_notify'
519
+ ];
520
+
521
+ return iconNames.some(name => filename.toLowerCase().startsWith(name.toLowerCase()));
522
+ }
523
+
524
+ /**
525
+ * Get icon type from filename
526
+ */
527
+ getIconType(filename) {
528
+ if (filename.includes('launcher_foreground')) return 'adaptive';
529
+ if (filename.includes('launcher_background')) return 'adaptive';
530
+ if (filename.includes('launcher') || filename.includes('icon')) return 'launcher';
531
+ if (filename.includes('notification') || filename.includes('ic_stat_')) return 'notification';
532
+ return 'unknown';
533
+ }
534
+
535
+ /**
536
+ * Get density from directory name
537
+ */
538
+ getDensityFromDir(dirname) {
539
+ const densities = ['mdpi', 'hdpi', 'xhdpi', 'xxhdpi', 'xxxhdpi', 'nodpi', 'anydpi'];
540
+ return densities.find(d => dirname.includes(d)) || 'unknown';
541
+ }
542
+ }
543
+
544
+ module.exports = IconManager;