├── asset ├── noteimage_1.png ├── noteimage_10.png ├── noteimage_2.png ├── noteimage_3.png ├── noteimage_4.png ├── noteimage_5.png ├── noteimage_6.png ├── noteimage_7.png ├── noteimage_8.png └── noteimage_9.png ├── src ├── main │ └── java │ │ └── com │ │ └── aftersnows │ │ ├── JVM │ │ ├── DumpClass │ │ │ └── DumpProcessMemory.java │ │ ├── Defense │ │ │ ├── DEFUtils.java │ │ │ ├── DEF.java │ │ │ ├── HTTPServiceDEF.java │ │ │ └── agentDEF.java │ │ ├── JVMTest.java │ │ └── HotSpot.java │ │ ├── Agent │ │ └── socketFilter.java │ │ ├── filter │ │ ├── valuesFilter.java │ │ ├── servletsFilter.java │ │ ├── listenersFilter.java │ │ ├── filtersFilter.java │ │ ├── WebsocketsFilter.java │ │ └── TimerFilter.java │ │ ├── visitor │ │ ├── TimerKillVisitor.java │ │ ├── ValueKillVisitor.java │ │ ├── WebsocketKillVisitor.java │ │ ├── ServletKillVisitor.java │ │ ├── ListenerKillVisitor.java │ │ └── FilterKillVisitor.java │ │ ├── transform │ │ ├── ClassDumpTransformer.java │ │ ├── KillFilter.java │ │ ├── KillWebsocket.java │ │ ├── KillValue.java │ │ ├── KillListener.java │ │ ├── KillServlet.java │ │ ├── KillTimer.java │ │ └── socketVim.java │ │ ├── utils │ │ ├── utils.java │ │ ├── JarFileHelper.java │ │ ├── VirtualMachineClassLoader.java │ │ └── ModuleLoader.java │ │ ├── FindEvilAgent.java │ │ └── check │ │ └── CheckUtils.java └── test │ └── java │ ├── test.java │ └── FindEvilAgent.java ├── pom.xml └── README.md /asset/noteimage_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AfterSnows/ApricusFindEvil/HEAD/asset/noteimage_1.png -------------------------------------------------------------------------------- /asset/noteimage_10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AfterSnows/ApricusFindEvil/HEAD/asset/noteimage_10.png -------------------------------------------------------------------------------- /asset/noteimage_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AfterSnows/ApricusFindEvil/HEAD/asset/noteimage_2.png -------------------------------------------------------------------------------- /asset/noteimage_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AfterSnows/ApricusFindEvil/HEAD/asset/noteimage_3.png -------------------------------------------------------------------------------- /asset/noteimage_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AfterSnows/ApricusFindEvil/HEAD/asset/noteimage_4.png -------------------------------------------------------------------------------- /asset/noteimage_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AfterSnows/ApricusFindEvil/HEAD/asset/noteimage_5.png -------------------------------------------------------------------------------- /asset/noteimage_6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AfterSnows/ApricusFindEvil/HEAD/asset/noteimage_6.png -------------------------------------------------------------------------------- /asset/noteimage_7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AfterSnows/ApricusFindEvil/HEAD/asset/noteimage_7.png -------------------------------------------------------------------------------- /asset/noteimage_8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AfterSnows/ApricusFindEvil/HEAD/asset/noteimage_8.png -------------------------------------------------------------------------------- /asset/noteimage_9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AfterSnows/ApricusFindEvil/HEAD/asset/noteimage_9.png -------------------------------------------------------------------------------- /src/main/java/com/aftersnows/JVM/DumpClass/DumpProcessMemory.java: -------------------------------------------------------------------------------- 1 | package com.aftersnows.JVM.DumpClass; 2 | 3 | public class DumpProcessMemory { 4 | public String TargetClass; 5 | public DumpProcessMemory() { 6 | 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/com/aftersnows/JVM/Defense/DEFUtils.java: -------------------------------------------------------------------------------- 1 | package com.aftersnows.JVM.Defense; 2 | 3 | import java.io.IOException; 4 | 5 | public class DEFUtils { 6 | public static String getProperty(String key) { 7 | return "DEF VALID"; 8 | } 9 | 10 | public static void attach(int i) { 11 | System.out.println("Windbg HOOK"); 12 | } 13 | 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/aftersnows/JVM/JVMTest.java: -------------------------------------------------------------------------------- 1 | package com.aftersnows.JVM; 2 | 3 | 4 | import java.lang.reflect.Field; 5 | import java.util.ArrayList; 6 | import java.util.HashSet; 7 | import java.util.List; 8 | 9 | public class JVMTest { 10 | public void shit(){ 11 | System.out.println("shit"); 12 | } 13 | 14 | 15 | public static void main(String[] args) throws NoSuchMethodException, ClassNotFoundException { 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/aftersnows/Agent/socketFilter.java: -------------------------------------------------------------------------------- 1 | package com.aftersnows.Agent; 2 | 3 | import java.lang.reflect.Field; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | 7 | import static com.aftersnows.check.CheckUtils.*; 8 | import static com.aftersnows.utils.utils.ConvertClassToArray; 9 | 10 | public class socketFilter { 11 | public Class[] socketClasses; 12 | 13 | public socketFilter(Class[] allClasses) { 14 | List> socketClasses = new ArrayList<>(); 15 | for (Class c : allClasses) { 16 | if ("sun.nio.ch.SocketChannelImpl".equals(c.getName())){ 17 | socketClasses.add(c); 18 | if(!ClassFileIsExists(c)||IsBadClassLoader(c)){ 19 | socketClasses.add(c); 20 | } 21 | } 22 | } 23 | this.socketClasses=ConvertClassToArray(socketClasses); 24 | } 25 | } 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/main/java/com/aftersnows/filter/valuesFilter.java: -------------------------------------------------------------------------------- 1 | package com.aftersnows.filter; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import static com.aftersnows.check.CheckUtils.*; 7 | import static com.aftersnows.utils.utils.ConvertClassToArray; 8 | 9 | public class valuesFilter { 10 | public Class[] valuesClasses; 11 | public Class[] MayBValuesClasses; 12 | public valuesFilter(Class[] allClasses){ 13 | List> ValueClassList = new ArrayList<>(); 14 | List> MayBValueClassList = new ArrayList<>(); 15 | for (Class c : allClasses) { 16 | if (IsValve(c)){ 17 | ValueClassList.add(c); 18 | if(!ClassFileIsExists(c)||IsBadClassLoader(c)){ 19 | MayBValueClassList.add(c); 20 | } 21 | } 22 | } 23 | this.valuesClasses =ConvertClassToArray(ValueClassList); 24 | this.MayBValuesClasses =ConvertClassToArray(MayBValueClassList); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/aftersnows/filter/servletsFilter.java: -------------------------------------------------------------------------------- 1 | package com.aftersnows.filter; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import static com.aftersnows.check.CheckUtils.*; 7 | import static com.aftersnows.utils.utils.ConvertClassToArray; 8 | 9 | public class servletsFilter { 10 | public Class[] servletClasses; 11 | public Class[] MayBServletClasses; 12 | public servletsFilter(Class[] allClasses){ 13 | List> ServletClassList = new ArrayList<>(); 14 | List> MayBServletClassList = new ArrayList<>(); 15 | for (Class c : allClasses) { 16 | if (IsServlet(c)){ 17 | ServletClassList.add(c); 18 | if(!ClassFileIsExists(c)||IsBadClassLoader(c)){ 19 | MayBServletClassList.add(c); 20 | } 21 | } 22 | } 23 | this.servletClasses =ConvertClassToArray(ServletClassList); 24 | this.MayBServletClasses =ConvertClassToArray(MayBServletClassList); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/test/java/test.java: -------------------------------------------------------------------------------- 1 | import com.sun.tools.attach.AgentInitializationException; 2 | import com.sun.tools.attach.AgentLoadException; 3 | import com.sun.tools.attach.AttachNotSupportedException; 4 | import com.sun.tools.attach.VirtualMachine; 5 | 6 | import java.io.IOException; 7 | import java.nio.file.Path; 8 | import java.nio.file.Paths; 9 | 10 | 11 | public class test { 12 | public static void main(String[] args) throws IOException, AttachNotSupportedException, AgentLoadException, AgentInitializationException { 13 | System.out.println("running JVM start "); 14 | String pid="19884"; 15 | VirtualMachine vm = VirtualMachine.attach(pid); 16 | Path agentPath = Paths.get("D:\\WorkSpace\\java\\ApricusFindEvil\\target\\ApricusFindEvil-1.0-SNAPSHOT-jar-with-dependencies.jar"); 17 | String path = agentPath.toAbsolutePath().toString(); 18 | vm.loadAgent(path); 19 | vm.detach(); 20 | 21 | 22 | } 23 | } 24 | // curl "http://localhost:2333/cc" --data-binary "D:\java project collection\HardWork\cc11Step2.ser" 25 | -------------------------------------------------------------------------------- /src/main/java/com/aftersnows/filter/listenersFilter.java: -------------------------------------------------------------------------------- 1 | package com.aftersnows.filter; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import static com.aftersnows.check.CheckUtils.*; 7 | import static com.aftersnows.utils.utils.ConvertClassToArray; 8 | 9 | public class listenersFilter { 10 | public Class[] listenerClasses; 11 | public Class[] MayBListenerClasses; 12 | public listenersFilter(Class[] allClasses){ 13 | List> ListenerClassList = new ArrayList<>(); 14 | List> MayBListenerClassList = new ArrayList<>(); 15 | for (Class c : allClasses) { 16 | if (IsListener(c)){ 17 | ListenerClassList.add(c); 18 | if(!ClassFileIsExists(c)||IsBadClassLoader(c)){ 19 | MayBListenerClassList.add(c); 20 | } 21 | } 22 | } 23 | this.listenerClasses =ConvertClassToArray(ListenerClassList); 24 | this.MayBListenerClasses =ConvertClassToArray(MayBListenerClassList); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/aftersnows/visitor/TimerKillVisitor.java: -------------------------------------------------------------------------------- 1 | package com.aftersnows.visitor; 2 | 3 | import org.objectweb.asm.ClassVisitor; 4 | import org.objectweb.asm.MethodVisitor; 5 | 6 | import static org.objectweb.asm.Opcodes.RETURN; 7 | 8 | public class TimerKillVisitor extends ClassVisitor { 9 | public TimerKillVisitor(int i, ClassVisitor classVisitor) { 10 | super(i, classVisitor); 11 | } 12 | @Override 13 | public MethodVisitor visitMethod(int access, String methodName, String descriptor, 14 | String signature, String[] exceptions) { 15 | 16 | // 判断方法的名称是否为doFilter,方法的描述符是否匹配 17 | MethodVisitor methodVisitor = super.visitMethod(access, methodName, descriptor, signature, exceptions); 18 | if (methodVisitor != null && methodName.equals("run")) { 19 | methodVisitor.visitCode(); 20 | methodVisitor.visitInsn(RETURN); 21 | methodVisitor.visitMaxs(2, 2); 22 | methodVisitor.visitEnd(); 23 | } 24 | return methodVisitor; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/aftersnows/filter/filtersFilter.java: -------------------------------------------------------------------------------- 1 | package com.aftersnows.filter; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import static com.aftersnows.check.CheckUtils.*; 7 | import static com.aftersnows.utils.utils.ConvertClassToArray; 8 | 9 | public class filtersFilter { 10 | public Class[] filterClasses;//用于返回jvm运行的所有filter 11 | public Class[] MayBFilterClasses;//用于返回可能是内存马的filter 12 | public filtersFilter(Class[] allClasses){ 13 | List> FilterClassList = new ArrayList<>(); 14 | List> MayBFilterClassList = new ArrayList<>(); 15 | for (Class c : allClasses) { 16 | if (IsFilter(c)){ 17 | FilterClassList.add(c); 18 | if(!ClassFileIsExists(c)||IsBadClassLoader(c)){ 19 | MayBFilterClassList.add(c); 20 | } 21 | } 22 | } 23 | this.filterClasses=ConvertClassToArray(FilterClassList); 24 | this.MayBFilterClasses =ConvertClassToArray(MayBFilterClassList); 25 | } 26 | 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/aftersnows/filter/WebsocketsFilter.java: -------------------------------------------------------------------------------- 1 | package com.aftersnows.filter; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import static com.aftersnows.check.CheckUtils.*; 7 | import static com.aftersnows.utils.utils.ConvertClassToArray; 8 | 9 | public class WebsocketsFilter { 10 | public Class[] websocketClasses;//用于返回jvm运行的所有filter 11 | public Class[] MayBWebsocketClasses;//用于返回可能是内存马的filter 12 | public WebsocketsFilter(Class[] allClasses){ 13 | List> FilterClassList = new ArrayList<>(); 14 | List> MayBFilterClassList = new ArrayList<>(); 15 | for (Class c : allClasses) { 16 | if (IsWebsocket(c)){ 17 | FilterClassList.add(c); 18 | if(!ClassFileIsExists(c)||IsBadClassLoader(c)){ 19 | MayBFilterClassList.add(c); 20 | } 21 | } 22 | } 23 | this.websocketClasses =ConvertClassToArray(FilterClassList); 24 | this.MayBWebsocketClasses =ConvertClassToArray(MayBFilterClassList); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/aftersnows/visitor/ValueKillVisitor.java: -------------------------------------------------------------------------------- 1 | package com.aftersnows.visitor; 2 | 3 | import org.objectweb.asm.ClassVisitor; 4 | import org.objectweb.asm.MethodVisitor; 5 | 6 | import static org.objectweb.asm.Opcodes.RETURN; 7 | 8 | public class ValueKillVisitor extends ClassVisitor { 9 | 10 | public ValueKillVisitor(int api, ClassVisitor classVisitor) { 11 | super(api, classVisitor); 12 | } 13 | @Override 14 | public MethodVisitor visitMethod(int access, String methodName, String descriptor, 15 | String signature, String[] exceptions) { 16 | 17 | // 判断方法的名称是否为doFilter,方法的描述符是否匹配 18 | MethodVisitor methodVisitor = super.visitMethod(access, methodName, descriptor, signature, exceptions); 19 | if (methodVisitor != null && methodName.equals("invoke")) { 20 | methodVisitor.visitCode(); 21 | methodVisitor.visitInsn(RETURN); 22 | methodVisitor.visitMaxs(0, 3); 23 | methodVisitor.visitEnd(); 24 | } 25 | return methodVisitor; 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/aftersnows/visitor/WebsocketKillVisitor.java: -------------------------------------------------------------------------------- 1 | package com.aftersnows.visitor; 2 | 3 | import org.objectweb.asm.ClassVisitor; 4 | import org.objectweb.asm.MethodVisitor; 5 | 6 | import static org.objectweb.asm.Opcodes.RETURN; 7 | 8 | public class WebsocketKillVisitor extends ClassVisitor { 9 | public WebsocketKillVisitor(int api, ClassVisitor classVisitor) { 10 | super(api, classVisitor); 11 | } 12 | @Override 13 | public MethodVisitor visitMethod(int access, String methodName, String descriptor, 14 | String signature, String[] exceptions) { 15 | 16 | // 判断方法的名称是否为doFilter,方法的描述符是否匹配 17 | MethodVisitor methodVisitor = super.visitMethod(access, methodName, descriptor, signature, exceptions); 18 | if (methodVisitor != null && methodName.equals("onMessage")) { 19 | methodVisitor.visitCode(); 20 | methodVisitor.visitInsn(RETURN); 21 | methodVisitor.visitMaxs(0, 3); 22 | methodVisitor.visitEnd(); 23 | } 24 | return methodVisitor; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/aftersnows/visitor/ServletKillVisitor.java: -------------------------------------------------------------------------------- 1 | package com.aftersnows.visitor; 2 | 3 | import org.objectweb.asm.ClassVisitor; 4 | import org.objectweb.asm.MethodVisitor; 5 | 6 | import static org.objectweb.asm.Opcodes.RETURN; 7 | 8 | public class ServletKillVisitor extends ClassVisitor { 9 | 10 | public ServletKillVisitor(int api, ClassVisitor classVisitor) { 11 | super(api, classVisitor); 12 | } 13 | 14 | @Override 15 | public MethodVisitor visitMethod(int access, String methodName, String descriptor, 16 | String signature, String[] exceptions) { 17 | 18 | // 判断方法的名称是否为doFilter,方法的描述符是否匹配 19 | MethodVisitor methodVisitor = super.visitMethod(access, methodName, descriptor, signature, exceptions); 20 | if (methodVisitor != null && methodName.equals("service")) { 21 | methodVisitor.visitCode(); 22 | methodVisitor.visitInsn(RETURN); 23 | methodVisitor.visitMaxs(0, 3); 24 | methodVisitor.visitEnd(); 25 | } 26 | return methodVisitor; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/aftersnows/visitor/ListenerKillVisitor.java: -------------------------------------------------------------------------------- 1 | package com.aftersnows.visitor; 2 | 3 | import org.objectweb.asm.ClassVisitor; 4 | import org.objectweb.asm.MethodVisitor; 5 | import static org.objectweb.asm.Opcodes.RETURN; 6 | 7 | public class ListenerKillVisitor extends ClassVisitor { 8 | 9 | public ListenerKillVisitor(int api, ClassVisitor classVisitor) { 10 | super(api, classVisitor); 11 | } 12 | @Override 13 | public MethodVisitor visitMethod(int access, String methodName, String descriptor, 14 | String signature, String[] exceptions) { 15 | 16 | // 判断方法的名称是否为doFilter,方法的描述符是否匹配 17 | MethodVisitor methodVisitor = super.visitMethod(access, methodName, descriptor, signature, exceptions); 18 | if (methodVisitor != null && methodName.equals("requestDestroyed")||methodName.equals("requestInitialized")) { 19 | methodVisitor.visitCode(); 20 | methodVisitor.visitInsn(RETURN); 21 | methodVisitor.visitMaxs(0, 2); 22 | methodVisitor.visitEnd(); 23 | } 24 | return methodVisitor; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/aftersnows/visitor/FilterKillVisitor.java: -------------------------------------------------------------------------------- 1 | package com.aftersnows.visitor; 2 | 3 | import org.objectweb.asm.ClassVisitor; 4 | import org.objectweb.asm.MethodVisitor; 5 | import org.objectweb.asm.Opcodes; 6 | 7 | import static org.objectweb.asm.Opcodes.*; 8 | 9 | //todo 完成查杀,对于dofilter方法其实还需要进一步看看比如检测或者查杀之类的 10 | public class FilterKillVisitor extends ClassVisitor { 11 | public FilterKillVisitor(int i, ClassVisitor classVisitor) { 12 | super(i, classVisitor); 13 | } 14 | 15 | @Override 16 | public MethodVisitor visitMethod(int access, String methodName, String descriptor, 17 | String signature, String[] exceptions) { 18 | 19 | // 判断方法的名称是否为doFilter,方法的描述符是否匹配 20 | MethodVisitor methodVisitor = super.visitMethod(access, methodName, descriptor, signature, exceptions); 21 | if (methodVisitor != null && methodName.equals("doFilter")) { 22 | methodVisitor.visitCode(); 23 | methodVisitor.visitInsn(RETURN); 24 | methodVisitor.visitMaxs(0, 1); 25 | methodVisitor.visitEnd(); 26 | } 27 | return methodVisitor; 28 | } 29 | } 30 | 31 | 32 | -------------------------------------------------------------------------------- /src/main/java/com/aftersnows/transform/ClassDumpTransformer.java: -------------------------------------------------------------------------------- 1 | package com.aftersnows.transform; 2 | 3 | import java.lang.instrument.ClassFileTransformer; 4 | import java.lang.instrument.IllegalClassFormatException; 5 | import java.security.ProtectionDomain; 6 | import java.util.Base64; 7 | 8 | import static com.aftersnows.utils.utils.CanonicalClassNameToPoint; 9 | 10 | 11 | public class ClassDumpTransformer implements ClassFileTransformer { 12 | public String Base64ClassByte; 13 | public String HexClassByte; 14 | private final String TargetClass; 15 | 16 | public ClassDumpTransformer(String targetClass) { 17 | TargetClass = targetClass; 18 | } 19 | 20 | 21 | @Override 22 | public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { 23 | className = CanonicalClassNameToPoint(className); 24 | if(className.equals(TargetClass)){ 25 | System.out.println("Dumping class: " + className); 26 | this.Base64ClassByte=Base64.getEncoder().encodeToString(classfileBuffer); 27 | System.out.println(Base64ClassByte); 28 | } 29 | return classfileBuffer; 30 | } 31 | 32 | 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/aftersnows/transform/KillFilter.java: -------------------------------------------------------------------------------- 1 | package com.aftersnows.transform; 2 | 3 | import com.aftersnows.visitor.FilterKillVisitor; 4 | import org.objectweb.asm.*; 5 | 6 | import java.io.IOException; 7 | import java.lang.instrument.ClassFileTransformer; 8 | import java.security.ProtectionDomain; 9 | 10 | import static com.aftersnows.utils.utils.*; 11 | 12 | public class KillFilter implements ClassFileTransformer { 13 | private final String TargetClass; 14 | public byte[] data; 15 | 16 | public KillFilter(String targetClass) { 17 | TargetClass = targetClass; 18 | } 19 | 20 | @Override 21 | public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) { 22 | className = CanonicalClassNameToPoint(className); 23 | if(className.equals(TargetClass)){ 24 | System.out.println("scalpel on Target: " + className); 25 | ClassReader cr = new ClassReader(classfileBuffer); 26 | ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); 27 | ClassVisitor cv = new FilterKillVisitor(Opcodes.ASM9, cw); 28 | int parsingOptions = ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES; 29 | cr.accept(cv, parsingOptions); 30 | return cw.toByteArray(); 31 | 32 | } 33 | return new byte[0]; 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/aftersnows/JVM/Defense/DEF.java: -------------------------------------------------------------------------------- 1 | package com.aftersnows.JVM.Defense; 2 | 3 | import com.aftersnows.JVM.HotSpot; 4 | import sun.misc.Unsafe; 5 | 6 | import java.lang.reflect.Modifier; 7 | 8 | 9 | public class DEF { 10 | public String LockClassName ; 11 | public String MethodName; 12 | public DEF() {} 13 | public DEF(String lockClassName,String methodName){ 14 | System.out.println("开始进行对HttpServlet service的操作"); 15 | this.LockClassName=lockClassName; 16 | this.MethodName=methodName; 17 | try { 18 | HotSpot hotSpot = new HotSpot(); 19 | Unsafe unsafe = hotSpot.unsafe; 20 | Class LockClass = Class.forName(LockClassName); 21 | long LockClassHandle= HotSpot.getKlass(LockClass); 22 | long LockClassMethodHandle = HotSpot.getMethodByKlass(LockClassHandle,MethodName,HotSpot.getMethodSignature(LockClass.getMethod(MethodName))); 23 | long _access_flagsFields=HotSpot.getFieldOffset("Method","_access_flags"); 24 | int newMethodAccessFlags = unsafe.getInt(LockClassMethodHandle+_access_flagsFields); 25 | newMethodAccessFlags =newMethodAccessFlags | Modifier.FINAL; 26 | unsafe.putInt((LockClassMethodHandle+_access_flagsFields),newMethodAccessFlags); 27 | } catch (ClassNotFoundException | NoSuchMethodException e) { 28 | e.printStackTrace(); 29 | } 30 | 31 | } 32 | } 33 | 34 | -------------------------------------------------------------------------------- /src/main/java/com/aftersnows/JVM/Defense/HTTPServiceDEF.java: -------------------------------------------------------------------------------- 1 | package com.aftersnows.JVM.Defense; 2 | 3 | 4 | import com.aftersnows.JVM.HotSpot; 5 | import sun.misc.Unsafe; 6 | 7 | import java.lang.reflect.Modifier; 8 | 9 | 10 | public class HTTPServiceDEF { 11 | public HotSpot hotSpot = new HotSpot(); 12 | public Unsafe unsafe = hotSpot.unsafe; 13 | public String LockClassName ="javax.servlet.http.HttpServlet"; 14 | public HTTPServiceDEF() {} 15 | public HTTPServiceDEF(boolean IfDEF){ 16 | System.out.println("开始进行对HttpServlet service的操作"); 17 | try { 18 | Class LockClass = Class.forName(LockClassName); 19 | long LockClassHandle= HotSpot.getKlass(LockClass); 20 | long LockClassMethodHandle = HotSpot.getMethodByKlass(LockClassHandle,"service",HotSpot.getMethodSignature(LockClass.getMethod("service",Class.forName("javax.servlet.ServletRequest"),Class.forName("javax.servlet.ServletResponse")))); 21 | long _access_flagsFields=HotSpot.getFieldOffset("Method","_access_flags"); 22 | int newMethodAccessFlags = unsafe.getInt(LockClassMethodHandle+_access_flagsFields); 23 | newMethodAccessFlags =newMethodAccessFlags | Modifier.FINAL; 24 | unsafe.putInt((LockClassMethodHandle+_access_flagsFields),newMethodAccessFlags); 25 | } catch (ClassNotFoundException | NoSuchMethodException e) { 26 | e.printStackTrace(); 27 | } 28 | 29 | } 30 | 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/aftersnows/transform/KillWebsocket.java: -------------------------------------------------------------------------------- 1 | package com.aftersnows.transform; 2 | 3 | import com.aftersnows.visitor.WebsocketKillVisitor; 4 | import org.objectweb.asm.ClassReader; 5 | import org.objectweb.asm.ClassVisitor; 6 | import org.objectweb.asm.ClassWriter; 7 | import org.objectweb.asm.Opcodes; 8 | 9 | import java.lang.instrument.ClassFileTransformer; 10 | import java.security.ProtectionDomain; 11 | 12 | import static com.aftersnows.utils.utils.CanonicalClassNameToPoint; 13 | 14 | public class KillWebsocket implements ClassFileTransformer { 15 | private final String TargetClass; 16 | public byte[] data; 17 | public KillWebsocket(String targetClass) { 18 | TargetClass = targetClass; 19 | } 20 | 21 | @Override 22 | public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) { 23 | className = CanonicalClassNameToPoint(className); 24 | if(className.equals(TargetClass)){ 25 | System.out.println("scalpel on Target: " + className); 26 | ClassReader cr = new ClassReader(classfileBuffer); 27 | ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); 28 | ClassVisitor cv = new WebsocketKillVisitor(Opcodes.ASM9, cw); 29 | int parsingOptions = ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES; 30 | cr.accept(cv, parsingOptions); 31 | return cw.toByteArray(); 32 | 33 | } 34 | return new byte[0]; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/aftersnows/transform/KillValue.java: -------------------------------------------------------------------------------- 1 | package com.aftersnows.transform; 2 | 3 | import com.aftersnows.visitor.FilterKillVisitor; 4 | import com.aftersnows.visitor.ValueKillVisitor; 5 | import org.objectweb.asm.ClassReader; 6 | import org.objectweb.asm.ClassVisitor; 7 | import org.objectweb.asm.ClassWriter; 8 | import org.objectweb.asm.Opcodes; 9 | 10 | import java.lang.instrument.ClassFileTransformer; 11 | import java.security.ProtectionDomain; 12 | 13 | import static com.aftersnows.utils.utils.CanonicalClassNameToPoint; 14 | 15 | public class KillValue implements ClassFileTransformer { 16 | private final String TargetClass; 17 | public byte[] data; 18 | public KillValue(String targetClass) { 19 | TargetClass = targetClass; 20 | } 21 | 22 | @Override 23 | public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) { 24 | className = CanonicalClassNameToPoint(className); 25 | if(className.equals(TargetClass)){ 26 | System.out.println("scalpel on Target: " + className); 27 | ClassReader cr = new ClassReader(classfileBuffer); 28 | ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); 29 | ClassVisitor cv = new ValueKillVisitor(Opcodes.ASM9, cw); 30 | int parsingOptions = ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES; 31 | cr.accept(cv, parsingOptions); 32 | return cw.toByteArray(); 33 | 34 | } 35 | return new byte[0]; 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/aftersnows/transform/KillListener.java: -------------------------------------------------------------------------------- 1 | package com.aftersnows.transform; 2 | 3 | import com.aftersnows.visitor.ListenerKillVisitor; 4 | import com.aftersnows.visitor.ValueKillVisitor; 5 | import org.objectweb.asm.ClassReader; 6 | import org.objectweb.asm.ClassVisitor; 7 | import org.objectweb.asm.ClassWriter; 8 | import org.objectweb.asm.Opcodes; 9 | 10 | import java.lang.instrument.ClassFileTransformer; 11 | import java.lang.instrument.IllegalClassFormatException; 12 | import java.security.ProtectionDomain; 13 | 14 | import static com.aftersnows.utils.utils.CanonicalClassNameToPoint; 15 | 16 | public class KillListener implements ClassFileTransformer { 17 | private final String TargetClass; 18 | public byte[] data; 19 | public KillListener(String targetClass) { 20 | TargetClass = targetClass; 21 | } 22 | 23 | @Override 24 | public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) { 25 | className = CanonicalClassNameToPoint(className); 26 | if(className.equals(TargetClass)){ 27 | System.out.println("scalpel on Target: " + className); 28 | ClassReader cr = new ClassReader(classfileBuffer); 29 | ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); 30 | ClassVisitor cv = new ListenerKillVisitor(Opcodes.ASM9, cw); 31 | int parsingOptions = ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES; 32 | cr.accept(cv, parsingOptions); 33 | return cw.toByteArray(); 34 | 35 | } 36 | return new byte[0]; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/aftersnows/transform/KillServlet.java: -------------------------------------------------------------------------------- 1 | package com.aftersnows.transform; 2 | 3 | import com.aftersnows.visitor.ListenerKillVisitor; 4 | import com.aftersnows.visitor.ServletKillVisitor; 5 | import org.objectweb.asm.ClassReader; 6 | import org.objectweb.asm.ClassVisitor; 7 | import org.objectweb.asm.ClassWriter; 8 | import org.objectweb.asm.Opcodes; 9 | 10 | import java.lang.instrument.ClassFileTransformer; 11 | import java.lang.instrument.IllegalClassFormatException; 12 | import java.security.ProtectionDomain; 13 | 14 | import static com.aftersnows.utils.utils.CanonicalClassNameToPoint; 15 | 16 | public class KillServlet implements ClassFileTransformer { 17 | private final String TargetClass; 18 | public byte[] data; 19 | public KillServlet(String targetClass) { 20 | TargetClass = targetClass; 21 | } 22 | 23 | @Override 24 | public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) { 25 | className = CanonicalClassNameToPoint(className); 26 | if(className.equals(TargetClass)){ 27 | System.out.println("scalpel on Target: " + className); 28 | ClassReader cr = new ClassReader(classfileBuffer); 29 | ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); 30 | ClassVisitor cv = new ServletKillVisitor(Opcodes.ASM9, cw); 31 | int parsingOptions = ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES; 32 | cr.accept(cv, parsingOptions); 33 | return cw.toByteArray(); 34 | 35 | } 36 | return new byte[0]; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/aftersnows/filter/TimerFilter.java: -------------------------------------------------------------------------------- 1 | package com.aftersnows.filter; 2 | 3 | import java.lang.reflect.Field; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | 7 | import static com.aftersnows.check.CheckUtils.ClassFileIsExists; 8 | import static com.aftersnows.check.CheckUtils.IsBadClassLoader; 9 | import static com.aftersnows.utils.utils.*; 10 | 11 | 12 | public class TimerFilter { 13 | public Class[] MayBTimerClasses; 14 | public Thread[] EvilTread; 15 | public TimerFilter() throws Exception { 16 | List> MayTimerClasses = new ArrayList<>(); 17 | List MayTimerThread = new ArrayList<>(); 18 | 19 | Thread[] threads = (Thread[]) ((Thread[]) getField(getSystemThreadGroup(), "threads")); 20 | Object target; 21 | for (Thread nowThread : threads) { 22 | if(nowThread==null){ 23 | continue; 24 | } 25 | target = getField(nowThread, "target"); 26 | if (target == null) { 27 | continue; 28 | } else { 29 | //确认thread的runnable是否有问题,直接给它封掉 30 | if (!ClassFileIsExists(target.getClass()) || IsBadClassLoader(target.getClass())) { 31 | String nowThreadName=nowThread.getName(); 32 | System.out.println("存在恶意线程: "+nowThreadName); 33 | MayTimerClasses.add(target.getClass()); 34 | MayTimerThread.add(nowThread); 35 | nowThread.stop(); 36 | System.out.println("已停止恶意线程: "+nowThreadName+",需要进一步确认生成此线程的内存马"); 37 | } 38 | } 39 | } 40 | this.MayBTimerClasses =ConvertClassToArray(MayTimerClasses); 41 | this.EvilTread=ConvertThreadToArray(MayTimerThread); 42 | 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/aftersnows/transform/KillTimer.java: -------------------------------------------------------------------------------- 1 | package com.aftersnows.transform; 2 | 3 | import com.aftersnows.visitor.FilterKillVisitor; 4 | import com.aftersnows.visitor.TimerKillVisitor; 5 | import org.objectweb.asm.ClassReader; 6 | import org.objectweb.asm.ClassVisitor; 7 | import org.objectweb.asm.ClassWriter; 8 | import org.objectweb.asm.Opcodes; 9 | 10 | import java.lang.instrument.ClassFileTransformer; 11 | import java.lang.instrument.IllegalClassFormatException; 12 | import java.lang.reflect.Field; 13 | import java.security.ProtectionDomain; 14 | 15 | import static com.aftersnows.utils.utils.CanonicalClassNameToPoint; 16 | 17 | public class KillTimer implements ClassFileTransformer{ 18 | private final String TargetClass; 19 | public KillTimer(String targetClass) { 20 | TargetClass = targetClass; 21 | } 22 | @Override 23 | public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { 24 | className = CanonicalClassNameToPoint(className); 25 | if(className.equals(TargetClass)){ 26 | System.out.println("scalpel on Target: " + className); 27 | ClassReader cr = new ClassReader(classfileBuffer); 28 | ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); 29 | ClassVisitor cv = new TimerKillVisitor(Opcodes.ASM9, cw); 30 | int parsingOptions = ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES; 31 | cr.accept(cv, parsingOptions); 32 | return cw.toByteArray(); 33 | } 34 | return new byte[0]; 35 | } 36 | public static void cancelGCDaemon(Thread gcDaemonThread) { 37 | try { 38 | // 使用反射获取 Thread 类的 daemon 字段 39 | Field daemonField = Thread.class.getDeclaredField("daemon"); 40 | daemonField.setAccessible(true); 41 | 42 | // 反射取消 GC Daemon 2 线程的守护进程属性 43 | daemonField.setBoolean(gcDaemonThread, false); 44 | } catch (Exception e) { 45 | e.printStackTrace(); 46 | } 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/aftersnows/utils/utils.java: -------------------------------------------------------------------------------- 1 | package com.aftersnows.utils; 2 | 3 | import java.lang.reflect.Field; 4 | import java.util.List; 5 | 6 | public class utils { 7 | //将点换成斜杠 8 | public static String CanonicalClassNameToBackslash(String className){ 9 | return className.replace(".", "/"); 10 | } 11 | public static String CanonicalClassNameToPoint(String className){ 12 | return className.replace("/", "."); 13 | } 14 | public static Class[] ConvertClassToArray(List> classList) { 15 | Class[] array = new Class[classList.size()]; 16 | classList.toArray(array); 17 | return array; 18 | } 19 | public static Object getField(Object object, String fieldName) throws Exception { 20 | Field field = null; 21 | Class clazz = object.getClass(); 22 | 23 | while (clazz != Object.class) { 24 | try { 25 | field = clazz.getDeclaredField(fieldName); 26 | break; 27 | } catch (NoSuchFieldException var5) { 28 | clazz = clazz.getSuperclass(); 29 | } 30 | } 31 | 32 | if (field == null) { 33 | throw new NoSuchFieldException(fieldName); 34 | } else { 35 | field.setAccessible(true); 36 | return field.get(object); 37 | } 38 | } 39 | public static ThreadGroup getSystemThreadGroup() { 40 | ThreadGroup group = Thread.currentThread().getThreadGroup(); 41 | while (!group.getName().equals("system")) { 42 | group = group.getParent(); 43 | } 44 | return group; 45 | } 46 | public static String extractMainClassName(String innerClassName) { 47 | int index = innerClassName.lastIndexOf('$'); 48 | if (index != -1) { 49 | return innerClassName.substring(0, index); 50 | } 51 | return innerClassName; 52 | } 53 | 54 | public static Thread[] ConvertThreadToArray(List ThreadList) { 55 | Thread[] array = new Thread[ThreadList.size()]; 56 | ThreadList.toArray(array); 57 | return array; 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /src/test/java/FindEvilAgent.java: -------------------------------------------------------------------------------- 1 | import com.aftersnows.filter.filtersFilter; 2 | import com.aftersnows.transform.KillFilter; 3 | 4 | import java.lang.instrument.Instrumentation; 5 | import java.lang.instrument.UnmodifiableClassException; 6 | 7 | public class FindEvilAgent { 8 | public static Instrumentation staticInstrumentation; 9 | public static Class[] staticClasses; 10 | 11 | private FindEvilAgent() { 12 | throw new InstantiationError("Don't instantiate the class"); 13 | } 14 | 15 | public static void premain(String agentArgs, Instrumentation instrumentation) { 16 | staticClasses = (Class[]) instrumentation.getAllLoadedClasses(); 17 | 18 | } 19 | public static void agentmain(String agentArgs, Instrumentation instrumentation) throws UnmodifiableClassException { 20 | staticClasses = (Class[]) instrumentation.getAllLoadedClasses(); 21 | filtersFilter filtersFilter = new filtersFilter(staticClasses); 22 | for (Class c : filtersFilter.filterClasses) { 23 | System.out.println("ALL:"+c.getName()); 24 | } 25 | for (Class c : filtersFilter.MayBFilterClasses) { 26 | System.out.println("May B: " + c.getName()); 27 | // 使用 ClassDumpTransformer 查看经过 KillFilter 之前的类转换 28 | // ClassDumpTransformer dumpTransformer = new ClassDumpTransformer(c.getName()); 29 | // instrumentation.addTransformer(dumpTransformer, true); 30 | // 使用 KillFilter 进行类转换 31 | KillFilter killFilter = new KillFilter(c.getName()); 32 | instrumentation.addTransformer(killFilter, true); 33 | instrumentation.retransformClasses(c); 34 | instrumentation.removeTransformer(killFilter); 35 | } 36 | // for (Class c : filtersFilter.MayBfilterClasses) { 37 | // System.out.println("after:"); 38 | // ClassDumpTransformer dumpTransformer = new ClassDumpTransformer(c.getName()); 39 | // instrumentation.addTransformer(dumpTransformer, true); 40 | // instrumentation.retransformClasses(c); 41 | // instrumentation.removeTransformer(dumpTransformer); 42 | // 43 | // } 44 | 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/aftersnows/utils/JarFileHelper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2021 Baidu Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.aftersnows.utils; 18 | 19 | import com.aftersnows.FindEvilAgent; 20 | import java.io.IOException; 21 | import java.io.UnsupportedEncodingException; 22 | import java.lang.instrument.Instrumentation; 23 | import java.net.URL; 24 | import java.net.URLDecoder; 25 | import java.util.jar.JarFile; 26 | 27 | 28 | /** 29 | * Created by tyy on 3/29/17. 30 | * 对jar文件操作的工具类 31 | */ 32 | public class JarFileHelper { 33 | 34 | /** 35 | * 添加jar文件到jdk的跟路径下,优先加载 36 | * 37 | * @param inst {@link Instrumentation} 38 | */ 39 | public static void addJarToBootstrap(Instrumentation inst) throws IOException { 40 | String localJarPath = getLocalJarPath(); 41 | inst.appendToBootstrapClassLoaderSearch(new JarFile(localJarPath)); 42 | } 43 | 44 | /** 45 | * 获取当前所在jar包的路径 46 | * 47 | * @return jar包路径 48 | */ 49 | public static String getLocalJarPath() { 50 | URL localUrl = FindEvilAgent.class.getProtectionDomain().getCodeSource().getLocation(); 51 | String path = null; 52 | try { 53 | path = URLDecoder.decode( 54 | localUrl.getFile().replace("+", "%2B"), "UTF-8"); 55 | } catch (UnsupportedEncodingException e) { 56 | System.err.println("[OpenRASP] Failed to get jarFile path."); 57 | e.printStackTrace(); 58 | } 59 | return path; 60 | } 61 | 62 | /** 63 | * 获取当前jar包所在的文件夹路径 64 | * 65 | * @return jar包所在文件夹路径 66 | */ 67 | public static String getLocalJarParentPath() { 68 | String jarPath = getLocalJarPath(); 69 | return jarPath.substring(0, jarPath.lastIndexOf("/")); 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | org.example 8 | ApricusFindEvil 9 | 1.0-SNAPSHOT 10 | 11 | 12 | org.ow2.asm 13 | asm 14 | 9.2 15 | 16 | 17 | jdk 18 | tools 19 | 1.0.0 20 | system 21 | ${java.home}/../lib/tools.jar 22 | 23 | 24 | org.javassist 25 | javassist 26 | 3.29.2-GA 27 | 28 | 29 | 30 | 31 | 8 32 | 8 33 | 34 | 35 | 36 | 37 | org.apache.maven.plugins 38 | maven-assembly-plugin 39 | 40 | 41 | package 42 | 43 | single 44 | 45 | 46 | 47 | 48 | 49 | true 50 | 51 | 52 | com.aftersnows.FindEvilAgent 53 | com.aftersnows.FindEvilAgent 54 | true 55 | true 56 | 57 | 58 | 59 | jar-with-dependencies 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /src/main/java/com/aftersnows/utils/VirtualMachineClassLoader.java: -------------------------------------------------------------------------------- 1 | package com.aftersnows.utils; 2 | 3 | import java.io.File; 4 | import java.lang.reflect.Method; 5 | import java.net.URL; 6 | import java.net.URLClassLoader; 7 | import java.security.AccessController; 8 | import java.security.PrivilegedActionException; 9 | import java.security.PrivilegedExceptionAction; 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | 13 | /** 14 | * 从lib/tools.jar加载VirtualMachine 15 | */ 16 | public class VirtualMachineClassLoader { 17 | private static final String VIRTUAL_MACHINE_CLASSNAME = "com.sun.tools.attach.VirtualMachine"; 18 | private static volatile Class vmClass; 19 | 20 | static { 21 | try { 22 | vmClass = getVirtualMachineClass(); 23 | } catch (ClassNotFoundException e) { 24 | System.err.println("not found tools.jar"); 25 | } 26 | } 27 | 28 | public static Class getVmClass() { 29 | return vmClass; 30 | } 31 | 32 | private static Class getVirtualMachineClass() throws ClassNotFoundException { 33 | try { 34 | return AccessController.doPrivileged(new PrivilegedExceptionAction>() { 35 | public Class run() throws Exception { 36 | try { 37 | return ClassLoader.getSystemClassLoader().loadClass(VIRTUAL_MACHINE_CLASSNAME); 38 | } catch (ClassNotFoundException cnfe) { 39 | for (File jar : getPossibleToolsJars()) { 40 | try { 41 | Method method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class); 42 | method.setAccessible(true); 43 | method.invoke(ClassLoader.getSystemClassLoader(), jar.toURI().toURL()); 44 | 45 | return ClassLoader.getSystemClassLoader().loadClass(VIRTUAL_MACHINE_CLASSNAME); 46 | } catch (Exception t) { 47 | System.err.println("Exception while loading tools.jar from " + jar + " " + t); 48 | } 49 | } 50 | throw new ClassNotFoundException(VIRTUAL_MACHINE_CLASSNAME); 51 | } 52 | } 53 | }); 54 | } catch (PrivilegedActionException pae) { 55 | Throwable actual = pae.getCause(); 56 | if (actual instanceof ClassNotFoundException) { 57 | throw (ClassNotFoundException) actual; 58 | } 59 | throw new AssertionError("Unexpected checked exception : " + actual); 60 | } 61 | } 62 | 63 | private static List getPossibleToolsJars() { 64 | List jars = new ArrayList<>(); 65 | 66 | File javaHome = new File(System.getProperty("java.home")); 67 | File jreSourced = new File(javaHome, "lib/tools.jar"); 68 | if (jreSourced.exists()) { 69 | jars.add(jreSourced); 70 | } 71 | if ("jre".equals(javaHome.getName())) { 72 | File jdkHome = new File(javaHome, "../"); 73 | File jdkSourced = new File(jdkHome, "lib/tools.jar"); 74 | if (jdkSourced.exists()) { 75 | jars.add(jdkSourced); 76 | } 77 | } 78 | return jars; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/com/aftersnows/transform/socketVim.java: -------------------------------------------------------------------------------- 1 | package com.aftersnows.transform; 2 | 3 | import java.lang.instrument.ClassFileTransformer; 4 | import java.lang.instrument.IllegalClassFormatException; 5 | import java.lang.reflect.Field; 6 | import java.security.ProtectionDomain; 7 | 8 | 9 | public class socketVim implements ClassFileTransformer { 10 | 11 | @Override 12 | public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { 13 | try { 14 | if (className.equals("sun/nio/ch/SocketChannelImpl")) { 15 | Class socketChannelImplClass = Class.forName("sun.nio.ch.SocketChannelImpl"); 16 | Field fdField = socketChannelImplClass.getDeclaredField("fd"); 17 | // 设置私有字段 fd 可访问性 18 | fdField.setAccessible(true); 19 | return new ClassAdapter(classfileBuffer, fdField).getBytes(); 20 | } 21 | } catch (Exception e) { 22 | e.printStackTrace(); 23 | } 24 | return null; 25 | } 26 | static class ClassAdapter { 27 | private final byte[] classBytes; 28 | private final Field fdField; 29 | 30 | ClassAdapter(byte[] classBytes, Field fdField) { 31 | this.classBytes = classBytes; 32 | this.fdField = fdField; 33 | } 34 | 35 | byte[] getBytes() throws Exception { 36 | // 使用字节码操作库修改字节码 37 | // 在 SocketChannelImpl.read() 方法中插入代码获取地址信息 38 | // 以下示例代码仅用于演示,实际应根据需求进行修改 39 | // 在这里将地址信息直接打印到控制台 40 | // 例如:System.out.println("Socket Address: " + socketAddress); 41 | 42 | // 在 SocketChannelImpl.read() 方法返回之前,插入获取地址信息的处理代码 43 | String getCode = "java.nio.channels.SocketChannel channel = (java.nio.channels.SocketChannel) this;\n" + 44 | "java.net.Socket socket = channel.socket();\n" + 45 | "java.net.InetSocketAddress address = (java.net.InetSocketAddress) socket.getRemoteSocketAddress();\n" + 46 | "if (address != null) {\n" + 47 | " String socketAddress = address.getAddress().getHostAddress() + ':' + address.getPort();\n" + 48 | " System.out.println(\"Socket Address: \" + socketAddress);\n" + 49 | "}"; 50 | 51 | int getCodeIndex = -1; 52 | 53 | // 查找 SocketChannelImpl.read() 方法对应的字节码位置 54 | for (int i = 0; i < classBytes.length - 4; i++) { 55 | if (classBytes[i] == (byte) 0x2a && classBytes[i+1] == (byte) 0xb7 && classBytes[i+3] == (byte) 0xc4) { 56 | getCodeIndex = i; 57 | break; 58 | } 59 | } 60 | 61 | if (getCodeIndex < 0) { 62 | throw new IllegalStateException("Failed to find SocketChannelImpl.read() method"); 63 | } 64 | 65 | // 将插入的代码转成字节数组 66 | byte[] getCodeBytes = getCode.getBytes(); 67 | 68 | // 创建新的字节数组,包括原始字节码和插入的代码 69 | byte[] newClassBytes = new byte[classBytes.length + getCodeBytes.length]; 70 | System.arraycopy(classBytes, 0, newClassBytes, 0, getCodeIndex); 71 | System.arraycopy(getCodeBytes, 0, newClassBytes, getCodeIndex, getCodeBytes.length); 72 | System.arraycopy(classBytes, getCodeIndex, newClassBytes, getCodeIndex + getCodeBytes.length, classBytes.length - getCodeIndex); 73 | 74 | return newClassBytes; 75 | } 76 | } 77 | 78 | 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/com/aftersnows/FindEvilAgent.java: -------------------------------------------------------------------------------- 1 | package com.aftersnows; 2 | import com.aftersnows.JVM.Defense.HTTPServiceDEF; 3 | import com.aftersnows.JVM.Defense.agentDEF; 4 | import com.aftersnows.filter.*; 5 | import com.aftersnows.transform.*; 6 | 7 | import java.lang.instrument.Instrumentation; 8 | import static com.aftersnows.check.CheckUtils.IsRunnable; 9 | 10 | public class FindEvilAgent { 11 | public static Instrumentation staticInstrumentation; 12 | public static Class[] staticClasses; 13 | 14 | private FindEvilAgent() { 15 | throw new InstantiationError("Don't instantiate the class"); 16 | } 17 | 18 | public static void premain(String agentArgs, Instrumentation instrumentation) { 19 | staticClasses = (Class[]) instrumentation.getAllLoadedClasses(); 20 | } 21 | 22 | public static void agentmain(String agentArgs, Instrumentation instrumentation) throws Exception { 23 | staticClasses = (Class[]) instrumentation.getAllLoadedClasses(); 24 | filtersFilter filtersFilter = new filtersFilter(staticClasses); 25 | WebsocketsFilter websocketsFilter = new WebsocketsFilter(staticClasses); 26 | listenersFilter listenersFilter=new listenersFilter(staticClasses); 27 | valuesFilter valuesFilter=new valuesFilter(staticClasses); 28 | servletsFilter servletsFilter=new servletsFilter(staticClasses); 29 | 30 | TimerFilter timerFilter= new TimerFilter(); 31 | System.out.println("Agent start..."); 32 | new HTTPServiceDEF(true); 33 | new agentDEF(); 34 | 35 | 36 | //KillWebsocket 37 | for (Class c : websocketsFilter.MayBWebsocketClasses) { 38 | System.out.println("May Bad websocket: " + c.getName()); 39 | KillWebsocket killWebsocket = new KillWebsocket(c.getName()); 40 | ClassDumpTransformer dumpTransformer = new ClassDumpTransformer(c.getName()); 41 | instrumentation.addTransformer(killWebsocket, true); 42 | System.out.println("Kill ing"); 43 | instrumentation.addTransformer(dumpTransformer, true); 44 | instrumentation.retransformClasses(c); 45 | } 46 | 47 | //KillListener 48 | for (Class c : listenersFilter.MayBListenerClasses) { 49 | System.out.println("May Bad websocket: " + c.getName()); 50 | KillListener killListener=new KillListener(c.getName()); 51 | ClassDumpTransformer dumpTransformer = new ClassDumpTransformer(c.getName()); 52 | instrumentation.addTransformer(killListener, true); 53 | System.out.println("Kill ing"); 54 | instrumentation.addTransformer(dumpTransformer, true); 55 | instrumentation.retransformClasses(c); 56 | } 57 | 58 | //KillValue 59 | for (Class c : valuesFilter.MayBValuesClasses) { 60 | System.out.println("May Bad websocket: " + c.getName()); 61 | KillValue KillValue=new KillValue(c.getName()); 62 | ClassDumpTransformer dumpTransformer = new ClassDumpTransformer(c.getName()); 63 | instrumentation.addTransformer(KillValue, true); 64 | System.out.println("Kill ing"); 65 | instrumentation.addTransformer(dumpTransformer, true); 66 | instrumentation.retransformClasses(c); 67 | } 68 | 69 | //KillServlet 70 | for (Class c : servletsFilter.MayBServletClasses) { 71 | System.out.println("May Bad websocket: " + c.getName()); 72 | KillServlet KillServlet=new KillServlet(c.getName()); 73 | ClassDumpTransformer dumpTransformer = new ClassDumpTransformer(c.getName()); 74 | instrumentation.addTransformer(KillServlet, true); 75 | System.out.println("Kill ing"); 76 | instrumentation.addTransformer(dumpTransformer, true); 77 | instrumentation.retransformClasses(c); 78 | } 79 | 80 | //KillFilter 81 | for (Class c : filtersFilter.MayBFilterClasses) { 82 | System.out.println("May Bad filter: " + c.getName()); 83 | KillFilter killFilter = new KillFilter(c.getName()); 84 | ClassDumpTransformer dumpTransformer = new ClassDumpTransformer(c.getName()); 85 | instrumentation.addTransformer(killFilter, true); 86 | System.out.println("Kill ing"); 87 | instrumentation.addTransformer(dumpTransformer, true); 88 | instrumentation.retransformClasses(c); 89 | } 90 | 91 | 92 | //KillTimer 93 | for (Class c : timerFilter.MayBTimerClasses) { 94 | System.out.println("May Bad Timer: " + c.getName()); 95 | if(IsRunnable(c)){ 96 | KillTimer killTimer=new KillTimer(c.getName()); 97 | instrumentation.addTransformer(killTimer, true); 98 | } 99 | ClassDumpTransformer dumpTransformer = new ClassDumpTransformer(c.getName()); 100 | instrumentation.addTransformer(dumpTransformer, true); 101 | instrumentation.retransformClasses(c); 102 | } 103 | 104 | 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 记录对于恶意代码查杀 2 | 3 | 小记:第一套程序 4 | 5 | 用java,采用rasp同样的方式,hook住jvm后查杀内存马,之后可能还会对无文件的agent进行操作 6 | 7 | 测试用文件放在asset文件夹 8 | # 序言 9 | 10 | java内存马技术十分成熟,基于组件的,特性的内存马类型层出不穷,基于java agent内存马的攻防逐渐成为在jvm上攻防。 11 | # 内存马检测查杀思路 12 | 13 | 万变不离其宗:找内存马就是找可以利用的Request 和Response 14 | 15 | 由于内存马它环境不同导致其多变的原因,应该把查杀改为修改内存马原有逻辑更恰当 16 | 17 | 18 | 19 | **内存马类型检测是检测不完的,最好的就是行为检测** 20 | 21 | 其实一开始有把所有class在本地无文件的类给删除的想法,但是实际上有很多的内部类等等正常运行的类会获取不到其classloader或者得到了却拿不到其urlpath的问题,有两种方法 22 | 23 | 1.要么是改良其中对于无文件检测,将其进行排除干扰选项,然后遍历查找所有恶意的无文件的类进行删除,这里有许多不稳定的因素,对于干扰项可能是排除不完的,对于内存马写入我们定义的干扰选项中也是束手无策。 24 | 25 | 2.对于特定类型进行特征检测,这样能基本筛选干扰选项,但是对于新类型的内存马就束手无策。 26 | 27 | 28 | 29 | #### 常见四大类型:filter,listener,value,servlet 30 | 31 | 这里以filter为典型说一下 32 | 33 | > **public void doFilter (ServletRequest, ServletResponse, FilterChain)** 34 | > 该方法完成实际的过滤操作,当客户端请求方法与过滤器设置匹配的URL时,Servlet容器将先调用过滤器的doFilter方法。FilterChain用户访问后续过滤器。 35 | 36 | ###### 四大类型内存马检测: 37 | 38 | 简单粗暴,对于每种filter类型进行排查,是否存在实际文件,在加上是否通过恶意classloader加载满足以上两点直接这个潜在内存马的字节码,交给用户判断dofilter是否存在恶意逻辑。 39 | 40 | ###### 四大类型内存马查杀: 41 | 42 | 用户指定的filter通过asm修改其中dofilter的方法,让其方法体为空。【这里注意要避免写添加或者删除方法的asm方法,比如说返回一个new MethodVisitor(Opcodes.ASM9) {...}】 43 | 44 | 将方法体置空如下操作 45 | 46 | ``` 47 | methodVisitor.visitCode(); 48 | methodVisitor.visitInsn(RETURN); 49 | methodVisitor.visitMaxs( , ); 50 | methodVisitor.visitEnd(); 51 | 要分方法写,visitMaxs这些参数的值需要根据方法的逻辑和字节码指令的使用情况来确定 52 | ``` 53 | 54 | ###### 恢复误删除的运行中的类的一点思路: 55 | 56 | 可以执行retransformClasses来重新触发这个Transformer的拦截。类加载的字节码被修改后,除非再次被retransform,否则不会恢复。 57 | 58 | 在我写的时候,发现了一个有意思的东西,在例如 59 | 60 | ``` 61 | KillFilter killFilter = new KillFilter(c.getName()); 62 | instrumentation.addTransformer(killFilter, true); 63 | instrumentation.retransformClasses(c); 64 | instrumentation.removeTransformer(killFilter); 65 | ClassDumpTransformer dumpTransformer = new ClassDumpTransformer(c.getName()); 66 | instrumentation.addTransformer(dumpTransformer, true); 67 | instrumentation.retransformClasses(c); 68 | ``` 69 | 70 | removeTransformer之后又用其他的transform实现retransformClasses(c);同一个类的话,会使得类重置恢复,这里也许可以用于用户误删错误的类导致的问题,同样也可以用于撤销内存马的查杀逻辑。 71 | 72 | 73 | 74 | ![image-20231130141045078](asset/noteimage_1.png) 75 | 76 | #### 基于通信的内存马类型:WebSocket 77 | 78 | 79 | 80 | > WebSocket是一种全双工通信协议,它可以用来做代理 81 | > 82 | > 例如有一台不出网主机,有反序列化漏洞。 83 | > 84 | > 以前在这种场景下,之前可能会考虑上reGeorg或者利用端口复用来搭建代理。 85 | > 86 | > 现在可以利用反序列化漏洞直接注入websocket代理内存马,然后直接连上用上全双工通信协议的代理 87 | 88 | 在获取classloader的时候,遇到delegatingloader这种,用于反射加载的类难以处理 89 | 90 | ###### WebSocket查杀思路: 91 | 92 | 跟四大类型大同小异,因为还是要注册class到map中,检测本地文件存在和是否继承自endpoint,检测到直接改写其中注册到map中的endpoint类的onmessage的逻辑置空 93 | 94 | ![image-20231201113623889](asset/noteimage_2.png) 95 | 96 | 97 | 98 | ## websocket查杀测试 99 | 100 | 使用sockboy测试即可 101 | 102 | ![image-20231201114020283](asset/noteimage_3.png) 103 | 104 | 杀完如图所示 105 | 106 | #### 基于线程定时任务的内存马类型 107 | 108 | ###### 线程型内存马 109 | 110 | 对于在thread创建新的守护线程,由于在线程要加入自己写的恶意代码,必然在Runnable() 接口的run方法实现恶意代码逻辑,对于这类如何检测,经过我测试,对于每个thread中的target 111 | 112 | ``` 113 | private void init(ThreadGroup g, Runnable target, String name, 114 | long stackSize, AccessControlContext acc, 115 | boolean inheritThreadLocals) 116 | ``` 117 | 118 | 都是一个runnable实现接口,如果target为null,这个线程不会进行任何操作,如果不为null,就是存在任务了,所以检测逻辑如下: 119 | 120 | 先遍历每个thread,获取target不为null的thread,同时拿到这个target的类名,查看是否存在在本地文件,如果不存在说明即为thread内存马。 121 | 122 | ###### 线程型内存马查杀 123 | 124 | 对于检测到的thread,将守护线程的daemon反射修改为false,之后进行stop操作。之后对注入恶意线程的类进行修改达到查杀的目的。 125 | 126 | ###### 线程型内存马测试 127 | 128 | ![image-20231205161043709](asset/noteimage_7.png) 129 | 130 | ![image-20231205162622475](asset/noteimage_8.png) 131 | 132 | ![image-20231205162648447](asset/noteimage_9.png) 133 | 134 | 135 | 136 | ### Agent马查杀 137 | 138 | 难点:【其中都是agent里面的原因造成的= =】 139 | 140 | agent的局限性导致用agent查杀修改变得困难了起来,对于使用HSDB对应的sa-jdi.jar虽然可以check当前类,但是这种方法会导致JVM挂起 141 | 142 | 1.对于调用`retransformClass`方法的时候参数中的字节码并不是调用`redefineClass`后被修改的类的字节码 143 | 144 | > retransformClasses:已经加载的类重新进行转换处理,即会触发重新加载类定义,新加载的类不能修改旧有的类声明,譬如不能增加属性、不能修改方法声明 145 | > 146 | > redefineClasses:与如上类似,但不是重新进行转换处理,而是直接把处理结果(bytecode)直接给JVM 147 | > 148 | > 假如在redifine时,目标类正在执行中,那么执行中的行为还是按照原来字节码的定义执行,当对该类行为发起新的调用时,将会使用redefine之后的新行为 149 | 150 | 2.对于agent 防检测如何绕过,对于删除pid后该如何办【能不能把之前所有的agent连接也删除了】,到时候还得测试一下zhouyu的防检测绕过 151 | 152 | 3.怎么查怎么删 153 | 154 | 155 | 156 | 对于jvm操作的原理 157 | 158 | > 多次调用某个方法,使其成为热点代码触发即时编译,然后通过oop的数据结构偏移计算出JIT地址,最后使用unsafe写内存的功能,将shellcode写入到JIT地址 159 | 160 | 而冰蝎用的`sun/tools/attach/VirtualMachineImpl#enqueue`Native方法来attach,基于基于JDK自带的Native方法 161 | 162 | 163 | 164 | ###### agent马查杀思路 165 | 166 | 对于httpservice等方法进行修改成原来的service 167 | 168 | ###### agent马查杀测试 169 | 170 | ![image-20231205164104346](asset/noteimage_10.png) 171 | 172 | # Agent防御 173 | 174 | 鉴于冰蝎的防检测逻辑,如果让后续agent所有连接都无效的方法太粗暴,也许可以用其他的办法。 175 | 176 | 学习 jvmti的方式,有很多的玩法,比如hook system.getProperty这个native方法,在冰蝎中有很多关于system.getProperty的逻辑检测,如果无效即自己报错,当然,在system.getProperty肯定有影响业务逻辑的风险,但这只是个想法,对于攻防对抗的思路。 177 | 178 | 179 | 180 | 现在这个工具如果提前运行的话,会将底层方法system.getProperty hook掉,干扰在此后运行的对于system.getProperty获取信息的逻辑。 181 | 182 | 183 | 184 | 对于linux的防检测,同样能操作jvm直接实现Instrumentation,而不用通过socket来attach 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | ![image-20231202123112452](asset/noteimage_4.png) 195 | 196 | ![image-20231203150419537](asset/noteimage_5.png) 197 | 198 | 可以看到冰蝎用于返回基本信息的类注入 199 | 200 | ![image-20231203154909222](asset/noteimage_6.png) 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 参考 214 | https://mp.weixin.qq.com/s/89Bmvy_uY97TZm3vR9lyWw?ref=www.ctfiot.com 215 | https://paper.seebug.org/1945/#_3 216 | -------------------------------------------------------------------------------- /src/main/java/com/aftersnows/JVM/Defense/agentDEF.java: -------------------------------------------------------------------------------- 1 | package com.aftersnows.JVM.Defense; 2 | 3 | import com.aftersnows.JVM.HotSpot; 4 | 5 | public class agentDEF { 6 | 7 | public agentDEF() throws NoSuchMethodException, ClassNotFoundException { 8 | SystemGetPropertyHook(); 9 | HTTPServiceHook(); 10 | } 11 | 12 | public void SystemGetPropertyHook() throws NoSuchMethodException { 13 | long LockClassHandle = HotSpot.getKlass(DEFUtils.class); 14 | long systemClassHandle = HotSpot.getKlass(System.class); 15 | String methodType = HotSpot.hasType("methodOopDesc") ? "methodOopDesc" : "Method"; 16 | long systemMethodHandle = HotSpot.getMethodByKlass(systemClassHandle, "getProperty", HotSpot.getMethodSignature(System.class.getMethod("getProperty", String.class))); 17 | long hotspotMethodHandle = HotSpot.getMethodByKlass(LockClassHandle, "getProperty", HotSpot.getMethodSignature(DEFUtils.class.getMethod("getProperty", String.class))); 18 | System.out.println("被hook的方法地址 system->getProperty : 0x" + Long.toHexString(systemMethodHandle)); 19 | String constMethodType = HotSpot.hasType("ConstMethod") ? "ConstMethod" : "constMethodOopDesc"; 20 | long methodIdField = HotSpot.getFieldOffset(constMethodType, HotSpot.hasField(constMethodType, "_method_idnum") ? "_method_idnum" : "_method_index"); 21 | long _constMethodField = HotSpot.getFieldOffset(methodType, "_constMethod"); 22 | long targetConstMethod = HotSpot.unsafe.getAddress(systemMethodHandle + _constMethodField); 23 | long newConstMethod = HotSpot.unsafe.getAddress(hotspotMethodHandle + _constMethodField); 24 | short hotspot_method_idnum = HotSpot.unsafe.getShort(newConstMethod + methodIdField); 25 | short system_method_idnum = HotSpot.unsafe.getShort(newConstMethod + methodIdField); 26 | HotSpot.typeSwap(methodType, hotspotMethodHandle, systemMethodHandle); 27 | 28 | HotSpot.unsafe.putShort(targetConstMethod + methodIdField, hotspot_method_idnum); 29 | HotSpot.unsafe.putShort(newConstMethod + methodIdField, system_method_idnum); 30 | } 31 | 32 | public void HTTPServiceHook() throws NoSuchMethodException, ClassNotFoundException { 33 | String LockClassName = "javax.servlet.http.HttpServlet"; 34 | Class LockClass = Class.forName(LockClassName); 35 | long LockClassHandle = HotSpot.getKlass(LockClass); 36 | String methodType = HotSpot.hasType("methodOopDesc") ? "methodOopDesc" : "Method"; 37 | long systemMethodHandle = HotSpot.getMethodByKlass(LockClassHandle, "service", HotSpot.getMethodSignature(LockClass.getMethod("service", Class.forName("javax.servlet.ServletRequest"),Class.forName("javax.servlet.ServletResponse")))); 38 | long hotspotMethodHandle = HotSpot.getMethodByKlass(LockClassHandle, "service", HotSpot.getMethodSignature(LockClass.getMethod("service", Class.forName("javax.servlet.ServletRequest"),Class.forName("javax.servlet.ServletResponse")))); 39 | 40 | 41 | System.out.println("被hook的方法地址 HttpServlet->service : 0x" + Long.toHexString(systemMethodHandle)); 42 | String constMethodType = HotSpot.hasType("ConstMethod") ? "ConstMethod" : "constMethodOopDesc"; 43 | long methodIdField = HotSpot.getFieldOffset(constMethodType, HotSpot.hasField(constMethodType, "_method_idnum") ? "_method_idnum" : "_method_index"); 44 | long _constMethodField = HotSpot.getFieldOffset(methodType, "_constMethod"); 45 | long targetConstMethod = HotSpot.unsafe.getAddress(systemMethodHandle + _constMethodField); 46 | long newConstMethod = HotSpot.unsafe.getAddress(hotspotMethodHandle + _constMethodField); 47 | short hotspot_method_idnum = HotSpot.unsafe.getShort(newConstMethod + methodIdField); 48 | short system_method_idnum = HotSpot.unsafe.getShort(newConstMethod + methodIdField); 49 | HotSpot.typeSwap(methodType, hotspotMethodHandle, systemMethodHandle); 50 | HotSpot.unsafe.putShort(targetConstMethod + methodIdField, hotspot_method_idnum); 51 | HotSpot.unsafe.putShort(newConstMethod + methodIdField, system_method_idnum); 52 | } 53 | public void WindDumperHook() throws NoSuchMethodException, ClassNotFoundException { 54 | String LockClassName = "sun.jvm.hotspot.debugger.windbg.WindbgDebuggerLocal"; 55 | Class LockClass = Class.forName(LockClassName); 56 | long LockClassHandle = HotSpot.getKlass(LockClass); 57 | String methodType = HotSpot.hasType("methodOopDesc") ? "methodOopDesc" : "Method"; 58 | long systemMethodHandle = HotSpot.getMethodByKlass(LockClassHandle, "attach", HotSpot.getMethodSignature(LockClass.getMethod("attach", int.class))); 59 | long hotspotMethodHandle = HotSpot.getMethodByKlass(LockClassHandle, "attach", HotSpot.getMethodSignature(DEFUtils.class.getMethod("attach", int.class))); 60 | System.out.println("被hook的方法地址 WindbgDebuggerLocal->attach : 0x" + Long.toHexString(systemMethodHandle)); 61 | String constMethodType = HotSpot.hasType("ConstMethod") ? "ConstMethod" : "constMethodOopDesc"; 62 | long methodIdField = HotSpot.getFieldOffset(constMethodType, HotSpot.hasField(constMethodType, "_method_idnum") ? "_method_idnum" : "_method_index"); 63 | long _constMethodField = HotSpot.getFieldOffset(methodType, "_constMethod"); 64 | long targetConstMethod = HotSpot.unsafe.getAddress(systemMethodHandle + _constMethodField); 65 | long newConstMethod = HotSpot.unsafe.getAddress(hotspotMethodHandle + _constMethodField); 66 | short hotspot_method_idnum = HotSpot.unsafe.getShort(newConstMethod + methodIdField); 67 | short system_method_idnum = HotSpot.unsafe.getShort(newConstMethod + methodIdField); 68 | HotSpot.typeSwap(methodType, hotspotMethodHandle, systemMethodHandle); 69 | 70 | HotSpot.unsafe.putShort(targetConstMethod + methodIdField, hotspot_method_idnum); 71 | HotSpot.unsafe.putShort(newConstMethod + methodIdField, system_method_idnum); 72 | } 73 | 74 | 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/com/aftersnows/check/CheckUtils.java: -------------------------------------------------------------------------------- 1 | package com.aftersnows.check; 2 | 3 | import java.net.URL; 4 | 5 | import static com.aftersnows.utils.utils.CanonicalClassNameToBackslash; 6 | import static jdk.nashorn.internal.runtime.regexp.joni.Config.log; 7 | 8 | public class CheckUtils { 9 | public static boolean ClassFileIsExists(Class clazz){ 10 | if(clazz == null){ 11 | log.println("class is null"); 12 | return false; 13 | } 14 | String className = clazz.getName(); 15 | String classNamePath = CanonicalClassNameToBackslash(className) + ".class"; 16 | Object clazzLoader = clazz.getClassLoader(); 17 | URL is; 18 | if (clazzLoader==null){ 19 | is = ClassLoader.getSystemClassLoader().getResource(classNamePath); 20 | return true; 21 | }else{ 22 | is = clazz.getClassLoader().getResource(classNamePath); 23 | } 24 | if(is == null){ 25 | return false; 26 | }else{ 27 | return true; 28 | } 29 | 30 | } 31 | public static boolean IsBadClassLoader(Class targetClass){ 32 | ClassLoader classLoader = null; 33 | if(targetClass.getClassLoader() != null) { 34 | classLoader = targetClass.getClassLoader(); 35 | }else{ 36 | classLoader = Thread.currentThread().getContextClassLoader(); 37 | } 38 | if(classLoader.getClass().getName().contains("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl$TransletClassLoader") 39 | || classLoader.getClass().getName().contains("com.sun.org.apache.bcel.internal.util.ClassLoader")){ 40 | return true; 41 | } 42 | return false; 43 | } 44 | 45 | public static boolean IsFilter(Class targetClass){ 46 | ClassLoader classLoader = null; 47 | if(targetClass.getClassLoader() != null) { 48 | classLoader = targetClass.getClassLoader(); 49 | }else{ 50 | classLoader = Thread.currentThread().getContextClassLoader(); 51 | } 52 | 53 | Class clsFilter = null; 54 | try { 55 | clsFilter = classLoader.loadClass("javax.servlet.Filter"); 56 | }catch (Exception e){ 57 | } 58 | 59 | if(clsFilter != null && clsFilter.isAssignableFrom(targetClass)){ 60 | return true; 61 | }else{ 62 | return false; 63 | } 64 | } 65 | public static boolean IsRunnable(Class targetClass){ 66 | ClassLoader classLoader = null; 67 | if(targetClass.getClassLoader() != null) { 68 | classLoader = targetClass.getClassLoader(); 69 | }else{ 70 | classLoader = Thread.currentThread().getContextClassLoader(); 71 | } 72 | 73 | Class clsFilter = null; 74 | try { 75 | clsFilter = classLoader.loadClass("java.lang.Runnable"); 76 | }catch (Exception e){ 77 | } 78 | 79 | if(clsFilter != null && clsFilter.isAssignableFrom(targetClass)){ 80 | return true; 81 | }else{ 82 | return false; 83 | } 84 | } 85 | 86 | 87 | public static boolean IsServlet(Class targetClass){ 88 | ClassLoader classLoader = null; 89 | if(targetClass.getClassLoader() != null) { 90 | classLoader = targetClass.getClassLoader(); 91 | }else{ 92 | classLoader = Thread.currentThread().getContextClassLoader(); 93 | } 94 | 95 | Class clsFilter = null; 96 | try { 97 | clsFilter = classLoader.loadClass("javax.servlet.Servlet"); 98 | }catch (Exception e){ 99 | } 100 | 101 | if(clsFilter != null && clsFilter.isAssignableFrom(targetClass)){ 102 | return true; 103 | }else{ 104 | return false; 105 | } 106 | } 107 | 108 | public static boolean IsListener(Class targetClass){ 109 | ClassLoader classLoader = null; 110 | if(targetClass.getClassLoader() != null) { 111 | classLoader = targetClass.getClassLoader(); 112 | }else{ 113 | classLoader = Thread.currentThread().getContextClassLoader(); 114 | } 115 | 116 | Class clsFilter = null; 117 | try { 118 | clsFilter = classLoader.loadClass("javax.servlet.ServletRequestListener"); 119 | }catch (Exception e){ 120 | } 121 | 122 | if(clsFilter != null && clsFilter.isAssignableFrom(targetClass)){ 123 | return true; 124 | }else{ 125 | return false; 126 | } 127 | } 128 | public static boolean IsValve(Class targetClass){ 129 | ClassLoader classLoader = null; 130 | if(targetClass.getClassLoader() != null) { 131 | classLoader = targetClass.getClassLoader(); 132 | }else{ 133 | classLoader = Thread.currentThread().getContextClassLoader(); 134 | } 135 | 136 | Class clsFilter = null; 137 | try { 138 | clsFilter = classLoader.loadClass("org.apache.catalina.Valve"); 139 | }catch (Exception e){ 140 | } 141 | 142 | if(clsFilter != null && clsFilter.isAssignableFrom(targetClass)){ 143 | return true; 144 | }else{ 145 | return false; 146 | } 147 | } 148 | public static boolean IsWebsocket(Class targetClass){ 149 | ClassLoader classLoader = null; 150 | if(targetClass.getClassLoader() != null) { 151 | classLoader = targetClass.getClassLoader(); 152 | }else{ 153 | classLoader = Thread.currentThread().getContextClassLoader(); 154 | } 155 | 156 | Class clsFilter = null; 157 | try { 158 | clsFilter = classLoader.loadClass("javax.websocket.Endpoint"); 159 | }catch (Exception e){ 160 | } 161 | 162 | if(clsFilter != null && clsFilter.isAssignableFrom(targetClass)){ 163 | return true; 164 | }else{ 165 | return false; 166 | } 167 | } 168 | 169 | } 170 | -------------------------------------------------------------------------------- /src/main/java/com/aftersnows/utils/ModuleLoader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2021 Baidu Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.aftersnows.utils; 18 | 19 | import java.io.File; 20 | import java.io.FilenameFilter; 21 | import java.io.UnsupportedEncodingException; 22 | import java.lang.instrument.Instrumentation; 23 | import java.lang.reflect.Method; 24 | import java.net.URL; 25 | import java.net.URLClassLoader; 26 | import java.net.URLDecoder; 27 | 28 | /** 29 | * Created by tyy on 18-1-23. 30 | * 31 | * 用于加载和初始化引擎模块 32 | */ 33 | public class ModuleLoader { 34 | 35 | public static final String ENGINE_JAR = "rasp-engine.jar"; 36 | 37 | public static String baseDirectory; 38 | 39 | private static ModuleLoader instance; 40 | 41 | public static ClassLoader moduleClassLoader; 42 | 43 | 44 | // ModuleLoader 为 classloader加载的,不能通过getProtectionDomain()的方法获得JAR路径 45 | static { 46 | // juli 47 | try { 48 | Class clazz = Class.forName("java.nio.file.FileSystems"); 49 | clazz.getMethod("getDefault", new Class[0]).invoke(null); 50 | } catch (Throwable t) { 51 | // ignore 52 | } 53 | Class clazz = ModuleLoader.class; 54 | // path值示例: file:/opt/apache-tomcat-xxx/rasp/rasp.jar!/com/fuxi/javaagent/Agent.class 55 | String path = clazz.getResource("/" + clazz.getName().replace(".", "/") + ".class").getPath(); 56 | if (path.startsWith("file:")) { 57 | path = path.substring(5); 58 | } 59 | if (path.contains("!")) { 60 | path = path.substring(0, path.indexOf("!")); 61 | } 62 | try { 63 | baseDirectory = URLDecoder.decode(new File(path).getParent(), "UTF-8"); 64 | } catch (UnsupportedEncodingException e) { 65 | baseDirectory = new File(path).getParent(); 66 | } 67 | ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader(); 68 | while (systemClassLoader.getParent() != null 69 | && !systemClassLoader.getClass().getName().equals("sun.misc.Launcher$ExtClassLoader")) { 70 | systemClassLoader = systemClassLoader.getParent(); 71 | } 72 | moduleClassLoader = systemClassLoader; 73 | } 74 | 75 | // /** 76 | // * 构造所有模块 77 | // * 78 | // * @param mode 启动模式 79 | // * @param inst {@link Instrumentation} 80 | // */ 81 | // private ModuleLoader(String mode, Instrumentation inst) throws Throwable { 82 | // 83 | // if (Module.START_MODE_NORMAL == mode) { 84 | // setStartupOptionForJboss(); 85 | // } 86 | // engineContainer = new ModuleContainer(ENGINE_JAR); 87 | // engineContainer.start(mode, inst); 88 | // } 89 | // 90 | // public static synchronized void release(String mode) { 91 | // try { 92 | // if (engineContainer != null) { 93 | // System.out.println("[OpenRASP] Start to release OpenRASP"); 94 | // 95 | // engineContainer.release(mode); 96 | // engineContainer = null; 97 | // } else { 98 | // System.out.println("[OpenRASP] Engine is initialized, skipped"); 99 | // } 100 | // } catch (Throwable throwable) { 101 | // // ignore 102 | // } 103 | // } 104 | // 105 | // /** 106 | // * 加载所有 RASP 模块 107 | // * 108 | // * @param mode 启动模式 109 | // * @param inst {@link Instrumentation} 110 | // */ 111 | // public static synchronized void load(String mode, String action, Instrumentation inst) throws Throwable { 112 | // if (Module.START_ACTION_INSTALL.equals(action)) { 113 | // if (instance == null) { 114 | // try { 115 | // instance = new ModuleLoader(mode, inst); 116 | // } catch (Throwable t) { 117 | // instance = null; 118 | // throw t; 119 | // } 120 | // } else { 121 | // System.out.println("[OpenRASP] The OpenRASP has bean initialized and cannot be initialized again"); 122 | // } 123 | // } else if (Module.START_ACTION_UNINSTALL.equals(action)) { 124 | // release(mode); 125 | // } else { 126 | // throw new IllegalStateException("[OpenRASP] Can not support the action: " + action); 127 | // } 128 | // } 129 | 130 | 131 | /** 132 | * 判断是否是weblogic或者jdk9、10和11 133 | */ 134 | public static boolean isCustomClassloader() { 135 | try { 136 | String classLoader = ClassLoader.getSystemClassLoader().getClass().getName(); 137 | if (classLoader.startsWith("com.oracle") && classLoader.contains("weblogic")) { 138 | return true; 139 | } 140 | return isModularityJdk(); 141 | } catch (Exception e) { 142 | return false; 143 | } 144 | } 145 | 146 | public static boolean isModularityJdk() { 147 | String javaVersion = System.getProperty("java.version"); 148 | String[] version = javaVersion.split("\\."); 149 | if (version.length >= 2) { 150 | int major; 151 | int minor; 152 | try { 153 | major = Integer.parseInt(version[0]); 154 | minor = Integer.parseInt(version[1]); 155 | } catch (NumberFormatException e) { 156 | return false; 157 | } 158 | if (major == 1) { 159 | return minor >= 9; 160 | } else if (major >= 9) { 161 | return true; 162 | } 163 | } else if (javaVersion.startsWith("9")) { 164 | return true; 165 | } else if (javaVersion.length() >= 2) { 166 | char first = javaVersion.charAt(0); 167 | char second = javaVersion.charAt(1); 168 | if (first >= '1' && first <= '9' && second >= '0' && second <= '9') { 169 | return true; 170 | } 171 | } 172 | return false; 173 | } 174 | 175 | /** 176 | * 判断当前进程是否为jboss7 版本,并设置相关属性和预加载包 177 | */ 178 | public static void setStartupOptionForJboss() { 179 | String jbossHome = ""; 180 | boolean isJboss = false; 181 | String splitChar = System.getProperty("path.separator") == null ? ";" : System.getProperty("path.separator"); 182 | String jarPaths[] = System.getProperty("java.class.path").split(splitChar); 183 | for (int i = 0; i < jarPaths.length; ++i) { 184 | if (jarPaths[i].endsWith("jboss-modules.jar")) { 185 | File jarFile = new File(jarPaths[i]); 186 | if (null != jarFile) { 187 | jbossHome = jarFile.getParent(); 188 | } 189 | isJboss = true; 190 | break; 191 | } 192 | } 193 | 194 | if (isJboss) { 195 | String moduleBaseDir = ""; 196 | File moduleBase = new File(jbossHome + "/modules/system/layers/base"); 197 | if (null != moduleBase && moduleBase.isDirectory()) { 198 | moduleBaseDir = jbossHome + "/modules/system/layers/base"; 199 | } else { 200 | moduleBaseDir = jbossHome + "/modules"; 201 | } 202 | setSystemProperty(moduleBaseDir); 203 | } 204 | } 205 | 206 | /** 207 | * 设置jboss的jboss.modules.system.pkgs,java.util.logging.manager,以及对logmanager的预加载项 208 | * 209 | * @param moduleBaseDir 210 | */ 211 | public static void setSystemProperty(String moduleBaseDir) { 212 | 213 | String pkgs = System.getProperty("jboss.modules.system.pkgs"); 214 | System.out.println("origin pkgs = " + pkgs); 215 | if (null == pkgs || !pkgs.contains("baidu.openrasp")) { 216 | if (pkgs != null && !pkgs.isEmpty()) { 217 | pkgs = System.setProperty("jboss.modules.system.pkgs", 218 | pkgs + ",org.jboss.logmanager,com.baidu.openrasp,com.sdwaf,javax.servlet,javax.el"); 219 | } else { 220 | pkgs = System.setProperty("jboss.modules.system.pkgs", 221 | "org.jboss.logmanager,com.baidu.openrasp,com.sdwaf,javax.servlet,javax.el"); 222 | } 223 | pkgs = System.setProperty("jboss.modules.system.pkgs", pkgs + ",org.jboss.logmanager,com.baidu.openrasp,com.sdwaf,javax.servlet,javax.el"); 224 | System.out.println("default pkgs = " + pkgs); 225 | } 226 | 227 | String logManager = System.getProperty("java.util.logging.manager"); 228 | System.out.println("origin java.util.logging.manager = " + logManager); 229 | if (null == logManager || logManager.isEmpty()) { 230 | System.setProperty("java.util.logging.manager", "org.jboss.logmanager.LogManager"); 231 | System.out.println("add org.jboss.logmanager.LogManager to java.util.logging.manager"); 232 | } else if (!logManager.contains("org.jboss.logmanager.LogManager")) { 233 | System.setProperty("java.util.logging.manager", logManager + ",org.jboss.logmanager.LogManager"); 234 | System.out.println("add logmanager on old value=" + logManager); 235 | } 236 | 237 | String logBootPath = ""; 238 | String splitChar = System.getProperty("path.separator") == null ? ";" : System.getProperty("path.separator"); 239 | logBootPath = appendPathAfterLoadJar(logBootPath, moduleBaseDir + "/org/jboss/logmanager/main/", "jboss-logmanager-"); 240 | logBootPath = appendPathAfterLoadJar(logBootPath, moduleBaseDir + "/org/jboss/log4j/logmanager/main/", "jboss-logmanager-");//wildfly8+ 241 | logBootPath = appendPathAfterLoadJar(logBootPath, moduleBaseDir + "/org/jboss/logmanager/log4j/main/", "jboss-logmanager-");//jboss-as7 242 | logBootPath = appendPathAfterLoadJar(logBootPath, moduleBaseDir + "/org//apache//log4j/main/", "log4j-");//jboss-as7 243 | logBootPath = appendPathAfterLoadJar(logBootPath, moduleBaseDir + "/org/wildfly/common/main/", "wildfly-common-");//wildfly16 244 | String bootClasspath = System.getProperty("sun.boot.class.path"); 245 | if (null == bootClasspath || bootClasspath.isEmpty()) { 246 | System.setProperty("sun.boot.class.path", logBootPath); 247 | } else if (false == bootClasspath.contains("jboss-logmanager")) { 248 | logBootPath = logBootPath + splitChar + bootClasspath; 249 | System.setProperty("sun.boot.class.path", logBootPath); 250 | System.out.println("add boot classpath on value=" + logBootPath); 251 | } 252 | 253 | loadJarFromPath(moduleBaseDir + "/javax/servlet/jsp/api/main/", "jsp-api"); 254 | loadJarFromPath(moduleBaseDir + "/javax/servlet/jstl/api/main/", "jstl-api"); 255 | loadJarFromPath(moduleBaseDir + "/javax/servlet/jstl/api/main/", "taglibs-standard-");//wildfly16 256 | loadJarFromPath(moduleBaseDir + "/javax/el/api/main/", "el-api"); 257 | loadJarFromPath(moduleBaseDir + "/javax/servlet/api/main/", "servlet-api"); 258 | } 259 | 260 | public static String appendPathAfterLoadJar(String oldPath, String libPath, String jarName) { 261 | String newPath = oldPath; 262 | String splitChar = System.getProperty("path.separator") == null ? ";" : System.getProperty("path.separator"); 263 | String jarPath = loadJarFromPath(libPath, jarName); 264 | if (false == jarPath.isEmpty()) { 265 | newPath = oldPath.isEmpty() ? jarPath : jarPath + splitChar + oldPath; 266 | } 267 | return newPath; 268 | } 269 | 270 | /* 271 | *从路径加载文件名称匹配项 272 | */ 273 | public static String loadJarFromPath(String libPath, String jarName) { 274 | //boolean bLoaded = false; 275 | String jarPath = ""; 276 | File libDir = new File(libPath); 277 | if (false == libDir.isDirectory()) { 278 | System.out.println("library path:" + libPath + " is not directory."); 279 | return jarPath; 280 | } 281 | File[] jarFiles = libDir.listFiles(new FilenameFilter() { 282 | public boolean accept(File dir, String name) { 283 | return (name.endsWith(".jar")); 284 | } 285 | }); 286 | for (File file : jarFiles) { 287 | String filePath = file.getAbsolutePath(); 288 | if (filePath != null) { 289 | if (filePath.contains(jarName)) { 290 | loadJar(file); 291 | jarPath = file.getAbsolutePath(); 292 | } 293 | } 294 | } 295 | return jarPath; 296 | } 297 | 298 | public static boolean loadJar(File file) { 299 | boolean loadResult = true; 300 | try { 301 | Method method = URLClassLoader.class.getDeclaredMethod("addURL", new Class[]{URL.class}); 302 | boolean accessible = method.isAccessible(); 303 | try { 304 | if (!accessible) { 305 | method.setAccessible(true); 306 | } 307 | try { 308 | URL url = file.toURI().toURL(); 309 | if (moduleClassLoader instanceof URLClassLoader) { 310 | method.invoke(moduleClassLoader, new Object[]{url}); 311 | } else if (ModuleLoader.isCustomClassloader()) { 312 | moduleClassLoader = ClassLoader.getSystemClassLoader(); 313 | method = moduleClassLoader.getClass().getDeclaredMethod("appendToClassPathForInstrumentation", String.class); 314 | method.setAccessible(true); 315 | try { 316 | method.invoke(moduleClassLoader, file.getCanonicalPath()); 317 | } catch (Exception e) { 318 | method.invoke(moduleClassLoader, file.getAbsolutePath()); 319 | } 320 | } 321 | } catch (Exception localException) { 322 | loadResult = false; 323 | localException.printStackTrace(); 324 | } 325 | 326 | } finally { 327 | method.setAccessible(accessible); 328 | } 329 | } catch (NoSuchMethodException e1) { 330 | loadResult = false; 331 | e1.printStackTrace(); 332 | } catch (SecurityException e1) { 333 | loadResult = false; 334 | e1.printStackTrace(); 335 | } 336 | //System.out.println("Load jar path:"+file.getAbsolutePath()+", return:"+loadResult); 337 | return loadResult; 338 | } 339 | 340 | } 341 | -------------------------------------------------------------------------------- /src/main/java/com/aftersnows/JVM/HotSpot.java: -------------------------------------------------------------------------------- 1 | package com.aftersnows.JVM; 2 | 3 | import com.aftersnows.JVM.Defense.agentDEF; 4 | import sun.misc.Unsafe; 5 | 6 | import java.io.ByteArrayOutputStream; 7 | import java.io.File; 8 | import java.io.PrintStream; 9 | import java.lang.management.ManagementFactory; 10 | import java.lang.reflect.Constructor; 11 | import java.lang.reflect.Field; 12 | import java.lang.reflect.Method; 13 | import java.util.*; 14 | 15 | 16 | public class HotSpot { 17 | static final String[] libPaths = {"/lib/server/","/lib/client/","/lib/amd64/server/","/lib/amd64/client/","/lib/i386/server/","/lib/i386/client/","/bin/server/","/bin/client/","/lib/sparcv9/server/","/lib/sparcv9/client/","/lib/sparc/client/","/lib/sparc/server/","/lib/ia64/server/","/lib/ia64/client/"}; 18 | static final String[] libNames = {"jvm.dll","libjvm.so","libjvm.dylib"}; 19 | static Class NativeLibraryClass; 20 | public static Unsafe unsafe; 21 | static Object oldModule; 22 | static HashMap symEntryCacheMap = new HashMap(); 23 | static LinkedHashMap HotSpotTypeFields = new LinkedHashMap(); 24 | static LinkedHashMap HotSpotTypes = new LinkedHashMap(); 25 | static LinkedHashMap HotSpotVMIntConstants = new LinkedHashMap(); 26 | static LinkedHashMap HotSpotVMLongConstants = new LinkedHashMap(); 27 | static LinkedHashMap HotSpotVMFlags = new LinkedHashMap(); 28 | static final Class currentClass = HotSpot.class; 29 | static Object NativeLibraryObject; 30 | static Method findEntryMethod; 31 | static short javaVersion; 32 | static long HeapWordSize; 33 | static long narrowOopBase; 34 | static long narrowOopShift; 35 | static String InstanceKlassTypeName; 36 | static long MethodSize; 37 | 38 | 39 | Object _calcObjectAddress; 40 | 41 | static { 42 | initialize(); 43 | } 44 | 45 | public HotSpot(){ 46 | 47 | } 48 | 49 | public static void initialize(){ 50 | if (initializeContext()){ 51 | readHotSpotVMIntConstants(); 52 | readgHotSpotVMLongConstants(); 53 | readHotSpotVMStructEntrys(); 54 | readHotSpotVMTypeEntrys(); 55 | javaVersion = (short) (Double.parseDouble(System.getProperty("java.class.version")) - 44); 56 | HeapWordSize = lookupIntConstant("HeapWordSize"); 57 | narrowOopBase = 0; 58 | narrowOopShift = 0; 59 | try { 60 | if (hasType("CompressedOops")){ 61 | narrowOopBase = unsafe.getAddress(getFieldAddress("CompressedOops","_narrow_oop._base")); 62 | narrowOopShift = unsafe.getAddress(getFieldAddress("CompressedOops","_narrow_oop._shift")); 63 | }else{ 64 | narrowOopBase = unsafe.getAddress(getFieldAddress("Universe","_narrow_oop._base")); 65 | narrowOopShift = unsafe.getAddress(getFieldAddress("Universe","_narrow_oop._shift")); 66 | } 67 | }catch (Exception e){ 68 | 69 | } 70 | readHotSpotFlags(); 71 | InstanceKlassTypeName = hasType("InstanceKlass")?"InstanceKlass":"instanceKlass"; 72 | MethodSize = hasType("methodOopDesc") ? sizeOf("methodOopDesc") : sizeOf("Method"); 73 | 74 | } 75 | } 76 | public static boolean initializeContext(){ 77 | PrintStream printStream = new PrintStream(new ByteArrayOutputStream()); 78 | PrintStream sysOut = System.out; 79 | System.setOut(printStream); 80 | try { 81 | Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe"); 82 | unsafeField.setAccessible(true); 83 | unsafe = (Unsafe) unsafeField.get(null); 84 | try { 85 | // java 5-14 86 | NativeLibraryClass = Class.forName("java.lang.ClassLoader$NativeLibrary"); 87 | Object[] createInstanceArgs = new Object[2]; 88 | Object[] loadMethodArgs = new Object[1]; 89 | Method loadMethod = null; 90 | Constructor constructor = null; 91 | 92 | constructor = getConstructor(NativeLibraryClass,new Class[]{Class.class,String.class}); 93 | 94 | if (constructor == null){ 95 | constructor = getConstructor(NativeLibraryClass,new Class[]{Class.class,String.class,boolean.class}); 96 | createInstanceArgs = new Object[3]; 97 | createInstanceArgs[2] = false; 98 | } 99 | // find load method 100 | loadMethod = getMethod(NativeLibraryClass,"load",new Class[]{String.class}); 101 | if (loadMethod == null){ 102 | loadMethod = getMethod(NativeLibraryClass,"load",new Class[]{String.class,boolean.class}); 103 | loadMethodArgs = new Object[2]; 104 | loadMethodArgs[1] = false; 105 | 106 | if (loadMethod == null){ 107 | loadMethod = getMethod(NativeLibraryClass,"load",new Class[]{String.class,boolean.class,boolean.class}); 108 | loadMethodArgs = new Object[3]; 109 | loadMethodArgs[1] = false; 110 | loadMethodArgs[2] = true; 111 | } 112 | 113 | } 114 | if (loadMethod == null){ 115 | loadMethod = getMethod(NativeLibraryClass,"load0",new Class[]{String.class}); 116 | } 117 | if (loadMethod == null){ 118 | loadMethod = getMethod(NativeLibraryClass,"load0",new Class[]{String.class,boolean.class}); 119 | loadMethodArgs = new Object[2]; 120 | loadMethodArgs[1] = false; 121 | } 122 | 123 | // find findEntry method 124 | findEntryMethod = getMethod(NativeLibraryClass,"findEntry",new Class[]{String.class}); 125 | if (findEntryMethod == null){ 126 | findEntryMethod = getMethod(NativeLibraryClass,"find",new Class[]{String.class}); 127 | } 128 | 129 | LinkedList libs = findLibraryFiles(); 130 | Iterator libsIterator= libs.iterator(); 131 | findEntryMethod.setAccessible(true); 132 | loadMethod.setAccessible(true); 133 | constructor.setAccessible(true); 134 | while (libsIterator.hasNext()){ 135 | try { 136 | String libPath = libsIterator.next().toString(); 137 | 138 | createInstanceArgs[0] = Object.class; 139 | createInstanceArgs[1] = libPath; 140 | Object _NativeLibraryObject = constructor.newInstance(createInstanceArgs); 141 | 142 | try { 143 | Field nativeLibraryContextField = NativeLibraryClass.getDeclaredField("nativeLibraryContext"); 144 | nativeLibraryContextField.setAccessible(true); 145 | Collection ctx = (Collection) nativeLibraryContextField.get(null); 146 | ctx.add(_NativeLibraryObject); 147 | }catch (Throwable e){ 148 | 149 | } 150 | 151 | loadMethodArgs[0] = libPath; 152 | loadMethod.invoke(_NativeLibraryObject,loadMethodArgs); 153 | 154 | long address = (Long) findEntryMethod.invoke(_NativeLibraryObject,new Object[]{"gHotSpotVMStructs"}); 155 | if (address != 0){ 156 | NativeLibraryObject = _NativeLibraryObject; 157 | readHotSpotVMStructEntrys(); 158 | boolean flag = isInvalidJvm(); 159 | HotSpotTypeFields.clear(); 160 | symEntryCacheMap.clear(); 161 | if (!flag){ 162 | break; 163 | } 164 | NativeLibraryObject = null; 165 | } 166 | }catch (Throwable ex){ 167 | 168 | } 169 | } 170 | 171 | } catch (ClassNotFoundException e) { 172 | //java 15-18 173 | try { 174 | NativeLibraryClass = Class.forName("jdk.internal.loader.NativeLibraries$NativeLibraryImpl"); 175 | bypassModule(); 176 | Constructor constructor = getConstructor(NativeLibraryClass,new Class[]{Class.class,String.class,boolean.class,boolean.class}); 177 | LinkedList libs = findLibraryFiles(); 178 | Iterator libsIterator= libs.iterator(); 179 | Method openLibMethod = getMethod(NativeLibraryClass,"open",new Class[0]); 180 | findEntryMethod = getMethod(NativeLibraryClass,"find",new Class[]{String.class}); 181 | findEntryMethod.setAccessible(true); 182 | openLibMethod.setAccessible(true); 183 | constructor.setAccessible(true); 184 | while (libsIterator.hasNext()){ 185 | try { 186 | String libPath = libsIterator.next().toString(); 187 | Object _NativeLibraryObject = constructor.newInstance(Object.class,libPath,false,false); 188 | openLibMethod.invoke(_NativeLibraryObject,new Object[0]); 189 | long address = (Long) findEntryMethod.invoke(_NativeLibraryObject,new Object[]{"gHotSpotVMStructs"}); 190 | if (address != 0){ 191 | NativeLibraryObject = _NativeLibraryObject; 192 | readHotSpotVMStructEntrys(); 193 | boolean flag = isInvalidJvm(); 194 | HotSpotTypeFields.clear(); 195 | symEntryCacheMap.clear(); 196 | if (!flag){ 197 | break; 198 | } 199 | NativeLibraryObject = null; 200 | } 201 | }catch (Throwable ex){ 202 | 203 | } 204 | } 205 | 206 | } catch (ClassNotFoundException classNotFoundException) { 207 | restoreModule(); 208 | } 209 | } 210 | }catch (Throwable e){ 211 | e.printStackTrace(); 212 | } 213 | System.setOut(sysOut); 214 | return NativeLibraryObject == null ? false : true; 215 | } 216 | public static boolean fileExists(String path){ 217 | try { 218 | return new File(path).exists(); 219 | }catch (Exception e){ 220 | 221 | } 222 | return false; 223 | } 224 | public static LinkedList findLibraryFiles(){ 225 | LinkedList list = new LinkedList(); 226 | String javaHome = System.getProperty("java.home"); 227 | if (javaHome != null){ 228 | for (int i = 0; i < libPaths.length; i++) { 229 | for (int j = 0; j < libNames.length; j++) { 230 | String realPath = javaHome + libPaths[i] + libNames[j]; 231 | String realJrePath = javaHome + "/jre/" +libPaths[i] + libNames[j]; 232 | if (fileExists(realPath)){ 233 | list.add(realPath); 234 | } 235 | if (fileExists(realJrePath)){ 236 | list.add(realJrePath); 237 | } 238 | } 239 | } 240 | } 241 | return list; 242 | } 243 | public static void bypassModule(){ 244 | try { 245 | Method getModuleMethod = getMethod(Class.class, "getModule", new Class[0]); 246 | if (getModuleMethod != null) { 247 | Class targetClass = NativeLibraryClass; 248 | oldModule = getModuleMethod.invoke(currentClass, new Object[]{}); 249 | Object targetModule = getModuleMethod.invoke(targetClass, new Object[]{}); 250 | unsafe.putObject(currentClass, unsafe.objectFieldOffset(Class.class.getDeclaredField("module")), targetModule); 251 | } 252 | }catch (Exception e){ 253 | e.printStackTrace(); 254 | } 255 | } 256 | public static void restoreModule(){ 257 | try { 258 | Method getModuleMethod = getMethod(Class.class, "getModule", new Class[0]); 259 | if (getModuleMethod != null) { 260 | unsafe.putObject(currentClass, unsafe.objectFieldOffset(Class.class.getDeclaredField("module")), oldModule); 261 | } 262 | }catch (Exception e){ 263 | e.printStackTrace(); 264 | } 265 | } 266 | private static Method getMethod(Class clazz, String methodName, Class[] params) { 267 | Method method = null; 268 | while (clazz!=null){ 269 | try { 270 | method = clazz.getDeclaredMethod(methodName,params); 271 | break; 272 | }catch (NoSuchMethodException e){ 273 | clazz = clazz.getSuperclass(); 274 | } 275 | } 276 | return method; 277 | } 278 | private static Constructor getConstructor(Class clazz, Class[] params){ 279 | Constructor constructor = null; 280 | try { 281 | constructor = clazz.getDeclaredConstructor(params); 282 | }catch (NoSuchMethodException e){ 283 | 284 | } 285 | return constructor; 286 | } 287 | public static byte[] readData(Object obj,int offset,int length) { 288 | byte[] temp = new byte[length]; 289 | for (int i = 0; i < length; i++) { 290 | temp[i] = unsafe.getByteVolatile(obj,offset+i); 291 | } 292 | return temp; 293 | } 294 | public static byte[] readData(long address,int length) { 295 | if (address == 0){ 296 | return null; 297 | } 298 | byte[] temp = new byte[length]; 299 | for (int i = 0; i < length; i++) { 300 | temp[i] = unsafe.getByte(address + i); 301 | } 302 | return temp; 303 | } 304 | public static void writeData(long address,byte[] data) { 305 | if (address == 0){ 306 | return; 307 | } 308 | 309 | for (int i = 0; i < data.length; i++) { 310 | unsafe.putByte(address,data[i]); 311 | address++; 312 | } 313 | 314 | } 315 | public static long readAddressByOop(long address){ 316 | if (isCompressedOopsEnabled()){ 317 | return readCompOopAddressValue(address); 318 | }else { 319 | return unsafe.getAddress(address); 320 | } 321 | } 322 | public static long readLong(long address){ 323 | if (address == 0){ 324 | return 0; 325 | } 326 | return unsafe.getLong(address); 327 | } 328 | public static int readInt(long address){ 329 | if (address == 0){ 330 | return 0; 331 | } 332 | return unsafe.getInt(address); 333 | } 334 | public static String readCString(long address){ 335 | if (address == 0){ 336 | return null; 337 | } 338 | StringBuilder stringBuilder=new StringBuilder(); 339 | byte c; 340 | while ((c=unsafe.getByte(address))!=0){ 341 | address++; 342 | stringBuilder.append(((char)c)); 343 | } 344 | return stringBuilder.toString(); 345 | } 346 | public static String readCStringByPtr(long address){ 347 | if (address == 0){ 348 | return null; 349 | } 350 | long realAddress = unsafe.getAddress(address); 351 | if (realAddress == 0){ 352 | return null; 353 | } 354 | return readCString(realAddress); 355 | } 356 | public static long findSym(String symName){ 357 | long address = 0; 358 | if (!symEntryCacheMap.containsKey(symName)){ 359 | try { 360 | address = (Long) findEntryMethod.invoke(NativeLibraryObject,new Object[]{symName}); 361 | symEntryCacheMap.put(symName,address); 362 | } catch (Throwable e) { 363 | 364 | } 365 | }else { 366 | return (Long) symEntryCacheMap.get(symName); 367 | } 368 | 369 | return address; 370 | } 371 | //ASSIGN_OFFSET_TO_64BIT_VAR = long 372 | public static long findSymAndGetLongValue(String symName){ 373 | return readLong(findSym(symName)); 374 | } 375 | public static long findSymAndGetAddressValue(String symName){ 376 | long sym = findSym(symName); 377 | if (sym!=0){ 378 | return unsafe.getAddress(sym); 379 | } 380 | throw new IllegalArgumentException("sym == " + sym); 381 | } 382 | public static void readHotSpotVMStructEntrys(){ 383 | long gHotSpotVMStructs = findSymAndGetAddressValue("gHotSpotVMStructs"); 384 | long gHotSpotVMStructEntryTypeNameOffset = findSymAndGetLongValue("gHotSpotVMStructEntryTypeNameOffset"); 385 | long gHotSpotVMStructEntryFieldNameOffset = findSymAndGetLongValue("gHotSpotVMStructEntryFieldNameOffset"); 386 | long gHotSpotVMStructEntryTypeStringOffset = findSymAndGetLongValue("gHotSpotVMStructEntryTypeStringOffset"); 387 | long gHotSpotVMStructEntryIsStaticOffset = findSymAndGetLongValue("gHotSpotVMStructEntryIsStaticOffset"); 388 | long gHotSpotVMStructEntryOffsetOffset = findSymAndGetLongValue("gHotSpotVMStructEntryOffsetOffset"); 389 | long gHotSpotVMStructEntryAddressOffset = findSymAndGetLongValue("gHotSpotVMStructEntryAddressOffset"); 390 | long gHotSpotVMStructEntryArrayStride = findSymAndGetLongValue("gHotSpotVMStructEntryArrayStride"); 391 | 392 | // typedef struct { 393 | // const char* typeName; // The type name containing the given field (example: "Klass") 394 | // const char* fieldName; // The field name within the type (example: "_name") 395 | // const char* typeString; // Quoted name of the type of this field (example: "Symbol*"; 396 | // // parsed in Java to ensure type correctness 397 | // int32_t isStatic; // Indicates whether following field is an offset or an address 398 | // uint64_t offset; // Offset of field within structure; only used for nonstatic fields 399 | // void* address; // Address of field; only used for static fields 400 | // // ("offset" can not be reused because of apparent SparcWorks compiler bug 401 | // // in generation of initializer data) 402 | // } VMStructEntry; 403 | 404 | 405 | long offset = gHotSpotVMStructs; 406 | while (offset != 0){ 407 | 408 | String typeName = null; 409 | String fieldName = null; 410 | String typeString = null; 411 | boolean isStatic = false; 412 | long offsetOffset = 0; 413 | long address = 0; 414 | 415 | String generic = null; 416 | 417 | typeName = readCStringByPtr(offset + gHotSpotVMStructEntryTypeNameOffset); 418 | if (typeName == null){ 419 | break; 420 | } 421 | 422 | StringTokenizer tokenizer = new StringTokenizer(typeName,"<>"); 423 | if (tokenizer.countTokens() == 2){ 424 | typeName = tokenizer.nextToken(); 425 | generic = tokenizer.nextToken(); 426 | } 427 | 428 | //VMStructEntry 429 | LinkedList fields = (LinkedList) HotSpotTypeFields.get(typeName); 430 | LinkedHashMap fieldInfo = new LinkedHashMap(); 431 | if (fields == null){ 432 | fields = new LinkedList(); 433 | HotSpotTypeFields.put(typeName, fields); 434 | } 435 | fields.add(fieldInfo); 436 | 437 | fieldName = readCStringByPtr(offset + gHotSpotVMStructEntryFieldNameOffset); 438 | isStatic = readInt(offset + gHotSpotVMStructEntryIsStaticOffset) == 1 ? true : false; 439 | 440 | //如果是static字段就说明是绝对地址 反则相对地址 441 | if (isStatic){ 442 | address = readLong(offset + gHotSpotVMStructEntryAddressOffset); 443 | }else { 444 | offsetOffset = readLong(offset + gHotSpotVMStructEntryOffsetOffset); 445 | } 446 | 447 | typeString = readCStringByPtr(offset + gHotSpotVMStructEntryTypeStringOffset); 448 | 449 | 450 | fieldInfo.put("generic",generic); 451 | fieldInfo.put("typeName",typeName); 452 | fieldInfo.put("fieldName",fieldName); 453 | fieldInfo.put("typeString",typeString); 454 | fieldInfo.put("isStatic",isStatic); 455 | fieldInfo.put("offset",offsetOffset); 456 | fieldInfo.put("address",address); 457 | 458 | 459 | if ("_dictionary".equals(fieldName)){ 460 | System.out.println(); 461 | } 462 | 463 | 464 | offset += gHotSpotVMStructEntryArrayStride; 465 | // System.out.println("typeName : "+typeName+" fieldName : "+fieldName+" typeString: "+typeString + " isStatic: "+isStatic+" offset : "+offsetOffset + " address : "+address); 466 | } 467 | } 468 | public static void readHotSpotVMTypeEntrys(){ 469 | long gHotSpotVMTypes = findSymAndGetAddressValue("gHotSpotVMTypes"); 470 | long gHotSpotVMTypeEntryTypeNameOffset = findSymAndGetLongValue("gHotSpotVMTypeEntryTypeNameOffset"); 471 | long gHotSpotVMTypeEntrySuperclassNameOffset = findSymAndGetLongValue("gHotSpotVMTypeEntrySuperclassNameOffset"); 472 | long gHotSpotVMTypeEntryIsOopTypeOffset = findSymAndGetLongValue("gHotSpotVMTypeEntryIsOopTypeOffset"); 473 | long gHotSpotVMTypeEntryIsIntegerTypeOffset = findSymAndGetLongValue("gHotSpotVMTypeEntryIsIntegerTypeOffset"); 474 | long gHotSpotVMTypeEntryIsUnsignedOffset = findSymAndGetLongValue("gHotSpotVMTypeEntryIsUnsignedOffset"); 475 | long gHotSpotVMTypeEntrySizeOffset = findSymAndGetLongValue("gHotSpotVMTypeEntrySizeOffset"); 476 | long gHotSpotVMTypeEntryArrayStride = findSymAndGetLongValue("gHotSpotVMTypeEntryArrayStride"); 477 | 478 | 479 | // typedef struct { 480 | // const char* typeName; // Type name (example: "Method") 481 | // const char* superclassName; // Superclass name, or null if none (example: "oopDesc") 482 | // int32_t isOopType; // Does this type represent an oop typedef? (i.e., "Method*" or 483 | // // "Klass*", but NOT "Method") 484 | // int32_t isIntegerType; // Does this type represent an integer type (of arbitrary size)? 485 | // int32_t isUnsigned; // If so, is it unsigned? 486 | // uint64_t size; // Size, in bytes, of the type 487 | // } VMTypeEntry; 488 | 489 | long offset = gHotSpotVMTypes; 490 | 491 | while (offset != 0){ 492 | String typeName = readCStringByPtr(offset + gHotSpotVMTypeEntryTypeNameOffset); 493 | 494 | if (typeName == null){ 495 | break; 496 | } 497 | String superclassName = readCStringByPtr(offset + gHotSpotVMTypeEntrySuperclassNameOffset); 498 | boolean isOopType = readInt(offset + gHotSpotVMTypeEntryIsOopTypeOffset) == 0 ? false : true; 499 | boolean isIntegerType = readInt(offset + gHotSpotVMTypeEntryIsIntegerTypeOffset) == 0 ? false : true; 500 | boolean isUnsigned = readInt(offset + gHotSpotVMTypeEntryIsUnsignedOffset) == 0 ? false : true; 501 | long size = readLong(offset + gHotSpotVMTypeEntrySizeOffset); 502 | 503 | 504 | String generic = null; 505 | 506 | StringTokenizer tokenizer = new StringTokenizer(typeName,"<>"); 507 | if (tokenizer.countTokens() == 2){ 508 | typeName = tokenizer.nextToken(); 509 | } 510 | 511 | 512 | LinkedHashMap typePropertys = new LinkedHashMap(); 513 | HotSpotTypes.put(typeName,typePropertys); 514 | 515 | 516 | typePropertys.put("generic",tokenizer.nextToken()); 517 | typePropertys.put("typeName",typeName); 518 | typePropertys.put("superclassName",superclassName); 519 | typePropertys.put("isOopType",isOopType); 520 | typePropertys.put("isIntegerType",isIntegerType); 521 | typePropertys.put("isUnsigned",isUnsigned); 522 | typePropertys.put("size",size); 523 | typePropertys.put("fields",HotSpotTypeFields.get(typeName)); 524 | 525 | 526 | offset = offset + gHotSpotVMTypeEntryArrayStride; 527 | // System.out.println("typeName: "+typeName+" superclassName: "+superclassName+" isOopType: "+isOopType + "isIntegerType: "+isIntegerType + " isUnsigned: "+isUnsigned + " size: "+size + " fields: " );//+ HotSpotTypeFields.get(typeName)); 528 | } 529 | 530 | } 531 | public static void readHotSpotVMIntConstants(){ 532 | long gHotSpotVMIntConstants = findSymAndGetLongValue("gHotSpotVMIntConstants"); 533 | long gHotSpotVMIntConstantEntryNameOffset = findSymAndGetLongValue("gHotSpotVMIntConstantEntryNameOffset"); 534 | long gHotSpotVMIntConstantEntryValueOffset = findSymAndGetLongValue("gHotSpotVMIntConstantEntryValueOffset"); 535 | long gHotSpotVMIntConstantEntryArrayStride = findSymAndGetLongValue("gHotSpotVMIntConstantEntryArrayStride"); 536 | 537 | // typedef struct { 538 | // const char* name; // Name of constant (example: "_thread_in_native") 539 | // int32_t value; // Value of constant 540 | // } VMIntConstantEntry; 541 | 542 | long offset = gHotSpotVMIntConstants; 543 | 544 | while (offset != 0){ 545 | String name = readCStringByPtr(offset + gHotSpotVMIntConstantEntryNameOffset); 546 | 547 | if (name == null){ 548 | break; 549 | } 550 | 551 | int value = readInt(offset + gHotSpotVMIntConstantEntryValueOffset); 552 | HotSpotVMIntConstants.put(name, value); 553 | offset = offset + gHotSpotVMIntConstantEntryArrayStride; 554 | // System.out.println("name: "+name+" value: "+value); 555 | } 556 | 557 | 558 | } 559 | public static void readgHotSpotVMLongConstants(){ 560 | long gHotSpotVMLongConstants = findSymAndGetAddressValue("gHotSpotVMLongConstants"); 561 | long gHotSpotVMLongConstantEntryNameOffset = findSymAndGetLongValue("gHotSpotVMLongConstantEntryNameOffset"); 562 | long gHotSpotVMLongConstantEntryValueOffset = findSymAndGetLongValue("gHotSpotVMLongConstantEntryValueOffset"); 563 | long gHotSpotVMLongConstantEntryArrayStride = findSymAndGetLongValue("gHotSpotVMLongConstantEntryArrayStride"); 564 | 565 | // typedef struct { 566 | // const char* name; // Name of constant (example: "_thread_in_native") 567 | // uint64_t value; // Value of constant 568 | // } VMLongConstantEntry; 569 | 570 | long offset = gHotSpotVMLongConstants; 571 | 572 | while (offset != 0){ 573 | String name = readCStringByPtr(offset + gHotSpotVMLongConstantEntryNameOffset); 574 | if (name == null){ 575 | break; 576 | } 577 | long value = readLong(offset + gHotSpotVMLongConstantEntryValueOffset); 578 | HotSpotVMLongConstants.put(name, value); 579 | offset = offset + gHotSpotVMLongConstantEntryArrayStride; 580 | // System.out.println("name: "+name+" value: "+value); 581 | } 582 | 583 | } 584 | public static int lookupIntConstant(String constantName) { 585 | return (Integer) HotSpotVMIntConstants.get(constantName); 586 | } 587 | public static long lookupLongConstant(String constantName) { 588 | return (Long) HotSpotVMLongConstants.get(constantName); 589 | } 590 | public static int getOopSize(){ 591 | try { 592 | return lookupIntConstant("oopSize"); 593 | }catch (Exception e){ 594 | return (int) sizeOf("char*"); 595 | } 596 | } 597 | public static String getCurrentPid() { 598 | String name = ManagementFactory.getRuntimeMXBean().getName(); 599 | return name.split("@")[0]; 600 | } 601 | public static boolean hasType(String typeName){ 602 | boolean flag = (HotSpotTypeFields.containsKey(typeName) || HotSpotTypes.containsKey(typeName)); 603 | return flag; 604 | } 605 | public static Object getTypeProperty(String typeName,String property){ 606 | if (hasType(typeName)){ 607 | Map typeProperty = (Map) HotSpotTypes.get(typeName); 608 | return typeProperty.get(property); 609 | } 610 | return null; 611 | } 612 | public static boolean isOopType(String typeName){ 613 | return (Boolean) getTypeProperty(typeName,"isOopType"); 614 | } 615 | public static boolean isUnsigned(String typeName){ 616 | return (Boolean) getTypeProperty(typeName,"isUnsigned"); 617 | } 618 | public static boolean isIntegerType(String typeName){ 619 | return (Boolean) getTypeProperty(typeName,"isIntegerType"); 620 | } 621 | public static long sizeOf(String typeName){ 622 | if (typeName.trim().endsWith("*")){ 623 | return unsafe.addressSize(); 624 | } 625 | return (Long) getTypeProperty(typeName,"size"); 626 | } 627 | public static String getSuperclassName(String typeName){ 628 | return (String) getTypeProperty(typeName,"superclassName"); 629 | } 630 | public static List getFieldInfoOfType(String typeName){ 631 | return (List) getTypeProperty(typeName,"fields"); 632 | } 633 | public static String[] getFields(String typeName){ 634 | String[] fields = null; 635 | if (hasType(typeName)){ 636 | LinkedList fieldList = (LinkedList) HotSpotTypeFields.get(typeName); 637 | fields = new String[fieldList.size()]; 638 | for (int i = 0; i < fields.length; i++) { 639 | Map fieldInfo = (Map) fieldList.get(i); 640 | fields[i] = (String) fieldInfo.get("fieldName"); 641 | } 642 | } 643 | return fields; 644 | } 645 | public static Object getFieldProperty(String typeName,String fieldName,String property){ 646 | if (hasType(typeName)){ 647 | LinkedList fieldList = (LinkedList) HotSpotTypeFields.get(typeName); 648 | if (fieldList != null){ 649 | Iterator iterator = fieldList.iterator(); 650 | while (iterator.hasNext()){ 651 | Map fieldInfo = (Map) iterator.next(); 652 | if (fieldName.equals(fieldInfo.get("fieldName"))){ 653 | return fieldInfo.get(property); 654 | } 655 | } 656 | } 657 | } 658 | 659 | return null; 660 | } 661 | public static String getFieldType(String typeName,String fieldName){ 662 | return (String) getFieldProperty(typeName,fieldName,"typeString"); 663 | } 664 | public static boolean isStaticField(String typeName,String fieldName){ 665 | return (Boolean) getFieldProperty(typeName,fieldName,"isStatic"); 666 | } 667 | public static long getFieldAddress(String typeName,String fieldName){ 668 | Object value = getFieldProperty(typeName,fieldName,"address"); 669 | if (value == null){ 670 | value = HotSpotVMIntConstants.get(typeName + "::" + fieldName); 671 | } 672 | return ((Number) value).longValue(); 673 | } 674 | public static long getFieldAddressOr(String typeName,String fieldName,String fieldName2){ 675 | try{ 676 | return getFieldAddress(typeName,fieldName); 677 | }catch (Exception e){ 678 | return getFieldAddress(typeName,fieldName2); 679 | } 680 | } 681 | public static long getFieldOffset(String typeName,String fieldName){ 682 | Object value = getFieldProperty(typeName,fieldName,"offset"); 683 | if (value == null){ 684 | value = HotSpotVMIntConstants.get(typeName + "::" + fieldName); 685 | } 686 | return ((Number) value).longValue(); 687 | } 688 | public static long getFieldOffsetOr(String typeName,String... fieldNames){ 689 | long offset = 0; 690 | boolean flag = false; 691 | for (int i = 0; i < fieldNames.length; i++) { 692 | try { 693 | offset = getFieldOffset(typeName,fieldNames[i]); 694 | flag = true; 695 | break; 696 | }catch (NullPointerException e){ 697 | continue; 698 | } 699 | } 700 | if (!flag){ 701 | throw new NullPointerException(typeName); 702 | } 703 | 704 | return offset; 705 | } 706 | public static long getFieldOffsetOr(String typeName,String fieldName,String fieldName2){ 707 | try{ 708 | return getFieldOffset(typeName,fieldName); 709 | }catch (Exception e){ 710 | return getFieldOffset(typeName,fieldName2); 711 | } 712 | } 713 | 714 | public static boolean hasFields(String typeName,String... fieldNames){ 715 | for (int i = 0; i < fieldNames.length; i++) { 716 | String fieldName = fieldNames[i]; 717 | if (!hasField(typeName,fieldName)){ 718 | return false; 719 | } 720 | } 721 | return true; 722 | } 723 | public static boolean hasField(String typeName,String fieldName){ 724 | List fieldList = (List) HotSpotTypeFields.get(typeName); 725 | if (fieldList != null){ 726 | for (int i = 0; i < fieldList.size(); i++) { 727 | Map fieldInfo = (Map) fieldList.get(i); 728 | if (fieldInfo!=null && fieldName.equals(fieldInfo.get("fieldName"))){ 729 | return true; 730 | } 731 | } 732 | } 733 | return false; 734 | } 735 | public static void readHotSpotFlags(){ 736 | String FlagVmName = "Flag"; 737 | if (!hasType(FlagVmName)){ 738 | FlagVmName = "JVMFlag"; 739 | } 740 | 741 | int numFlags = unsafe.getInt(getFieldAddress(FlagVmName,"numFlags")); 742 | long typeField = 0; 743 | long nameField = 0; 744 | long addrField = 0; 745 | long kindField = 0; 746 | long _flagsField = 0; 747 | 748 | try { 749 | typeField = getFieldOffset(FlagVmName,"type"); 750 | nameField = getFieldOffset(FlagVmName,"name"); 751 | addrField = getFieldOffset(FlagVmName,"addr"); 752 | kindField = getFieldOffset(FlagVmName,"kind"); 753 | }catch (Exception e){ 754 | typeField = getFieldOffset(FlagVmName,"_type"); 755 | nameField = getFieldOffset(FlagVmName,"_name"); 756 | addrField = getFieldOffset(FlagVmName,"_addr"); 757 | _flagsField = getFieldOffset(FlagVmName,"_flags"); 758 | } 759 | 760 | long address = unsafe.getAddress(getFieldAddress(FlagVmName,"flags")); 761 | 762 | String _typeTypeString = null; 763 | 764 | if ((_typeTypeString = getFieldType(FlagVmName,"_type"))==null){ 765 | _typeTypeString = getFieldType(FlagVmName,"type"); 766 | } 767 | 768 | for (int i = 0; i < (numFlags -1); i++) { 769 | Object type = null; 770 | if (_typeTypeString.contains("char*")){ 771 | type = readCStringByPtr(address + typeField); 772 | }else { 773 | type = unsafe.getInt(address + typeField); 774 | } 775 | 776 | 777 | String name = readCStringByPtr(address + nameField); 778 | long addr = unsafe.getAddress(address + addrField); 779 | String kind = null; 780 | int flags = 0; 781 | 782 | if (kindField == 0){ 783 | if (_flagsField != 0){ 784 | flags = unsafe.getShort(addr + _flagsField) & 0xFFFF; 785 | } 786 | }else { 787 | kind = readCStringByPtr(address + kindField); 788 | } 789 | 790 | // System.out.println("flag type : " + type+" name : "+name + " addr : "+addr+ " kind : "+kind +" flags :"+flags); 791 | 792 | LinkedHashMap flagInfo = new LinkedHashMap(); 793 | 794 | flagInfo.put("type",type); 795 | flagInfo.put("name",name); 796 | flagInfo.put("addr",addr); 797 | flagInfo.put("kind",kind); 798 | flagInfo.put("flags",flags); 799 | 800 | HotSpotVMFlags.put(name,flagInfo); 801 | 802 | address = address + sizeOf(FlagVmName); 803 | } 804 | } 805 | public static boolean isCompressedOopsEnabled(){ 806 | Map flagInfo = (Map) HotSpotVMFlags.get("UseCompressedOops"); 807 | try { 808 | if (flagInfo != null){ 809 | long addr = (Long) flagInfo.get("addr"); 810 | return unsafe.getByte(addr) == 1? true :false; 811 | } 812 | }catch (Exception e){ 813 | 814 | } 815 | return false; 816 | } 817 | public static long getArrayOopLength(long arrayOop){ 818 | long lengthOffset = 0; 819 | if (isCompressedOopsEnabled()){ 820 | lengthOffset = sizeOf("arrayOopDesc") - sizeOf("jint"); 821 | } else if (javaVersion == 5){ 822 | lengthOffset = sizeOf("arrayOopDesc") - getOopSize(); 823 | }else { 824 | lengthOffset = sizeOf("arrayOopDesc"); 825 | } 826 | return unsafe.getInt(arrayOop + lengthOffset) & 0xFFFFFF; 827 | } 828 | public static long getHeapOopSize(){ 829 | 830 | if (isCompressedOopsEnabled()) { 831 | return sizeOf("jint"); 832 | } else { 833 | return getOopSize(); 834 | } 835 | } 836 | public static String readSymbol(long symbol){ 837 | long _lengthField = 0; //str len 838 | long _bodyField = 0; //str 839 | 840 | int strlen = 0; 841 | 842 | if (hasType("symbolOopDesc")){ 843 | _lengthField = getFieldOffset("symbolOopDesc","_length"); 844 | _bodyField = getFieldOffset("symbolOopDesc","_body"); 845 | 846 | strlen = unsafe.getShort(symbol + _lengthField); 847 | }else if (hasType("Symbol")){ 848 | try { 849 | _lengthField = getFieldOffset("Symbol","_length"); 850 | strlen = unsafe.getShort(symbol + _lengthField); 851 | }catch (Exception e){ 852 | _lengthField = getFieldOffset("Symbol","_length_and_refcount"); 853 | strlen = (unsafe.getInt(symbol + _lengthField) >> 16); 854 | } 855 | 856 | _bodyField = getFieldOffset("Symbol","_body"); 857 | }else { 858 | throw new UnsupportedOperationException("Sym not found"); 859 | } 860 | 861 | if (strlen == 0){ 862 | return new String(); 863 | }else { 864 | return new String(readData(symbol + _bodyField, strlen)); 865 | } 866 | } 867 | public static long alignUp(long size, long alignment) { 868 | return ((size + alignment) - 1) & ((alignment - 1) ^ -1); 869 | } 870 | private static long arrayHeaderSizeInBytes() { 871 | long headerSize = 0; 872 | if (isCompressedOopsEnabled()) { 873 | headerSize = sizeOf("arrayOopDesc"); 874 | } else { 875 | headerSize = alignUp(sizeOf("arrayOopDesc") + sizeOf("jint"), HeapWordSize); 876 | } 877 | return headerSize; 878 | } 879 | private static long arrayHeaderSize() { 880 | 881 | return arrayHeaderSizeInBytes() / HeapWordSize; 882 | } 883 | public static long readCompOopAddressValue(long address) { 884 | long value = unsafe.getInt(address); 885 | if (value < 0 ){ 886 | value = value & 0xFFFFFFFFL; 887 | } 888 | if (value != 0) { 889 | return narrowOopBase + (value << narrowOopShift); 890 | } 891 | return value; 892 | } 893 | public static long unpackCompOopAddress(int address) { 894 | long value = address; 895 | if (value < 0 ){ 896 | value = value & 0xFFFFFFFFL; 897 | } 898 | if (value != 0) { 899 | return narrowOopBase + (value << narrowOopShift); 900 | } 901 | return value; 902 | } 903 | public static int compOopAddress(long address) { 904 | long value = address; 905 | int ret = 0; 906 | if (value != 0) { 907 | value = narrowOopBase - (value >> narrowOopShift); 908 | ret = (int) (value&0xFFFFFFFF); 909 | } 910 | return ret; 911 | } 912 | public static long getOopHandleAt(long arrayOop,long index) { 913 | long oopSize = sizeOf("oopDesc"); 914 | long offset = arrayHeaderSize() * HeapWordSize + (getHeapOopSize() * index); 915 | if (isCompressedOopsEnabled()) { 916 | return readCompOopAddressValue(arrayOop + offset); 917 | } 918 | return unsafe.getAddress(arrayOop + offset); 919 | } 920 | public static long getObjectAddress(Object obj){ 921 | if (obj == null){ 922 | return 0; 923 | } 924 | try { 925 | Field field = currentClass.getDeclaredField("_calcObjectAddress"); 926 | // verify 927 | HotSpot h =new HotSpot(); 928 | h._calcObjectAddress = null; 929 | int check1 = unsafe.getInt(h,unsafe.objectFieldOffset(field)); 930 | h._calcObjectAddress = obj; 931 | int check2 = unsafe.getInt(h,unsafe.objectFieldOffset(field)); 932 | 933 | if (check1 !=0 || check1 == check2 || check2 == 0){ 934 | h._calcObjectAddress = null; 935 | throw new UnsupportedOperationException("old hotspot version"); 936 | } 937 | long address = 0; 938 | if (isCompressedOopsEnabled()){ 939 | int compressedOops = unsafe.getInt(h,unsafe.objectFieldOffset(field)); 940 | address = unpackCompOopAddress(compressedOops); 941 | }else { 942 | if (getOopSize() == 8){ 943 | address = unsafe.getLong(h,unsafe.objectFieldOffset(field)); 944 | }else { 945 | address = unsafe.getInt(h,unsafe.objectFieldOffset(field)); 946 | } 947 | } 948 | h._calcObjectAddress = null; 949 | return address; 950 | } catch (NoSuchFieldException e) { 951 | throw new UnsupportedOperationException(e); 952 | } 953 | } 954 | public static LinkedList getMethodsOldVersionByInstanceKlass(long instanceKlass){ 955 | long oopSize = sizeOf("oopDesc"); 956 | LinkedList methods = new LinkedList(); 957 | long array = unsafe.getAddress(instanceKlass + getFieldOffset(InstanceKlassTypeName,"_methods") + oopSize); 958 | 959 | if (array == 0){ 960 | return methods; 961 | } 962 | long methodLength = getArrayOopLength(array); 963 | long constantPoolOopSize = hasType("ConstantPool")?sizeOf("ConstantPool"):sizeOf("constantPoolOopDesc"); 964 | for (int i = 0; i < methodLength; i++) { 965 | long methodOopDesc = getOopHandleAt(array,i); 966 | if (methodOopDesc == 0 || methodOopDesc == 1){ 967 | continue; 968 | } 969 | long constMethodOopDesc = unsafe.getAddress(methodOopDesc + getFieldOffset("methodOopDesc","_constMethod")); 970 | long constantPoolOop = 0; 971 | long _name_index = unsafe.getShort(constMethodOopDesc + getFieldOffset("constMethodOopDesc","_name_index"))& 0xffff; 972 | long _signature_index = unsafe.getShort(constMethodOopDesc + getFieldOffset("constMethodOopDesc","_signature_index"))& 0xffff; 973 | long methodNameSymbolAddress = 0; 974 | long methodSignatureSymbolAddress = 0; 975 | 976 | if(hasField("methodOopDesc","_constants")){ 977 | constantPoolOop = unsafe.getAddress(methodOopDesc + getFieldOffset("methodOopDesc","_constants")); 978 | methodNameSymbolAddress = unsafe.getAddress(( constantPoolOop + getOopSize() * _name_index) + constantPoolOopSize); 979 | methodSignatureSymbolAddress = unsafe.getAddress(( constantPoolOop + getOopSize() * _signature_index) + constantPoolOopSize); 980 | }else { 981 | constantPoolOop = unsafe.getAddress(constMethodOopDesc + getFieldOffset("constMethodOopDesc","_constants")); 982 | methodNameSymbolAddress = unsafe.getAddress(( constantPoolOop + getOopSize() * _name_index) + constantPoolOopSize) ^ 1; 983 | methodSignatureSymbolAddress = unsafe.getAddress(( constantPoolOop + getOopSize() * _signature_index) + constantPoolOopSize) ^ 1; 984 | } 985 | 986 | String methodName = readSymbol(methodNameSymbolAddress); 987 | String methodSignature = readSymbol(methodSignatureSymbolAddress); 988 | 989 | long _code = unsafe.getAddress(methodOopDesc + getFieldOffset("methodOopDesc","_code")); 990 | long _from_compiled_entry = unsafe.getAddress(methodOopDesc + getFieldOffsetOr("methodOopDesc","_from_compiled_entry","_from_compiled_code_entry_point")); 991 | long _from_interpreted_entry = unsafe.getAddress(methodOopDesc + getFieldOffsetOr("methodOopDesc","_from_interpreted_entry","_interpreter_entry")); 992 | int _access_flags = unsafe.getInt(methodOopDesc + getFieldOffset("methodOopDesc","_access_flags")); 993 | 994 | 995 | LinkedHashMap methodInfo = new LinkedHashMap(); 996 | methodInfo.put("name", methodName); 997 | methodInfo.put("signature", methodSignature); 998 | methodInfo.put("_code", _code); 999 | methodInfo.put("_access_flags", _access_flags); 1000 | methodInfo.put("_from_compiled_entry", _from_compiled_entry); 1001 | methodInfo.put("_from_interpreted_entry", _from_interpreted_entry); 1002 | methodInfo.put("name_index", _name_index); 1003 | methodInfo.put("signature_index", _signature_index); 1004 | methodInfo.put("address", methodOopDesc); 1005 | methodInfo.put("constantPoolOop", constantPoolOop); 1006 | methodInfo.put("slot", i); 1007 | methods.add(methodInfo); 1008 | } 1009 | return methods; 1010 | } 1011 | public static LinkedList getMethodsNewVersionByInstanceKlass(long instanceKlass){ 1012 | LinkedList methods = new LinkedList(); 1013 | long oopSize = getOopSize(); 1014 | long constantPoolSize = sizeOf("ConstantPool"); 1015 | 1016 | long _methodsField = getFieldOffset(InstanceKlassTypeName,"_methods"); 1017 | long arrayLengthField = getFieldOffset("Array","_length"); 1018 | long arrayDataField = getFieldOffset("Array","_data[0]"); 1019 | 1020 | long _constMethodField = getFieldOffset("Method","_constMethod"); 1021 | long _codeField = getFieldOffset("Method","_code"); 1022 | long _from_interpreted_entryField = getFieldOffset("Method","_from_interpreted_entry"); 1023 | long _from_compiled_entryField = getFieldOffset("Method","_from_compiled_entry"); 1024 | long _access_flagsField = getFieldOffset("Method","_access_flags"); 1025 | 1026 | 1027 | long _constantsField = getFieldOffset("ConstMethod","_constants"); 1028 | long _name_indexField = getFieldOffset("ConstMethod","_name_index"); 1029 | long _signature_indexField = getFieldOffset("ConstMethod","_signature_index"); 1030 | 1031 | long methodArray = unsafe.getAddress(instanceKlass + _methodsField); 1032 | 1033 | if (methodArray == 0){ 1034 | return methods; 1035 | } 1036 | 1037 | long methodLength = unsafe.getInt(methodArray + arrayLengthField); 1038 | 1039 | for (int i = 0; i < methodLength; i++) { 1040 | long mehtod = unsafe.getAddress( methodArray + arrayDataField + (i * oopSize)); 1041 | 1042 | long constMethod = unsafe.getAddress(mehtod + _constMethodField); 1043 | long _code = unsafe.getAddress(mehtod + _codeField); 1044 | int _access_flags = unsafe.getInt(mehtod + _access_flagsField); 1045 | long _from_compiled_entry = unsafe.getAddress(mehtod + _from_compiled_entryField); 1046 | long _from_interpreted_entry = unsafe.getAddress(mehtod + _from_interpreted_entryField); 1047 | 1048 | long constantPool = unsafe.getAddress(constMethod + _constantsField); 1049 | long name_index = unsafe.getShort(constMethod + _name_indexField) & 0xffff; 1050 | long signature_index = unsafe.getShort(constMethod + _signature_indexField) & 0xffff; 1051 | 1052 | long nameSym = unsafe.getAddress(constantPool + constantPoolSize + (name_index * oopSize)); 1053 | long signatureSym = unsafe.getAddress(constantPool + constantPoolSize + (signature_index * oopSize)); 1054 | 1055 | 1056 | if (nameSym !=0 && signatureSym !=0){ 1057 | String methodName = readSymbol(nameSym); 1058 | String methodSignature = readSymbol(signatureSym); 1059 | 1060 | LinkedHashMap methodInfo = new LinkedHashMap(); 1061 | methodInfo.put("name", methodName); 1062 | methodInfo.put("signature", methodSignature); 1063 | methodInfo.put("_code", _code); 1064 | methodInfo.put("_access_flags", _access_flags); 1065 | methodInfo.put("_from_compiled_entry", _from_compiled_entry); 1066 | methodInfo.put("_from_interpreted_entry", _from_interpreted_entry); 1067 | methodInfo.put("name_index", name_index); 1068 | methodInfo.put("signature_index", signature_index); 1069 | methodInfo.put("address", mehtod); 1070 | methodInfo.put("constantPoolOop", constantPool); 1071 | methodInfo.put("slot", i); 1072 | 1073 | 1074 | methods.add(methodInfo); 1075 | } 1076 | } 1077 | return methods; 1078 | } 1079 | public static LinkedList getMethodsByInstanceKlass(long instanceKlass){ 1080 | if (instanceKlass == 0){ 1081 | return new LinkedList(); 1082 | } 1083 | 1084 | String methodsType = getFieldType(InstanceKlassTypeName,"_methods"); 1085 | if ("Array*".equals(methodsType)){ 1086 | return getMethodsNewVersionByInstanceKlass(instanceKlass); 1087 | }else { 1088 | return getMethodsOldVersionByInstanceKlass(instanceKlass); 1089 | } 1090 | 1091 | } 1092 | public static LinkedList getAllLoadedClassesOldVersion(boolean readMethods) { 1093 | 1094 | LinkedList classes = new LinkedList(); 1095 | 1096 | long tableSizeField = getFieldOffset("BasicHashtable","_table_size"); 1097 | 1098 | long bucketsField = getFieldOffset("BasicHashtable","_buckets"); 1099 | 1100 | long bucketSize = sizeOf("HashtableBucket"); 1101 | 1102 | long _entry = getFieldOffset("HashtableBucket","_entry"); 1103 | 1104 | long _dictionary = unsafe.getAddress(getFieldAddress("SystemDictionary","_dictionary")); 1105 | 1106 | long BasicHashtableEntry__next = getFieldOffset("BasicHashtableEntry","_next"); 1107 | 1108 | 1109 | if (_dictionary == 0){ 1110 | return classes; 1111 | } 1112 | 1113 | int tblSize = unsafe.getInt(_dictionary + tableSizeField); 1114 | 1115 | long literalField = 0; 1116 | 1117 | try { 1118 | literalField = getFieldOffset("IntptrHashtableEntry","_literal"); 1119 | }catch (Exception e){ 1120 | literalField = getFieldOffset("HashtableEntry","_literal"); 1121 | } 1122 | 1123 | long KlassNameField = getFieldOffset("Klass","_name"); //Symbol* 1124 | long _class_loaderField = getFieldOffsetOr(InstanceKlassTypeName,"_class_loader","_class_loader_data"); //Symbol* 1125 | 1126 | long classLoaderData_class_loaderField = hasField("ClassLoaderData","_class_loader") ? getFieldOffset("ClassLoaderData","_class_loader") : -1; 1127 | 1128 | long oopSize = sizeOf("oopDesc"); 1129 | 1130 | long klassSize = sizeOf("Klass"); 1131 | 1132 | for (int index = 0; index < tblSize; index++) { 1133 | long bucket = unsafe.getAddress(_dictionary + bucketsField) + bucketSize * index; 1134 | bucket = unsafe.getAddress(bucket) + _entry; 1135 | for (long probe = bucket; probe != 0; probe = unsafe.getAddress(probe + BasicHashtableEntry__next)) { 1136 | long literal = unsafe.getAddress(probe + literalField);// InstanceKlass 1137 | long symbol; 1138 | long classloader; 1139 | if (javaVersion >= 8){ 1140 | symbol = unsafe.getAddress(KlassNameField + literal); 1141 | classloader = unsafe.getAddress(_class_loaderField + literal); 1142 | if (classLoaderData_class_loaderField != -1){ //ClassLoaderData 1143 | classloader = unsafe.getAddress(classloader + classLoaderData_class_loaderField); 1144 | } 1145 | }else { 1146 | symbol = unsafe.getAddress(KlassNameField + literal + oopSize); 1147 | classloader = unsafe.getAddress(_class_loaderField + literal + oopSize); 1148 | } 1149 | String className = readSymbol(symbol); 1150 | 1151 | if (className != null){ 1152 | className = className.replace("/","."); 1153 | } 1154 | 1155 | LinkedHashMap classInfo = new LinkedHashMap(); 1156 | classInfo.put("className",className); 1157 | classInfo.put("classLoader",classloader); 1158 | classInfo.put("instanceKlass",literal); 1159 | if (readMethods){ 1160 | classInfo.put("methods",getMethodsByInstanceKlass(literal)); 1161 | } 1162 | classes.add(classInfo); 1163 | 1164 | } 1165 | } 1166 | return classes; 1167 | } 1168 | public static LinkedList getAllLoadedClassesNewVersion(boolean readMethods){ 1169 | LinkedList classes = new LinkedList(); 1170 | long _klassesField = getFieldOffset("ClassLoaderData","_klasses"); 1171 | long _class_loaderField = getFieldOffset("ClassLoaderData","_class_loader"); 1172 | long _nextField = getFieldOffset("ClassLoaderData","_next"); 1173 | 1174 | long _next_linkField = getFieldOffset("Klass","_next_link"); 1175 | 1176 | long klassNameField = getFieldOffset("Klass","_name"); //Symbol* 1177 | 1178 | long classIndex = 0; 1179 | long classLoaderDataIndex = 0; 1180 | for (long classLoaderData = unsafe.getAddress(getFieldAddress("ClassLoaderDataGraph","_head")); 1181 | classLoaderData != 0; classLoaderData = unsafe.getAddress(classLoaderData + _nextField)){ 1182 | 1183 | long classloader = unsafe.getAddress(classLoaderData + _class_loaderField); 1184 | 1185 | for (long instanceKlass = unsafe.getAddress(classLoaderData + _klassesField); 1186 | instanceKlass != 0; instanceKlass = unsafe.getAddress(instanceKlass + _next_linkField)) { 1187 | 1188 | long classNameSym = unsafe.getAddress(instanceKlass + klassNameField); 1189 | String className = readSymbol(classNameSym); 1190 | if (className != null){ 1191 | className = className.replace("/","."); 1192 | } 1193 | 1194 | LinkedHashMap classInfo = new LinkedHashMap(); 1195 | classInfo.put("className",className); 1196 | classInfo.put("classLoader",classloader); 1197 | classInfo.put("instanceKlass",instanceKlass); 1198 | if (readMethods){ 1199 | classInfo.put("methods",getMethodsByInstanceKlass(instanceKlass)); 1200 | } 1201 | classes.add(classInfo); 1202 | classIndex++; 1203 | } 1204 | classLoaderDataIndex++; 1205 | } 1206 | return classes; 1207 | } 1208 | public static LinkedList getAllLoadedClasses(boolean readMethods){ 1209 | if (hasField("ClassLoaderData","_klasses")){ 1210 | return getAllLoadedClassesNewVersion(readMethods); 1211 | }else { 1212 | return getAllLoadedClassesOldVersion(readMethods); 1213 | } 1214 | } 1215 | public static boolean isInvalidJvm(){ 1216 | try { 1217 | if (unsafe.getAddress(getFieldAddress("SystemDictionary","_dictionary")) != 0){ 1218 | return false; 1219 | } 1220 | }catch (NullPointerException e){ 1221 | 1222 | } 1223 | 1224 | try { 1225 | if (unsafe.getAddress(getFieldAddress("ClassLoaderDataGraph","_head")) != 0){ 1226 | return false; 1227 | } 1228 | }catch (NullPointerException e){ 1229 | 1230 | } 1231 | 1232 | 1233 | return true; 1234 | } 1235 | public static long getKlass(Class clazz) { 1236 | int _klass_offset = 0; 1237 | int oopSize = getOopSize(); 1238 | if (hasField("java_lang_Class","_klass_offset")){ 1239 | _klass_offset = unsafe.getInt(getFieldAddress("java_lang_Class","_klass_offset")); 1240 | } 1241 | long classAddress = getObjectAddress(clazz); 1242 | 1243 | if (javaVersion < 8){ 1244 | LinkedList classList = getAllLoadedClasses(false); 1245 | String clazzName = clazz.getName(); 1246 | for (int i = 0; i 1000; i++) { 1701 | abc--; 1702 | } 1703 | if (this2 == null){ 1704 | return; 1705 | } 1706 | System.out.println("Access hook exit"); 1707 | System.out.println("this ->" + this2); 1708 | System.out.println("exit status ->" + status); 1709 | } 1710 | 1711 | 1712 | public static long testCall(long v){ 1713 | return 1234; 1714 | } 1715 | 1716 | public static String getProperty(String key) { 1717 | return "hacker"; 1718 | } 1719 | 1720 | public long getId() { 1721 | return 88888888L; 1722 | } 1723 | 1724 | /** 1725 | * 直接替换方法的解释地址与编译地址 1726 | * */ 1727 | public static void testReplaceAddress() throws Throwable{ 1728 | long hotspotClassHandle = getKlass(HotSpot.class); 1729 | long runtimeClassHandle = getKlass(Runtime.class); 1730 | 1731 | boolean isHighVer = !hasType("methodOop"); 1732 | 1733 | long exitMethodHandle = getMethodByKlass(runtimeClassHandle,"exit",getMethodSignature(Runtime.class.getMethod("exit", int.class))); 1734 | 1735 | long hotspotExitHandle = getMethodByKlass(hotspotClassHandle,"exit",getMethodSignature(HotSpot.class.getMethod("exit", Object.class, int.class))); 1736 | 1737 | 1738 | System.out.println("Hotspot Class Address : 0x" + Long.toHexString(hotspotClassHandle)); 1739 | System.out.println("Runtime Class Address : 0x" + Long.toHexString(runtimeClassHandle)); 1740 | System.out.println("被hook的方法地址 Runtime->exit : 0x" + Long.toHexString(exitMethodHandle)); 1741 | //第一个参数指向this 1742 | System.out.println("hook的方法地址 Hotspot->exit : 0x" + Long.toHexString(hotspotExitHandle)); 1743 | 1744 | 1745 | long _codeField = 0; 1746 | long _from_interpreted_entryField = 0; 1747 | long _from_compiled_entry_entryField = 0; 1748 | long _access_flagsField = 0; 1749 | if (isHighVer){ 1750 | _from_interpreted_entryField = getFieldOffset("Method","_from_interpreted_entry"); 1751 | _from_compiled_entry_entryField = getFieldOffset("Method","_from_compiled_entry"); 1752 | _codeField = getFieldOffset("Method","_code"); 1753 | _access_flagsField = getFieldOffset("Method","_access_flags"); 1754 | }else { 1755 | _codeField = getFieldOffset("methodOopDesc","_code"); 1756 | _from_interpreted_entryField = getFieldOffsetOr("methodOopDesc","_from_interpreted_entry","_interpreter_entry"); 1757 | _from_compiled_entry_entryField = getFieldOffsetOr("methodOopDesc","_from_compiled_entry","_from_compiled_code_entry_point"); 1758 | _access_flagsField = getFieldOffset("methodOopDesc","_access_flags"); 1759 | } 1760 | 1761 | boolean compileFlag = false; 1762 | 1763 | long hotspotNMethodAddress = 0; 1764 | 1765 | for (int i = 0; i < 100000; i++) { 1766 | exit(null,1); 1767 | hotspotNMethodAddress = unsafe.getAddress(hotspotExitHandle + _codeField); 1768 | if (hotspotNMethodAddress != 0){ 1769 | break; 1770 | } 1771 | } 1772 | if (hotspotNMethodAddress == 0){ 1773 | System.out.println("未能成功编译方法 无法继续执行"); 1774 | return; 1775 | } 1776 | 1777 | System.out.println("jit地址 : 0x" + Long.toHexString(unsafe.getAddress(hotspotExitHandle + _from_compiled_entry_entryField))); 1778 | 1779 | 1780 | unsafe.putAddress(exitMethodHandle + _from_interpreted_entryField,unsafe.getAddress(hotspotExitHandle + _from_interpreted_entryField)); 1781 | unsafe.putAddress(exitMethodHandle + _from_compiled_entry_entryField,unsafe.getAddress(hotspotExitHandle + _from_compiled_entry_entryField)); 1782 | unsafe.putAddress(exitMethodHandle + _codeField,unsafe.getAddress(hotspotExitHandle + _codeField)); 1783 | 1784 | 1785 | 1786 | //把断点数量设置大于0 禁止该方法经过其它编译器进行编译 1787 | if (hasField("Method","_method_counters")){ 1788 | long _number_of_breakpointsField = getFieldOffset("MethodCounters","_number_of_breakpoints"); 1789 | long _method_countersField = getFieldOffset("Method","_method_counters"); 1790 | long _method_counters = unsafe.getAddress(hotspotExitHandle + _method_countersField); 1791 | unsafe.putAddress(exitMethodHandle + _method_countersField,_method_counters); 1792 | unsafe.putShort(_method_counters + _number_of_breakpointsField,(short) 111); 1793 | }else if (hasField("methodOopDesc","_number_of_breakpoints")){ 1794 | long _number_of_breakpointsField = getFieldOffset("methodOopDesc","_number_of_breakpoints"); 1795 | unsafe.putShort(hotspotExitHandle + _number_of_breakpointsField , (short) 111); 1796 | unsafe.putShort(hotspotExitHandle + _number_of_breakpointsField , (short) 111); 1797 | } 1798 | 1799 | 1800 | /** 1801 | * 1802 | * // Enumeration to distinguish tiers of compilation 1803 | * enum CompLevel { 1804 | * CompLevel_any = -1, 1805 | * CompLevel_all = -1, 1806 | * CompLevel_none = 0, // Interpreter 1807 | * CompLevel_simple = 1, // C1 1808 | * CompLevel_limited_profile = 2, // C1, invocation & backedge counters 1809 | * CompLevel_full_profile = 3, // C1, invocation & backedge counters + mdo 1810 | * CompLevel_full_optimization = 4, // C2 or Shark 1811 | * **/ 1812 | if (isHighVer){ 1813 | //设置编译等级 目前c2编译器是最高的 禁止其它编译器再编译 1814 | unsafe.putInt(unsafe.getAddress(hotspotExitHandle + _codeField) + getFieldOffset("nmethod","_comp_level"),4); 1815 | } 1816 | 1817 | 1818 | System.out.println("开始调用 Runtime.getRuntime().exit(0)"); 1819 | 1820 | Runtime.getRuntime().exit(0); 1821 | System.exit(0); 1822 | System.out.println("调用结束 进程没有退出 成功hook!"); 1823 | } 1824 | /** 1825 | * 直接替换Native方法的解释地址与编译地址 并且调用旧的方法 1826 | * */ 1827 | public static void testReplaceNativeAddressAndCallOld() throws Throwable{ 1828 | long hotspotClassHandle = getKlass(HotSpot.class); 1829 | long systemClassHandle = getKlass(System.class); 1830 | 1831 | boolean isHighVer = !hasType("methodOop"); 1832 | 1833 | long systemMethodHandle = getMethodByKlass(systemClassHandle,"currentTimeMillis",getMethodSignature(System.class.getMethod("currentTimeMillis"))); 1834 | 1835 | long hotspotMethodHandle = getMethodByKlass(hotspotClassHandle,"currentTimeMillis2",getMethodSignature(HotSpot.class.getMethod("currentTimeMillis2"))); 1836 | 1837 | 1838 | System.out.println("Hotspot Class Address : 0x" + Long.toHexString(hotspotClassHandle)); 1839 | System.out.println("Runtime Class Address : 0x" + Long.toHexString(systemClassHandle)); 1840 | System.out.println("被hook的方法地址 System->currentTimeMillis : 0x" + Long.toHexString(systemMethodHandle)); 1841 | System.out.println("hook的方法地址 Hotspot->currentTimeMillis : 0x" + Long.toHexString(hotspotMethodHandle)); 1842 | 1843 | 1844 | long _codeField = 0; 1845 | long _from_interpreted_entryField = 0; 1846 | long _from_compiled_entry_entryField = 0; 1847 | long _access_flagsField = 0; 1848 | long _i2i_entryField = 0; 1849 | if (isHighVer){ 1850 | _from_interpreted_entryField = getFieldOffset("Method","_from_interpreted_entry"); 1851 | _from_compiled_entry_entryField = getFieldOffset("Method","_from_compiled_entry"); 1852 | _codeField = getFieldOffset("Method","_code"); 1853 | _access_flagsField = getFieldOffset("Method","_access_flags"); 1854 | _i2i_entryField = getFieldOffset("Method","_i2i_entry"); 1855 | }else { 1856 | _codeField = getFieldOffset("methodOopDesc","_code"); 1857 | _from_interpreted_entryField = getFieldOffsetOr("methodOopDesc","_from_interpreted_entry","_interpreter_entry"); 1858 | _from_compiled_entry_entryField = getFieldOffsetOr("methodOopDesc","_from_compiled_entry","_from_compiled_code_entry_point"); 1859 | _access_flagsField = getFieldOffset("methodOopDesc","_access_flags"); 1860 | } 1861 | 1862 | boolean compileFlag = false; 1863 | 1864 | long hotspotNMethodAddress = 0; 1865 | 1866 | for (int i = 0; i < 10000; i++) { 1867 | currentTimeMillis2(); 1868 | System.currentTimeMillis(); 1869 | hotspotNMethodAddress = unsafe.getAddress(hotspotMethodHandle + _codeField); 1870 | if (hotspotNMethodAddress != 0){ 1871 | break; 1872 | } 1873 | } 1874 | 1875 | //什么也不做 1876 | if(hotspotNMethodAddress == 0 ){ 1877 | 1878 | } 1879 | 1880 | System.out.println("jit地址 : 0x" + Long.toHexString(unsafe.getAddress(hotspotMethodHandle + _from_compiled_entry_entryField))); 1881 | 1882 | 1883 | long system_from_interpreted_entry = unsafe.getAddress(systemMethodHandle + _from_interpreted_entryField); 1884 | long system_from_compiled_entry_entry = unsafe.getAddress(systemMethodHandle + _from_compiled_entry_entryField); 1885 | long system_code = unsafe.getAddress(systemMethodHandle + _codeField); 1886 | long system_i2i_entry = unsafe.getAddress(systemMethodHandle + _i2i_entryField); 1887 | 1888 | unsafe.putAddress(systemMethodHandle + _from_interpreted_entryField,unsafe.getAddress(hotspotMethodHandle + _from_interpreted_entryField)); 1889 | unsafe.putAddress(systemMethodHandle + _from_compiled_entry_entryField,unsafe.getAddress(hotspotMethodHandle + _from_compiled_entry_entryField)); 1890 | unsafe.putAddress(systemMethodHandle + _codeField,unsafe.getAddress(hotspotMethodHandle + _codeField)); 1891 | // 由于native方法和普通的java方法传参不同需要修适配器 1892 | unsafe.putAddress(systemMethodHandle + _i2i_entryField,unsafe.getAddress(hotspotMethodHandle + _i2i_entryField)); 1893 | 1894 | 1895 | //禁止该方法经过其它编译器进行编译 1896 | int newMethodAccessFlags = unsafe.getInt(hotspotMethodHandle + _access_flagsField); 1897 | // newMethodAccessFlags = newMethodAccessFlags | Modifier.NATIVE; 1898 | 1899 | unsafe.putInt((hotspotMethodHandle + _access_flagsField),newMethodAccessFlags); 1900 | 1901 | unsafe.putAddress(hotspotMethodHandle + _from_interpreted_entryField,system_from_interpreted_entry); 1902 | unsafe.putAddress(hotspotMethodHandle + _from_compiled_entry_entryField,system_from_compiled_entry_entry); 1903 | unsafe.putAddress(hotspotMethodHandle + _codeField,0); 1904 | // //由于native方法和普通的java方法传参不同需要修适配器 1905 | unsafe.putAddress(hotspotMethodHandle + _i2i_entryField,system_i2i_entry); 1906 | 1907 | 1908 | for (int i = 0; i < 10000; i++) { 1909 | currentTimeMillis2(); 1910 | } 1911 | 1912 | /** 1913 | * 1914 | * // Enumeration to distinguish tiers of compilation 1915 | * enum CompLevel { 1916 | * CompLevel_any = -1, 1917 | * CompLevel_all = -1, 1918 | * CompLevel_none = 0, // Interpreter 1919 | * CompLevel_simple = 1, // C1 1920 | * CompLevel_limited_profile = 2, // C1, invocation & backedge counters 1921 | * CompLevel_full_profile = 3, // C1, invocation & backedge counters + mdo 1922 | * CompLevel_full_optimization = 4, // C2 or Shark 1923 | * **/ 1924 | if (isHighVer && hotspotNMethodAddress !=0){ 1925 | //设置编译等级 禁止其它编译器再编译 1926 | unsafe.putInt(hotspotNMethodAddress + getFieldOffset("nmethod","_comp_level"),4); 1927 | } 1928 | 1929 | System.out.println("开始调用 System.currentTimeMillis()"); 1930 | 1931 | System.out.println(System.currentTimeMillis()); 1932 | System.out.println(currentTimeMillis2()); 1933 | 1934 | System.out.println("调用结束 进程没有退出 成功hook!"); 1935 | } 1936 | 1937 | /** 1938 | * 直接替换方法的内存 1939 | * */ 1940 | public static void testReplaceMemory() throws Throwable{ 1941 | long hotspotClassHandle = getKlass(HotSpot.class); 1942 | long systemClassHandle = getKlass(Thread.class); 1943 | 1944 | 1945 | String methodType = hasType("methodOopDesc")?"methodOopDesc":"Method"; 1946 | 1947 | long systemMethodHandle = getMethodByKlass(systemClassHandle,"getId",getMethodSignature(Thread.class.getMethod("getId"))); 1948 | 1949 | long hotspotMethodHandle = getMethodByKlass(hotspotClassHandle,"getId",getMethodSignature(HotSpot.class.getMethod("getId"))); 1950 | 1951 | 1952 | 1953 | System.out.println("Hotspot Class Address : 0x" + Long.toHexString(hotspotClassHandle)); 1954 | System.out.println("System Class Address : 0x" + Long.toHexString(systemClassHandle)); 1955 | System.out.println("被hook的方法地址 Runtime->exit : 0x" + Long.toHexString(systemMethodHandle)); 1956 | //第一个参数指向this 1957 | System.out.println("hook的方法地址 Hotspot->exit : 0x" + Long.toHexString(hotspotMethodHandle)); 1958 | 1959 | 1960 | 1961 | String constMethodType = hasType("ConstMethod")?"ConstMethod":"constMethodOopDesc"; 1962 | long methodIdField = getFieldOffset(constMethodType,hasField(constMethodType,"_method_idnum")?"_method_idnum":"_method_index"); 1963 | long _constMethodField = getFieldOffset(methodType,"_constMethod"); 1964 | 1965 | long targetConstMethod = unsafe.getAddress(systemMethodHandle + _constMethodField); 1966 | long newConstMethod = unsafe.getAddress(hotspotMethodHandle + _constMethodField); 1967 | 1968 | short hotspot_method_idnum = unsafe.getShort(newConstMethod + methodIdField); 1969 | short system_method_idnum = unsafe.getShort(targetConstMethod + methodIdField); 1970 | 1971 | 1972 | unsafe.putAddress(systemMethodHandle + _constMethodField,newConstMethod + _constMethodField); 1973 | // typeSwap(methodType,hotspotMethodHandle,systemMethodHandle); 1974 | 1975 | // unsafe.putShort(targetConstMethod + methodIdField,hotspot_method_idnum); 1976 | unsafe.putShort(newConstMethod + methodIdField,system_method_idnum); 1977 | 1978 | 1979 | long result = Thread.currentThread().getId(); 1980 | System.out.println("调用被hook方法 System.getProperty(\"java.home\") 返回 : " + result); 1981 | 1982 | 1983 | if ("hacker".equals(result)){ 1984 | System.out.println("成功Hook!"); 1985 | }else { 1986 | System.out.println("Hook失败!"); 1987 | } 1988 | 1989 | } 1990 | 1991 | /** 1992 | * 直接交换方法的内存 并且调用旧的方法 1993 | * */ 1994 | public static void testReplaceMemoryAndCallOld() throws Throwable{ 1995 | long hotspotClassHandle = getKlass(agentDEF.class); 1996 | long systemClassHandle = getKlass(System.class); 1997 | 1998 | 1999 | String methodType = hasType("methodOopDesc")?"methodOopDesc":"Method"; 2000 | 2001 | long systemMethodHandle = getMethodByKlass(systemClassHandle,"getProperty",getMethodSignature(System.class.getMethod("getProperty", String.class))); 2002 | 2003 | long hotspotMethodHandle = getMethodByKlass(hotspotClassHandle,"getProperty",getMethodSignature(agentDEF.class.getMethod("getProperty", String.class))); 2004 | 2005 | 2006 | System.out.println("Hotspot Class Address : 0x" + Long.toHexString(hotspotClassHandle)); 2007 | System.out.println("System Class Address : 0x" + Long.toHexString(systemClassHandle)); 2008 | System.out.println("被hook的方法地址 Runtime->exit : 0x" + Long.toHexString(systemMethodHandle)); 2009 | //第一个参数指向this 2010 | System.out.println("hook的方法地址 Hotspot->exit : 0x" + Long.toHexString(hotspotMethodHandle)); 2011 | 2012 | 2013 | String constMethodType = hasType("ConstMethod")?"ConstMethod":"constMethodOopDesc"; 2014 | long methodIdField = getFieldOffset(constMethodType,hasField(constMethodType,"_method_idnum")?"_method_idnum":"_method_index"); 2015 | long _constMethodField = getFieldOffset(methodType,"_constMethod"); 2016 | 2017 | long targetConstMethod = unsafe.getAddress(systemMethodHandle + _constMethodField); 2018 | long newConstMethod = unsafe.getAddress(hotspotMethodHandle + _constMethodField); 2019 | 2020 | short hotspot_method_idnum = unsafe.getShort(newConstMethod + methodIdField); 2021 | short system_method_idnum = unsafe.getShort(newConstMethod + methodIdField); 2022 | 2023 | 2024 | 2025 | typeSwap(methodType,hotspotMethodHandle,systemMethodHandle); 2026 | 2027 | unsafe.putShort(targetConstMethod + methodIdField,hotspot_method_idnum); 2028 | unsafe.putShort(newConstMethod + methodIdField,system_method_idnum); 2029 | 2030 | String result = System.getProperty("java.home"); 2031 | System.out.println("调用被hook方法 System.getProperty(\"java.home\") 返回 : " + result); 2032 | System.out.println("调用原方法 HotSpot.getProperty(\"java.home\") 返回 : " + getProperty("java.home")); 2033 | 2034 | if ("hacker".equals(result)){ 2035 | System.out.println("成功Hook!"); 2036 | }else { 2037 | System.out.println("Hook失败!"); 2038 | } 2039 | 2040 | } 2041 | 2042 | /** 2043 | * 直接替换目标方法的Bytecode 2044 | * */ 2045 | public static void testReplaceBytecode() throws Throwable{ 2046 | long hotspotClassHandle = getKlass(HotSpot.class); 2047 | 2048 | 2049 | String methodType = hasType("methodOopDesc")?"methodOopDesc":"Method"; 2050 | String constMethodType = hasType("constMethodOopDesc")?"constMethodOopDesc":"ConstMethod"; 2051 | 2052 | 2053 | long hotspotMethodHandle = getMethodByKlass(hotspotClassHandle,"testCall",getMethodSignature(HotSpot.class.getMethod("testCall", long.class))); 2054 | 2055 | 2056 | System.out.println("Hotspot Class Address : 0x" + Long.toHexString(hotspotClassHandle)); 2057 | System.out.println("被修改的方法地址 Hotspot->testCall : 0x" + Long.toHexString(hotspotMethodHandle)); 2058 | 2059 | long constMethodOopDescSize = sizeOf(constMethodType); 2060 | 2061 | long _constMethodField = getFieldOffset(methodType,"_constMethod"); 2062 | long _codeField = getFieldOffset(methodType,"_code"); 2063 | long _code_sizeField = getFieldOffset(constMethodType,"_code_size"); 2064 | 2065 | long _from_compiled_entry_entryField = getFieldOffsetOr(methodType,"_from_compiled_entry","_from_compiled_code_entry_point"); 2066 | 2067 | // long _max_localsField = getFieldOffset(constMethodType,"_max_locals"); 2068 | // long _max_stackField = getFieldOffset(constMethodType,"_max_stack"); 2069 | 2070 | 2071 | long hotspotConstMethodHandle = unsafe.getAddress(hotspotMethodHandle + _constMethodField); 2072 | 2073 | // lload_0 2074 | // lreturn 2075 | byte[] bytecode = hexToByte("b2ad"); 2076 | 2077 | int bytecodeSize = unsafe.getShort(hotspotConstMethodHandle + _code_sizeField) & 0xffff; 2078 | 2079 | System.out.println("目标方法bytecode大小: " + bytecodeSize); 2080 | 2081 | if (bytecodeSize < bytecode.length){ 2082 | System.out.println("目标方法bytecode太小 可以尝试复制ConstMethod的内存 然后扩大_code_size字段的大小"); 2083 | //hotspotConstMethodHandle = github.beichendream.AsmUtil.allocateAndUpdateBytecodeSize(hotspotConstMethodHandle,bytecode.length); 2084 | //unsafe.putAddress(hotspotMethodHandle + _constMethodField,hotspotConstMethodHandle); 2085 | //// github.beichendream.AsmUtil.replaceBodyAndFixConstantPool(hotspotConstMethodHandle,"return $1;"); 2086 | return; 2087 | } 2088 | 2089 | for (int i = 0; i < bytecode.length; i++) { 2090 | unsafe.putByte(hotspotConstMethodHandle + constMethodOopDescSize + i,bytecode[i]); 2091 | } 2092 | 2093 | //取消jit编译 让修改后的代码立即生效 2094 | unsafe.putAddress(hotspotMethodHandle + _codeField,0); 2095 | unsafe.putAddress(hotspotMethodHandle + _from_compiled_entry_entryField,0); 2096 | 2097 | // unsafe.putShort(hotspotConstMethodHandle + _code_sizeField, (short) 2); 2098 | 2099 | //如果方法堆栈大小与目标相差太大记得修改堆栈大小 2100 | // unsafe.putShort(hotspotConstMethodHandle + _max_stackField, (short) 2); 2101 | // unsafe.putShort(hotspotConstMethodHandle + _max_localsField, (short) 2); 2102 | 2103 | //目标方法原本应该返回123456 现在我们修改了bytecode 它会返回我们传入的参数 2104 | // 新的bytecode 2105 | // lload_0 2106 | // lreturn 2107 | long result = testCall(778899); 2108 | 2109 | System.out.println("testCall result : " + result); 2110 | 2111 | if (778899 == result){ 2112 | System.out.println("修改bytecode成功"); 2113 | } 2114 | } 2115 | 2116 | /** 2117 | * 直接替换目标方法的_from_compiled_entry 2118 | * */ 2119 | public static void testRunShellcode()throws Exception{ 2120 | String methodType = hasType("methodOopDesc")?"methodOopDesc":"Method"; 2121 | long instanceKlass = getKlass(HotSpot.class); 2122 | long methodHandle = getMethodByKlass(instanceKlass,HotSpot.class.getMethod("runShellcodeFunctionByJit")); 2123 | long _codeField = getFieldOffset(methodType,"_code"); 2124 | 2125 | //nmethod 2126 | long _code = 0; 2127 | long _from_compiled_entry_entryField = getFieldOffsetOr(methodType,"_from_compiled_entry","_from_compiled_code_entry_point"); 2128 | 2129 | long _from_compiled_entry = 0; 2130 | 2131 | byte[] shellcode = new byte[]{(byte) 0xc3}; 2132 | 2133 | for (int i = 0; i < 20000; i++) { 2134 | _code = unsafe.getAddress(methodHandle + _codeField); 2135 | if (_code != 0){ 2136 | _from_compiled_entry = unsafe.getAddress(methodHandle + _from_compiled_entry_entryField); 2137 | 2138 | System.out.println("已被Jit优化现在执行shellcode shellcodeAddress: 0x" + Long.toHexString(_from_compiled_entry)); 2139 | break; 2140 | }else { 2141 | System.out.println("执行次数: "+i+" 未被Jit生成优化汇编 开始下一次执行"); 2142 | } 2143 | runShellcodeFunctionByJit(); 2144 | } 2145 | if (_from_compiled_entry == 0 || _code == 0){ 2146 | System.out.println("_from_compiled_entry == 0x"+Long.toHexString(_from_compiled_entry)+" || _code == 0x"+Long.toHexString(_from_compiled_entry)); 2147 | System.out.println("_from_compiled_entry == 0 || _code == 0"); 2148 | return; 2149 | } 2150 | 2151 | long codeSize = getCompiledCodeSize(_code); 2152 | 2153 | System.out.println("Java方法编译后的二进制代码大小 : " + codeSize); 2154 | 2155 | if (codeSize < shellcode.length){ 2156 | System.out.println("shellcode长度小于Java方法编译后的二进制代码大小 无法执行shellcode 请尝试增大方法体大小"); 2157 | return; 2158 | } 2159 | 2160 | memCpy(shellcode,_from_compiled_entry,0,shellcode.length); 2161 | int result = runShellcodeFunctionByJit(); 2162 | System.out.println("shellcode result : " + result); 2163 | if (result != 123){ 2164 | System.out.println("运行shellcode成功"); 2165 | } 2166 | } 2167 | 2168 | public static void aaa(){ 2169 | System.mapLibraryName("aaa"); 2170 | } 2171 | 2172 | public static void main(String[] args) throws Throwable{ 2173 | 2174 | 2175 | 2176 | // testReplaceBytecode(); 2177 | // testReplaceAddress(); 2178 | // testReplaceMemory(); 2179 | // testReplaceMemoryAndCallOld(); 2180 | // testReplaceNativeAddressAndCallOld(); 2181 | // testRunShellcode(); 2182 | testReplaceBytecode(); 2183 | 2184 | 2185 | 2186 | } 2187 | 2188 | 2189 | 2190 | public static String byteArrayToHexPrefix(byte[] bytes) { 2191 | String strHex = ""; 2192 | StringBuilder sb = new StringBuilder(); 2193 | for (int n = 0; n < bytes.length; n++) { 2194 | strHex = Integer.toHexString(bytes[n] & 0xFF); 2195 | sb.append((strHex.length() == 1) ? "0" + strHex : strHex); // 每个字节由两个字符表示,位数不够,高位补0 2196 | } 2197 | return sb.toString().trim(); 2198 | } 2199 | public static byte[] hexToByte(String hexStr) { 2200 | byte[] data = hexStr.getBytes(); 2201 | int len = data.length; 2202 | byte[] out = new byte[len / 2]; 2203 | for (int i = 0, j = 0; j < len; i++) { 2204 | int f = Character.digit(data[j++], 16) << 4; 2205 | f |= Character.digit(data[j++], 16); 2206 | out[i] = (byte)(f & 0xFF); 2207 | } 2208 | return out; 2209 | } 2210 | } 2211 | --------------------------------------------------------------------------------