在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

javaassit如何實現(xiàn)代對目標類的代理

科技綠洲 ? 來源:了不起 ? 作者:了不起 ? 2023-09-25 11:18 ? 次閱讀

有沒有想過,XMind是如何被破解的?那么今天我們就來看看javaassit這項技術,其實在你接觸的很多其他工具中這個工具早就被廣泛使用了

javaassit

我們知道,java是一門面向對象的編程語言,更是一門面向切面的編程語言,正是這個特性,讓Java更加地靈活。

可能你寫過基于Spring AOP的代碼,其原理都是基于JDK動態(tài)代理或者CGLIB來實現(xiàn),其局限性在于我們只能以方法作為連接點,來實現(xiàn)基于方法執(zhí)行過程的代理。

你可還知道更厲害的代理工具:AspectJ、javaassit,這些都是基于字節(jié)碼,屬于更底層,但是功能更強大的代理。

知識點

  • ASM

通過指令修改class字節(jié)碼,主要基于ClassReader結合JVM指令集直接操作字節(jié)碼,Cglib即是通過該技術實現(xiàn)。

  • JavaAssit

基于org.javassist:javassist類庫提供的CtPool工具類對字節(jié)碼進行修改

  • Instrumentation

JVM提供的一個可以修改已加載類的類庫,通過編寫java代碼即可完成對字節(jié)碼的修改

  • JavaAgent

JVM加載類之前與JVM運行時,基于JavaAssit、Instrumentation實現(xiàn)字節(jié)碼修改并加載到JVM

應用場景

  • IDE的調試功能,例如 Eclipse、IntelliJ IDEA
  • 熱部署功能,例如 JRebel、XRebel、spring-loaded
  • 線上診斷工具,例如 Btrace、Greys,還有阿里的 Arthas
  • 性能分析工具,例如 Visual VM、JConsole、TProfiler等
  • 全鏈路性能檢測工具,例如 Skywalking、Pinpoint等

示例

下面我們基于javaagent以及運行時Attach的模式看下javaassit如何實現(xiàn)目標類的代理的:

基于javaagent

  1. 編寫代理類

方法簽名固定,方法名為 premain ,參數(shù)分別對應args(不是數(shù)組)以及Instrumentation

public class JavaAgent {

    private static final String TARGET_CLASS_NAME = "com.sucl.blog.javaassit.Target";

    public static void premain(String args, Instrumentation instrumentation){
        AgentHelper.create(TARGET_CLASS_NAME).proxy(args, instrumentation);
    }

}
  1. 打包代理類

這里我們借助maven插件 maven-shade-plugin ,主要是為了打包時修改/META-INF/MANIFEST.MF文件,需要加上Premain-Class這項

< plugin >
    < groupId >org.apache.maven.plugins< /groupId >
    < artifactId >maven-shade-plugin< /artifactId >
    < version >2.3< /version >
    < executions >
        < execution >
            < phase >package< /phase >
            < goals >
                < goal >shade< /goal >
            < /goals >
            < configuration >
                < transformers >
                    < transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer" >
                        < manifestEntries >
                            < Premain-Class >com.sucl.blog.agent.JavaAgent< /Premain-Class >
                            < Agent-Class >com.sucl.blog.agent.AttachAgent< /Agent-Class >
                            < Can-Redefine-Classes >true< /Can-Redefine-Classes >
                            < Can-Retransform-Classes >true< /Can-Retransform-Classes >
                        < /manifestEntries >
                    < /transformer >
                < /transformers >
            < /configuration >
        < /execution >
    < /executions >
< /plugin >
  1. 編寫測試類

目的很簡單,每隔3秒打印當前時間

public class JavaAgentMain {
    public static void main(String[] args) throws InterruptedException {
        Target target = new Target();
        while (true) {
            target.print(new Date());
            TimeUnit.SECONDS.sleep(3);
        }
    }
}

@Slf4j
class Target {
    public void print(Object obj) {
        log.info("打印內容:{}", obj);
    }
}
  1. 配置代理

如何讓我們編寫的代理生效,這里提供兩種方法:

  • 當你使用IDEA啟動時,可以在Config Configurations中通過配置VM OPTION,添加如下內容:

-javaagent:/your_jar_path/agent.jar=param=value

  • 當你使用java命令啟動時:

java -javaagent:/path/agent.jar=param=value -jar xxx.jar

  1. 測試

執(zhí)行測試類main方法,你可以看到,在打印時間前后,分別會打印“開始執(zhí)行方法:print”,“結束執(zhí)行方法:print”,這也是我們代理類實現(xiàn)的功能。

>> > 開始執(zhí)行方法:print
14:46:09.457 [main] INFO com.sucl.blog.javaassit.Target - 打印內容:Fri Mar 10 14:46:09 CST 2023
 >> > 結束執(zhí)行方法:print

基于Attach

  1. 編寫代理類

方法簽名固定,方法名為 attachmain ,參數(shù)分別對應args(不是數(shù)組)以及Instrumentation; 和上面的相比唯一的不同是方法名稱。

public class AttachAgent {
    
    private static final String TARGET_CLASS_NAME = "com.sucl.blog.javaassit.Target";
    
    public static void agentmain(String args, Instrumentation instrumentation){
        System.out.println(String.format(" >> > agentmain starting, args: %s",args));
        AgentHelper.create(TARGET_CLASS_NAME).proxy(args, instrumentation);
        System.out.println(String.format(" >> > agentmain finished"));
    }

}
  1. 打包代理類

同樣借助插件 maven-shade-plugin ,主要是為了打包時修改/META-INF/MANIFEST.MF文件,需要加上Agent-Class這項

< !-- 省略 ...-- >
< Agent-Class >com.sucl.blog.agent.AttachAgent< /Agent-Class >
< !-- 省略 ...-- >

注意,這里我們使用了ClassPool、CtClass、CtMethod相關的類,記得在pom.xml中引入對應的依賴

< dependency >
    < groupId >org.javassist< /groupId >
    < artifactId >javassist< /artifactId >
< /dependency >
  1. 編寫測試類

測試類完全一樣,由于啟動代理織入的方式不一樣,因此分為兩個類

public class AttachAgentMain {

    public static void main(String[] args) throws InterruptedException {
        Target target = new Target();
        while (true) {
            target.print(new Date());
            TimeUnit.SECONDS.sleep(3);
        }
    }

}
  1. 執(zhí)行代理

如何將編寫的代碼(AttachAgent)織入到目標類完成對目標類(Target)方法的代理?

這里我們需要用到jdk中的tool.jar,你可以在測試模塊中添加下面的依賴:

< dependency >
    < groupId >com.sun< /groupId >
    < artifactId >tools< /artifactId >
    < version >1.8< /version >
    < scope >system< /scope >
    < systemPath >${java.home}/../lib/tools.jar< /systemPath >
< /dependency >

如何在運行時進行代理織入:

public class AttachAgentTests {

    private static String JAR_PATH = AttachAgentTests.class.getClassLoader().getResource("").getPath().replace("test-classes/","")+"agent.jar";
    
    @Test
    public void attachAgent() throws Exception {
        String pid = findPid(KEY); // 通過jps命令找到AttachAgentMain執(zhí)行的pid
        VirtualMachine virtualMachine = VirtualMachine.attach(pid);
        virtualMachine.loadAgent(JAR_PATH.substring(1));
        virtualMachine.detach();
    }
}
  1. 測試
  • a. 先執(zhí)行測試代碼(AttachAgentMain.java),此時每間隔3秒會打印當前時間。
  • b. 執(zhí)行代理織入方法(AttachAgentTests#attachAgent)
  • c. 觀察測試代碼輸出結果,你會會發(fā)現(xiàn)此時每次打印時間前后都會有“開始執(zhí)行方法:print”,“結束執(zhí)行方法:print”

AgentHelper

public class AgentHelper {

    private String targetClassName;

    private AgentHelper(String targetClassName) {
        this.targetClassName = targetClassName;
    }

    public static AgentHelper create(String targetClassName){
        AgentHelper agentHelper = new AgentHelper(targetClassName);
        return agentHelper;
    }

    public void proxy(String args, Instrumentation instrumentation){
        Class targetClass = obtainTargetClass(instrumentation);

        try {
            instrumentation.addTransformer(new SimpleTransformer(targetClassName), true);
            instrumentation.retransformClasses(targetClass); //
        } catch (Exception e) {
            System.out.println(String.format(" >> > agentmain failure, error: %s: %s", e.getClass().getName(),e.getLocalizedMessage()));
            e.printStackTrace();
        }
    }

    private Class obtainTargetClass(Instrumentation instrumentation) {
        Class targetClass = null;
        for (Class loadedClass : instrumentation.getAllLoadedClasses()) {
            if(targetClassName.equals(loadedClass.getName())){
                targetClass = loadedClass;
            }
        }

        if(targetClass == null){
            try {
                // 無法加載
                targetClass = Class.forName(targetClassName);
            } catch (ClassNotFoundException e) {
                System.out.println(String.format(" >> > Class [%s] not found", targetClassName));
            }
        }
        return targetClass;
    }

    public static class SimpleTransformer implements ClassFileTransformer {

        private String targetClassName;

        public SimpleTransformer(String targetClassName) {
            this.targetClassName = targetClassName;
        }

        @Override
        public byte[] transform(ClassLoader loader, String className, Class< ? > classBeingRedefined,
                                ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
            if(!className.equals(targetClassName.replaceAll(".","/"))){
                return null;
            }

            ClassPool classPool = ClassPool.getDefault();
            System.out.println(String.format("+++++ 代理類名:%s", className));
            try {
                CtClass ctClass = classPool.get(className.replace("/","."));
                CtMethod[] ctMethods = ctClass.getDeclaredMethods();
                for (CtMethod ctMethod : ctMethods) { // 所有類方法
                    ctMethod.insertBefore(String.format("{System.out.println(" >> > 開始執(zhí)行方法:%s");}",ctMethod.getName()));
                    ctMethod.insertAfter(String.format("{System.out.println(" >> > 結束執(zhí)行方法:%s");}",ctMethod.getName()));
                }
                return ctClass.toBytecode();
            } catch (NotFoundException | CannotCompileException | IOException e) {
                System.out.println(String.format("+++++ 代理出錯:%s",e.getMessage()));
                e.printStackTrace();
            }
            return classfileBuffer;
        }
    }
}

通過上面的例子可以看到,兩種方式的比對如下:

對比JavaAgentAttachAgent
/META-INF/MANIFEST.MFPremain-ClassAgent-Class
代理類方法名稱premainattachmain
代理入口VM配置:-javaagentJVM attach進程ID
代理時機JVM加載字節(jié)碼時程序運行時
作用Java桌面程序Web應用

原理

代理可以發(fā)送在編譯時,類加載時或者是運行時。

這里你要清楚, java程序的入口是main方法 ,不管是普通程序(比如桌面應用、可執(zhí)行jar)或是Web應用(在Web容器中運行的基于Servlet的應用)

以javaagent為例,是在執(zhí)行main方法前對已經(jīng)加載到JVM的類進行修改,從而實現(xiàn)對目標類的代理,這里的修改是在字節(jié)碼層面的,當然我們可以基于ASM工具庫來實現(xiàn),但是門檻太高。

基于Instrumentation可以與編寫java代碼一樣,實現(xiàn)修改字節(jié)碼來

ClassPool:保存CtClass的池子,通過classPool.get(類全路徑名)來獲取CtClass CtClass:編譯時類信息,它是一個class文件在代碼中的抽象表現(xiàn)形式 CtMethod:對應類中的方法 CtField:對應類中的屬性、變量

XMind

還記得XMind8的破解之法嗎?

是不是需要在XMind.ini文件中插入這樣一段:-javaagent:.../XMindCrack.jar 要是你打開這個jar,你會看到這樣的內容:

圖片

首先你需要知道其原理,是通過/plugins/net.xmind.verify.jar中提供的方法LicenseVerifier#doCheckLicenseKeyBlacklisted來進行身份校驗

我們是不是只用修改License的校驗方法 doCheckLicenseKeyBlacklisted ,忽略其校驗過程并直接返回true就完事了?當然截圖中就是這樣做的,如果你想看懂那幾行代碼,可能你先要去學習ASM相關的知識。

InsnList insnList = methodNode.instructions;
insnList.clear();
insnList.add((AbstractInsnNode)new InsnNode(4));
insnList.add((AbstractInsnNode)new InsnNode(172));
methodNode.exceptions.clear();
methodNode.visitEnd();

以上代碼其實就是講方法體清除,并寫入“return true”

結束語

通過示例了解javaassit如何實現(xiàn)代對目標類的代理。是不是覺得java應用程序都能被修改,那不是太不安全了?所以,你覺得呢...

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權轉載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • JAVA
    +關注

    關注

    20

    文章

    2982

    瀏覽量

    106398
  • 編程語言
    +關注

    關注

    10

    文章

    1952

    瀏覽量

    35556
  • 代碼
    +關注

    關注

    30

    文章

    4872

    瀏覽量

    69913
  • 代理
    +關注

    關注

    1

    文章

    44

    瀏覽量

    11279
收藏 人收藏

    評論

    相關推薦

    JDK動態(tài)代理的原理

    在Java中,動態(tài)代理是一種機制,允許在運行時動態(tài)地創(chuàng)建代理對象來代替某個實際對象,從而在其前后執(zhí)行額外的邏輯。 為什么JDK動態(tài)代理只能代理接口
    的頭像 發(fā)表于 09-30 10:51 ?727次閱讀

    求教labview是否實現(xiàn)代碼編程? 多謝

    本帖最后由 leasor 于 2013-7-15 14:11 編輯 求教labview是否實現(xiàn)代碼編程? 多謝, 當我看見一些基本的編程結構, 也使用圖形方式, 我可恥的吐了, 使用腳本一的編程方式對于這些大牛來說, 實現(xiàn)
    發(fā)表于 07-15 13:58

    適配器模式和代理模式的區(qū)別

      代理模式  組成:  抽象角色:通過接口或抽象聲明真實角色實現(xiàn)的業(yè)務方法。  代理角色:實現(xiàn)抽象角色,是真實角色的
    發(fā)表于 10-22 15:17

    實現(xiàn)代碼自動生成的步驟

    文章目錄一、 目的二、 基本思想三、 代碼實現(xiàn)四、 其他工作五、 補充一、 目的工作中有時候感覺編程也是一種重復性勞動,尤其是涉及到讀寫數(shù)據(jù)一的內容,還有一些需要進行配置的場合,有時候就想,既然是
    發(fā)表于 08-17 09:14

    基于聚融合的多目標跟蹤算法

    目標跟蹤是多傳感器數(shù)據(jù)融合中的一個重要問題?;谀J阶R別理論,提出了一種通過對傳感器測量數(shù)據(jù)集,以區(qū)分源于不同目標的測量數(shù)據(jù)集合。對各個對應的
    發(fā)表于 07-01 08:40 ?18次下載

    java動態(tài)代理機制詳解的和接口描述

    的我們的功能,我們更需要學習的是其底層是怎么樣的一個原理,而AOP的原理就是java的動態(tài)代理機制,所以本篇隨筆就是對java的動態(tài)機制進行一個回顧。 在java的動態(tài)代理機制中,有兩個重要的或接口
    發(fā)表于 09-28 13:33 ?0次下載

    基于聚的多目標遺傳算法在職責分配中的應用

    在面向對象軟件設計與實現(xiàn)過程中,職責分配是其中最重要且復雜的步驟之一,它在很大程度上影響軟件質量。為了實現(xiàn)職責自動分配的目標,從軟件內聚
    發(fā)表于 11-28 17:35 ?0次下載
    基于聚<b class='flag-5'>類</b>的多<b class='flag-5'>目標</b>遺傳算法在<b class='flag-5'>類</b>職責分配中的應用

    空間鄰近的點目標實現(xiàn)方法

    了基于空間鄰近的點目標方法,通過Voronoi建模識別點目標間的空間鄰近關系,并以Voronoi勢力范圍來定義相似度準則,最終構建樹結構以實現(xiàn)
    發(fā)表于 12-19 10:47 ?0次下載
    空間鄰近的點<b class='flag-5'>目標</b>聚<b class='flag-5'>類</b><b class='flag-5'>實現(xiàn)</b>方法

    java的動態(tài)代理

    代理的對象與一個委托的對象關聯(lián),代理的對象本身并不真正實現(xiàn)服務,而是通過調用委托
    發(fā)表于 03-12 14:12 ?0次下載

    基于JDK和CGLB分別實現(xiàn)的動態(tài)代理

    本文檔內容介紹了基于JDK和CGLB分別實現(xiàn)的動態(tài)代理及源代碼
    發(fā)表于 03-12 14:56 ?0次下載

    如何在Golang中實現(xiàn)反向代理

    【導讀】在本文中,我們將了解反向代理,它的應用場景以及如何在 Golang 中實現(xiàn)它。 反向代理是位于 Web 服務器前面并將客戶端(例如 Web 瀏覽器)的請求轉發(fā)到 Web 服務器的服務器。它們
    的頭像 發(fā)表于 08-23 10:22 ?2273次閱讀

    http代理概述及代碼實現(xiàn)方法

    本文詳細介紹了Golang 實現(xiàn) http 代理實現(xiàn),在實際業(yè)務中有需求的同學可以學起來了!
    的頭像 發(fā)表于 05-14 15:02 ?4403次閱讀

    Golang實現(xiàn)一個簡單的http代理

    本文詳細介紹了Golang 實現(xiàn) http 代理實現(xiàn),在實際業(yè)務中有需求的同學可以學起來了!
    的頭像 發(fā)表于 04-10 11:29 ?1592次閱讀

    mybatis接口動態(tài)代理原理

    ,從而實現(xiàn)數(shù)據(jù)庫操作的動態(tài)生成和執(zhí)行。接下來,我將詳細介紹MyBatis接口動態(tài)代理的原理。 動態(tài)代理概念介紹 在Java語言中,動態(tài)代理是一種使用
    的頭像 發(fā)表于 12-03 11:52 ?1096次閱讀

    Python庫解析:通過庫實現(xiàn)代理請求與數(shù)據(jù)抓取

    在Python中,有多個庫可以幫助你實現(xiàn)代理請求和數(shù)據(jù)抓取。這些庫提供了豐富的功能和靈活的API,使得你可以輕松地發(fā)送HTTP請求、處理響應、解析HTML/XML/JSON數(shù)據(jù),以及進行復雜的網(wǎng)絡操作。
    的頭像 發(fā)表于 10-24 07:54 ?373次閱讀
    主站蜘蛛池模板: 国产女人在线视频 | 美女视频黄又黄又免费高清 | 黄色福利站 | 四虎最新紧急更新地址 | 性欧美高清强烈性视频 | 黄色美女网站免费 | 久久99国产精品免费观看 | 最新理论三级中文在线观看 | 日本一级大片 | 天堂成人 | 婷婷激情亚洲 | 日本特黄特色大片免费播放视频 | 国产va免费精品高清在线观看 | 亚洲国产色图 | 午夜在线视频免费 | 韩国一区二区三区视频 | 国产一区二卡三区四区 | 午夜爱爱毛片xxxx视频免费看 | 黄色视屏在线免费播放 | 香港日本三级在线播放 | 奇米影视四色首页手机在线 | 免费大片黄在线观看日本 | 欧美性猛交xxx嘿人猛交 | 四虎永久在线精品 | 天天爱天天做久久天天狠狼 | 六月婷婷啪啪 | 日韩免费视频一区二区 | 在线视频这里只有精品 | 国模啪啪一区二区三区 | 国产一级特黄aa大片在线 | 视频在线视频免费观看 | 久久伊人色 | 青草网址 | 午夜免费视频观看在线播放 | 77788色淫免费网站视频 | 久久香蕉综合精品国产 | 五月天狠狠操 | 黄色成人在线网站 | 欧美爽爽 | 999影院成 人在线影院 | 国产一线在线观看 |