前言 Java是一种语言,JDK是Java这门语言的开发工具包,JDK= JRE + java 需要注意的是,Java 的版本号命名规则在 JDK 9 之后发生了变化。在 JDK 9 之前,版本号采用 “1.x.x” 的格式(如 1.8.0),而从 JDK 9 开始,版本号不再带有前缀的 “1”,而是直接使用主版本号作为前缀(如 9.0.4)。
jar包操作 1 2 jar -xf 你的jar包名称 jar cf D:\jar包\temp\temp.jar .
Java本地命令执行 Runtime对象可以调用exec()方法执行命令,同时他也是java命令执行编写最简单的方法,属于最外层
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 import java.io.BufferedReader;import java.io.InputStreamReader;public class JavaVersionExample { public static void main (String[] args) { try { String[] command = { "net" , "user" }; Process process = Runtime.getRuntime().exec(command); BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); String line; while ((line = reader.readLine()) != null ) { System.out.println(line); } process.waitFor(); } catch (Exception e) { e.printStackTrace(); } } }
底层分析 Runtime 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class Runtime { private static Runtime currentRuntime = new Runtime(); private Runtime () {} public static Runtime getRuntime () { return currentRuntime; } public Process exec (String cmdarray[]) throws IOException { return exec(cmdarray, null , null ); } ...... public Process exec (String[] cmdarray, String[] envp, File dir) throws IOException { return new ProcessBuilder(cmdarray) .environment(envp) .directory(dir) .start(); }
ProcessBuilder 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public Process start () throws IOException { String[] cmdarray = command.toArray(new String[command.size()]); cmdarray = cmdarray.clone(); for (String arg : cmdarray) if (arg == null ) throw new NullPointerException(); String prog = cmdarray[0 ]; SecurityManager security = System.getSecurityManager(); if (security != null ) security.checkExec(prog); String dir = directory == null ? null : directory.toString(); for (int i = 1 ; i < cmdarray.length; i++) { if (cmdarray[i].indexOf('\u0000' ) >= 0 ) { throw new IOException("invalid null character in command" ); } } try { return ProcessImpl.start(cmdarray, environment, dir, redirects, redirectErrorStream); }
ProcessImpl 1 2 3 4 5 6 7 8 9 10 11 12 13 final class ProcessImpl extends Process { static Process start (String cmdarray[], java.util.Map<String,String> environment, String dir, ProcessBuilder.Redirect[] redirects, boolean redirectErrorStream) throws IOException {................. return new ProcessImpl(cmdarray, envblock, dir, stdHandles, redirectErrorStream);
java 反射 Java反射操作是类 ,类不管实现的实例是什么,都属于java.lang.Class对象 Java反射就是说,对于任意的一个类,我们都可以通过反射获取这个类中所有的属性和方法;从而调用实例的方法和修改实例的属性,Java反射机制是Java语言的动态性的重要体现,也是Java的各种框架底层实现的灵魂。注意反射是通过类修改实例
获取类,获取实例 在反射中 get…(..)就是返回调用单个public getDeclared…(..) 返回调用任意可见性(调用顺序1.private/2.protected/3.package/4.public) get….s() 返回所有
调用private/protected需要setAccessible(true)
1 2 3 4 5 6 7 8 9 Class runtimeClass1 = Runtime.class; Class runtimeClass1 = Runtime.getClass(); Class runtimeClass1 = Class.forName("java.lang.Runtime" ); Constructor constructor = runtimeClass1.getDeclaredConstructor(); constructor.setAccessible(true ); Object runtimeInstance = constructor.newInstance();
获取方法,调用方法 1 2 3 4 5 6 7 8 Method runtimeMethod = Runtime.class.getMethod("exec" , String.class); Process process = (Process) runtimeMethod.invoke(runtimeInstance, cmd); InputStream in = process.getInputStream(); System.out.println(org.apache.commons.io.IOUtils.toString(in, "UTF-8" ));
获取值,修改值 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public class test { public static void main (String[] args) throws Exception { Class<?> stu = Class.forName("com.testforclass.Student" ); Constructor<?> con2 = stu.getConstructor(String.class,int .class,String.class); Object obj = con2.newInstance("aaa" ,18 ,"中国" ); System.out.println(obj); Field field = stu.getDeclaredField("name" ); field.setAccessible(true ); String value = field.get(obj); field.set(obj,"mayylu" ); } }
修改被final关键字修饰的成员变量 1 2 3 4 5 Field modifiers = stu.getClass().getDeclaredField("modifiers" ); modifiers.setAccessible(true ); modifiers.setInt(stu, stu.getModifiers() & ~Modifier.FINAL);
Javassist的使用 Javassist 的底层是 字节码操作机制。相比ASM,Javassist提供了更加简单便捷的API 它可以读取或创造字节码,解析字节码(自己解析而非使用JVM生成类),使用接口操作修改类的内容,最后调用底层的 ClassLoader.defineClass();把修改后的字节码注册到 JVM,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 CtField[] ctFields = ctClass.getDeclaredFields(); CtMethod[] ctMethods = ctClass.getDeclaredMethods(); System.out.println( "解析类名:" + ctClass.getName() + ",父类:" + ctClass.getSuperclass().getName() + ",实现接口:" + Arrays.toString(ctClass.getInterfaces()) ); ClassPool classPool=ClassPool.getDefault(); CtClass ctClass=classPool.makeClass("a" ); pool.appendClassPath("E:\\lib\\extra.jar" ); CtClass superClass = pool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet" ); ctClass.setSuperclass(superClass); CtConstructor constructor = CtNewConstructor.make("public A(){Runtime.getRuntime().exec(\"" + cmd + "\");\n}" , ctClass); ctClass.addConstructor(constructor); ctClass.makeClassInitializer().setBody("Runtime.getRuntime().exec(\"" +cmd+"\");" ); byte [] bytes=ctClass.toBytecode(); Object a = ctClass.toClass().newInstance(); ctClass.defrost();
Java内省机制 内省(Introspection) 通常是指使用反射机制 来检测一个对象的属性和方法。内省主要用于JavaBeans ,它允许开发者在运行时检查一个JavaBean的属性和方法。在Java中,内省机制通常与设计模式,如MVC,结合使用,以便动态获取对象信息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import java.beans.Introspector;import java.beans.PropertyDescriptor;public class User { private String name; public String getName () { return name; } public void setName (String name) { this .name = name; } }
内省提供了操作 JavaBean 的 API。 Introspector 类:这是Java内省机制的核心类,提供了获取BeanInfo的静态方法。 BeanInfo 类:该类包含了关于Java Bean的元数据信息,如属性、事件和方法等。 PropertyDescriptor 类:该类描述了Java Bean的一个属性,包括其getter和setter方法。
1 2 3 4 5 6 7 BeanInfo info= Introspector.getBeanInfo(User.class); BeanDescriptor beanDescriptor = beanInfo.getBeanDescriptor(); PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors(); MethodDescriptor[] methodDescriptors = beanInfo.getMethodDescriptors();
Java 动态代理 Java 动态代理是一种在运行时动态生成代理对象的机制。通过使用 Java 提供的相关 API,可以在不修改原始类的情况下 创建一个代理类,该代理类可以拦截原始类的方法调用 ,并在方法执行前后插入额外的逻辑 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;public interface flag { void getflag (String flag) ; } public interface flag1 { void getflag1 () ; } public class realproxy implements InvocationHandler { @Override public Object invoke (Object proxy, Method method, Object[] args) throws Throwable { if (method.toString().contains("java.lang.String" )){ System.out.println("方法名为: " +method); System.out.println("参数为: " +args[0 ]); Object result = method.invoke(proxy, args); System.out.println("函数调用后" ); } else { System.out.println("方法名为: " +method); System.out.println("没有传入参数" ); } return null ; } } public class test { public static void main (String[] args) { InvocationHandler handler = new realproxy(); flag flag = (flag) Proxy.newProxyInstance(test.class.getClassLoader(), new Class[]{flag.class}, handler); flag1 flag1 = (flag1) Proxy.newProxyInstance(test.class.getClassLoader(),new Class[]{flag1.class},handler); flag.getflag("flag" ); flag1.getflag1(); } }
设计模式 因为java是大二的时候学的,所以在写这篇博客时没有关注太基础的内容,由于后面代码审计过程经常遇到了这种设计思路,就详细写写
java 工厂类与实例 使用工厂类(Factory Class)相比传统的直接实例化(直接使用 new 关键字),就是将创建对象时需要初始化数据、调用其他服务的方法封装在一个类中,便于 集中管理和代码修改,实例就是工程类创造的对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 interface Animal { void makeSound () ; } class Dog implements Animal { @Override public void makeSound () { System.out.println("Woof! Woof!" ); } } class Cat implements Animal { @Override public void makeSound () { System.out.println("Meow! Meow!" ); } } class AnimalFactory { public static Animal createAnimal (String type) { if ("dog" .equalsIgnoreCase(type)) { return new Dog(); } else if ("cat" .equalsIgnoreCase(type)) { return new Cat(); } else { return null ; } } } public class Main { public static void main (String[] args) { Animal dog = AnimalFactory.createAnimal("dog" ); dog.makeSound(); Animal cat = AnimalFactory.createAnimal("cat" ); cat.makeSound(); } }
当然也可以将工厂抽象成两层,AbsFactory(抽象工厂)和具体实现的工厂子类dogFactory和catFactory,这种模式叫做抽象工厂模式
Java Agent机制 在 jdk 1.5 之后引入了 java.lang.instrument 包,允许JVM在加载某个class文件之前对其字节码进行修改,同时也支持对已加载的class(类字节码)进行重新加载(Retransform),通过 java.lang.instrument 实现的工具我们称之为 Java Agent ,Java Agent 能够在不影响正常编译的情况下来修改字节码,即动态修改已加载或者未加载的类,包括类的属性、方法
两种加载方式 1 2 3 4 5 6 7 public static void premain (String args, Instrumentation inst) {}public static void agentmain (String args, Instrumentation inst) {}
使用jar命令打包应用程序 Java Agent限制了我们必须以jar包的形式运行或加载
1 2 3 4 5 6 7 8 9 10 11 Manifest-Version: 1.0 Main-Class: Hello Premain-Class: com.anbai.sec.agent.CrackLicenseAgent Agent-Class: com.anbai.sec.agent.CrackLicenseAgent Can-Retransform-Classes: true
然后就可以利用 编写的mf文件 编译jar包了
jar cvfm hello.jar hello.mf Hello.class
动态修改字节码 Instrumentation 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public interface Instrumentation { void addTransformer (ClassFileTransformer transformer) ; boolean removeTransformer (ClassFileTransformer transformer) ; void retransformClasses (Class<?>... classes) throws UnmodifiableClassException ; boolean isModifiableClass (Class<?> theClass) ; @SuppressWarnings("rawtypes") Class[] getAllLoadedClasses(); boolean isRetransformClassesSupported () ; }
InstrumentationImpl 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 private final TransformerManager mTransformerManager = new TransformerManager(false );..... public void addTransformer (ClassFileTransformer var1) { this .addTransformer(var1, false ); } public synchronized void addTransformer (ClassFileTransformer var1, boolean var2) { if (var1 == null ) { throw new NullPointerException("null passed as 'transformer' in addTransformer" ); } else { if (var2) { if (!this .isRetransformClassesSupported()) { throw new UnsupportedOperationException("adding retransformable transformers is not supported in this environment" ); } if (this .mRetransfomableTransformerManager == null ) { this .mRetransfomableTransformerManager = new TransformerManager(true ); } this .mRetransfomableTransformerManager.addTransformer(var1); if (this .mRetransfomableTransformerManager.getTransformerCount() == 1 ) { this .setHasRetransformableTransformers(this .mNativeAgent, true ); } } else { this .mTransformerManager.addTransformer(var1); } } }
当有新的类被JVM加载时JVM会自动回调用我们自定义的Transformer类的transform方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public interface ClassFileTransformer { byte [] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte [] classfileBuffer); }
启动后加载 agent 要上传的jar包内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 import java.lang.instrument.Instrumentation;public class AgentMain { public static final String ClassName = "org.apache.catalina.core.ApplicationFilterChain" ; public static void agentmain (String agentArgs, Instrumentation ins) { ins.addTransformer(new DefineTransformer(),true ); Class[] classes = ins.getAllLoadedClasses(); for (Class clas:classes){ if (clas.getName().equals(ClassName)){ try { ins.retransformClasses(new Class[]{clas}); } catch (Exception e){ e.printStackTrace(); } } } } } public class DefineTransformer implements ClassFileTransformer { }
命令执行内容
1 2 3 4 5 6 7 8 9 10 11 String path = "AgentMain.jar的路径" ; List<VirtualMachineDescriptor> list = VirtualMachine.list(); for (VirtualMachineDescriptor v:list){ System.out.println(v.displayName()); if (v.displayName().contains("AgentMainDemo" )){ VirtualMachine vm = VirtualMachine.attach(v.id()); vm.loadAgent(path); vm.detach(); }