diff --git a/.build.config.example b/.build.config.example new file mode 100644 index 0000000..4c5e51b --- /dev/null +++ b/.build.config.example @@ -0,0 +1,14 @@ +# AntiHook 构建配置文件 +# 复制此文件为 .build.config 并修改配置 + +# Kiro 服务器地址 +# 默认: http://localhost:8045 +SERVER_URL="http://localhost:8045" + +# 后端服务器地址 +# 默认: http://localhost:8008 +BACKEND_URL="http://localhost:8008" + +# 生产环境示例: +# SERVER_URL="https://your-kiro-server.com" +# BACKEND_URL="https://your-backend-server.com" \ No newline at end of file diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 58d651e..4be3b4b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,12 +13,25 @@ env: BACKEND_URL: ${{ secrets.BACKEND_URL || 'http://localhost:8008' }} jobs: - build-and-release: - name: Build and Release - runs-on: windows-latest + build: + name: Build ${{ matrix.os }} + runs-on: ${{ matrix.os }} if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master') - permissions: - contents: write + strategy: + matrix: + include: + - os: windows-latest + goos: windows + goarch: amd64 + output: antihook.exe + - os: macos-latest + goos: darwin + goarch: amd64 + output: antihook-darwin-amd64 + - os: macos-latest + goos: darwin + goarch: arm64 + output: antihook-darwin-arm64 steps: - name: Checkout code @@ -34,19 +47,64 @@ jobs: - name: Generate version id: version + shell: bash run: | - $short_sha = "${{ github.sha }}".Substring(0, 7) - $date = Get-Date -Format "yyyyMMdd-HHmmss" - $version = "dev-$date-$short_sha" - echo "VERSION=$version" >> $env:GITHUB_OUTPUT + short_sha=$(echo "${{ github.sha }}" | cut -c1-7) + date=$(date -u +"%Y%m%d-%H%M%S") + version="dev-$date-$short_sha" + echo "VERSION=$version" >> $GITHUB_OUTPUT echo "Generated version: $version" - shell: pwsh - - name: Build - run: go build -v -ldflags="-s -w -X main.DefaultServerURL=${{ env.KIRO_SERVER_URL }} -X main.DefaultBackendURL=${{ env.BACKEND_URL }}" -o antihook.exe . - env: - KIRO_SERVER_URL: ${{ secrets.KIRO_SERVER_URL || 'http://localhost:8045' }} - BACKEND_URL: ${{ secrets.BACKEND_URL || 'http://localhost:8008' }} + - name: Build for ${{ matrix.goos }}-${{ matrix.goarch }} + shell: bash + run: | + LDFLAGS="-s -w" + LDFLAGS="$LDFLAGS -X 'main.DefaultServerURL=${{ secrets.KIRO_SERVER_URL || 'http://localhost:8045' }}'" + LDFLAGS="$LDFLAGS -X 'main.DefaultBackendURL=${{ secrets.BACKEND_URL || 'http://localhost:8008' }}'" + LDFLAGS="$LDFLAGS -X 'main.BuildVersion=${{ steps.version.outputs.VERSION }}'" + LDFLAGS="$LDFLAGS -X 'main.BuildTime=$(date -u +"%Y-%m-%dT%H:%M:%SZ")'" + + GOOS=${{ matrix.goos }} GOARCH=${{ matrix.goarch }} \ + go build -v -ldflags="$LDFLAGS" -o ${{ matrix.output }} . + + echo "Built: ${{ matrix.output }}" + ls -lh ${{ matrix.output }} + + - name: Upload artifact + uses: actions/upload-artifact@v3 + with: + name: ${{ matrix.output }} + path: ${{ matrix.output }} + retention-days: 7 + + release: + name: Create Release + needs: build + runs-on: ubuntu-latest + if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master') + permissions: + contents: write + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Generate version + id: version + run: | + short_sha=$(echo "${{ github.sha }}" | cut -c1-7) + date=$(date -u +"%Y%m%d-%H%M%S") + version="dev-$date-$short_sha" + echo "VERSION=$version" >> $GITHUB_OUTPUT + echo "Generated version: $version" + + - name: Download all artifacts + uses: actions/download-artifact@v3 + with: + path: artifacts + + - name: Display structure of downloaded files + run: ls -R artifacts - name: Create and push tag run: | @@ -62,9 +120,28 @@ jobs: with: tag_name: ${{ steps.version.outputs.VERSION }} name: Release ${{ steps.version.outputs.VERSION }} - files: antihook.exe + files: | + artifacts/antihook.exe/antihook.exe + artifacts/antihook-darwin-amd64/antihook-darwin-amd64 + artifacts/antihook-darwin-arm64/antihook-darwin-arm64 draft: false prerelease: false generate_release_notes: true + body: | + ## 📦 构建产物 + + - `antihook.exe` - Windows (amd64) + - `antihook-darwin-amd64` - macOS Intel + - `antihook-darwin-arm64` - macOS Apple Silicon + + ## 🔧 配置信息 + + - Kiro 服务器: ${{ secrets.KIRO_SERVER_URL || 'http://localhost:8045' }} + - 后端服务器: ${{ secrets.BACKEND_URL || 'http://localhost:8008' }} + - 构建版本: ${{ steps.version.outputs.VERSION }} + + ## 📖 使用说明 + + 请参考 [README.md](https://github.com/${{ github.repository }}/blob/main/README.md) 了解详细的安装和使用方法。 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.gitignore b/.gitignore index 40c583d..6b90b7f 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,12 @@ /bin/ /dist/ +/build/ + +# 构建产物 +antihook +antihook-* +*.app logs/ *.log @@ -21,4 +27,7 @@ go.work *~ .DS_Store -Thumbs.db \ No newline at end of file +Thumbs.db + +# 构建配置(包含敏感信息) +.build.config \ No newline at end of file diff --git a/BUILD_GUIDE.md b/BUILD_GUIDE.md new file mode 100644 index 0000000..221d519 --- /dev/null +++ b/BUILD_GUIDE.md @@ -0,0 +1,178 @@ +# AntiHook 构建指南 + +本指南说明如何配置和构建 AntiHook,使打包后的程序自动使用您的后端地址。 + +## 🎯 快速开始 + +### 1. 创建配置文件 + +```bash +# 复制配置模板 +cp .build.config.example .build.config + +# 编辑配置文件 +vim .build.config # 或使用您喜欢的编辑器 +``` + +### 2. 配置您的后端地址 + +在 `.build.config` 文件中设置: + +```bash +# Kiro 服务器地址 +SERVER_URL="https://your-kiro-server.com" + +# 后端服务器地址 +BACKEND_URL="https://your-backend-server.com" +``` + +### 3. 构建程序 + +```bash +# 构建所有平台 +./build.sh all + +# 或构建特定平台 +./build.sh darwin # macOS +./build.sh windows # Windows +./build.sh linux # Linux +``` + +构建完成后,生成的二进制文件会自动包含您配置的后端地址! + +## 📋 配置优先级 + +AntiHook 支持三种配置方式,优先级从高到低: + +1. **运行时环境变量**(最高优先级) + ```bash + export KIRO_SERVER_URL="https://runtime-server.com" + export BACKEND_URL="https://runtime-backend.com" + ./antihook + ``` + +2. **构建时配置文件**(推荐用于发布) + ```bash + # .build.config 文件 + SERVER_URL="https://build-server.com" + BACKEND_URL="https://build-backend.com" + ``` + +3. **构建时环境变量** + ```bash + SERVER_URL="https://env-server.com" ./build.sh all + ``` + +4. **默认值**(开发环境) + - SERVER_URL: `http://localhost:8045` + - BACKEND_URL: `http://localhost:8008` + +## 🔧 使用场景 + +### 场景一:本地开发 + +不需要任何配置,直接构建即可使用 localhost: + +```bash +go build -o antihook . +./antihook +``` + +### 场景二:生产环境发布 + +1. 创建 `.build.config` 配置生产环境地址 +2. 构建发布版本 +3. 分发给用户 + +```bash +# .build.config +SERVER_URL="https://prod-kiro.example.com" +BACKEND_URL="https://prod-api.example.com" + +# 构建 +./build.sh all + +# 构建产物在 build/ 目录 +ls -lh build/ +``` + +### 场景三:多环境构建 + +为不同环境构建不同版本: + +```bash +# 测试环境 +SERVER_URL="https://test-api.com" \ +BACKEND_URL="https://test-backend.com" \ +./build.sh darwin +mv build/antihook-darwin-amd64 build/antihook-darwin-amd64-test + +# 生产环境 +SERVER_URL="https://prod-api.com" \ +BACKEND_URL="https://prod-backend.com" \ +./build.sh darwin +mv build/antihook-darwin-amd64 build/antihook-darwin-amd64-prod +``` + +## 🔍 验证构建配置 + +构建完成后,可以验证配置是否正确注入: + +```bash +# macOS/Linux +strings build/antihook-darwin-amd64 | grep -E "https?://" + +# Windows (使用 PowerShell) +Select-String -Path build/antihook-windows-amd64.exe -Pattern "https?://" -AllMatches +``` + +您应该能看到配置的 URL 地址。 + +## 📝 注意事项 + +1. **`.build.config` 文件已添加到 `.gitignore`** + - 不会被提交到版本控制 + - 可以安全地包含敏感信息 + +2. **配置文件格式** + - 使用 Bash 变量格式 + - URL 必须包含协议(http:// 或 https://) + - 不要在末尾添加斜杠 + +3. **构建信息** + - 构建时会自动注入构建时间和版本号 + - 可用于追踪和调试 + +## 🚀 CI/CD 集成 + +在 CI/CD 流水线中使用环境变量: + +```yaml +# GitHub Actions 示例 +- name: Build AntiHook + env: + SERVER_URL: ${{ secrets.PROD_SERVER_URL }} + BACKEND_URL: ${{ secrets.PROD_BACKEND_URL }} + run: ./build.sh all +``` + +## 💡 最佳实践 + +1. **开发环境**:不使用配置文件,使用默认 localhost +2. **测试环境**:使用 `.build.config` 配置测试服务器 +3. **生产环境**:使用 CI/CD 环境变量或专门的配置文件 +4. **多环境**:为每个环境维护单独的配置文件 + +## ❓ 常见问题 + +**Q: 构建后的程序还能通过环境变量修改地址吗?** +A: 可以!运行时环境变量的优先级最高,会覆盖构建时的配置。 + +**Q: 如何验证程序使用的是哪个地址?** +A: 可以通过抓包工具(如 Charles、Wireshark)查看实际请求的地址。 + +**Q: 可以不同平台使用不同配置吗?** +A: 可以,在构建特定平台前临时修改 `.build.config` 或使用环境变量。 + +**Q: 配置文件支持注释吗?** +A: 支持!使用 `#` 开头的行会被忽略。 \ No newline at end of file diff --git a/MAC_INSTALL_GUIDE.md b/MAC_INSTALL_GUIDE.md new file mode 100644 index 0000000..4f728f8 --- /dev/null +++ b/MAC_INSTALL_GUIDE.md @@ -0,0 +1,237 @@ +# AntiHook macOS 安装和使用指南 + +## 📦 快速安装 + +### 步骤 1: 运行构建的程序 + +```bash +# 进入项目目录 +cd /Users/xswu/work/project/code/AntiHook + +# 根据您的 Mac 芯片选择对应版本: + +# Intel Mac (x86_64) +./build/antihook-darwin-amd64 + +# Apple Silicon Mac (M1/M2/M3) +./build/antihook-darwin-arm64 +``` + +**首次运行时**,macOS 可能会提示"无法验证开发者",请按以下步骤操作: + +1. 打开 **系统设置** → **隐私与安全性** +2. 在"安全性"部分找到被阻止的 antihook 程序 +3. 点击"仍要打开" +4. 在弹出的对话框中确认"打开" + +### 步骤 2: 完成安装 + +程序运行后会自动: +1. ✅ 将自身复制到 `~/.local/bin/Antihub/` +2. ✅ 注册 `kiro://` 和 `anti://` 协议处理器 +3. ✅ 添加到系统 PATH(需要重启终端生效) +4. ✅ 显示"Hooked successfully!"提示 + +## 🔧 如何 Hook Kiro 协议 + +### Hook 原理 + +AntiHook 会接管 `kiro://` 协议的处理,当您在浏览器中点击 `kiro://` 链接时: + +1. **原始流程**:浏览器 → Kiro 官方应用 +2. **Hook 后**:浏览器 → AntiHook → 您的后端服务器 + +### 协议处理流程 + +``` +用户点击授权 + ↓ +浏览器打开: https://prod.us-east-1.auth.desktop.kiro.dev/login... + ↓ +用户完成 Google 授权 + ↓ +浏览器重定向: kiro://kiro.kiroAgent/authenticate-success?code=xxx + ↓ +macOS 调用 AntiHook 处理 kiro:// 协议 + ↓ +AntiHook 将完整 URL 发送到: https://api.mortis.edu.kg/api/kiro/oauth/callback + ↓ +后端处理登录逻辑 + ↓ +显示"Login successful!" +``` + +## 🧪 测试 Hook 是否生效 + +### 方法 1: 直接测试协议 + +```bash +# 手动触发 kiro:// 协议 +open "kiro://test-callback?code=test123&state=test456" +``` + +**预期结果**: +- 终端输出日志:`Received kiro:// callback: kiro://test-callback?code=test123...` +- 弹出对话框:显示登录状态 + +### 方法 2: 检查协议注册 + +```bash +# 查看已注册的协议处理器 +defaults read ~/Library/Preferences/com.apple.LaunchServices/com.apple.launchservices.secure.plist | grep -A 5 "kiro" +``` + +### 方法 3: 查看已安装的文件 + +```bash +# 检查程序是否安装到正确位置 +ls -la ~/.local/bin/Antihub/antihook + +# 检查是否可执行 +~/.local/bin/Antihub/antihook --help +``` + +## 📝 使用说明 + +### 正常使用流程 + +1. **打开需要登录的应用或网站** +2. **点击"使用 Kiro 登录"按钮** +3. **浏览器会打开授权页面** +4. **完成 Google 授权** +5. **浏览器会重定向到 `kiro://` 协议** +6. **AntiHook 自动接管处理** +7. **显示"Login successful!"** + +### 查看调试日志 + +程序会在终端输出详细日志: + +```bash +# 在终端中运行(可以看到日志) +~/.local/bin/Antihub/antihook "kiro://your-callback-url" + +# 日志示例: +Received kiro:// callback: kiro://kiro.kiroAgent/authenticate-success?code=xxx +Posting to: https://api.mortis.edu.kg/api/kiro/oauth/callback +Request body: {"callback_url":"kiro://..."} +Response status: 200 +Response body: {...} +Login successful! +``` + +## 🔄 重新安装或更新 + +```bash +# 1. 重新运行安装 +./build/antihook-darwin-amd64 + +# 2. 如果需要清理旧版本 +rm -rf ~/.local/bin/Antihub/antihook + +# 3. 重新安装 +./build/antihook-darwin-amd64 +``` + +## ⚠️ 常见问题 + +### Q1: 点击 kiro:// 链接没有反应? + +**解决方案**: +```bash +# 1. 重新注册协议 +./build/antihook-darwin-amd64 + +# 2. 重启浏览器 + +# 3. 检查程序是否有执行权限 +chmod +x ~/.local/bin/Antihub/antihook +``` + +### Q2: 提示"无法打开,因为无法验证开发者"? + +**解决方案**: +```bash +# 移除 macOS 的隔离属性 +xattr -d com.apple.quarantine ./build/antihook-darwin-amd64 + +# 或者通过系统设置允许 +# 系统设置 → 隐私与安全性 → 点击"仍要打开" +``` + +### Q3: 如何恢复原始的 Kiro 处理器? + +**解决方案**: +```bash +# 卸载 AntiHook 的协议注册 +# 方法1: 删除程序(系统会自动清理) +rm -rf ~/.local/bin/Antihub/ + +# 方法2: 重新安装 Kiro 官方应用 +# Kiro 会重新注册协议处理器 +``` + +### Q4: 登录时一直卡在授权页面? + +**解决方案**: +1. **查看终端日志**,确认是否收到回调 +2. **检查后端地址**是否正确: + ```bash + # 查看当前配置 + strings ~/.local/bin/Antihub/antihook | grep "mortis.edu.kg" + ``` +3. **测试后端连接**: + ```bash + curl -v https://api.mortis.edu.kg/api/kiro/oauth/callback + ``` +4. 参考 [`TROUBLESHOOTING.md`](TROUBLESHOOTING.md) 详细排查 + +## 🎯 高级配置 + +### 使用自定义后端地址 + +如果需要临时使用不同的后端地址: + +```bash +# 设置环境变量 +export KIRO_SERVER_URL="https://your-server.com" + +# 然后触发协议 +open "kiro://your-callback" +``` + +### 查看程序版本信息 + +```bash +strings ~/.local/bin/Antihub/antihook | grep -E "BuildVersion|BuildTime" +``` + +## 🚀 完整测试流程 + +```bash +# 1. 安装 +./build/antihook-darwin-amd64 + +# 2. 验证安装 +ls -la ~/.local/bin/Antihub/antihook + +# 3. 测试协议(会看到详细日志) +~/.local/bin/Antihub/antihook "kiro://test?code=abc123" + +# 4. 查看日志输出,确认是否正常工作 +``` + +## 📞 获取帮助 + +如果遇到问题: +1. 查看终端的详细日志输出 +2. 参考 [`TROUBLESHOOTING.md`](TROUBLESHOOTING.md) +3. 检查 `~/.local/bin/Antihub/` 目录权限 +4. 确认后端服务器地址可访问 + +--- + +**当前配置**: +- KIRO_SERVER_URL: `https://api.mortis.edu.kg` +- BACKEND_URL: `https://tunnel.mortis.edu.kg` +- 构建版本: `1.0.0` \ No newline at end of file diff --git a/MIGRATION_SUMMARY.md b/MIGRATION_SUMMARY.md new file mode 100644 index 0000000..33a4a84 --- /dev/null +++ b/MIGRATION_SUMMARY.md @@ -0,0 +1,272 @@ +# AntiHook macOS 适配总结 + +## 项目概述 + +已成功将 AntiHook 项目从 Windows 专用工具改造为跨平台应用,现在支持 macOS、Windows 和 Linux。 + +## 主要改动 + +### 1. 文件结构变化 + +#### 新增文件 + +- **`registry/registry_darwin.go`** - macOS 协议注册实现 + - 使用 AppleScript 创建协议处理器应用 + - 支持 duti 工具设置默认处理器 + - 协议处理器位置:`~/.config/antihook/` + +- **`main_darwin.go`** - macOS 平台特定实现 + - 使用 `osascript` 显示对话框 + - 使用 `open` 命令打开浏览器 + - 自动配置 shell RC 文件(.zshrc/.bash_profile) + +- **`main_windows.go`** - Windows 平台特定实现 + - 从原 main.go 分离的 Windows 特定代码 + - MessageBox、注册表操作等 + +- **`build.sh`** - 跨平台构建脚本 + - 支持构建 macOS (Intel/ARM)、Windows、Linux 版本 + - 自动检测当前平台 + - 统一输出到 `build/` 目录 + +- **`install_mac.sh`** - macOS 快速安装脚本 + - 自动检测 CPU 架构(Intel/Apple Silicon) + - 一键安装和配置 + - 自动配置环境变量 + +#### 修改文件 + +- **`registry/registry.go`** → **`registry/registry_windows.go`** + - 重命名并添加 Windows 构建标签 + - 保持原有 Windows 注册表实现不变 + +- **`main.go`** + - 移除所有平台特定代码 + - 保留跨平台通用逻辑 + - 修改安装路径为跨平台兼容 + - 移除 Windows 特定导入 + +- **`.gitignore`** + - 添加构建产物忽略规则 + - 添加 macOS 特定文件忽略 + +- **`README.md`** + - 完全重写,添加详细的跨平台使用说明 + - 包含安装、配置、故障排除等完整文档 + +### 2. 技术实现差异 + +#### Windows 实现 +- **协议注册**: Windows 注册表 +- **消息框**: Win32 API (MessageBoxW) +- **浏览器**: rundll32 +- **安装路径**: `%LOCALAPPDATA%\Antihub\` +- **PATH 配置**: 用户环境变量注册表 + +#### macOS 实现 +- **协议注册**: AppleScript 应用 + duti +- **消息框**: osascript 对话框 +- **浏览器**: open 命令 +- **安装路径**: `~/.local/bin/Antihub/` +- **PATH 配置**: Shell RC 文件 (.zshrc/.bash_profile) + +### 3. 构建标签使用 + +使用 Go 的构建标签实现平台特定代码分离: + +```go +// +build darwin +// macOS 专用代码 + +// +build windows +// Windows 专用代码 +``` + +## 使用方法 + +### macOS 快速安装 + +```bash +# 克隆仓库 +git clone +cd AntiHook + +# 方法1: 使用快速安装脚本(推荐) +./install_mac.sh + +# 方法2: 手动构建和安装 +./build.sh darwin +sudo cp build/antihook-darwin-arm64 /usr/local/bin/antihook # Apple Silicon +sudo cp build/antihook-darwin-amd64 /usr/local/bin/antihook # Intel +./antihook +``` + +### Windows 安装 + +```bash +# 构建 +./build.sh windows + +# 运行安装 +./build/antihook-windows-amd64.exe +``` + +### 构建所有平台 + +```bash +./build.sh all +``` + +## 关键特性 + +### ✅ 已实现 + +1. **跨平台协议注册** + - macOS: AppleScript + duti + - Windows: 注册表 + - Linux: 可扩展 + +2. **统一的 OAuth 流程** + - 所有平台使用相同的 OAuth 逻辑 + - 本地 HTTP 服务器端口 42532 + - 超时和错误处理 + +3. **自动化安装** + - macOS: install_mac.sh + - Windows: 双击运行 exe + - 自动配置 PATH + +4. **构建系统** + - 统一构建脚本 + - 支持交叉编译 + - 自动检测架构 + +### 🔄 平台差异 + +| 功能 | Windows | macOS | Linux | +|------|---------|-------|-------| +| 协议注册 | ✅ 注册表 | ✅ AppleScript | ⚠️ 需实现 | +| 消息提示 | ✅ MessageBox | ✅ osascript | ⚠️ 需实现 | +| 浏览器启动 | ✅ rundll32 | ✅ open | ⚠️ 需实现 | +| PATH 配置 | ✅ 注册表 | ✅ RC 文件 | ⚠️ 需实现 | + +## macOS 特别说明 + +### 依赖建议 + +```bash +# 安装 duti 以获得更好的协议处理 +brew install duti +``` + +### 协议处理器位置 + +``` +~/.config/antihook/kiro_handler.app +~/.config/antihook/anti_handler.app +``` + +### 首次使用 + +1. 运行安装程序后需要重启浏览器 +2. 首次点击协议链接时系统会询问确认 +3. 选择"始终允许"以获得最佳体验 + +## 测试清单 + +### macOS 测试 + +- [ ] 在 Intel Mac 上构建和安装 +- [ ] 在 Apple Silicon Mac 上构建和安装 +- [ ] 测试 kiro:// 协议处理 +- [ ] 测试 anti:// 协议处理 +- [ ] 验证 PATH 配置 +- [ ] 测试 duti 集成 +- [ ] 验证环境变量支持 + +### Windows 测试 + +- [ ] 在 Windows 上构建和安装 +- [ ] 测试协议注册 +- [ ] 测试 --recover 功能 +- [ ] 验证注册表配置 + +### 通用测试 + +- [ ] OAuth 流程完整性 +- [ ] 错误处理 +- [ ] 超时机制 +- [ ] 网络请求 +- [ ] 环境变量读取 + +## 潜在改进 + +### 短期 + +1. **Linux 支持完善** + - 实现 XDG 桌面文件协议注册 + - 添加 notify-send 消息提示 + - 使用 xdg-open 打开浏览器 + +2. **错误处理增强** + - 添加日志文件支持 + - 更详细的错误信息 + - 崩溃恢复机制 + +3. **配置文件支持** + - YAML/JSON 配置文件 + - 默认服务器地址 + - 自定义端口号 + +### 长期 + +1. **GUI 安装程序** + - macOS: .app 包 + DMG + - Windows: NSIS 安装程序 + - Linux: .deb/.rpm 包 + +2. **自动更新** + - 版本检查 + - 自动下载更新 + - 增量更新 + +3. **多语言支持** + - i18n 国际化 + - 中文/英文界面 + +## 迁移注意事项 + +### 破坏性变更 + +1. **安装路径变化** + - Windows: `%LOCALAPPDATA%\Antihub\antihook.exe` + - macOS: `~/.local/bin/Antihub/antihook` + +2. **可执行文件名** + - Windows: 保持 `antihook.exe` + - macOS/Linux: 改为 `antihook`(无扩展名) + +3. **配置位置** + - macOS 协议处理器在 `~/.config/antihook/` + +### 兼容性 + +- 保持与现有服务器 API 完全兼容 +- OAuth 流程不变 +- 环境变量配置方式相同 + +## 文档 + +- **README.md**: 完整的用户文档 +- **MIGRATION_SUMMARY.md**: 本文档,技术迁移总结 +- 代码注释已更新以反映跨平台特性 + +## 总结 + +AntiHook 现在是一个真正的跨平台应用,通过合理的代码组织和构建标签实现了平台特定功能的分离,同时保持了核心逻辑的统一。macOS 用户现在可以享受与 Windows 用户相同的便捷登录体验。 + +--- + +**版本**: 1.0.0 +**日期**: 2025-12-08 +**状态**: ✅ 完成 \ No newline at end of file diff --git a/README.md b/README.md index 71df5fb..95269cc 100644 --- a/README.md +++ b/README.md @@ -1 +1,466 @@ -# Auto login for AntiHub beta \ No newline at end of file +# AntiHook - 自动登录工具 + +AntiHook 是一个跨平台的协议处理器工具,支持 `kiro://` 和 `anti://` 自定义 URL 协议,实现 OAuth 自动登录流程。 + +## 支持平台 + +- ✅ macOS (Intel & Apple Silicon) +- ✅ Windows +- ✅ Linux + +## 功能特性 + +- 🔐 自动处理 OAuth 登录流程 +- 🌐 支持自定义协议 (`kiro://` 和 `anti://`) +- 🔄 自动注册协议处理器 +- 💻 跨平台支持 +- ⚡ 快速响应 +- 📦 **构建时自动配置后端地址** +- 📝 **详细的日志记录系统** +- 🎨 **静默运行,无中间弹框干扰** + +## 快速开始 + +### macOS 安装 + +#### 方法一:使用构建脚本 + +```bash +# 克隆仓库 +git clone +cd AntiHook + +# 构建 macOS 版本 +./build.sh darwin + +# 安装(构建后的二进制文件会在 build 目录) +# Intel Mac +sudo cp build/antihook-darwin-amd64 /usr/local/bin/antihook + +# Apple Silicon Mac +sudo cp build/antihook-darwin-arm64 /usr/local/bin/antihook + +# 运行安装程序(注册协议处理器) +antihook +``` + +#### 方法二:直接编译 + +```bash +# 克隆仓库 +git clone +cd AntiHook + +# 编译 +go build -o antihook . + +# 运行安装 +./antihook +``` + +#### 安装 duti(可选,用于更好的协议处理) + +```bash +brew install duti +``` + +### Windows 安装 + +```bash +# 克隆仓库 +git clone +cd AntiHook + +# 构建 Windows 版本 +./build.sh windows + +# 或者在 Windows 上直接编译 +go build -o antihook.exe . + +# 运行安装程序(将自动注册协议处理器) +./antihook.exe +``` + +### Linux 安装 + +```bash +# 克隆仓库 +git clone +cd AntiHook + +# 构建 +./build.sh linux + +# 安装 +sudo cp build/antihook-linux-amd64 /usr/local/bin/antihook + +# 运行安装 +antihook +``` + +## 使用说明 + +### 安装协议处理器 + +直接运行程序即可自动安装: + +```bash +# macOS/Linux +./antihook + +# Windows +antihook.exe +``` + +安装后会: +1. 将程序复制到系统目录 +2. 注册 `kiro://` 和 `anti://` 协议处理器 +3. 添加到系统 PATH(可选) + +### 协议使用 + +#### Kiro 协议 + +``` +kiro://your-callback-url +``` + +程序会自动将回调 URL 转发到服务器完成登录。 + +#### Anti 协议 + +``` +anti://?identity=your-bearer-token&is_shared=0 +``` + +参数说明: +- `identity`: Bearer token(可以不带 "Bearer " 前缀) +- `is_shared`: 是否共享(0 或 1,默认 0) + +### 恢复原始处理器(仅 Windows) + +```bash +antihook.exe --recover +``` + +### 🆕 构建时配置后端地址(新功能) + +AntiHook 支持在构建时自动注入后端地址,打包后的程序会自动使用您配置的生产环境地址,**无需用户手动配置**。 + +#### 什么是"打包/构建"? + +**打包(Build)** 是将源代码编译成可执行文件的过程。在这个过程中,您配置的后端地址会被自动注入到程序中。 + +``` +源代码 + 配置文件 → 构建脚本 → 可执行文件(已包含配置) +``` + +#### 方法一:使用配置文件(推荐) + +**适用场景**:发布生产版本,配置会永久保存 + +1. 复制配置文件模板: +```bash +cp .build.config.example .build.config +``` + +2. 编辑 `.build.config` 文件,设置您的后端地址: +```bash +# Kiro 服务器地址(用于 kiro:// 协议回调) +SERVER_URL="https://tunnel.mortis.edu.kg" + +# 后端服务器地址(用于 anti:// 协议 OAuth) +BACKEND_URL="https://tunnel.mortis.edu.kg" +``` + +3. 运行构建,配置会自动注入: +```bash +./build.sh all +``` + +**完成后**: +- ✅ 生成的可执行文件已包含您的后端地址 +- ✅ 用户运行时无需任何配置 +- ✅ 自动连接到您指定的服务器 + +#### 方法二:使用临时环境变量 + +**适用场景**:测试不同环境,不影响配置文件 + +```bash +# 临时设置环境变量并构建 +SERVER_URL="https://test-server.com" \ +BACKEND_URL="https://test-backend.com" \ +./build.sh all +``` + +#### 方法三:运行时环境变量(兼容旧版) + +**适用场景**:调试或临时切换服务器 + +```bash +# Kiro 服务器地址(默认: http://localhost:8045) +export KIRO_SERVER_URL="https://your-kiro-server.com" + +# 后端服务器地址(默认: http://localhost:8008) +export BACKEND_URL="https://your-backend-server.com" +``` + +**注意**:运行时环境变量优先级最高,会覆盖构建时的配置。 + +#### 验证配置是否生效 + +构建完成后,验证配置是否正确注入: + +```bash +# macOS/Linux - 检查配置是否注入 +strings build/antihook-darwin-amd64 | grep "https" + +# 应该能看到您配置的地址 +# https://tunnel.mortis.edu.kg + +# Windows - 使用 PowerShell +Select-String -Path build/antihook-windows-amd64.exe -Pattern "https://" -AllMatches +``` + +## 构建说明 + +### 构建所有平台 + +```bash +./build.sh all +``` + +### 构建特定平台 + +```bash +# macOS +./build.sh darwin + +# Windows +./build.sh windows + +# Linux +./build.sh linux +``` + +构建产物位于 `build/` 目录: +- `antihook-darwin-amd64` - macOS Intel +- `antihook-darwin-arm64` - macOS Apple Silicon +- `antihook-windows-amd64.exe` - Windows 64位 +- `antihook-linux-amd64` - Linux 64位 + +## 技术架构 + +### 目录结构 + +``` +AntiHook/ +├── main.go # 主程序(跨平台核心逻辑) +├── main_darwin.go # macOS 特定实现 +├── main_windows.go # Windows 特定实现 +├── registry/ +│ ├── registry_darwin.go # macOS 协议注册 +│ └── registry_windows.go # Windows 协议注册 +├── build.sh # 构建脚本 +├── go.mod # Go 模块定义 +└── README.md # 说明文档 +``` + +### 工作流程 + +1. **协议注册** + - macOS: 使用 AppleScript 创建协议处理器 + - Windows: 使用注册表注册协议 + +2. **OAuth 流程** + - 接收协议 URL + - 解析参数 + - 启动本地 HTTP 服务器(端口 42532) + - 打开浏览器进行授权 + - 接收回调完成登录 + +3. **API 接口** + - `POST /api/kiro/oauth/callback` - Kiro 回调 + - `POST /api/plugin-api/oauth/authorize` - Anti 授权 + - `POST /api/plugin-api/oauth/callback` - Anti 回调 + +## macOS 特别说明 + +### 协议处理器位置 + +macOS 版本会在以下位置创建协议处理器: + +``` +~/.config/antihook/kiro_handler.app +~/.config/antihook/anti_handler.app +``` + +### 首次使用提示 + +在 macOS 上首次点击 `kiro://` 或 `anti://` 链接时,系统会询问是否允许打开该应用。请选择"始终允许"以获得最佳体验。 + +### 浏览器刷新 + +注册协议后可能需要重启浏览器才能使新协议生效。 + +## 故障排除 + +### macOS 协议未生效 + +```bash +# 重新安装 +./antihook + +# 检查处理器是否存在 +ls -la ~/.config/antihook/ + +# 重启浏览器 +``` + +### Windows 协议未生效 + +```bash +# 以管理员权限运行 +# 右键 -> 以管理员身份运行 +antihook.exe + +# 检查注册表 +# 运行 regedit,查看 HKEY_CURRENT_USER\Software\Classes\kiro +``` + +## 🆕 日志系统 + +### 自动日志记录 + +所有操作都会自动记录到日志文件中,方便调试和排查问题: + +**日志文件位置**:`~/.config/antihook/kiro.log` + +**日志内容示例**: +``` +=== 2025-12-08 15:02:22 === +Received kiro:// callback: kiro://kiro.kiroAgent/authenticate-success?code=xxx&state=yyy +Posting to: https://tunnel.mortis.edu.kg/api/kiro/oauth/callback +Request body: {"callback_url":"kiro://..."} +Response status: 200 +Response body: {"success":true} +Login successful! +``` + +### 查看日志 + +```bash +# 查看完整日志 +cat ~/.config/antihook/kiro.log + +# 实时监控日志 +tail -f ~/.config/antihook/kiro.log + +# 查看最后 20 行 +tail -20 ~/.config/antihook/kiro.log +``` + +### 清理日志 + +```bash +# 清空日志文件 +> ~/.config/antihook/kiro.log + +# 删除日志文件 +rm ~/.config/antihook/kiro.log +``` + +## 🎨 用户体验优化 + +### 静默运行模式 + +程序在处理登录时不会显示中间过程弹框,只在以下情况显示提示: + +- ✅ **登录成功**:显示"Login successful!" +- ❌ **登录失败**:显示具体错误信息 +- 🔄 **处理中**:静默处理,无弹框干扰 + +这样可以提供更流畅的用户体验,避免不必要的打扰。 + +### 查看构建信息 + +构建后的程序包含了构建时的配置信息: + +```bash +# macOS/Linux - 查看所有 HTTP(S) 地址 +strings build/antihook-darwin-amd64 | grep "https" + +# 查看构建版本和时间 +strings build/antihook-darwin-amd64 | grep -E "BuildVersion|BuildTime" +``` + +## 🆕 完整的文档体系 + +- **[BUILD_GUIDE.md](BUILD_GUIDE.md)** - 详细的构建配置指南和多环境部署 +- **[MAC_INSTALL_GUIDE.md](MAC_INSTALL_GUIDE.md)** - macOS 完整安装和使用教程 +- **[TROUBLESHOOTING.md](TROUBLESHOOTING.md)** - 常见问题排查和解决方案 +- **[API_ISSUE_REPORT.md](API_ISSUE_REPORT.md)** - API 问题诊断报告 + +## 故障排查 + +### 端口冲突 + +如果端口 42532 被占用,请关闭占用该端口的程序: + +```bash +# macOS/Linux +lsof -i :42532 +kill + +# Windows +netstat -ano | findstr :42532 +taskkill /PID /F +``` + +## 开发 + +### 依赖安装 + +```bash +go mod download +``` + +### 本地测试 + +```bash +# 编译 +go build -o antihook . + +# 测试协议 +./antihook "kiro://test" +./antihook "anti://?identity=test-token&is_shared=0" +``` + +### 添加新平台支持 + +1. 创建 `main_.go` 文件 +2. 实现平台特定函数: + - `showMessageBox()` + - `addToPath()` + - `recoverOriginal()` + - `openBrowser()` +3. 创建 `registry/registry_.go` +4. 实现 `ProtocolHandler` 接口 +5. 更新构建脚本 + +## 许可证 + +[添加许可证信息] + +## 贡献 + +欢迎提交 Issue 和 Pull Request! + +## 更新日志 + +### v1.0.0 +- ✨ 初始版本 +- ✅ 支持 macOS、Windows、Linux +- 🔐 实现 OAuth 自动登录 +- 📦 提供跨平台构建脚本 \ No newline at end of file diff --git a/TROUBLESHOOTING.md b/TROUBLESHOOTING.md new file mode 100644 index 0000000..0ca9b36 --- /dev/null +++ b/TROUBLESHOOTING.md @@ -0,0 +1,178 @@ +# AntiHook 故障排查指南 + +## 问题:登录授权页面卡住 + +如果在使用 `anti://` 协议登录时,浏览器一直停留在授权页面无法完成登录,请按以下步骤排查: + +### 1. 检查回调服务器端口 + +OAuth 回调需要在本地 `42532` 端口启动服务器。 + +**检查端口是否被占用:** + +```bash +# macOS/Linux +lsof -i :42532 + +# Windows +netstat -ano | findstr :42532 +``` + +**如果端口被占用,请关闭占用进程:** + +```bash +# macOS/Linux +kill + +# Windows +taskkill /PID /F +``` + +### 2. 检查防火墙设置 + +确保防火墙允许本地 `42532` 端口的连接。 + +**macOS:** +- 系统设置 → 网络 → 防火墙 → 防火墙选项 +- 确保允许 antihook 的传入连接 + +**Windows:** +- 控制面板 → Windows Defender 防火墙 → 允许应用通过防火墙 +- 添加 antihook.exe 到允许列表 + +### 3. 检查后端 API 地址 + +确认后端地址是否正确配置并且可访问。 + +**当前配置的地址:** +```bash +KIRO_SERVER_URL: https://api.mortis.edu.kg +BACKEND_URL: https://tunnel.mortis.edu.kg +``` + +**测试后端连接:** + +```bash +# 测试授权接口 +curl -v -X POST https://tunnel.mortis.edu.kg/api/plugin-api/oauth/authorize \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer YOUR_TOKEN" \ + -d '{"is_shared": 0}' +``` + +如果返回错误,说明后端地址配置有问题。 + +### 4. 查看详细日志 + +最新版本已添加调试日志。运行程序时会在终端输出: + +``` +Received OAuth callback: http://localhost:42532/oauth-callback?code=xxx&state=yyy +``` + +**如果没有看到这条日志:** +- 说明浏览器没有成功回调到本地服务器 +- 检查浏览器是否阻止了 localhost 重定向 +- 尝试在浏览器中手动访问 `http://localhost:42532/oauth-callback` + +### 5. 常见问题解决方案 + +#### 问题 A:浏览器显示"无法连接到 localhost:42532" + +**原因:** 回调服务器未启动或启动失败 + +**解决方案:** +1. 确认没有其他程序占用 42532 端口 +2. 以管理员/root 权限运行程序 +3. 检查系统日志查看错误信息 + +#### 问题 B:浏览器完成授权但没有弹出"登录成功"提示 + +**原因:** 回调 URL 构造不正确或后端接口返回错误 + +**解决方案:** +1. 检查终端输出的回调 URL 是否完整 +2. 确认后端 `/api/plugin-api/oauth/callback` 接口正常 +3. 查看后端日志了解详细错误 + +#### 问题 C:显示"OAuth timeout"错误 + +**原因:** 授权超时(默认超时时间由后端 `expires_in` 字段控制) + +**解决方案:** +1. 加快授权操作速度 +2. 联系后端管理员增加超时时间 +3. 检查网络连接是否稳定 + +### 6. 手动测试 OAuth 流程 + +可以手动测试完整的 OAuth 流程: + +```bash +# 1. 获取授权 URL +curl -X POST https://tunnel.mortis.edu.kg/api/plugin-api/oauth/authorize \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer YOUR_TOKEN" \ + -d '{"is_shared": 0}' + +# 响应示例: +# { +# "success": true, +# "data": { +# "auth_url": "https://...", +# "state": "...", +# "expires_in": 300 +# } +# } + +# 2. 在浏览器中打开 auth_url + +# 3. 完成授权后,浏览器会重定向到: +# http://localhost:42532/oauth-callback?code=xxx&state=yyy + +# 4. 程序会自动调用回调接口: +curl -X POST https://tunnel.mortis.edu.kg/api/plugin-api/oauth/callback \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer YOUR_TOKEN" \ + -d '{"callback_url": "http://localhost:42532/oauth-callback?code=xxx&state=yyy"}' +``` + +### 7. 版本更新说明 + +**v1.0.1 修复内容:** +- ✅ 修复回调 URL 构造问题(使用 `RequestURI()` 而不是 `String()`) +- ✅ 增加服务器启动等待时间(100ms → 500ms) +- ✅ 添加调试日志输出 +- ✅ 改进错误处理 + +**如果问题仍然存在:** + +请提供以下信息以便进一步诊断: +1. 操作系统和版本 +2. 使用的是哪个协议(`kiro://` 还是 `anti://`) +3. 终端输出的完整日志 +4. 浏览器控制台的错误信息(F12 → Console) +5. 后端服务器的日志 + +### 8. 临时解决方案 + +如果急需使用,可以尝试: + +1. **使用环境变量覆盖配置:** + ```bash + export BACKEND_URL="https://your-working-backend.com" + ./antihook "anti://?identity=YOUR_TOKEN&is_shared=0" + ``` + +2. **直接使用 kiro:// 协议(如果适用):** + ```bash + ./antihook "kiro://your-callback-url" + ``` + +3. **检查是否有代理或 VPN 干扰:** + - 临时关闭代理/VPN + - 添加 localhost 到代理例外列表 + +## 联系支持 + +如果以上方法都无法解决问题,请提供详细的错误信息和日志,我们会尽快协助解决。 \ No newline at end of file diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..0edf4fc --- /dev/null +++ b/build.sh @@ -0,0 +1,127 @@ +#!/bin/bash + +# AntiHook 构建脚本 +# 支持 macOS 和 Windows 平台 + +set -e + +VERSION="1.0.0" +BUILD_DIR="build" + +# 默认配置(开发环境) +DEFAULT_SERVER_URL="http://localhost:8045" +DEFAULT_BACKEND_URL="http://localhost:8008" + +# 读取配置文件(如果存在) +CONFIG_FILE=".build.config" +if [ -f "$CONFIG_FILE" ]; then + echo "Loading configuration from $CONFIG_FILE..." + source "$CONFIG_FILE" +fi + +# 环境变量优先级最高 +SERVER_URL="${SERVER_URL:-$DEFAULT_SERVER_URL}" +BACKEND_URL="${BACKEND_URL:-$DEFAULT_BACKEND_URL}" + +# 颜色输出 +GREEN='\033[0;32m' +BLUE='\033[0;34m' +YELLOW='\033[0;33m' +NC='\033[0m' # No Color + +echo -e "${BLUE}AntiHook Build Script${NC}" +echo "Version: $VERSION" +echo -e "${YELLOW}Server URL: $SERVER_URL${NC}" +echo -e "${YELLOW}Backend URL: $BACKEND_URL${NC}" +echo "" + +# 创建构建目录 +mkdir -p "$BUILD_DIR" + +# 检测当前操作系统 +OS="$(uname -s)" +case "${OS}" in + Linux*) MACHINE=Linux;; + Darwin*) MACHINE=Mac;; + CYGWIN*) MACHINE=Cygwin;; + MINGW*) MACHINE=MinGw;; + *) MACHINE="UNKNOWN:${OS}" +esac + +echo "Detected OS: $MACHINE" +echo "" + +# 获取构建时间 +BUILD_TIME=$(date -u +"%Y-%m-%dT%H:%M:%SZ") + +# 构建 ldflags +LDFLAGS="-s -w" +LDFLAGS="$LDFLAGS -X 'main.DefaultServerURL=$SERVER_URL'" +LDFLAGS="$LDFLAGS -X 'main.DefaultBackendURL=$BACKEND_URL'" +LDFLAGS="$LDFLAGS -X 'main.BuildVersion=$VERSION'" +LDFLAGS="$LDFLAGS -X 'main.BuildTime=$BUILD_TIME'" + +# 构建 macOS 版本 +build_darwin() { + echo -e "${GREEN}Building for macOS (Intel)...${NC}" + GOOS=darwin GOARCH=amd64 go build -ldflags="$LDFLAGS" -o "$BUILD_DIR/antihook-darwin-amd64" . + echo "✓ Built: $BUILD_DIR/antihook-darwin-amd64" + + echo -e "${GREEN}Building for macOS (ARM64)...${NC}" + GOOS=darwin GOARCH=arm64 go build -ldflags="$LDFLAGS" -o "$BUILD_DIR/antihook-darwin-arm64" . + echo "✓ Built: $BUILD_DIR/antihook-darwin-arm64" +} + +# 构建 Windows 版本 +build_windows() { + echo -e "${GREEN}Building for Windows (amd64)...${NC}" + GOOS=windows GOARCH=amd64 go build -ldflags="$LDFLAGS" -o "$BUILD_DIR/antihook-windows-amd64.exe" . + echo "✓ Built: $BUILD_DIR/antihook-windows-amd64.exe" +} + +# 构建 Linux 版本 +build_linux() { + echo -e "${GREEN}Building for Linux (amd64)...${NC}" + GOOS=linux GOARCH=amd64 go build -ldflags="$LDFLAGS" -o "$BUILD_DIR/antihook-linux-amd64" . + echo "✓ Built: $BUILD_DIR/antihook-linux-amd64" +} + +# 根据参数构建 +case "$1" in + darwin|mac|macos) + build_darwin + ;; + windows|win) + build_windows + ;; + linux) + build_linux + ;; + all) + build_darwin + build_windows + build_linux + ;; + *) + echo "Usage: $0 {darwin|windows|linux|all}" + echo "" + echo "Building for current platform ($MACHINE)..." + case "${MACHINE}" in + Mac) + build_darwin + ;; + Linux) + build_linux + ;; + *) + echo "Unsupported platform for auto-detection. Please specify target platform." + exit 1 + ;; + esac + ;; +esac + +echo "" +echo -e "${GREEN}Build completed!${NC}" +echo "Output directory: $BUILD_DIR" +ls -lh "$BUILD_DIR" \ No newline at end of file diff --git a/install_mac.sh b/install_mac.sh new file mode 100755 index 0000000..66c86eb --- /dev/null +++ b/install_mac.sh @@ -0,0 +1,117 @@ +#!/bin/bash + +# AntiHook macOS 快速安装脚本 + +set -e + +# 颜色定义 +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +echo -e "${BLUE}================================${NC}" +echo -e "${BLUE} AntiHook macOS 安装程序${NC}" +echo -e "${BLUE}================================${NC}" +echo "" + +# 检测架构 +ARCH=$(uname -m) +if [ "$ARCH" = "arm64" ]; then + echo -e "${GREEN}✓ 检测到 Apple Silicon (ARM64)${NC}" + BINARY="antihook-darwin-arm64" +elif [ "$ARCH" = "x86_64" ]; then + echo -e "${GREEN}✓ 检测到 Intel (x86_64)${NC}" + BINARY="antihook-darwin-amd64" +else + echo -e "${RED}✗ 不支持的架构: $ARCH${NC}" + exit 1 +fi + +# 检查是否已构建 +if [ ! -d "build" ]; then + echo -e "${YELLOW}! 未找到构建目录,开始构建...${NC}" + ./build.sh darwin +fi + +# 检查二进制文件 +if [ ! -f "build/$BINARY" ]; then + echo -e "${RED}✗ 未找到构建文件: build/$BINARY${NC}" + echo "请先运行: ./build.sh darwin" + exit 1 +fi + +echo "" +echo -e "${BLUE}安装步骤:${NC}" +echo "" + +# 1. 创建目标目录 +INSTALL_DIR="$HOME/.local/bin/Antihub" +echo -e "${YELLOW}[1/4]${NC} 创建安装目录: $INSTALL_DIR" +mkdir -p "$INSTALL_DIR" + +# 2. 复制可执行文件 +echo -e "${YELLOW}[2/4]${NC} 复制可执行文件" +cp "build/$BINARY" "$INSTALL_DIR/antihook" +chmod +x "$INSTALL_DIR/antihook" + +# 3. 添加到 PATH +echo -e "${YELLOW}[3/4]${NC} 配置环境变量" + +# 检测使用的 shell +SHELL_NAME=$(basename "$SHELL") +if [ "$SHELL_NAME" = "zsh" ]; then + RC_FILE="$HOME/.zshrc" +elif [ "$SHELL_NAME" = "bash" ]; then + RC_FILE="$HOME/.bash_profile" +else + RC_FILE="$HOME/.profile" +fi + +# 检查是否已添加 +if ! grep -q "$INSTALL_DIR" "$RC_FILE" 2>/dev/null; then + echo "" >> "$RC_FILE" + echo "# Added by AntiHook" >> "$RC_FILE" + echo "export PATH=\"$INSTALL_DIR:\$PATH\"" >> "$RC_FILE" + echo -e "${GREEN} ✓ 已添加到 $RC_FILE${NC}" +else + echo -e "${GREEN} ✓ PATH 已配置${NC}" +fi + +# 4. 注册协议处理器 +echo -e "${YELLOW}[4/4]${NC} 注册协议处理器" +"$INSTALL_DIR/antihook" + +echo "" +echo -e "${GREEN}================================${NC}" +echo -e "${GREEN} ✓ 安装完成!${NC}" +echo -e "${GREEN}================================${NC}" +echo "" +echo -e "${BLUE}后续步骤:${NC}" +echo "" +echo "1. 重新加载 shell 配置:" +echo -e " ${YELLOW}source $RC_FILE${NC}" +echo "" +echo "2. 或者重启终端" +echo "" +echo "3. 验证安装:" +echo -e " ${YELLOW}antihook --help${NC}" +echo "" + +# 检查 duti +if ! command -v duti &> /dev/null; then + echo -e "${YELLOW}提示: 建议安装 duti 以获得更好的协议处理体验${NC}" + echo -e " ${YELLOW}brew install duti${NC}" + echo "" +fi + +echo -e "${BLUE}配置环境变量 (可选):${NC}" +echo "" +echo "# Kiro 服务器地址" +echo "export KIRO_SERVER_URL=\"http://localhost:8045\"" +echo "" +echo "# 后端服务器地址" +echo "export BACKEND_URL=\"http://localhost:8008\"" +echo "" +echo -e "${GREEN}使用愉快!${NC}" \ No newline at end of file diff --git a/main.go b/main.go index a29243e..62ea536 100644 --- a/main.go +++ b/main.go @@ -10,15 +10,10 @@ import ( "net/http" "net/url" "os" - "os/exec" "path/filepath" "strings" "sync" - "syscall" "time" - "unsafe" - - "golang.org/x/sys/windows/registry" protocolRegistry "antihook/registry" ) @@ -27,14 +22,19 @@ const ( ProtocolDescription = "Kiro Protocol Handler" AntiProtocolDescription = "Anti Protocol Handler" TargetDirName = "Antihub" - ExeName = "antihook.exe" OAuthCallbackPort = 42532 ) -var DefaultServerURL = "http://localhost:8045" -var DefaultBackendURL = "http://localhost:8008" +// 这些变量可以在编译时通过 -ldflags 注入 +var ( + DefaultServerURL = "http://localhost:8045" + DefaultBackendURL = "http://localhost:8008" + BuildVersion = "dev" + BuildTime = "unknown" +) func init() { + // 环境变量优先级最高 if url := os.Getenv("KIRO_SERVER_URL"); url != "" { DefaultServerURL = url } @@ -78,13 +78,13 @@ func main() { } func install() error { - localAppData := os.Getenv("LOCALAPPDATA") - if localAppData == "" { - return fmt.Errorf("cannot get LOCALAPPDATA environment variable") + homeDir, err := os.UserHomeDir() + if err != nil { + return fmt.Errorf("failed to get home directory: %w", err) } - targetDir := filepath.Join(localAppData, TargetDirName) - targetPath := filepath.Join(targetDir, ExeName) + targetDir := filepath.Join(homeDir, ".local", "bin", TargetDirName) + targetPath := filepath.Join(targetDir, "antihook") currentPath, err := os.Executable() if err != nil { @@ -106,6 +106,11 @@ func install() error { if err := copyFile(currentPath, targetPath); err != nil { return fmt.Errorf("failed to copy file: %w", err) } + + // 确保可执行权限 + if err := os.Chmod(targetPath, 0755); err != nil { + return fmt.Errorf("failed to set executable permission: %w", err) + } } kiroHandler := &protocolRegistry.ProtocolHandler{ @@ -156,17 +161,46 @@ func copyFile(src, dst string) error { } func handleProtocolCall(rawURL string) { - go showMessageBox("Info", "Logging in...", 0x40) + // 创建日志文件 + homeDir, _ := os.UserHomeDir() + logFile, err := os.OpenFile(filepath.Join(homeDir, ".config", "antihook", "kiro.log"), os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644) + if err == nil { + defer logFile.Close() + logFile.WriteString(fmt.Sprintf("\n=== %s ===\n", time.Now().Format("2006-01-02 15:04:05"))) + logFile.WriteString(fmt.Sprintf("Received kiro:// callback: %s\n", rawURL)) + } + + // 记录接收到的回调 URL + fmt.Printf("Received kiro:// callback: %s\n", rawURL) + + // 移除了 "Logging in..." 弹框 if err := postCallback(rawURL); err != nil { + errMsg := fmt.Sprintf("Login failed: %v\n", err) + fmt.Printf(errMsg) + if logFile != nil { + logFile.WriteString(errMsg) + } showMessageBox("Error", "Login failed: "+err.Error(), 0x10) return } + successMsg := "Login successful!\n" + fmt.Printf(successMsg) + if logFile != nil { + logFile.WriteString(successMsg) + } showMessageBox("Success", "Login successful!", 0x40) } func postCallback(callbackURL string) error { + // 打开日志文件 + homeDir, _ := os.UserHomeDir() + logFile, _ := os.OpenFile(filepath.Join(homeDir, ".config", "antihook", "kiro.log"), os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644) + if logFile != nil { + defer logFile.Close() + } + requestBody := map[string]string{ "callback_url": callbackURL, } @@ -183,18 +217,48 @@ func postCallback(callbackURL string) error { apiURL := serverURL + "/api/kiro/oauth/callback" + // 记录详细的请求信息 + logMsg := fmt.Sprintf("Posting to: %s\n", apiURL) + fmt.Printf(logMsg) + if logFile != nil { + logFile.WriteString(logMsg) + } + + logMsg = fmt.Sprintf("Request body: %s\n", string(jsonData)) + fmt.Printf(logMsg) + if logFile != nil { + logFile.WriteString(logMsg) + } + resp, err := http.Post( apiURL, "application/json", bytes.NewBuffer(jsonData), ) if err != nil { + errMsg := fmt.Sprintf("HTTP request failed: %v\n", err) + if logFile != nil { + logFile.WriteString(errMsg) + } return fmt.Errorf("failed to send request: %w", err) } defer resp.Body.Close() + // 读取响应内容 + body, _ := io.ReadAll(resp.Body) + logMsg = fmt.Sprintf("Response status: %d\n", resp.StatusCode) + fmt.Printf(logMsg) + if logFile != nil { + logFile.WriteString(logMsg) + } + + logMsg = fmt.Sprintf("Response body: %s\n", string(body)) + fmt.Printf(logMsg) + if logFile != nil { + logFile.WriteString(logMsg) + } + if resp.StatusCode != http.StatusOK { - body, _ := io.ReadAll(resp.Body) return fmt.Errorf("server returned error: %d, %s", resp.StatusCode, string(body)) } @@ -221,7 +285,7 @@ func parseAntiProtocolURL(rawURL string) (*AntiProtocolParams, error) { withoutProtocol = strings.TrimPrefix(withoutProtocol, "ANTI://") parts := strings.SplitN(withoutProtocol, "?", 2) - + var bearer string isShared := 0 @@ -362,7 +426,11 @@ func startOAuthCallbackServer(ctx context.Context, callbackChan chan<- string, e mux := http.NewServeMux() mux.HandleFunc("/oauth-callback", func(w http.ResponseWriter, r *http.Request) { - callbackURL := fmt.Sprintf("http://localhost:%d%s", OAuthCallbackPort, r.URL.String()) + // 构造完整的回调 URL,包含所有查询参数 + callbackURL := fmt.Sprintf("http://localhost:%d%s", OAuthCallbackPort, r.URL.RequestURI()) + + // 记录日志(可选,用于调试) + fmt.Printf("Received OAuth callback: %s\n", callbackURL) w.Header().Set("Content-Type", "text/html; charset=utf-8") w.WriteHeader(http.StatusOK) @@ -421,15 +489,12 @@ func startOAuthCallbackServer(ctx context.Context, callbackChan chan<- string, e } }() - time.Sleep(100 * time.Millisecond) + // 等待更长时间确保服务器完全启动 + time.Sleep(500 * time.Millisecond) return server } -func openBrowser(url string) error { - return exec.Command("rundll32", "url.dll,FileProtocolHandler", url).Start() -} - func postOAuthCallbackManual(serverURL, bearer, callbackURL string) error { apiURL := serverURL + "/api/plugin-api/oauth/callback" @@ -464,71 +529,3 @@ func postOAuthCallbackManual(serverURL, bearer, callbackURL string) error { return nil } - -func showMessageBox(title, message string, flags uint) { - var mod = syscall.NewLazyDLL("user32.dll") - var proc = mod.NewProc("MessageBoxW") - - titlePtr, _ := syscall.UTF16PtrFromString(title) - messagePtr, _ := syscall.UTF16PtrFromString(message) - - proc.Call( - 0, - uintptr(unsafe.Pointer(messagePtr)), - uintptr(unsafe.Pointer(titlePtr)), - uintptr(flags), - ) -} - -func addToPath(dir string) error { - key, err := registry.OpenKey(registry.CURRENT_USER, `Environment`, registry.QUERY_VALUE|registry.SET_VALUE) - if err != nil { - return fmt.Errorf("failed to open Environment key: %w", err) - } - defer key.Close() - - currentPath, _, err := key.GetStringValue("Path") - if err != nil && err != registry.ErrNotExist { - return fmt.Errorf("failed to read PATH: %w", err) - } - - if strings.Contains(strings.ToLower(currentPath), strings.ToLower(dir)) { - return nil - } - - var newPath string - if currentPath == "" { - newPath = dir - } else { - newPath = currentPath + ";" + dir - } - - if err := key.SetStringValue("Path", newPath); err != nil { - return fmt.Errorf("failed to set PATH: %w", err) - } - - return nil -} - -func recoverOriginal() error { - localAppData := os.Getenv("LOCALAPPDATA") - if localAppData == "" { - return fmt.Errorf("cannot get LOCALAPPDATA environment variable") - } - - originalPath := filepath.Join(localAppData, "Programs", "Kiro", "Kiro.exe") - originalCommand := fmt.Sprintf(`"%s" "--open-url" "--" "%%1"`, originalPath) - - keyPath := `Software\Classes\kiro\shell\open\command` - key, err := registry.OpenKey(registry.CURRENT_USER, keyPath, registry.SET_VALUE) - if err != nil { - return fmt.Errorf("failed to open command key: %w", err) - } - defer key.Close() - - if err := key.SetStringValue("", originalCommand); err != nil { - return fmt.Errorf("failed to set command: %w", err) - } - - return nil -} \ No newline at end of file diff --git a/main_darwin.go b/main_darwin.go new file mode 100644 index 0000000..86cd88b --- /dev/null +++ b/main_darwin.go @@ -0,0 +1,84 @@ +// +build darwin + +package main + +import ( + "fmt" + "os" + "os/exec" + "path/filepath" +) + +func showMessageBox(title, message string, flags uint) { + // macOS 使用 osascript 显示对话框 + script := fmt.Sprintf(`display dialog "%s" with title "%s" buttons {"OK"} default button "OK"`, message, title) + cmd := exec.Command("osascript", "-e", script) + cmd.Run() +} + +func addToPath(dir string) error { + homeDir, err := os.UserHomeDir() + if err != nil { + return fmt.Errorf("failed to get home directory: %w", err) + } + + // 检查使用的 shell + shell := os.Getenv("SHELL") + var rcFile string + + if filepath.Base(shell) == "zsh" { + rcFile = filepath.Join(homeDir, ".zshrc") + } else { + rcFile = filepath.Join(homeDir, ".bash_profile") + } + + // 读取现有内容 + content, err := os.ReadFile(rcFile) + if err != nil && !os.IsNotExist(err) { + return fmt.Errorf("failed to read rc file: %w", err) + } + + pathLine := fmt.Sprintf("\n# Added by AntiHook\nexport PATH=\"%s:$PATH\"\n", dir) + + // 检查是否已经添加 + if len(content) > 0 && containsString(string(content), dir) { + return nil + } + + // 追加到文件 + f, err := os.OpenFile(rcFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + if err != nil { + return fmt.Errorf("failed to open rc file: %w", err) + } + defer f.Close() + + if _, err := f.WriteString(pathLine); err != nil { + return fmt.Errorf("failed to write to rc file: %w", err) + } + + fmt.Printf("Added to PATH in %s. Please run: source %s\n", rcFile, rcFile) + return nil +} + +func containsString(s, substr string) bool { + return len(s) >= len(substr) && (s == substr || len(s) > len(substr) && + (s[:len(substr)] == substr || s[len(s)-len(substr):] == substr || + findSubstring(s, substr))) +} + +func findSubstring(s, substr string) bool { + for i := 0; i <= len(s)-len(substr); i++ { + if s[i:i+len(substr)] == substr { + return true + } + } + return false +} + +func recoverOriginal() error { + return fmt.Errorf("recover is only supported on Windows") +} + +func openBrowser(url string) error { + return exec.Command("open", url).Start() +} \ No newline at end of file diff --git a/main_windows.go b/main_windows.go new file mode 100644 index 0000000..0573927 --- /dev/null +++ b/main_windows.go @@ -0,0 +1,87 @@ +// +build windows + +package main + +import ( + "fmt" + "os" + "os/exec" + "path/filepath" + "strings" + "syscall" + "unsafe" + + "golang.org/x/sys/windows/registry" +) + +func showMessageBox(title, message string, flags uint) { + var mod = syscall.NewLazyDLL("user32.dll") + var proc = mod.NewProc("MessageBoxW") + + titlePtr, _ := syscall.UTF16PtrFromString(title) + messagePtr, _ := syscall.UTF16PtrFromString(message) + + proc.Call( + 0, + uintptr(unsafe.Pointer(messagePtr)), + uintptr(unsafe.Pointer(titlePtr)), + uintptr(flags), + ) +} + +func addToPath(dir string) error { + key, err := registry.OpenKey(registry.CURRENT_USER, `Environment`, registry.QUERY_VALUE|registry.SET_VALUE) + if err != nil { + return fmt.Errorf("failed to open Environment key: %w", err) + } + defer key.Close() + + currentPath, _, err := key.GetStringValue("Path") + if err != nil && err != registry.ErrNotExist { + return fmt.Errorf("failed to read PATH: %w", err) + } + + if strings.Contains(strings.ToLower(currentPath), strings.ToLower(dir)) { + return nil + } + + var newPath string + if currentPath == "" { + newPath = dir + } else { + newPath = currentPath + ";" + dir + } + + if err := key.SetStringValue("Path", newPath); err != nil { + return fmt.Errorf("failed to set PATH: %w", err) + } + + return nil +} + +func recoverOriginal() error { + localAppData := os.Getenv("LOCALAPPDATA") + if localAppData == "" { + return fmt.Errorf("cannot get LOCALAPPDATA environment variable") + } + + originalPath := filepath.Join(localAppData, "Programs", "Kiro", "Kiro.exe") + originalCommand := fmt.Sprintf(`"%s" "--open-url" "--" "%%1"`, originalPath) + + keyPath := `Software\Classes\kiro\shell\open\command` + key, err := registry.OpenKey(registry.CURRENT_USER, keyPath, registry.SET_VALUE) + if err != nil { + return fmt.Errorf("failed to open command key: %w", err) + } + defer key.Close() + + if err := key.SetStringValue("", originalCommand); err != nil { + return fmt.Errorf("failed to set command: %w", err) + } + + return nil +} + +func openBrowser(url string) error { + return exec.Command("rundll32", "url.dll,FileProtocolHandler", url).Start() +} \ No newline at end of file diff --git a/registry/registry_darwin.go b/registry/registry_darwin.go new file mode 100644 index 0000000..09ea324 --- /dev/null +++ b/registry/registry_darwin.go @@ -0,0 +1,200 @@ +// +build darwin + +package registry + +import ( + "fmt" + "os" + "os/exec" + "path/filepath" + "strings" +) + +const ( + ProtocolName = "kiro" + ProtocolScheme = "kiro://" + AntiProtocolName = "anti" + AntiProtocolScheme = "anti://" +) + +type ProtocolHandler struct { + Protocol string + ExePath string + Description string +} + +func NewProtocolHandler(protocol, description string) (*ProtocolHandler, error) { + exePath, err := os.Executable() + if err != nil { + return nil, fmt.Errorf("failed to get executable path: %w", err) + } + + exePath, err = filepath.Abs(exePath) + if err != nil { + return nil, fmt.Errorf("failed to get absolute path: %w", err) + } + + return &ProtocolHandler{ + Protocol: protocol, + ExePath: exePath, + Description: description, + }, nil +} + +func (h *ProtocolHandler) Register() error { + homeDir, err := os.UserHomeDir() + if err != nil { + return fmt.Errorf("failed to get home directory: %w", err) + } + + // 创建存储目录 + configDir := filepath.Join(homeDir, ".config", "antihook") + if err := os.MkdirAll(configDir, 0755); err != nil { + return fmt.Errorf("failed to create config directory: %w", err) + } + + // 创建 AppleScript 来处理协议 + scriptPath := filepath.Join(configDir, fmt.Sprintf("%s_handler.scpt", h.Protocol)) + scriptContent := fmt.Sprintf(`on open location this_URL + do shell script "%s " & quoted form of this_URL +end open location`, h.ExePath) + + if err := os.WriteFile(scriptPath, []byte(scriptContent), 0755); err != nil { + return fmt.Errorf("failed to write AppleScript: %w", err) + } + + appPath := strings.TrimSuffix(scriptPath, ".scpt") + ".app" + + // 编译 AppleScript + cmd := exec.Command("osacompile", "-o", appPath, scriptPath) + if err := cmd.Run(); err != nil { + return fmt.Errorf("failed to compile AppleScript: %w", err) + } + + // 创建 Info.plist 为应用添加 bundle identifier 和 URL scheme + infoPlistPath := filepath.Join(appPath, "Contents", "Info.plist") + bundleID := fmt.Sprintf("com.antihook.%s-handler", h.Protocol) + + // 读取现有的 Info.plist + plistContent, err := os.ReadFile(infoPlistPath) + if err != nil { + return fmt.Errorf("failed to read Info.plist: %w", err) + } + + // 在 前添加 CFBundleIdentifier 和 CFBundleURLTypes + plistStr := string(plistContent) + insertPos := strings.LastIndex(plistStr, "") + if insertPos == -1 { + return fmt.Errorf("invalid Info.plist format") + } + + addition := fmt.Sprintf(` CFBundleIdentifier + %s + CFBundleURLTypes + + + CFBundleURLName + %s URL + CFBundleURLSchemes + + %s + + + +`, bundleID, h.Description, h.Protocol) + + newPlistContent := plistStr[:insertPos] + addition + plistStr[insertPos:] + + if err := os.WriteFile(infoPlistPath, []byte(newPlistContent), 0644); err != nil { + return fmt.Errorf("failed to write Info.plist: %w", err) + } + + // 重置 LaunchServices 数据库以识别新的应用 + cmd = exec.Command("/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister", "-f", appPath) + if err := cmd.Run(); err != nil { + fmt.Printf("Warning: failed to register with LaunchServices: %v\n", err) + } + + // 使用 duti 设置为默认处理器 + cmd = exec.Command("duti", "-s", bundleID, h.Protocol, "all") + if err := cmd.Run(); err != nil { + // 如果 duti 失败,尝试使用 open 命令注册 + fmt.Printf("Warning: duti failed, trying alternative method: %v\n", err) + + // 使用 open 命令打开一次来注册 + testURL := fmt.Sprintf("%s://test", h.Protocol) + cmd = exec.Command("open", "-a", appPath, testURL) + _ = cmd.Run() // 忽略错误 + } + + fmt.Printf("✓ Protocol handler registered for '%s://'\n", h.Protocol) + fmt.Printf(" Handler app: %s\n", appPath) + fmt.Printf(" Bundle ID: %s\n", bundleID) + fmt.Printf("\nPlease restart your browser for changes to take effect.\n") + + return nil +} + +func (h *ProtocolHandler) Unregister() error { + homeDir, err := os.UserHomeDir() + if err != nil { + return fmt.Errorf("failed to get home directory: %w", err) + } + + configDir := filepath.Join(homeDir, ".config", "antihook") + scriptPath := filepath.Join(configDir, fmt.Sprintf("%s_handler", h.Protocol)) + + // 删除 AppleScript 应用 + if err := os.RemoveAll(scriptPath + ".app"); err != nil && !os.IsNotExist(err) { + return fmt.Errorf("failed to remove handler app: %w", err) + } + + // 删除脚本文件 + if err := os.Remove(scriptPath + ".scpt"); err != nil && !os.IsNotExist(err) { + return fmt.Errorf("failed to remove script: %w", err) + } + + return nil +} + +func (h *ProtocolHandler) IsRegistered() (bool, error) { + homeDir, err := os.UserHomeDir() + if err != nil { + return false, fmt.Errorf("failed to get home directory: %w", err) + } + + scriptPath := filepath.Join(homeDir, ".config", "antihook", fmt.Sprintf("%s_handler.app", h.Protocol)) + _, err = os.Stat(scriptPath) + if os.IsNotExist(err) { + return false, nil + } + if err != nil { + return false, fmt.Errorf("failed to check registration: %w", err) + } + + return true, nil +} + +func (h *ProtocolHandler) GetRegisteredHandler() (string, error) { + homeDir, err := os.UserHomeDir() + if err != nil { + return "", fmt.Errorf("failed to get home directory: %w", err) + } + + scriptPath := filepath.Join(homeDir, ".config", "antihook", fmt.Sprintf("%s_handler.app", h.Protocol)) + return scriptPath, nil +} + +func (h *ProtocolHandler) Backup() (map[string]string, error) { + // macOS 不需要备份,因为我们创建的是新的处理器 + return nil, nil +} + +func (h *ProtocolHandler) Restore(backup map[string]string) error { + // macOS 上只需要注销即可 + return h.Unregister() +} + +func (h *ProtocolHandler) IsSelfRegistered() (bool, error) { + return h.IsRegistered() +} \ No newline at end of file diff --git a/registry/registry.go b/registry/registry_windows.go similarity index 99% rename from registry/registry.go rename to registry/registry_windows.go index bef31df..d6e6038 100644 --- a/registry/registry.go +++ b/registry/registry_windows.go @@ -1,3 +1,5 @@ +// +build windows + package registry import (