1*795d594fSAndroid Build Coastguard Worker#!/usr/bin/python3 2*795d594fSAndroid Build Coastguard Worker# 3*795d594fSAndroid Build Coastguard Worker# Copyright (C) 2021 The Android Open Source Project 4*795d594fSAndroid Build Coastguard Worker# 5*795d594fSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); 6*795d594fSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License. 7*795d594fSAndroid Build Coastguard Worker# You may obtain a copy of the License at 8*795d594fSAndroid Build Coastguard Worker# 9*795d594fSAndroid Build Coastguard Worker# http://www.apache.org/licenses/LICENSE-2.0 10*795d594fSAndroid Build Coastguard Worker# 11*795d594fSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 12*795d594fSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, 13*795d594fSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14*795d594fSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and 15*795d594fSAndroid Build Coastguard Worker# limitations under the License. 16*795d594fSAndroid Build Coastguard Worker 17*795d594fSAndroid Build Coastguard Worker# 18*795d594fSAndroid Build Coastguard Worker# This script can get info out of dexfiles using libdexfile.so external API 19*795d594fSAndroid Build Coastguard Worker# 20*795d594fSAndroid Build Coastguard Worker 21*795d594fSAndroid Build Coastguard Workerfrom abc import ABC 22*795d594fSAndroid Build Coastguard Workerfrom ctypes import * 23*795d594fSAndroid Build Coastguard Workerimport os.path 24*795d594fSAndroid Build Coastguard Workerimport functools 25*795d594fSAndroid Build Coastguard Workerimport zipfile 26*795d594fSAndroid Build Coastguard Worker 27*795d594fSAndroid Build Coastguard Workerlibdexfile = CDLL( 28*795d594fSAndroid Build Coastguard Worker os.path.expandvars("$ANDROID_HOST_OUT/lib64/libdexfiled.so")) 29*795d594fSAndroid Build Coastguard Worker 30*795d594fSAndroid Build Coastguard WorkerExtDexFile = c_void_p 31*795d594fSAndroid Build Coastguard Worker 32*795d594fSAndroid Build Coastguard Workerclass ExtMethodInfo(Structure): 33*795d594fSAndroid Build Coastguard Worker """Output format for MethodInfo""" 34*795d594fSAndroid Build Coastguard Worker _fields_ = [("sizeof_struct", c_size_t), 35*795d594fSAndroid Build Coastguard Worker ("addr", c_int32), 36*795d594fSAndroid Build Coastguard Worker ("size", c_int32), 37*795d594fSAndroid Build Coastguard Worker ("name", POINTER(c_char)), 38*795d594fSAndroid Build Coastguard Worker ("name_size", c_size_t)] 39*795d594fSAndroid Build Coastguard Worker 40*795d594fSAndroid Build Coastguard WorkerAllMethodsCallback = CFUNCTYPE(c_int, c_void_p, POINTER(ExtMethodInfo)) 41*795d594fSAndroid Build Coastguard Workerlibdexfile.ExtDexFileOpenFromMemory.argtypes = [ 42*795d594fSAndroid Build Coastguard Worker c_void_p, 43*795d594fSAndroid Build Coastguard Worker POINTER(c_size_t), 44*795d594fSAndroid Build Coastguard Worker c_char_p, 45*795d594fSAndroid Build Coastguard Worker POINTER(ExtDexFile) 46*795d594fSAndroid Build Coastguard Worker] 47*795d594fSAndroid Build Coastguard Workerlibdexfile.ExtDexFileOpenFromMemory.restype = c_int 48*795d594fSAndroid Build Coastguard Workerlibdexfile.ExtDexFileGetAllMethodInfos.argtypes = [ 49*795d594fSAndroid Build Coastguard Worker ExtDexFile, c_int, AllMethodsCallback, c_void_p 50*795d594fSAndroid Build Coastguard Worker] 51*795d594fSAndroid Build Coastguard Worker 52*795d594fSAndroid Build Coastguard Workerclass DexClass(object): 53*795d594fSAndroid Build Coastguard Worker """Accessor for DexClass Data""" 54*795d594fSAndroid Build Coastguard Worker 55*795d594fSAndroid Build Coastguard Worker def __init__(self, name): 56*795d594fSAndroid Build Coastguard Worker self.name = name.strip() 57*795d594fSAndroid Build Coastguard Worker self.arrays = name.count("[") 58*795d594fSAndroid Build Coastguard Worker self.base_name = self.name if self.arrays == 0 else self.name[:-( 59*795d594fSAndroid Build Coastguard Worker self.arrays * 2)] 60*795d594fSAndroid Build Coastguard Worker 61*795d594fSAndroid Build Coastguard Worker def __repr__(self): 62*795d594fSAndroid Build Coastguard Worker return self.name 63*795d594fSAndroid Build Coastguard Worker 64*795d594fSAndroid Build Coastguard Worker @functools.cached_property 65*795d594fSAndroid Build Coastguard Worker def descriptor(self): 66*795d594fSAndroid Build Coastguard Worker """The name as a descriptor""" 67*795d594fSAndroid Build Coastguard Worker if self.base_name == "int": 68*795d594fSAndroid Build Coastguard Worker return "[" * self.arrays + "I" 69*795d594fSAndroid Build Coastguard Worker elif self.base_name == "short": 70*795d594fSAndroid Build Coastguard Worker return "[" * self.arrays + "S" 71*795d594fSAndroid Build Coastguard Worker elif self.base_name == "long": 72*795d594fSAndroid Build Coastguard Worker return "[" * self.arrays + "J" 73*795d594fSAndroid Build Coastguard Worker elif self.base_name == "char": 74*795d594fSAndroid Build Coastguard Worker return "[" * self.arrays + "C" 75*795d594fSAndroid Build Coastguard Worker elif self.base_name == "boolean": 76*795d594fSAndroid Build Coastguard Worker return "[" * self.arrays + "Z" 77*795d594fSAndroid Build Coastguard Worker elif self.base_name == "byte": 78*795d594fSAndroid Build Coastguard Worker return "[" * self.arrays + "B" 79*795d594fSAndroid Build Coastguard Worker elif self.base_name == "float": 80*795d594fSAndroid Build Coastguard Worker return "[" * self.arrays + "F" 81*795d594fSAndroid Build Coastguard Worker elif self.base_name == "double": 82*795d594fSAndroid Build Coastguard Worker return "[" * self.arrays + "D" 83*795d594fSAndroid Build Coastguard Worker elif self.base_name == "void": 84*795d594fSAndroid Build Coastguard Worker return "[" * self.arrays + "V" 85*795d594fSAndroid Build Coastguard Worker else: 86*795d594fSAndroid Build Coastguard Worker return "[" * self.arrays + "L{};".format("/".join( 87*795d594fSAndroid Build Coastguard Worker self.base_name.split("."))) 88*795d594fSAndroid Build Coastguard Worker 89*795d594fSAndroid Build Coastguard Worker 90*795d594fSAndroid Build Coastguard Workerclass Method(object): 91*795d594fSAndroid Build Coastguard Worker """Method info wrapper""" 92*795d594fSAndroid Build Coastguard Worker 93*795d594fSAndroid Build Coastguard Worker def __init__(self, mi): 94*795d594fSAndroid Build Coastguard Worker self.offset = mi.addr 95*795d594fSAndroid Build Coastguard Worker self.len = mi.size 96*795d594fSAndroid Build Coastguard Worker self.name = string_at(mi.name, mi.name_size).decode("utf-8") 97*795d594fSAndroid Build Coastguard Worker 98*795d594fSAndroid Build Coastguard Worker def __repr__(self): 99*795d594fSAndroid Build Coastguard Worker return "(" + self.name + ")" 100*795d594fSAndroid Build Coastguard Worker 101*795d594fSAndroid Build Coastguard Worker @functools.cached_property 102*795d594fSAndroid Build Coastguard Worker def descriptor(self): 103*795d594fSAndroid Build Coastguard Worker """name as a descriptor""" 104*795d594fSAndroid Build Coastguard Worker ret = DexClass(self.name.split(" ")[0]) 105*795d594fSAndroid Build Coastguard Worker non_ret = self.name[len(ret.name) + 1:] 106*795d594fSAndroid Build Coastguard Worker arg_str = non_ret[non_ret.find("(") + 1:-1] 107*795d594fSAndroid Build Coastguard Worker args = [] if arg_str == "" else map( 108*795d594fSAndroid Build Coastguard Worker lambda a: DexClass(a.strip()).descriptor, arg_str.split(",")) 109*795d594fSAndroid Build Coastguard Worker class_and_meth = non_ret[0:non_ret.find("(")] 110*795d594fSAndroid Build Coastguard Worker class_only = DexClass(class_and_meth[0:class_and_meth.rfind(".")]) 111*795d594fSAndroid Build Coastguard Worker meth = class_and_meth[class_and_meth.rfind(".") + 1:] 112*795d594fSAndroid Build Coastguard Worker return "{cls}->{meth}({args}){ret}".format( 113*795d594fSAndroid Build Coastguard Worker cls=class_only.descriptor, 114*795d594fSAndroid Build Coastguard Worker meth=meth, 115*795d594fSAndroid Build Coastguard Worker args="".join(args), 116*795d594fSAndroid Build Coastguard Worker ret=ret.descriptor) 117*795d594fSAndroid Build Coastguard Worker 118*795d594fSAndroid Build Coastguard Worker @functools.cached_property 119*795d594fSAndroid Build Coastguard Worker def name_only(self): 120*795d594fSAndroid Build Coastguard Worker """name without the return-type or arguments in java format""" 121*795d594fSAndroid Build Coastguard Worker ret = DexClass(self.name.split(" ")[0]) 122*795d594fSAndroid Build Coastguard Worker non_ret = self.name[len(ret.name) + 1:] 123*795d594fSAndroid Build Coastguard Worker class_and_meth = non_ret[0:non_ret.find("(")] 124*795d594fSAndroid Build Coastguard Worker return class_and_meth 125*795d594fSAndroid Build Coastguard Worker 126*795d594fSAndroid Build Coastguard Worker @functools.cached_property 127*795d594fSAndroid Build Coastguard Worker def klass(self): 128*795d594fSAndroid Build Coastguard Worker """declaring DexClass.""" 129*795d594fSAndroid Build Coastguard Worker ret = DexClass(self.name.split(" ")[0]) 130*795d594fSAndroid Build Coastguard Worker non_ret = self.name[len(ret.name) + 1:] 131*795d594fSAndroid Build Coastguard Worker class_and_meth = non_ret[0:non_ret.find("(")] 132*795d594fSAndroid Build Coastguard Worker return DexClass(class_and_meth[0:class_and_meth.rfind(".")]) 133*795d594fSAndroid Build Coastguard Worker 134*795d594fSAndroid Build Coastguard Worker 135*795d594fSAndroid Build Coastguard Workerclass BaseDexFile(ABC): 136*795d594fSAndroid Build Coastguard Worker """DexFile base class""" 137*795d594fSAndroid Build Coastguard Worker 138*795d594fSAndroid Build Coastguard Worker def __init__(self): 139*795d594fSAndroid Build Coastguard Worker self.ext_dex_file_ = None 140*795d594fSAndroid Build Coastguard Worker return 141*795d594fSAndroid Build Coastguard Worker 142*795d594fSAndroid Build Coastguard Worker @functools.cached_property 143*795d594fSAndroid Build Coastguard Worker def methods(self): 144*795d594fSAndroid Build Coastguard Worker """Methods in the dex-file""" 145*795d594fSAndroid Build Coastguard Worker meths = [] 146*795d594fSAndroid Build Coastguard Worker 147*795d594fSAndroid Build Coastguard Worker @AllMethodsCallback 148*795d594fSAndroid Build Coastguard Worker def my_cb(_, info): 149*795d594fSAndroid Build Coastguard Worker """Callback visitor for method infos""" 150*795d594fSAndroid Build Coastguard Worker meths.append(Method(info[0])) 151*795d594fSAndroid Build Coastguard Worker return 0 152*795d594fSAndroid Build Coastguard Worker 153*795d594fSAndroid Build Coastguard Worker libdexfile.ExtDexFileGetAllMethodInfos(self.ext_dex_file_, 154*795d594fSAndroid Build Coastguard Worker c_int(1), my_cb, c_void_p()) 155*795d594fSAndroid Build Coastguard Worker return meths 156*795d594fSAndroid Build Coastguard Worker 157*795d594fSAndroid Build Coastguard Workerclass MemDexFile(BaseDexFile): 158*795d594fSAndroid Build Coastguard Worker """DexFile using memory""" 159*795d594fSAndroid Build Coastguard Worker 160*795d594fSAndroid Build Coastguard Worker def __init__(self, dat, loc): 161*795d594fSAndroid Build Coastguard Worker assert type(dat) == bytes 162*795d594fSAndroid Build Coastguard Worker super().__init__() 163*795d594fSAndroid Build Coastguard Worker # Don't want GC to screw us over. 164*795d594fSAndroid Build Coastguard Worker self.mem_ref = (c_byte * len(dat)).from_buffer_copy(dat) 165*795d594fSAndroid Build Coastguard Worker res_fle_ptr = pointer(c_void_p()) 166*795d594fSAndroid Build Coastguard Worker res = libdexfile.ExtDexFileOpenFromMemory( 167*795d594fSAndroid Build Coastguard Worker self.mem_ref, byref(c_size_t(len(dat))), 168*795d594fSAndroid Build Coastguard Worker create_string_buffer(bytes(loc, "utf-8")), res_fle_ptr) 169*795d594fSAndroid Build Coastguard Worker if res != 0: 170*795d594fSAndroid Build Coastguard Worker raise Exception("Failed to open file: {}. Error {}.".format(loc, res)) 171*795d594fSAndroid Build Coastguard Worker self.ext_dex_file_ = res_fle_ptr.contents 172*795d594fSAndroid Build Coastguard Worker 173*795d594fSAndroid Build Coastguard Workerclass FileDexFile(MemDexFile): 174*795d594fSAndroid Build Coastguard Worker """DexFile using a file""" 175*795d594fSAndroid Build Coastguard Worker 176*795d594fSAndroid Build Coastguard Worker def __init__(self, file, loc): 177*795d594fSAndroid Build Coastguard Worker if type(file) == str: 178*795d594fSAndroid Build Coastguard Worker self.file = open(file, "rb") 179*795d594fSAndroid Build Coastguard Worker self.loc = file 180*795d594fSAndroid Build Coastguard Worker else: 181*795d594fSAndroid Build Coastguard Worker self.file = file 182*795d594fSAndroid Build Coastguard Worker self.loc = "file_obj" 183*795d594fSAndroid Build Coastguard Worker super().__init__(self.file.read(), self.loc) 184*795d594fSAndroid Build Coastguard Worker 185*795d594fSAndroid Build Coastguard Workerdef OpenJar(fle): 186*795d594fSAndroid Build Coastguard Worker """Opens all classes[0-9]*.dex files in a zip archive""" 187*795d594fSAndroid Build Coastguard Worker res = [] 188*795d594fSAndroid Build Coastguard Worker with zipfile.ZipFile(fle) as zf: 189*795d594fSAndroid Build Coastguard Worker for f in zf.namelist(): 190*795d594fSAndroid Build Coastguard Worker if f.endswith(".dex") and f.startswith("classes"): 191*795d594fSAndroid Build Coastguard Worker res.append( 192*795d594fSAndroid Build Coastguard Worker MemDexFile(zf.read(f), "classes" if type(fle) != str else fle)) 193*795d594fSAndroid Build Coastguard Worker return res 194