fix: 修复 Megaphone 图标导入拼写错误

This commit is contained in:
2026-06-06 12:29:11 +08:00
parent 1cb8f11acd
commit 21b337f7c3
4 changed files with 1750 additions and 1 deletions
+443
View File
@@ -0,0 +1,443 @@
package inject
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"os"
"os/exec"
"path/filepath"
"strings"
"time"
)
// Platform 平台类型
type Platform string
const (
PlatformAndroid Platform = "android"
PlatformWindows Platform = "windows"
PlatformLinux Platform = "linux"
PlatformIOS Platform = "ios"
)
// Arch 架构类型
type Arch string
const (
ArchX86 Arch = "x86"
ArchX64 Arch = "x64"
ArchARM Arch = "arm"
ArchARM64 Arch = "arm64"
ArchUniversal Arch = "universal"
)
// InjectConfig 注入配置
type InjectConfig struct {
Platform Platform `json:"platform"`
Arch Arch `json:"arch"`
TemplateName string `json:"template_name"`
ShowVerification bool `json:"show_verification"`
ShowAd bool `json:"show_ad"`
AdURL string `json:"ad_url"`
CountdownSeconds int `json:"countdown_seconds"`
AppName string `json:"app_name"`
VerifyURL string `json:"verify_url"`
AppKey string `json:"app_key"`
}
// InjectResult 注入结果
type InjectResult struct {
Success bool `json:"success"`
ResultFile string `json:"result_file"`
ResultSize int64 `json:"result_size"`
Error string `json:"error"`
ProcessTime int64 `json:"process_time"` // 处理耗时(毫秒)
}
// InjectService 注入服务
type InjectService struct {
ApktoolPath string
KeystorePath string
KeystorePass string
KeystoreAlias string
KeyPass string
ApksignerPath string
WorkDir string
}
// NewInjectService 创建注入服务
func NewInjectService(workDir string) *InjectService {
// 确保 workDir 是绝对路径
absWorkDir, err := filepath.Abs(workDir)
if err != nil {
absWorkDir = workDir
}
return &InjectService{
WorkDir: absWorkDir,
ApktoolPath: getEnvOrDefault("APKTOOL_PATH", "apktool"),
KeystorePath: getEnvOrDefault("KEYSTORE_PATH", ""),
KeystorePass: getEnvOrDefault("KEYSTORE_PASS", "123456"),
KeystoreAlias: getEnvOrDefault("KEYSTORE_ALIAS", "mykey"),
KeyPass: getEnvOrDefault("KEY_PASS", "123456"),
ApksignerPath: getEnvOrDefault("APKSIGNER_PATH", "apksigner"),
}
}
func getEnvOrDefault(key, defaultValue string) string {
if value := os.Getenv(key); value != "" {
return value
}
return defaultValue
}
// Inject 执行注入
func (s *InjectService) Inject(inputFile string, config InjectConfig) (*InjectResult, error) {
startTime := time.Now()
// 创建工作目录
taskID := fmt.Sprintf("inject_%d", time.Now().UnixNano())
taskDir := filepath.Join(s.WorkDir, taskID)
if err := os.MkdirAll(taskDir, 0755); err != nil {
return nil, fmt.Errorf("创建工作目录失败: %v", err)
}
defer os.RemoveAll(taskDir) // 清理工作目录
var result *InjectResult
var err error
switch config.Platform {
case PlatformAndroid:
result, err = s.injectAndroid(inputFile, config, taskDir)
case PlatformWindows:
result, err = s.injectWindows(inputFile, config, taskDir)
case PlatformLinux:
result, err = s.injectLinux(inputFile, config, taskDir)
case PlatformIOS:
result, err = s.injectIOS(inputFile, config, taskDir)
default:
err = fmt.Errorf("不支持的平台: %s", config.Platform)
}
if err != nil {
return &InjectResult{
Success: false,
Error: err.Error(),
ProcessTime: time.Since(startTime).Milliseconds(),
}, err
}
result.ProcessTime = time.Since(startTime).Milliseconds()
return result, nil
}
// injectAndroid Android APK 注入
func (s *InjectService) injectAndroid(inputFile string, config InjectConfig, taskDir string) (*InjectResult, error) {
log.Printf("[INFO] 开始 Android APK 注入: %s", inputFile)
// 1. 反编译 APK
decompileDir := filepath.Join(taskDir, "decompiled")
if err := s.decompileAPK(inputFile, decompileDir); err != nil {
return nil, fmt.Errorf("反编译失败: %v", err)
}
// 2. 复制注入模板的 smali 文件
templateDir := filepath.Join("inject", "android")
if err := s.mergeSmali(templateDir, decompileDir); err != nil {
return nil, fmt.Errorf("合并 smali 失败: %v", err)
}
// 3. 配置注入参数
if err := s.configureAndroid(decompileDir, config); err != nil {
return nil, fmt.Errorf("配置失败: %v", err)
}
// 4. 修改 AndroidManifest.xml
if err := s.modifyAndroidManifest(decompileDir); err != nil {
return nil, fmt.Errorf("修改 AndroidManifest 失败: %v", err)
}
// 5. 回编译 APK
unsignedAPK := filepath.Join(taskDir, "unsigned.apk")
if err := s.recompileAPK(decompileDir, unsignedAPK); err != nil {
return nil, fmt.Errorf("回编译失败: %v", err)
}
// 6. 签名 APK
signedAPK := filepath.Join(taskDir, "signed.apk")
if err := s.signAPK(unsignedAPK, signedAPK); err != nil {
return nil, fmt.Errorf("签名失败: %v", err)
}
// 7. 获取结果文件信息
fileInfo, err := os.Stat(signedAPK)
if err != nil {
return nil, fmt.Errorf("获取文件信息失败: %v", err)
}
// 8. 复制结果文件到输出目录
outputDir := filepath.Join("uploads", "inject")
if err := os.MkdirAll(outputDir, 0755); err != nil {
return nil, fmt.Errorf("创建输出目录失败: %v", err)
}
outputFile := filepath.Join(outputDir, fmt.Sprintf("injected_%d.apk", time.Now().Unix()))
if err := copyFile(signedAPK, outputFile); err != nil {
return nil, fmt.Errorf("复制结果文件失败: %v", err)
}
log.Printf("[INFO] Android APK 注入完成: %s", outputFile)
return &InjectResult{
Success: true,
ResultFile: "/" + strings.ReplaceAll(outputFile, "\\", "/"),
ResultSize: fileInfo.Size(),
}, nil
}
// injectWindows Windows PE 注入 (占位)
func (s *InjectService) injectWindows(inputFile string, config InjectConfig, taskDir string) (*InjectResult, error) {
return nil, fmt.Errorf("Windows 平台注入暂未实现")
}
// injectLinux Linux ELF 注入 (占位)
func (s *InjectService) injectLinux(inputFile string, config InjectConfig, taskDir string) (*InjectResult, error) {
return nil, fmt.Errorf("Linux 平台注入暂未实现")
}
// injectIOS iOS IPA 注入 (占位)
func (s *InjectService) injectIOS(inputFile string, config InjectConfig, taskDir string) (*InjectResult, error) {
return nil, fmt.Errorf("iOS 平台注入暂未实现")
}
// decompileAPK 反编译 APK
func (s *InjectService) decompileAPK(apkFile, outputDir string) error {
cmd := exec.Command("java", "-jar", s.ApktoolPath, "d", apkFile, "-o", outputDir, "-f")
output, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("apktool d 失败: %v, output: %s", err, string(output))
}
log.Printf("[DEBUG] decompile output: %s", string(output))
return nil
}
// recompileAPK 回编译 APK
func (s *InjectService) recompileAPK(decompileDir, outputFile string) error {
cmd := exec.Command("java", "-jar", s.ApktoolPath, "b", decompileDir, "-o", outputFile)
output, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("apktool b 失败: %v, output: %s", err, string(output))
}
log.Printf("[DEBUG] recompile output: %s", string(output))
return nil
}
// signAPK 签名 APK
func (s *InjectService) signAPK(unsignedAPK, signedAPK string) error {
// 如果没有配置 keystore,使用测试签名
if s.KeystorePath == "" {
log.Printf("[WARN] 未配置 keystore,使用 zipalign 对齐")
// 使用 zipalign 对齐
cmd := exec.Command("zipalign", "-v", "-p", "4", unsignedAPK, signedAPK)
output, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("zipalign 失败: %v, output: %s", err, string(output))
}
return nil
}
// 使用 apksigner 签名
cmd := exec.Command(s.ApksignerPath, "sign",
"--ks", s.KeystorePath,
"--ks-key-alias", s.KeystoreAlias,
"--ks-pass", "pass:"+s.KeystorePass,
"--key-pass", "pass:"+s.KeyPass,
"--out", signedAPK,
unsignedAPK)
output, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("apksigner 签名失败: %v, output: %s", err, string(output))
}
log.Printf("[DEBUG] sign output: %s", string(output))
return nil
}
// mergeSmali 合并 smali 文件
func (s *InjectService) mergeSmali(templateDir, decompileDir string) error {
// 复制模板 smali 文件到反编译目录
smaliDirs := []string{"smali", "smali_classes2", "smali_classes3"}
for _, smaliDir := range smaliDirs {
srcSmali := filepath.Join(templateDir, smaliDir)
if _, err := os.Stat(srcSmali); os.IsNotExist(err) {
continue
}
dstSmali := filepath.Join(decompileDir, smaliDir)
if smaliDir == "smali" {
// 模板的 smali 直接覆盖
if err := copyDir(srcSmali, dstSmali); err != nil {
return err
}
} else {
// 其他 smali_classes 需要合并
if err := copyDir(srcSmali, dstSmali); err != nil {
return err
}
}
}
return nil
}
// configureAndroid 配置 Android 注入参数
func (s *InjectService) configureAndroid(decompileDir string, config InjectConfig) error {
// 查找并修改 HookApplication.java 中的配置
hookAppPath := filepath.Join(decompileDir, "smali", "com", "example", "shell", "HookApplication.smali")
if _, err := os.Stat(hookAppPath); os.IsNotExist(err) {
log.Printf("[WARN] HookApplication.smali 不存在,跳过配置")
return nil
}
// 读取文件内容
content, err := ioutil.ReadFile(hookAppPath)
if err != nil {
return err
}
contentStr := string(content)
// 替换配置参数
replacements := map[string]string{
"[OpenClassName]": config.AppName,
"\"https://example.com\"": fmt.Sprintf("\"%s\"", config.VerifyURL),
"[AppKey]": config.AppKey,
}
for old, new := range replacements {
contentStr = strings.ReplaceAll(contentStr, old, new)
}
// 写回文件
return ioutil.WriteFile(hookAppPath, []byte(contentStr), 0644)
}
// modifyAndroidManifest 修改 AndroidManifest.xml
func (s *InjectService) modifyAndroidManifest(decompileDir string) error {
manifestPath := filepath.Join(decompileDir, "AndroidManifest.xml")
content, err := ioutil.ReadFile(manifestPath)
if err != nil {
return err
}
contentStr := string(content)
// 检查是否已有 android:name
if strings.Contains(contentStr, "android:name=") {
// 替换现有的 android:name
re := strings.NewReplacer(
`android:name="android.app.Application"`, `android:name="com.example.shell.HookApplication"`,
)
contentStr = re.Replace(contentStr)
} else {
// 在 <application 标签添加 android:name
contentStr = strings.Replace(contentStr, "<application", `<application android:name="com.example.shell.HookApplication"`, 1)
}
return ioutil.WriteFile(manifestPath, []byte(contentStr), 0644)
}
// copyFile 复制文件
func copyFile(src, dst string) error {
input, err := ioutil.ReadFile(src)
if err != nil {
return err
}
return ioutil.WriteFile(dst, input, 0644)
}
// copyDir 复制目录
func copyDir(src, dst string) error {
// 确保目标目录存在
if err := os.MkdirAll(dst, 0755); err != nil {
return err
}
entries, err := ioutil.ReadDir(src)
if err != nil {
return err
}
for _, entry := range entries {
srcPath := filepath.Join(src, entry.Name())
dstPath := filepath.Join(dst, entry.Name())
if entry.IsDir() {
if err := copyDir(srcPath, dstPath); err != nil {
return err
}
} else {
if err := copyFile(srcPath, dstPath); err != nil {
return err
}
}
}
return nil
}
// GetSupportedPlatforms 获取支持的平台和架构
func GetSupportedPlatforms() map[string][]string {
return map[string][]string{
"android": {"x86", "x64", "arm", "arm64"},
"windows": {"x86", "x64"},
"linux": {"x86", "x64", "arm", "arm64"},
"ios": {"arm", "arm64"},
}
}
// DetectPlatform 检测文件平台
func DetectPlatform(filename string) (Platform, error) {
ext := strings.ToLower(filepath.Ext(filename))
switch ext {
case ".apk":
return PlatformAndroid, nil
case ".exe", ".dll":
return PlatformWindows, nil
case ".so", "", ".elf":
if strings.Contains(strings.ToLower(filename), "linux") || ext == ".so" {
return PlatformLinux, nil
}
case ".ipa":
return PlatformIOS, nil
}
// 根据文件名猜测
lowerName := strings.ToLower(filename)
if strings.Contains(lowerName, "android") || strings.Contains(lowerName, "apk") {
return PlatformAndroid, nil
}
if strings.Contains(lowerName, "windows") || strings.Contains(lowerName, "win") || strings.Contains(lowerName, ".exe") {
return PlatformWindows, nil
}
if strings.Contains(lowerName, "linux") || strings.Contains(lowerName, ".so") {
return PlatformLinux, nil
}
if strings.Contains(lowerName, "ios") || strings.Contains(lowerName, ".ipa") {
return PlatformIOS, nil
}
return "", fmt.Errorf("无法检测文件平台: %s", filename)
}
// ToJSON 转换为 JSON
func (c *InjectConfig) ToJSON() string {
data, _ := json.Marshal(c)
return string(data)
}
// ParseConfig 从 JSON 解析配置
func ParseConfig(jsonStr string) (*InjectConfig, error) {
var config InjectConfig
err := json.Unmarshal([]byte(jsonStr), &config)
return &config, err
}