一个快速简单的工具来分析SSL/TLS协议数据帧 (C/C++代码实现)

news2024/9/25 19:19:37

SSL/TLS协议是网络安全通信的基石,它通过在客户端和服务器之间建立一个加密的通道,确保数据传输的安全性和完整性。SSL(Secure Sockets Layer)最初由Netscape公司开发,而TLS(Transport Layer Security)则是SSL的后续版本,由IETF标准化。通常,我们提到的SSL/TLS协议是指这两种协议的集合。

SSL/TLS协议的工作原理

SSL/TLS协议的工作原理主要包括握手阶段和数据传输阶段。在握手阶段,客户端和服务器通过交换信息来协商加密算法、验证身份,并生成用于数据加密的对话密钥。这个过程包括客户端问候、服务器问候、证书交换、密钥交换等步骤。一旦握手完成,双方就会使用协商的密钥来加密和解密传输的数据。

TLS记录的相关原理包括:

  1. 分片(Fragmentation):应用层数据被分割成多个TLS记录,每个记录都有自己的头部和有效载荷。这样做可以确保数据块的大小适合网络传输,并且可以独立处理。

  2. 头部(Header):每个TLS记录的头部包含了一些重要信息,如内容类型(例如,握手消息、应用数据等)、版本号、长度等。

  3. 有效载荷(Payload):记录的有效载荷部分包含了实际要传输的数据。在传输之前,数据会被加密,以确保数据的机密性和完整性。

  4. 加密:TLS记录在传输前会被加密,以防止数据在传输过程中被窃听。加密过程使用了在TLS握手阶段协商的加密算法和密钥。

  5. 消息认证码(MAC):为了提供数据完整性,每个TLS记录都会计算一个MAC。这个MAC会在解密后验证,以确保数据在传输过程中没有被篡改。

  6. 压缩:在加密之前,TLS记录的有效载荷可能会被压缩,以减少传输数据的大小。

  7. 序列号:每个TLS记录都有一个序列号,用于确保记录的顺序和防止重放攻击。

  8. 握手协议:在TLS握手阶段,通信双方会协商加密算法、密钥交换参数等,为后续的TLS记录加密和认证提供基础。

TLS记录的设计使得它能够适应不同的网络环境和应用需求,同时提供了强大的安全保障。RFC 8446是TLS 1.3的官方文档,它详细描述了TLS协议的规范,包括TLS记录的处理方式。如果你需要查看RFC 8446的具体内容,可以访问RFC 8446。如果链接无法访问,可能是因为网络问题或链接本身的问题,请检查链接的有效性或稍后再试。

在实际应用中,SSL/TLS协议广泛应用于HTTPS、SMTPS、FTPS等安全通信场景。通过使用SSL/TLS协议,可以确保网站和用户之间的通信安全,防止数据被窃取或篡改。

为了分析SSL/TLS协议的数据帧,可以使用像Wireshark这样的网络抓包工具。Wireshark能够捕获网络数据包,并展示SSL/TLS协议的握手过程、加密算法、证书信息等。通过Wireshark的抓包分析,可以直观地了解SSL/TLS协议的实际执行过程,包括客户端和服务器之间的消息交换、加密套件的选择、证书的验证以及最终的密钥交换。

在进行SSL/TLS协议分析时,需要注意的是,随着网络安全威胁的不断演变,SSL/TLS协议也面临着一些挑战和漏洞。因此,我们需要不断关注协议的发展动态,并采取相应的措施来保护数据安全。同时,证书管理、服务器和客户端的配置、定期更新以及安全审计等方面也是确保SSL/TLS协议有效性的关键因素。

什么是TLS record?

传输层安全性(TLS),也称为安全套接层(SSL),是通信协议中广泛使用的标准之一,支持应用层(如HTTP、SMTP等)数据传输的加密保护隧道。TLS通过将消息分割成一系列名为TLS记录的可管理块,在两个通信对等体之间提供安全通道。一旦两台主机成功协商了加密密钥和保密参数,每条记录都会在选定的密钥材料下受到独立保护。加密对高层(即应用层)的有效载荷检查施加了限制;然而,在其他方面,具有讽刺意味的是,记录提供了一些新的重要信息,如密码套件、证书、服务器名称指示(SNI)等的偏好。在这方面,记录流被认为反映了通信方的独特应用性质或行为特征。

一个快速简单的工具来分析SSL/TLS协议数据帧 (C/C++代码实现)

...



/* Flow status */
#define F_SAW_SYN              0x1
#define F_SAW_SYNACK           0x2
#define F_END_SYN_HS           0x4
#define F_END_FIN_HS           0x8
#define F_BASE_SEQ_SET        0x10
#define F_LOST_HELLO          0x20
#define F_FRAME_OVERLAP       0x40

/* TCP packet status */
#define TCP_A_ACK_LOST_PACKET                0x1
#define TCP_A_DUPLICATE_ACK                  0x2
#define TCP_A_KEEP_ALIVE                     0x4
#define TCP_A_KEEP_ALIVE_ACK                 0x8
#define TCP_A_LOST_PACKET                   0x10
#define TCP_A_FAST_RETRANSMISSION           0x20
#define TCP_A_OUT_OF_ORDER                  0x40
#define TCP_A_SPURIOUS_RETRANSMISSION       0x80
#define TCP_A_RETRANSMISSION               0x100
#define TCP_A_WINDOW_FULL                  0x200
#define TCP_A_WINDOW_UPDATE                0x400
#define TCP_A_ZERO_WINDOW                  0x800
#define TCP_A_ZERO_WINDOW_PROBE           0x1000
#define TCP_A_ZERO_WINDOW_PROBE_ACK       0x2000
#define TCP_A_NON_RECORD                  0x4000

#define MAX_RECORD_LEN       0x4800
#define MAX_QUEUE_CAPACITY       50

static const std::map<uint8_t, std::pair<std::string, uint8_t>> recordType = {
    { 20, { "Change Cipher Spec", 18 } },
    { 21, { "Alert", 5 } },
    { 22, { "Handshake", 9 } },
    { 23, { "Application Data", 16 } }
};

static const std::map<uint8_t, std::pair<std::string, uint8_t>> handshakeType = {
    { 0,  { "(hello request)", 15} },
    { 1,  { "(client hello)", 14} },
    { 2,  { "(server hello)", 14} },
    { 3,  { "(hello verify request)", 22} },
    { 4,  { "(new session ticket)", 20} },
    { 5,  { "(end of early data)", 19} },
    { 6,  { "(hello retry request)", 21} },
    { 8,  { "(encrypted extensions)", 22} },
    { 11, { "(certificate)", 13} },
    { 12, { "(server key exchange)", 21} },
    { 13, { "(certificate request)", 21} },
    { 14, { "(server hello done)", 19} },
    { 15, { "(certificate verify)", 20} },
    { 16, { "(client key exchange)", 21} },
    { 21, { "(certificate url)", 17} },
    { 22, { "(certificate status)", 20} },
    { 23, { "(supplemental data)", 19} },
    { 24, { "(key update)", 12} },
    { 25, { "(compressed certificate)", 24} }
};

namespace pump
{

    /* 用于保存捕获偏好的数据结构 */
    struct CaptureConfig
    {
        uint32_t maxPacket;         
        uint32_t maxTime;           
        uint32_t maxRcd;            
        uint32_t maxRcdpf;          
        bool outputTypeHex;         
        bool quitemode;             
        std::string outputFileTo;   
    };

    /* 用于解决记录解析例程的数据结构 */
    struct RecordPointer{
        uint16_t rcd_len;           
        uint16_t rcd_pos;           
        uint16_t hs_len;            
        uint16_t hs_pos;            
        uint8_t prev_rcd_type;      
        uint8_t hd[9];              
    };

    /* 此结构包含段边界信息 */
    struct SegInfo{
        uint32_t seq = 0;           
        uint16_t seglen = 0;        
        bool is_newrcd = false;    

        bool operator<(const SegInfo& other) const
        {
            return (seq < other.seq);
        }

        bool operator==(const SegInfo& other) const
        {
            return (seq == other.seq);
        }
    };

    /* 保存流数据的数据结构 */
    struct Flow {
        uint32_t ip = 0;
        uint16_t port = 0;
        uint32_t win = 0xFFFFFFFF;
        uint32_t baseseq = 0;
        uint16_t flags = 0;
        uint16_t a_flags = 0;
        uint32_t nextseq = 0;
        uint32_t lastack = 0;
        uint16_t rcd_cnt = 0;
        uint16_t rcd_idx = 0;
        RecordPointer rcd_pt = {0,0,0,0,0,{}};
        std::set<SegInfo> reserved_seq = {};
    };

    /* 保持双向流信息的数据结构 */
    struct Stream {
        Flow client;
        Flow server;
    };

    uint32_t hashStream(pump::Packet* packet);

    bool isTcpSyn(pump::Packet* packet);

    bool isClient(pump::Packet* packet, Stream* ss);

    bool isTLSrecord(uint8_t* data, uint32_t seglen);

    bool isSSLv2record(uint8_t* data, uint32_t seglen);

    bool isUnencryptedHS(uint8_t curr_rcd_type, uint8_t prev_rcd_type);

    class Assembly
    {

        private:

            uint32_t ab_pkt_cnt;
            uint32_t ab_flow_cnt;
            uint32_t ab_rcd_cnt;
            uint64_t ab_totalbytes;

            bool ab_stop;

            struct timeval ab_init_tv, ab_print_tv;

            std::map<uint32_t, int> ab_flowtable;

            std::map<uint32_t, bool> ab_initiated;

            std::map<uint32_t, Stream> ab_smap;

            int addNewStream(pump::Packet* packet);

            int getStreamNumber(pump::Packet* packet);

            void writeTLSrecord(int idx, bool peer);

            void displayTLSrecord(Stream* ss, bool peer, uint8_t rcd_type, uint8_t hs_type);

            void cleanOldPacket(int idx, bool peer, Flow* fwd, CaptureConfig* config);

            void parseReservedPacket(int idx, bool peer, uint32_t seq, CaptureConfig* config);

        public:

            Assembly(timeval tv);

            ~Assembly();

            void registerEvent();

            uint32_t getTotalPacket() { return ab_pkt_cnt; };

            uint32_t getTotalStream() { return ab_flow_cnt; }

            uint32_t getTotalRecord() { return ab_rcd_cnt; }

            uint64_t getTotalByteLen() { return ab_totalbytes; }

            bool isTerminated() {return ab_stop; }

            void parsePacket(pump::Packet* packet, CaptureConfig* config);

            void managePacket(pump::Packet* packet, CaptureConfig* config);

            void mergeRecord(CaptureConfig* config);

            void close();

    };

}

...

namespace pump
{

    std::string currTime();

    void clearTLSniff();

    class EventHandler
    {

        public:

            typedef void (*EventHandlerCallback)(void* cookie);

            static EventHandler& getInstance()
            {
                static EventHandler instance;
                return instance;
            }

            void onInterrupted(EventHandlerCallback handler, void* cookie);

        private:

            EventHandlerCallback h_interrupt_handler;
            void* h_interrupt_cookie;

            static void handlerRoutine(int signum);

    };

}

...
namespace pump
{

    class Packet
    {

        protected:
            
            uint8_t* pk_data;
            uint16_t pk_datalen;
            bool pk_delete_data;
            uint16_t pk_linktype;
            uint8_t pk_proto_types;
            Layer* pk_firstlayer;
            Layer* pk_lastlayer;

            void Init();

            Layer* initLayer(uint16_t linktype);

        public:

            Packet();

            Packet(const uint8_t* data, uint16_t datalen, bool delete_rawdata, uint16_t layertype = LINKTYPE_ETHERNET);

            ~Packet() { clearData(); }

            bool setData(const uint8_t* data, uint16_t datalen, uint16_t layertype = LINKTYPE_ETHERNET);

            template<class TLayer> TLayer* getLayer() const;

            template<class TLayer> TLayer* getNextLayer(Layer* layertype) const;

            const uint8_t* getData() const { return pk_data; }

            uint16_t getDataLen() const { return pk_datalen; }

            uint8_t getProtocolTypes() const { return pk_proto_types; }

            bool isTypeOf(uint8_t protocol) const { return pk_proto_types & protocol; }

            void clearData();

    };

    template<class T> T* Packet::getLayer() const
    {
        if (dynamic_cast<T*>(pk_firstlayer) != NULL)
            return (T*)pk_firstlayer;

        return getNextLayer<T>(pk_firstlayer);
    }

    template<class T> T* Packet::getNextLayer(Layer* layertype) const
    {
        if (layertype == NULL)
            return NULL;

        Layer* curr_layer = layertype->getNextLayer();
        while ((curr_layer != NULL) && (dynamic_cast<T*>(curr_layer) == NULL))
        {
            curr_layer = curr_layer->getNextLayer();
        }

        return (T*)curr_layer;
    }

}

...
namespace pump
{

    class LiveReader;

    typedef void (*OnPacketArrival)(Packet* packet, LiveReader* lrdr, void* cookie);

    class Reader
    {

        protected:

            bool rdr_on;
            pcap_t* rdr_descriptor;

            Reader(): rdr_on(false), rdr_descriptor(NULL) {}
        
        public:

            virtual ~Reader() {}

            virtual bool open() = 0;

            virtual void close() = 0;

    };

    class PcapReader : public Reader
    {

        protected:

            char* prdr_datasrc;
            uint16_t prdr_linktype;

        public:

            PcapReader(const char* pcapfile);

            ~PcapReader() { close(); }

            static PcapReader* getReader(const char* pcapfile);

            bool open();

            bool getNextPacket(Packet& packet);

            void close();

    };

    class LiveReader : public Reader
    {

        protected:

            char* lrdr_datasrc;
            uint16_t lrdr_linktype;
            bool lrdr_on_capture;
            bool lrdr_on_stop;
            OnPacketArrival lrdr_pkt_arrival;
            void* lrdr_pkt_arrival_cookie;
            pthread_t lrdr_thread_capture;

            pcap_t* LiveInit();

            static void* captureThreadMain(void* ptr);

            static void onPacketArrival(uint8_t* user, const struct pcap_pkthdr* pkt_hdr, const uint8_t* packet);

        public:

            LiveReader(pcap_if_t* pInterface, bool calculateMTU, bool calculateMacAddress, bool calculateDefaultGateway);

            ~LiveReader() { close(); }

            bool open();

            void startCapture(OnPacketArrival onPacketArrival, void* onPacketArrivalCookie);

            void stopCapture();

            const char* getName() const { return lrdr_datasrc; }

            uint16_t getLinkType() const { return lrdr_linktype; }

            void close();

    };

    class LiveInterfaces
    {

        private:

            std::vector<LiveReader*> li_ifacelist;

            LiveInterfaces();

        public:

            static LiveInterfaces& getInstance()
            {
                static LiveInterfaces instance;
                return instance;
            }

            LiveReader* getLiveReader(const std::string& name) const;

    };

}
...


static struct option TLSniffOptions[] =
{
    {"count",  required_argument, 0, 'c'},
    {"duration",  required_argument, 0, 'd'},
    {"interface",  required_argument, 0, 'i'},
    {"rcd-count", required_argument, 0, 'm'},
    {"rcd-count-perflow",  required_argument, 0, 'l'},
    {"input-file",  required_argument, 0, 'r'},
    {"output-file", required_argument, 0, 'w'},
    {"quite-mode", no_argument, 0, 'q'},
    {"byte-type", no_argument, 0, 'x'},
    {"help", no_argument, 0, 'h'},
    {0, 0, 0, 0}
};

/* 处理数据包转储的结构 */
struct PacketArrivedData
{
    pump::Assembly* assembly;
    struct pump::CaptureConfig* config;
};


...

/* 开始从发现的网络接口收集记录信息 */
void doTLSniffOnLive(pump::LiveReader* rdr, struct pump::CaptureConfig* config)
{
    // 打开网络接口进行捕获
    if (!rdr->open())
        EXIT_WITH_CONFERROR("###ERROR : Could not open the device");

    PacketArrivedData data;
    pump::Assembly assembly(init_tv);
    data.assembly = &assembly;
    data.config = config;
    rdr->startCapture(packetArrive, &data);

    while(!assembly.isTerminated())
        sleep(1);

    rdr->stopCapture();

    if(!(config->quitemode)) printf("\n");

    pump::print_progressM(assembly.getTotalPacket());
    printf(" **%lu Bytes**\n", assembly.getTotalByteLen());

    if(config->outputFileTo != "")
    {
        assembly.registerEvent();
        assembly.mergeRecord(config);
    }

    // Close the capture pipe
    assembly.close();
    delete rdr;
}

/* 开始从发现的网络接口收集记录信息 */
void doTLSniffOnPcap(std::string pcapFile, struct pump::CaptureConfig* config)
{
    pump::PcapReader* rdr = pump::PcapReader::getReader(pcapFile.c_str());
    
    // 打开pcap文件进行捕获
    if (!rdr->open())
        EXIT_WITH_CONFERROR("###ERROR : Could not open input pcap file");

    pump::Assembly assembly(init_tv);
    pump::Packet packet;

    // 以无休止的循环运行,直到用户按下Ctrl+C
    // 或者程序遇到文件末尾
    while(rdr->getNextPacket(packet) && !assembly.isTerminated())
    {
        assembly.managePacket(&packet, config);
    }

    if(!(config->quitemode)) printf("\n");

    pump::print_progressM(assembly.getTotalPacket());
    printf(" **%lu Bytes**\n", assembly.getTotalByteLen());

    // 将所有捕获的记录写入指定文件
    if(config->outputFileTo != "")
    {
        assembly.registerEvent();
        assembly.mergeRecord(config);
    }

    assembly.close();
    delete rdr;
}

int main(int argc, char* argv[])
{
...

    // 使用命令行选项中的值设置首选项
    while((opt = getopt_long (argc, argv, "c:d:i:l:m:r:w:qxh", TLSniffOptions, &optionIndex)) != -1)
    {
        switch (opt)
        {
            case 0:
                break;
            case 'c':
                maxPacket = atoi(optarg);
                break;
            case 'd':
                maxTime = atoi(optarg);
                break;
            case 'i':
                readPacketsFromInterface = optarg;
                break;
            case 'l':
                maxRcdpf = atoi(optarg);
                break;
            case 'm':
                maxRcd = atoi(optarg);
                break;
            case 'r':
                readPacketsFromPcap = optarg;
                break;
            case 'w':
                outputFileTo = optarg;
                break;
            case 'q':
                quitemode = true;
                break;
            case 'x':
                outputTypeHex = true;
                break;
            case 'h':
                printUsage();
                break;
            default:
                printUsage();
                exit(-1);
        }
    }

    // 如果没有提供输入pcap文件或网络接口,则退出并出错
    if (readPacketsFromPcap == "" && readPacketsFromInterface == "")
        EXIT_WITH_OPTERROR("###ERROR : Neither interface nor input pcap file were provided");

    // 应只选择一个选项:pcap或接口-带错误退出
    if (readPacketsFromPcap != "" && readPacketsFromInterface != "")
        EXIT_WITH_OPTERROR("###ERROR : Choose only one option, pcap or interface");

    // 不允许出现负值
    if (maxPacket <= 0)
        EXIT_WITH_OPTERROR("###ERROR : #Packet can't be a non-positive integer");

    if (maxTime <= 0)
        EXIT_WITH_OPTERROR("###ERROR : Duration can't be a non-positive integer");

    if (maxRcd <= 0)
        EXIT_WITH_OPTERROR("###ERROR : #Record can't be a non-positive integer");

    if (maxRcdpf <= 0)
        EXIT_WITH_OPTERROR("###ERROR : #Record per flow can't be a non-positive integer");

    struct pump::CaptureConfig config = {
        .maxPacket = maxPacket,
        .maxTime = maxTime,
        .maxRcd = maxRcd,
        .maxRcdpf = maxRcdpf,
        .outputTypeHex = outputTypeHex,
        .quitemode = quitemode,
        .outputFileTo = outputFileTo
    };

...
    if (readPacketsFromPcap != "")
    {
        doTLSniffOnPcap(readPacketsFromPcap, &config);
    }
    else
    {
        pump::LiveReader* rdr = pump::LiveInterfaces::getInstance().getLiveReader(readPacketsFromInterface);

        if (rdr == NULL)
            EXIT_WITH_CONFERROR("###ERROR : Couldn't find interface by provided name");

        doTLSniffOnLive(rdr, &config);
    }

    // 清除临时目录中的内容
    pump::clearTLSniff();

    printf("**All Done**\n");
    WRITE_LOG("===Process Finished");
    return 0;
}


If you need the complete source code, please add the WeChat number (c17865354792)

使用Pcap文件提取TLS记录:

sudo tlsniff -r example.pcap

它将打印两台主机之间传输的记录消息及其方向

在wireshark

实时网络接口上的TLS记录提取:

sudo tlsniff -i eth0 -w example.csv

以不那么冗长的模式编写一个csv文件:

在1分钟(60秒)内捕获TLS记录

sudo tlsniff -d 60 -i eth0 -w example.csv

捕获前100条TLS记录

sudo tlsniff -m 100 -i eth0 -w example.csv

代码用途

尽管有如此多的网络流量分析器支持对TLS层的检查,但由于包括重传、乱序数据包、数据包丢失等在内的几个问题,它们很难保持记录帧的原始形式和顺序。此外,单个记录的每个部分都可能占据不同数据包有效载荷内的连续比特,因为记录大小可能大于数据包传递所允许的最大传输单元。即使记录的大小小到可以放置在单个数据包中,TLS协议也不喜欢频繁传输数据块。

因此,它将一束连续的记录塞进一个数据包中,剩余的位被截断并传递给下一个数据包包含,以此类推。大多数工具包旨在分析数据包级的传递,而不仅仅是TLS特定的消息。与这些相比,旨在从分段的有效载荷数据中重建原始TLS记录流。此工具从pcap格式文件或实时网络接口读取数据包数据,并在输出文件上写入共享同一对源/目标IP地址和端口号的每个TCP会话的记录序列。

总结

TLS(Transport Layer Security)记录是TLS协议中用于封装和传输数据的基本单元。TLS是一种安全协议,用于在计算机网络上提供加密通信和数据完整性保障。TLS记录层位于TLS协议栈的较低层,它负责将应用层数据(如HTTP、SMTP等)分割成小块,并为这些数据块提供封装、加密、MAC(Message Authentication Code)计算和压缩等功能。

We also undertake the development of program requirements here. If necessary, please follow the WeChat official account 【程序猿编码】and contact me

参考:https://datatracker.ietf.org/doc/html/rfc8446

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

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

相关文章

Renesa Version Board开发RT-Thread 之UART驱动应用

目录 概述 1 硬件介绍 2 软件配置 2.1 RT-Thread Studio配置参数 2.2 FSP配置MCU 3 RT-Thread中UART的接口介绍 3.1 RT-Thread UART简介 3.2 RT-Thread 下的UART接口 4 UART的应用 4.1 应用功能实现 4.2 源代码文件 5 测试 程序下载地址&#xff1a; RenesaVersio…

Open3D 最小点约束的体素滤波(34)

Open3D 最小点约束的体素滤波(34) 一、算法介绍二、算法流程三、算法代码四、实现效果一、算法介绍 体素滤波可以达到快速减少点云数量的目的,而海量点云往往是需要这一步骤才可以进行后续处理的,比如配准时使用千万级别的原始点云配准显然是不合理的,因此体素滤波是比较重…

Mybatis搭建配置文件基础用法参数传递

背景介绍 Mybatis原是Apache的一个开源项目iBatis 2010碾开发团队转移到谷歌旗下改名为Mybatis Mybatis介绍 Mybatis是一个优秀的数据持久层框架(dao层 数据访问层 数据持久层) Mybatis是对jdbc进行封装,比见面jdbc这手动设置参数,手动映射结果的操作 Mybatis是将jdbc中的…

高变比LLC谐振变换器中“十”字型低匝比平面变压器设计

导语 为了解决高变比LLC谐振变换器的变压器绕组匝数过多、绕组结构复杂这一技术瓶颈背后的核心科学问题&#xff0c;本文提出一种“十”字型低匝比平面变压器&#xff0c;用于高变比LLC谐振变换器。 1.0引言 大数据中心的建设离不开供配电系统—电源设备的建设&#xff0c;为大…

15天速通java基础:java(J2SE)阶段学习总结(数据类型、数组、方法、面向对象、异常处理、容器、流、多线程、网络编程)

有一段时间没有写博客了&#xff0c;我这段时间去学习java了&#xff0c;谁也不会想到&#xff0c;短短两周的时间&#xff0c;我的java学习已经学习了不少东西了&#xff0c;毕竟python这座山也可以去攻java这块玉&#xff0c;对应python那就是基础的大部分内容&#xff0c;不…

吴恩达机器学习课后作业-06支持向量机(SVM)

SVM 线性可分SVM题目绘制决策边界改变C,观察决策边界代码 线性不可分SVM核函数代码 寻找最优C、gamma垃圾邮件过滤 线性可分SVM 题目 数据分布 绘制决策边界 import numpy as np import matplotlib.pyplot as plt import scipy.io as sio from scipy.optimize import minimi…

电子电路学习之二极管-1

特别感谢&#xff1a;B站博主&#xff1a;唐老师讲电赛 工科男孙老师 本文图片出自两位博主视频 再次感谢 1 半导体 1. 本征半导体 (Intrinsic Semiconductor) 定义&#xff1a;本征半导体是指纯净的、不掺杂任何杂质的半导体材料&#xff0c;如硅&#xff08;Si&#xff09;…

日本人形机器人仿真环境搭建

机器人 官网在这里 http://ai2001.ifdef.jp/uvc/uvc.html 我们可以下载他们的源码 ODE引擎安装 这个是刚体动力学模拟引擎&#xff0c;我用的是windows&#xff0c;大家可以自己参考着官网来&#xff08;http://ode.org/wiki/index.php/Manual#Install_and_Use&#xff09;…

【SpringBoot】自定义spring-boot-starter

目录 定义和目的# 命名规范# 准备阶段# 开发步骤# 一、创建 Starter 项目# 二、导入必要的依赖# 三、编写属性类# 四、自定义业务类# 五、编写自动配置类# 六、编写 spring.factories# ​编辑 七、编写配置提示文件# 八、测试 starter# 测试 starter# 一、导入自…

如何玩转CentOS Linux内核升级?手把手教你内核编译升级至最新版本

文章目录 如何玩转CentOS Linux内核升级&#xff1f;手把手教你内核编译升级至最新版本1 升级环境2 升级需求2.1 升级前的内核版本2.2 升级后的内核版本 3 升级步骤3.1 安装编译环境3.2 更新GCC版本3.2.1 多GCC版本共存3.2.2 永久替换旧的GCC 3.3 下载内核源代码并解压3.4 配置…

《数据结构》(408代码题及应用题)(王道收编真题)

一、线性表 1、线性表的线性表示 分析&#xff1a; “循环”左移&#xff0c;那这个循环就应该是我们需要重点思考的点。先考虑最简单的我们可以设置两个数组&#xff0c;其中一个数组保存的是原数据&#xff0c;另一个初始为空。接着想要实现循环左移就只需要找出相对应的位…

2002-2023年中债国债3年期到期收益率

国债是一种政府发行的债券&#xff0c;它为投资者提供了一种相对安全的投资渠道&#xff0c;因为背后有国家信用的支撑。国债的发行可以帮助政府筹集资金&#xff0c;用于公共支出、基础设施建设、社会福利等项目。国债通常分为两种形式&#xff1a;固定利率国债和浮动利率国债…

PHP 7.4.21 development server 源码泄露漏洞复现

原漏洞地址&#xff1a;https://blog.projectdiscovery.io/php-http-server-source-disclosure/ 版本&#xff1a;PHP<7.4.21 漏洞成因&#xff1a; 通过&#xff1a;PHP -s 开启的内置web服务器存在源码泄露漏洞&#xff0c;可以将PHP文件作为静态代码直接输出源码 POC&…

【区间dp】 P1775 石子合并(弱化版) 题解

题目描述 设有 N ( N ≤ 300 ) N(N \le 300) N(N≤300) 堆石子排成一排&#xff0c;其编号为 1 , 2 , 3 , ⋯ , N 1,2,3,\cdots,N 1,2,3,⋯,N。每堆石子有一定的质量 m i ( m i ≤ 1000 ) m_i\ (m_i \le 1000) mi​ (mi​≤1000)。现在要将这 N N N 堆石子合并成为一堆。每…

20240825 每日AI必读资讯

超6亿&#xff01;文心大模型日调用量半年增长超10倍&#xff0c;AI成百度最强加速引擎 - 文心大模型日调用量超过6亿次&#xff0c;日均处理Tokens文本超1万亿。与23年Q4公布的5000万次日均调用量相比&#xff0c;半年增长超10倍。 - 第二季度&#xff0c;百度云业务营收51亿…

【计算机网络】socket网络编程 --- 实现简易UDP网络程序之字符串回响

&#x1f466;个人主页&#xff1a;Weraphael ✍&#x1f3fb;作者简介&#xff1a;目前正在学习c和算法 ✈️专栏&#xff1a;Linux &#x1f40b; 希望大家多多支持&#xff0c;咱一起进步&#xff01;&#x1f601; 如果文章有啥瑕疵&#xff0c;希望大佬指点一二 如果文章对…

【北京仁爱堂】脖子歪斜,拉扯疼痛怎么办?规律的生活让痉挛性斜颈的恢复事半功倍!

痉挛性斜颈是一种肌张力障碍性疾病&#xff0c;也是一种让人非常痛苦不堪的疾病&#xff0c;他不仅影响患者的外貌&#xff0c;也会对患者的身体和心理造成双重的打击&#xff0c;严重影响正常的生活&#xff0c;社交和工作。 痉挛性斜颈的病因尚不明确&#xff0c;因为做任何仪…

Java Web —— 第八天(登录功能)

基础登录功能 LoginController 类 RestController //用于处理 HTTP 请求 Slf4j //记录日志 RequestMapping("/login") public class LoginController {Autowiredprivate EmpService empService;PostMappingpublic Result login(RequestBody Emp emp){log.info(&quo…

FFmpeg的入门实践系列六(编程入门之常见处理流程)

欢迎诸位来阅读在下的博文~ 在这里&#xff0c;在下会不定期发表一些浅薄的知识和经验&#xff0c;望诸位能与在下多多交流&#xff0c;共同努力 文章目录 前期博客参考书籍一、FFmpeg常见的处理流程复制编解码器的参数完整代码 二、创建并写入音视频文件三、总结附页 前期博客…

《黑神话:悟空》中的实景三维建模

这几天&#xff0c;国产游戏《黑神话:悟空》终于面世&#xff0c;迅速引爆了全球游戏市场。作为一款以《西游记》为背景的3A级动作角色扮演游戏&#xff0c;《黑神话:悟空》不仅在文化表达上极具吸引力&#xff0c;其背后的技术实力更是令人瞩目。本文将深入探讨&#xff0c;3A…