Java Instrumentation

​ java Instrumentation指的是可以用独立于应用程序之外的代理(agent)程序来监测和协助运行在JVM上的应用程序。这种监测和协助包括但不限于获取JVM运行时状态,替换和修改类定义等。简单一句话概括下:Java Instrumentation可以在JVM启动后,动态修改已加载或者未加载的类,包括类的属性、方法。

java agent技术原理及简单实现 - kokov - 博客园 (cnblogs.com)

什么是java agent?

IDEA + maven 零基础构建 java agent 项目 - 一灰灰Blog - 博客园 (cnblogs.com)

java agent本质上可以理解为一个插件,该插件就是一个精心提供的jar包,这个jar包通过JVMTI(JVM Tool Interface)完成加载,最终借助JPLISAgent(Java Programming Language Instrumentation Services Agent)完成对目标代码的修改。

java agent技术的主要功能如下:

  • 可以在加载java文件之前做拦截把字节码做修改
  • 可以在运行期将已经加载的类的字节码做变更
  • 还有其他的一些小众的功能
    • 获取所有已经被加载过的类
    • 获取所有已经被初始化过了的类
    • 获取某个对象的大小
    • 将某个jar加入到bootstrapclasspath里作为高优先级被bootstrapClassloader加载
    • 将某个jar加入到classpath里供AppClassloard去加载
    • 设置某些native方法的前缀,主要在查找native方法的时候做规则匹配

Instrument

(32条消息) ClassPool CtClass浅析_罗小辉的专栏-CSDN博客

​ instrument是JVM提供的一个可以修改已加载类的类库,专门为Java语言编写的插桩服务提供支持。它需要依赖JVMTI的Attach API机制实现。在JDK 1.6以前,instrument只能在JVM刚启动开始加载类时生效,而在JDK 1.6之后,instrument支持了在运行时对类定义的修改。要使用instrument的类修改功能,我们需要实现它提供的ClassFileTransformer接口,定义一个类文件转换器。接口中的transform()方法会在类文件被加载时调用,而在transform方法里,我们可以利用ASM或Javassist对传入的字节码进行改写或替换,生成新的字节码数组后返回。

​ 总之,transform返回值为需要替换的class的字节码。有两种方法获取字节码,一种使用文件读取的方式,直接读取相应class文件的字节码,还有一种使用Javaassist包,结合反射机制进行字节码的替换。

我们来看一下第二种的示例代码

SimpleAgent.java 作为Javagent去注入目标程序

import java.lang.instrument.Instrumentation;
import java.lang.reflect.Method;
import java.util.LinkedList;
import java.util.List; public class SimpleAgent { /**
* jvm 参数形式启动,运行此方法
*
* @param agentArgs
* @param inst
*/
private static String className = "com.company.BaseMain";
private static String methodName = "print";
public static void premain(String agentArgs, Instrumentation instrumentation) {
System.out.println("premain");
//instrumentation.addTransformer(new TestTransformer(className, methodName));
} /**
* 动态 attach 方式启动,运行此方法
*
* @param agentArgs
* @param instrumentation
*/
public static void agentmain(String agentArgs, Instrumentation instrumentation) {
System.out.println("agentmain");
instrumentation.addTransformer(new TestTransformer(className, methodName),true);
try {
List<Class> needRetransFormClasses = new LinkedList<>();
Class[] loadedClass = instrumentation.getAllLoadedClasses();//获取所有加载的类
for (Class c : loadedClass) {
//System.out.println(loadedClass[i].getName());
if (c.getName().equals(className)) {
System.out.println("---find!!!---");
Method[] methods = c.getDeclaredMethods();
for(Method method : methods)
{System.out.println(method.getName());}
instrumentation.retransformClasses(c);
}
} } catch (Exception e) { } }
}

TestTransformer.java 替换目标类的函数

import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.Modifier;
public class TestTransformer implements ClassFileTransformer {
//目标类名称, .分隔
private String targetClassName;
//目标类名称, /分隔
private String targetVMClassName;
private String targetMethodName; public TestTransformer(String className,String methodName){
this.targetVMClassName = new String(className).replaceAll("\\.","\\/");
this.targetMethodName = methodName;
this.targetClassName=className;
}
//类加载时会执行该函数,其中参数 classfileBuffer为类原始字节码,返回值为目标字节码,className为/分隔
@Override
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
//判断类名是否为目标类名
if(!className.equals(targetVMClassName)){
System.out.println("not do transform");
return classfileBuffer;
}
try {
System.out.println("do transform");
ClassPool classPool = ClassPool.getDefault();
CtClass cls = classPool.get(this.targetClassName);
System.out.println(cls.getName());
CtMethod ctMethod = cls.getDeclaredMethod(this.targetMethodName);
System.out.println(ctMethod.getName());
ctMethod.insertBefore("{ System.out.println(\"start\"); }");
ctMethod.insertAfter("{ System.out.println(\"end\"); }");
return cls.toBytecode();
} catch (Exception e) { }
return classfileBuffer;
} }

参考链接IDEA + maven 零基础构建 java agent 项目 - 一灰灰Blog - 博客园 (cnblogs.com),将他们打包。

编写测试程序

BaseMain.java

package com.company;

public class BaseMain {

    public int print(int i) {
System.out.println("i: " + i);
return i + 2;
} public void run() {
int i = 1;
while (true) {
i = print(i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} public static void main(String[] args) throws InterruptedException {
BaseMain main = new BaseMain();
main.run();
Thread.sleep(1000 * 60 * 60);
}
}

编写注入程序 attachwithjps.java

import com.sun.tools.attach.AgentInitializationException;
import com.sun.tools.attach.AgentLoadException;
import com.sun.tools.attach.AttachNotSupportedException;
import com.sun.tools.attach.VirtualMachine; import java.io.IOException; public class attachwithjps {
public static void main(String[] args)
throws IOException, AgentLoadException, AgentInitializationException, AttachNotSupportedException {
// attach方法参数为目标应用程序的进程号,命令行使用jps -l可以查看相关jvm的进程号
VirtualMachine vm = VirtualMachine.attach(目标应用程序的进程号);
// 请用你自己的agent绝对地址,替换这个
vm.loadAgent("E:/内存马/java-agent/target/java-agent-1.0-SNAPSHOT-jar-with-dependencies.jar");
vm.detach();
}
}

注入步骤:

  • ​ 运行被测试程序
  • ​ cmd 输入jps -l 查找目标进程号
  • ​ 运行attach程序

运行结果

web应用注入--tomcat

要在tomcat中选择类进行替换实现webshell,需要降低对url的依赖,在tomcat处理请求流程中选择最通用的类。

如internalDoFilter,调用了dofilter,在此之前可以插入代码对request和response作出操作。

具体代码参考rebeyond师傅的

利用“进程注入”实现无文件复活 WebShell - FreeBuf网络安全行业门户

但是,一旦重启tomcat,内存马就会消失,失去目标服务器的权限。要实现服务器重启后,仍能够维持权限,必须要在服务器关闭前将相关代码保存下来,在重启时自动加载。这里rebeyond师傅使用了ShutdownHook技术.

ShutdownHook是JDK提供的一个用来在JVM关掉时清理现场的机制,这个钩子可以在如下场景中被JVM调用:

1.程序正常退出

2.使用System.exit()退出

3.用户使用Ctrl+C触发的中断导致的退出

4.用户注销或者系统关机

5.OutofMemory导致的退出

6.Kill pid命令导致的退出所以ShutdownHook可以很好的保证在tomcat关闭时,我们有机会埋下复活的种子

相关代码

  public static void persist() {
try {
Thread t = new Thread() {
public void run() {
try {
writeFiles("inject.jar",Agent.injectFileBytes);
writeFiles("agent.jar",Agent.agentFileBytes);
startInject();
} catch (Exception e) {
}
}
};
t.setName("shutdown Thread");
Runtime.getRuntime().addShutdownHook(t);
} catch (Throwable t) {
}

JVM关闭前,会先调用writeFiles把inject.jar和agent.jar写到磁盘上,然后调用startInject,startInject通过Runtime.exec启动java -jar inject.jar。

应用:在有能够进行命令执行的情况下,上传agent.jar与需要注入的jar。而后运行agent.jar对其进行注入即可。

JavaAgent型内存马基础的更多相关文章

  1. 6. 站在巨人的肩膀学习Java Filter型内存马

    本文站在巨人的肩膀学习Java Filter型内存马,文章里面的链接以及图片引用于下面文章,参考文章: <Tomcat 内存马学习(一):Filter型> <tomcat无文件内存w ...

  2. 议题解析与复现--《Java内存攻击技术漫谈》(二)无文件落地Agent型内存马

    无文件落地Agent型内存马植入 可行性分析 使用jsp写入或者代码执行漏洞,如反序列化等,不需要上传agent Java 动态调试技术原理及实践 - 美团技术团队 (meituan.com) 首先, ...

  3. Java安全之基于Tomcat的Filter型内存马

    Java安全之基于Tomcat的Filter型内存马 写在前面 现在来说,内存马已经是一种很常见的攻击手法了,基本红队项目中对于入口点都是选择打入内存马.而对于内存马的支持也是五花八门,甚至各大公司都 ...

  4. Java Filter型内存马的学习与实践

    完全参考:https://www.cnblogs.com/nice0e3/p/14622879.html 这篇笔记,来源逗神的指点,让我去了解了内存马,这篇笔记记录的是filter类型的内存马 内存马 ...

  5. 针对spring mvc的controller内存马-学习和实验

    1 基础 实际上java内存马的注入已经有很多方式了,这里在学习中动手研究并写了一款spring mvc应用的内存马.一般来说实现无文件落地的java内存马注入,通常是利用反序列化漏洞,所以动手写了一 ...

  6. 简单学习java内存马

    看了雷石的内存马深入浅出,就心血来潮看了看,由于本人java贼菜就不介绍原理了,本文有关知识都贴链接吧 前置知识 本次主要看的是tomcat的内存马,所以前置知识有下列 1.tomcat结构,tomc ...

  7. tomcat内存马原理解析及实现

    内存马 简介 ​ Webshell内存马,是在内存中写入恶意后门和木马并执行,达到远程控制Web服务器的一类内存马,其瞄准了企业的对外窗口:网站.应用.但传统的Webshell都是基于文件类型的,黑客 ...

  8. 利用shiro反序列化注入冰蝎内存马

    利用shiro反序列化注入冰蝎内存马 文章首发先知社区:https://xz.aliyun.com/t/10696 一.shiro反序列化注入内存马 1)tomcat filter内存马 先来看一个普 ...

  9. Java安全之Spring内存马

    Java安全之Spring内存马 基础知识 Bean bean 是 Spring 框架的一个核心概念,它是构成应用程序的主干,并且是由 Spring IoC 容器负责实例化.配置.组装和管理的对象. ...

随机推荐

  1. Linux处理二进制文件工具

    处理目标文件的工具 在Linux系统中有大量可用的工具可以帮助我们理解和处理目标文件.特别地,GNU binutils包尤其有帮助,而且可以运行在每一个Linux平台上 序号 命令 说明 1 AR 创 ...

  2. linux清空文件

    https://www.cnblogs.com/mrwang1101/p/6166326.html

  3. 基于Ubuntu18.04一站式部署(python-mysql-redis-nginx)

    基于Ubuntu18.04一站式部署 Python3.6.8的安装 1. 安装依赖 ~$ sudo apt install openssl* zlib* 2. 安装python3.6.8(个人建议从官 ...

  4. JDK 1.7 正式发布,Oracle 官宣免费提供!“新版任你发,我用JDK 8”或成历史?

    Oracle公司JDK 17正式发布,JDK 17属于长期支持(LTS)版本,也就是获得8年的技术支持,自2021年9月至2029年9月截止. JDK 17版本更新了很多比较实用的新特性,关于此版本的 ...

  5. Pikachu靶场通关之XSS(跨站脚本)

    一.XSS(跨站脚本)概述 Cross-Site Scripting 简称为"CSS",为避免与前端叠成样式表的缩写"CSS"冲突,故又称XSS.一般XSS可以 ...

  6. 【第五篇】-Maven 构建配置文件之Spring Cloud直播商城 b2b2c电子商务技术总结

    Maven 构建配置文件 构建配置文件是一系列的配置项的值,可以用来设置或者覆盖 Maven 构建默认值. 使用构建配置文件,你可以为不同的环境,比如说生产环境(Production)和开发(Deve ...

  7. 解决wampserver无法启动问题

    如果无法启动,找不到原因.直接依次点击打开到:控制面板--管理工具--事件查看器--windows日志--应用程序,查看对应进程错误信息对症下药即可. 我这个错误就是8099端口错误,运行cmd命令, ...

  8. DISCUZ论坛添加页头及页尾背景图片的几种方法

    先给大家分享页头添加背景图片的两种方法:1. 第一种效果,是给discuz的整体框架添加背景图片,见图示: 添加方法如下:找到你现在使用模板common文件下的header.html文件,在<h ...

  9. Shell系列(31)- 双分支if语句简介

    双分支if条件语句 if [ 条件判断式 ] then 条件成立,执行的程序 else 条件不成立,执行的程序 fi 需求 根据用户输入的目录名,判断是否存在 脚本: #!/bin/bash #使用r ...

  10. serialVersionUID序列化版本号与ObjectOutputStream对象输入输出流

    1. 观察ObjectOutputStream 我们观察ObjectOutputStream就可以发现该类没有无参构造,只有有参构造,所以他是一个包装流 2. 具体使用: public static ...