fix: 修复 Megaphone 图标导入拼写错误
This commit is contained in:
@@ -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
|
||||
}
|
||||
Reference in New Issue
Block a user