主頁 >  其他 > 從零開始的Java RASP實作(二)

從零開始的Java RASP實作(二)

2021-08-18 06:23:35 其他

目錄
  • 2 RASP-demo
    • 2.1 類加載機制
      • 雙親委派
      • BootStrap ClassLoader
    • 2.2 Instrumentation介紹
      • Instrumentation類中常用方法
      • Instrumentation觸發流程
      • Instrumentation的局限性
    • 2.3 javassist
      • 方法1,通過ClassLoader#defineClass方法覆寫jvm中的位元組碼
      • 方法2,通過ClassFileTransformer#transform方法修改位元組碼
    • 2.4 總結
  • 參考

最近面試有點多,停更了兩周,接著上一篇繼續寫java RASP的實作程序,

2 RASP-demo

通過前面的例子已經可以看到,通過Instrumentation物件添加Transformer,在transform方法中可以做到對加載的類進行動態修改,如果transform方法可以獲取到所有系統類的加載,豈不是就可以有針對性的對有風險的類進行修改,在其危險方法執行前后獲取引數加以判斷,從而實作RASP,但事情不是那么美好的

以前面javaagent使用流程舉例,對主要方法進一些修改,看看效果:

package com.bitterz;

import java.io.IOException;
import java.lang.Runtime;
import java.lang.String;

public class Main {
    public static class A{
        public void t(){System.out.println("Main$A.t()");}
    }

    public static void main(String[] args) throws InterruptedException, IOException {
        System.out.println("-------Main.main() start-------");
        Runtime.getRuntime().exec("calc");
        String a = "a";
        System.out.println(a);
        A a1 = new A();
        a1.t();
        System.out.println("-------Main.main() end-------");
    }
}
package com.bitterz;

import java.io.IOException;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;


public class PreMain {
        public static void premain(String agentArgs, Instrumentation inst) throws IOException {
            System.out.println("++++++++Premain start++++++++");
            System.out.println(ClassLoader.getSystemClassLoader().toString());  // 查看當前代理類是被哪個類加載器加載的
            inst.addTransformer(new DefineTransformer(), true);
            System.out.println("++++++++Premain end++++++++");
        }

    public static class DefineTransformer implements ClassFileTransformer {
        @Override  // 添加override會讓transform只接收到appClassLoader加載的類,去掉就可以接收重要的系統類
        public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
            System.out.println(className.toString() + "    " + loader.toString());  // 類名 和 類加載器
            System.out.println("");
            return classfileBuffer;
        }
    }
}

輸出結果如下

這里注意兩個細節

  • premain和main都是被同一個類加載器鎖加載的
  • main方法要new一個A類物件時,會先進入transform函式,且類加載器和前面一樣,然后再執行A類中的方法

除了這兩個細節外,我們應該注意到,像String、Runtime這些類,也被呼叫了,transform函式卻獲取不到!這里就跟類加載機制有關了!

2.1 類加載機制

雙親委派

首先要理解一下類加載的雙親委派機制,每一個類加載器創建時都要指定一個父加載器,當類加載器A要加載B類時,會先由其父加載器對B類進行加載,如果父加載器無法加載,則由它自己進行加載,看一下ClassLoader的源代碼就很好理解了

protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
    synchronized (getClassLoadingLock(name)) {
        // First, check if the class has already been loaded
        Class<?> c = findLoadedClass(name);  // 查看是不是本身已經加載過的類
        if (c == null) {
            long t0 = System.nanoTime();
            try {  // 父類先嘗試加載
                if (parent != null) {
                    c = parent.loadClass(name, false);
                } else {
                    c = findBootstrapClassOrNull(name);
                }
            } catch (ClassNotFoundException e) {
                // ClassNotFoundException thrown if class not found
                // from the non-null parent class loader
            }

            if (c == null) {  // 父類加載失敗,自己加載
                // If still not found, then invoke findClass in order
                // to find the class.
                long t1 = System.nanoTime();
                c = findClass(name);

                // this is the defining class loader; record the stats
                sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                sun.misc.PerfCounter.getFindClasses().increment();
            }
        }
        if (resolve) {
            resolveClass(c);
        }
        return c;
    }
}

結合源代碼也就很好理解,特別需要指出的是Class<?> c = findLoadedClass(name);這一行可以看出,每個ClassLoader都記錄著一個自己加載過的類,

BootStrap ClassLoader

jvm中所有的類都是由類加載器加載的,那類加載器又是誰加載的呢?首先,jvm啟動之后,會執行一段機器碼,也就是C寫好的程式,這段程式被成為Bootstrap ClassLoader,注意它不是可以訪問的java物件,由它去加載系統類,以及擴展類加載器(extClassLoader)和應用程式類加載器(AppClassLoader),

  • 其中extClassLoader負責加載<JAVA_HOME>/lib/ext目錄下或者由系統變數-Djava.ext.dir指定位路徑中的類別庫
  • AppClassLoader負責加載系統類路徑java -classpath-D java.class.path 指定路徑下的類別庫,也就是我們經常用到的classpath路徑

他們的關系如下:

  • 啟動類加載器,由C++實作,沒有父類,

  • 拓展類加載器(ExtClassLoader),由Java語言實作,父類加載器為null

  • 系統類加載器(AppClassLoader),由Java語言實作,父類加載器為ExtClassLoader

另外需要指出的是,一個Java類,由它的全限定名稱和加載它的classloader兩者確定,

說了這么多,回到2.1章節最開始的問題,agentmain和main都是同一個appClassLoader加載的,并且我們寫好的各種類都是AppClassLoader加載的,那BootstrapClassLoader和extClassLoader加載的類呼叫我們寫好的代理方法,這些類加載器向上委派尋找類時,擴展類加載器和引導類加載器都沒有加過,直接違背雙親委派原則!舉個例子,因為我們可以在transform函式里面獲取到類位元組碼,并加以修改,如果我們在系統類方法前面插了代理方法,由于這些系統類是被Bootstrap ClassLoader加載的,當BootstrapClassLoader檢查這些代理方法是否被加載時,直接就報錯了,因為代理類是appClassLoader加載的(見2.1 類加載機制這個章節上面的圖),要解決這個問題,我們就應該想辦法把代理類通過BootstrapClassLoader進行加載,從百度的OpenRASP可以學到解決方案:

// localJarPath為代理jar包的絕對路徑
inst.appendToBootstrapClassLoaderSearch(new JarFile(localJarPath))

通過appendToBootstrapClassLoaderSearch方法,可以把一個jar包放到Bootstrap ClassLoader的搜索路徑,也就是說,當Bootstrap ClassLoader檢查自身加載過的類,發現沒有找到目標類時,會在指定的jar檔案中搜索,從而避免前面提到的違背雙親委派問題,參考官方檔案和翻譯檔案

2.2 Instrumentation介紹

為了方便用戶對JVM進行操作,JDK1.5之后引入了這個Instrumentation特性,通過Instrumentation的實體物件,可以對jvm進行一定的操作,例如修改位元組碼、插樁等等,它的實作原理是JVMTI(JVM Tool Interface),也就是JVM向用戶提供的操作jvm的介面,JVMTI是事件驅動的,當發生一定的處理邏輯時,才會呼叫回呼介面,而這些介面可以讓用戶擴展一些邏輯,例如前面的transform函式呼叫,就是JVMTI監聽到類加載,就會基于這個事件,回呼instrumentation中的所有ClassTransformer.transform函式,進行類轉換(Class transform),所以我們可以理解為獲得instrumentation物件,就可以實作對一個jvm的一定操作,獲取的這個物件的方法就是前文提到的javaagent和attach方法,

Instrumentation類中常用方法

void addTransformer(ClassFileTransformer transformer, boolean canRetransform)//注冊ClassFileTransformer實體,注冊多個會按照注冊順序進行呼叫,所有的類被加載完畢之后會呼叫ClassFileTransformer實體,相當于它們通過了redefineClasses方法進行重定義,布林值引數canRetransform決定這里被重定義的類是否能夠通過retransformClasses方法進行回滾,

void addTransformer(ClassFileTransformer transformer)//相當于addTransformer(transformer, false),也就是通過ClassFileTransformer實體重定義的類不能進行回滾,

boolean removeTransformer(ClassFileTransformer transformer)//移除(反注冊)ClassFileTransformer實體,

void retransformClasses(Class<?>... classes)//已加載類進行重新轉換的方法,重新轉換的類會被回呼到ClassFileTransformer的串列中進行處理,

void appendToBootstrapClassLoaderSearch(JarFile jarfile)//指定 JAR 檔案,放到Bootstrap ClassLoader搜索路徑

void appendToSystemClassLoaderSearch(JarFile jarfile)//將某個jar加入到Classpath里供AppClassloard去加載,

Class[] getAllLoadedClasses()//回傳 JVM 當前加載的所有類的陣列

Class[] getInitiatedClasses(ClassLoader loader)//獲取所有已經被初始化過了的類,

boolean isModifiableClass(Class<?> theClass)//確定一個類是否可以被 retransformation 或 redefinition 修改

void redefineClasses(ClassDefinition... definitions)//重定義類,也就是對已經加載的類進行重定義,ClassDefinition型別的入參包括了對應的型別Class<?>物件和位元組碼檔案對應的位元組陣列,

Instrumentation觸發流程

摘自https://www.cnblogs.com/rickiyang/p/11368932.html

JVMTIAgent是一個利用JVMTI暴露出來的介面提供了代理啟動時加載(agent on load)、代理通過attach形式加載(agent on attach)和代理卸載(agent on unload)功能的動態庫,instrument agent可以理解為一類JVMTIAgent動態庫,別名是JPLISAgent(Java Programming Language Instrumentation Services Agent),也就是專門為java語言撰寫的插樁服務提供支持的代理

啟動時加載instrument agent程序:

  1. 創建并初始化 JPLISAgent;
  2. 監聽 VMInit 事件,在 JVM 初始化完成之后做下面的事情:
    1. 創建 InstrumentationImpl 物件 ;
    2. 監聽 ClassFileLoadHook 事件 ;
    3. 呼叫 InstrumentationImpl 的loadClassAndCallPremain方法,在這個方法里會去呼叫 javaagent 中 MANIFEST.MF 里指定的Premain-Class 類的 premain 方法 ;
  3. 決議 javaagent 中 MANIFEST.MF 檔案的引數,并根據這些引數來設定 JPLISAgent 里的一些內容,

運行時加載instrument agent程序:

通過 JVM 的attach機制來請求目標 JVM 加載對應的agent,程序大致如下:

  1. 創建并初始化JPLISAgent;
  2. 決議 javaagent 里 MANIFEST.MF 里的引數;
  3. 創建 InstrumentationImpl 物件;
  4. 監聽 ClassFileLoadHook 事件;
  5. 呼叫 InstrumentationImpl 的loadClassAndCallAgentmain方法,在這個方法里會去呼叫javaagent里 MANIFEST.MF 里指定的Agent-Class類的agentmain方法,

Instrumentation的局限性

摘自https://www.cnblogs.com/rickiyang/p/11368932.html

  1. premain和agentmain兩種方式修改位元組碼的時機都是類檔案加載之后,也就是說必須要帶有Class型別的引數,不能通過位元組碼檔案和自定義的類名重新定義一個本來不存在的類,
  2. 類的位元組碼修改稱為類轉換(Class Transform),類轉換其實最終都回歸到類重定義Instrumentation#redefineClasses()方法,此方法有以下限制:
    1. 新類和老類的父類必須相同;
    2. 新類和老類實作的介面數也要相同,并且是相同的介面;
    3. 新類和老類訪問符必須一致, 新類和老類欄位數和欄位名要一致;
    4. 新類和老類新增或洗掉的方法必須是private static/final修飾的;
    5. 可以修改方法體,

除了上面的方式,如果想要重新定義一個類,可以考慮基于類加載器隔離的方式:創建一個新的自定義類加載器去通過新的位元組碼去定義一個全新的類,不過也存在只能通過反射呼叫該全新類的局限性,

另外由于JVM維護運行執行緒和邏輯的安全,禁止對運行時的類新加方法并重新定義該方法,只允許修改方法體中的邏輯,如果多次attach時,顯然就會出現重復插樁,造成重復警告等,影響業務執行緒,

2.3 javassist

前面提到修改位元組碼以實作在系統類或其它重要類執行前后插入代碼,運行時防御惡意攻擊,基于javassist容易上手的特點,所以直接用javassist修改位元組碼,javassist有幾個重要的類及其方法:

ClassPool

  • getDefault:回傳一個ClassPool物件,ClassPool是單例模式,保存了當前運行環境中創建過的CtClass
  • get/getCtClass:根據類名獲取該類的CtClass物件,而后進一步操作CtClass物件

CtClass:對class檔案的決議,將其描述為一個java物件

  • detach,將一個class從ClassPool中洗掉,減小記憶體消耗

  • getDeclaredMethod(String arg), 獲取一個CtMethod物件

  • getDeclaredMethods,獲取所有的這個類中所有的方法,并回傳CtMethod陣列

  • getConstructor(String arg),獲取一個指定的構造方法,回傳CtConstructor物件

  • getConstructors,獲取所有的構造方法,回傳CtConstructor陣列

  • toBytecode,將ctClass物件的位元組碼轉換成位元組陣列,這個方法在rasp中非常需要

  • writeFile,將ctClass物件的修改保存到檔案中,

CtMethod:對類中的方法的描述,可以通過這個物件,在一個方法前后添加代碼

  • insertBefore,很明顯,在給定的方法前插入一段代碼
  • insertAfter,也很明顯,在給定方法的所有return前,插入一段代碼,報錯會掠過這些代碼
  • insertAt,在指定位置插入代碼,一般不這么操作,改變系統類中方法的邏輯,可能會引發更多的問題
  • setBody,將方法的內容設定為指定的代碼,如果是abstrict修飾的方法,該修飾符將被移除,指定的代碼用$1,$2代表實際傳入的引數
ctMethod.setBody("{$1='a';if ($2==1) return 0;.....}")

問題來了:如何修改jvm中的位元組碼

根據前面提到的這些方法,我們可以思考這樣一個問題,就算用了javassist提供的各種方法,給位元組碼中添加了各類方法,但也只能保存到class檔案中,而這個類jvm在運行時,我們修改class檔案并不會影響jvm中的位元組碼,想要執行插入的代碼,只能重新運行class檔案,能不能動態修改jvm中的位元組碼,改變程式運行結果呢??

方法1,通過ClassLoader#defineClass方法覆寫jvm中的位元組碼

package com.bitterz.assist;
import javassist.*;
import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.security.ProtectionDomain;

public class Test {
    private final String name="laowang";

    @Override
    public boolean equals(Object o){
        System.out.println(this.name);
        return false;
    }

    public static void main(String[] args) throws NotFoundException, CannotCompileException, IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException {
        // 先測驗一次equals方法的輸出
        Test test1 = new Test();
        test1.equals("a");
        System.out.println("+++++++++++++++++分割+++++++++++++++++");
        // 獲取當前class檔案位置
        Process chdir = Runtime.getRuntime().exec("cmd /c chdir");
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(chdir.getInputStream(), "gbk"));
        String baseDir = bufferedReader.readLine();
        String classDir = baseDir + "/target/classes".replace("/", "\\");
        // 添加class路徑并找到Test類的位元組碼
        ClassPool classPool = new ClassPool(true);
        classPool.appendClassPath(classDir);
        CtClass ctClass = classPool.get("com.bitterz.assist.Test");
        CtMethod[] methods = ctClass.getMethods();
        String src = "https://www.cnblogs.com/bitterz/p/System.out.println("+ "\" insert by javassist \"" +");";

        // 找到對應的method,并在前后插入代碼
        for (CtMethod method : methods) {
            if (method.getName().contains("equals")){
                method.insertBefore(src);
            }
        }
        // 獲取位元組碼
        byte[] bytes = ctClass.toBytecode();

        // 反射呼叫defineClass方法,將位元組碼覆寫到jvm中
        ClassLoader classLoader = new ClassLoader() {
        };

        String name = Test.class.getName();
        Method defineClass = Class.forName(ClassLoader.class.getName()).getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class, ProtectionDomain.class);
        defineClass.setAccessible(true);
        Class<?> test = (Class<?>) defineClass.invoke(classLoader,  name, bytes, 0, bytes.length, null);
        // 這里取巧用了equals方法,是因為所有類都繼承自Object類,因此能過夠編譯通過,前面重寫了equals方法,所以會呼叫到重寫的equals方法中
        test.newInstance().equals("a");
    }
}

輸出結果如下:

可見,通過javassist修改位元組碼后,利用ClassLoader#defineClass方法可以覆寫jvm中的位元組碼,實作對jvm中已加載類的動態修改,

  • 不過這種方式受限于類加載機制和jvm對重復類定義的檢查,每個類最好用新的類加載器去加載,否則在defineClass時會出現報錯
  • 并且,受限于jvm的安全機制,無法修改系統類,例如java.lang下的類

方法2,通過ClassFileTransformer#transform方法修改位元組碼

在前一篇筆記中,記錄了通過javaagent機制可以對jvm加載過的類進行類轉換(Class Transform),也就是在ClassFileTransformer#transform中回傳新的位元組碼,會覆寫原有的位元組碼,下面來試試對java.lang.ProcessBuilder的位元組碼進行修改,實作hook

首先時需要執行的main方法

// 專案名為test,跟后面的agent不在一個專案
package com.bitterz;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.String;

public class Main {
    public static void main(String[] args) throws InterruptedException, IOException {
        System.out.println("main start!");
        ProcessBuilder processBuilder = new ProcessBuilder();
        processBuilder.command("cmd", "/c", "chdir");
        Process process = processBuilder.start();
        InputStream inputStream =  process.getInputStream();
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, "gbk"));
        System.out.println(bufferedReader.readLine());
    }
}

test專案對應的pom.xml檔案,可以直接打包成jar,并且指定了啟動類
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.bitterz</groupId>
    <artifactId>test</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <configuration>
                    <archive>
                        <manifestEntries>
                            <Main-Class>com.bitterz.Main</Main-Class>
                        </manifestEntries>
                    </archive>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

接下來是agent專案

// premain,這個類會在前面那個專案的main方法啟動前執行
package com.bitterz;

import com.bitterz.hook.ProcessBuilderHook;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.instrument.Instrumentation;
import java.lang.instrument.UnmodifiableClassException;

public class PreMain {
    public static void premain(String agentArgs, Instrumentation inst) throws IOException, UnmodifiableClassException {
        // 先測驗一次使用ProcessBuilder獲取當前路徑
        System.out.println("\n");
        ProcessBuilder processBuilder = new ProcessBuilder();
        processBuilder.command("cmd", "/c", "chdir");
        Process process = processBuilder.start();
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream(), "gbk"));
        System.out.println(bufferedReader.readLine());

        // 添加ClassFileTransformer類
        ProcessBuilderHook processBuilderHook = new ProcessBuilderHook(inst);
        inst.addTransformer(processBuilderHook, true);

        // 獲取所有jvm中加載過的類
        Class[] allLoadedClasses = inst.getAllLoadedClasses();
        for (Class aClass : allLoadedClasses) {
            if (inst.isModifiableClass(aClass) && !aClass.getName().startsWith("java.lang.invoke.LambdaForm")){
                // 呼叫instrumentation中所有的ClassFileTransformer#transform方法,實作類位元組碼修改
                inst.retransformClasses(new Class[]{aClass});
            }
        }
        System.out.println("++++++++++++++++++hook finished++++++++++++++++++\n");
    }
}
// 在transform中執行類轉換的邏輯,插入過濾代碼
package com.bitterz.hook;

import java.io.IOException;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;
import javassist.*;

public class ProcessBuilderHook implements ClassFileTransformer {
    private Instrumentation inst;
    private ClassPool classPool;
    public ProcessBuilderHook(Instrumentation inst){
        this.inst = inst;
        this.classPool = new ClassPool(true);
    }

    public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) {
        if (className.equals("java/lang/ProcessBuilder")){
            CtClass ctClass = null;
            try {
                // 找到ProcessBuilder對應的位元組碼
                ctClass = this.classPool.get("java.lang.ProcessBuilder");
                // 獲取所有method
                CtMethod[] methods = ctClass.getMethods();
                // $0代表this,這里this = 用戶創建的ProcessBuilder實體物件
                String src = "https://www.cnblogs.com/bitterz/p/if ($0.command.get(0).equals(/"cmd\"))" +
                        "{System.out.println(\"危險!\");" +
                        "System.out.println();"+
                        "return null;}";
                for (CtMethod method : methods) {
                    // 找到start方法,并插入攔截代碼
                    if (method.getName().equals("start")){
                        method.insertBefore(src);
                        break;
                    }
                }
                classfileBuffer = ctClass.toBytecode();
            }
            catch (NotFoundException | CannotCompileException | IOException e) {
                e.printStackTrace();
            }
            finally {
                if (ctClass != null){
                    ctClass.detach();
                }
            }
        }
        return classfileBuffer;
    }
}

然后是agent專案對應的pom.xml檔案,因為要用javassist包,所以把依賴也打包進去了

premain對應的pom.xml,可以直接用maven打包成jar,并且自動填寫了MANIFEST.MF中需要的欄位
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.bitterz</groupId>
    <artifactId>java-agent</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>javassist</groupId>
            <artifactId>javassist</artifactId>
            <version>3.12.0.GA</version>
        </dependency>
    </dependencies>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
            </plugin>

            <plugin>
                <artifactId>maven-assembly-plugin</artifactId>
                <configuration>
                    <archive>
                        <manifestEntries>
                            <Premain-Class>com.bitterz.PreMain</Premain-Class>
                            <Can-Redefine-Classes>true</Can-Redefine-Classes>
                            <Can-Retransform-Classes>true</Can-Retransform-Classes>
                        </manifestEntries>
                    </archive>
                    <descriptorRefs>
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                </configuration>
                <executions>
                    <execution>
                        <id>make-assembly</id>
                        <phase>package</phase>
                        <goals>
                            <goal>single</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

做好前面的準備作業之后,執行一下,看看效果

  • 很明顯,agent專案中使用ProcessBuilder執行系統命令時,因為還沒有插入檢測邏輯,所以沒有被攔截,成果回傳當前路徑
  • hook完成后,main方法啟動,執行start后,觸發插入的檢測代碼,并回傳一個null
  • Main.java的15行報錯,出現了null值,看看15行是啥InputStream inputStream = process.getInputStream();,很明顯了,hook成功,回傳一個null,所以15行這里會報錯

2.4 總結

到此java RASP的雛形就出來了,通過javaagent機制,在專案啟動前,添加類轉換方法,利用javassist、ASM這些修改位元組碼的工具對關鍵系統類,在類轉換方法中修改位元組碼,并回傳給jvm,這樣可以繞過defineClass對系統類的保護,完成對系統類和基礎類的位元組碼修改,實作對這些類的hook,

針對不同類,還需要進一步實作不同的方法的hook以及檢測邏輯,考慮到通用性,或許可以用json、Yaml這類規則,以便于在php、python、go等語言中能夠通用規則,或者使用OpenRASP的思路,java負責hook方法,把引數交給js來實作檢測邏輯,同時也能兼顧php等語言的規則通用,或許,使用機器學習或深度學習模型,也能對引數進行檢測,這就涉及到很廣闊的思路了,

另外就是各種心跳、云控的設計了,不是RASP的重點,所以沒有進一步實作,java RASP的研究就到這里了(好像有點淺嘗則止呢?不過結合OpenRASP的原始碼,很容易就能理解java RASP的實作思路了)

參考

https://www.cnblogs.com/rickiyang/p/11368932.html

https://www.cnblogs.com/kendoziyu/p/maven-auto-build-javaagent-jar.html

Java底層防護 - OpenRASP核心原始碼淺析

Java RASP淺析——以百度OpenRASP為例

淺談 RASP

appendToBootstrapClassLoaderSearch方法說明

Instrumentation中文檔案

Instrumentation官方檔案

JVMTI解讀

深入理解Java類加載器(ClassLoader)

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/294371.html

標籤:其他

上一篇:OpenCV(十六)邊緣檢測2 -- Laplace(拉普拉斯)二階微分算子

下一篇:UVa11054 Gergovia的酒交易(數學歸納法)

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more