Home  >  Article  >  Backend Development  >  How to modify apk in golang

How to modify apk in golang

PHPz
PHPzOriginal
2023-04-25 10:44:11788browse

Recently, because I needed to inject some of my own encryption logic into the APK file, I tried to use golang to modify the APK file and successfully achieved the goal.

First, we need to learn how to parse APK files. The APK file is a file in zip format and consists of multiple parts, including AndroidManifest.xml, classes.dex and resource files. Before parsing, we need to first understand the structure of the dex file.

dex file consists of multiple parts, each part has a fixed size and format. You can use the following golang structure to parse dex files:

type DexFileHeader struct {
    magic       [8]byte
    checksum    uint32
    signature   [20]byte
    fileSize    uint32
    headerSize  uint32
    endianTag   uint32
    ...
}

Among them, magic, checksum, signature, fileSize and headerSize fields represent the meta-information of the dex file, and endianTag represents the byte order of the dex file. For more specific dex file structure, please refer to the dex file specification.

Next, we need to use golang’s archive/zip package to decompress the APK file, and use the dex2jar tool to convert the classes.dex file into a jar file. Finally, we can use golang's jar package to decompile the jar file and modify the source code. After the modification is completed, we need to use the dx tool to recompile the modified source code into a dex file and put it back into the original APK file.

The following is the specific process of modifying the APK file:

  1. Parse the APK file and convert the classes.dex file into a jar file.
apkFile, err := zip.OpenReader(apkPath)
if err != nil {
    panic(err)
}
defer apkFile.Close()

var dexFile *zip.File
for _, f := range apkFile.File {
    if f.Name == "classes.dex" {
        dexFile = f
        break
    }
}
if dexFile == nil {
    panic("no classes.dex found")
}

dexReader, err := dexFile.Open()
if err != nil {
    panic(err)
}
defer dexReader.Close()

tmpDexPath := filepath.Join(tmpDir, "classes.dex")
tmpJarPath := filepath.Join(tmpDir, "classes.jar")

tmpDexFile, err := os.Create(tmpDexPath)
if err != nil {
    panic(err)
}
defer tmpDexFile.Close()

io.Copy(tmpDexFile, dexReader)

cmd := exec.Command("d2j-dex2jar", tmpDexPath, "-f", "-o", tmpJarPath)
if err := cmd.Run(); err != nil {
    panic(err)
}
  1. Decompile the jar file and modify the source code.
jarFile, err := jar.Open(tmpJarPath)
if err != nil {
    panic(err)
}
defer jarFile.Close()

for _, classFile := range jarFile.Files() {
    if !strings.HasSuffix(classFile.Name, ".class") {
        continue
    }

    className := strings.TrimSuffix(classFile.Name, ".class")
    className = strings.ReplaceAll(className, "/", ".")

    classReader, err := classFile.Open()
    if err != nil {
        panic(err)
    }
    defer classReader.Close()

    classBytes, err := ioutil.ReadAll(classReader)
    if err != nil {
        panic(err)
    }

    // 修改源代码
    modifiedClassBytes := modifyClassBytes(classBytes)

    tmpClassPath := filepath.Join(tmpDir, className+".class")
    tmpClassFile, err := os.Create(tmpClassPath)
    if err != nil {
        panic(err)
    }
    defer tmpClassFile.Close()

    _, err = tmpClassFile.Write(modifiedClassBytes)
    if err != nil {
        panic(err)
    }
}

When modifying the source code, you can use golang's go/javaparser package to parse Java code and modify the AST (Abstract Syntax Tree).

unit, err := parser.ParseFile(token.NewFileSet(), "", modifiedSource, parser.ParseComments)
if err != nil {
    panic(err)
}

// 修改AST

var buf bytes.Buffer
printer.Fprint(&buf, token.NewFileSet(), unit)

return buf.Bytes()
  1. Compile the modified source code into a dex file and put it back into the original APK file.
cmd = exec.Command("d2j-jar2dex", tmpJarPath, "-o", tmpDexPath)
if err := cmd.Run(); err != nil {
    panic(err)
}

outDex, err := os.Open(tmpDexPath)
if err != nil {
    panic(err)
}
defer outDex.Close()

outDexInfo, err := os.Stat(tmpDexPath)
if err != nil {
    panic(err)
}

outDexHeader := &zip.FileHeader{
    Name:   "classes.dex",
    Method: zip.Store,
}

outDexHeader.SetModTime(outDexInfo.ModTime())

outDexWriter, err := apkWriter.CreateHeader(outDexHeader)
if err != nil {
    panic(err)
}

if _, err := io.Copy(outDexWriter, outDex); err != nil {
    panic(err)
}

Finally, we can get a modified APK file and successfully inject our own encryption logic into it. The entire process is implemented using golang. The code is concise and easy to understand, and has high maintainability and scalability.

The above is the detailed content of How to modify apk in golang. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn