reverse-skills-max: initial public release (restored, redacted, hardened)
This commit is contained in:
158
reverse-skill-repo/skills/apk-reverse/scripts/decode.ps1
Normal file
158
reverse-skill-repo/skills/apk-reverse/scripts/decode.ps1
Normal file
@@ -0,0 +1,158 @@
|
||||
#requires -Version 5
|
||||
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$ApkPath,
|
||||
|
||||
[string]$Name,
|
||||
|
||||
[string]$OutRoot,
|
||||
|
||||
[switch]$SkipJadx,
|
||||
|
||||
[switch]$SkipApktool,
|
||||
|
||||
[switch]$Clean
|
||||
)
|
||||
|
||||
Set-StrictMode -Version Latest
|
||||
$ErrorActionPreference = 'Stop'
|
||||
[Console]::InputEncoding = [System.Text.UTF8Encoding]::new($false)
|
||||
[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new($false)
|
||||
$OutputEncoding = [System.Text.UTF8Encoding]::new($false)
|
||||
|
||||
. (Join-Path $PSScriptRoot '..\..\scripts\lib\ToolDiscovery.ps1')
|
||||
|
||||
function Get-SafeName {
|
||||
param([Parameter(Mandatory = $true)][string]$PathValue)
|
||||
|
||||
$raw = [System.IO.Path]::GetFileNameWithoutExtension($PathValue)
|
||||
if ([string]::IsNullOrWhiteSpace($raw)) {
|
||||
$raw = 'apk'
|
||||
}
|
||||
return ($raw -replace '[^A-Za-z0-9._-]', '_')
|
||||
}
|
||||
|
||||
function Get-DefaultOutRoot {
|
||||
param([Parameter(Mandatory = $true)][string]$ApkFilePath)
|
||||
|
||||
$parent = Split-Path -Path $ApkFilePath -Parent
|
||||
if ([string]::IsNullOrWhiteSpace($parent)) {
|
||||
return [System.IO.Directory]::GetCurrentDirectory()
|
||||
}
|
||||
return $parent
|
||||
}
|
||||
|
||||
function Get-ManifestPackage {
|
||||
param([Parameter(Mandatory = $true)][string]$ManifestPath)
|
||||
|
||||
if (-not (Test-Path -LiteralPath $ManifestPath)) {
|
||||
return ''
|
||||
}
|
||||
|
||||
try {
|
||||
$xml = [xml](Get-Content -LiteralPath $ManifestPath -Raw -Encoding UTF8)
|
||||
return [string]$xml.manifest.package
|
||||
}
|
||||
catch {
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
if (-not (Test-Path -LiteralPath $ApkPath)) {
|
||||
throw "APK not found: $ApkPath"
|
||||
}
|
||||
|
||||
$jadxSpec = $null
|
||||
$apktoolSpec = $null
|
||||
$bootstrapScript = Join-Path $PSScriptRoot '..\..\scripts\bootstrap-reverse.ps1'
|
||||
|
||||
if (-not $SkipJadx) {
|
||||
$jadxSpec = Resolve-ReverseToolSpec -Name 'jadx'
|
||||
if (-not $jadxSpec.Available) {
|
||||
Write-Host 'INFO: jadx not found, attempting auto-bootstrap...' -ForegroundColor Yellow
|
||||
& powershell.exe -NoProfile -ExecutionPolicy Bypass -File $bootstrapScript -Capability @('jadx') -SkipRefresh
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw 'Bootstrap failed for jadx. Please install manually: https://github.com/skylot/jadx'
|
||||
}
|
||||
$jadxSpec = Resolve-ReverseToolSpec -Name 'jadx'
|
||||
if (-not $jadxSpec.Available) {
|
||||
throw 'jadx still not available after bootstrap. Check installation at %USERPROFILE%\Tools\jadx\'
|
||||
}
|
||||
Write-Host 'INFO: jadx bootstrapped successfully.' -ForegroundColor Green
|
||||
}
|
||||
}
|
||||
if (-not $SkipApktool) {
|
||||
$apktoolSpec = Resolve-ReverseToolSpec -Name 'apktool'
|
||||
if (-not $apktoolSpec.Available) {
|
||||
Write-Host 'INFO: apktool not found, attempting auto-bootstrap...' -ForegroundColor Yellow
|
||||
& powershell.exe -NoProfile -ExecutionPolicy Bypass -File $bootstrapScript -Capability @('apktool') -SkipRefresh
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw 'Bootstrap failed for apktool. Please install manually: https://apktool.org/'
|
||||
}
|
||||
$apktoolSpec = Resolve-ReverseToolSpec -Name 'apktool'
|
||||
if (-not $apktoolSpec.Available) {
|
||||
throw 'apktool still not available after bootstrap. Check installation at %USERPROFILE%\Tools\apktool\'
|
||||
}
|
||||
Write-Host 'INFO: apktool bootstrapped successfully.' -ForegroundColor Green
|
||||
}
|
||||
}
|
||||
|
||||
$taskName = if ($Name) { $Name } else { Get-SafeName -PathValue $ApkPath }
|
||||
$resolvedOutRoot = if ([string]::IsNullOrWhiteSpace($OutRoot)) { Get-DefaultOutRoot -ApkFilePath $ApkPath } else { $OutRoot }
|
||||
$taskRoot = Join-Path $resolvedOutRoot $taskName
|
||||
$jadxOut = Join-Path $taskRoot 'jadx'
|
||||
$apktoolOut = Join-Path $taskRoot 'apktool'
|
||||
|
||||
if (-not (Test-Path -LiteralPath $resolvedOutRoot)) {
|
||||
New-Item -ItemType Directory -Path $resolvedOutRoot -Force | Out-Null
|
||||
}
|
||||
|
||||
if ($Clean -and (Test-Path -LiteralPath $taskRoot)) {
|
||||
Remove-Item -LiteralPath $taskRoot -Recurse -Force
|
||||
}
|
||||
|
||||
if (-not (Test-Path -LiteralPath $taskRoot)) {
|
||||
New-Item -ItemType Directory -Path $taskRoot -Force | Out-Null
|
||||
}
|
||||
|
||||
$jadxExitCode = $null
|
||||
if (-not $SkipJadx) {
|
||||
if (Test-Path -LiteralPath $jadxOut) {
|
||||
Remove-Item -LiteralPath $jadxOut -Recurse -Force
|
||||
}
|
||||
& $jadxSpec.Command @($jadxSpec.PrefixArgs + @('-d', $jadxOut, $ApkPath))
|
||||
$jadxExitCode = $LASTEXITCODE
|
||||
}
|
||||
|
||||
$apktoolExitCode = $null
|
||||
if (-not $SkipApktool) {
|
||||
if (Test-Path -LiteralPath $apktoolOut) {
|
||||
Remove-Item -LiteralPath $apktoolOut -Recurse -Force
|
||||
}
|
||||
& $apktoolSpec.Command @($apktoolSpec.PrefixArgs + @('d', $ApkPath, '-o', $apktoolOut, '-f'))
|
||||
$apktoolExitCode = $LASTEXITCODE
|
||||
}
|
||||
|
||||
$manifestPath = Join-Path $apktoolOut 'AndroidManifest.xml'
|
||||
$packageName = if (-not $SkipApktool) { Get-ManifestPackage -ManifestPath $manifestPath } else { '' }
|
||||
$javaCount = if ((Test-Path -LiteralPath $jadxOut)) { (Get-ChildItem -LiteralPath $jadxOut -Recurse -File -Filter '*.java' | Measure-Object).Count } else { 0 }
|
||||
$smaliDirCount = if ((Test-Path -LiteralPath $apktoolOut)) { (Get-ChildItem -LiteralPath $apktoolOut -Directory -Filter 'smali*' | Measure-Object).Count } else { 0 }
|
||||
$libCount = if ((Test-Path -LiteralPath $apktoolOut)) { (Get-ChildItem -LiteralPath $apktoolOut -Recurse -File -Filter '*.so' | Measure-Object).Count } else { 0 }
|
||||
$resXmlCount = if ((Test-Path -LiteralPath $apktoolOut)) { (Get-ChildItem -LiteralPath $apktoolOut -Recurse -File -Filter '*.xml' | Measure-Object).Count } else { 0 }
|
||||
|
||||
"task_root=$taskRoot"
|
||||
"jadx_out=$jadxOut"
|
||||
"apktool_out=$apktoolOut"
|
||||
"package=$packageName"
|
||||
"jadx_exit_code=$jadxExitCode"
|
||||
"apktool_exit_code=$apktoolExitCode"
|
||||
"java_files=$javaCount"
|
||||
"smali_dirs=$smaliDirCount"
|
||||
"so_files=$libCount"
|
||||
"xml_files=$resXmlCount"
|
||||
|
||||
if (($jadxExitCode -ne $null) -and ($jadxExitCode -ne 0)) {
|
||||
"warning=jadx returned non-zero exit code; inspect output but treat exported sources as usable if present"
|
||||
}
|
||||
127
reverse-skill-repo/skills/apk-reverse/scripts/decode.sh
Normal file
127
reverse-skill-repo/skills/apk-reverse/scripts/decode.sh
Normal file
@@ -0,0 +1,127 @@
|
||||
#!/usr/bin/env bash
|
||||
# decode.sh — APK 解包(jadx 反编译 + apktool 解包)
|
||||
# 等价于 Windows 版的 decode.ps1
|
||||
#
|
||||
# 用法:
|
||||
# bash decode.sh <apk_path> [--name <task_name>] [--out <output_dir>]
|
||||
# [--skip-jadx] [--skip-apktool] [--clean]
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
KALI_BOOTSTRAP="$(cd "$SCRIPT_DIR/../../../kali/scripts" 2>/dev/null && pwd)/bootstrap-reverse.sh"
|
||||
|
||||
# ─── 参数解析 ──────────────────────────────────────────────────────────────────────
|
||||
|
||||
APK_PATH=""
|
||||
TASK_NAME=""
|
||||
OUT_ROOT=""
|
||||
SKIP_JADX=false
|
||||
SKIP_APKTOOL=false
|
||||
CLEAN=false
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--name) TASK_NAME="$2"; shift 2 ;;
|
||||
--out) OUT_ROOT="$2"; shift 2 ;;
|
||||
--skip-jadx) SKIP_JADX=true; shift ;;
|
||||
--skip-apktool) SKIP_APKTOOL=true; shift ;;
|
||||
--clean) CLEAN=true; shift ;;
|
||||
-*) echo "未知选项: $1"; exit 1 ;;
|
||||
*) APK_PATH="$1"; shift ;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ -z "$APK_PATH" ]]; then
|
||||
echo "用法: $0 <apk_path> [--name <name>] [--out <dir>] [--skip-jadx] [--skip-apktool] [--clean]"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -f "$APK_PATH" ]]; then
|
||||
echo "ERR: APK 文件不存在: $APK_PATH"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ─── 工具检测与自动安装 ─────────────────────────────────────────────────────────────
|
||||
|
||||
ensure_tool() {
|
||||
local name="$1"
|
||||
if command -v "$name" &>/dev/null; then
|
||||
return 0
|
||||
fi
|
||||
echo "INFO: $name 未找到,尝试自动安装..."
|
||||
if [[ -x "$KALI_BOOTSTRAP" ]]; then
|
||||
bash "$KALI_BOOTSTRAP" "$name" --skip-refresh 2>/dev/null || true
|
||||
fi
|
||||
if ! command -v "$name" &>/dev/null; then
|
||||
echo "ERR: $name 安装失败,请手动安装"
|
||||
return 1
|
||||
fi
|
||||
echo "INFO: $name 安装成功"
|
||||
}
|
||||
|
||||
[[ "$SKIP_JADX" != "true" ]] && ensure_tool "jadx"
|
||||
[[ "$SKIP_APKTOOL" != "true" ]] && ensure_tool "apktool"
|
||||
|
||||
# ─── 路径计算 ──────────────────────────────────────────────────────────────────────
|
||||
|
||||
APK_BASENAME=$(basename "$APK_PATH" .apk | sed 's/[^A-Za-z0-9._-]/_/g')
|
||||
TASK_NAME="${TASK_NAME:-$APK_BASENAME}"
|
||||
OUT_ROOT="${OUT_ROOT:-$(dirname "$APK_PATH")}"
|
||||
TASK_ROOT="$OUT_root:REDACTED"
|
||||
JADX_OUT="$TASK_ROOT/jadx"
|
||||
APKTOOL_OUT="$TASK_ROOT/apktool"
|
||||
|
||||
if [[ "$CLEAN" == "true" && -d "$TASK_ROOT" ]]; then
|
||||
rm -rf "$TASK_ROOT"
|
||||
fi
|
||||
|
||||
mkdir -p "$TASK_ROOT"
|
||||
|
||||
# ─── jadx 反编译 ──────────────────────────────────────────────────────────────────
|
||||
|
||||
JADX_EXIT=0
|
||||
if [[ "$SKIP_JADX" != "true" ]]; then
|
||||
rm -rf "$JADX_OUT"
|
||||
echo "=== jadx 反编译 ==="
|
||||
jadx -d "$JADX_OUT" "$APK_PATH" || JADX_EXIT=$?
|
||||
fi
|
||||
|
||||
# ─── apktool 解包 ─────────────────────────────────────────────────────────────────
|
||||
|
||||
APKTOOL_EXIT=0
|
||||
if [[ "$SKIP_APKTOOL" != "true" ]]; then
|
||||
rm -rf "$APKTOOL_OUT"
|
||||
echo "=== apktool 解包 ==="
|
||||
apktool d "$APK_PATH" -o "$APKTOOL_OUT" -f || APKTOOL_EXIT=$?
|
||||
fi
|
||||
|
||||
# ─── 统计输出 ──────────────────────────────────────────────────────────────────────
|
||||
|
||||
PACKAGE=""
|
||||
if [[ -f "$APKTOOL_OUT/AndroidManifest.xml" ]]; then
|
||||
PACKAGE=$(grep -oP 'package="[^"]*"' "$APKTOOL_OUT/AndroidManifest.xml" 2>/dev/null | head -1 | sed 's/package="//;s/"//')
|
||||
fi
|
||||
|
||||
JAVA_COUNT=0
|
||||
[[ -d "$JADX_OUT" ]] && JAVA_COUNT=$(find "$JADX_OUT" -name "*.java" | wc -l)
|
||||
|
||||
SMALI_DIRS=0
|
||||
[[ -d "$APKTOOL_OUT" ]] && SMALI_DIRS=$(find "$APKTOOL_OUT" -maxdepth 1 -type d -name "smali*" | wc -l)
|
||||
|
||||
SO_COUNT=0
|
||||
[[ -d "$APKTOOL_OUT" ]] && SO_COUNT=$(find "$APKTOOL_OUT" -name "*.so" | wc -l)
|
||||
|
||||
echo ""
|
||||
echo "═══════════════════════════════════════════"
|
||||
echo " APK 解包完成"
|
||||
echo "═══════════════════════════════════════════"
|
||||
echo " task_root=$TASK_ROOT"
|
||||
echo " jadx_out=$JADX_OUT"
|
||||
echo " apktool_out=$APKTOOL_OUT"
|
||||
echo " package=$PACKAGE"
|
||||
echo " jadx_exit_code=$JADX_EXIT"
|
||||
echo " apktool_exit_code=$APKTOOL_EXIT"
|
||||
echo " java_files=$JAVA_COUNT"
|
||||
echo " smali_dirs=$SMALI_DIRS"
|
||||
echo " so_files=$SO_COUNT"
|
||||
146
reverse-skill-repo/skills/apk-reverse/scripts/frida-run.ps1
Normal file
146
reverse-skill-repo/skills/apk-reverse/scripts/frida-run.ps1
Normal file
@@ -0,0 +1,146 @@
|
||||
#requires -Version 5
|
||||
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[string]$Package,
|
||||
|
||||
[string]$Process,
|
||||
|
||||
[string]$RemoteHost = '127.0.0.1:27042',
|
||||
|
||||
[string]$ScriptPath,
|
||||
|
||||
[switch]$Usb,
|
||||
|
||||
[switch]$Spawn,
|
||||
|
||||
[switch]$Pause,
|
||||
|
||||
[switch]$ListDevices,
|
||||
|
||||
[switch]$ListProcesses
|
||||
)
|
||||
|
||||
Set-StrictMode -Version Latest
|
||||
$ErrorActionPreference = 'Stop'
|
||||
[Console]::InputEncoding = [System.Text.UTF8Encoding]::new($false)
|
||||
[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new($false)
|
||||
$OutputEncoding = [System.Text.UTF8Encoding]::new($false)
|
||||
|
||||
function Get-PythonScriptCandidates {
|
||||
param([Parameter(Mandatory = $true)][string]$ExecutableName)
|
||||
|
||||
$roots = @(
|
||||
(Join-Path $env:APPDATA 'Python'),
|
||||
(Join-Path $env:LOCALAPPDATA 'Programs\Python')
|
||||
)
|
||||
|
||||
$candidates = New-Object System.Collections.Generic.List[string]
|
||||
foreach ($root in $roots) {
|
||||
if (-not (Test-Path -LiteralPath $root)) {
|
||||
continue
|
||||
}
|
||||
|
||||
$matches = Get-ChildItem -LiteralPath $root -Directory -ErrorAction SilentlyContinue
|
||||
foreach ($dir in $matches) {
|
||||
$candidate = Join-Path $dir.FullName (Join-Path 'Scripts' $ExecutableName)
|
||||
if (Test-Path -LiteralPath $candidate) {
|
||||
$candidates.Add($candidate)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $candidates
|
||||
}
|
||||
|
||||
function Get-ToolPath {
|
||||
param([Parameter(Mandatory = $true)][string]$Name)
|
||||
|
||||
$cmd = Get-Command $Name -ErrorAction SilentlyContinue
|
||||
if ($cmd) {
|
||||
return $cmd.Source
|
||||
}
|
||||
|
||||
foreach ($candidate in Get-PythonScriptCandidates -ExecutableName ($Name + '.exe')) {
|
||||
if (Test-Path -LiteralPath $candidate) {
|
||||
return $candidate
|
||||
}
|
||||
}
|
||||
|
||||
# Attempt auto-bootstrap for frida tools
|
||||
$bootstrapScript = Join-Path $PSScriptRoot '..\..\scripts\bootstrap-reverse.ps1'
|
||||
if (($Name -in @('frida', 'frida-ps', 'frida-ls-devices')) -and (Test-Path -LiteralPath $bootstrapScript)) {
|
||||
Write-Host "INFO: $Name not found, attempting auto-bootstrap (pip install frida-tools)..." -ForegroundColor Yellow
|
||||
& powershell.exe -NoProfile -ExecutionPolicy Bypass -File $bootstrapScript -Capability @('frida') -SkipRefresh
|
||||
$cmd = Get-Command $Name -ErrorAction SilentlyContinue
|
||||
if ($cmd) {
|
||||
Write-Host "INFO: $Name bootstrapped successfully." -ForegroundColor Green
|
||||
return $cmd.Source
|
||||
}
|
||||
foreach ($candidate in Get-PythonScriptCandidates -ExecutableName ($Name + '.exe')) {
|
||||
if (Test-Path -LiteralPath $candidate) {
|
||||
return $candidate
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw "Missing required CLI tool: $Name — install with: pip install frida-tools"
|
||||
}
|
||||
|
||||
$fridaLsDevices = Get-ToolPath -Name 'frida-ls-devices'
|
||||
$fridaPs = Get-ToolPath -Name 'frida-ps'
|
||||
$frida = Get-ToolPath -Name 'frida'
|
||||
$python = Get-Command python -ErrorAction SilentlyContinue
|
||||
|
||||
if (-not $python) {
|
||||
throw 'Missing required CLI tool: python'
|
||||
}
|
||||
|
||||
$pythonExe = $python.Source
|
||||
|
||||
if ($ListDevices) {
|
||||
& $pythonExe -c "import frida; [print(f'{d.id}`t{d.type}`t{d.name}') for d in frida.enumerate_devices()]"
|
||||
exit $LASTEXITCODE
|
||||
}
|
||||
|
||||
$target = if ($Package) { $Package } elseif ($Process) { $Process } else { '' }
|
||||
if ([string]::IsNullOrWhiteSpace($target) -and -not $ListProcesses) {
|
||||
throw 'Provide -Package or -Process, or use -ListProcesses.'
|
||||
}
|
||||
|
||||
$deviceFlag = if ($Usb) { '-U' } else { '-H' }
|
||||
|
||||
if ($ListProcesses) {
|
||||
$processArgs = @()
|
||||
if ($Usb) {
|
||||
$processArgs += '-U'
|
||||
}
|
||||
else {
|
||||
$processArgs += @('-H', $RemoteHost)
|
||||
}
|
||||
& $fridaPs @processArgs
|
||||
exit $LASTEXITCODE
|
||||
}
|
||||
|
||||
if (-not (Test-Path -LiteralPath $ScriptPath)) {
|
||||
throw "Frida script not found: $ScriptPath"
|
||||
}
|
||||
|
||||
$fridaArgs = @($deviceFlag)
|
||||
if (-not $Usb) {
|
||||
$fridaArgs += $RemoteHost
|
||||
}
|
||||
if ($Spawn) {
|
||||
$fridaArgs += '-f'
|
||||
} else {
|
||||
$fridaArgs += '-n'
|
||||
}
|
||||
$fridaArgs += $target
|
||||
$fridaArgs += '-l'
|
||||
$fridaArgs += $ScriptPath
|
||||
if (-not $Pause) {
|
||||
$fridaArgs += '--no-pause'
|
||||
}
|
||||
|
||||
& $frida @fridaArgs
|
||||
exit $LASTEXITCODE
|
||||
126
reverse-skill-repo/skills/apk-reverse/scripts/frida-run.sh
Normal file
126
reverse-skill-repo/skills/apk-reverse/scripts/frida-run.sh
Normal file
@@ -0,0 +1,126 @@
|
||||
#!/usr/bin/env bash
|
||||
# frida-run.sh — Frida 动态注入脚本
|
||||
# 等价于 Windows 版的 frida-run.ps1
|
||||
#
|
||||
# 用法:
|
||||
# bash frida-run.sh --package <pkg> --script <js> [--usb] [--spawn]
|
||||
# bash frida-run.sh --list-devices
|
||||
# bash frida-run.sh --list-processes [--usb]
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
KALI_BOOTSTRAP="$(cd "$SCRIPT_DIR/../../../kali/scripts" 2>/dev/null && pwd)/bootstrap-reverse.sh"
|
||||
|
||||
# ─── 参数 ──────────────────────────────────────────────────────────────────────────
|
||||
|
||||
PACKAGE=""
|
||||
PROCESS=""
|
||||
REMOTE_HOST="127.0.0.1:27042"
|
||||
SCRIPT_PATH=""
|
||||
USB=false
|
||||
SPAWN=false
|
||||
PAUSE=false
|
||||
LIST_DEVICES=false
|
||||
LIST_PROCESSES=false
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--package|-p) PACKAGE="$2"; shift 2 ;;
|
||||
--process) PROCESS="$2"; shift 2 ;;
|
||||
--remote|-H) REMOTE_HOST="$2"; shift 2 ;;
|
||||
--script|-l) SCRIPT_PATH="$2"; shift 2 ;;
|
||||
--usb|-U) USB=true; shift ;;
|
||||
--spawn|-f) SPAWN=true; shift ;;
|
||||
--pause) PAUSE=true; shift ;;
|
||||
--list-devices) LIST_DEVICES=true; shift ;;
|
||||
--list-processes) LIST_PROCESSES=true; shift ;;
|
||||
-*) echo "未知选项: $1"; exit 1 ;;
|
||||
*) echo "未知参数: $1"; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# ─── 工具检测 ──────────────────────────────────────────────────────────────────────
|
||||
|
||||
ensure_frida() {
|
||||
if command -v frida &>/dev/null; then
|
||||
return 0
|
||||
fi
|
||||
echo "INFO: frida 未找到,尝试安装..."
|
||||
if [[ -x "$KALI_BOOTSTRAP" ]]; then
|
||||
bash "$KALI_BOOTSTRAP" frida --skip-refresh 2>/dev/null || true
|
||||
fi
|
||||
if ! command -v frida &>/dev/null; then
|
||||
echo "ERR: frida 不可用。安装: pip3 install frida-tools"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
ensure_frida
|
||||
|
||||
# ─── 列出设备 ──────────────────────────────────────────────────────────────────────
|
||||
|
||||
if [[ "$LIST_DEVICES" == "true" ]]; then
|
||||
frida-ls-devices 2>/dev/null || python3 -c "
|
||||
import frida
|
||||
for d in frida.enumerate_devices():
|
||||
print(f'{d.id}\t{d.type}\t{d.name}')
|
||||
"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# ─── 列出进程 ──────────────────────────────────────────────────────────────────────
|
||||
|
||||
if [[ "$LIST_PROCESSES" == "true" ]]; then
|
||||
if [[ "$USB" == "true" ]]; then
|
||||
frida-ps -U
|
||||
else
|
||||
frida-ps -H "$REMOTE_HOST"
|
||||
fi
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# ─── 注入执行 ──────────────────────────────────────────────────────────────────────
|
||||
|
||||
TARGET="${PACKAGE:-$PROCESS}"
|
||||
if [[ -z "$TARGET" ]]; then
|
||||
echo "用法: $0 --package <pkg> --script <js> [--usb] [--spawn]"
|
||||
echo " 或: $0 --list-devices"
|
||||
echo " 或: $0 --list-processes [--usb]"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -z "$SCRIPT_PATH" || ! -f "$SCRIPT_PATH" ]]; then
|
||||
echo "ERR: Frida 脚本不存在: ${SCRIPT_PATH:-未指定}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
FRIDA_ARGS=()
|
||||
|
||||
if [[ "$USB" == "true" ]]; then
|
||||
FRIDA_ARGS+=("-U")
|
||||
else
|
||||
FRIDA_ARGS+=("-H" "$REMOTE_HOST")
|
||||
fi
|
||||
|
||||
if [[ "$SPAWN" == "true" ]]; then
|
||||
FRIDA_ARGS+=("-f")
|
||||
else
|
||||
FRIDA_ARGS+=("-n")
|
||||
fi
|
||||
|
||||
FRIDA_ARGS+=("$TARGET")
|
||||
FRIDA_ARGS+=("-l" "$SCRIPT_PATH")
|
||||
|
||||
if [[ "$PAUSE" != "true" ]]; then
|
||||
FRIDA_ARGS+=("--no-pause")
|
||||
fi
|
||||
|
||||
echo "=== Frida 注入 ==="
|
||||
echo " 目标: $TARGET"
|
||||
echo " 脚本: $SCRIPT_PATH"
|
||||
echo " 模式: $([ "$SPAWN" == "true" ] && echo "spawn" || echo "attach")"
|
||||
echo " 连接: $([ "$USB" == "true" ] && echo "USB" || echo "$REMOTE_HOST")"
|
||||
echo ""
|
||||
|
||||
frida "${FRIDA_ARGS[@]}"
|
||||
@@ -0,0 +1,80 @@
|
||||
#requires -Version 5
|
||||
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$ManifestPath
|
||||
)
|
||||
|
||||
Set-StrictMode -Version Latest
|
||||
$ErrorActionPreference = 'Stop'
|
||||
[Console]::InputEncoding = [System.Text.UTF8Encoding]::new($false)
|
||||
[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new($false)
|
||||
$OutputEncoding = [System.Text.UTF8Encoding]::new($false)
|
||||
|
||||
if (-not (Test-Path -LiteralPath $ManifestPath)) {
|
||||
throw "Manifest not found: $ManifestPath"
|
||||
}
|
||||
|
||||
$xml = [xml](Get-Content -LiteralPath $ManifestPath -Raw -Encoding UTF8)
|
||||
$androidNs = 'http://schemas.android.com/apk/res/android'
|
||||
$nsMgr = [System.Xml.XmlNamespaceManager]::new($xml.NameTable)
|
||||
$nsMgr.AddNamespace('android', $androidNs)
|
||||
|
||||
function Get-AndroidAttr {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]$Node,
|
||||
[Parameter(Mandatory = $true)][string]$Name
|
||||
)
|
||||
|
||||
return [string]$Node.GetAttribute($Name, $androidNs)
|
||||
}
|
||||
|
||||
$manifest = $xml.manifest
|
||||
$packageName = [string]$manifest.package
|
||||
|
||||
"package=$packageName"
|
||||
|
||||
$permissions = @($manifest.SelectNodes('uses-permission'))
|
||||
"permission_count=$($permissions.Count)"
|
||||
foreach ($perm in $permissions) {
|
||||
$name = Get-AndroidAttr -Node $perm -Name 'name'
|
||||
if ($name) {
|
||||
"permission=$name"
|
||||
}
|
||||
}
|
||||
|
||||
$componentQueries = @(
|
||||
@{ Label = 'activity'; Path = 'application/activity' },
|
||||
@{ Label = 'service'; Path = 'application/service' },
|
||||
@{ Label = 'receiver'; Path = 'application/receiver' },
|
||||
@{ Label = 'provider'; Path = 'application/provider' }
|
||||
)
|
||||
|
||||
foreach ($query in $componentQueries) {
|
||||
$nodes = @($manifest.SelectNodes($query.Path))
|
||||
($query.Label + '_count=' + $nodes.Count)
|
||||
foreach ($node in $nodes) {
|
||||
$name = Get-AndroidAttr -Node $node -Name 'name'
|
||||
$exported = Get-AndroidAttr -Node $node -Name 'exported'
|
||||
$enabled = Get-AndroidAttr -Node $node -Name 'enabled'
|
||||
($query.Label + '=' + $name + "`t" + $exported + "`t" + $enabled)
|
||||
}
|
||||
}
|
||||
|
||||
$mainActivities = @()
|
||||
$activities = @($manifest.SelectNodes('application/activity'))
|
||||
foreach ($activity in $activities) {
|
||||
$filters = @($activity.SelectNodes('intent-filter'))
|
||||
foreach ($filter in $filters) {
|
||||
$hasMain = @($filter.SelectNodes('action')).Where({ (Get-AndroidAttr -Node $_ -Name 'name') -eq 'android.intent.action.MAIN' }).Count -gt 0
|
||||
$hasLauncher = @($filter.SelectNodes('category')).Where({ (Get-AndroidAttr -Node $_ -Name 'name') -eq 'android.intent.category.LAUNCHER' }).Count -gt 0
|
||||
if ($hasMain -and $hasLauncher) {
|
||||
$mainActivities += (Get-AndroidAttr -Node $activity -Name 'name')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($entry in ($mainActivities | Select-Object -Unique)) {
|
||||
"main_activity=$entry"
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
#requires -Version 5
|
||||
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$ProjectDir,
|
||||
|
||||
[string]$OutDir,
|
||||
|
||||
[string]$BaseName,
|
||||
|
||||
[string]$KeystorePath,
|
||||
|
||||
[string]$KeyAlias = 'androiddebugkey',
|
||||
|
||||
[string]$StorePass = 'android',
|
||||
|
||||
[string]$KeyPass = 'android',
|
||||
|
||||
[string]$DeviceSerial,
|
||||
|
||||
[switch]$Install,
|
||||
|
||||
[switch]$Reinstall,
|
||||
|
||||
[switch]$Clean
|
||||
)
|
||||
|
||||
Set-StrictMode -Version Latest
|
||||
$ErrorActionPreference = 'Stop'
|
||||
[Console]::InputEncoding = [System.Text.UTF8Encoding]::new($false)
|
||||
[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new($false)
|
||||
$OutputEncoding = [System.Text.UTF8Encoding]::new($false)
|
||||
|
||||
if ([string]::IsNullOrWhiteSpace($KeystorePath)) {
|
||||
$KeystorePath = Join-Path (Join-Path $env:USERPROFILE '.android') 'debug.keystore'
|
||||
}
|
||||
|
||||
function Get-ToolPath {
|
||||
param([Parameter(Mandatory = $true)][string]$Name)
|
||||
|
||||
$cmd = Get-Command $Name -ErrorAction SilentlyContinue
|
||||
if ($cmd) {
|
||||
return $cmd.Source
|
||||
}
|
||||
|
||||
$sdkRoot = Join-Path $env:LOCALAPPDATA 'Android\Sdk'
|
||||
$buildTools = @()
|
||||
if (Test-Path -LiteralPath (Join-Path $sdkRoot 'build-tools')) {
|
||||
$buildTools = Get-ChildItem -LiteralPath (Join-Path $sdkRoot 'build-tools') -Directory -ErrorAction SilentlyContinue |
|
||||
Sort-Object -Property Name -Descending
|
||||
}
|
||||
|
||||
$fallbacks = @{
|
||||
'apktool' = @(
|
||||
(Join-Path $env:USERPROFILE 'Tools\apktool\apktool.bat')
|
||||
)
|
||||
'zipalign' = @($buildTools | ForEach-Object { Join-Path $_.FullName 'zipalign.exe' })
|
||||
'apksigner' = @($buildTools | ForEach-Object { Join-Path $_.FullName 'apksigner.bat' })
|
||||
'keytool' = @()
|
||||
'adb' = @(
|
||||
(Join-Path $sdkRoot 'platform-tools\adb.exe'),
|
||||
(Join-Path $env:USERPROFILE 'AppData\Local\Android\Sdk\platform-tools\adb.exe')
|
||||
)
|
||||
}
|
||||
|
||||
if ($fallbacks.Contains($Name)) {
|
||||
foreach ($candidate in $fallbacks[$Name]) {
|
||||
if (-not [string]::IsNullOrWhiteSpace($candidate) -and (Test-Path -LiteralPath $candidate)) {
|
||||
return $candidate
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Attempt auto-bootstrap for supported tools
|
||||
$bootstrapScript = Join-Path $PSScriptRoot '..\..\scripts\bootstrap-reverse.ps1'
|
||||
$bootstrapSupported = @('adb', 'apktool')
|
||||
if ($Name -in $bootstrapSupported -and (Test-Path -LiteralPath $bootstrapScript)) {
|
||||
Write-Host "INFO: $Name not found, attempting auto-bootstrap..." -ForegroundColor Yellow
|
||||
& powershell.exe -NoProfile -ExecutionPolicy Bypass -File $bootstrapScript -Capability @($Name) -SkipRefresh
|
||||
$cmd = Get-Command $Name -ErrorAction SilentlyContinue
|
||||
if ($cmd) {
|
||||
Write-Host "INFO: $Name bootstrapped successfully." -ForegroundColor Green
|
||||
return $cmd.Source
|
||||
}
|
||||
# Re-check fallbacks after bootstrap
|
||||
if ($fallbacks.Contains($Name)) {
|
||||
foreach ($candidate in $fallbacks[$Name]) {
|
||||
if (-not [string]::IsNullOrWhiteSpace($candidate) -and (Test-Path -LiteralPath $candidate)) {
|
||||
Write-Host "INFO: $Name found at fallback path after bootstrap." -ForegroundColor Green
|
||||
return $candidate
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Clear error message for tools that cannot be auto-bootstrapped
|
||||
$manualHint = switch ($Name) {
|
||||
'zipalign' { 'Install Android Build-Tools via Android SDK Manager (sdkmanager "build-tools;35.0.0")' }
|
||||
'apksigner' { 'Install Android Build-Tools via Android SDK Manager (sdkmanager "build-tools;35.0.0")' }
|
||||
default { "Install $Name manually" }
|
||||
}
|
||||
throw "Missing required CLI tool: $Name — $manualHint"
|
||||
}
|
||||
|
||||
function Ensure-DebugKeystore {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)][string]$Path,
|
||||
[Parameter(Mandatory = $true)][string]$Keytool,
|
||||
[Parameter(Mandatory = $true)][string]$Alias,
|
||||
[Parameter(Mandatory = $true)][string]$StorePassword,
|
||||
[Parameter(Mandatory = $true)][string]$KeyPassword
|
||||
)
|
||||
|
||||
if (Test-Path -LiteralPath $Path) {
|
||||
return
|
||||
}
|
||||
|
||||
$parent = Split-Path -Path $Path -Parent
|
||||
if (-not (Test-Path -LiteralPath $parent)) {
|
||||
New-Item -ItemType Directory -Path $parent -Force | Out-Null
|
||||
}
|
||||
|
||||
& $Keytool -genkeypair -v -keystore $Path -storepass $StorePassword -keypass $KeyPassword -alias $Alias -keyalg RSA -keysize 2048 -validity 10000 -dname 'CN=Android Debug,O=ReverseSkill,C=CN'
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw 'Failed to generate debug keystore.'
|
||||
}
|
||||
}
|
||||
|
||||
if (-not (Test-Path -LiteralPath $ProjectDir)) {
|
||||
throw "Project directory not found: $ProjectDir"
|
||||
}
|
||||
|
||||
if ([string]::IsNullOrWhiteSpace($OutDir)) {
|
||||
$projectParent = Split-Path -Path $ProjectDir -Parent
|
||||
if ([string]::IsNullOrWhiteSpace($projectParent)) {
|
||||
$projectParent = [System.IO.Directory]::GetCurrentDirectory()
|
||||
}
|
||||
$OutDir = $projectParent
|
||||
}
|
||||
|
||||
$apktool = Get-ToolPath -Name 'apktool'
|
||||
$zipalign = Get-ToolPath -Name 'zipalign'
|
||||
$apksigner = Get-ToolPath -Name 'apksigner'
|
||||
$keytool = Get-ToolPath -Name 'keytool'
|
||||
$adb = Get-ToolPath -Name 'adb'
|
||||
|
||||
Ensure-DebugKeystore -Path $KeystorePath -Keytool $keytool -Alias $KeyAlias -StorePassword $StorePass -KeyPassword $KeyPass
|
||||
|
||||
if (-not (Test-Path -LiteralPath $OutDir)) {
|
||||
New-Item -ItemType Directory -Path $OutDir -Force | Out-Null
|
||||
}
|
||||
|
||||
$name = if ($BaseName) { $BaseName } else { Split-Path -Path $ProjectDir -Leaf }
|
||||
$unsignedApk = Join-Path $OutDir ($name + '-unsigned.apk')
|
||||
$alignedApk = Join-Path $OutDir ($name + '-aligned.apk')
|
||||
$signedApk = Join-Path $OutDir ($name + '-signed.apk')
|
||||
|
||||
if ($Clean) {
|
||||
foreach ($path in @($unsignedApk, $alignedApk, $signedApk)) {
|
||||
if (Test-Path -LiteralPath $path) {
|
||||
Remove-Item -LiteralPath $path -Force
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
& $apktool b $ProjectDir -o $unsignedApk
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw 'apktool build failed.'
|
||||
}
|
||||
|
||||
& $zipalign -f -p 4 $unsignedApk $alignedApk
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw 'zipalign failed.'
|
||||
}
|
||||
|
||||
Copy-Item -LiteralPath $alignedApk -Destination $signedApk -Force
|
||||
& $apksigner sign --ks $KeystorePath --ks-key-alias $KeyAlias --ks-pass "pass:$StorePass" --key-pass "pass:$KeyPass" --out $signedApk $alignedApk
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw 'apksigner sign failed.'
|
||||
}
|
||||
|
||||
& $apksigner verify --print-certs $signedApk
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw 'apksigner verify failed.'
|
||||
}
|
||||
|
||||
"unsigned_apk=$unsignedApk"
|
||||
"aligned_apk=$alignedApk"
|
||||
"signed_apk=$signedApk"
|
||||
"keystore=$KeystorePath"
|
||||
|
||||
if ($Install) {
|
||||
$installArgs = @()
|
||||
if ($DeviceSerial) {
|
||||
$installArgs += '-s'
|
||||
$installArgs += $DeviceSerial
|
||||
}
|
||||
$installArgs += 'install'
|
||||
if ($Reinstall) {
|
||||
$installArgs += '-r'
|
||||
}
|
||||
$installArgs += $signedApk
|
||||
|
||||
& $adb @installArgs
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw 'adb install failed.'
|
||||
}
|
||||
|
||||
"install_device=$DeviceSerial"
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
#!/usr/bin/env bash
|
||||
# rebuild-sign-install.sh — APK 重打包 + 签名 + 安装
|
||||
# 等价于 Windows 版的 rebuild-sign-install.ps1
|
||||
#
|
||||
# 用法:
|
||||
# bash rebuild-sign-install.sh <project_dir> [--out <dir>] [--name <base>]
|
||||
# [--keystore <path>] [--install] [--reinstall]
|
||||
# [--device <serial>] [--clean]
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
KALI_BOOTSTRAP="$(cd "$SCRIPT_DIR/../../../kali/scripts" 2>/dev/null && pwd)/bootstrap-reverse.sh"
|
||||
DEFAULT_KEYSTORE="$HOME/.android/debug.keystore"
|
||||
|
||||
# ─── 参数 ──────────────────────────────────────────────────────────────────────────
|
||||
|
||||
PROJECT_DIR=""
|
||||
OUT_DIR=""
|
||||
BASE_NAME=""
|
||||
KEYSTORE="$DEFAULT_KEYSTORE"
|
||||
KEY_ALIAS="androiddebugkey"
|
||||
STORE_PASS="android"
|
||||
KEY_PASS="android"
|
||||
DEVICE_SERIAL=""
|
||||
DO_INSTALL=false
|
||||
REINSTALL=false
|
||||
CLEAN=false
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--out) OUT_DIR="$2"; shift 2 ;;
|
||||
--name) BASE_NAME="$2"; shift 2 ;;
|
||||
--keystore) KEYSTORE="$2"; shift 2 ;;
|
||||
--alias) KEY_ALIAS="$2"; shift 2 ;;
|
||||
--store-pass) STORE_PASS="$2"; shift 2 ;;
|
||||
--key-pass) KEY_PASS="$2"; shift 2 ;;
|
||||
--device) DEVICE_SERIAL="$2"; shift 2 ;;
|
||||
--install) DO_INSTALL=true; shift ;;
|
||||
--reinstall) REINSTALL=true; DO_INSTALL=true; shift ;;
|
||||
--clean) CLEAN=true; shift ;;
|
||||
-*) echo "未知选项: $1"; exit 1 ;;
|
||||
*) PROJECT_DIR="$1"; shift ;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ -z "$PROJECT_DIR" || ! -d "$PROJECT_DIR" ]]; then
|
||||
echo "用法: $0 <apktool_project_dir> [options]"
|
||||
echo " --out <dir> 输出目录(默认: 项目父目录)"
|
||||
echo " --name <base> 输出文件名前缀"
|
||||
echo " --keystore <path> 签名密钥库(默认: ~/.android/debug.keystore)"
|
||||
echo " --install 签名后安装到设备"
|
||||
echo " --reinstall 覆盖安装"
|
||||
echo " --device <serial> 指定设备"
|
||||
echo " --clean 清理旧产物"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ─── 工具检测 ──────────────────────────────────────────────────────────────────────
|
||||
|
||||
ensure_tool() {
|
||||
local name="$1"
|
||||
if command -v "$name" &>/dev/null; then
|
||||
return 0
|
||||
fi
|
||||
echo "INFO: $name 未找到,尝试自动安装..."
|
||||
if [[ -x "$KALI_BOOTSTRAP" ]]; then
|
||||
bash "$KALI_BOOTSTRAP" "$name" --skip-refresh 2>/dev/null || true
|
||||
fi
|
||||
if ! command -v "$name" &>/dev/null; then
|
||||
echo "ERR: $name 不可用。"
|
||||
case "$name" in
|
||||
zipalign|apksigner) echo " 安装: sudo apt install android-sdk-build-tools 或 sdkmanager 'build-tools;35.0.0'" ;;
|
||||
*) echo " 请手动安装 $name" ;;
|
||||
esac
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
ensure_tool "apktool"
|
||||
ensure_tool "zipalign"
|
||||
ensure_tool "apksigner"
|
||||
ensure_tool "keytool"
|
||||
[[ "$DO_INSTALL" == "true" ]] && ensure_tool "adb"
|
||||
|
||||
# ─── 生成 debug keystore(如果不存在) ─────────────────────────────────────────────
|
||||
|
||||
if [[ ! -f "$KEYSTORE" ]]; then
|
||||
echo "INFO: 生成 debug keystore: $KEYSTORE"
|
||||
keytool -genkeypair -v \
|
||||
-keystore "$KEYSTORE" \
|
||||
-storepass "$STORE_PASS" \
|
||||
-keypass "$KEY_PASS" \
|
||||
-alias "$KEY_ALIAS" \
|
||||
-keyalg RSA -keysize 2048 -validity 10000 \
|
||||
-dname "CN=Android Debug,O=ReverseSkill,C=CN"
|
||||
fi
|
||||
|
||||
# ─── 路径计算 ──────────────────────────────────────────────────────────────────────
|
||||
|
||||
OUT_DIR="${OUT_DIR:-$(dirname "$PROJECT_DIR")}"
|
||||
BASE_NAME="${BASE_NAME:-$(basename "$PROJECT_DIR")}"
|
||||
mkdir -p "$OUT_DIR"
|
||||
|
||||
UNSIGNED_APK="$OUT_DIR/${BASE_NAME}-unsigned.apk"
|
||||
ALIGNED_APK="$OUT_DIR/${BASE_NAME}-aligned.apk"
|
||||
SIGNED_APK="$OUT_DIR/${BASE_NAME}-signed.apk"
|
||||
|
||||
if [[ "$CLEAN" == "true" ]]; then
|
||||
rm -f "$UNSIGNED_APK" "$ALIGNED_APK" "$SIGNED_APK"
|
||||
fi
|
||||
|
||||
# ─── 重打包 ───────────────────────────────────────────────────────────────────────
|
||||
|
||||
echo "=== apktool 重打包 ==="
|
||||
apktool b "$PROJECT_DIR" -o "$UNSIGNED_APK"
|
||||
|
||||
# ─── 对齐 ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
echo "=== zipalign 对齐 ==="
|
||||
zipalign -f -p 4 "$UNSIGNED_APK" "$ALIGNED_APK"
|
||||
|
||||
# ─── 签名 ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
echo "=== apksigner 签名 ==="
|
||||
apksigner sign \
|
||||
--ks "$KEYSTORE" \
|
||||
--ks-key-alias "$KEY_ALIAS" \
|
||||
--ks-pass "pass:$STORE_PASS" \
|
||||
--key-pass "pass:$KEY_PASS" \
|
||||
--out "$SIGNED_APK" \
|
||||
"$ALIGNED_APK"
|
||||
|
||||
# ─── 验证 ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
echo "=== 验证签名 ==="
|
||||
apksigner verify --print-certs "$SIGNED_APK"
|
||||
|
||||
echo ""
|
||||
echo "═══════════════════════════════════════════"
|
||||
echo " APK 重打包完成"
|
||||
echo "═══════════════════════════════════════════"
|
||||
echo " unsigned_apk=$UNSIGNED_APK"
|
||||
echo " aligned_apk=$ALIGNED_APK"
|
||||
echo " signed_apk=$SIGNED_APK"
|
||||
echo " keystore=$KEYSTORE"
|
||||
|
||||
# ─── 安装 ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
if [[ "$DO_INSTALL" == "true" ]]; then
|
||||
echo "=== adb 安装 ==="
|
||||
ADB_ARGS=()
|
||||
[[ -n "$DEVICE_SERIAL" ]] && ADB_ARGS+=("-s" "$DEVICE_SERIAL")
|
||||
ADB_ARGS+=("install")
|
||||
[[ "$REINSTALL" == "true" ]] && ADB_ARGS+=("-r")
|
||||
ADB_ARGS+=("$SIGNED_APK")
|
||||
|
||||
adb "${ADB_ARGS[@]}"
|
||||
echo " install_device=${DEVICE_SERIAL:-default}"
|
||||
fi
|
||||
Reference in New Issue
Block a user