ERR_UNSAFE_PORT浏览器安全问题导致无法访问的解决方案

news2024/12/24 11:35:02

前言

出发点是Java Agent内存马的自动分析与查杀,实际上其他内存马都可以通过这种方式查杀

本文主要的难点主要是以下三个,我会在文中逐个解答

1.如何dumpJVM真正的当前的字节码
2.如何解决由于LAMBDA表达式导致非法字节码无法分析的问题
3.如何对字节码进行分析以确定某个类是内存马

背景

对于Java内存马的攻防一直没有停止,是Java安全领域的重点

回顾TomcatSpring内存马:FilterController等都需要注册新的组件

针对于需要注册新组件的内存马查杀起来比较容易:

例如c0ny1师傅的java-memshell-scanner项目,利用了Tomcat API删除添加的组件。优点在于一个简单的JSP文件即可查看所有的组件信息,结合人工审查(类名和ClassLoader等信息)对内存马进行查杀,也可以对有风险的Class进行dump后反编译分析

或者LandGrey师傅基于Alibaba Arthas编写的copagent项目,分析JVM中所有的Class,根据危险注解和类名等信息dump可疑的组件,结合人工反编译后进行分析

但实战中,可能并不是以上这种注册新组件的内存马

例如师傅们常用的冰蝎内存马,是Java Agent内存马。以下这段是冰蝎内存马一段代码,简单分析后可以发现冰蝎内存马是利用Java Agent注入到javax.servlet.http.HttpServletservice方法中,这是JavaEE的规范,理论上部署在Tomcat的都要符合这个规范,简单来理解这是Tomcat处理请求最先且总是经过的地方,在该类加入内存马的逻辑,可以保证稳定触发

类似的逻辑,可以使用Java Agent将内存马注入org.apache.catalina.core.ApplicationFilterChain类中,该类位于Filter链头部,也就是说经过Tomcat的请求都会交经过该类的doFilter方法处理,所以在该方法中加入内存马逻辑,也是一种稳定触发的方式(据说这是老版本冰蝎内存马的方式)

还可以对类似的类进行注入,例如org.springframework.web.servlet.DispatcherServlet类,针对于Spring框架的底层进行注入。或者一些巧妙的思路,比如注入Tomcat自带的Filter之一org.apache.tomcat.websocket.server.WsFilter类,这也是Java Agent内存马可以做到的

上文简单地介绍了各种内存马的利用方式与普通内存马的查杀,之所以最后介绍Java Agent内存马的查杀,是因为比较困难。宽字节安全的师傅提出查杀思路:基于javaAgent内存马检测查杀指南

引用文章讲到Java Agent内存马检测的难点:

调用retransformClass方法的时候参数中的字节码并不是调用redefineClass后被修改的类的字节码。对于冰蝎来讲,根本无法获取被冰蝎修改后的字节码。我们自己写Java Agent清除内存马的时候,同样也是无法获取到被redefineClass修改后的字节码,只能获取到被retransformClass修改后的字节码。通过JavaassistASM工具获取到类的字节码,也只是读取磁盘上响应类的字节码,而不是JVM中的字节码

宽字节安全的师傅找到了一种检测手段:sa-jdi.jar

借用公众号师傅的图片,这是一个GUI工具,可以查看JVM中所有已加载的类。区别在于这里获取到的是真正的当前的字节码,而不是获取到原始的,本地的字节码,所以是可以查看被Java Agent调用redefineClass后被修改的类的字节码。进一步可以dump下来认为存在风险的类然后反编译人工审核

介绍

以上是背景,接下来介绍我做了些什么,能够实现怎样的效果

不难看出,以上内存马查杀手段都是半自动结合人工审核的方式,当检测出内存马后

是否可以找到一种方式,做到一条龙式服务:

  • 检测(同时支持普通内存马和Java Agent内存马的检测)
  • 分析(如何确定该类是内存马,仅根据恶意类名和注解等信息不完善)
  • 查杀(当确定内存马存在,如何自动地删除内存马并恢复正常业务逻辑)

大致看来,实现起来似乎不难,然而实际中遇到了很多坑,接下来我会逐个介绍

【→>所有资源关注我,查看“资料”获取<←】> 1、网络安全学习路线> 2、电子书籍(白帽子)> 3、安全大厂内部视频> 4、100份src文档> 5、常见安全面试题> 6、ctf大赛经典题目解析> 7、全套工具包> 8、应急响应笔记

SA-JDI分析

我尝试通过Java Agent技术来获取当前的字节码,发现如师傅所说拿不到被修改的字节码

所以为了可以检测Agent马需要从sa-jdi.jar本身入手,想办法dump得到当前字节码(这样不止可以分析被修改了字节码的Agent马也可以分析普通类型的内存马)

注意到其中一个类:sun.jvm.hotspot.tools.jcore.ClassDump并通过查资料发现该类功能正是dump当前的Class(根据类名也可猜测出)其中的main方法提供一个dump class的命令行工具

于是我想了一些办法,用代码实现了命令行工具的功能,并可以设置一个Filter

ClassDump classDump = new ClassDump ();
// my filter
classDump . setClassFilter ( filter );
classDump . setOutputDirectory ( "out" );
// protected start method
Class <?> toolClass = Class . forName ( "sun.jvm.hotspot.tools.Tool" );
Method method = toolClass . getDeclaredMethod ( "start" , String []. class );
method . setAccessible ( true );
// jvm pid
String [] params = new String []{ String . valueOf ( pid )};
try {method . invoke ( classDump , ( Object ) params );
} catch ( Exception ignored ) {logger . error ( "unknown error" );return ;
}
logger . info ( "dump class finish" );
// detach
Field field = toolClass . getDeclaredField ( "agent" );
field . setAccessible ( true );
HotSpotAgent agent = ( HotSpotAgent ) field . get ( classDump );
agent . detach (); 

上文提到设置一个Filter是用于确定需要对哪些类进行dump操作(dump过多会导致性能等问题)

public class NameFilter implements ClassFilter{@Overridepublic boolean canInclude ( InstanceKlass instanceKlass ) {String klassName = instanceKlass . getName (). asString ();// 在黑名单中的类需要dumpif ( blackList . contains ( klassName )) {return true ;}// 包含了关键字的类也需要dumpfor ( String k : Constant . keyword ) {if ( klassName . contains ( k )) {return true ;}}return false ;}
} 

以上包含了类的黑名单和关键字:

  • 黑名单:Java Agent内存马通常会Hook的地方,需要dump下来进行分析
  • 关键字:类名如果出现memshellshell等关键字认为可能是普通内存马,需要分析
public class Constant {// BLACKLIST (Analysis Target)// CLASS_NAME#METHOD_NAMEpublic static List < String > blackList = new ArrayList <>();// SHELL KEYWORDpublic static List < String > keyword = new ArrayList <>();static {blackList . add ( "javax/servlet/http/HttpServlet#service" );blackList . add ( "org/apache/catalina/core/ApplicationFilterChain#doFilter" );blackList . add ( "org/springframework/web/servlet/DispatcherServlet#doService" );blackList . add ( "org/apache/tomcat/websocket/server/WsFilter#doFilter" );keyword . add ( "shell" );keyword . add ( "memshell" );keyword . add ( "agentshell" );keyword . add ( "exploit" );keyword . add ( "payload" );keyword . add ( "rebeyond" );keyword . add ( "metasploit" );}
} 

另外如果想在Maven项目中加入JDK/lib下的依赖,需要特殊配置

 <dependency><groupId> sun.jvm.hotspot </groupId><artifactId> sa-jdi </artifactId><version> jdk-8 </version><scope> system </scope><systemPath> ${env.JAVA_HOME}/lib/sa-jdi.jar </systemPath>
</dependency> 

在打包成工具Jar包时默认情况下不会加入system scope的依赖,所以需要特殊处理

 <artifactId> maven-assembly-plugin </artifactId>
<configuration><appendAssemblyId> false </appendAssemblyId><descriptors><descriptor> assembly.xml </descriptor></descriptors><archive><manifest><mainClass> org.sec.Main </mainClass></manifest></archive>
</configuration> 

编写assembly.xml文件

 <!-- 省略部分 -->
<dependencySets><dependencySet><outputDirectory> / </outputDirectory><unpack> true </unpack><scope> system </scope></dependencySet>
</dependencySets> 

接着就可以通过代码的方式,根据黑名单和关键字来确定需要dump哪些类然后进行dump操作了

我在测试中遇到一个小问题,值得分享:HttpServlet是正常可以dump的但是ApplicationFilterChain类没有找到。这是因为SpringBoot的懒加载问题,需要手动请求下某个接口就可以了

解决非法字节码

接下来我遇到了一个比较大的坑,通过sa-jdidump下来的字节码是非法的

在对ApplicationFilterChain类分析的时候,会报如下的错

起初我怀疑是自己用了最新版ASM框架:9.2

于是逐渐降级,发现降级到7.0后不再报错,但ClassReader不报错,在分析时候会报错

经过对比,发现是以下的情况

不报错版本

稍微分析了下,发现是ApplicationFilterChain类包含了LAMBDA

不止这个类,不少的类都有可能会包含LAMBDA

发现通过sa-jdi获取的字节码在存在LAMBDA的情况下是非法字节码,无法进行分析

这时候如果还想进行分析,只有两个选择:

  • 自己解析CLASS文件做分析(本末倒置)
  • 改写ASM源码使跳过LAMBDA

根据Java基础知识可以得知:LAMBDAINVOKEDYNAMIC指令相关,于是我改了ASM的代码

(这里不解释为什么这么改了,是经过多次调试确定的)

org/objectweb/asm/ClassReader#274

bootstrapMethodOffsets = null ; 

org/objectweb/asm/ClassReader#2456

case Opcodes . INVOKEDYNAMIC :{return ;} 

改了源码后,就可以正常对非法字节码进行分析了。目前来看没有什么大问题,可以正常分析,但不确定这样的修改是否会存在一些隐患和BUG。总之目前能继续了

分析字节码

分析字节码并不需要太深入做,因为大部分可能出现的内存马都是Runtime.exec或冰蝎反射调ClassLoader.defineClass实现的,针对于这两种情况做分析,足以应对绝大多数情况

以下代码是读取dump的字节码并针对两种情况对所有方法分析

List < Result > results = new ArrayList <>();
int api = Opcodes . ASM9 ;
int parsingOptions = ClassReader . SKIP_DEBUG | ClassReader . SKIP_FRAMES ;
for ( String fileName : files ) {byte [] bytes = Files . readAllBytes ( Paths . get ( fileName ));if ( bytes . length == 0 ) {continue ;}ClassReader cr ;ClassVisitor cv ;try {// runtime exec analysiscr = new ClassReader ( bytes );cv = new ShellClassVisitor ( api , results );cr . accept ( cv , parsingOptions );// classloader defineClass analysiscr = new ClassReader ( bytes );cv = new DefineClassVisitor ( api , results );cr . accept ( cv , parsingOptions );} catch ( Exception ignored ) {}
}
for ( Result r : results ) {logger . info ( r . getKey () + " -> " + r . getTypeWord ());
} 

对于Runtime.exec型的分析最为简单,仅判断已dump的字节码中所有方法中是否存在该方法的调用即可(理论上会存在误报,但黑名单类不可能存在该方法,关键字类本身就是可疑的,所以这样做并无不妥)

 @Override
public void visitMethodInsn ( int opcode , String owner , String name , String descriptor , boolean isInterface ) {boolean runtimeCondition = owner . equals ( "java/lang/Runtime" ) && name . equals ( "exec" ) &&descriptor . equals ( "(Ljava/lang/String;)Ljava/lang/Process;" );if ( runtimeCondition ) {Result result = new Result ();result . setKey ( this . owner );result . setType ( Result . RUNTIME_EXEC_TIME );results . add ( result );}super . visitMethodInsn ( opcode , owner , name , descriptor , isInterface );
} 

但这种情况不适用于冰蝎反射调ClassLoader.defineClass

代码不长,但对应的字节码较复杂

Method m = ClassLoader . class . getDeclaredMethod ( "defineClass" ,  String . class ,ByteBuffer . class ,ProtectionDomain . class );
m . invoke ( null ); 

对应字节码

LDC Ljava/lang/ClassLoader;.class // 重点关注
LDC "defineClass" // 重点关注
ICONST_3
ANEWARRAY java/lang/Class
DUP
ICONST_0
LDC Ljava/lang/String;.class
AASTORE
DUP
ICONST_1
LDC Ljava/nio/ByteBuffer;.class
AASTORE
DUP
ICONST_2
LDC Ljava/security/ProtectionDomain;.class
AASTORE
INVOKEVIRTUAL java/lang/Class.getDeclaredMethod (Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method; // 重点关注
ASTORE 1
L1
LINENUMBER 11 L1
ALOAD 1
ACONST_NULL
ICONST_0
ANEWARRAY java/lang/Object
INVOKEVIRTUAL java/lang/reflect/Method.invoke (Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object; // 重点关注
POP 

这种操作需要多个步骤,并不是简单的一个INVOKE那么简单,不特殊处理的话,由于反射和ClassLoader相关操作都算是比较常见的,有一定的误报可能

于是继续掏出栈帧分析大法,具体不再介绍,之前文章 已有详细解释

根据字节码,在defineClassLjava/lang/ClassLoader;通过LDC指令入栈之前,应该认为这是恶意操作,模拟JVM指令执行后应该在栈顶设置污点

 @Override
public void visitLdcInsn ( Object value ) {if ( value instanceof String ) {if ( value . equals ( "defineClass" )) {super . visitLdcInsn ( value );this . operandStack . set ( 0 , "LDC_STRING" );return ;}} else {if ( value . equals ( Type . getType ( "Ljava/lang/ClassLoader;" ))) {super . visitLdcInsn ( value );this . operandStack . set ( 0 , "LDC_CL" );return ;}}super . visitLdcInsn ( value );
} 

后续主要是对于两个INVOKE进行分析

  • 如果getDeclaredMethod传入的是上文LDC处设置的污点,认为方法返回值也是污点,给栈顶的返回值设置REFLECTION_METHOD标志
  • 如果Method.invoke方法中的Method被标记了REFLECTION_METHOD则可以确定这是内存马
  • 开头一部分代码主要是根据方法参数的实际情况对参数在操作数栈中的索引位置进行确定,是一种动态和自动的确认方式,而不是直接根据经验或者调试写死索引,算是优雅写法
public void visitMethodInsn ( int opcode ,String owner ,String name ,String descriptor ,boolean isInterface ) {Type [] argTypes = Type . getArgumentTypes ( descriptor );if ( opcode != Opcodes . INVOKESTATIC ) {Type [] extendedArgTypes = new Type [ argTypes . length + 1 ];System . arraycopy ( argTypes , 0 , extendedArgTypes , 1 , argTypes . length );extendedArgTypes [ 0 ] = Type . getObjectType ( owner );argTypes = extendedArgTypes ;}boolean reflectionMethod = owner . equals ( "java/lang/Class" ) &&opcode == Opcodes . INVOKEVIRTUAL && name . equals ( "getDeclaredMethod" );boolean methodInvoke = owner . equals ( "java/lang/reflect/Method" ) &&opcode == Opcodes . INVOKEVIRTUAL && name . equals ( "invoke" );if ( reflectionMethod ) {int targetIndex = 0 ;for ( int i = 0 ; i < argTypes . length ; i ++) {if ( argTypes [ i ]. getClassName (). equals ( "java.lang.String" )) {targetIndex = i ;break ;}}if ( operandStack . get ( argTypes . length - targetIndex - 1 ). contains ( "LDC_STRING" )) {super . visitMethodInsn ( opcode , owner , name , descriptor , isInterface );operandStack . set ( TOP , "REFLECTION_METHOD" );return ;}}if ( methodInvoke ) {int targetIndex = 0 ;for ( int i = 0 ; i < argTypes . length ; i ++) {if ( argTypes [ i ]. getClassName (). equals ( "java.lang.reflect.Method" )) {targetIndex = i ;break ;}}if ( operandStack . get ( argTypes . length - targetIndex - 1 ). contains ( "REFLECTION_METHOD" )) {super . visitMethodInsn ( opcode , owner , name , descriptor , isInterface );Result result = new Result ();result . setKey ( owner );result . setType ( Result . CLASSLOADER_DEFINE );results . add ( result );return ;}}super . visitMethodInsn ( opcode , owner , name , descriptor , isInterface );
} 

检测效果如下:

先写个内存马注入的Agent注入到HttpServlet中(关于这个不是文章重点)

然后跑起来我写的工具

  • 其中红色框内是注入的Agent内存马,可以分析出
  • 发现上面还有两个内存马结果,这是我模拟的普通内存马,直接写入到代码中做测试的

自动修复

接下来是内存马的修复,自行写一个Java Agent即可

暂时只处理ApplicationFilterChainHttpServlet的情况(也是最常见的情况)

public class RepairAgent {public static void agentmain ( String agentArgs ,Instrumentation ins ) {ClassFileTransformer transformer = new RepairTransformer ();ins . addTransformer ( transformer , true );Class <?>[] classes = ins . getAllLoadedClasses ();for ( Class <?> clas : classes ) {if ( clas . getName (). equals ( "org.apache.catalina.core.ApplicationFilterChain" )|| clas . getName (). equals ( "javax.servlet.http.HttpServlet" )) {try {ins . retransformClasses ( clas );} catch ( Exception e ) {e . printStackTrace ();}}}}
} 

处理的逻辑并不复杂

  • 由于ApplicationFilterChain中包含了LAMBDA所以我直接简化了代码,变成简单的一句internalDoFilter($1,$2)做修复(慎重选择,为什么这样做我将在总结里解释)
  • 修改方法的参数需要用$1 $2这样表示,不能写reqresp
  • 这里HttpServlet的情况稍复杂,其中有两个service方法,实际上对任何一个进行修改都可以导致内存马的效果,所以我要做的事情是恢复这两个方法,而不是只针对某一个
  • 注意任何非java.lang下的类都需要完整类名
public class RepairTransformer implements ClassFileTransformer {@Overridepublic byte [] transform ( ClassLoader loader ,String className ,Class <?> classBeingRedefined ,ProtectionDomain protectionDomain ,byte [] classfileBuffer ) {className = className . replace ( "/" , "." );ClassPool pool = ClassPool . getDefault ();if ( className . equals ( "org.apache.catalina.core.ApplicationFilterChain" )) {try {CtClass c = pool . getCtClass ( className );CtMethod m = c . getDeclaredMethod ( "doFilter" );m . setBody ( "{internalDoFilter($1,$2);}" );byte [] bytes = c . toBytecode ();c . detach ();return bytes ;} catch ( Exception e ) {e . printStackTrace ();}}if ( className . equals ( "javax.servlet.http.HttpServlet" )) {try {CtClass c = pool . getCtClass ( className );CtClass [] params = new CtClass []{pool . getCtClass ( "javax.servlet.ServletRequest" ),pool . getCtClass ( "javax.servlet.ServletResponse" ),};CtMethod m = c . getDeclaredMethod ( "service" , params );m . setBody ( "{" +"javax.servlet.http.HttpServletRequestrequest;\n" +"javax.servlet.http.HttpServletResponse response;\n" +"\n" +"try {\n" +"request = (javax.servlet.http.HttpServletRequest) $1;\n" +"response = (javax.servlet.http.HttpServletResponse) $2;\n" +"} catch (ClassCastException e) {\n" +"throw new javax.servlet.ServletException(lStrings.getString("http.non_http"));\n" +"}\n" +"service(request, response);" +"}" );CtClass [] paramsProtected = new CtClass []{pool . getCtClass ( "javax.servlet.http.HttpServletRequest" ),pool . getCtClass ( "javax.servlet.http.HttpServletResponse" ),};CtMethod mProtected = c . getDeclaredMethod ( "service" , paramsProtected );mProtected . setBody ( "{" +"String method = $1.getMethod();\n" +"\n" +"if (method.equals(METHOD_GET)) {\n" +"long lastModified = getLastModified($1);\n" +"if (lastModified == -1) {\n" +"doGet($1, $2);\n" +"} else {\n" +"long ifModifiedSince;\n" +"try {\n" +"ifModifiedSince = $1.getDateHeader(HEADER_IFMODSINCE);\n" +"} catch (IllegalArgumentException iae) {\n" +"ifModifiedSince = -1;\n" +"}\n" +"if (ifModifiedSince < (lastModified / 1000 * 1000)) {\n" +"maybeSetLastModified($2, lastModified);\n" +"doGet($1, $2);\n" +"} else {\n" +"$2.setStatus(javax.servlet.http.HttpServletResponse.SC_NOT_MODIFIED);\n" +"}\n" +"}\n" +"\n" +"} else if (method.equals(METHOD_HEAD)) {\n" +"long lastModified = getLastModified($1);\n" +"maybeSetLastModified($2, lastModified);\n" +"doHead($1, $2);\n" +"\n" +"} else if (method.equals(METHOD_POST)) {\n" +"doPost($1, $2);\n" +"\n" +"} else if (method.equals(METHOD_PUT)) {\n" +"doPut($1, $2);\n" +"\n" +"} else if (method.equals(METHOD_DELETE)) {\n" +"doDelete($1, $2);\n" +"\n" +"} else if (method.equals(METHOD_OPTIONS)) {\n" +"doOptions($1, $2);\n" +"\n" +"} else if (method.equals(METHOD_TRACE)) {\n" +"doTrace($1, $2);\n" +"\n" +"} else {\n" +"String errMsg = lStrings.getString("http.method_not_implemented");\n" +"Object[] errArgs = new Object[1];\n" +"errArgs[0] = method;\n" +"errMsg = java.text.MessageFormat.format(errMsg, errArgs);\n" +"\n" +"$2.sendError(javax.servlet.http.HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);\n" +"}"+ "}" );byte [] bytes = c . toBytecode ();c . detach ();return bytes ;} catch ( Exception e ) {e . printStackTrace ();}}return new byte [ 0 ];}
} 

当我们写好了Agent后,需要加入自动修复的逻辑

List < Result > results = Analysis . doAnalysis ( files );
if ( command . repair ) {RepairService . start ( results , pid );
} 

如果分析出了结果,且用户选择了修复功能,才会进入修复逻辑(暂只修复这两个最常见的类)

public static void start ( List < Result >resultList ,int pid ) {logger . info ( "try repair agent memshell" );for ( Result result : resultList ) {String className = result . getKey (). replace ( "/" , "." );if ( className . equals ( "org.apache.catalina.core.ApplicationFilterChain" ) ||className . equals ( "javax/servlet/http/HttpServlet" )) {try {start ( pid );return ;} catch ( Exception ignored ) {}}}
} 

修复的核心代码:把打包好的Agent拿过来,做一下AtachLoad将字节码替换为正常情况即可

public static void start ( int pid ) {try {String agent = Paths . get ( "RepairAgent.jar" ). toAbsolutePath (). toString ();VirtualMachine vm = VirtualMachine . attach ( String . valueOf ( pid ));logger . info ( "load agent..." );vm . loadAgent ( agent );logger . info ( "repair..." );vm . detach ();logger . info ( "detach agent..." );} catch ( Exception e ) {e . printStackTrace ();}
} 

注意使用VirtualMachineAPI需要加入tools.jar,由于上文已经配置了打包插件,所以可以直接打入Jar包,使用时候java -jar xxx.jar --pid 000这样会比较方便

 <dependency><groupId> com.sun.tools </groupId><artifactId> tools </artifactId><version> jdk-8 </version><scope> system </scope><systemPath> ${env.JAVA_HOME}/lib/tools.jar </systemPath>
</dependency> 

通过以上这些修复手段可以做到的效果:

  • 启动某SpringBoot应用
  • 通过Agent注入内存马,访问后内存马可用
  • 通过工具检测到内存马,尝试修改,使字节码被还原
  • 再次访问后内存马失效,不需要重启

总结

关于Dump字节码

经过我的一些测试,使用sa-jdi库不能保证dump所有的字节码,会出现莫名其妙的异常,猜测是某些字节码不允许被dump下来。但测试了常见TomcatSpringBoot等程序,发现基本没有问题

关于非法字节码

只要是包含LAMBDA的字节码都是非法字节码,无法正常处理,需要用修改源码后的ASM来做。这种方式终究不是完美的办法,是否存在能够dump下来合法字节码的方式呢(经过一些尝试没有找到办法)

关于检测

可以看到,字节码分析的过程比较简单,尤其是Runtime.exec的普通执行命令内存马,很容易绕过,但个人认为这已足够,因为之前的一些条件已经限制了分析的类是不可能包含Runtime.exec的黑名单类,且大多数用户都是脚本小子,使用免杀型内存马的可能性不大。大多数用户可能直接用了现成的工具,例如冰蝎型内存马的检测方式已完成,暂时来看这样做是足够的,没有必要加入各种免杀检测手段

关于查杀

使用Agent恢复字节码的修复方式理论上没有问题。但其中的ApplicationFilterChain类的doFilter方法中包含了LAMBDA和匿名内部类,这两者都是Javassist框架不支持的内容,可以用ASM来做,但可能难度较高

另外对于普通型内存马的修复,通过Agent技术只能覆盖方法体,不可以增加或删除方法。所以理论上可以根据方法的返回值类型,做返回NULL的处理进行修复

关于拓展

例如代码中我定义的黑名单和关键字,可以根据实战经验自行添加新的类,以实现更完善的效果。在查杀方面我做了最常见的两种,可以根据实际情况自行添加更多的逻辑

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/138329.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

短视频带货流程话术

现在短视频热度高涨&#xff0c;已经成为人们娱乐休闲的一种方式。短视频人流量大&#xff0c;已经衍生出了直播带货、橱窗链接带货等方式&#xff0c;也吸引了一批想要带货的用户。前言现在短视频热度高涨&#xff0c;已经成为人们娱乐休闲的一种方式。短视频人流量大&#xf…

JDBC(使用java语言操作数据库)

JDBC概念&#xff1a;使用Java语言操作关系型数据库的一套api&#xff08;可以用一套标准的jdbc操作所有类型的数据库&#xff0c;jdbc是接口&#xff0c;每一个数据库&#xff08;mysql、oracle、db2...&#xff09;都去实现jdbc的接口&#xff0c;每个数据库的实现类都不一样…

LeetCode:11. 盛最多水的容器

11. 盛最多水的容器1&#xff09;题目2&#xff09;思路3&#xff09;代码4&#xff09;结果1&#xff09;题目 给定一个长度为 n 的整数数组 height 。有 n 条垂线&#xff0c;第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。 找出其中的两条线&#xff0c;使得它们与 x …

2023年1月4日:fastadminApi接口开发项目时遇到的问题

SelectPage selectpage(官方文档&#xff1a;动态下拉(SelectPage) - FastAdmin框架文档 - FastAdmin开发文档) 常规用法 下面介绍一个基础的动态下拉列表示例&#xff0c;如下 <input id"c-name" data-rule"required" data-source"category/sel…

AVS3变换之IST和ISTS

IST&#xff08;Implicit Selected Transform&#xff09;是AVS3中新增的针对intra块的变换工具&#xff0c;IST对intra块提供了两种可分离的变换核&#xff0c;编码器根据RDO选择最优的变换核&#xff0c;但是对于选中的变换核不在码流中传输其索引&#xff0c;而是将其索引隐…

day34【代码随想录】贪心算法之跳跃游戏、跳跃游戏||、K次取反后最大化的数组和

文章目录前言一、跳跃游戏&#xff08;力扣55&#xff09;二、跳跃游戏 II&#xff08;力扣45&#xff09;三、K次取反后最大化的数组和&#xff08;力扣1005&#xff09;前言 1、跳跃游戏 2、跳跃游戏|| 3、K次取反后最大化的数组和 一、跳跃游戏&#xff08;力扣55&#xff…

火热的低代码到底是什么?

低代码风头正紧&#xff0c;也是最近一段时间跟云原生一起被热捧&#xff0c;不管你是做后端开发、还是前端设计、销售、售前&#xff0c;如果你没接触过低代码你都不好意思说自己在软件领域工作&#xff0c;这边文章从我的角度聊聊低代码是什么、以怎么样的方式开发、及低代码…

react笔记_05函数组件与类组件的区别之capture value特性

函数组件与类组件在写法没有好坏之分&#xff0c;性能差距也几乎可以忽略&#xff0c;而且 React 会长期支持这两种写法。 React的函数式组件和类组件之间的根本区别 在心智模型上。 函数式组件具有 capture value 特性。 capture value特性 Capture Value 从字面上可以理解…

【已解决】office提示你的许可证不是正版,你可能是盗版软件的受害者?

三步即可先看效果步骤1、下载工具2、找到 修复Office许可证明问题3、填入KMS主机&#xff0c;点击修复如何查看正在使用的 KMS 主机地址&#xff1f;先看效果 步骤 1、下载工具 工具下载&#xff1a;https://otp.landian.vip/zh-cn/ 2、找到 修复Office许可证明问题 3、填入…

PEM格式RSA密钥解析(一)

Base64转16进制格式 后缀是.PEM 的数字证书是 BASE64 编码的&#xff0c;以 ASCII 码来表示。常见的证书如下所示&#xff08;以 RSA1024 为例&#xff09;&#xff1a; 私钥&#xff1a; ----BEGIN RSA PRIVATE KEY----- MIICWwIBAAKBgHU4CF6yvqb5WBhwcYfvh/o3NpwcSJlcfj0nIZ…

下载神器IDM安装与使用(保姆级教程)

下载神器IDM安装与使用&#xff08;保姆级教程&#xff09; 文章目录下载神器IDM安装与使用&#xff08;保姆级教程&#xff09;前言一、下载地址二、IDM是什么&#xff1f;三、作用与特点四、安装步骤总结前言 众所周知&#xff0c;下载工具是大家电脑里必装的软件之一。 但大…

大数据时代,制造业人要知道,为什么有ERP还要上SRM系统

每个公司都是在一个供应链中&#xff0c;每个公司都有自己的上下游。如今&#xff0c;企业间的竞争已不再是单一企业的单打独斗&#xff0c;而是由价值链与价值链、供应链与供应链的较量。上游厂商&#xff08;供应商&#xff09;直接影响到公司的产品与服务的价格及竞争能力。…

零售企业全域数据打通最佳实践

在数字化转型的实战中&#xff0c;零售企业面临着巨大的挑战&#xff0c;过去三年线下流量锐减&#xff0c;甚至不少门店关停&#xff0c;不少零售企业布局线上&#xff0c;但是这个也给零售企业带来另外一个挑战&#xff1a;IT资源有限的情况下&#xff0c;线上&线下的流量…

密码学_RC4算法

在密码学中&#xff0c;RC4&#xff08;来自Rivest Cipher 4的缩写&#xff09;是一种流加密算法&#xff0c;密钥长度可变。它加解密使用相同的密钥&#xff0c;因此也属于对称加密算法。RC4是有线等效加密&#xff08;WEP&#xff09;中采用的加密算法&#xff0c;也曾经是TL…

电脑看不了视频?视频恢复,3个方案解决

一般电脑播放不了视频&#xff0c;原因大多是Flash插件损坏了&#xff0c;因为电脑上的视频和所有的Flash都需要Flash插件才可以正常使用。电脑看不了视频怎么办&#xff1f;你需要对Flash进行修复或者重新安装它。一起来看看下面三个方案&#xff0c;解决电脑视频看不了的问题…

传输系统WSON倒换时的路由震荡问题【脱敏版】

【摘要】在省干传输OXC系统中使用WSON倒换时&#xff0c;测试过程中发现WSON倒换时存在电路频繁up down的现象&#xff0c;研究发现倒换时光信道建立时间过程中光信号不是一个稳定的状态&#xff0c;设备的色散补偿模块会在这个不稳态下出现异常&#xff0c;导致色散补偿模块不…

2023,出海企业,数据跨境传输如何实现合规?

随着防疫政策的优化&#xff0c;2023年出海企业开始加速海外业务&#xff0c;此前&#xff0c;浙江、四川、广东、江苏等地政府都纷纷出手“包机出海抢订单“。而伴随着企业的全球业务拓展&#xff0c;数据跨境活动日益频繁&#xff0c;跨境数据的安全风险也日益凸显&#xff0…

通过反射机制调用方法

package com.javase.reflect;import com.javase.service.UserService;import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ResourceBundle;/*** 通过反射机制调用方法&#xff08;必须掌握&#xff0c;五颗星*****&#…

小程序开发经验分享(2)前端开发

一、开发前的准备 开发环境:微信推出的官方开发工具。 申请 Appid :相关公司或个人申请到 Appid 后可以进行真机的调试和预览,否则只能在开发工具里进行调试。 备案域名和证书:微信小程序仅支持 https 协议,所以务必绑定含有证书的域名,才能在正式上线后进行使用。 二…

jumpserver源码包安装

cd /opt wget https://github.com/jumpserver/installer/releases/download/v2.20.1/jumpserver-installer-v2.20.1.tar.gz tar -xf jumpserver-installer-v2.20.1.tar.gz cd jumpserver-installer-v2.20.1 ​ vim config-example.txt 在config-example配置文件里配置mysql&…