通过安全日志读取WFP防火墙放行日志

前言

之前的文档中,描写了如何对WFP防火墙进行操作以及如何在防火墙日志中读取被防火墙拦截网络通讯的日志。这边文档,着重描述如何读取操作系统中所有被放行的网络通信行为。
读取系统中放行的网络通信行为日志,在win10之后的操作系统上,也可以通过前一篇提到的读取阻断日志的方式进行读取(以FWPM_NET_EVENT0.type字段区分),但是在较老的系统中却不支持直接读取。为了保持系统兼容性,可以通过读取操作系统安全日志(EventId:5156)的方式进行网络通信日志的采集。

需要注意的坑点

  1. 查询放行日志时需要注意,每个网络通信行为在日志中只会出现一条放行记录,对应的筛选器ID,只会是首次对其进行审计的过滤器ID。因此,如果有其他权重更高的子层对网络连接进行了审计时,就无法通过筛选器ID匹配的方式获取。如果有这方面需求的话,解决方法只能是尽可能将自身子层的权重设为最高。
  2. 网络日志中读取连入行为时,WIN10/2016/2019源IP和目的IP字段与其它更早的操作系统相反,需要特殊处理。连出行为无异常。

开启审计

采用读取安全日志的方式进行网络事件获取,首先需要在系统中开启审计功能。在代码里面也有多种方式可以开启,之后会单开一篇文档进行描述,在这里先手动开启。

  • 打开本地安全策略(开始——运行——secpol.msc),依次打开:安全设置——本地策略——审核策略如图
    在这里插入图片描述

  • 在右侧窗口中打开 审核对象 标签页,勾选 “成功” 复选框后,点击保存,即可开启网络访问的审计功能
    在这里插入图片描述

  • 右键单击 “我的电脑”——“管理”——“计算机管理”——“系统工具”——“事件查看器”——“Windows日志”——“安全”中,查看5156日志即可。
    在这里插入图片描述

网络通信日志默认情况下是开启状态,为了以防万一,每次获取之前需要使用代码开启一次。使用代码的开启方式下次单开文档分享。

使用WMI方式进行查询

使用ReadEventLog进行查询

优点:兼容性高,可支持XP/2003操作系统。读取性能高。
缺点:无法做过滤,在大量日志中提取少量日志时效率较低
使用ReadEventLog读取Windows的安全日志只需要三步即可,1、打开EventLog句柄;2、使用ReadEventLog循环读取日志;3、关闭EventLog句柄。具体API描述如下。

打开EventLog句柄

    HANDLE OpenEventLog( 
                LPCSTR lpUNCServerName, 
                LPCSTR lpSourceName 
            );
  • 输入参数
    • lpUNCServerName:远程服务器的名称。读取本地的话传入NULL即可。
    • lpSourceName:日志名称。这里读取安全日志传入“Serurity”。其他对应值:系统日志“System”,应用程序日志“Application”
  • 输出参数
    • 返回日志读取句柄。在ReadEventLog中使用,需要调用CloseEventLog手动关闭。

读取日志

BOOL ReadEventLog( 
                        HANDLE hEventLog, 
                        DWORD dwReadFlags, 
                        DWORD dwRecordOffset, 
                        LPVOID lpBuffer, 
                        DWORD nNumberOfBytesToRead, 
                        DWORD *pnBytesRead, 
                        DWORD *pnMinNumberOfBytesNeeded 
                );
  • 输入参数

    • hEventLog:需要读取日志的句柄,就是刚才OpenEventLog返回的那个
    • dwReadFlags:读取标志,可以选择从指定偏移读取(EVENTLOG_SEEK_READ)或按顺序读取(EVENTLOG_SEQUENTIAL_READ),也可以指定正序读取(EVENTLOG_FORWARDS_READ)或倒序读取(EVENTLOG_BACKWARDS_READ)
    • dwRecordOffset:当dwReadFlags中包含EVENTLOG_SEEK_READ时有效,表示开始的位置
    • lpBuffer:分配的缓冲区,由外部划分内存
    • nNumberOfBytesToRead:lpBuffer缓冲区的大小
    • pnBytesRead:返回接收字节数
    • pnMinNumberOfBytesNeeded:返回lpBuffer所需最小缓冲区大小。仅当lpBuffer过小时返回,可判断GetLastError()返回ERROR_INSUFFICIENT_BUFFER
      时有效。
  • 输出参数

    • 正常执行返回非0值,失败后返回0

    关闭EventLog句柄

BOOL CloseEventLog( 
                            HANDLE hEventLog 
                    );
  • 输入参数
    • hEventLog:事件句柄,由OpenEventLog返回
  • 输出参数
    • 成功与否,这玩意没啥好判断的。

关联结构体

typedef struct _EVENTLOGRECORD { 
                        DWORD Length; 
                        DWORD Reserved; 
                        DWORD RecordNumber; 
                        DWORD TimeGenerated; 
                        DWORD TimeWritten; 
                        DWORD EventID; 
                        WORD EventType; 
                        WORD NumStrings; 
                        WORD EventCategory; 
                        WORD ReservedFlags; 
                        DWORD ClosingRecordNumber; 
                        DWORD StringOffset; 
                        DWORD UserSidLength; 
                        DWORD UserSidOffset; 
                        DWORD DataLength; 
                        DWORD DataOffset; 
                        } EVENTLOGRECORD, *PEVENTLOGRECORD;
  • 参数说明
    • Length:当前结构体的长度,由于ReadEventLog是以内存块的方式返回,单次返回的内存块中可能包含多个,特别是为了节省资源,可能会在代码中刻意一次读取大量的EventLogRecord结构体。这些结构体在内存块中,就以Length参数作为分界线来进行分割

    • Reserved:保留,没有可以研究过用于啥

    • RecordNumber:日志序号,可配合ReadEventLog函数中的dwReadFlags参数和dwReadOffset参数设置读取的偏移地址

    • TimeGenerated:事件时间,转time_t就可以

    • TimeWrittern:日志写入时间,time_t格式

    • EventID:事件ID,最高两位代表严重性,第三位代表是否为系统事件,低16位代表在安全日志中可见的ID。

    • EventType:事件类型,

    • NumStrings:包含字符串数目

    • EventCategory:事件类别

    • ReservedFlags:保留

    • ClosingRecordNumber:保留

    • StringOffset:事件包含字符串起始地址的偏移。字符串按顺序依次在内存中存储,0长度的字符串代表结束。字符串的顺序,和在事件查看器中看到的顺序一致,如图
      在这里插入图片描述

    • UserSidLength/UserSidOffset: 用户SID的长度及偏移,与字符串类似

    • DataLength/DataOffset:数据部分的长度及偏移

参考代码

#include <iostream>
#include <windows.h>
#include <string>
#include <vector>

#define MAX_EVENTLOG_READONCE   2048
void ReadLogsByReadEventLogAPI()
{
    LPBYTE pEventLogBuffer = new(std::nothrow) BYTE[MAX_EVENTLOG_READONCE];
    if (pEventLogBuffer == NULL)
    {
        return;
    }
    ZeroMemory(pEventLogBuffer, MAX_EVENTLOG_READONCE);

    HANDLE hEventLog = OpenEventLog(NULL, L"Security");
    if (hEventLog == NULL)
    {
        delete[] pEventLogBuffer;
        pEventLogBuffer = NULL;
        return;
    }

    DWORD dwMemoryLen = MAX_EVENTLOG_READONCE;
    DWORD dwReaded = 0, dwMiniMemoryNeeded = 0;
    while (true)//  -_-|||
    {
        BOOL isSucc = ReadEventLog(hEventLog, EVENTLOG_BACKWARDS_READ | EVENTLOG_SEQUENTIAL_READ, 0, pEventLogBuffer, dwMemoryLen, &dwReaded, &dwMiniMemoryNeeded);
        if (isSucc == FALSE && GetLastError() == ERROR_INSUFFICIENT_BUFFER && dwMiniMemoryNeeded)
        {
            delete[] pEventLogBuffer;
            pEventLogBuffer = NULL;

            pEventLogBuffer = new (std::nothrow) BYTE[dwMiniMemoryNeeded];
            if (pEventLogBuffer == NULL)
            {
                break;
            }
            ZeroMemory(pEventLogBuffer, dwMiniMemoryNeeded);
            dwMemoryLen = dwMiniMemoryNeeded;
            isSucc = ReadEventLog(hEventLog, EVENTLOG_BACKWARDS_READ | EVENTLOG_SEQUENTIAL_READ, 0, pEventLogBuffer, dwMemoryLen, &dwReaded, &dwMiniMemoryNeeded);
        }

        if (isSucc == FALSE)
        {
            break;
        }

        int pos = 0;
        do 
        {
            EVENTLOGRECORD* pTempRecord = (EVENTLOGRECORD*)(pEventLogBuffer + pos);
            __time32_t occurTime = pTempRecord->TimeWritten;
            int nEventId = pTempRecord->EventID & 0xffff;
            if (nEventId != 5156)
            {
                pos = pos + pTempRecord->Length;
                continue;
            }

            std::vector<std::wstring> vecEventParam;

            LPBYTE pTempBuffer = (LPBYTE)pTempRecord + pTempRecord->StringOffset;
            int strCount = 0;
            while ((pTempBuffer < ((LPBYTE)pTempRecord + pTempRecord->Length)) && (strCount < pTempRecord->NumStrings))
            {
                int len = wcslen((wchar_t*)pTempBuffer);
                if (len == 0)
                {
                    break;
                }

                vecEventParam.push_back((wchar_t*)pTempBuffer);

                pTempBuffer = pTempBuffer + (len + 1) * sizeof(wchar_t);
                strCount++;
            }

            if (vecEventParam.size() > 10)
            {
                std::wcout << "\n\n=========================================" << std::endl;
                std::wcout << L"Source:\t" << vecEventParam[3] << L"[" << vecEventParam[4] << L"]" << std::endl;
                std::wcout << L"Destination:\t" << vecEventParam[5] << L"[" << vecEventParam[6] << L"]" << std::endl;
                std::wcout << L"Protocol Code:\t" << vecEventParam[7] << std::endl;
                std::wcout << L"Process Id:\t" << vecEventParam[0] << std::endl;
                std::wcout << L"Process Name:\t" << vecEventParam[1] << std::endl;
                std::wcout << "=========================================\n\n" << std::endl;
            }
            pos = pos + pTempRecord->Length;
        } while (pos < dwReaded);
    }

    CloseEventLog(hEventLog);
    
    delete[] pEventLogBuffer;
    pEventLogBuffer = NULL;
}

输出截图
在这里插入图片描述

使用Evt系列API进行查询

优点:性能高,可自由配置过滤条件,方便在海量日志中检索,可读取的内容相当丰富
缺点:不支持xp/2003操作系统
Evt系列API涉及到的功能较多,如果只需要读取系统日志的话,只需要枚举、遍历、读取、关闭四步即可完成。

枚举当前日志

EVT_HANDLE EvtQuery( 
                                EVT_HANDLE Session, 
                                LPCWSTR Path, 
                                LPCWSTR Query, 
                                DWORD Flags 
                        );

遍历日志,获取下一条

BOOL EvtNext( 
                                    EVT_HANDLE ResultSet, 
                                    DWORD EventsSize, 
                                    PEVT_HANDLE Events, 
                                    DWORD Timeout, 
                                    DWORD Flags, 
                                    PDWORD Returned 
                                );

读取日志内容

BOOL EvtRender( 
                                    EVT_HANDLE Context, 
                                    EVT_HANDLE Fragment, 
                                    DWORD Flags, 
                                    DWORD BufferSize, 
                                    PVOID Buffer, 
                                    PDWORD BufferUsed, 
                                    PDWORD PropertyCount 
                            );
  • 输入参数
    • Context:
    • Fragment:
    • Flags:
    • BufferSize:
    • Buffer:
    • BufferUsed:
    • PropertyCount:
  • 输出参数
    *

关闭枚举句柄

BOOL EvtClose( 
                            EVT_HANDLE Object 
                        );

实例代码


void parseEventXML(std::wstring wstrXML)
{
    std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> cv;
    std::string utfXML = cv.to_bytes(wstrXML);

    tinyxml2::XMLDocument rootXML;
    if (rootXML.Parse(utfXML.c_str()) != tinyxml2::XML_SUCCESS)
    {
        return;
    }

    tinyxml2::XMLNode* rootNode = rootXML.FirstChild();
    if (rootNode == NULL)
    {
        return;
    }

    std::string processId;
    std::string processName;
    std::string sourceAddress;
    std::string sourcePort;
    std::string destAddress;
    std::string destPort;
    std::string protocol;

    tinyxml2::XMLNode* eventNode = rootXML.FirstChildElement("Event");
    if (eventNode)
    {
        tinyxml2::XMLElement* eventDataNode = eventNode->FirstChildElement("EventData");
        if (eventDataNode == NULL)
        {
            return;
        }
        tinyxml2::XMLElement* dataNode = eventDataNode->FirstChildElement("Data");
        do 
        {
            std::string strName = dataNode->Attribute("Name");
            if (_stricmp(strName.c_str(), "ProcessID") == 0)
            {
                processId = dataNode->GetText();
            }
			else if (_stricmp(strName.c_str(), "Application") == 0)
			{
				processName = dataNode->GetText();
			}
            else if (_stricmp(strName.c_str(), "SourceAddress") == 0)
			{
				sourceAddress = dataNode->GetText();
			}
            else if (_stricmp(strName.c_str(), "SourcePort") == 0)
			{
				sourcePort = dataNode->GetText();
			}
            else if (_stricmp(strName.c_str(), "DestAddress") == 0)
			{
				destAddress = dataNode->GetText();
			}
            else if (_stricmp(strName.c_str(), "DestPort") == 0)
			{
				destPort = dataNode->GetText();
			}
            else if (_stricmp(strName.c_str(), "Protocol") == 0)
			{
				protocol = dataNode->GetText();
			}

            dataNode = dataNode->NextSiblingElement("Data");
        } while (dataNode);

		std::cout << "\n\n=========================================" << std::endl;
		std::cout << "Source:\t" << sourceAddress << "[" << sourcePort << "]" << std::endl;
		std::cout << "Destination:\t" << destAddress << "[" << destPort << "]" << std::endl;
		std::cout << "Protocol Code:\t" << protocol << std::endl;
		std::cout << "Process Id:\t" << processId << std::endl;
		std::cout << "Process Name:\t" << processName << std::endl;
		std::cout << "=========================================\n\n" << std::endl;
    }
}

void ReadLogsByEvtAPI()
{
    DWORD dwEventLogBufferLen = MAX_EVENTLOG_READONCE;
	LPBYTE pEventLogBuffer = new(std::nothrow) BYTE[MAX_EVENTLOG_READONCE];
	if (pEventLogBuffer == NULL)
	{
		return;
	}
	ZeroMemory(pEventLogBuffer, MAX_EVENTLOG_READONCE);

    std::wstring wstrQuery = std::wstring(
        L"<QueryList>"
        L"	<Query>"
        L"		<Select>Event/System[EventID=5156]</Select>"
        L"	</Query>"
        L"</QueryList>");

    EVT_HANDLE hResult = EvtQuery(NULL, L"Security", wstrQuery.c_str(), EvtQueryChannelPath | EvtQueryReverseDirection);
    if (hResult == NULL)
    {
        delete[] pEventLogBuffer;
        pEventLogBuffer = NULL;
        return;
    }

    while (true) // -_-
    {

		EVT_HANDLE hEventArrs[MAX_PATH] = { 0 };
		DWORD dwReturnEvents = 0;
		BOOL isSucc = EvtNext(hResult, MAX_PATH, hEventArrs, INFINITE, 0, &dwReturnEvents);
		if (isSucc && dwReturnEvents)
		{
			for (int eventPos = 0; eventPos < dwReturnEvents; eventPos++)
			{
				DWORD dwBufferUsed = 0;
				DWORD dwPropertyCount = 0;
				int nBufferSize = dwEventLogBufferLen;
				ZeroMemory(pEventLogBuffer, nBufferSize);
				isSucc = EvtRender(NULL, hEventArrs[eventPos], EvtRenderEventXml, nBufferSize, pEventLogBuffer, &dwBufferUsed, &dwPropertyCount);
				if (isSucc == FALSE && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
				{
					delete[] pEventLogBuffer;
					pEventLogBuffer = NULL;

					dwEventLogBufferLen = dwBufferUsed + 2;
					pEventLogBuffer = new(std::nothrow) BYTE[dwEventLogBufferLen];
					if (pEventLogBuffer == NULL)
					{
						break;
					}

					nBufferSize = dwEventLogBufferLen;
					ZeroMemory(pEventLogBuffer, dwEventLogBufferLen);
					isSucc = EvtRender(NULL, hEventArrs[eventPos], EvtRenderEventXml, nBufferSize, pEventLogBuffer, &dwBufferUsed, &dwPropertyCount);
				}

				if (isSucc)
				{
					std::wstring strEventXML = std::wstring((wchar_t*)pEventLogBuffer);
					parseEventXML(strEventXML);
				}
			}
		}
    }

    if (pEventLogBuffer)
    {
		delete[] pEventLogBuffer;
		pEventLogBuffer = NULL;
    }
}

输出截图
在这里插入图片描述

备注

实例代码中解析xml使用的是tinyxml2库,对应github地址为:https://github.com/leethomason/tinyxml2/tree/master
当前最新版(9.0.0)版本下载地址:https://download.csdn.net/download/QQ1113130712/88235095

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Monitor.Analog烧机室|高温老化箱软件概要设计

Monitor.Analog产品老化试验软件概要设计&#xff1a; 1. 引言&#xff1a; 模拟量采集软件的目标是实现对模拟量信号的采集、处理和展示。该软件旨在提供一个用户友好的界面&#xff0c;允许用户配置采集参数、实时监测模拟量信号&#xff0c;并提供数据分析和导出功能。 2. 功…

LVS-DR集群(一台LVS,一台CIP,两台web,一台NFS)的构建以及LVS-DR模式工作原理和特点

一.LVS-DR工作模式原理和特点 1.工作模式 2.模式特点 二.构建环境 1.五台关闭防火墙&#xff0c;关闭selinux&#xff0c;拥有固定IP&#xff0c;部署有http服务的虚拟机&#xff0c;LVS设备下载ipvsadm工具&#xff0c;NFS 设备需要下载rpcbind和nfs-utils 2.实现功能 3…

【简单认识Docker基本管理】

文章目录 一、Docker概述1、定义2.容器化流行的原因3.Docker和虚拟机的区别4.Docker核心概念 二、安装docker三、镜像管理1.搜索镜像2.下载&#xff08;拉取&#xff09;镜像3.查看已下载镜像4.查看镜像详细信息5.修改镜像标签6.删除镜像7.导出镜像文件和拉取本地镜像文件8.上传…

ruoyi-vue-pro yudao 项目报表设计器 积木报表模块启用及相关SQL脚本

目前ruoyi-vue-pro 项目虽然开源&#xff0c;但是report模块被屏蔽了&#xff0c;查看文档却要收费 199元&#xff08;知识星球&#xff09;&#xff0c;价格有点太高了吧。 分享下如何启用 report 模块&#xff0c;顺便贴上sql相关脚本。 一、启用模块 修改根目录 pom.xml …

《安富莱嵌入式周报》第320期:键盘敲击声解码, 军工级boot设计,开源CNC运动控制器,C语言设计笔记,开源GPS车辆跟踪器,一键生成RTOS任务链表

周报汇总地址&#xff1a;嵌入式周报 - uCOS & uCGUI & emWin & embOS & TouchGFX & ThreadX - 硬汉嵌入式论坛 - Powered by Discuz! 视频版&#xff1a; https://www.bilibili.com/video/BV1Cr4y1d7Mp/ 《安富莱嵌入式周报》第320期&#xff1a;键盘敲击…

空洞卷积网络实现

代码中涉及的图片实验数据下载地址&#xff1a;https://download.csdn.net/download/m0_37567738/88235543?spm1001.2014.3001.5501 代码&#xff1a; import torch.nn as nn import numpy as npfrom matplotlib import pyplot as plt import time #from utils import get_ac…

【深入探究人工智能】:常见机器学习算法总结

文章目录 1、前言1.1 机器学习算法的两步骤1.2 机器学习算法分类 2、逻辑回归算法2.1 逻辑函数2.2 逻辑回归可以用于多类分类2.3 逻辑回归中的系数 3、线性回归算法3.1 线性回归的假设3.2 确定线性回归模型的拟合优度3.3线性回归中的异常值处理 4、支持向量机&#xff08;SVM&a…

基于ArcGis提取道路中心线

基于ArcGis提取道路中心线 文章目录 基于ArcGis提取道路中心线前言一、生成缓冲区二、导出栅格数据三、导入栅格数据四、新建中心线要素五、生成中心线总结 前言 最近遇到一个问题&#xff0c;根据道路SHP数据生成模型的时候由于下载的道路数据杂项数据很多&#xff0c;所以导…

链表之第三回

欢迎来到我的&#xff1a;世界 该文章收入栏目&#xff1a;链表 希望作者的文章对你有所帮助&#xff0c;有不足的地方还请指正&#xff0c;大家一起学习交流 ! 目录 前言第一题&#xff1a;判断是否为环形链表第二题&#xff1a;找到两条链表的相交点第三题&#xff1a;返回…

webSocket 开发

1 认识webSocket WebSocket_ohana&#xff01;的博客-CSDN博客 一&#xff0c;什么是websocket WebSocket是HTML5下一种新的协议&#xff08;websocket协议本质上是一个基于tcp的协议&#xff09;它实现了浏览器与服务器全双工通信&#xff0c;能更好的节省服务器资源和带宽…

【机器学习】— 2 图神经网络GNN

一、说明 在本文中&#xff0c;我们探讨了图神经网络&#xff08;GNN&#xff09;在推荐系统中的潜力&#xff0c;强调了它们相对于传统矩阵完成方法的优势。GNN为利用图论来改进推荐系统提供了一个强大的框架。在本文中&#xff0c;我们将在推荐系统的背景下概述图论和图神经网…

回归预测 | MATLAB实现PSO-RBF粒子群优化算法优化径向基函数神经网络多输入单输出回归预测(多指标,多图)

回归预测 | MATLAB实现PSO-RBF粒子群优化算法优化径向基函数神经网络多输入单输出回归预测&#xff08;多指标&#xff0c;多图&#xff09; 目录 回归预测 | MATLAB实现PSO-RBF粒子群优化算法优化径向基函数神经网络多输入单输出回归预测&#xff08;多指标&#xff0c;多图&a…

matlab使用教程(19)—曲线拟合与一元方程求根

1.多项式曲线拟合 此示例说明如何使用 polyfit 函数将多项式曲线与一组数据点拟合。您可以按照以下语法&#xff0c;使用 polyfit 求出以最小二乘方式与一组数据拟合的多项式的系数 p polyfit(x,y,n), 其中&#xff1a; • x 和 y 是包含数据点的 x 和 y 坐标的向量 …

深入理解SSO原理,项目实践使用一个优秀开源单点登录项目(附源码)

深入理解SSO原理,项目实践使用一个优秀开源单点登录项目(附源码)。 一、简介 单点登录(Single Sign On),简称为 SSO。 它的解释是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。 ❝ 所谓一次登录,处处登录。同样一处退出,处处退出。 ❞ 二…

Axios使用CancelToken取消重复请求

处理重复请求&#xff1a;没有响应完成的请求&#xff0c;再去请求一个相同的请求&#xff0c;会把之前的请求取消掉 新增一个cancelRequest.js文件 import axios from "axios" const cancelTokens {}export const addPending (config) > {const requestKey …

时序预测 | MATLAB实现基于CNN-BiGRU卷积双向门控循环单元的时间序列预测-递归预测未来(多指标评价)

时序预测 | MATLAB实现基于CNN-BiGRU卷积双向门控循环单元的时间序列预测-递归预测未来(多指标评价) 目录 时序预测 | MATLAB实现基于CNN-BiGRU卷积双向门控循环单元的时间序列预测-递归预测未来(多指标评价)预测结果基本介绍程序设计参考资料 预测结果 基本介绍 MATLAB实现基于…

Vs code 使用中的小问题

1.Java在Vs code 中使用单元测试失败或者如何使用单元测试 创建Java项目&#xff0c;或者将要测试的文件夹添加进工作区 要出现lib包&#xff0c;并有两个测试用的jar包 编写测试文件 public class TestUnit{ public static void main(String[] args) {String str "…

Pycharm与Anaconda Python的开发环境搭建

目录 一&#xff1a;下载 二&#xff1a;安装python 三&#xff1a;设置Pycharm 一&#xff1a;下载 下载Anaconda&#xff1a; Anaconda | The World’s Most Popular Data Science Platform 安装好以后&#xff0c;设置一下环境变量&#xff1a; 打开命令行&#xff0c…

OpenCV-Python中的图像处理-图像特征

OpenCV-Python中的图像处理-图像特征 图像特征Harris角点检测亚像素级精度的角点检测Shi-Tomasi角点检测SIFT(Scale-Invariant Feature Transfrom)SURF(Speeded-Up Robust Features)FAST算法BRIEF(Binary Robust Independent Elementary Features)算法ORB (Oriented FAST and R…

aardio简单网站css或js下载练习

import win.ui; /*DSG{{*/ var winform win.form(text"下载网站css或js";right664;bottom290;maxfalse) winform.add( buttonClose{cls"button";text"退出";left348;top204;right498;bottom262;color14120960;fontLOGFONT(h-14);note" &qu…
最新文章