Java openrasp记录-01

例子1

https://github.com/anbai-inc/javaweb-expression  一个hook ognl、spel、MVEL表达式注入的例子

用的是asm5进行字节码修改

采用premain进行插桩,重写transform方法

expClassList是要hook的类,这里定义在MethodHookDesc

这里判断hook点通过类名,具体其中的方法名,以及方法的描述符

其中expClassList中定义了具体要hook的类,就mvel、ognl、spel三种

 

 匹配到以上三种类后即重写visitMethod方法,匹配具体要hook的方法名和方法描述符,如果匹配到了,则重写MethodVisitor的visitCode方法,进行字节码修改,这里因为是表达式注入,因此这里涉及到string类型的表达式,因此获取传到hook函数处的表达式字符串压入操作数栈,并通过调用expression方法弹出该值进行检测,这里要涉及到操作数栈和局部变量表,因此要清楚原本的方法帧中局部变量表下标索引几代表的是输入的表达式:

ognl:

ognl对应的是parseExpression这个方法,其中expressoin参数是具体解析的表达式

其对应的字节码指令如下所示,Aload0即对应的即为表达式,通过invokeSpecial调用

也可以通过jclasslib来查看

 

spel:

这里的hook点时init方法,这里的expression即为表达式

其init方法中aload1对应赋值时的栈顶元素,所以其为表达式,因此下标对应的是1

 

 

mvel:

这个用的局部变量表的下标也是1,然而实际上取表达式值时用的为下标为0的this来取

 

根据局部变量表中的表达式的值传入expression方法进行处理

 

其中expression将打印出当前的函数调用栈,该例子只是一个插桩+hook方法字节码修改的例子,并没有最终的判断入侵的检测规则

 

例子2

https://toutiao.io/posts/4kt0al/preview 中给了一个例子,也是用asm进行字节码的修改

整体设计分析:

premain方式进行插桩,调用init方法,进一步调用Config.initConfig方法进行初始化配置

此时用到resources/main.config文件,读取其内容,从其格式来看其为json文件,以不同的模块名来区分不同的hook类别

{"module":[{"moduleName": "java/lang/ProcessBuilder","loadClass": "xbear.javaopenrasp.visitors.rce.ProcessBuilderVisitor","mode": "block","whiteList":["javac"],"blackList": ["calc", "etc", "var", "opt", "apache", "bin", "passwd", "login", "cshrc", "profile","ifconfig", "tcpdump", "chmod", "cron", "sudo", "su", "rm", "wget", "sz", "kill", "apt-get","find", "/applications/calculator.app/contents/macos/calculator"]},{"moduleName": "java/io/ObjectInputStream","loadClass": "xbear.javaopenrasp.visitors.rce.DeserializationVisitor","mode": "black", "whiteList":[],"blackList":["org.apache.commons.collections.functors.InvokerTransformer", "org.apache.commons.collections.functors.InstantiateTransformer","org.apache.commons.collections4.functors.InvokerTransformer","org.apache.commons.collections4.functors.InstantiateTransformer","org.codehaus.groovy.runtime.ConvertedClosure","org.codehaus.groovy.runtime.MethodClosure","org.springframework.beans.factory.ObjectFactory"]},{"moduleName": "ognl/Ognl","loadClass": "xbear.javaopenrasp.visitors.rce.OgnlVisitor","mode": "black","whiteList":[],"blackList": ["ognl.OgnlContext", "ognl.TypeConverter","ognl.MemberAccess","_memberAccess","ognl.ClassResolver","java.lang.Runtime","java.lang.Class","java.lang.ClassLoader","java.lang.System","java.lang.ProcessBuilder","java.lang.Object","java.lang.Shutdown","java.io.File","javax.script.ScriptEngineManager","com.opensymphony.xwork2.ActionContext",]},{"moduleName": "com/mysql/jdbc/StatementImpl","loadClass": "xbear.javaopenrasp.visitors.sql.MySQLVisitor","mode": "check", "whiteList":[],"blackList":[]},{"moduleName": "com/microsoft/jdbc/base/BaseStatement","loadClass": "xbear.javaopenrasp.visitors.sql.SQLServerVisitor","mode": "check", "whiteList":[],"blackList":[]}]
}

接着取到module中的值放入ConcurrentHashmap中,对于每一个moduleName都对应一个ConcurrentHashmap,那么后面运行过程中根据moudlename就能获取到每种hook点的信息

 对于jvm将要加载的类,如果module中包含该类名,则使用asm来进行字节码修改,这里创建ClassVisitor通过Reflections.createVisitorIns方法,因为通常在这里将需要设计具体如何对class进行检查,那么对于不同的需要进行hook的类处理逻辑不同,因此这里是一个分支点,例子1也是相同的。

 

 根据当前的类名得到其相对应的loadclass的类名然后利用反射进行实例化

 

 这里定义了rce和sql两个大类

 

 具体对应的hook的类名和具体的loadclass类名映射关系为:

java/lang/ProcessBuilder -> xbear.javaopenrasp.visitors.rce.ProcessBuilderVisitor   //命令执行
java/io/ObjectInputStream -> xbear.javaopenrasp.visitors.rce.DeserializationVisitor  //反序列化
ognl/Ognl -> xbear.javaopenrasp.visitors.rce.OgnlVisitor //ognl表达式注入
com/mysql/jdbc/StatementImpl -> xbear.javaopenrasp.visitors.sql.MySQLVisitor  //sql注入
com/microsoft/jdbc/base/BaseStatement -> xbear.javaopenrasp.visitors.sql.SQLServerVisitor  //sql注入

从大体上整个插桩过程分析结束,初始化的主要工作还是对各种hook点如何进行初始配置,方便后面hook进行中的具体细化操作。

hook点处理分析:

命令执行hook点:

java中命令执行一般常用的有两种,Runtime.exec和Processbuilder.start,但是Runtime.exec实际上也是利用的Processbuilder,而Processbuilder最终利用的是ProcessImpl来执行命令,那么实际上这里选择hook点,选择Processbuilder的start即可,因为只要执行命令,都将走到该类的start方法,在这里就能拿到具体要执行的命令。

具体的逻辑如下,这里重写了onMethodEnter方法,asm5中的,即进入start内部之前执行

    @Overrideprotected void onMethodEnter() {mv.visitTypeInsn(NEW,"xbear/javaopenrasp/filters/rce/PrcessBuilderFilter"); //new一个命令执行过滤的对象压入栈mv.visitInsn(DUP); //再次压入该对象mv.visitMethodInsn(INVOKESPECIAL, "xbear/javaopenrasp/filters/rce/PrcessBuilderFilter", "<init>", "()V", false); //弹出对象进行初始化,此时栈中大小为2-1=1mv.visitVarInsn(ASTORE, 1); //弹出存储该对象到局部变量表1处,此时栈的大小为1-1=0mv.visitVarInsn(ALOAD, 1);  //加载局部变量表1处的对象压入栈,此时栈的大小为0+1=1mv.visitVarInsn(ALOAD, 0); //加载this压入栈,此时栈大小为1+1=2mv.visitFieldInsn(GETFIELD, "java/lang/ProcessBuilder", "command", "Ljava/util/List;"); //取this.command的值压入栈,栈大小为2mv.visitMethodInsn(INVOKEVIRTUAL,"xbear/javaopenrasp/filters/rce/PrcessBuilderFilter", "filter", //调用filer方法,弹出的值的数量为filter的方法参数大小1+1=2,栈顶的this.command的值作为参数,并将filter
方法的处理结果压入栈中,filter返回一个Boolean值,此时栈中大小为1"(Ljava/lang/Object;)Z", false);Label l92 = new Label();  //new一个label用来跳转mv.visitJumpInsn(IFNE, l92); //此时弹出filter处理的结果和0进行比较,如果不等与0,则跳到192lable,说明执行的当前的命令可以执行,则正常执行start方法,否则执行下一条指令,栈大小为0mv.visitTypeInsn(NEW, "java/io/IOException"); //new 一个io异常对象mv.visitInsn(DUP); //再次压入该对象,栈大小2mv.visitLdcInsn("invalid character in command because of security"); //压入该字符串,栈大小3mv.visitMethodInsn(INVOKESPECIAL,"java/io/IOException", "<init>", "(Ljava/lang/String;)V", false); //弹出1+1=2个值,初始化该异常对象,栈顶元素作为io异常的初始化参数,此时栈大小为1mv.visitInsn(ATHROW); //抛出该异常mv.visitLabel(l92);}

先看start方法部分如下:

这里如果直接用asm字节码指令来写就要结合源码和bytecode字节码指令来写,可以看到0处放入的即为this,最终command.toArray的结果放到局部变量表1处,上面写指令码的时候也ASTORE_1了一次,这里并不一定直到1处是否有值,但是指令码这里直接ASTORE1,因此我们不需要担心1处是否有值

 

这样就完成了hook点的构造,取command的值调用filter进行过滤,命令执行的filter如下所示:

    public boolean filter(Object forCheck) {String moduleName = "java/lang/ProcessBuilder"; List<String> commandList = (List<String>) forCheck;String command = StringUtils.join(commandList, " ").trim().toLowerCase();Console.log("即将执行命令:" + command);String mode = (String) Config.moduleMap.get(moduleName).get("mode"); //取对应的命令执行逻辑,mode为block,即阻断switch (mode) { case "block":Console.log("> 阻止执行命令:" + command);return false;  //如果直接为block,那么所有命令都执行不了,也可以更改模式,用黑白名单过滤case "white":if (Config.isWhite(moduleName, command)) {Console.log("> 允许执行命令:" + command);return true;}Console.log("> 阻止执行命令:" + command);return false;case "black":if (Config.isBlack(moduleName, command)) {Console.log("> 阻止执行命令:" + command);return false;}Console.log("> 允许执行命令:" + command);return true;case "log":default:Console.log("> 允许执行命令:" + command);Console.log("> 输出打印调用栈\r\n" + StackTrace.getStackTrace());return true;}}

 asm感觉还是挺麻烦的,语句越复杂要用到的指令越多,稍微不熟练就会出错

反序列化hook点:

在java.io.ObjectInputStream处进行hook,这里定义了一些反序列化的黑名单

    @Overridepublic MethodVisitor visitMethod(int access, String name, String desc,String signature, String[] exceptions) {MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);if ("resolveClass".equals(name) && "(Ljava/io/ObjectStreamClass;)Ljava/lang/Class;".equals(desc)) {mv = new DeserializationVisitorAdapter(mv, access, name, desc);}return mv;}

为什么选择resolveClass作为hook的方法?只要记住我们的目的是拿到将要反序列化的类名,那么实际上的反序列化过程中resolveClass的代码如下:

    protected Class<?> resolveClass(ObjectStreamClass desc)throws IOException, ClassNotFoundException{String name = desc.getName();try {return Class.forName(name, false, latestUserDefinedLoader());} catch (ClassNotFoundException ex) {Class<?> cl = primClasses.get(name);if (cl != null) {return cl;} else {throw ex;}}}

入口参数是ObjectStreamClass,那么在序列化过程中生成的序列化数据的过程中调用该类的lookup方法将生成类的描述信息,其中就包括的类名和SUID,那么调用该类的getName实际上就能拿到反序列化类的名字,所以只需拿到类描述符即可,从resolveClass的逻辑中将以类名通过反射进行类的加载获取反序列化类的class对象,以CommonsCollections2为例,涉及到PriorityQueue和InvokerTrasnformer和TransformingComparator,那么肯定要涉及到这两个类的反序列化

 比如如下图所示就能拿到反序列化的类名,然后再与黑名单进行匹配即可

对应的hook逻辑如下:

 @Overrideprotected void onMethodEnter() {mv.visitTypeInsn(NEW, "xbear/javaopenrasp/filters/rce/DeserializationFilter"); //new一个反序列化过滤对象压入栈,栈大小1mv.visitInsn(DUP); //再次压入该对象,栈大小为2mv.visitMethodInsn(INVOKESPECIAL, "xbear/javaopenrasp/filters/rce/DeserializationFilter", "<init>", "()V", false); //弹出一个对象进行实例化,栈大小为1mv.visitVarInsn(ASTORE, 2); //存储该对象到局部变量表,栈大小为0mv.visitVarInsn(ALOAD, 2); //取出该对象到栈,栈大小为1mv.visitVarInsn(ALOAD, 1); //这里要涉及到取局部变量表的值, 所以又得去看该方法的字节码指令,取到的即为desc,压入操作数栈,栈大小为1+1=2mv.visitMethodInsn(INVOKEVIRTUAL, "xbear/javaopenrasp/filters/rce/DeserializationFilterr", "filter", "(Ljava/lang/Object;)Z", false); //调用反序列化过滤方法,弹出1+1=2个值,栈顶的desc作为参数Label l92 = new Label(); //new一个labelmv.visitJumpInsn(IFNE, l92); //过滤的返回值和0比mv.visitTypeInsn(NEW, "java/io/IOException"); //如果等于0,则new一个异常对象mv.visitInsn(DUP); //再次压入mv.visitLdcInsn("invalid class in deserialization because of security"); //错误信息压栈mv.visitMethodInsn(INVOKESPECIAL, "java/io/IOException", "<init>", "(Ljava/lang/String;)V", false); //实例化异常mv.visitInsn(ATHROW); //抛出异常mv.visitLabel(l92); //不等于0,则说明反序列化的类不在黑名单中,进行正常反序列化过程}

从下图可以看到aload1,然后调用栈顶元素的getname方法,并把结果压入栈中,所以desc类描述符是在该方法的局部变量表1处存着,并且2处不管之前放什么元素,这里将被类名进行覆盖

 

在对应的过滤方法中再通过类描述符调用getName拿到类名,然后通过对应的mode为black,因此

 

 接着只要拿到预先配置好的黑名单来进行过滤即可

 

ognl的hook点:

hook的是ognl.Ognl的parseExpression这个方法,和第一个例子选择的hook点是相同的,因为该方法就能拿到要执行的表达式

那么对于对应的class文件直接看该方法的局部变量表就能看到表达式再局部变量表的0处,因此只要将该值传入过滤函数即可

 

 对应的hook处的逻辑:

    protected void onMethodEnter() {Label l30 = new Label(); //new一个labelmv.visitLabel(l30); //访问该label(貌似没有意义)mv.visitVarInsn(ALOAD, 0); //加载局部表量表0处的表达式值到栈mv.visitMethodInsn(INVOKESTATIC, "xbear/javaopenrasp/filters/rce/OgnlFilter", "staticFilter", "(Ljava/lang/Object;)Z", false);//调用过滤函数,传入表达式的值,因为是static方法,所以只需要提供入口参数即可Label l31 = new Label(); //new一个labelmv.visitJumpInsn(IFNE, l31); //如果过滤表达式不为0,则表达式正常执行Label l32 = new Label(); //new label,貌似没有mv.visitLabel(l32);mv.visitTypeInsn(NEW, "ognl/OgnlException"); //new一个异常对象mv.visitInsn(DUP); //再次压栈mv.visitLdcInsn("invalid class in ognl expression because of security"); //异常信息压栈mv.visitMethodInsn(INVOKESPECIAL, "ognl/OgnlException", "<init>", "(Ljava/lang/String;)V", false); //传入异常信息进行异常对象初始化mv.visitInsn(ATHROW); //抛出异常mv.visitLabel(l31);}

RASP绕过

1.多种姿势openrasp命令执行绕过-安全客 - 安全资讯平台

第一种是根据线程中rce,绕过了rasp对context url的判断,没有url则直接返回正常

第二种直接关掉了rasp的开关

两种措施都必须有代码执行的权限,也就是说必须有shell的前提下

2.de1ctf中的一道绕rasp的思路,思路虽然在园长的javaseccode中提到过,defineclass来绕过rasp检测,但是这种类的确不好找?

LandGrey's Blog

关于springboot为何能绕过rasp,首先defineclass,然后addclass说明已经添加到jvm中,然后class.forname再反射拿到该类时会进行类的链接从而执行static静态区的代码,不需要再重新loadclass

此时classforname时native方法直接加载加载该类,因此绕过了rasp对类加载机制的拦截

 

rasp的用途

1.代码审计

可以对一些漏洞,比如反序列化,ognl、spel等的关键函数处进行hook并记录,然后可以输出成类似日志的格式,结合其调用栈以及其入口参数提供给白盒代码审计工具进行自动化审计

2.0day捕获

对一些危险函数进行hook,并在执行时及时告警,比如Runtime.exec,Processs,但是个人感觉这样效率可能有点低,不如交给ids进行捕获效率更高

3.DevOps

因为进行hook时,asm中提供了大量有用的方法从而能够获得hook点处详细的信息:调用栈、代码行号、接口、父类等

rasp的缺陷

1.首先rasp拦截是侵入程序代码内部的,那么它实际上是和具体的语言强相关的,因此不同语言之间并不通用,需针对不同语言的特性进行开发

2.rasp是对关键函数进行hook,那么意味着无论攻击路径从哪条路走,最终都将汇集于某一个点,因此高效率的拦截要求设计rasp的hook规则时,开发者本身即必须对各种漏洞的利用方式以及一些关键函数点熟悉,因此存在遗漏的可能。

甲方如何应用rasp

1.直接根据开源的openrasp来进行二次开发,针对企业具体应用进行适配

问题:推广周期长,运维难度大,以及要保证现有的业务在布置rasp后仍旧能够正常运行,有一定的风险

2.在现有的APM程序上(cat,wiseapm)进行修改,弥补推广的周期,在稳定性也有一定的保证,只需要将rasp的一些想法加入到APM程序中,企业快速实践部署IAST/RASP的一种新思路 - FreeBuf网络安全行业门户这篇文章中介绍到平安银行是利用cat搜集的一些信息进行输出进行审计,比如apm本身就自带一些监控sql语句执行的功能

结合扫描器

如果能够得到具体的hook日志,则可以

1.流量设置标志位,对所有测试流量加某种标志位,如果hook的某个点有标志位进入,则认为该处可能存在漏洞(存在拼接且有入口)(例如sql注入,程序内部也可能有很多sql执行,这样能筛选出外部输入)

2.黑名单检测,检测hook点处函数入参是否在黑名单内,比如反序列化gadget的关键sink的黑名单或者sql注入的一些payload的黑名单(规则可以参考waf),sql注入还可以判断单引号的个数

3.判断request url中的参数和hook点处的参数是否相同,相同则为存在安全漏洞,hook点处的value是否包含一些敏感字符,比如sql注入的反斜杠 空格等关键payload

rasp和应用引入的第三方jar包之间版本问题

本地测试:

premain方式插桩,rasp引入fastjsonj1.2.48,应用引入fastjson1.2.47,使用1.2.47通杀payload进行尝试:

a.插桩之前:

b.插桩之后:

直接报autotype not support,说明此时理论上应用在使用fastjson时自动使用了rasp的1.2.48版本的fastjson

列出的jvm进程id来看,agent并非以独立进程运行,而是和应用统一jvm进程,用的同一个jvm虚拟机,那么类加载器都用的同一套,因此实际上类加载时,肯定不存在冲突问题,相同的类只会加载一次,此时主要就是看不同版本是加载哪里的,如下图所示:

此时可以看到应用在使用fastjson时将加载rasp指定目录下的fastjson,难道并不是智能地选择高版本????此时控制变量,改变rasp引入的fastjson为1.2.47,应用使用1.2.48,效果如下:

弹了。。。

因此结论为:

如果rasp中引入的第三方jar包的版本和应用引入的第三方jar包版本不同,此时应用在使用jar包时将优先加载rasp引入的对应版本的jar包。

参考

http://blog.nsfocus./rasp-tech/  已看

浅谈RASP技术攻防之基础篇 - FreeBuf网络安全行业门户 已看

https://www.03sec.com/3239.shtml 例子

https://toutiao.io/posts/4kt0al/preview 例子

https://paper.seebug.org/1041/

https://www.cnblogs.com/2014asm/p/10834818.html  有例子

Java RASP浅析--以百度OpenRASP为例 | c0d3p1ut0s  讲openrasp

多种姿势openrasp命令执行绕过-安全客 - 安全资讯平台 rasp绕过

OpenRASP梳理总结 - FreeBuf网络安全行业门户  openrasp梳理

OpenRASP 初探(二)之项目部署_openrasp部署-CSDN博客

IAST原理分析以及在SDL中的应用 - FreeBuf网络安全行业门户  rasp的应用

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

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

相关文章

Java并发编程:ThreadPoolExecutor详细源码解析与应用

Thread直接创建线程的弊端 在开始解析ThreadPoolExecutor类之前&#xff0c;让我们先来了解直接创建线程所带来的弊端。在Java中&#xff0c;线程是资源密集型对象&#xff0c;每当需要并发执行任务时&#xff0c;直接创建新线程会带来以下问题&#xff1a; 资源消耗 每个线…

贪吃蛇身子改进加贪吃蛇向右移动

1. 蛇移动的思想&#xff1a; 其实就是删除头节点 &#xff0c;增加尾节点&#xff1b;一句代码搞定 struct Snake *p; p head; head head -> next; free(p) 防止造成多的空间节点 2.增加尾节点代码思想&#xff1a; 2.1 .开辟new 节点的空间 struct Snake *new (stru…

解决Android studio更换sdk地址后flutter项目显示no device selected

问题描述 因为之前sdk的路径在c盘上&#xff0c;经常在更新或下在sdk后c盘饱满&#xff0c;于是就更换了sdk的路径&#xff0c;更换sdk路径后就导致flutter项目在选择设备的时候出现no device selected 找不到设备&#xff0c;但是在device Manager可以看到物理设备或者是虚拟…

Java设计模式:使用责任链模式和状态模式优化‘审批流程‘

Java设计模式&#xff1a;使用责任链模式和状态模式优化审批流程 摘要引言 需求流程图正文内容&#x1f4d0; 基本概念介绍 功能实现示例1:设计模式&#xff1a;责任链模式方法&#xff1a;好处&#xff1a; 示例2:设计模式&#xff1a;责任链模式方法和操作流程&#xff1a;好…

【canvas】前端创造的图片粒子动画效果:HTML5 Canvas 技术详解

前端创造的图片粒子动画效果&#xff1a;HTML5 Canvas 技术详解 我们将深入探讨如何通过 HTML5 的 Canvas 功能&#xff0c;将上传的图片转换成引人入胜的粒子动画效果。这种效果将图片分解成小粒子&#xff0c;并在用户与它们交互时产生动态变化。我们将分步骤详细解析代码&a…

EasyRecovery数据恢复软件2025永久免费电脑版下载

EasyRecovery数据恢复软件是一款业界知名的数据恢复工具&#xff0c;它凭借强大的恢复能力和广泛的数据兼容性&#xff0c;帮助用户从各种存储设备中恢复丢失或删除的数据。以下是关于EasyRecovery数据恢复软件的详细介绍。 EasyRecovery绿色破解下载网盘链接: https://pan.ba…

自动驾驶行业源代码防泄漏解决方案

行业背景&#xff1a; 随着新一代信息通信及人工智能技术的快速发展&#xff0c;汽车作为这些新技术应用的重要载体&#xff0c;正在加速向智能化和网联化转型&#xff0c;以自动驾驶研发为主业的企业也越来越多&#xff0c;如何保障自己研发的算法、模型、系统不被研发人员离…

Linux入门攻坚——20、systemd、(sysvinit、upstart重温)

再一次讲到Linux系统启动流程&#xff1a; POST --> Boot Sequence --> Bootloader(grub) --> kernel initramfs(initrd) --> rootfs --> /sbin/init 对于init&#xff0c;即系统内核加载完毕后&#xff08;加载kernel和切换根文件系统&#xff09;运行…

STM32H750外设ADC之开始和结束数据转换功能

目录 概述 1 开始转换 1.1 使能ADSTART 1.2 使能JADSTART 1.3 ADSTART 通过硬件清零 2 转换时序 3 停止正在进行的转换&#xff08; ADSTP、 JADSTP&#xff09; 3.1 停止转换功能实现 3.2 停止转换流程图 概述 本文主要讲述了STM32H750外设ADC之开始和结束数据转换…

CentOS8/RHEL8 root密码破解

我们知道root是CentOS8/RHEL8系统的管理员用户&#xff0c;一般情况下&#xff0c;我们是不会把其密码忘记的&#xff0c;如果万一忘记了&#xff0c;如果破解root密码呢&#xff0c;今天就为大家详细讲讲。 1.CentOS8/RHEL8 root密码破解 1.默认安装及默认配置情况下&#x…

实践遥感场景目标检测,基于YOLOv8全系列【n/s/m/l/x】参数模型开发构建遥感场景下MSTAR数据基础上的目标检测识别系统

遥感相关的实践在我们前面的系列博文中也有相关的一些实践&#xff0c;基于MASTAR数据集开发构建对应的目标检测系统在前文也有一些介绍&#xff0c;感兴趣的话可以自行移步阅读即可&#xff1a; 《基于YOLOv7开发构建MSTAR雷达影像目标检测系统》 《基于yolov5n的轻量级MSTA…

前端开发攻略---用原生JS在网页中也能实现语音识别

1、语音识别的过程 语音识别涉及三个过程&#xff1a;首先&#xff0c;需要设备的麦克风接收这段语音&#xff1b;其次&#xff0c;语音识别服务器会根据一系列语法 (基本上&#xff0c;语法是你希望在具体的应用中能够识别出来的词汇) 来检查这段语音&#xff1b;最后&#xf…

Git操作与异常处理

文章目录 常用操作1、代码拉取2、代码提交3、暂存区状态4、提交代码5、推送远程仓库 异常处理【1】报错信息&#xff1a;Cannot pull into a repository with state: MERGING【2】报错信息&#xff1a;You have not concluded your merge (MERGE_HEAD exists)【3】报错信息&…

【网络编程】网络编程概念 | TCP和UDP的区别 | UDP数据报套接字编程 | Socket

文章目录 网络编程一、什么是网络编程1.TCP和UDP的区别 二、UDP数据报套接字编程DatagramSocketDatagramPacket回显服务器&#xff08;echo server&#xff09; 网络编程 一、什么是网络编程 通过网络&#xff0c;让两个主机之间能够进行通信。基于通信来完成一定的功能。 ​…

2.7设计模式——Proxy 代理模式(结构型)

意图 为其它对象提供一种代理以控制这个对象的访问。 结构 Proxy保存一个引用使得代理可以访问实体&#xff1b;提供一个与Subject的接口相同的接口&#xff0c;使代理可以用来替代实体&#xff1b;控制实体的存取&#xff0c;并可能负责创建和删除它&#xff1b;其他功能依赖…

[实验]Keil 4下仿真三星2440A芯片的汇编及CPIO控制实验

一、安装Keil uVision4 (详细安装过程忽略) 点击finish完成安装 二、新建项目&#xff0c;导入项目文件 选择对应的芯片&#xff0c;此处我们选择三星的S3C2440A&#xff0c;点击OK 在Source Group 1处右键&#xff0c;点击Add Files to "Sourcce Group 1’…将下图…

Linux之ebpf(1)基础使用

Linux之ebpf(1)基础使用 Author: Once Day Date: 2024年4月20日 一位热衷于Linux学习和开发的菜鸟&#xff0c;试图谱写一场冒险之旅&#xff0c;也许终点只是一场白日梦… 漫漫长路&#xff0c;有人对你微笑过嘛… 全系列文章可以参考专栏&#xff1a;Linux基础知识_Once-D…

非对称渐开线齿轮学习笔记分享

最近有小伙伴遇到了非对称渐开线齿轮的加工问题,花了些时间学习了解一下,下面是总结的学习笔记,有兴趣的朋友可以瞅瞅: 目录: 为什么要采用非对称? 非对称有什么优点? 非对称齿形如何加工? 非对称齿轮怎么测量? 非对称齿轮建模 为什么要采用非对称? 现在的传动要求…

关于浏览器360导航无法更改

当前环境场景&#xff1a; 浏览器&#xff1a;Microsoft Edge 版本 121.0.2277.106 (正式版本) (64 位) 系统&#xff1a;Windows 11 家庭中文版 23H2 问题描述 首先出现这种情况会让我们非常的气愤但是又束手无策&#xff0c;看到这个页面简直就恨的牙根痒痒&#xff0c;但是…

14.MMD导入Blender及贴图步骤

MMD导出.abc文件 在MMD十周年桥版本导入一个人物模型&#xff0c;这里导入仆人 注意MMD的路径不能有中文 点击上面的MMDBridge 设定 第一个选择blender by 第二个选择实行 这里是选择帧数范围和帧率 帧率一定要是30&#xff0c;不然后面可能会出问题 点击文件导出视频…