public static Unsafe getUnsafe() {
Class cc = sun.reflect.Reflection.getCallerClass(2);
if (cc.getClassLoader() != null)
throw new SecurityException("Unsafe");
return theUnsafe;
}
프로그램 실행시 bootclasspath 옵션을 이용하여 코드를 신뢰하도록 할 수 있지만 너무 복잡하다
java -Xbootclasspath:/usr/jdk1.7.0/jre/lib/rt.jar:. com.mishadoff.magic.UnsafeClient
class A {
private long a;
public A() {
this.a = 1;
}
public long a() { return this.a; }
}
//------------------------------------------------------
A o1 = new A(); // constructor
o1.a(); // prints 1
A o2 = A.class.newInstance(); // reflection
o2.a(); // prints 1
A o3 = (A) unsafe.allocateInstance(A.class); // unsafe
o3.a(); // prints 0
public class Guard {
private int ACCESS_ALLOWED = 1;
public boolean giveAccess() {
return 42 == ACCESS_ALLOWED;
}
}
//------------------------------------------------------
Guard guard = new Guard();
guard.giveAccess(); // false, no access
Field f = guard.getClass().getDeclaredField("ACCESS_ALLOWED");
unsafe.putInt(guard, unsafe.objectFieldOffset(f), 42); // memory corruption
guard.giveAccess(); // true, access granted
메모리에 guard 인스턴스 다음에 또 다른 Guard 오브젝트가 있을 경우 다음과 같은 코드를 이용하여 ACCESS_ALLOWED값을 변경 할 수 있다
unsafe.putInt(guard, 16 + unsafe.objectFieldOffset(f), 42); // memory corruption
public static long sizeOf(Object o) throws Exception {
Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
unsafeField.setAccessible(true);
Unsafe unsafe = (Unsafe) unsafeField.get(null);
HashSet<Field> fields = new HashSet<Field>();
Class c = o.getClass();
while (c != Object.class) {
for (Field f : c.getDeclaredFields()) {
if ((f.getModifiers() & Modifier.STATIC) == 0) {
fields.add(f);
}
}
c = c.getSuperclass();
}
// get offset
long maxSize = 0;
for (Field f : fields) {
long offset = unsafe.objectFieldOffset(f);
if (offset > maxSize) {
maxSize = offset;
}
}
return ((maxSize/8) + 1) * 8; // padding
}
static long toAddress(Object obj) throws Exception {
Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
Unsafe unsafe = (Unsafe) f.get(null);
Object[] array = new Object[] {obj};
long baseOffset = unsafe.arrayBaseOffset(Object[].class);
return normalize(unsafe.getInt(array, baseOffset));
}
//------------------------------------------------------
String password = new String("l00k@myHor$e");
String fake = new String(password.replaceAll(".", "?"));
System.out.println(password); // l00k@myHor$e
System.out.println(fake); // ????????????
unsafe.copyMemory(fake, 0L, null, toAddress(password), sizeOf(password));
System.out.println(password); // ????????????
System.out.println(fake); // ????????????
interface Counter {
void increment();
long getCounter();
}
//------------------------------------------------------
class CASCounter implements Counter {
private volatile long counter = 0;
private Unsafe unsafe;
private long offset;
public CASCounter() throws Exception {
unsafe = getUnsafe();
offset = unsafe.objectFieldOffset(CASCounter.class.getDeclaredField("counter"));
}
@Override
public void increment() {
long before = counter;
while (!unsafe.compareAndSwapLong(this, offset, before, before + 1)) {
before = counter;
}
}
@Override
public long getCounter() {
return counter;
}
}