(
apktoolYmlUri: vscode.Uri,
)
| 267 | * - Patches main activity's smali to load the gadget |
| 268 | */ |
| 269 | export async function injectGadget( |
| 270 | apktoolYmlUri: vscode.Uri, |
| 271 | ): Promise<void> { |
| 272 | const projectDir = path.dirname(apktoolYmlUri.fsPath); |
| 273 | |
| 274 | // Ask user for gadget .so path |
| 275 | const gadgetFiles = await vscode.window.showOpenDialog({ |
| 276 | canSelectMany: false, |
| 277 | openLabel: "Select Frida Gadget .so", |
| 278 | filters: { "Shared Library": ["so"] }, |
| 279 | }); |
| 280 | if (!gadgetFiles || gadgetFiles.length === 0) return; |
| 281 | |
| 282 | const gadgetSoPath = gadgetFiles[0].fsPath; |
| 283 | |
| 284 | // Detect arch from ELF header, fall back to filename |
| 285 | const detectedArch = detectArch(gadgetSoPath); |
| 286 | const archOptions = ["arm64-v8a", "armeabi-v7a", "x86", "x86_64"]; |
| 287 | if (detectedArch && archOptions.includes(detectedArch)) { |
| 288 | archOptions.splice(archOptions.indexOf(detectedArch), 1); |
| 289 | archOptions.unshift(detectedArch); |
| 290 | } |
| 291 | const selectedArch = await vscode.window.showQuickPick(archOptions, { |
| 292 | placeHolder: detectedArch |
| 293 | ? `Detected: ${detectedArch} — confirm or pick another` |
| 294 | : "Select target architecture", |
| 295 | canPickMany: false, |
| 296 | }); |
| 297 | if (!selectedArch) return; |
| 298 | |
| 299 | // Find main activity |
| 300 | const manifestPath = path.join(projectDir, "AndroidManifest.xml"); |
| 301 | if (!fs.existsSync(manifestPath)) { |
| 302 | vscode.window.showErrorMessage( |
| 303 | "APKLab: AndroidManifest.xml not found in project.", |
| 304 | ); |
| 305 | return; |
| 306 | } |
| 307 | |
| 308 | const mainActivityClass = findMainActivity(manifestPath); |
| 309 | if (!mainActivityClass) { |
| 310 | vscode.window.showErrorMessage( |
| 311 | "APKLab: Could not find main/launcher activity in AndroidManifest.xml.", |
| 312 | ); |
| 313 | return; |
| 314 | } |
| 315 | |
| 316 | outputChannel.appendLine( |
| 317 | `Frida Gadget: Main activity is ${mainActivityClass}`, |
| 318 | ); |
| 319 | |
| 320 | // Generate random library name to avoid detection |
| 321 | const gadgetBaseName = generateGadgetName(); |
| 322 | const libName = `lib${gadgetBaseName}.so`; |
| 323 | |
| 324 | // 1. Copy gadget .so with random name to lib/<arch>/ |
| 325 | const libDir = path.join(projectDir, "lib", selectedArch); |
| 326 | if (!fs.existsSync(libDir)) { |
nothing calls this directly
no test coverage detected