Files

233 lines
10 KiB
PowerShell
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#Requires -Version 5.1
<#
Seep 工作台 —— 工具修复器(可选)
----------------------------------------------------------------
工具已随包内置在 Tool\mcp\Tool\safe\,正常无需运行本脚本。
仅当内置工具损坏/缺失时,用本脚本重新下载。
官方源优先,失败自动回退国内镜像。
#>
[CmdletBinding()]
param(
[string[]]$Only = @(), # 只装指定工具,如 -Only jadx,radare2
[switch]$Force # 已存在也重新下载
)
$ErrorActionPreference = 'Continue'
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
$Root = Split-Path -Parent $ScriptDir
# seep MCP 硬编码要求:TOOL_DIR = <脚本目录>/Tool → 工具必须落在 Tool\mcp\Tool\safe\
$ToolsDir = Join-Path $Root 'Tool\mcp\Tool\safe'
New-Item -ItemType Directory -Force -Path $ToolsDir | Out-Null
$Mirrors = @(
'', # 官方源
'https://ghproxy.net/',
'https://gh-proxy.com/'
)
function Write-Ok($m) { Write-Host " [OK] $m" -ForegroundColor Green }
function Write-Warn($m) { Write-Host " [!!] $m" -ForegroundColor Yellow }
function Write-Info($m) { Write-Host " [..] $m" -ForegroundColor Gray }
# 带镜像回退的下载
function Get-FileWithMirror {
param([string]$Url, [string]$OutFile)
foreach ($m in $Mirrors) {
$u = if ($m -eq '' -or $Url -notmatch '^https?://github') { $Url } else { "$m$Url" }
$tag = if ($m -eq '') { 'official' } else { $m.TrimEnd('/') }
try {
Write-Info "尝试 $tag ..."
$ProgressPreference = 'SilentlyContinue'
Invoke-WebRequest -Uri $u -OutFile $OutFile -TimeoutSec 300 -UseBasicParsing
if ((Test-Path $OutFile) -and (Get-Item $OutFile).Length -gt 1024) {
Write-Ok "下载成功 ($tag)"
return $true
}
} catch {
Write-Info "失败: $($_.Exception.Message.Split([Environment]::NewLine)[0])"
}
}
return $false
}
# 取 GitHub 最新 release 的 asset 下载地址
function Get-GitHubAsset {
param([string]$Repo, [string]$Pattern)
try {
$api = "https://api.github.com/repos/$Repo/releases/latest"
$rel = Invoke-RestMethod -Uri $api -TimeoutSec 60 -Headers @{ 'User-Agent' = 'seep-setup' }
$asset = $rel.assets | Where-Object { $_.name -match $Pattern } | Select-Object -First 1
if ($asset) { return @{ Url = $asset.browser_download_url; Name = $asset.name; Tag = $rel.tag_name } }
} catch {
Write-Warn "GitHub API 失败 ($Repo): $($_.Exception.Message.Split([Environment]::NewLine)[0])"
}
return $null
}
function Expand-ZipTo {
param([string]$Zip, [string]$Dest)
New-Item -ItemType Directory -Force -Path $Dest | Out-Null
Expand-Archive -Path $Zip -DestinationPath $Dest -Force
}
function Should-Run($name) {
if ($Only.Count -eq 0) { return $true }
return ($Only -contains $name)
}
$results = @{}
# ---------------------------------------------------------------- jadx
if (Should-Run 'jadx') {
Write-Host "`n [1/6] jadx (Java 反编译)" -ForegroundColor Cyan
$dst = Join-Path $ToolsDir 'jadx'
if ((Test-Path $dst) -and -not $Force) { Write-Ok '已存在,跳过'; $results['jadx'] = $true }
else {
$a = Get-GitHubAsset -Repo 'skylot/jadx' -Pattern '^jadx-[\d.]+\.zip$'
if ($a) {
$zip = Join-Path $env:TEMP $a.Name
if (Get-FileWithMirror -Url $a.Url -OutFile $zip) {
if (Test-Path $dst) { Remove-Item $dst -Recurse -Force }
Expand-ZipTo -Zip $zip -Dest $dst
Remove-Item $zip -Force -ErrorAction SilentlyContinue
Write-Ok "jadx $($a.Tag) -> tools\jadx"
$results['jadx'] = $true
} else { Write-Warn 'jadx 下载失败'; $results['jadx'] = $false }
} else { Write-Warn 'jadx: 无法获取 release 信息'; $results['jadx'] = $false }
}
}
# ---------------------------------------------------------------- radare2
if (Should-Run 'radare2') {
Write-Host "`n [2/6] radare2 (二进制逆向)" -ForegroundColor Cyan
$dst = Join-Path $ToolsDir 'radare2'
if ((Test-Path $dst) -and -not $Force) { Write-Ok '已存在,跳过'; $results['radare2'] = $true }
else {
$a = Get-GitHubAsset -Repo 'radareorg/radare2' -Pattern '^radare2-.*-w64\.zip$'
if ($a) {
$zip = Join-Path $env:TEMP $a.Name
if (Get-FileWithMirror -Url $a.Url -OutFile $zip) {
if (Test-Path $dst) { Remove-Item $dst -Recurse -Force }
Expand-ZipTo -Zip $zip -Dest $dst
Remove-Item $zip -Force -ErrorAction SilentlyContinue
Write-Ok "radare2 $($a.Tag) -> tools\radare2"
$results['radare2'] = $true
} else { Write-Warn 'radare2 下载失败'; $results['radare2'] = $false }
} else { Write-Warn 'radare2: 无法获取 release 信息'; $results['radare2'] = $false }
}
}
# ---------------------------------------------------------------- apktool
if (Should-Run 'apktool') {
Write-Host "`n [3/6] apktool (APK 解包/重打包)" -ForegroundColor Cyan
$dst = Join-Path $ToolsDir 'apktool'
if ((Test-Path $dst) -and -not $Force) { Write-Ok '已存在,跳过'; $results['apktool'] = $true }
else {
New-Item -ItemType Directory -Force -Path $dst | Out-Null
$a = Get-GitHubAsset -Repo 'iBotPeaches/Apktool' -Pattern '^apktool_[\d.]+\.jar$'
if ($a) {
$jar = Join-Path $dst 'apktool.jar'
if (Get-FileWithMirror -Url $a.Url -OutFile $jar) {
$bat = @'
@echo off
if "%JAVA_HOME%"=="" (set JAVA=java) else (set JAVA="%JAVA_HOME%\bin\java.exe")
%JAVA% -jar -Duser.language=en "%~dp0apktool.jar" %*
'@
Set-Content -Path (Join-Path $dst 'apktool.bat') -Value $bat -Encoding ASCII
Write-Ok "apktool $($a.Tag) -> tools\apktool"
$results['apktool'] = $true
} else { Write-Warn 'apktool 下载失败'; $results['apktool'] = $false }
} else { Write-Warn 'apktool: 无法获取 release 信息'; $results['apktool'] = $false }
}
}
# ---------------------------------------------------------------- playwright-mcp
if (Should-Run 'playwright-mcp') {
Write-Host "`n [4/6] playwright-mcp (浏览器自动化)" -ForegroundColor Cyan
if (-not (Get-Command npm -ErrorAction SilentlyContinue)) {
Write-Warn '未找到 npm,跳过'; $results['playwright-mcp'] = $false
} else {
$dst = Join-Path $ToolsDir 'playwright-mcp'
New-Item -ItemType Directory -Force -Path $dst | Out-Null
Push-Location $dst
try {
if (-not (Test-Path (Join-Path $dst 'package.json'))) {
'{ "name": "playwright-mcp-local", "private": true }' | Set-Content -Path 'package.json' -Encoding UTF8
}
Write-Info 'npm install @playwright/mcp ...'
& npm install '@playwright/mcp' --no-audit --no-fund 2>&1 | Out-Null
if ($LASTEXITCODE -eq 0) { Write-Ok 'playwright-mcp -> tools\playwright-mcp'; $results['playwright-mcp'] = $true }
else { Write-Warn 'npm install 失败(可改用 npx 方式)'; $results['playwright-mcp'] = $false }
} finally { Pop-Location }
}
}
# ---------------------------------------------------------------- js-reverse-mcp
if (Should-Run 'js-reverse-mcp') {
Write-Host "`n [5/6] js-reverse-mcp (JS 逆向)" -ForegroundColor Cyan
# 官方推荐 npx 免安装方式:无需下载,仅在 mcp.json 注册即可
if (-not (Get-Command npm -ErrorAction SilentlyContinue)) {
Write-Warn '未找到 npm,跳过(js-reverse-mcp 依赖 npx)'
$results['js-reverse-mcp'] = $false
} else {
Write-Info 'js-reverse-mcp 采用官方推荐的 npx 方式(无需本地下载)'
Write-Info '注册方式:mcp.json 中 command=npx, args=["js-reverse-mcp"]'
Write-Info '可选本地安装:git clone https://github.com/zhizhuodemao/js-reverse-mcp + npm i + npm run build'
Write-Ok 'js-reverse-mcp -> 走 npx(已在 mcp.json.template 中预置)'
$results['js-reverse-mcp'] = $true
}
}
# ---------------------------------------------------------------- ida-pro-mcp
if (Should-Run 'ida-pro-mcp') {
Write-Host "`n [6/6] ida-pro-mcp (IDA 桥接)" -ForegroundColor Cyan
$dst = Join-Path $ToolsDir 'ida-pro-mcp'
if ((Test-Path $dst) -and -not $Force) { Write-Ok '已存在,跳过'; $results['ida-pro-mcp'] = $true }
else {
Write-Info 'pip install ida-pro-mcp ...'
& python -m pip install --quiet ida-pro-mcp 2>&1 | Out-Null
if ($LASTEXITCODE -eq 0) {
New-Item -ItemType Directory -Force -Path $dst | Out-Null
Write-Ok 'ida-pro-mcp 已装入 Python 环境'
Write-Info '(IDA 本体需你自备,见 MANUAL\IDA-PRO.md)'
$results['ida-pro-mcp'] = $true
} else { Write-Warn 'ida-pro-mcp 安装失败'; $results['ida-pro-mcp'] = $false }
}
}
# ---------------------------------------------------------------- PATH
Write-Host "`n [PATH] 加入工具目录" -ForegroundColor Cyan
$addPaths = @()
foreach ($p in @('radare2', 'jadx\bin', 'apktool')) {
$full = Join-Path $ToolsDir $p
if (Test-Path $full) { $addPaths += $full }
}
if ($addPaths.Count -gt 0) {
$old = [Environment]::GetEnvironmentVariable('Path', 'User')
$parts = $old -split ';' | Where-Object { $_ -ne '' }
$added = 0
foreach ($p in $addPaths) {
if ($parts -notcontains $p) { $parts += $p; $added++ }
}
if ($added -gt 0) {
[Environment]::SetEnvironmentVariable('Path', ($parts -join ';'), 'User')
Write-Ok "已向用户 PATH 追加 $added 个目录(原 PATH 未备份,如需回滚请手动移除)"
} else { Write-Ok 'PATH 已包含这些目录' }
} else { Write-Warn '未发现可加入 PATH 的目录' }
# ---------------------------------------------------------------- 汇总
Write-Host "`n ---------- 工具下载汇总 ----------" -ForegroundColor White
foreach ($k in $results.Keys | Sort-Object) {
$v = $results[$k]
$mark = if ($v -eq $true) { '[OK] ' } elseif ($v -eq $false) { '[FAIL]' } else { '[SKIP]' }
Write-Host (" {0} {1}" -f $mark, $k)
}
$failed = @($results.Keys | Where-Object { $results[$_] -eq $false })
if ($failed.Count -gt 0) {
Write-Host "`n 失败项: $($failed -join ', ')" -ForegroundColor Yellow
Write-Host ' 可重跑本脚本,或按 MANUAL\PREREQUISITES.md 手动下载' -ForegroundColor Yellow
exit 1
}
exit 0