Java安全大部分都是從反射開始,可以動態呼叫一些Payload而不出現相關特征,
類初始化代碼執行順序
- https://www.cnblogs.com/wugongzi/p/11849266.html
- 首先
new一個類,加載的順序是(父)靜態變數->(父)靜態代碼塊->(父)代碼塊->(父)構造方法, - 如果初始化同一個類,靜態變數、靜態代碼塊只執行一次,
package Advance;
public class ExecutionOrder {
public static void main(String[] args) {
Student student = new Student();
}
}
class Student extends Person {
public Student() {
System.out.println("學生建構式");
}
{
System.out.println("學生代碼塊");
}
static {
System.out.println("學生靜態代碼塊");
}
}
class Person {
public static String staticName = "Pan3a";
public String name = "Forever";
{
System.out.println("人類代碼塊");
}
static {
System.out.println("人類靜態代碼塊");
}
public Person() {
System.out.println("人類建構式");
}
}
- 輸出結果
人類靜態代碼塊
學生靜態代碼塊
人類代碼塊
人類建構式
學生代碼塊
學生建構式
類加載器
- 這里暫時不去深究吧,后面學習參考文章
- 類加載器和Class.forName區別
- https://javasec.org/javase/ClassLoader/
環境
package Reflection;
public class ReflectData {
public ReflectData() {
}
}
class Person {
private int age;
private String name;
public long id = 9527;
public long grade;
protected float score;
protected int rank;
public Person() {
}
protected Person(long id) {
this(18, "Pan3a", id, 9, 9999, 31);
}
private Person(int age) {
this(age, "Pan3a", 9527, 9, 9999, 30);
}
public Person(int age, String name, long id, long grade, float score, int rank) {
this.age = age;
this.name = name;
this.id = id;
this.grade = grade;
this.score = score;
this.rank = rank;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public long getGrade() {
return grade;
}
public void setGrade(long grade) {
this.grade = grade;
}
public float getScore() {
return score;
}
public void setScore(float score) {
this.score = score;
}
public int getRank() {
return rank;
}
public void setRank(int rank) {
this.rank = rank;
}
private static void sayHello() {
System.out.println("Hello World");
}
private void sayHello(String name) {
System.out.println("Hello " + name);
}
@Override
public String toString() {
final StringBuffer stringBuffer = new StringBuffer("Person{");
stringBuffer.append("age=").append(age);
stringBuffer.append(", name='").append(name).append('\'');
stringBuffer.append(", id=").append(id);
stringBuffer.append(", grade=").append(grade);
stringBuffer.append(", score=").append(score);
stringBuffer.append(", rank=").append(rank);
stringBuffer.append('}');
return stringBuffer.toString();
}
}
class Teacher extends Person {
private String role = "Teacher";
public void sayHello() {
System.out.println("Hello Teacher");
}
}
class Student extends Teacher {
private String role = "Student";
static {
System.out.println("run Student static codes!");
}
{
System.out.println("run Student codes!");
}
@Override
public void sayHello() {
System.out.println("Hello Student");
}
public Student(){
}
public Student(String name){
System.out.printf("hello %s", name);
}
}
獲取物件
- 獲取物件有三種方式,
Class.forName(全類名)、物件.getclass()、類名.class, - 這里的三種方式雖然作用都一樣,但是都有各自的缺點,
class.forName()需要知道類名的全路徑,物件名.getclass()需要存在已經實體化的物件,類名.class需要提前在編譯前知道類名, - 這里都回傳
true了,說明這里回傳的物件都是同一個,因此Person.class只加載了一次,用instanceof不但匹配指定型別,還匹配指定型別的子類,而用==判斷class實體可以精確地判斷資料型別,但不能作子型別比較,
package Reflection;
public class ReflectGetObject {
public static void main(String[] args) throws ClassNotFoundException{
compareReflectGetObject();
}
public static void compareReflectGetObject() throws ClassNotFoundException {
Student student = new Student();
Teacher teacher = new Teacher();
Person person = new Person();
if(student instanceof Person){
System.out.println("Student 是Person子類");
}
Class<?> clazz1 = Class.forName("Reflection.Person");
System.out.println(clazz1.getName());
Class<?> clazz2 = person.getClass();
System.out.println(clazz2.getName());
Class<?> clazz3 = Person.class;
System.out.println(clazz3.getName());
System.out.println(clazz1 == clazz2);
System.out.println(clazz1 == clazz3);
}
}
- 輸出結果如下
run Student static codes!
run Student codes!
Student 是Person子類
Reflection.Person
Reflection.Person
Reflection.Person
true
true
- 這里的
Class.forName()有兩個函式多載,我們可以跟進看看,第一個函式是我們平常用的,第二個有引數boolean initialize、ClassLoader loader,可以指定類是否在加載時初始化,和指定類加載器, 重點,這里的boolean initialize引數是控制是否初始化,而不是實體化,初始化會加載靜態變數、靜態代碼塊,
@CallerSensitive
public static Class<?> forName(String className)
throws ClassNotFoundException {
Class<?> caller = Reflection.getCallerClass();
return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}
@CallerSensitive
public static Class<?> forName(String name, boolean initialize,
ClassLoader loader)
throws ClassNotFoundException
{
Class<?> caller = null;
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
// Reflective call to get caller class is only needed if a security manager
// is present. Avoid the overhead of making this call otherwise.
caller = Reflection.getCallerClass();
if (sun.misc.VM.isSystemDomainLoader(loader)) {
ClassLoader ccl = ClassLoader.getClassLoader(caller);
if (!sun.misc.VM.isSystemDomainLoader(ccl)) {
sm.checkPermission(
SecurityConstants.GET_CLASSLOADER_PERMISSION);
}
}
}
return forName0(name, initialize, loader, caller);
}
- 案例如下
- 輸出結果如下,這里的靜態代碼塊只執行了一次,后面沒執行,是因為
Student.class已經被前面加載到記憶體中了,
run Student static codes!
0
------------------
run Student codes!
------------------
0
------------------
run Student codes!
------------------
- 若注釋掉第一段
39-47行,輸出結果如下,可以看到靜態代碼塊是在newInstance()實體化后再加載到,因此可以得出initialize引數控制的是是否初始化,而不是實體化,
0
------------------
run Student static codes!
run Student codes!
------------------
構造方法
- 有以下四種獲取方法,
getConstructor()、getConstructors()、getDeclaredConstructor()、getDeclaredConstructors(),若構造方法帶引數,用對應的資料型別.class即可, - 這里注意非public的構造方法需要使用
getDeclaredConstructor()方法即可,然后如果需要實體化必須使用setAccessible(true),
package Reflection;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class ReflectGetObject {
public static void main(String[] args) throws Exception{
getConstructorsStudy();
}
public static void getConstructorsStudy() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
Class<?> clazz = Class.forName("Reflection.Person");
System.out.println("所有構造方法");
Constructor<?>[] constructors = clazz.getDeclaredConstructors();
for (Constructor<?> constructor:constructors){
System.out.println(constructor);
}
System.out.println();
System.out.println("public無引數構造方法");
// 默認當前類的無引數構造方法
Constructor<?> constructor1 = clazz.getConstructor();
System.out.println(constructor1);
System.out.println("protected帶引數構造方法");
Constructor<?> constructor2 = clazz.getDeclaredConstructor(long.class);
System.out.println(constructor2);
System.out.println("private帶引數構造方法");
Constructor<?> constructor3 = clazz.getDeclaredConstructor(int.class);
System.out.println(constructor3 + "\n");
System.out.println("public無引數構造方法創建物件");
Object person1 = constructor1.newInstance();
System.out.println(person1);
System.out.println("protected帶引數構造方法創建物件");
constructor2.setAccessible(true);
Object person2 = constructor2.newInstance(9528);
System.out.println(person2);
System.out.println("private帶引數構造方法創建物件");
constructor3.setAccessible(true);
Object person3 = constructor3.newInstance(18);
System.out.println(person3);
System.out.println("Person.class.newInstance()");
Class<?> class1 = Reflection.Person.class;
Object object = class1.newInstance();
System.out.println(object);
}
}
- 輸出結果展示
所有構造方法
protected Reflection.Person(long)
private Reflection.Person(int)
public Reflection.Person(int,java.lang.String,long,long,float,int)
public Reflection.Person()
public無引數構造方法
public Reflection.Person()
protected帶引數構造方法
protected Reflection.Person(long)
private帶引數構造方法
private Reflection.Person(int)
成員方法
- 獲取成員方法,也有四個方法,
getMethod(),getMethods(),getDeclaredMethod(),getDeclaredMethods(),invoke(),這里如果是繼承,invoke()呼叫的方法根據實際傳入的方法為準,不存在則向上父類尋找, - 獲取方法屬性,
getName(),getReturnType(),getParameterTypes(),getModifiers(),
package Reflection;
import java.lang.reflect.*;
import java.util.ArrayList;
import java.util.Arrays;
public class ReflectGetObject {
public static void main(String[] args) throws Exception{
getMethodStudy();
}
public static void getMethodStudy() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException {
Class<?> clazz = Class.forName("Reflection.Person");
Constructor<?> constructor = clazz.getDeclaredConstructor();
constructor.setAccessible(true);
Object person = constructor.newInstance();
Method method = clazz.getMethod("setAge", int.class);
method.invoke(person,18);
// getDeclaredMethods學習
System.out.println("所有方法");
Method[] methods = clazz.getDeclaredMethods();
for (Method methodTemp:methods){
System.out.println(methodTemp.getName() + "\t" + Arrays.toString(methodTemp.getParameterTypes()));
}
// getMethod學習
Method method1 = clazz.getMethod("getAge");
method1.setAccessible(true);
System.out.println(method1.invoke(person));;
// 由于這里和方法為靜態方法,因此object為null
Method method2 = clazz.getDeclaredMethod("sayHello");
method2.setAccessible(true);
method2.invoke(null);
// 多型,依舊根據傳入的實體化物件為準,如果沒有則向父類尋找
Class<?> teacherClass = Class.forName("Reflection.Teacher");
Method method3 = teacherClass.getMethod("sayHello");
method3.invoke(new Reflection.Student());
}
}
- 輸出結果
所有方法
setAge [int]
getAge []
sayHello [class java.lang.String]
sayHello []
setId [long]
getGrade []
setGrade [long]
getScore []
setScore [float]
getRank []
setRank [int]
toString []
getName []
setName [class java.lang.String]
getId []
18
Hello World
run Student static codes!
run Student codes!
Hello Student
成員變數
- 獲取成員變數需要知道一下四個方法,getField,getFields,getDeclaredField,getDeclaredFields,
- 還可獲取成員變數屬性的三個方法,getType,getModifiers,getName,分別是獲取變數型別,修飾符,成員名,
- 他們分別是獲取單個成員,和獲取所有成員,獲取單個成員(忽略修飾服限制,不包括父類),獲取多個成員(忽略修飾服限制,不包括父類),需注意的是使用后面兩個是需使用setAccessible(true)來忽略編譯時的安全檢查,
package Reflection;
import java.lang.reflect.*;
import java.util.ArrayList;
import java.util.Arrays;
public class ReflectGetObject {
public static void main(String[] args) throws Exception{
getFieldStudy();
}
public static void getFieldStudy() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException {
Class<?> clazz = Class.forName("Reflection.Person");
Constructor<?> constructor = clazz.getConstructor();
Object person = constructor.newInstance();
Method setAge = clazz.getMethod("setAge", int.class);
setAge.invoke(person,18);
Method getAge = clazz.getMethod("getAge");
System.out.println(getAge.invoke(person) + "\n");
// getgetDeclaredFields學習
System.out.println("所有方法");
Field[] fields = clazz.getDeclaredFields();
for (Field field:fields){
System.out.println(field.getName() + "\t" + field.getModifiers() + "\t" + field.getType());
}
// 反射修改私有成員值
Field field = clazz.getDeclaredField("age");
field.setAccessible(true);
field.set(person,20);
System.out.println(getAge.invoke(person));
}
}
- 輸出結果
18
所有方法
age 2 int
name 2 class java.lang.String
id 1 long
grade 1 long
score 4 float
rank 4 int
20
反射Runtime執行命令
- 代碼如下,依次來解釋
package Reflection;
import java.lang.reflect.Method;
public class ReflectRuntimeExec {
public static void main(String[] args) throws Exception{
Class<?> clazz = Class.forName("java.lang.Runtime");
Method getRuntime = clazz.getDeclaredMethod("getRuntime");
Object runtime = getRuntime.invoke(null);
Method exec = clazz.getDeclaredMethod("exec", String.class);
exec.invoke(runtime,"open /System/Applications/Calculator.app");
}
}
- 這里為什么要獲取兩個方法呢,為什么沒有實體化類就可以呼叫方法了,
- 跟進
Runtime,發現它的構造方法是私有的,發現有一個getRuntime的靜態方法,可以回傳實體化的物件,就像P神說的,“單例模式”因此我們通過獲取getRuntime方法來獲取Runtime的實體,還可以反射構造方法來獲取,只不過后者比前者麻煩, 疑問,這里為什么不可以獲取欄位來直接獲取變數值,我覺得可能是這里只是獲取的屬性,并沒有獲取變數的值吧,這里發現可以通過Filed.get(clazz)來獲取變數的值,問題解決,
getRuntime命令執行
package Reflection;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.Runtime;
public class ReflectRuntimeExec {
public static void main(String[] args) throws Exception {
Class<?> clazz = Class.forName("java.lang.Runtime");
Method getRuntime = clazz.getDeclaredMethod("getRuntime");
Method exec = clazz.getDeclaredMethod("exec", String.class);
Object runtime = getRuntime.invoke(null);
exec.invoke(runtime, "open /System/Applications/Calculator.app");
}
}
Constructor命令執行
- 這里沒有意外,直接反射操作即可,
package Reflection;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.Runtime;
public class ReflectRuntimeExec {
public static void main(String[] args) throws Exception {
Class<?> clazz = Class.forName("java.lang.Runtime");
Constructor<?> constructor = clazz.getDeclaredConstructor();
constructor.setAccessible(true);
Object runtime2 = constructor.newInstance();
System.out.println(runtime2 instanceof Runtime);
Method exec2 = clazz.getDeclaredMethod("exec", String.class);
exec2.invoke(runtime2, "open /System/Applications/Calculator.app");
}
}
Filed命令執行
- 了解到
field.get就可以獲取變數值了
package Reflection;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.Runtime;
public class ReflectRuntimeExec {
public static void main(String[] args) throws Exception {
Class<?> clazz = Class.forName("java.lang.Runtime");
Field field = clazz.getDeclaredField("currentRuntime");
field.setAccessible(true);
Object runtime1 = field.get(clazz);
System.out.println(runtime1 instanceof Runtime);
Method exec1 = clazz.getDeclaredMethod("exec", String.class);
exec1.invoke(runtime1, "open /System/Applications/Calculator.app");
}
}
逆水行舟,不進則退,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/507168.html
標籤:其他
上一篇:Linux安全加固
下一篇:Windows入侵排查
