最近因为需要在APK文件中注入一些自己的加密逻辑,我尝试使用golang来修改APK文件,并成功地实现了目标。
首先,我们需要学习如何解析APK文件。APK文件是一个zip格式的文件,由多个部分组成,包括AndroidManifest.xml、classes.dex和资源文件等。在解析前,我们需要先了解一下dex文件的结构。
dex文件由多个部分组成,每个部分都有固定的大小和格式。可以使用如下golang结构体来解析dex文件:
type DexFileHeader struct {
magic [8]byte
checksum uint32
signature [20]byte
fileSize uint32
headerSize uint32
endianTag uint32
...
}其中,magic、checksum、signature、fileSize和headerSize字段表示dex文件的元信息,而endianTag表示dex文件的字节序。更具体的dex文件结构可以参考dex文件规范。
接下来,我们需要使用golang的archive/zip包来解压APK文件,并使用dex2jar工具将classes.dex文件转换为jar文件。最后,我们可以使用golang的jar包来反编译jar文件,并修改源代码。修改完成后,我们需要使用dx工具将修改后的源代码重新编译成dex文件,并放回原APK文件中。
下面是修改APK文件的具体过程:
- 解析APK文件,并将classes.dex文件转换为jar文件。
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)
}- 反编译jar文件,并修改源代码。
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)
}
}在修改源代码时,可以使用golang的go/javaparser包来解析Java代码,并修改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()- 将修改后的源代码编译成dex文件,并放回原APK文件中。
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)
}最终,我们可以得到一个修改后的APK文件,并在其中成功注入了自己的加密逻辑。整个过程使用golang实现,代码简洁易懂,具有很高的可维护性和可扩展性。