1 /* 2 * Copyright (C) 2016 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 java.lang.reflect.Field; 20 import java.util.Base64; 21 import java.nio.ByteBuffer; 22 23 import dalvik.system.ClassExt; 24 import dalvik.system.InMemoryDexClassLoader; 25 26 public class Test981 { 27 28 static class Transform { sayHi()29 public void sayHi() { 30 System.out.println("hello"); 31 } 32 } 33 34 static class Transform2 { sayHi()35 public void sayHi() { 36 System.out.println("hello2"); 37 } 38 } 39 40 /** 41 * base64 encoded class/dex file for 42 * static class Transform { 43 * public void sayHi() { 44 * System.out.println("Goodbye"); 45 * } 46 * } 47 */ 48 private static final byte[] DEX_BYTES_1 = Base64.getDecoder().decode( 49 "ZGV4CjAzNQB+giqQAAAAAAAAAAAAAAAAAAAAAAAAAAC4AwAAcAAAAHhWNBIAAAAAAAAAAPQCAAAU" + 50 "AAAAcAAAAAkAAADAAAAAAgAAAOQAAAABAAAA/AAAAAQAAAAEAQAAAQAAACQBAAB0AgAARAEAAEQB" + 51 "AABMAQAAVQEAAG4BAAB9AQAAoQEAAMEBAADYAQAA7AEAAAACAAAUAgAAIgIAAC0CAAAwAgAANAIA" + 52 "AEECAABHAgAATAIAAFUCAABcAgAAAgAAAAMAAAAEAAAABQAAAAYAAAAHAAAACAAAAAkAAAAMAAAA" + 53 "DAAAAAgAAAAAAAAADQAAAAgAAABkAgAABwAEABAAAAAAAAAAAAAAAAAAAAASAAAABAABABEAAAAF" + 54 "AAAAAAAAAAAAAAAAAAAABQAAAAAAAAAKAAAA5AIAALgCAAAAAAAABjxpbml0PgAHR29vZGJ5ZQAX" + 55 "TGFydC9UZXN0OTgxJFRyYW5zZm9ybTsADUxhcnQvVGVzdDk4MTsAIkxkYWx2aWsvYW5ub3RhdGlv" + 56 "bi9FbmNsb3NpbmdDbGFzczsAHkxkYWx2aWsvYW5ub3RhdGlvbi9Jbm5lckNsYXNzOwAVTGphdmEv" + 57 "aW8vUHJpbnRTdHJlYW07ABJMamF2YS9sYW5nL09iamVjdDsAEkxqYXZhL2xhbmcvU3RyaW5nOwAS" + 58 "TGphdmEvbGFuZy9TeXN0ZW07AAxUZXN0OTgxLmphdmEACVRyYW5zZm9ybQABVgACVkwAC2FjY2Vz" + 59 "c0ZsYWdzAARuYW1lAANvdXQAB3ByaW50bG4ABXNheUhpAAV2YWx1ZQAAAQAAAAYAAAAFAAcOAAcA" + 60 "Bw4BCA8AAAAAAQABAAEAAABsAgAABAAAAHAQAwAAAA4AAwABAAIAAABxAgAACQAAAGIAAAAbAQEA" + 61 "AABuIAIAEAAOAAAAAAABAQCAgAT8BAEBlAUAAAICARMYAQIDAg4ECA8XCwACAAAAyAIAAM4CAADY" + 62 "AgAAAAAAAAAAAAAAAAAAEAAAAAAAAAABAAAAAAAAAAEAAAAUAAAAcAAAAAIAAAAJAAAAwAAAAAMA" + 63 "AAACAAAA5AAAAAQAAAABAAAA/AAAAAUAAAAEAAAABAEAAAYAAAABAAAAJAEAAAIgAAAUAAAARAEA" + 64 "AAEQAAABAAAAZAIAAAMgAAACAAAAbAIAAAEgAAACAAAAfAIAAAAgAAABAAAAuAIAAAQgAAACAAAA" + 65 "yAIAAAMQAAABAAAA2AIAAAYgAAABAAAA5AIAAAAQAAABAAAA9AIAAA=="); 66 67 /** 68 * base64 encoded class/dex file for 69 * static class Transform2 { 70 * public void sayHi() { 71 * System.out.println("Goodbye2"); 72 * } 73 * } 74 */ 75 private static final byte[] DEX_BYTES_2 = Base64.getDecoder().decode( 76 "ZGV4CjAzNQAhg+RVAAAAAAAAAAAAAAAAAAAAAAAAAAC8AwAAcAAAAHhWNBIAAAAAAAAAAPgCAAAU" + 77 "AAAAcAAAAAkAAADAAAAAAgAAAOQAAAABAAAA/AAAAAQAAAAEAQAAAQAAACQBAAB4AgAARAEAAEQB" + 78 "AABMAQAAVgEAAHABAAB/AQAAowEAAMMBAADaAQAA7gEAAAICAAAWAgAAJAIAADACAAAzAgAANwIA" + 79 "AEQCAABKAgAATwIAAFgCAABfAgAAAgAAAAMAAAAEAAAABQAAAAYAAAAHAAAACAAAAAkAAAAMAAAA" + 80 "DAAAAAgAAAAAAAAADQAAAAgAAABoAgAABwAEABAAAAAAAAAAAAAAAAAAAAASAAAABAABABEAAAAF" + 81 "AAAAAAAAAAAAAAAAAAAABQAAAAAAAAAKAAAA6AIAALwCAAAAAAAABjxpbml0PgAIR29vZGJ5ZTIA" + 82 "GExhcnQvVGVzdDk4MSRUcmFuc2Zvcm0yOwANTGFydC9UZXN0OTgxOwAiTGRhbHZpay9hbm5vdGF0" + 83 "aW9uL0VuY2xvc2luZ0NsYXNzOwAeTGRhbHZpay9hbm5vdGF0aW9uL0lubmVyQ2xhc3M7ABVMamF2" + 84 "YS9pby9QcmludFN0cmVhbTsAEkxqYXZhL2xhbmcvT2JqZWN0OwASTGphdmEvbGFuZy9TdHJpbmc7" + 85 "ABJMamF2YS9sYW5nL1N5c3RlbTsADFRlc3Q5ODEuamF2YQAKVHJhbnNmb3JtMgABVgACVkwAC2Fj" + 86 "Y2Vzc0ZsYWdzAARuYW1lAANvdXQAB3ByaW50bG4ABXNheUhpAAV2YWx1ZQAAAAEAAAAGAAAACgAH" + 87 "DgAMAAcOAQgPAAAAAAEAAQABAAAAcAIAAAQAAABwEAMAAAAOAAMAAQACAAAAdQIAAAkAAABiAAAA" + 88 "GwEBAAAAbiACABAADgAAAAAAAQEAgIAEgAUBAZgFAAACAgETGAECAwIOBAgPFwsAAgAAAMwCAADS" + 89 "AgAA3AIAAAAAAAAAAAAAAAAAABAAAAAAAAAAAQAAAAAAAAABAAAAFAAAAHAAAAACAAAACQAAAMAA" + 90 "AAADAAAAAgAAAOQAAAAEAAAAAQAAAPwAAAAFAAAABAAAAAQBAAAGAAAAAQAAACQBAAACIAAAFAAA" + 91 "AEQBAAABEAAAAQAAAGgCAAADIAAAAgAAAHACAAABIAAAAgAAAIACAAAAIAAAAQAAALwCAAAEIAAA" + 92 "AgAAAMwCAAADEAAAAQAAANwCAAAGIAAAAQAAAOgCAAAAEAAAAQAAAPgCAAA="); 93 94 /** 95 * base64 encoded class/dex file for 96 * class Transform3 { 97 * public void sayHi() { 98 * System.out.println("hello3"); 99 * } 100 * } 101 */ 102 private static final byte[] DEX_BYTES_3_INITIAL = Base64.getDecoder().decode( 103 "ZGV4CjAzNQC2W2fBsAeLNAwWYlG8FVigzfsV7nBWITzQAgAAcAAAAHhWNBIAAAAAAAAAADACAAAO" + 104 "AAAAcAAAAAYAAACoAAAAAgAAAMAAAAABAAAA2AAAAAQAAADgAAAAAQAAAAABAACwAQAAIAEAAGIB" + 105 "AABqAQAAeAEAAI8BAACjAQAAtwEAAMsBAADcAQAA3wEAAOMBAAD3AQAA/wEAAAQCAAANAgAAAQAA" + 106 "AAIAAAADAAAABAAAAAUAAAAHAAAABwAAAAUAAAAAAAAACAAAAAUAAABcAQAABAABAAsAAAAAAAAA" + 107 "AAAAAAAAAAANAAAAAQABAAwAAAACAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAGAAAAAAAAAB8CAAAA" + 108 "AAAAAQABAAEAAAAUAgAABAAAAHAQAwAAAA4AAwABAAIAAAAZAgAACQAAAGIAAAAbAQoAAABuIAIA" + 109 "EAAOAAAAAQAAAAMABjxpbml0PgAMTFRyYW5zZm9ybTM7ABVMamF2YS9pby9QcmludFN0cmVhbTsA" + 110 "EkxqYXZhL2xhbmcvT2JqZWN0OwASTGphdmEvbGFuZy9TdHJpbmc7ABJMamF2YS9sYW5nL1N5c3Rl" + 111 "bTsAD1RyYW5zZm9ybTMuamF2YQABVgACVkwAEmVtaXR0ZXI6IGphY2stNC4zMAAGaGVsbG8zAANv" + 112 "dXQAB3ByaW50bG4ABXNheUhpAAIABw4ABAAHDocAAAABAQCAgASgAgEBuAIAAAANAAAAAAAAAAEA" + 113 "AAAAAAAAAQAAAA4AAABwAAAAAgAAAAYAAACoAAAAAwAAAAIAAADAAAAABAAAAAEAAADYAAAABQAA" + 114 "AAQAAADgAAAABgAAAAEAAAAAAQAAASAAAAIAAAAgAQAAARAAAAEAAABcAQAAAiAAAA4AAABiAQAA" + 115 "AyAAAAIAAAAUAgAAACAAAAEAAAAfAgAAABAAAAEAAAAwAgAA"); 116 117 /** 118 * base64 encoded class/dex file for 119 * class Transform3 { 120 * public void sayHi() { 121 * System.out.println("Goodbye3"); 122 * } 123 * } 124 */ 125 private static final byte[] DEX_BYTES_3_FINAL = Base64.getDecoder().decode( 126 "ZGV4CjAzNQBAXE5GthgMydaFBuinf+ZBfXcBYIw2UlXQAgAAcAAAAHhWNBIAAAAAAAAAADACAAAO" + 127 "AAAAcAAAAAYAAACoAAAAAgAAAMAAAAABAAAA2AAAAAQAAADgAAAAAQAAAAABAACwAQAAIAEAAGIB" + 128 "AABqAQAAdAEAAIIBAACZAQAArQEAAMEBAADVAQAA5gEAAOkBAADtAQAAAQIAAAYCAAAPAgAAAgAA" + 129 "AAMAAAAEAAAABQAAAAYAAAAIAAAACAAAAAUAAAAAAAAACQAAAAUAAABcAQAABAABAAsAAAAAAAAA" + 130 "AAAAAAAAAAANAAAAAQABAAwAAAACAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAHAAAAAAAAACECAAAA" + 131 "AAAAAQABAAEAAAAWAgAABAAAAHAQAwAAAA4AAwABAAIAAAAbAgAACQAAAGIAAAAbAQEAAABuIAIA" + 132 "EAAOAAAAAQAAAAMABjxpbml0PgAIR29vZGJ5ZTMADExUcmFuc2Zvcm0zOwAVTGphdmEvaW8vUHJp" + 133 "bnRTdHJlYW07ABJMamF2YS9sYW5nL09iamVjdDsAEkxqYXZhL2xhbmcvU3RyaW5nOwASTGphdmEv" + 134 "bGFuZy9TeXN0ZW07AA9UcmFuc2Zvcm0zLmphdmEAAVYAAlZMABJlbWl0dGVyOiBqYWNrLTQuMzAA" + 135 "A291dAAHcHJpbnRsbgAFc2F5SGkAAgAHDgAEAAcOhwAAAAEBAICABKACAQG4AgANAAAAAAAAAAEA" + 136 "AAAAAAAAAQAAAA4AAABwAAAAAgAAAAYAAACoAAAAAwAAAAIAAADAAAAABAAAAAEAAADYAAAABQAA" + 137 "AAQAAADgAAAABgAAAAEAAAAAAQAAASAAAAIAAAAgAQAAARAAAAEAAABcAQAAAiAAAA4AAABiAQAA" + 138 "AyAAAAIAAAAWAgAAACAAAAEAAAAhAgAAABAAAAEAAAAwAgAA"); 139 run()140 public static void run() throws Exception { 141 Redefinition.setTestConfiguration(Redefinition.Config.COMMON_RETRANSFORM); 142 doTest(); 143 } 144 assertSame(Object a, Object b)145 private static void assertSame(Object a, Object b) throws Exception { 146 if (a != b) { 147 throw new AssertionError("'" + (a != null ? a.toString() : "null") + "' is not the same as " + 148 "'" + (b != null ? b.toString() : "null") + "'"); 149 } 150 } 151 getObjectField(Object o, String name)152 private static Object getObjectField(Object o, String name) throws Exception { 153 return getObjectField(o, o.getClass(), name); 154 } 155 getObjectField(Object o, Class<?> type, String name)156 private static Object getObjectField(Object o, Class<?> type, String name) throws Exception { 157 Field f = type.getDeclaredField(name); 158 f.setAccessible(true); 159 return f.get(o); 160 } 161 getOriginalDexFile(Class<?> k)162 private static Object getOriginalDexFile(Class<?> k) throws Exception { 163 ClassExt ext_data_object = (ClassExt) getObjectField(k, "extData"); 164 if (ext_data_object == null) { 165 return null; 166 } 167 168 return getObjectField(ext_data_object, "originalDexFile"); 169 } 170 doTest()171 public static void doTest() throws Exception { 172 // Make sure both of these are loaded prior to transformations being added so they have the same 173 // original dex files. 174 Transform t1 = new Transform(); 175 Transform2 t2 = new Transform2(); 176 177 assertSame(null, getOriginalDexFile(t1.getClass())); 178 assertSame(null, getOriginalDexFile(t2.getClass())); 179 assertSame(null, getOriginalDexFile(Test981.class)); 180 181 Redefinition.addCommonTransformationResult("art/Test981$Transform", new byte[0], DEX_BYTES_1); 182 Redefinition.addCommonTransformationResult("art/Test981$Transform2", new byte[0], DEX_BYTES_2); 183 Redefinition.enableCommonRetransformation(true); 184 Redefinition.doCommonClassRetransformation(Transform.class, Transform2.class); 185 186 assertSame(getOriginalDexFile(t1.getClass()), getOriginalDexFile(t2.getClass())); 187 assertSame(null, getOriginalDexFile(Test981.class)); 188 // Make sure that the original dex file is a DexCache object. 189 assertSame(getOriginalDexFile(t1.getClass()).getClass(), Class.forName("java.lang.DexCache")); 190 191 // Check that we end up with a byte[] if we do a direct RedefineClasses 192 Redefinition.enableCommonRetransformation(false); 193 Redefinition.doCommonClassRedefinition(Transform.class, new byte[0], DEX_BYTES_1); 194 assertSame((new byte[0]).getClass(), getOriginalDexFile(t1.getClass()).getClass()); 195 196 // Check we don't have anything if we don't have any originalDexFile if the onload 197 // transformation doesn't do anything. 198 Redefinition.enableCommonRetransformation(true); 199 Class<?> transform3Class = new InMemoryDexClassLoader( 200 ByteBuffer.wrap(DEX_BYTES_3_INITIAL), Test981.class.getClassLoader()).loadClass("Transform3"); 201 assertSame(null, getOriginalDexFile(transform3Class)); 202 203 // Check that we end up with a java.lang.Long pointer if we do an 'on-load' redefinition. 204 Redefinition.addCommonTransformationResult("Transform3", new byte[0], DEX_BYTES_3_FINAL); 205 Redefinition.enableCommonRetransformation(true); 206 Class<?> transform3ClassTransformed = new InMemoryDexClassLoader( 207 ByteBuffer.wrap(DEX_BYTES_3_INITIAL), Test981.class.getClassLoader()).loadClass("Transform3"); 208 assertSame(Long.class, getOriginalDexFile(transform3ClassTransformed).getClass()); 209 } 210 } 211