簡體   English   中英

ByteBuddy 附加到本地運行的進程

[英]ByteBuddy attach to a local running process

我正在嘗試使用 ByteBuddy 附加到在我的計算機上運行的正在運行的進程。 我希望在我附加到正在運行的程序時,我的代理將導致加載的類被重新加載並顯示我的 Transformer 的打印語句。

相反,當我停止附加到的正在運行的進程時,我會看到一些來自 Transformer 的打印語句,用於某些 JDK 類。

代碼貼在下面:

import net.bytebuddy.ByteBuddy;
import net.bytebuddy.agent.ByteBuddyAgent;
import net.bytebuddy.dynamic.loading.ClassReloadingStrategy;
import net.bytebuddy.implementation.FixedValue;

import java.io.*;

import static net.bytebuddy.matcher.ElementMatchers.named;

public class Thief {

    public static void main(String[] args) throws Throwable {
        String pid = "86476"; // <-- modify this to attach to any java process running on your computer
        System.out.println(new Thief().guessSecurityCode(pid));
    }

    public String guessSecurityCode(final String pid) throws Throwable {
        File jarFile = createAgent();
        ByteBuddyAgent.attach(jarFile, pid);
        return "0000";
    }


    private static String generateSimpleAgent() {

        return  "import java.lang.instrument.ClassFileTransformer;" + "\n" +
                "import java.lang.instrument.Instrumentation;" + "\n" +
                "import java.security.ProtectionDomain;" + "\n" +
                "\n\n" +
                "public class Agent {" +"\n" +
                "    public static void agentmain(String argument, Instrumentation inst) {" +"\n" +
                "        inst.addTransformer(new ClassFileTransformer() {" +"\n" +
                "            @Override" +"\n" +
                "            public byte[] transform(" +"\n" +
                "                ClassLoader loader," +"\n" +
                "                String className," +"\n" +
                "                Class<?> classBeingRedefined," +"\n" +
                "                ProtectionDomain protectionDomain," +"\n" +
                "                byte[] classFileBuffer) {" +"\n" +
                "            System.out.println(\"transform on : \" +className);" +"\n" +
                "            return classFileBuffer;" +"\n" +
                "            }" +"\n" +
                "        });" +"\n" +
                "    }" +"\n" +
                "}" +"\n";
    }

    private static String generateAgentManifest() {
        return  String.join("\n", "Agent-Class: Agent",
                                                         "Can-Retransform-Classes: true",
                                                         "Can-Redefine-Classes: true",
                                                         "Premain-Class: Agent"
        );
    }

    private static String generateAgentManifest2() {
        return  String.join("\n",
                "Manifest-Version: 1.0",
                            "Agent-Class: Agent",
                            "Permissions: all-permissions"
        );
    }

    private static String generateTransformer() {
        return String.join("\n",
                "import java.lang.instrument.ClassFileTransformer;",
                            "import java.security.ProtectionDomain;",
                            "import java.util.Arrays;",
                            "public class Transformer implements ClassFileTransformer {",
                            "    public byte[] transform(ClassLoader loader, String className, Class<?> cls, ProtectionDomain dom, byte[] buf) {",
                            "        return null;",
                            "    }",
                            "}"
        );
    }

    private static void writeFile(String path, String data) throws IOException {
        final PrintWriter out = new PrintWriter(path);
        out.print(data);
        out.close();
    }

    private static void runCommand(String cmd) throws Exception {
        System.out.println("[commmand] " + cmd);
        String s;
        Process p = Runtime.getRuntime().exec(cmd);
        BufferedReader out = new BufferedReader(new InputStreamReader(p.getInputStream()));
        while ((s = out.readLine()) != null) {
            System.out.println("[out] " + s);
        }
        out = new BufferedReader(new InputStreamReader(p.getErrorStream()));
        while ((s = out.readLine()) != null) {
            System.out.println("[err] " + s);
        }
        p.waitFor();
        System.out.println("[exit status] " + p.exitValue());
        p.destroy();
    }

    private static File createAgent() throws Throwable {
        writeFile("Agent.java", generateSimpleAgent());
        writeFile("Transformer.java", generateTransformer());
        writeFile("manifest.mf", generateAgentManifest2());
        runCommand("javac Agent.java Transformer.java");
        runCommand("jar -cfm agent.jar manifest.mf Agent.class Transformer.class");
        return new File("agent.jar");
    }
}

僅添加轉換器不會導致重新加載已加載的類。 默認情況下,您的轉換器只會看到新加載的類,因此您在退出時看到某些類的原因是這些類之前沒有使用過,而是專門為關閉過程加載的。

要重新轉換類,您首先必須使用addTransformer(yourTransformer, true)進行注冊,然后使用要轉換的類調用retransformClasses 注意getAllLoadedClassesgetInitiatedClasses(ClassLoader)的存在

作為附加說明,我強烈不鼓勵采用將 Java 代理作為源代碼字符串嵌入的方法,需要將它們寫入臨時文件,調用編譯器並最終創建 jar 文件。 您可以輕松地將 Agent 類集成到您的普通源代碼中。 然后,要生成僅包含 Agent 類的 jar 文件,您只需將現有的.class文件從應用程序的代碼庫復制到 Agent jar。 對於簡單的情況,您可以同時使您的應用程序 jar 文件成為有效的代理 jar 文件,並且只需使用該文件而無需任何額外的復制步驟。

此外,請記住, ClassFileTransformer應始終為所有未更改的類返回null 返回原始類文件字節在語義上是相同的,但它需要調用方額外的努力才能發現您沒有更改它。 對於將為每個加載的類調用的代碼,但通常只對少數感興趣(或只想打印信息而不更改任何內容),這樣的性能問題很重要。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM