1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package art; 18 19 import static art.Redefinition.CommonClassDefinition; 20 21 import java.util.Arrays; 22 import java.util.ArrayList; 23 import java.util.Base64; 24 import java.lang.reflect.*; 25 public class Test944 { 26 27 static class Transform { sayHi()28 public void sayHi() { 29 System.out.println("hello"); 30 } 31 } 32 33 static class Transform2 { sayHi()34 public void sayHi() { 35 System.out.println("hello2"); 36 } 37 } 38 39 /** 40 * base64 encoded class/dex file for 41 * static class Transform { 42 * public void sayHi() { 43 * System.out.println("Goodbye"); 44 * } 45 * } 46 */ 47 private static CommonClassDefinition TRANSFORM_DEFINITION = new CommonClassDefinition( 48 Transform.class, 49 Base64.getDecoder().decode( 50 "yv66vgAAADQAIAoABgAOCQAPABAIABEKABIAEwcAFQcAGAEABjxpbml0PgEAAygpVgEABENvZGUB" + 51 "AA9MaW5lTnVtYmVyVGFibGUBAAVzYXlIaQEAClNvdXJjZUZpbGUBAAxUZXN0OTQ0LmphdmEMAAcA" + 52 "CAcAGQwAGgAbAQAHR29vZGJ5ZQcAHAwAHQAeBwAfAQAVYXJ0L1Rlc3Q5NDQkVHJhbnNmb3JtAQAJ" + 53 "VHJhbnNmb3JtAQAMSW5uZXJDbGFzc2VzAQAQamF2YS9sYW5nL09iamVjdAEAEGphdmEvbGFuZy9T" + 54 "eXN0ZW0BAANvdXQBABVMamF2YS9pby9QcmludFN0cmVhbTsBABNqYXZhL2lvL1ByaW50U3RyZWFt" + 55 "AQAHcHJpbnRsbgEAFShMamF2YS9sYW5nL1N0cmluZzspVgEAC2FydC9UZXN0OTQ0ACAABQAGAAAA" + 56 "AAACAAAABwAIAAEACQAAAB0AAQABAAAABSq3AAGxAAAAAQAKAAAABgABAAAACgABAAsACAABAAkA" + 57 "AAAlAAIAAQAAAAmyAAISA7YABLEAAAABAAoAAAAKAAIAAAAMAAgADQACAAwAAAACAA0AFwAAAAoA" + 58 "AQAFABQAFgAI"), 59 Base64.getDecoder().decode( 60 "ZGV4CjAzNQCFgsuWAAAAAAAAAAAAAAAAAAAAAAAAAAC4AwAAcAAAAHhWNBIAAAAAAAAAAPQCAAAU" + 61 "AAAAcAAAAAkAAADAAAAAAgAAAOQAAAABAAAA/AAAAAQAAAAEAQAAAQAAACQBAAB0AgAARAEAAEQB" + 62 "AABMAQAAVQEAAG4BAAB9AQAAoQEAAMEBAADYAQAA7AEAAAACAAAUAgAAIgIAAC0CAAAwAgAANAIA" + 63 "AEECAABHAgAATAIAAFUCAABcAgAAAgAAAAMAAAAEAAAABQAAAAYAAAAHAAAACAAAAAkAAAAMAAAA" + 64 "DAAAAAgAAAAAAAAADQAAAAgAAABkAgAABwAEABAAAAAAAAAAAAAAAAAAAAASAAAABAABABEAAAAF" + 65 "AAAAAAAAAAAAAAAAAAAABQAAAAAAAAAKAAAA5AIAALgCAAAAAAAABjxpbml0PgAHR29vZGJ5ZQAX" + 66 "TGFydC9UZXN0OTQ0JFRyYW5zZm9ybTsADUxhcnQvVGVzdDk0NDsAIkxkYWx2aWsvYW5ub3RhdGlv" + 67 "bi9FbmNsb3NpbmdDbGFzczsAHkxkYWx2aWsvYW5ub3RhdGlvbi9Jbm5lckNsYXNzOwAVTGphdmEv" + 68 "aW8vUHJpbnRTdHJlYW07ABJMamF2YS9sYW5nL09iamVjdDsAEkxqYXZhL2xhbmcvU3RyaW5nOwAS" + 69 "TGphdmEvbGFuZy9TeXN0ZW07AAxUZXN0OTQ0LmphdmEACVRyYW5zZm9ybQABVgACVkwAC2FjY2Vz" + 70 "c0ZsYWdzAARuYW1lAANvdXQAB3ByaW50bG4ABXNheUhpAAV2YWx1ZQAAAQAAAAYAAAAKAAcOAAwA" + 71 "Bw4BCA8AAAAAAQABAAEAAABsAgAABAAAAHAQAwAAAA4AAwABAAIAAABxAgAACQAAAGIAAAAbAQEA" + 72 "AABuIAIAEAAOAAAAAAABAQCAgAT8BAEBlAUAAAICARMYAQIDAg4ECA8XCwACAAAAyAIAAM4CAADY" + 73 "AgAAAAAAAAAAAAAAAAAAEAAAAAAAAAABAAAAAAAAAAEAAAAUAAAAcAAAAAIAAAAJAAAAwAAAAAMA" + 74 "AAACAAAA5AAAAAQAAAABAAAA/AAAAAUAAAAEAAAABAEAAAYAAAABAAAAJAEAAAIgAAAUAAAARAEA" + 75 "AAEQAAABAAAAZAIAAAMgAAACAAAAbAIAAAEgAAACAAAAfAIAAAAgAAABAAAAuAIAAAQgAAACAAAA" + 76 "yAIAAAMQAAABAAAA2AIAAAYgAAABAAAA5AIAAAAQAAABAAAA9AIAAA==")); 77 78 /** 79 * base64 encoded class/dex file for 80 * static class Transform2 { 81 * public void sayHi() { 82 * System.out.println("Goodbye2"); 83 * } 84 * } 85 */ 86 private static CommonClassDefinition TRANSFORM2_DEFINITION = new CommonClassDefinition( 87 Transform2.class, 88 Base64.getDecoder().decode( 89 "yv66vgAAADQAIAoABgAOCQAPABAIABEKABIAEwcAFQcAGAEABjxpbml0PgEAAygpVgEABENvZGUB" + 90 "AA9MaW5lTnVtYmVyVGFibGUBAAVzYXlIaQEAClNvdXJjZUZpbGUBAAxUZXN0OTQ0LmphdmEMAAcA" + 91 "CAcAGQwAGgAbAQAIR29vZGJ5ZTIHABwMAB0AHgcAHwEAFmFydC9UZXN0OTQ0JFRyYW5zZm9ybTIB" + 92 "AApUcmFuc2Zvcm0yAQAMSW5uZXJDbGFzc2VzAQAQamF2YS9sYW5nL09iamVjdAEAEGphdmEvbGFu" + 93 "Zy9TeXN0ZW0BAANvdXQBABVMamF2YS9pby9QcmludFN0cmVhbTsBABNqYXZhL2lvL1ByaW50U3Ry" + 94 "ZWFtAQAHcHJpbnRsbgEAFShMamF2YS9sYW5nL1N0cmluZzspVgEAC2FydC9UZXN0OTQ0ACAABQAG" + 95 "AAAAAAACAAAABwAIAAEACQAAAB0AAQABAAAABSq3AAGxAAAAAQAKAAAABgABAAAABQABAAsACAAB" + 96 "AAkAAAAlAAIAAQAAAAmyAAISA7YABLEAAAABAAoAAAAKAAIAAAAHAAgACAACAAwAAAACAA0AFwAA" + 97 "AAoAAQAFABQAFgAI"), 98 Base64.getDecoder().decode( 99 "ZGV4CjAzNQAUg8BCAAAAAAAAAAAAAAAAAAAAAAAAAAC8AwAAcAAAAHhWNBIAAAAAAAAAAPgCAAAU" + 100 "AAAAcAAAAAkAAADAAAAAAgAAAOQAAAABAAAA/AAAAAQAAAAEAQAAAQAAACQBAAB4AgAARAEAAEQB" + 101 "AABMAQAAVgEAAHABAAB/AQAAowEAAMMBAADaAQAA7gEAAAICAAAWAgAAJAIAADACAAAzAgAANwIA" + 102 "AEQCAABKAgAATwIAAFgCAABfAgAAAgAAAAMAAAAEAAAABQAAAAYAAAAHAAAACAAAAAkAAAAMAAAA" + 103 "DAAAAAgAAAAAAAAADQAAAAgAAABoAgAABwAEABAAAAAAAAAAAAAAAAAAAAASAAAABAABABEAAAAF" + 104 "AAAAAAAAAAAAAAAAAAAABQAAAAAAAAAKAAAA6AIAALwCAAAAAAAABjxpbml0PgAIR29vZGJ5ZTIA" + 105 "GExhcnQvVGVzdDk0NCRUcmFuc2Zvcm0yOwANTGFydC9UZXN0OTQ0OwAiTGRhbHZpay9hbm5vdGF0" + 106 "aW9uL0VuY2xvc2luZ0NsYXNzOwAeTGRhbHZpay9hbm5vdGF0aW9uL0lubmVyQ2xhc3M7ABVMamF2" + 107 "YS9pby9QcmludFN0cmVhbTsAEkxqYXZhL2xhbmcvT2JqZWN0OwASTGphdmEvbGFuZy9TdHJpbmc7" + 108 "ABJMamF2YS9sYW5nL1N5c3RlbTsADFRlc3Q5NDQuamF2YQAKVHJhbnNmb3JtMgABVgACVkwAC2Fj" + 109 "Y2Vzc0ZsYWdzAARuYW1lAANvdXQAB3ByaW50bG4ABXNheUhpAAV2YWx1ZQAAAAEAAAAGAAAABQAH" + 110 "DgAHAAcOAQgPAAAAAAEAAQABAAAAcAIAAAQAAABwEAMAAAAOAAMAAQACAAAAdQIAAAkAAABiAAAA" + 111 "GwEBAAAAbiACABAADgAAAAAAAQEAgIAEgAUBAZgFAAACAgETGAECAwIOBAgPFwsAAgAAAMwCAADS" + 112 "AgAA3AIAAAAAAAAAAAAAAAAAABAAAAAAAAAAAQAAAAAAAAABAAAAFAAAAHAAAAACAAAACQAAAMAA" + 113 "AAADAAAAAgAAAOQAAAAEAAAAAQAAAPwAAAAFAAAABAAAAAQBAAAGAAAAAQAAACQBAAACIAAAFAAA" + 114 "AEQBAAABEAAAAQAAAGgCAAADIAAAAgAAAHACAAABIAAAAgAAAIACAAAAIAAAAQAAALwCAAAEIAAA" + 115 "AgAAAMwCAAADEAAAAQAAANwCAAAGIAAAAQAAAOgCAAAAEAAAAQAAAPgCAAA=")); 116 run()117 public static void run() throws Exception { 118 Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE); 119 doTest(); 120 System.out.println("Passed"); 121 } 122 checkIsInstance(Class<?> klass, Object o)123 private static void checkIsInstance(Class<?> klass, Object o) throws Exception { 124 if (!klass.isInstance(o)) { 125 throw new Exception(klass + " is not the class of " + o); 126 } 127 } 128 arrayContains(long[] arr, long value)129 private static boolean arrayContains(long[] arr, long value) { 130 if (arr == null) { 131 return false; 132 } 133 for (int i = 0; i < arr.length; i++) { 134 if (arr[i] == value) { 135 return true; 136 } 137 } 138 return false; 139 } 140 141 /** 142 * Checks that we can find the dex-file for the given class in its classloader. 143 * 144 * Throws if it fails. 145 */ checkDexFileInClassLoader(Class<?> klass)146 private static void checkDexFileInClassLoader(Class<?> klass) throws Exception { 147 // If all the android BCP classes were availible when compiling this test and access checks 148 // weren't a thing this function would be written as follows: 149 // 150 // long dexFilePtr = getDexFilePointer(klass); 151 // dalvik.system.BaseDexClassLoader loader = 152 // (dalvik.system.BaseDexClassLoader)klass.getClassLoader(); 153 // dalvik.system.DexPathList pathListValue = loader.pathList; 154 // dalvik.system.DexPathList.Element[] elementArrayValue = pathListValue.dexElements; 155 // int array_length = elementArrayValue.length; 156 // for (int i = 0; i < array_length; i++) { 157 // dalvik.system.DexPathList.Element curElement = elementArrayValue[i]; 158 // dalvik.system.DexFile curDexFile = curElement.dexFile; 159 // if (curDexFile == null) { 160 // continue; 161 // } 162 // long[] curCookie = (long[])curDexFile.mCookie; 163 // long[] curInternalCookie = (long[])curDexFile.mInternalCookie; 164 // if (arrayContains(curCookie, dexFilePtr) || arrayContains(curInternalCookie, dexFilePtr)) { 165 // return; 166 // } 167 // } 168 // throw new Exception( 169 // "Unable to find dex file pointer " + dexFilePtr + " in class loader for " + klass); 170 171 // Get all the fields and classes we need by reflection. 172 Class<?> baseDexClassLoaderClass = Class.forName("dalvik.system.BaseDexClassLoader"); 173 Field pathListField = baseDexClassLoaderClass.getDeclaredField("pathList"); 174 175 Class<?> dexPathListClass = Class.forName("dalvik.system.DexPathList"); 176 Field elementArrayField = dexPathListClass.getDeclaredField("dexElements"); 177 178 Class<?> dexPathListElementClass = Class.forName("dalvik.system.DexPathList$Element"); 179 Field dexFileField = dexPathListElementClass.getDeclaredField("dexFile"); 180 181 Class<?> dexFileClass = Class.forName("dalvik.system.DexFile"); 182 Field dexFileCookieField = dexFileClass.getDeclaredField("mCookie"); 183 Field dexFileInternalCookieField = dexFileClass.getDeclaredField("mInternalCookie"); 184 185 // Make all the fields accessible 186 AccessibleObject.setAccessible(new AccessibleObject[] { pathListField, 187 elementArrayField, 188 dexFileField, 189 dexFileCookieField, 190 dexFileInternalCookieField }, true); 191 192 long dexFilePtr = getDexFilePointer(klass); 193 194 ClassLoader loader = klass.getClassLoader(); 195 checkIsInstance(baseDexClassLoaderClass, loader); 196 // DexPathList pathListValue = ((BaseDexClassLoader) loader).pathList; 197 Object pathListValue = pathListField.get(loader); 198 199 checkIsInstance(dexPathListClass, pathListValue); 200 201 // DexPathList.Element[] elementArrayValue = pathListValue.dexElements; 202 Object elementArrayValue = elementArrayField.get(pathListValue); 203 if (!elementArrayValue.getClass().isArray() || 204 elementArrayValue.getClass().getComponentType() != dexPathListElementClass) { 205 throw new Exception("elementArrayValue is not an " + dexPathListElementClass + " array!"); 206 } 207 // int array_length = elementArrayValue.length; 208 int array_length = Array.getLength(elementArrayValue); 209 for (int i = 0; i < array_length; i++) { 210 // DexPathList.Element curElement = elementArrayValue[i]; 211 Object curElement = Array.get(elementArrayValue, i); 212 checkIsInstance(dexPathListElementClass, curElement); 213 214 // DexFile curDexFile = curElement.dexFile; 215 Object curDexFile = dexFileField.get(curElement); 216 if (curDexFile == null) { 217 continue; 218 } 219 checkIsInstance(dexFileClass, curDexFile); 220 221 // long[] curCookie = (long[])curDexFile.mCookie; 222 long[] curCookie = (long[])dexFileCookieField.get(curDexFile); 223 // long[] curInternalCookie = (long[])curDexFile.mInternalCookie; 224 long[] curInternalCookie = (long[])dexFileInternalCookieField.get(curDexFile); 225 226 if (arrayContains(curCookie, dexFilePtr) || arrayContains(curInternalCookie, dexFilePtr)) { 227 return; 228 } 229 } 230 throw new Exception( 231 "Unable to find dex file pointer " + dexFilePtr + " in class loader for " + klass); 232 } 233 doTest()234 private static void doTest() throws Exception { 235 Transform t = new Transform(); 236 Transform2 t2 = new Transform2(); 237 238 long initial_t1_dex = getDexFilePointer(Transform.class); 239 long initial_t2_dex = getDexFilePointer(Transform2.class); 240 if (initial_t2_dex != initial_t1_dex) { 241 throw new Exception("The classes " + Transform.class + " and " + Transform2.class + " " + 242 "have different initial dex files!"); 243 } 244 checkDexFileInClassLoader(Transform.class); 245 checkDexFileInClassLoader(Transform2.class); 246 247 // Make sure they are loaded 248 t.sayHi(); 249 t2.sayHi(); 250 // Redefine both of the classes. 251 Redefinition.doMultiClassRedefinition(TRANSFORM_DEFINITION, TRANSFORM2_DEFINITION); 252 // Make sure we actually transformed them! 253 t.sayHi(); 254 t2.sayHi(); 255 256 long final_t1_dex = getDexFilePointer(Transform.class); 257 long final_t2_dex = getDexFilePointer(Transform2.class); 258 if (final_t2_dex == final_t1_dex) { 259 throw new Exception("The classes " + Transform.class + " and " + Transform2.class + " " + 260 "have the same initial dex files!"); 261 } else if (final_t1_dex == initial_t1_dex) { 262 throw new Exception("The class " + Transform.class + " did not get a new dex file!"); 263 } else if (final_t2_dex == initial_t2_dex) { 264 throw new Exception("The class " + Transform2.class + " did not get a new dex file!"); 265 } 266 // Check to make sure the new dex files are in the class loader. 267 checkDexFileInClassLoader(Transform.class); 268 checkDexFileInClassLoader(Transform2.class); 269 } 270 271 // Gets the 'long' (really a native pointer) that is stored in the ClassLoader representing the 272 // DexFile a class is loaded from. This is plucked out of the internal DexCache object associated 273 // with the class. getDexFilePointer(Class<?> target)274 private static long getDexFilePointer(Class<?> target) throws Exception { 275 // If all the android BCP classes were available when compiling this test and access checks 276 // weren't a thing this function would be written as follows: 277 // 278 // java.lang.DexCache dexCacheObject = target.dexCache; 279 // if (dexCacheObject == null) { 280 // return 0; 281 // } 282 // return dexCacheObject.dexFile; 283 Field dexCacheField = Class.class.getDeclaredField("dexCache"); 284 285 Class<?> dexCacheClass = Class.forName("java.lang.DexCache"); 286 Field dexFileField = dexCacheClass.getDeclaredField("dexFile"); 287 288 AccessibleObject.setAccessible(new AccessibleObject[] { dexCacheField, dexFileField }, true); 289 290 Object dexCacheObject = dexCacheField.get(target); 291 if (dexCacheObject == null) { 292 return 0; 293 } 294 checkIsInstance(dexCacheClass, dexCacheObject); 295 return dexFileField.getLong(dexCacheObject); 296 } 297 } 298