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.invoke.MethodHandle; 20 import java.lang.invoke.MethodHandles; 21 import java.lang.ref.*; 22 import java.lang.reflect.*; 23 import java.util.*; 24 25 public class Test1975 { run()26 public static void run() throws Exception { 27 Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE); 28 doTest(); 29 } 30 31 private static final boolean PRINT_NONDETERMINISTIC = false; 32 33 public static WeakHashMap<Object, Long> id_nums = new WeakHashMap<>(); 34 public static long next_id = 0; 35 printGeneric(Object o)36 public static String printGeneric(Object o) { 37 Long id = id_nums.get(o); 38 if (id == null) { 39 id = Long.valueOf(next_id++); 40 id_nums.put(o, id); 41 } 42 if (o == null) { 43 return "(ID: " + id + ") <NULL>"; 44 } 45 Class oc = o.getClass(); 46 if (oc.isArray() && oc.getComponentType() == Byte.TYPE) { 47 return "(ID: " 48 + id 49 + ") " 50 + Arrays.toString(Arrays.copyOf((byte[]) o, 10)).replace(']', ',') 51 + " ...]"; 52 } else { 53 return "(ID: " + id + ") " + o.toString(); 54 } 55 } 56 57 // Since we are adding fields we redefine this class with the Transform1975 class to add new 58 // field-reads. 59 public static final class ReadTransformFields implements Runnable { run()60 public void run() { 61 System.out.println("Read CUR_CLASS field: " + printGeneric(Transform1975.CUR_CLASS)); 62 System.out.println( 63 "Read REDEFINED_DEX_BYTES field: " + printGeneric(Transform1975.REDEFINED_DEX_BYTES)); 64 } 65 } 66 67 /* Base64 encoded dex file for: 68 * public static final class ReadTransformFields implements Runnable { 69 * public void run() { 70 * System.out.println("Read CUR_CLASS field: " + printGeneric(Transform1975.CUR_CLASS)); 71 * System.out.println("Read REDEFINED_DEX_BYTES field: " + printGeneric(Transform1975.REDEFINED_DEX_BYTES)); 72 * System.out.println("Read NEW_STRING field: " + printGeneric(Transform1975.NEW_STRING)); 73 * } 74 * } 75 */ 76 private static final byte[] NEW_READ_BYTES = 77 Base64.getDecoder() 78 .decode( 79 "ZGV4CjAzNQCHIfWvfkMos9E+Snhux5rSGhnDAbiVJlyYBgAAcAAAAHhWNBIAAAAAAAAAANQFAAAk" 80 + "AAAAcAAAAA4AAAAAAQAABQAAADgBAAAEAAAAdAEAAAgAAACUAQAAAQAAANQBAACkBAAA9AEAAO4C" 81 + "AAD2AgAAAQMAAAQDAAAIAwAALAMAADwDAABRAwAAdQMAAJUDAACsAwAAvwMAANMDAADpAwAA/QMA" 82 + "ABgEAAAsBAAAOAQAAE0EAABlBAAAfgQAAKAEAAC1BAAAxAQAAMcEAADLBAAAzwQAANwEAADkBAAA" 83 + "6gQAAO8EAAD9BAAABgUAAAsFAAAVBQAAHAUAAAQAAAAFAAAABgAAAAcAAAAIAAAACQAAAAoAAAAL" 84 + "AAAADAAAAA0AAAAOAAAADwAAABcAAAAZAAAAAgAAAAkAAAAAAAAAAwAAAAkAAADgAgAAAwAAAAoA" 85 + "AADoAgAAFwAAAAwAAAAAAAAAGAAAAAwAAADoAgAAAgAGAAEAAAACAAkAEAAAAAIADQARAAAACwAF" 86 + "AB0AAAAAAAMAAAAAAAAAAwAgAAAAAQABAB4AAAAFAAQAHwAAAAcAAwAAAAAACgADAAAAAAAKAAIA" 87 + "GwAAAAoAAAAhAAAAAAAAABEAAAAHAAAA2AIAABYAAADEBQAAowUAAAAAAAABAAEAAQAAAMYCAAAE" 88 + "AAAAcBAEAAAADgAFAAEAAgAAAMoCAABVAAAAYgADAGIBAABxEAIAAQAMASICCgBwEAUAAgAaAxIA" 89 + "biAGADIAbiAGABIAbhAHAAIADAFuIAMAEABiAAMAYgECAHEQAgABAAwBIgIKAHAQBQACABoDFABu" 90 + "IAYAMgBuIAYAEgBuEAcAAgAMAW4gAwAQAGIAAwBiAQEAcRACAAEADAEiAgoAcBAFAAIAGgMTAG4g" 91 + "BgAyAG4gBgASAG4QBwACAAwBbiADABAADgAEAA4ABgAOARwPARwPARwPAAABAAAACAAAAAEAAAAH" 92 + "AAAAAQAAAAkABjxpbml0PgAJQ1VSX0NMQVNTAAFMAAJMTAAiTGFydC9UZXN0MTk3NSRSZWFkVHJh" 93 + "bnNmb3JtRmllbGRzOwAOTGFydC9UZXN0MTk3NTsAE0xhcnQvVHJhbnNmb3JtMTk3NTsAIkxkYWx2" 94 + "aWsvYW5ub3RhdGlvbi9FbmNsb3NpbmdDbGFzczsAHkxkYWx2aWsvYW5ub3RhdGlvbi9Jbm5lckNs" 95 + "YXNzOwAVTGphdmEvaW8vUHJpbnRTdHJlYW07ABFMamF2YS9sYW5nL0NsYXNzOwASTGphdmEvbGFu" 96 + "Zy9PYmplY3Q7ABRMamF2YS9sYW5nL1J1bm5hYmxlOwASTGphdmEvbGFuZy9TdHJpbmc7ABlMamF2" 97 + "YS9sYW5nL1N0cmluZ0J1aWxkZXI7ABJMamF2YS9sYW5nL1N5c3RlbTsACk5FV19TVFJJTkcAE1JF" 98 + "REVGSU5FRF9ERVhfQllURVMAFlJlYWQgQ1VSX0NMQVNTIGZpZWxkOiAAF1JlYWQgTkVXX1NUUklO" 99 + "RyBmaWVsZDogACBSZWFkIFJFREVGSU5FRF9ERVhfQllURVMgZmllbGQ6IAATUmVhZFRyYW5zZm9y" 100 + "bUZpZWxkcwANVGVzdDE5NzUuamF2YQABVgACVkwAAltCAAthY2Nlc3NGbGFncwAGYXBwZW5kAARu" 101 + "YW1lAANvdXQADHByaW50R2VuZXJpYwAHcHJpbnRsbgADcnVuAAh0b1N0cmluZwAFdmFsdWUAdn5+" 102 + "RDh7ImNvbXBpbGF0aW9uLW1vZGUiOiJkZWJ1ZyIsIm1pbi1hcGkiOjEsInNoYS0xIjoiYTgzNTJm" 103 + "MjU0ODg1MzYyY2NkOGQ5MDlkMzUyOWM2MDA5NGRkODk2ZSIsInZlcnNpb24iOiIxLjYuMjAtZGV2" 104 + "In0AAgMBIhgBAgQCGgQZHBcVAAABAQCBgAT0AwEBjAQAAAAAAAAAAgAAAJQFAACaBQAAuAUAAAAA" 105 + "AAAAAAAAAAAAABAAAAAAAAAAAQAAAAAAAAABAAAAJAAAAHAAAAACAAAADgAAAAABAAADAAAABQAA" 106 + "ADgBAAAEAAAABAAAAHQBAAAFAAAACAAAAJQBAAAGAAAAAQAAANQBAAABIAAAAgAAAPQBAAADIAAA" 107 + "AgAAAMYCAAABEAAAAwAAANgCAAACIAAAJAAAAO4CAAAEIAAAAgAAAJQFAAAAIAAAAQAAAKMFAAAD" 108 + "EAAAAgAAALQFAAAGIAAAAQAAAMQFAAAAEAAAAQAAANQFAAA="); 109 ReadFields()110 static void ReadFields() throws Exception { 111 Runnable r = new ReadTransformFields(); 112 System.out.println("Reading with reflection."); 113 for (Field f : Transform1975.class.getFields()) { 114 System.out.println(f.toString() + " = " + printGeneric(f.get(null))); 115 } 116 System.out.println("Reading normally in same class."); 117 Transform1975.readFields(); 118 System.out.println("Reading with native."); 119 readNativeFields(Transform1975.class, getNativeFields(Transform1975.class.getFields())); 120 System.out.println("Reading normally in other class."); 121 r.run(); 122 System.out.println("Reading using method handles."); 123 readMethodHandles(getMethodHandles(Transform1975.class.getFields())); 124 System.out.println("Doing modification maybe"); 125 Transform1975.doSomething(); 126 System.out.println("Reading with reflection after possible modification."); 127 for (Field f : Transform1975.class.getFields()) { 128 System.out.println(f.toString() + " = " + printGeneric(f.get(null))); 129 } 130 System.out.println("Reading normally in same class after possible modification."); 131 Transform1975.readFields(); 132 System.out.println("Reading with native after possible modification."); 133 readNativeFields(Transform1975.class, getNativeFields(Transform1975.class.getFields())); 134 System.out.println("Reading normally in other class after possible modification."); 135 r.run(); 136 System.out.println("Reading using method handles."); 137 readMethodHandles(getMethodHandles(Transform1975.class.getFields())); 138 } 139 140 public static final class MethodHandleWrapper { 141 private MethodHandle mh; 142 private Field f; MethodHandleWrapper(MethodHandle mh, Field f)143 public MethodHandleWrapper(MethodHandle mh, Field f) { 144 this.f = f; 145 this.mh = mh; 146 } getHandle()147 public MethodHandle getHandle() { 148 return mh; 149 } getField()150 public Field getField() { 151 return f; 152 } invoke()153 public Object invoke() throws Throwable { 154 return mh.invoke(); 155 } toString()156 public String toString() { 157 return mh.toString(); 158 } 159 } 160 getMethodHandles(Field[] fields)161 public static MethodHandleWrapper[] getMethodHandles(Field[] fields) throws Exception { 162 final MethodHandles.Lookup l = MethodHandles.lookup(); 163 MethodHandleWrapper[] res = new MethodHandleWrapper[fields.length]; 164 for (int i = 0; i < res.length; i++) { 165 res[i] = new MethodHandleWrapper(l.unreflectGetter(fields[i]), fields[i]);; 166 } 167 return res; 168 } 169 readMethodHandles(MethodHandleWrapper[] handles)170 public static void readMethodHandles(MethodHandleWrapper[] handles) throws Exception { 171 for (MethodHandleWrapper h : handles) { 172 try { 173 System.out.println(printGeneric(h) + " (" + h.getField() + ") = " + printGeneric(h.invoke())); 174 } catch (Throwable t) { 175 if (t instanceof Exception) { 176 throw (Exception)t; 177 } else if (t instanceof Error) { 178 throw (Error)t; 179 } else { 180 throw new RuntimeException("Unexpected throwable thrown!", t); 181 } 182 } 183 } 184 } doTest()185 public static void doTest() throws Exception { 186 // TODO It would be good to have a test of invoke-custom too but since that requires smali and 187 // internally we just store the resolved MethodHandle this should all be good enough. 188 189 // Grab Field objects from before the transformation. 190 Field[] old_fields = Transform1975.class.getFields(); 191 for (Field f : old_fields) { 192 System.out.println("Saving Field object " + printGeneric(f) + " for later"); 193 } 194 // Grab jfieldIDs from before the transformation. 195 long[] old_native_fields = getNativeFields(Transform1975.class.getFields()); 196 // Grab MethodHandles from before the transformation. 197 MethodHandleWrapper[] handles = getMethodHandles(Transform1975.class.getFields()); 198 for (MethodHandleWrapper h : handles) { 199 System.out.println("Saving MethodHandle object " + printGeneric(h) + " for later"); 200 } 201 // Grab a 'setter' MethodHandle from before the redefinition. 202 Field cur_class_field = Transform1975.class.getDeclaredField("CUR_CLASS"); 203 MethodHandleWrapper write_wrapper = new MethodHandleWrapper(MethodHandles.lookup().unreflectSetter(cur_class_field), cur_class_field); 204 System.out.println("Saving writable MethodHandle " + printGeneric(write_wrapper) + " for later"); 205 206 // Read the fields in all possible ways. 207 System.out.println("Reading fields before redefinition"); 208 ReadFields(); 209 // Redefine the transform class. Also change the ReadTransformFields so we don't have to deal 210 // with annoying compilation stuff. 211 Redefinition.doCommonStructuralClassRedefinition( 212 Transform1975.class, Transform1975.REDEFINED_DEX_BYTES); 213 Redefinition.doCommonClassRedefinition( 214 ReadTransformFields.class, new byte[] {}, NEW_READ_BYTES); 215 // Read the fields in all possible ways. 216 System.out.println("Reading fields after redefinition"); 217 ReadFields(); 218 // Check that the old Field, jfieldID, and MethodHandle objects were updated. 219 System.out.println("reading reflectively with old reflection objects"); 220 for (Field f : old_fields) { 221 System.out.println("OLD FIELD OBJECT: " + f.toString() + " = " + printGeneric(f.get(null))); 222 } 223 System.out.println("reading natively with old jfieldIDs"); 224 readNativeFields(Transform1975.class, old_native_fields); 225 // Make sure the fields keep the same id. 226 System.out.println("reading natively with new jfieldIDs"); 227 long[] new_fields = getNativeFields(Transform1975.class.getFields()); 228 Arrays.sort(old_native_fields); 229 Arrays.sort(new_fields); 230 boolean different = new_fields.length == old_native_fields.length; 231 for (int i = 0; i < old_native_fields.length && !different; i++) { 232 different = different || new_fields[i] != old_native_fields[i]; 233 } 234 if (different) { 235 System.out.println( 236 "Missing expected fields! " 237 + Arrays.toString(new_fields) 238 + " vs " 239 + Arrays.toString(old_native_fields)); 240 } 241 // Make sure the old handles work. 242 System.out.println("Reading with old method handles"); 243 readMethodHandles(handles); 244 System.out.println("Reading with new method handles"); 245 readMethodHandles(getMethodHandles(Transform1975.class.getFields())); 246 System.out.println("Writing " + printGeneric(Test1975.class) + " to CUR_CLASS with old method handle"); 247 try { 248 write_wrapper.getHandle().invokeExact(Test1975.class); 249 } catch (Throwable t) { 250 throw new RuntimeException("something threw", t); 251 } 252 System.out.println("Reading changed value"); 253 System.out.println("CUR_CLASS is now " + printGeneric(Transform1975.CUR_CLASS)); 254 } 255 printNativeField(long id, Field f, Object value)256 private static void printNativeField(long id, Field f, Object value) { 257 System.out.println( 258 "Field" + (PRINT_NONDETERMINISTIC ? " " + id : "") + " " + f + " = " + printGeneric(value)); 259 } 260 getNativeFields(Field[] fields)261 public static native long[] getNativeFields(Field[] fields); 262 readNativeFields(Class<?> field_class, long[] sfields)263 public static native void readNativeFields(Class<?> field_class, long[] sfields); 264 } 265