Javaリフレクションメカニズム
Javaリフレクション(Reflection
)はJavaの非常に重要な動的機能です。リフレクションを使用することで、任意のクラスのメンバーメソッド(Methods
)、メンバー変数(Fields
)、コンストラクター( )などの情報を取得できるだけでなくConstructors
、Javaクラスインスタンスを動的に作成することもできます。クラスメソッドを呼び出したり、任意のクラスメンバー変数値を変更したりします。Javaリフレクションメカニズムは、Java言語の動的な性質の重要な実施形態であり、さまざまなJavaフレームワークの基盤となる実装の魂でもあります。
クラスオブジェクトを取得
Javaリフレクションはjava.lang.Class
オブジェクトに対して機能するため、最初にClassオブジェクトを取得する方法を見つける必要があります。通常、クラスのClassオブジェクトを取得するには次の方法があります。
Class.forName("ClassName");
ClassLoader.loadClass("ClassName");
//実際の使用では、などの特定のローダーを指定してからClassLoader.getSystemClassLoader()
、を呼び出す必要がありますloadClass方法
ClassName.class;
//クラスがロードされました、java.lang.Class
オブジェクトを取得するだけですobj.getClass();
//コンテキストにクラスのインスタンスobjがある場合に使用可能
Class.forName(String)
これは、クラスを取得するために最も一般的に使用される方法でforname()
あり、2つのオーバーロードされたメソッドがあります。
-
public static Class<?> forName(String className)
-
public static Class<?> forName(String name, boolean initialize, ClassLoader loader)
最初のメソッドは2番目のメソッドのパッケージとして理解でき、このメソッドを呼び出すことは2番目のメソッドと同等です。
Class.forName("Foo")
と同等です:
Class.forName("Foo", true, this.getClass().getClassLoader())
2番目のメソッドの2番目と3番目のパラメーター、2番目のパラメーターは初期化するかどうかを示し、3番目のパラメーターはClassLoader
(前のセクションの内容で説明)であることに注意してください。
2番目のパラメーターinitializeは初始化
、クラスの初期化を参照します。前のセクションの例では、ステートメントブロックが現在呼び出されていることにClass.forname()
も言及しています(悪意のあるクラスを記述し、悪意のあるコードを静的ステートメントブロックに入れてロードします) it)、p Java Security Talkで神が示した例は、3つの「初期化」メソッドの違いと、それらが呼び出される順序を示しています。类初始化
static{}
別のメソッドClassLoader.loadClass(String)
にも、上記のようなメソッドのオーバーロードがあります
Class<?> loadClass(String name) //调用此方法等效于调用loadClass(name, false)
Class<?> loadClass(String name,
boolean resolve)
最初の(Class.forName();
)は次のようになります。
- このコードを呼び出すクラスをロードするクラスローダーを使用します
- クラスを初期化します(つまり、すべての静的初期化子が実行されます)
別の(ClassLoader.getSystemClassLoader().loadClass();
)は:
- 「システム」クラスローダーを使用する(オーバーライド可能)
- クラスは初期化されていません
例-RuntimeクラスClassオブジェクトコードスニペットを取得します。
String className = "java.lang.Runtime";
Class runtimeClass1 = Class.forName(className);
Class runtimeClass2 = ClassLoader.getSystemClassLoader().loadClass(className);
Class runtimeClass3 = java.lang.Runtime.class;
インナークラスを取得する
$
代わりに、リフレクションによって.
、たとえばロードCommon$Inner
によって内部クラスを呼び出すときに使用する必要があります。Class.forname("Common$Inner");
class Common {
static {
System.out.printf("CommonClass: %s\n", Common.class);
}
class Inner{
Inner(){
System.out.printf("InnerClass: %s\n", Inner.class);}}}
コンパイルすると、2つのファイルになり、2つのクラスCommon$Inner.class
とCommon.class
同じようなります。
伏線は終わりました。以下のポイントに行きましょう。
リフレクションを使用してオブジェクトを生成および操作する
Classオブジェクトは以下を取得できます。
- メソッド(
Method对象
で表される)、Method对象
メソッドを実行することによって; - コンストラクター(
Constructor对象
で表される)。Constructor对象
これにより、コンストラクターが呼び出されます。 - オブジェクトのメンバー変数値に直接アクセスして変更することによるメンバー変数値(
Field对象
で表される) 。Field对象
オブジェクトの作成(クラスインスタンス)
メソッドを使用しClass对象
て指定されたものを取得してから构造方法(Constructor对象)
、Constructorオブジェクトのメソッドを呼び出しnewInstance()
(関数は取得したパラメーターなしのコンストラクターを呼び出す)、Classオブジェクトの対応するクラスのインスタンスを作成する必要があります。
Classクラスでコンストラクターを取得するには、通常2つの方法があります。
getConstructor*()
メソッドでは、パブリックメソッドのみを取得できます。
* Constructor<?>[] getConstructors()
* Constructor<T> getConstructor(类<?>... parameterTypes)
getDeclaredConstructor*()
クラスのプライベートコンストラクター(他の修飾子を持つコンストラクターを含む)を取得するメソッドは、setAccessible()
trueに設定する必要があります。
* Constructor<?>[] getDeclaredConstructors()
* Constructor<T> getDeclaredConstructor(类<?>... parameterTypes)
メソッドを呼び出す
クラス(Classオブジェクト)のインスタンスを取得した後、メソッドはClassオブジェクトのgetMethod*()
ORgetDeclaredMethod*()
を介して取得されます。
getMethod*()
それ自体、基本クラスから継承されたすべてのパブリックメソッド、およびインターフェイスから実装されたすべてのパブリックメソッドを含む、クラスのすべてのパブリックメソッドを取得するメソッド。
Method[] getMethods() //全部方法 返回Method对象
Method getMethod(String name, 类<?>... parameterTypes) //指定方法 返回Method数组
getDeclaredMethod*()
メソッドを使用すると、パブリックメソッド、プロテクトメソッド、プライベートメソッドを含め、クラス自体によって宣言されたすべてのメソッドを取得できます。
Method[] getDeclaredMethods()
Method getDeclaredMethod(String name, 类<?>... parameterTypes)
次に、Methodオブジェクトが取得された後、invoke()
対応するメソッドをMethodオブジェクトのメソッドを介して呼び出すことができます。
Object invoke(Object obj, Object... args)
obj - 被调用方法的类实例对象(静态方法则为类)
args - 用于方法调用的参数类型列表(Java存在方法重载,以此确定要调用的方法)
オブジェクトのプライベートメソッドを呼び出すときは、Methon.setAccessible(true)
最初にアクセス権を無視するように設定された制限を使用する必要があります。
メンバー変数を呼び出す
クラスに含まれる指定されたメンバー変数は、ClassオブジェクトのgetField*()
orメソッドを介して取得できます。getDeclaredField*()
getField*()
公的に装飾された変数にのみアクセスできます。
Field[] getFields() //全部变量
Field getField(String name) //指定变量
getDeclaredField*()
プライベートメンバー変数を含む、指定されたオブジェクトのすべてのメンバー変数に自由にアクセスできます。
Field[] getDeclaredFields()
Field getDeclaredField(String name)
メンバー変数値を取得します。
Object obj = field.get(类实例对象);
メンバー変数値を変更します。
field.set(类实例对象, 修改后的值);
プライベート変数を操作する場合、field.setAccessible(true)
メンバー変数へのアクセスに関するアクセス制限を無視できます。
キーワードで変更されたメンバー変数を変更final
するには、最初にsetメソッドを変更する必要があります。
// 反射获取Field类的modifiers
Field modifiers = field.getClass().getDeclaredField("modifiers");
// 设置modifiers修改权限
modifiers.setAccessible(true);
// 修改成员变量的Field对象的modifiers值
modifiers.setInt(field, field.getModifiers() & ~Modifier.FINAL);
// 修改成员变量值
field.set(类实例对象, 修改后的值);
上記の3つの部分はすべてsetAccessible()
、修飾子のアクセス許可の問題を解決するためのメソッドの呼び出しについて説明しています。メソッドは3つのカテゴリのいずれにも属していませんがsetAccessible()
、Field、Method、およびConstructorオブジェクトの親クラスに属していますAccessibleObject
。リフレクションオブジェクトを使用するときにデフォルトのJava言語アクセス制御チェックを抑制するオブジェクトをマークする機能を提供します。そのオーバーライドプロパティはデフォルトでfalseになり、呼び出し可能なsetAccessible()
メソッドが変更されます。したがって、Field、Method、Constructorはこのメソッドを呼び出すことができます。
例-リフレクションjava.lang.Runtimeコマンドの実行
// 获取Runtime类对象
Class runtimeClazz = Class.forName("java.lang.Runtime");
// 获取Runtime类的无参构造方法(该方法为私有方法)
Constructor constructor = runtimeClazz.getDeclaredConstructor();
//修改方法的访问权限(constructor.setAccessible(true))
constructor.setAccessible(true);
// 创建Runtime类实例,等价于 Runtime rt = new Runtime();
Object runtimeInstance = constructor.newInstance();
// 获取Runtime的exec(String cmd)方法
Method runtimeMethod = runtimeClazz.getMethod("exec", String.class);
// 调用exec方法,等价于 rt.exec(cmd);
runtimeMethod.invoke(runtimeInstance, "calc.exe");
ペイロードを実行するコマンドを作成するときに最も一般的に使用されるクラスはランタイムであり、ランタイムクラスはシングルトンモードです。
コンストラクターはプライベートですがgetRuntime()
、Runtimeオブジェクトを取得するための静的メソッドが指定されているため、次のペイロードを使用することもできます
Class clazz = Class.forName("java.lang.Runtime");
Method execMethod = clazz.getMethod("exec", String.class);
Method getRuntimeMethod = clazz.getMethod("getRuntime");
Object runtime = getRuntimeMethod.invoke(clazz);
execMethod.invoke(runtime, "calc.exe");
Javaリフレクションメカニズムの概要
Javaリフレクションメカニズムは、Javaダイナミクスの最も重要な表現です。リフレクションメカニズムを使用すると、Javaクラスの動的呼び出しを簡単に実現できます。Javaのフレームワークのほとんどはリフレクションメカニズム(など)を使用して実装されてSpring MVC
おりORM框架
、Javaリフレクションは、エクスプロイトコードの記述、コード監査、およびRASPメソッドの制限の回避において重要な役割を果たします。
thod.invoke(clazz);
execMethod.invoke(runtime、 "calc.exe");
## Java反射机制总结
Java反射机制是Java动态性中最为重要的体现,利用反射机制我们可以轻松的实现Java类的动态调用。Java的大部分框架都是采用了反射机制来实现的(如:`Spring MVC`、`ORM框架`等),Java反射在编写漏洞利用代码、代码审计、绕过RASP方法限制等中起到了至关重要的作用。