反射hacking javaプログラムの利用

4255 ワード

一般にclientプログラムはprivateメソッドを直接呼び出すことはできないが,反射により実現できる.

package chentao;

public class A
{
	private static String getPassword() {
		return "call the method!";
	}
}

package chentao;

import java.lang.reflect.InvocationTargetException;

public class Test
{

	/**
	 * @param args
	 * @throws ClassNotFoundException 
	 * @throws InvocationTargetException 
	 * @throws IllegalAccessException 
	 * @throws IllegalArgumentException 
	 */
	public static void main(String[] args) throws ClassNotFoundException, IllegalArgumentException, IllegalAccessException, InvocationTargetException
	{
		// ERROR
		/*String password = A.getPassword();
		System.out.println("I got it:" + password);*/
		
		Class cl = Class.forName("chentao.A");
		java.lang.reflect.Method[] m = cl.getDeclaredMethods();
		m[0].setAccessible(true);
		String password = (String) m[0].invoke(null, null);
		System.out.println("I got it:" + password);
		
	}	
}

/*output:
I got it:call the method!*/

OK,singletonモードは取得したインスタンスの一意性を保証することができ,以下では反射によってこの制限を変更することができる.

package chentao;

public class B
{
	public static final B singleton = new B("I'm the only instance of class B");
	private String name; 
	private B(String name) {
	    this.name = name;
	}
	public String toString() {
	    return this.name;
	}
}
package chentao;

package chentao;

import java.lang.reflect.InvocationTargetException;

public class TestB
{

	/**
	 * @param args
	 * @throws ClassNotFoundException 
	 * @throws InvocationTargetException 
	 * @throws IllegalAccessException 
	 * @throws IllegalArgumentException 
	 * @throws InstantiationException 
	 */
	public static void main(String[] args) throws ClassNotFoundException, IllegalArgumentException, IllegalAccessException, InvocationTargetException, InstantiationException
	{
		Class cl = Class.forName("chentao.B");
		java.lang.reflect.Constructor[] c = cl.getDeclaredConstructors();
		c[0].setAccessible(true);
		B anotherB  = (B) c[0].newInstance(new Object[]{"Not anymore!!"});
		System.out.println(B.singleton);
		System.out.println(anotherB);
	}	
}

/*output:
	I'm the only instance of class B
	Not anymore!!
	*/

最後の例では、Runtimeクラスには、現在のruntimeインスタンスを表すprivate static fieldがあります.まず、現在のruntimeインスタンスを取得し、印刷します.そして、currentRuntimeはclass初期化時に初期化されるのでRuntimeを設定する.CurrentRuntime静的ドメインはnullであり、今回の変更を検証するためにruntimeインスタンスを再度取得し、印刷します.最後に、dosコマンドdirを現在のruntimeインスタンスで呼び出してみましょう.

package chentao;

import java.lang.reflect.InvocationTargetException;

public class TestRuntime
{

	/**
	 * @param args
	 * @throws ClassNotFoundException 
	 * @throws InvocationTargetException 
	 * @throws IllegalAccessException 
	 * @throws IllegalArgumentException 
	 * @throws InstantiationException 
	 */
	public static void main(String[] args) throws Exception
	{
		Runtime r = Runtime.getRuntime();
		System.out.println("Before: Runtime.getRuntime() yields " + r);

		Class cl = Class.forName("java.lang.Runtime");
		java.lang.reflect.Field f = cl.getDeclaredField("currentRuntime");
		f.setAccessible(true);
		f.set(null, null);
		
		r = Runtime.getRuntime();
		System.out.println("After: Runtime.getRuntime() yields " + r);
		
		r.exec("dir"); //raises NullPointerException!!
		
	}	
}

/*Output:

	 Before: Runtime.getRuntime() yields java.lang.Runtime@cac268
	 After: Runtime.getRuntime() yields null
	 Exception in thread "main" java.lang.NullPointerException
	       at Test.main(Test.java:59)
	 */