前言 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 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反序列化中常用反射 反射Runtime执行本地命令代码片段:System.out.println(org.apache.commons.io.IOUtils.toString(Runtime.getRuntime().exec("whoami").getInputStream(), "UTF-8"));
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 46 47 48 49 50 51 Class runtimeClass1 = Class.forName("java.lang.Runtime" ); Constructor constructor = runtimeClass1.getDeclaredConstructor(); constructor.setAccessible(true ); Object runtimeInstance = constructor.newInstance(); Method runtimeMethod = runtimeClass1.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" )); 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,"Arsene.Tang" ); } } Field modifiers = field.getClass().getDeclaredField("modifiers" ); modifiers.setAccessible(true ); modifiers.setInt(field, field.getModifiers() & ~Modifier.FINAL); field.set(类实例对象, 修改后的值);
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(); }