Java openrasp记录-02

news2025/1/15 6:46:14

主要分析以下四个部分:

1.openrasp agent

这里主要进行插桩的定义,其pom.xml中定义了能够当类重新load时重定义以及重新转换

 

这里定义了两种插桩方式对应之前安装时的独立web的jar的attach或者修改启动脚本添加rasp的jar的方式

 

其中init操作则需要将rasp.jar添加到Bootstrap路径中,因为后面修改字节码时将涉及到bootstraploader加载的一些类,正常情况下由rasp位于System class path根据类加载机制是拦截不到的bootstrapclassloader的类加载路径下的class,加入到Bootstrapclassloader的搜索路径下以后,才能拦截到

 

 接着调用Moduleloader.load,通过选择mode(premain或者agentmain),action(install或者uninstall),该类主要进行加载和初始化引擎模块rasp-engine.jar

 

load方法将会根据选择的action来new一个moduloader,传入模式和inst

 

moduleLoader中将使用rasp引擎jar文件new一个ModuleContainer容器(static代码块主要完成获取rasp.jar路径以及设置moduleclassloader),然后启动该引擎容器,传入插桩方式mode和插桩实例inst

 

启动引擎函数:

根据加载的agent\java\engine下面的主类来启动rasp引擎

 也就是rasp-engine.jar的manifest.mf里面所定义的EngineBoot类的start方法,模块名为rasp-engine,采用低版本的1.6.0_45打包可以兼容高版本

 

2.openrasp engine

主要的一些rasp具体的操作逻辑,包括hook操作

 根据第一部分初始化的最后一个阶段调用rasp引擎模块的start方法,对应Engineboot类,所以直接定位到该类:

public class EngineBoot implements Module { //该类是实现Moudle接口的,因此可以调用start方法

    private CustomClassTransformer transformer; //定义类转换器

    @Override
    public void start(String mode, Instrumentation inst) throws Exception {
        System.out.println("\n\n" + //rasp打印标志
                "   ____                   ____  ___   _____ ____ \n" +
                "  / __ \\____  ___  ____  / __ \\/   | / ___// __ \\\n" +
                " / / / / __ \\/ _ \\/ __ \\/ /_/ / /| | \\__ \\/ /_/ /\n" +
                "/ /_/ / /_/ /  __/ / / / _, _/ ___ |___/ / ____/ \n" +
                "\\____/ .___/\\___/_/ /_/_/ |_/_/  |_/____/_/      \n" +
                "    /_/                                          \n\n");
        try {
            Loader.load(); //加载v8引擎,用于解释js
        } catch (Exception e) {
            System.out.println("[OpenRASP] Failed to load native library, please refer to https://rasp.baidu.com/doc/install/software.html#faq-v8-load for possible solutions.");
            e.printStackTrace();
            return;
        }
        if (!loadConfig()) { //进行rasp引擎的初始化配置
            return;
        }
        //缓存rasp的build信息
        Agent.readVersion();
        BuildRASPModel.initRaspInfo(Agent.projectVersion, Agent.buildTime, Agent.gitCommit);
        // 初始化js插件系统
        if (!JS.Initialize()) {
            return;
        }
        CheckerManager.init(); //初始化所有类型的checker,包括js插件检测,java本地检测,服务器基线检测
        initTransformer(inst);
        if (CloudUtils.checkCloudControlEnter()) {
            CrashReporter.install(Config.getConfig().getCloudAddress() + "/v1/agent/crash/report",
                    Config.getConfig().getCloudAppId(), Config.getConfig().getCloudAppSecret(),
                    CloudCacheModel.getInstance().getRaspId());
        }
        deleteTmpDir();
        String message = "[OpenRASP] Engine Initialized [" + Agent.projectVersion + " (build: GitCommit="
                + Agent.gitCommit + " date=" + Agent.buildTime + ")]";
        System.out.println(message);
        Logger.getLogger(EngineBoot.class.getName()).info(message);
    }

    @Override
    public void release(String mode) {
        CloudManager.stop();
        CpuMonitorManager.release();
        if (transformer != null) {
            transformer.release();
        }
        JS.Dispose();
        CheckerManager.release();
        String message = "[OpenRASP] Engine Released [" + Agent.projectVersion + " (build: GitCommit="
                + Agent.gitCommit + " date=" + Agent.buildTime + ")]";
        System.out.println(message);
    }

    private void deleteTmpDir() {
        try {
            File file = new File(Config.baseDirectory + File.separator + "jar_tmp");
            if (file.exists()) {
                FileUtils.deleteDirectory(file);
            }
        } catch (Throwable t) {
            Logger.getLogger(EngineBoot.class.getName()).warn("failed to delete jar_tmp directory: " + t.getMessage());
        }
    }

    /**
     * 初始化配置
     *
     * @return 配置是否成功
     */
    private boolean loadConfig() throws Exception {
        LogConfig.ConfigFileAppender();  //初始化log4j的logger
        //单机模式下动态添加获取删除syslog
        if (!CloudUtils.checkCloudControlEnter()) {
            LogConfig.syslogManager();
        } else {
            System.out.println("[OpenRASP] RASP ID: " + CloudCacheModel.getInstance().getRaspId());
        }
        return true;
    }

    /**
     * 初始化类字节码的转换器
     *
     * @param inst 用于管理字节码转换器
     */
    private void initTransformer(Instrumentation inst) throws UnmodifiableClassException {
        transformer = new CustomClassTransformer(inst);
        transformer.retransform();
    }

}
v8的引擎的初始化,调用的为本地java代码的initalize方法

    public synchronized static boolean Initialize() {
        try {
            if (!V8.Initialize()) {
                throw new Exception("[OpenRASP] Failed to initialize V8 worker threads");
            }
            V8.SetLogger(new com.baidu.openrasp.v8.Logger() { //设置v8的logger
                @Override
                public void log(String msg) {
                    PLUGIN_LOGGER.info(msg);
                }
            });
            V8.SetStackGetter(new com.baidu.openrasp.v8.StackGetter() { //设置v8获取栈信息的getter方法,这里获得的栈信息,每一条信息包括类名、方法名和行号classname@methodname(linenumber)
                @Override
                public byte[] get() {
                    try {
                        ByteArrayOutputStream stack = new ByteArrayOutputStream();
                        JsonStream.serialize(StackTrace.getParamStackTraceArray(), stack);
                        stack.write(0);
                        return stack.getByteArray();
                    } catch (Exception e) {
                        return null;
                    }
                }
            });
            Context.setKeys();
            if (!CloudUtils.checkCloudControlEnter()) {
                UpdatePlugin(); //加载js插件到v8引擎中
                InitFileWatcher(); //启动对js插件的文件监控,从而实现热部署,动态的增删js中的检测规则
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            LOGGER.error(e);
            return false;
        }
    }

updatePlugin:

其中涉及到rasp hook功能的开关,关于rasp绕过的一种方式就是通过反射关掉这个引擎

 

接着获取到js插件的目录plugins

 

默认就是official.js,检测各种攻击的逻辑就写在里面,用js写实现热部署,并加载到v8引擎中

InitFileWatcher:

这里利用jnotify对js插件目录进行监控,用的代码是openrasp二次开发过的GitHub - baidu-security/openrasp-jnotify: 为 OpenRASP 专门定制开发的 jnotify 版本,支持 Mac、Win、Linux

public synchronized static void InitFileWatcher() throws Exception {
        boolean oldValue = HookHandler.enableHook.getAndSet(false); 
        if (watchId != null) { //监听器id
            FileScanMonitor.removeMonitor(watchId); //移除监听器
            watchId = null;
        }
        watchId = FileScanMonitor.addMonitor(Config.getConfig().getScriptDirectory(), new FileScanListener() {
            @Override
            public void onFileCreate(File file) {
                if (file.getName().endsWith(".js")) {
                    UpdatePlugin();
                }
            }

            @Override
            public void onFileChange(File file) {
                if (file.getName().endsWith(".js")) {
                    UpdatePlugin();
                }
            }

            @Override
            public void onFileDelete(File file) {
                if (file.getName().endsWith(".js")) {
                    UpdatePlugin();
                }
            }
        });
        HookHandler.enableHook.set(oldValue);
    }

addMonitor将传入监听目录和事件回调接口,最后返回监听器id,其中mask定义了创建+删除+修改三种模式,对应回调函数则重写了OnfileCreate、OnfileChange、OnfileDelete三种方法,只要是后缀为js的文件被创建、删除或者修改了则调用UpdatePlugin方法重新读取plugins目录下的检测js逻辑并重新加载到v8引擎中

 

 CheckerManager.init方法:

public class CheckerManager {

    private static EnumMap<Type, Checker> checkers = new EnumMap<Type, Checker>(Type.class);

    public synchronized static void init() throws Exception {
        for (Type type : Type.values()) {
            checkers.put(type, type.checker); //加载所有类型的检测放入checkers,type.checker就是某种检测对应的类
        }
    }

    public synchronized static void release() {
        checkers = null;
    }

    public static boolean check(Type type, CheckParameter parameter) {
        return checkers.get(type).check(parameter); //调用检测类进行参数检测
    }

}

包括使用js插件进行检测的,对应的是类V8AttackChecker,就是调用V8引擎加载js进行检测

本地检测的两种攻击:

 

另外一些也是是本地的类检查的,一些服务器安全配置检查,数据库连接以及日志检查

 

接着CheckManager.init结束以后,此时将初始换插桩用的转换器

 自定义classTransformer:

/*
 * Copyright 2017-2020 Baidu Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.baidu.openrasp.transformer;

import com.baidu.openrasp.ModuleLoader;
import com.baidu.openrasp.config.Config;
import com.baidu.openrasp.dependency.DependencyFinder;
import com.baidu.openrasp.detector.ServerDetectorManager;
import com.baidu.openrasp.hook.AbstractClassHook;
import com.baidu.openrasp.messaging.ErrorType;
import com.baidu.openrasp.messaging.LogTool;
import com.baidu.openrasp.tool.annotation.AnnotationScanner;
import com.baidu.openrasp.tool.annotation.HookAnnotation;
import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.LoaderClassPath;
import org.apache.log4j.Logger;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.lang.ref.SoftReference;
import java.security.ProtectionDomain;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentSkipListSet;

/**
 * 自定义类字节码转换器,用于hook类的方法
 */
public class CustomClassTransformer implements ClassFileTransformer {
    public static final Logger LOGGER = Logger.getLogger(CustomClassTransformer.class.getName());
    private static final String SCAN_ANNOTATION_PACKAGE = "com.baidu.openrasp.hook"; //hook的类所在的包,hook的类都有对应的注解标注
    private static HashSet<String> jspClassLoaderNames = new HashSet<String>(); //保存要用到的一些类加载器
    private static ConcurrentSkipListSet<String> necessaryHookType = new ConcurrentSkipListSet<String>(); 
    private static ConcurrentSkipListSet<String> dubboNecessaryHookType = new ConcurrentSkipListSet<String>(); //dubbo要hook的类型
    public static ConcurrentHashMap<String, SoftReference<ClassLoader>> jspClassLoaderCache = new ConcurrentHashMap<String, SoftReference<ClassLoader>>();

    private Instrumentation inst;
    private HashSet<AbstractClassHook> hooks = new HashSet<AbstractClassHook>(); //各种攻击对应的hook类的实例
    private ServerDetectorManager serverDetector = ServerDetectorManager.getInstance();

    public static volatile boolean isNecessaryHookComplete = false; //volatile修饰,保证多线程下该共享变量的可见性,值更改后立即刷新到主存,工作线程才能够从内存中取到新的值
    public static volatile boolean isDubboNecessaryHookComplete = false; //dubbo的hook

    static {
        jspClassLoaderNames.add("org.apache.jasper.servlet.JasperLoader");  //类加载要用到的一些类加载器
        jspClassLoaderNames.add("com.caucho.loader.DynamicClassLoader");
        jspClassLoaderNames.add("com.ibm.ws.jsp.webcontainerext.JSPExtensionClassLoader");
        jspClassLoaderNames.add("weblogic.servlet.jsp.JspClassLoader");
        dubboNecessaryHookType.add("dubbo_preRequest");
        dubboNecessaryHookType.add("dubboRequest");
    }

    public CustomClassTransformer(Instrumentation inst) {
        this.inst = inst;
        inst.addTransformer(this, true);
        addAnnotationHook(); //在这要操作所有带hook注解的类了,虽然看注解用上貌似效率慢一点,但是这里用起来感觉还是很方便
    }

    public void release() {
        inst.removeTransformer(this);
        retransform();
    }

    public void retransform() {
        LinkedList<Class> retransformClasses = new LinkedList<Class>();
        Class[] loadedClasses = inst.getAllLoadedClasses();
        for (Class clazz : loadedClasses) {
            if (isClassMatched(clazz.getName().replace(".", "/"))) {
                if (inst.isModifiableClass(clazz) && !clazz.getName().startsWith("java.lang.invoke.LambdaForm")) {
                    try {
                        // hook已经加载的类,或者是回滚已经加载的类
                        inst.retransformClasses(clazz);
                    } catch (Throwable t) {
                        LogTool.error(ErrorType.HOOK_ERROR,
                                "failed to retransform class " + clazz.getName() + ": " + t.getMessage(), t);
                    }
                }
            }
        }
    }

    private void addHook(AbstractClassHook hook, String className) { //正常情况下将添加所有带注解的hook点
        if (hook.isNecessary()) { //默认是false
            necessaryHookType.add(hook.getType()); //每种hook类对应一个type,例如读文件、删除文件、xxe、ognl
        }
        String[] ignore = Config.getConfig().getIgnoreHooks(); //拿到不hook的类名,支持配置的
        for (String s : ignore) {
            if (hook.couldIgnore() && (s.equals("all") || s.equals(hook.getType()))) { //hook点可以忽略
                LOGGER.info("ignore hook type " + hook.getType() + ", class " + className);
                return;
            }
        }
        hooks.add(hook);
    }

    private void addAnnotationHook() {
        Set<Class> classesSet = AnnotationScanner.getClassWithAnnotation(SCAN_ANNOTATION_PACKAGE, HookAnnotation.class); //取到所有带HookAnnotaion.class注解的类
        for (Class clazz : classesSet) { 
            try {
                Object object = clazz.newInstance(); //实例化每种攻击对应的hook类
                if (object instanceof AbstractClassHook) {
                    addHook((AbstractClassHook) object, clazz.getName());
                }
            } catch (Exception e) {
                LogTool.error(ErrorType.HOOK_ERROR, "add hook failed: " + e.getMessage(), e);
            }
        }
    }

    /**
     * 过滤需要hook的类,进行字节码更改
     *
     * @see ClassFileTransformer#transform(ClassLoader, String, Class, ProtectionDomain, byte[])
     */
    @Override
    public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
                            ProtectionDomain domain, byte[] classfileBuffer) throws IllegalClassFormatException {  //transform也就是实际插桩生效的地方,loadclass到jvm中时触发
        if (loader != null) {
            DependencyFinder.addJarPath(domain); 
          //因为用到的class可能是某个jar包中的,因此这里根据当前保护域去找到当前load的class的绝对路径,若其存在,则将对应的jar包加到loadedJarPath中
        }
        if (loader != null && jspClassLoaderNames.contains(loader.getClass().getName())) { //如果当前的类加载器是jsp相关的类加载器
            jspClassLoaderCache.put(className.replace("/", "."), new SoftReference<ClassLoader>(loader)); 
        //这里用softReference对jsp相关的classloader进行弱引用封装,SoftReference 所指向的对象,当没有强引用指向它时,会在内存中停留一段的时间,
            后面jvm再根据内存情况(堆上情况)和SoftReference.get来决定要不要回收该对象,弱引用封装的对象通过get拿到对象的强引用再使用对象,这里是为了防止classloader内存泄露
        }
        for (final AbstractClassHook hook : hooks) { //对添加到hooks中的所有类别的hook点进行遍历
            if (hook.isClassMatched(className)) {  //此时要判断要hook的类名
                CtClass ctClass = null;
                try {
                    ClassPool classPool = new ClassPool(); //要用到javaassist技术改变字节码了
                    addLoader(classPool, loader);  //初始化class文件的搜索路径
                    ctClass = classPool.makeClass(new ByteArrayInputStream(classfileBuffer));
                    if (loader == null) {
                        hook.setLoadedByBootstrapLoader(true);
                    }
                    classfileBuffer = hook.transformClass(ctClass);
                    if (classfileBuffer != null) {
                        checkNecessaryHookType(hook.getType());
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    if (ctClass != null) {
                        ctClass.detach();
                    }
                }
            }
        }
        serverDetector.detectServer(className, loader, domain);
        return classfileBuffer;
    }

    private void checkNecessaryHookType(String type) {
        if (!isNecessaryHookComplete && necessaryHookType.contains(type)) {
            necessaryHookType.remove(type);
            if (necessaryHookType.isEmpty()) {
                isNecessaryHookComplete = true;
            }
        }

        if (!isDubboNecessaryHookComplete && dubboNecessaryHookType.contains(type)) {
            dubboNecessaryHookType.remove(type);
            if (dubboNecessaryHookType.isEmpty()) {
                isDubboNecessaryHookComplete = true;
            }
        }
    }


    public boolean isClassMatched(String className) {
        for (final AbstractClassHook hook : getHooks()) {
            if (hook.isClassMatched(className)) {
                return true;
            }
        }
        return serverDetector.isClassMatched(className);
    }

    private void addLoader(ClassPool classPool, ClassLoader loader) {
        classPool.appendSystemPath(); //添加jvm启动时的一些搜索路径比如扩展类,rt.jar或者classpath下的类
        classPool.appendClassPath(new ClassClassPath(ModuleLoader.class));
        if (loader != null) {
            classPool.appendClassPath(new LoaderClassPath(loader));
        }
    }

    public HashSet<AbstractClassHook> getHooks() {
        return hooks;
    }

}

hook的相关类

 

 判断是不是某个注解的hook类对应的要进行插桩的class

 

3.openrasp安装时的一些检测代码

其中App.java为安装rasp的主程序

 

根据nodetect选择安装模式:

nodetect模式下attach方法:

找到服务器对应的启动脚本并修改

 

不同系统支持的平台如下所示:

 

 operateServer主要在这个阶段要完成的是:

1.根据不同的操作系统种类使用不同的工厂类,调用工厂类的getInstaller来根据nodetect参数判断目标程序是否是以springboot型的独立jar启动选择GenericInstaller模式安装(此时将定义不需要修改启动shell脚本去插入一下启动rasp的配置项,直接使用attach模式根据提供的pid进行attach)。若nodetect为false,则要探测一些服务器的标志文件去判断目标服务器种类拿到Installer的实例,后面则要根据不同服务器种类去修改相应的服务器的shell启动脚本添加加载rasp的配置项

2.拿到GenericInstaller或者Installer后调用其install方法进行rasp的安装,Installer的install调用中需要去找到服务器的启动脚本添加配置项

4.openrasp的攻击检测插件,检查攻击的源码

之前分析到rasp在初始化js插件时将会把plugins下的js文件加载到v8引擎中,来实现热部署,这部分检测逻辑代码太多啦,这里对于不同语言使用js来实现检测逻辑,从而实现通用检测,我只关心java相关的漏洞检查,除了下面列出的一些CVE,还包括java的一些通用漏洞的检测,这部分单独将进行研究。

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

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

相关文章

贪吃蛇项目(小白保姆级教程)

游戏介绍 游戏背景&#xff1a; 贪吃蛇游戏是经典的游戏项目之一&#xff0c;也是很简单的小游戏 实现背景&#xff1a; 这里我们是基于32位的Win32_API进行实现的 需要的知识点&#xff1a; C语言函数、枚举、结构体、动态内存管理、预处理指令、链表、Win32_API等 适合人群&a…

【学习AI-相关路程-工具使用-自我学习-cudavisco-开发工具尝试-基础样例 (2)】

【学习AI-相关路程-工具使用-自我学习-cuda&visco-开发工具尝试-基础样例 &#xff08;2&#xff09;】 1、前言2、环境说明3、总结说明4、工具安装0、验证cuda1、软件下载2、插件安装 5、软件设置与编程练习1、创建目录2、编译软件进入目录&创建两个文件3、编写配置文…

OpenNJet评测,探寻云原生之美

在信息时代的大海上&#xff0c;云原生应用引擎如一艘航行于波涛之间的帆船&#xff0c;承载着创新的梦想和数字化的未来。本文将带领您登上这艘船&#xff0c;聚焦其中之一的OpenNJet&#xff0c;一同探寻其中的奥秘和精妙&#xff0c;领略其独特之美。 OpenNJet 内容浅析 O…

【0day】湖南建研工程质量检测系统InstrumentUsageRecordExport接口处存在任意文件读取漏洞

免责声明&#xff1a;文章来源互联网收集整理&#xff0c;请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;所产生的一切不良后果与文章作者无关。该…

Flink窗口理论到实践 | 大数据技术

⭐简单说两句⭐ ✨ 正在努力的小叮当~ &#x1f496; 超级爱分享&#xff0c;分享各种有趣干货&#xff01; &#x1f469;‍&#x1f4bb; 提供&#xff1a;模拟面试 | 简历诊断 | 独家简历模板 &#x1f308; 感谢关注&#xff0c;关注了你就是我的超级粉丝啦&#xff01; &a…

P1873 [COCI 2011/2012 #5] EKO / 砍树

原题链接&#xff1a;[COCI 2011/2012 #5] EKO / 砍树 - 洛谷 目录 1. 题目描述 2. 思路分析 3. 代码实现 1. 题目描述 2. 思路分析 二分答案。 根据题目中的“帮助 Mirko 找到伐木机锯片的最大的整数高度 H&#xff0c;使得他能得到的木材至少为 M 米。换句话说&#xf…

【redis】redis持久化分析

目录 持久化Redis持久化redis持久化的方式持久化策略的设置1. RDB&#xff08;快照&#xff09;fork(多进程)RDB配置触发RDB备份自动备份手动执行命令备份&#xff08;save | bgsave&#xff09;flushall命令主从同步触发动态停止RDB RDB 文件恢复验证 RDB 文件是否被加载 RDB …

【毕业设计】基于SSM的运动用品商城的设计与实现

1.项目介绍 在这个日益数字化和信息化的时代&#xff0c;随着人们购物习惯的转变&#xff0c;传统的实体商店已经无法满足人们日益增长的在线购物需求。因此&#xff0c;基于SSM&#xff08;Spring Spring MVC MyBatis&#xff09;框架的运动用品商城项目应运而生&#xff0…

基于YOLOv8+PyQt5复杂场景下船舶目标检测系统

1. 应用场景 复杂场景下船舶目标检测系统的应用场景包括&#xff1a; 港口管理和安全&#xff1a;监控港口区域&#xff0c;确保船舶安全地进出港口&#xff0c;预防相撞事故的发生。 海洋交通监控&#xff1a;实时追踪海上交通流&#xff0c;并识别违规或异常航行行为&#x…

基于Java.Web框架React、Vue.js技术开发的一套(C#医院体检系统成品源码、支持二开)

医院体检系统是一种专为体检中心/医院体检科等体检机构开发的全流程管理系统。该系统通过软件实现检测仪器数据的自动提取&#xff0c;内置多级医生工作台&#xff0c;细化工作并将体检检查结果汇总&#xff0c;生成体检报告登记到计算机系统中。此外&#xff0c;该系统还能进行…

对XYctf的一些总结

对XYctf的一些总结 WEB 1.http请求头字段 此次比赛中出现的&#xff1a; X-Forwarded-For/Client-ip&#xff1a;修改来源ip via&#xff1a;修改代理服务器 还有一些常见的字段&#xff1a; GET&#xff1a;此方法用于请求指定的资源。GET请求应该安全且幂等&#xff0c…

C++学习笔记——仿函数

文章目录 仿函数——思维导图仿函数是什么仿函数的优势理解仿函数仿函数的原理举例 仿函数——思维导图 仿函数是什么 使用对象名调用operator&#xff08;&#xff09;函数看起来像是在使用函数一样&#xff0c;因此便有了仿函数的称呼&#xff1b;仿函数存在的意义是&#x…

揭秘!如何利用自动化工具提升抖音推广效果

亲爱的读者朋友们&#xff0c;你是否在为抖音的推广效果而苦恼&#xff1f;看着别人家的视频轻松获得大量曝光&#xff0c;你是否也心生羡慕&#xff1f;今天&#xff0c;我们就来分享一个秘密武器&#xff0c;让你轻松提升抖音推广效果&#xff01; 首先&#xff0c;让我们来了…

Maria DB 安装(含客户端),看这一篇就够了

文章目录 一 安装前准备1 版本与Win平台对应2 推荐安装 二 安装步骤1 安装主体程序2 添加系统路径Path 三 客户端 一 安装前准备 1 版本与Win平台对应 版本对应关系可参考&#xff1a; https://www.codebye.com/mariadb-deprecated-package-platforms.html。 2 推荐安装 经…

Ansible 自动化运维工具 - 了解和模块应用

目录 一. Ansible 的相关知识 1.1 Ansible 工具的简介 1.2 Ansible的四大组件 1.3 运维自动化工具 1.4 Ansible 和其它自动化运维工具对比 1.5 Ansible 的优缺点 二. Ansible 环境安装部署 2.1 管理端安装 ansible 2.2 配置主机清单 三. ansible 命令行模块 3.1 comm…

SpringBoot+Vue+Element-UI实现协同过滤算法商品推荐系统

前言介绍 本次设计任务是要设计一个基于协同过滤算法的商品推荐系统&#xff0c;通过这个系统能够满足商品推荐系统的管理功能。系统的主要包括首页&#xff0c;个人中心&#xff0c;用户管理&#xff0c;商品类型管理&#xff0c;商品信息管理&#xff0c;系统管理&#xff0…

Java请求第三方接口的一些步骤

一、前言 Java请求第三方接口的一些步骤。 在Java中请求第三方接口通常涉及以下步骤。这些步骤涵盖了从准备请求到处理响应的整个过程。 1. 确定接口详情 接口URL&#xff1a;你要请求的URL。请求方法&#xff1a;如GET、POST、PUT、DELETE等。请求参数&#xff1a;包括URL…

Vue中Element的下载

打开vscode让项目在终端中打开 输入npm install element-ui2.15.3 然后进行下载 在node_modules中出现element-ui表示下载完成 然后在输入Vue.use(ElementUI); import Vue from vue import App from ./App.vue import router from ./router import ElementUI from element-ui…

Python 机器学习 基础 之 构建第一个机器学习应用

Python 机器学习 基础 之 构建第一个机器学习应用 目录 Python 机器学习 基础 之 构建第一个机器学习应用 一、简单介绍 二、第一个机器学习测试应用介绍&#xff1a;鸢尾花分类 三、第一个机器学习测试应用 &#xff1a;前置环境&#xff0c;知识点介绍 jupyter notebo…

数据结构十一:数组相关经典面试题

本篇博客详细介绍分析数组/顺序表常见的面试题&#xff0c;对于前面所学知识进行一个巩固&#xff0c;同时介绍一些力扣刷题中的一些概念&#xff1a;如&#xff1a;输出型参数等&#xff0c;在刷题中培养自己的编程思维&#xff0c;掌握常见的编程套路&#xff0c;形成题感&am…