.net byte转java byte_Java Web安全 || Java基础 Java Agent
點(diǎn)擊上方“凌天實(shí)驗(yàn)室”,“星標(biāo)或置頂公眾號”
漏洞、技術(shù)還是其他,我都想第一時(shí)間和你分享
“【歷史】已連載更新全部內(nèi)容:【菜單欄】-【JAVA SEC】
01
?Java Agent
JDK1.5開始,Java新增了Instrumentation(Java Agent API)和JVMTI(JVM Tool Interface)功能,允許JVM在加載某個(gè)class文件之前對其字節(jié)碼進(jìn)行修改,同時(shí)也支持對已加載的class(類字節(jié)碼)進(jìn)行重新加載(Retransform)。
利用Java Agent這一特性衍生出了APM(Application Performance Management,應(yīng)用性能管理)、RASP(Runtime application self-protection,運(yùn)行時(shí)應(yīng)用自我保護(hù))、IAST(Interactive Application Security Testing,交互式應(yīng)用程序安全測試)等相關(guān)產(chǎn)品,它們都無一例外的使用了Instrumentation/JVMTI的API來實(shí)現(xiàn)動(dòng)態(tài)修改Java類字節(jié)碼并插入監(jiān)控或檢測代碼。
Java Agent有兩種運(yùn)行模式:
啟動(dòng)Java程序時(shí)添加-javaagent(Instrumentation API實(shí)現(xiàn)方式)或-agentpath/-agentlib(JVMTI的實(shí)現(xiàn)方式)參數(shù),如java -javaagent:/data/XXX.jar LingXeTest。
JDK1.6新增了attach(附加方式)方式,可以對運(yùn)行中的Java進(jìn)程附加Agent。
這兩種運(yùn)行方式的最大區(qū)別在于第一種方式只能在程序啟動(dòng)時(shí)指定Agent文件,而attach方式可以在Java程序運(yùn)行后根據(jù)進(jìn)程ID動(dòng)態(tài)注入Agent到JVM。
02
Java Agent Hello World
讓我們來運(yùn)行一個(gè)Java的HelloWorld程序。
HelloWorld示例代碼:
package com.anbai.sec.agent;/**
* Creator: yz
* Date: 2020/1/2
*/
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello World...");
}
}
程序運(yùn)行結(jié)果:
Hello World...假設(shè)我們現(xiàn)在有一個(gè)需求:必須在不重新編譯某個(gè)類的情況下(甚至有可能是不重啟應(yīng)用服務(wù)的情況下)動(dòng)態(tài)的改變類方法的執(zhí)行邏輯是非常困難的,但如果使用Agent的Instrumentation API就可以非常容易的實(shí)現(xiàn)了,例如將下列程序(HelloWorld.java)的輸出變成Hello Agent...。
首先我們需要修改:javaweb-sec/javaweb-sec-source/javasec-agent/src/main/resources/MANIFEST.MF文件中的Premain-Class配置為com.anbai.sec.agent.JavaSecHelloWorldAgent,然后再執(zhí)行如下命令使用Maven構(gòu)建Agent Jar包:
cd javaweb-sec/javaweb-sec-source/javasec-agentmvn clean install
Maven構(gòu)建完成后在javaweb-sec/javaweb-sec-source/javasec-agent/target目錄會自動(dòng)生成一個(gè)javasec-agent.jar文件,這個(gè)文件也就是我們寫好的用于處理HelloWorld程序輸出結(jié)果的Java Agent程序。
JavaSecHelloWorldAgent動(dòng)態(tài)替換HelloWorld字符串示例代碼:
/** 靈蜥Java Agent版 [Web應(yīng)用安全智能防護(hù)系統(tǒng)]
* ----------------------------------------------------------------------
* Copyright ? 安百科技(北京)有限公司
*/
package com.anbai.sec.agent;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;
import java.util.Arrays;
/**
* Creator: yz
* Date: 2020/1/2
*/
public class JavaSecHelloWorldAgent {
/**
* 替換HelloWorld的輸出字符串為"Hello Agent...",將二進(jìn)制轉(zhuǎn)換成字符串?dāng)?shù)組,替換字符串?dāng)?shù)組并生成新的二進(jìn)制
*
* @param className 類名
* @param classBuffer 類字節(jié)碼
* @return 替換后的類字節(jié)碼
*/
private static byte[] replaceBytes(String className, byte[] classBuffer) {
// 將類字節(jié)碼轉(zhuǎn)換成byte字符串
String bufferStr = Arrays.toString(classBuffer);
System.out.println(className + "類替換前的字節(jié)碼:" + bufferStr);
bufferStr = bufferStr.replace("[", "").replace("]", "");
// 查找需要替換的Java二進(jìn)制內(nèi)容
byte[] findBytes = "Hello World...".getBytes();
// 把搜索的字符串byte轉(zhuǎn)換成byte字符串
String findStr = Arrays.toString(findBytes).replace("[", "").replace("]", "");
// 二進(jìn)制替換后的byte值,注意這個(gè)值需要和替換的字符串長度一致,不然會破壞常量池
byte[] replaceBytes = "Hello Agent...".getBytes();
// 把替換的字符串byte轉(zhuǎn)換成byte字符串
String replaceStr = Arrays.toString(replaceBytes).replace("[", "").replace("]", "");
bufferStr = bufferStr.replace(findStr, replaceStr);
// 切割替換后的byte字符串
String[] byteArray = bufferStr.split("\\s*,\\s*");
// 創(chuàng)建新的byte數(shù)組,存儲替換后的二進(jìn)制
byte[] bytes = new byte[byteArray.length];
// 將byte字符串轉(zhuǎn)換成byte
for (int i = 0; i < byteArray.length; i++) {
bytes[i] = Byte.parseByte(byteArray[i]);
}
System.out.println(className + "類替換后的字節(jié)碼:" + Arrays.toString(bytes));
// 返回修改后的二進(jìn)制
return bytes;
}
/**
* Java Agent模式入口
*
* @param args 命令參數(shù)
* @param inst Agent Instrumentation 實(shí)例
*/
public static void premain(String args, final Instrumentation inst) {
// 添加自定義的Transformer
inst.addTransformer(new ClassFileTransformer() {
/**
* 類文件轉(zhuǎn)換方法,重寫transform方法可獲取到待加載的類相關(guān)信息
*
* @param loader 定義要轉(zhuǎn)換的類加載器;如果是引導(dǎo)加載器,則為 null
* @param className 類名,如:java/lang/Runtime
* @param classBeingRedefined 如果是被重定義或重轉(zhuǎn)換觸發(fā),則為重定義或重轉(zhuǎn)換的類;如果是類加載,則為 null
* @param protectionDomain 要定義或重定義的類的保護(hù)域
* @param classfileBuffer 類文件格式的輸入字節(jié)緩沖區(qū)(不得修改)
* @return 返回一個(gè)通過ASM修改后添加了防御代碼的字節(jié)碼byte數(shù)組。
*/
@Override
public byte[] transform(ClassLoader loader, String className, Class> classBeingRedefined,
ProtectionDomain protectionDomain, byte[] classfileBuffer) {
// 將目錄路徑替換成Java類名
className = className.replace("/", ".");
// 只處理com.anbai.sec.agent.HelloWorld類的字節(jié)碼
if (className.equals("com.anbai.sec.agent.HelloWorld")) {
// 替換HelloWorld的輸出字符串
return replaceBytes(className, classfileBuffer);
}
return classfileBuffer;
}
}, true);// 第二個(gè)參數(shù)true表示是否允許Agent Retransform,需配合MANIFEST.MF中的Can-Retransform-Classes: true配置
}
}
我們需要在運(yùn)行HelloWorld的時(shí)候添加-javaagent:jar路徑參數(shù),例如:
java -jar -javaagent:/Users/yz/IdeaProjects/javaweb-sec/javaweb-sec-source/javasec-agent/target/javasec-agent.jar com.anbai.sec.agent.HelloWorld程序執(zhí)行結(jié)果:
com.anbai.sec.agent.HelloWorld類替換前的字節(jié)碼:[....省去兩則一致的數(shù)據(jù), 87, 111, 114, 108, 100, ...]com.anbai.sec.agent.HelloWorld類替換后的字節(jié)碼:[....省去兩則一致的數(shù)據(jù), 65, 103, 101, 110, 116, ...]
Hello Agent...
由上可以看到程序的最終執(zhí)行結(jié)果已經(jīng)被我們動(dòng)態(tài)的修改為了:Hello Agent...,這種方式是最為簡單暴力的修改二進(jìn)制中的字符串值的方式在實(shí)際的業(yè)務(wù)場景下很顯然是不可行的,因?yàn)橹灰薷暮蟮淖址L度不一致就會破壞常量池導(dǎo)致程序無法執(zhí)行,為了能夠精準(zhǔn)有效的修改類字節(jié)碼我們通常會使用ASM庫。
03
?Instrumentation
java.lang.instrument.Instrumentation是Java提供的監(jiān)測運(yùn)行在JVM程序的API。利用Instrumentation我們可以實(shí)現(xiàn)如下功能:
動(dòng)態(tài)添加自定義的Transformer(addTransformer)。
動(dòng)態(tài)修改classpath(appendToBootstrapClassLoaderSearch、appendToSystemClassLoaderSearch)。
動(dòng)態(tài)獲取所有JVM已加載的類(getAllLoadedClasses)。
動(dòng)態(tài)獲取某個(gè)類加載器已實(shí)例化的所有類(getInitiatedClasses)。
直接修改已加載的類的字節(jié)碼(redefineClasses)。
動(dòng)態(tài)設(shè)置JNI前綴(setNativeMethodPrefix)。
重加載指定類字節(jié)碼(retransformClasses)。
Instrumentation類方法如下:
04
?ClassFileTransforme
java.lang.instrument.ClassFileTransformer是一個(gè)轉(zhuǎn)換類文件的代理接口,我們可以在獲取到Instrumentation對象后通過addTransformer方法添加自定義類文件轉(zhuǎn)換器。
示例中我們使用了addTransformer注冊了一個(gè)我們自定義的Transformer到Java Agent,當(dāng)有新的類被JVM加載時(shí)JVM會自動(dòng)回調(diào)用我們自定義的Transformer類的transform方法,傳入該類的transform信息(類名、類加載器、類字節(jié)碼等),我們可以根據(jù)傳入的類信息決定是否需要修改類字節(jié)碼,修改完字節(jié)碼后我們將新的類字節(jié)碼返回給JVM,JVM會驗(yàn)證類和相應(yīng)的修改是否合法,如果符合類加載要求JVM會加載我們修改后的類字節(jié)碼。
ClassFileTransformer類代碼:
package java.lang.instrument;public interface ClassFileTransformer {
/**
* 類文件轉(zhuǎn)換方法,重寫transform方法可獲取到待加載的類相關(guān)信息
*
* @param loader 定義要轉(zhuǎn)換的類加載器;如果是引導(dǎo)加載器,則為 null
* @param className 類名,如:java/lang/Runtime
* @param classBeingRedefined 如果是被重定義或重轉(zhuǎn)換觸發(fā),則為重定義或重轉(zhuǎn)換的類;如果是類加載,則為 null
* @param protectionDomain 要定義或重定義的類的保護(hù)域
* @param classfileBuffer 類文件格式的輸入字節(jié)緩沖區(qū)(不得修改)
* @return 返回一個(gè)通過ASM修改后添加了防御代碼的字節(jié)碼byte數(shù)組。
*/
byte[] transform(ClassLoader loader, String className, Class> classBeingRedefined,
ProtectionDomain protectionDomain, byte[] classfileBuffer);
}
重寫transform方法需要注意以下事項(xiàng):
ClassLoader如果是被Bootstrap ClassLoader(引導(dǎo)類加載器)所加載那么loader參數(shù)的值是空。
修改類字節(jié)碼時(shí)需要特別注意插入的代碼在對應(yīng)的ClassLoader中可以正確的獲取到,否則會報(bào)ClassNotFoundException,比如修改java.io.FileInputStream(該類由Bootstrap ClassLoader加載)時(shí)插入了我們檢測代碼,那么我們將必須保證FileInputStream能夠獲取到我們的檢測代碼類。
JVM類名的書寫方式路徑方式:java/lang/String而不是我們常用的類名方式:java.lang.String。
類字節(jié)必須符合JVM校驗(yàn)要求,如果無法驗(yàn)證類字節(jié)碼會導(dǎo)致JVM崩潰或者VerifyError(類驗(yàn)證錯(cuò)誤)。
如果修改的是retransform類(修改已被JVM加載的類),修改后的類字節(jié)碼不得新增方法、修改方法參數(shù)、類成員變量。
addTransformer時(shí)如果沒有傳入retransform參數(shù)(默認(rèn)是false)就算MANIFEST.MF中配置了Can-Redefine-Classes: true而且手動(dòng)調(diào)用了retransformClasses方法也一樣無法retransform。
卸載transform時(shí)需要使用創(chuàng)建時(shí)的Instrumentation實(shí)例。
**如果您在閱讀文章的時(shí)候發(fā)現(xiàn)任何問題都可以通過Vchat與我們聯(lián)系,也歡迎大家加入javasec微信群一起交流。
Vchat獲取方式:對話框發(fā)送“javasec”
凌天實(shí)驗(yàn)室凌天實(shí)驗(yàn)室,是安百科技旗下針對應(yīng)用安全領(lǐng)域進(jìn)行攻防研究的專業(yè)技術(shù)團(tuán)隊(duì),其核心成員來自原烏云創(chuàng)始團(tuán)隊(duì)及社區(qū)知名白帽子,團(tuán)隊(duì)專業(yè)性強(qiáng)、技術(shù)層次高且富有實(shí)戰(zhàn)經(jīng)驗(yàn)。實(shí)驗(yàn)室成立于2016年,發(fā)展至今團(tuán)隊(duì)成員已達(dá)35人,在應(yīng)用安全領(lǐng)域深耕不輟,向網(wǎng)絡(luò)安全行業(yè)頂尖水平攻防技術(shù)團(tuán)隊(duì)的方向夯實(shí)邁進(jìn)。
總結(jié)
以上是生活随笔為你收集整理的.net byte转java byte_Java Web安全 || Java基础 Java Agent的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 超级好看的14 款 Chrome 官方主
- 下一篇: java 数组存入数据库_Java中关于