この記事では、java のリフレクション機構に関する問題を中心に、Java に関する知識をお届けします Java 言語のプログラム情報を動的に取得したり、オブジェクトを動的に呼び出す機能をリフレクションといいます。 , 皆様のお役に立てれば幸いです。
推奨される学習: 「java チュートリアル 」
偉い人の話を聞いたり、フォーラムやその他の学習方法を見るたびにjavaの脆弱性を逆シリアル化する際に、リフレクションメカニズムという言葉があります。上司はこの言葉を使ってペイロードを作成します。Javaの逆シリアル化を学んだばかりの人にとっては、少し混乱するかもしれません。とにかく、私は混乱しています。それで私はすぐに教訓を学びました。そうしないと、私と偉い人たちとの差はますます広がってしまいます。したがって、この記事では主に Java リフレクション メカニズムについて説明します
Java のリフレクション (リフレクション) メカニズムとは、プログラムの実行状態で、任意のクラスのオブジェクトを構築できることを意味します。理解できる 任意のオブジェクトが属するクラスは、任意のクラスのメンバー変数とメソッドを理解でき、任意のオブジェクトのプロパティとメソッドを呼び出すことができます。この動的にプログラム情報を取得し、動的にオブジェクトを呼び出す機能をJava言語のリフレクション機構と呼びます。リフレクションは動的言語の鍵とみなされます。
私は言葉で表現するのがあまり得意ではないので、上の写真を例にしてみましょう
反射機構なし
//定义一个animals接口interface animals { public abstract void print();}//定义类来实现animals接口的抽象方法class Dog implements animals { public void print() { System.out.println("Dog"); }}class Cat implements animals { public void print() { System.out.println("Cat"); }}// 构造一个zoo类// 之后如果我们在添加其他的实例的时候只需要修改zoo类class zoo { public static animals getInstance(String animalsName) { animals a = null; if ("Dog".equals(animalsName)) { a = new Dog(); } if ("Cat".equals(animalsName)) { a = new Cat(); } return a; }}public class reflection { public static void main(String[] args) { //借助zoo类寻找对应的类来实现接口 animals a=zoo.getInstance("Cat"); if(a!=null) a.print(); }}
この時点で動物を追加するには、 just
//定义一个animals接口interface animals { public abstract void print();}//定义类来实现animals接口的抽象方法class Dog implements animals { public void print() { System.out.println("Dog"); }}class Cat implements animals { public void print() { System.out.println("Cat"); }}// 构造一个zoo类// 之后如果我们在添加其他的实例的时候只需要修改zoo类class zoo { public static animals getInstance(String className) { animals a = null; try { //借助Class.forName寻找类名,并用newInstance实例化类似于new a = (animals) Class.forName(className).newInstance(); } catch (Exception e) { e.printStackTrace(); } return a; }}public class reflection { public static void main(String[] args) { //借助zoo类寻找对应的类来实现接口(classname为当前包名加类名) animals a = zoo.getInstance("com.cc1.Dog"); if (a != null) a.print(); }}
この時点で動物を追加する必要があるのは、
クラスを追加する#リフレクション メカニズムのメソッド
## です。 #forName (クラスの呼び出し)
Class.forName(className).getMethod(methodName).invoke(Class.forName(className).newInstance());
Runtime.getRuntime().exec("notepad");getRuntime 関数を見てみましょう
この関数がランタイムのための方法であることを学びましたオブジェクトを取得するためのクラスです 個人的には毎回呼び出すのが面倒に感じます オブジェクト作成のために一度呼び出さないように関数にカプセル化しています
クラスの取得方法object
Class.forName (クラス名を取得)
Zoo クラスを変更し、初期ブロック、静的初期ブロック、コンストラクターを追加しますclass zoo {
//初始块
{
System.out.println("1 " + this.getClass());
}
//静态初始块
static {
System.out.println("2 " + zoo.class);
}
public zoo() {
System.out.println("3 " + this.getClass());
}
public static animals getInstance(String className) {
animals a = null;
try {
//借助Class.forName寻找类名,并用newInstance实例化类似于new
a = (animals) Class.forName(className).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return a;
}}
: 静的初期ブロック
クラスインスタンス化実行シーケンス
: 静的初期ブロック→初期ブロック→コンストラクター
このことから、クラスの初期化とクラスのインスタンス化は異なることがわかります
次に、zoo1 クラスを追加して、zoo クラスを継承しますclass zoo1 extends zoo{
//初始块
{
System.out.println("11 " + this.getClass());
}
//静态初始块
static {
System.out.println("12 " + zoo.class);
}
public zoo1() {
System.out.println("13 " + this.getClass());
}}
: 親クラスの静的初期化ブロック - > サブクラスの静的初期化ブロック
#サブクラスのインスタンス化シーケンス
: 親クラスの静的初期化ブロック - > サブクラスの静的初期化ブロック - >親クラスの初期化ブロック - > 親クラスのコンストラクター - > サブクラスの初期化ブロック - > サブクラスのコンストラクター
# #上記のことから、Class.forName が使用され、クラスが静的初期化ブロックは制御可能であり、任意のコードを実行できます。
#内部クラスの呼び出し
#Class.forName("java.lang.Runtime" ) クラスを取得します (java.lang.Runtime は Runtime クラスのフルパスです)
getMethod
getMethod の関数は次のとおりです。リフレクションを通じてクラスの特定のパブリック メソッドを取得します。Java はクラスのオーバーロードをサポートしていますが、関数は関数名だけでは決定できないため、getMethod を呼び出すときは、パラメーターの型のリストをそのメソッドに渡す必要があります。
Class.forName("java.lang.Runtime ") .getMethod(“exec”, String.class)
invoke
静态和动态方法的区别
invoke方法在getMethod类下,作用时传递参数,执行方法
public Object invoke(Object obj, Object… args)
第一个参数是getMethod获取的方法的类对象(如果方法是静态方法则传类)
获取exec函数的类对象
Class.forName(“java.lang.Runtime”).getMethod(“getRuntime”).invoke(Class.forName(“java.lang.Runtime”))
由于getRuntime是静态方法,所以传类
invoke(Class.forName(“java.lang.Runtime”).getMethod(“getRuntime”).invoke(Class.forName(“java.lang.Runtime”)),“calc.exe”)最后我们合并一下
Class.forName("java.lang.Runtime"). getMethod("exec", String.class). invoke(Class.forName("java.lang.Runtime").getMethod("getRuntime").invoke(Class.forName("java.lang.Runtime")), "notepad");指定构造方法生成实例
String str="notepad";ProcessBuilder pb = new ProcessBuilder(str);pb.start();getConsturctor(函数可以选定指定接口格式的构造函数(由于构造函数也可以根据参数来进行重载)
选定后我们可以通过newInstance(),并传入构造函数的参数执行构造函数ProcessBuilder类有两个构造函数
- public ProcessBuilder(String… command)(String…变长的字符串数组String[].class)
- public ProcessBuilder(List command)
分别使用构造方法
- Class.forName(“java.lang.ProcessBuilder”).getConstructor(String[].class).newInstance(new String[][]{{“notepad”}})
- Class.forName(“java.lang.ProcessBuilder”).getConstructor(List.class).newInstance(Arrays.asList(“notepad”))
执行完构造方法实例后,在进行强制转化使用start函数即可
( (ProcessBuilder) Class.forName(“java.lang.ProcessBuilder”).getConstructor(List.class).newInstance(Arrays.asList(“notepad”))).start();
实际中,肯定用不了,哪有这么好的事,还是接着反射把
Class.forName(“java.lang.ProcessBuilder”).getMethod(“start”).invoke(clazz.getConstructor(List.class).newInstance(Arrays.asList(“notepad”)));
这里可能有人会好奇我写的里那的另一个构造函数,String…command这个传参为什么用new String[][]{{“notepad”}},不应该是new String[]{“notepad”},现在用应该的((ProcessBuilder) Class.forName(“java.lang.ProcessBuilder”).getConstructor(String[].class).newInstance(new String[]{“notepad”})).start();
在这行打断点调试
我们传的是一个字符串数组到了实例化的时候变成了一个字符串,再看看另一个构造函数(List)( (ProcessBuilder) Class.forName(“java.lang.ProcessBuilder”).getConstructor(List.class).newInstance(Arrays.asList(“notepad”))).start();
依旧还是这行打断点
由此可知,List传入时会被当作Object的第一项,而String[]会被当做Object,所以多加一层[]{}执行私有方法
通过函数getDeclaredConstructor获取私有方法,再利用setAccessible(true)打破私有方法限制
Class cls = Class.forName("java.lang.Runtime"); Constructor m = cls.getDeclaredConstructor(); m.setAccessible(true); cls.getMethod("exec", String.class).invoke(m.newInstance(), "notepad");推荐学习:《java视频教程》
以上がJava デシリアライゼーションのリフレクション メカニズムを例とともに詳しく説明しますの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。