This is the second part of the JVM Inside Out series. For your convenience you can find other parts in the table of contents in Part 1 — Getting object address
Last time we saw how to read object address. We can use similar trick to read object contents as integers. Let’s see this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
package unsafe; import java.lang.reflect.AccessibleObject; import java.lang.reflect.Field; public class Play { public static void main (String[] args) throws java.lang.Exception { Dummy foo = new Dummy(); foo.value = 0xBADF00D; foo.value2 = 0xDEADBEEF; foo.value3 = 0xFEDCFEDC; AddressExtractor addressExtractor = new AddressExtractor(); Class<? extends AddressExtractor> clazz = addressExtractor.getClass(); Field field = clazz.getDeclaredField("pointerValue"); Field type = Field.class.getDeclaredField("type"); AccessibleObject.setAccessible(new AccessibleObject[]{field, type}, true); type.set(field, Object.class); IntsExtractor intsExtractor = new IntsExtractor(); Class<? extends IntsExtractor> clazz2 = intsExtractor.getClass(); Field field2 = clazz2.getDeclaredField("ints"); AccessibleObject.setAccessible(new AccessibleObject[]{field2, type}, true); type.set(field2, long.class); field.set(addressExtractor, foo); long trickyAddress = addressExtractor.pointerValue - 8; field2.setLong(intsExtractor, trickyAddress); System.out.println("Length: " + intsExtractor.ints.length); for(int i=0;i<3;++i){ System.out.println(Integer.toHexString(intsExtractor.ints[i])); } } } class AddressExtractor { public long pointerValue; } class IntsExtractor { public int[] ints; } class Dummy{ public int value; public int value2; public int value3; } |
First, we create new object and set some dummy values. Next, we create helper instances for reflection.
In line 27 we do the same trick as last time. We assign object to a long field which in turn assigns reference. So we have an address.
Now, we would like to create an array of integers which would contain the object. This is a common trick, since array can be used to read the values, we can effectively use array as a pointer. Very similar to base pointer or segment address.
So we could assign the foo
object directly to that array but then we wouldn’t be able to read first field. That’s because first field would be internally storing array size. We need to move back by one long value, so in line 28 we calculate address of the fake array.
Next, in line 29 we just assign this fake object to int[]
field.
Finally, we can read all values using loop.
Obviously, this is very hacky approach and cannot be considered reliable. It highly depends on the architecture, JVM parameters (whether OOP are compressed or not) and multiple other scenarios.