VXUG-Papers/Infecting Android Applications The New Way/master/manifest/apkparser.go
2020-10-11 00:40:44 -05:00

175 lines
3.8 KiB
Go

// Package apkparser parses AndroidManifest.xml and resources.arsc from Android APKs.
package manifest
import (
"common"
"fmt"
"io"
"log"
"os"
)
type ApkParser struct {
apkPath string
zip *ZipReader
encoder ManifestEncoder
resources *ResourceTable
}
// save manifest to disk for binary patching
func (p *ApkParser) SaveManifestToDisk() {
file := p.zip.File["AndroidManifest.xml"]
if file == nil {
fmt.Errorf("Failed to find %s in APK!", "AndroidManifest.xml")
}
if err := file.Open(); err != nil {
panic(err)
}
defer file.Close()
// open output file
fo, err := os.Create(common.ManifestBinaryPath)
if err != nil {
panic(err)
}
// close fo on exit and check for its returned error
defer func() {
if err := fo.Close(); err != nil {
panic(err)
}
}()
// make a buffer to keep chunks that are read
buf := make([]byte, 1024)
for {
// read a chunk
n, err := file.Read(buf)
if err != nil && err != io.EOF {
panic(err)
}
if n == 0 {
break
}
// write a chunk
if _, err := fo.Write(buf[:n]); err != nil {
panic(err)
}
}
}
// Calls ParseApkReader
func ParseApk(path string, encoder ManifestEncoder) {
f, zipErr := os.Open(path)
if zipErr != nil {
log.Panic("Failed to open apk")
}
defer f.Close()
ParseApkReader(f, encoder)
}
// Parse APK's Manifest, including resolving refences to resource values.
// encoder expects an XML encoder instance, like Encoder from encoding/xml package.
//
// zipErr != nil means the APK couldn't be opened. The manifest will be parsed
// even when resourcesErr != nil, just without reference resolving.
func ParseApkReader(r io.ReadSeeker, encoder ManifestEncoder) {
zip, zipErr := OpenZipReader(r)
if zipErr != nil {
log.Panic("Failed to open zip reader")
}
defer zip.Close()
ParseApkWithZip(zip, encoder)
}
// Parse APK's Manifest, including resolving refences to resource values.
// encoder expects an XML encoder instance, like Encoder from encoding/xml package.
//
// Use this if you already opened the zip with OpenZip or OpenZipReader before.
// This method will not Close() the zip.
//
// The manifest will be parsed even when resourcesErr != nil, just without reference resolving.
func ParseApkWithZip(zip *ZipReader, encoder ManifestEncoder) {
apkParser := ApkParser{
zip: zip,
encoder: encoder,
}
fmt.Println("\t--Parsing resources...")
apkParser.parseResources()
fmt.Println("\t--Parsing manifest...")
apkParser.ParseXml("AndroidManifest.xml")
apkParser.SaveManifestToDisk()
}
// Prepare the ApkParser instance, load resources if possible.
// encoder expects an XML encoder instance, like Encoder from encoding/xml package.
//
// This method will not Close() the zip, you are still the owner.
func NewParser(zip *ZipReader, encoder ManifestEncoder) (parser *ApkParser) {
parser = &ApkParser{
zip: zip,
encoder: encoder,
}
parser.parseResources()
return
}
func (p *ApkParser) parseResources() {
if p.resources != nil {
log.Panic("resources is not nil")
}
defer func() {
if r := recover(); r != nil {
log.Panic("recover() not nil")
}
}()
resourcesFile := p.zip.File["resources.arsc"]
if resourcesFile == nil {
log.Panic("resource.arsc not found")
}
if err := resourcesFile.Open(); err != nil {
log.Panic("Failed to open resources.arsc: %s", err.Error())
}
defer resourcesFile.Close()
p.resources = ParseResourceTable(resourcesFile)
}
func (p *ApkParser) ParseXml(name string) {
file := p.zip.File[name]
if file == nil {
log.Panicf("Failed to find %s in APK!", name)
}
if err := file.Open(); err != nil {
log.Panic("Failed to open manifest")
}
defer file.Close()
var lastErr error
for file.Next() {
if err := ParseXml(&myReader{r: file}, p.encoder, p.resources); err != nil {
lastErr = err
}
}
if lastErr == ErrPlainTextManifest {
log.Panic("Manifest in plaintext")
}
}