基于wireshark打造安全分析师工具--解析suricata中的分析结果

news2024/11/28 14:45:23

从本篇文章开始,我将通过若干篇文章陆续介绍在实际安全运营的过程中,基于wireshark打造安全分析师趁手的流量威胁分析工具,帮助安全分析人员在面对网络数据包取证和分析时候达到事半功倍的效果。本篇文件介绍使用在使用iwreshark分析数据包事后,在UI上显示suricata的告警数据。

wireshark是最为常见的数据包分析工具,wireshark 的优势在于提供了最为广泛的协议解析能力,如下图1是wireshark支持的协议种类:
在这里插入图片描述
图1

可以看到支持的协议种类高达2000+。wireshark能供精确的解析协议,但是并没有能力判定协议每一个字段是否存在问题,某个会话过程是否是正常的。对于安全分析师来说,通常关注对于如下信息的判断:

  • 目的IP和源IP是否正常 ,例如为攻击者资产
  • HTTP中的URL是否存在风险,例如是否存在命令执行,注入,目录穿越等风险
  • 访问域名是否正常,例如是否为新注册的域名
  • TLS证书是否正常,例如是否为恶意组织申请的数字证书
  • HTTP,FTP,SMB等协议传输的文件是否正常 ,例如是否在传输病毒文件
  • SMB协议中是否存在风险,例如存在psexec利用

也许有经验的分析师能够根据HTTP等协议中的文本特征判断出常见的WEB类型的攻击,比如各种注入,命令执行,文件上传等威胁。但是IP地址是否存在风险,域名归属,证书是否正常,传输中文件是否恶意,SMB协议是否存在永恒之蓝等漏洞,对于绝大多数的安全分析师来说只能依赖于安全设备的检测给出答案。但是每一家安全设备的能力参差不齐,同时还需要兼顾误报,能力不可能非常的激进。

因此对于安全分析师们来说,一方面针对安全设备的告警数据包,构建本地的安全分析工具进行二次验证,丰富对于告警数据包不同检测点的识别,增强对于威胁的确认。另一方面针对一些需要调查的数据包,使用本地安全工具进行分析,找出潜在的威胁是很有帮助的。

针对数据包的调查取证来说,首要的是要能够解码数据包,wireshark的丰富协议解析能力是首选。针对威胁检测方面,数据包领域的开源威胁检测引擎包括suricata,snort,zeek,针对这三种引擎的使用,见这里。针对安全分析师来说,要做的就是将wireshark的协议解析能力和suricata的威胁分析能力进行结合。整体的思路很简单,利用wireshark的插件能力,将数据包的suricata分析结果融合进来,如下图2:
在这里插入图片描述

图2

可以看到,suricata的告警结果可以作为整个数据包层次的最顶层,本质其是对于该数据包中存在的攻击手段,威胁类型,威胁等级进行说明,让分析师在分析改流的时候第一时间了解攻击上下文。相应的插件代码如下,或者到该链接进行下载:

local ok, json = pcall(require, "cjson")
if not ok then
	json = require("dkjson")
end

--注册需要在wireshark UI上显示suricata eve文件中的信息,包括alert告警和主要协议信息
if (gui_enabled()) then 
	local suri_proto = Proto("suricata", "Suricata Analysis")
	local suri_sid = ProtoField.string("suricata.alert.sid", "SID", FT_INTEGER)
	local suri_msg = ProtoField.string("suricata.alert.msg", "Message", FT_STRING)
	local suri_category = ProtoField.string("suricata.alert.category", "Category", FT_INTEGER)
	local suri_severity = ProtoField.string("suricata.alert.severity", "Severity", FT_INTEGER)

	local suri_tls_subject = ProtoField.string("suricata.tls.subject", "TLS subject", FT_STRING)
	local suri_tls_issuerdn = ProtoField.string("suricata.tls.issuerdn", "TLS issuer DN", FT_STRING)
	local suri_tls_fingerprint = ProtoField.string("suricata.tls.fingerprint", "TLS fingerprint", FT_STRING)
	local suri_tls_version = ProtoField.string("suricata.tls.version", "TLS version", FT_STRING)

	local suri_ssh_client_version = ProtoField.string("suricata.ssh.client.version", "SSH client version", FT_STRING)
	local suri_ssh_client_proto = ProtoField.string("suricata.ssh.client.proto", "SSH client protocol", FT_STRING)
	local suri_ssh_server_version = ProtoField.string("suricata.ssh.server.version", "SSH server version", FT_STRING)
	local suri_ssh_server_proto = ProtoField.string("suricata.ssh.server.proto", "SSH server protocol", FT_STRING)

	local suri_fileinfo_filename = ProtoField.string("suricata.fileinfo.filename", "Fileinfo filename", FT_STRING)
	local suri_fileinfo_magic = ProtoField.string("suricata.fileinfo.magic", "Fileinfo magic", FT_STRING)
	local suri_fileinfo_md5 = ProtoField.string("suricata.fileinfo.md5", "Fileinfo md5", FT_STRING)
	local suri_fileinfo_sha1 = ProtoField.string("suricata.fileinfo.sha1", "Fileinfo sha1", FT_STRING)
	local suri_fileinfo_sha256 = ProtoField.string("suricata.fileinfo.sha256", "Fileinfo sha256", FT_STRING)
	local suri_fileinfo_size = ProtoField.string("suricata.fileinfo.size", "Fileinfo size", FT_INTEGER)
	local suri_fileinfo_stored = ProtoField.string("suricata.fileinfo.stored", "Fileinfo stored", FT_STRING)

	local suri_http_url = ProtoField.string("suricata.http.url", "HTTP URL", FT_STRING)
	local suri_http_hostname = ProtoField.string("suricata.http.hostname", "HTTP hostname", FT_STRING)
	local suri_http_user_agent = ProtoField.string("suricata.http.user_agent", "HTTP user agent", FT_STRING)
	local suri_http_content_type = ProtoField.string("suricata.http.content_type", "HTTP Content Type", FT_STRING)
	local suri_http_method = ProtoField.string("suricata.http.method", "HTTP Method", FT_STRING)
	local suri_http_protocol = ProtoField.string("suricata.http.protocol", "HTTP Protocol", FT_STRING)
	local suri_http_status = ProtoField.string("suricata.http.status", "HTTP Status", FT_STRING)
	local suri_http_length = ProtoField.string("suricata.http.length", "HTTP Length", FT_STRING)
	local suri_http_referer = ProtoField.string("suricata.http.referer", "HTTP Referer", FT_STRING)

	local suri_smb_command = ProtoField.string("suricata.smb.command", "SMB Command", FT_STRING)
	local suri_smb_filename = ProtoField.string("suricata.smb.filename", "SMB Filename", FT_STRING)
	local suri_smb_share = ProtoField.string("suricata.smb.share", "SMB Share", FT_STRING)
	local suri_smb_status = ProtoField.string("suricata.smb.status", "SMB Status", FT_STRING)

	local suri_prefs = suri_proto.prefs
	local suri_running = false

	local suri_alerts = {}

	local suricata_alert_expert_info = ProtoExpert.new("suricata.alert.expert","Suricata Alerts",expert.group.MALFORMED,expert.severity.WARN)
	local suricata_proto_expert_info = ProtoExpert.new("suricata.proto.expert","Suricata Proto Info",expert.group.DEBUG,expert.severity.NOTE)
	--注册分析下的专家信息
	suri_proto.experts = {suricata_alert_expert_info,suricata_proto_expert_info}

	suri_prefs.alert_file = Pref.string("EVE file", "/var/log/suricata/eve.json",
					    "EVE file containing information about pcap")
	suri_proto.fields = {suri_sid, suri_msg, suri_category, suri_severity, suri_tls_subject, suri_tls_issuerdn, suri_tls_fingerprint, suri_tls_version,
				suri_ssh_client_version, suri_ssh_client_proto, suri_ssh_server_version, suri_ssh_server_proto,
				suri_fileinfo_filename, suri_fileinfo_magic, suri_fileinfo_md5, suri_fileinfo_sha1, suri_fileinfo_sha256,
				suri_fileinfo_size, suri_fileinfo_stored, 
				suri_http_url, suri_http_hostname, suri_http_user_agent,
				suri_http_content_type, suri_http_method, suri_http_protocol, suri_http_status, suri_http_length, suri_http_referer,
				suri_smb_command, suri_smb_filename, suri_smb_share, suri_smb_status
				}

	
	--在解析每个数据包的同时,将已经解析的eve log数据按照数据包的序号为索引填充到已经注册的字段中
	function suri_proto.dissector(buffer,pinfo,tree)
		if not(suri_alerts[pinfo.number] == nil) then
			for i, val in ipairs(suri_alerts[pinfo.number]) do
				if val['sid'] then
					--print(val['sid'])
					subtree = tree:add(suri_proto,
							"Suricata alert: "..val['sid'].." ("..val['msg']..")")
					
					subtree:add(suri_sid, val['sid'])
					subtree:add(suri_msg, val['msg'])
					subtree:add(suri_category, val['category'])
					subtree:add(suri_severity, val['severity'])
					subtree:add_proto_expert_info(suricata_alert_expert_info, val['msg'])
				end
				if val['tls_subject'] then
					subtree = tree:add(suri_proto, "Suricata TLS Info")

					subtree:add(suri_tls_subject, val['tls_subject'])
					subtree:add(suri_tls_issuerdn, val['tls_issuerdn'])
					subtree:add(suri_tls_fingerprint, val['tls_fingerprint'])
					subtree:add(suri_tls_version, val['tls_version'])
					subtree:add_proto_expert_info(suricata_proto_expert_info, "TLS info")
				end
				if val['ssh_client_version'] then
					subtree = tree:add(suri_proto, "Suricata SSH Info")

					subtree:add(suri_ssh_client_version, val['ssh_client_version'])
					subtree:add(suri_ssh_client_proto, val['ssh_client_proto'])
					subtree:add(suri_ssh_server_version, val['ssh_server_version'])
					subtree:add(suri_ssh_server_proto, val['ssh_server_proto'])
					subtree:add_proto_expert_info(suricata_proto_expert_info, "SSH info")
				end
				if val['fileinfo_filename'] then
					subtree = tree:add(suri_proto, "Suricata File Info")

					subtree:add(suri_fileinfo_filename, val['fileinfo_filename'])
					if val['fileinfo_magic'] then
						subtree:add(suri_fileinfo_magic, val['fileinfo_magic'])
					end
					if val['fileinfo_md5'] then
						subtree:add(suri_fileinfo_md5, val['fileinfo_md5'])
					end
					if val['fileinfo_sha1'] then
						subtree:add(suri_fileinfo_sha1, val['fileinfo_sha1'])
					end
					if val['fileinfo_sha256'] then
						subtree:add(suri_fileinfo_sha256, val['fileinfo_sha256'])
					end
					subtree:add(suri_fileinfo_size, val['fileinfo_size'])
					if val['fileinfo_stored'] then
						subtree:add(suri_fileinfo_stored, val['fileinfo_stored'])
					end
					subtree:add_proto_expert_info(suricata_proto_expert_info, "File info")
				end
				if val['http_url'] then
					--print(val['http_url'])
					subtree = tree:add(suri_proto, "Suricata HTTP Info")
					-- add protocol fields to subtree
					subtree:add(suri_http_url, val['http_url'])
					if val['http_hostname'] then
						subtree:add(suri_http_hostname, val['http_hostname'])
					end
					if val['http_user_agent'] then
						subtree:add(suri_http_user_agent, val['http_user_agent'])
					end
					if val['http_content_type'] then
						subtree:add(suri_http_content_type, val['http_content_type'])
					end
					if val['http_method'] then
						subtree:add(suri_http_method, val['http_method'])
					end
					if val['http_protocol'] then
						subtree:add(suri_http_protocol, val['http_protocol'])
					end
					if val['http_status'] then
						subtree:add(suri_http_status, val['http_status'])
					end
					if val['http_length'] then
						subtree:add(suri_http_length, val['http_length'])
					end
					if val['http_referer'] then
						subtree:add(suri_http_referer, val['http_referer'])
					end

					subtree:add_proto_expert_info(suricata_proto_expert_info, "HTTP info")
				end
				if val['smb_command'] then
					subtree = tree:add(suri_proto, "Suricata SMB Info")
					subtree:add(suri_smb_command, val['smb_command'])
					if val['smb_filename'] then
						subtree:add(suri_smb_filename, val['smb_filename'])
					end
					if val['smb_share'] then
						subtree:add(suri_smb_share, val['smb_share'])
					end
					if val['smb_status'] then
						subtree:add(suri_smb_status, val['smb_status'])
					end
					subtree:add_proto_expert_info(suricata_proto_expert_info, "SMB info")
				end
		     end
	     end
	end

	function suri_proto.init()
	end

	-- 解析suricata eve log日志
	function ids_load_log(eve_file)
		function suricata_eve_log_parser(file)
			local event
			local id = 0
			local s_text = ""
			suri_alerts = {}
			for s_text in io.lines(file) do
				event = json.decode(s_text)
				id = event["pcap_cnt"]
				if not (id == nil) then
					if event["event_type"] == "alert" then
						if suri_alerts[id] == nil then
							suri_alerts[id] = {}
						end
						table.insert(suri_alerts[id],
							{category = event["alert"]["category"], sid = tonumber(event["alert"]["signature_id"]),
							severity = tonumber(event["alert"]["severity"]), msg = event["alert"]["signature"]})
					elseif event["event_type"] == "tls" then
						if suri_alerts[id] == nil then
							suri_alerts[id] = {}
						end
						table.insert(suri_alerts[id],
							{ tls_subject = event["tls"]["subject"], tls_issuerdn = event["tls"]["issuerdn"],
							tls_fingerprint = event["tls"]["fingerprint"], tls_version = event["tls"]["version"]})
					elseif event["event_type"] == "ssh" then
						if suri_alerts[id] == nil then
							suri_alerts[id] = {}
						end
						table.insert(suri_alerts[id],
							{ ssh_client_version = event["ssh"]["client"]["software_version"],
							ssh_client_proto = event["ssh"]["client"]["proto_version"],
							ssh_server_version = event["ssh"]["server"]["software_version"],
							ssh_server_proto = event["ssh"]["server"]["proto_version"],
							})
					elseif event["event_type"] == "fileinfo" then
						if suri_alerts[id] == nil then
							suri_alerts[id] = {}
						end
						table.insert(suri_alerts[id],
							{ fileinfo_filename = event["fileinfo"]["filename"],
							  fileinfo_magic = event["fileinfo"]["magic"],
							  fileinfo_md5 = event["fileinfo"]["md5"],
							  fileinfo_sha1 = event["fileinfo"]["sha1"],
							  fileinfo_sha256 = event["fileinfo"]["sha256"],
							  fileinfo_size = tonumber(event["fileinfo"]["size"]),
							  fileinfo_stored = tostring(event["fileinfo"]["stored"]),
							})
					elseif event["event_type"] == "http" then
						if suri_alerts[id] == nil then
							suri_alerts[id] = {}
						end
						table.insert(suri_alerts[id],
							{
								http_url = event["http"]["url"],
								http_hostname = event["http"]["hostname"],
								http_user_agent = event["http"]["http_user_agent"],
								http_content_type = event["http"]["http_content_type"],
								http_method = event["http"]["http_method"],
								http_protocol = event["http"]["protocol"],
								http_status = event["http"]["status"],
								http_length = event["http"]["length"],
								http_referer = event["http"]["http_refer"]
							})
					elseif event["event_type"] == "smb" then
						if suri_alerts[id] == nil then
							suri_alerts[id] = {}
						end
						table.insert(suri_alerts[id],
							{
								smb_command = event["smb"]["command"],
								smb_filename = event["smb"]["filename"],
								smb_share = event["smb"]["share"],
								smb_status = event["smb"]["status"],
							})
					end
				end
			end
		end


		function suriwire_register(file)
			if file == "" then
				file = suri_prefs.alert_file
			end
			local filehandle = io.open(file, "r")

			if not (filehandle == nil) then
				filehandle:close()
				-- parse suricata log file
				suricata_eve_log_parser(file)
				-- register protocol dissector
				if suri_running == false then
					register_postdissector(suri_proto)
					suri_running = true
				end
				reload()
			else
				new_dialog("Unable to open '" .. file
					   .. "'. Choose another alert file",
					   suriwire_register,
					   "Choose file (Linux default:" .. suri_prefs.alert_file..")")
			end
		end
		-- run suricata
		-- set input file
		if eve_file then
			suriwire_register(eve_file)
		else
			new_dialog("Choose alert file",
			           suriwire_register,
			           "Choose file (Linux default:" .. suri_prefs.alert_file..")")
		end
	end

	function wireshark_page()
		browser_open_url("https://blog.csdn.net/javajiawei/category_9583097.html")
	end

	register_menu("导入入侵检测日志/导入Suricata日志", ids_load_log, MENU_TOOLS_UNSORTED)
	register_menu("Wireshark使用技巧", wireshark_page, MENU_TOOLS_UNSORTED)
	local eve_file = os.getenv("SURIWIRE_EVE_FILE")
	if eve_file then
		ids_load_log(eve_file)
	end
end

需要注意的是由于该脚本中引用了dkjson的lua脚本,因此需要先将该Lua脚本下载到本地wireshark目录,下载地址,这里 。或者到对应的github下载,这里。

Lua插件的加载方式为使用命令行打开wireshark,如下:

wireshark -r test.pcap -X lua_script:suricata.lua

或者将改脚本配置到init.lua中,进行自启动,init.lua中添加如下配置:

disable_lua = false--开启Lua解析
dofile(DATA_DIR.."suricata.lua")

启动wireshark之后,需要加载suricata的eve log文件,如下图3导入suricata的日志:
在这里插入图片描述

图3
加载对应的eve log之后,在专家信息中会生成对应的告警信息的解析,如下图4:
在这里插入图片描述

图4
图4中显示了所有出发告警的数据包信息,选择对应的告警就可以跳转到对应的数据包。图3中可以看到需要输入的文件为suricata eve格式的文件,示例如下:

{"timestamp":"2019-10-15T06:54:14.857081+0000","flow_id":1511806696075679,"pcap_cnt":351,"event_type":"alert","src_ip":"1.1.141.169","src_port":17725,"dest_ip":"1.2.61.25","dest_port":53,"proto":"TCP","alert":{"action":"allowed","gid":1,"signature_id":2030524,"rev":1,"signature":"ET INFO Possible NOP Sled Observed in Large DNS over TCP Packet M1","category":"Attempted Administrator Privilege Gain","severity":1,"metadata":{"affected_product":["Windows_DNS_server"],"created_at":["2020_07_15"],"former_category":["INFO"],"performance_impact":["Significant"],"signature_severity":["Informational"],"updated_at":["2020_07_15"]}},"flow":{"pkts_toserver":3,"pkts_toclient":1,"bytes_toserver":1638,"bytes_toclient":62,"start":"2019-10-15T06:54:14.700831+0000"}}
{"timestamp":"2019-10-15T06:54:14.872549+0000","flow_id":535741018300164,"pcap_cnt":372,"event_type":"dcerpc","src_ip":"1.1.171.176","src_port":7018,"dest_ip":"1.2.34.235","dest_port":6502,"proto":"TCP","dcerpc":{"request":"BIND","interfaces":[{"uuid":"62b93df0-8b02-11ce-876c-00805f842837","version":"1.0","ack_result":0}],"response":"BINDACK","call_id":0,"rpc_version":"5.0"}}
{"timestamp":"2019-10-15T06:54:14.902033+0000","flow_id":1632151679679309,"pcap_cnt":386,"event_type":"anomaly","src_ip":"1.2.33.242","src_port":80,"dest_ip":"1.1.30.100","dest_port":46189,"proto":"TCP","tx_id":0,"anomaly":{"app_proto":"http","type":"applayer","event":"INVALID_REQUEST_CHUNK_LEN","layer":"proto_parser"}}
{"timestamp":"2019-10-15T06:54:14.902033+0000","flow_id":1632151679679309,"pcap_cnt":386,"event_type":"http","src_ip":"1.1.30.100","src_port":46189,"dest_ip":"1.2.33.242","dest_port":80,"proto":"TCP","tx_id":0,"http":{"hostname":"pdfyoocaugsebmnhamlz","url":"/","http_method":"GET","protocol":"HTTP/1.1","length":0}}
{"timestamp":"2019-10-15T06:54:14.914528+0000","flow_id":535741018300164,"pcap_cnt":390,"event_type":"dcerpc","src_ip":"1.1.171.176","src_port":7018,"dest_ip":"1.2.34.235","dest_port":6502,"proto":"TCP","dcerpc":{"request":"REQUEST","req":{"opnum":43,"frag_cnt":1,"stub_data_size":20},"response":"RESPONSE","res":{"frag_cnt":1,"stub_data_size":8},"call_id":0,"rpc_version":"5.0"}}

可以看到上述脚本针对eve格式中eventype为alert,http,dns,tls,smb等类型进行了解析。alert类型告警的多少依赖于规则库的丰富程度,这块安全分析师需要个人进行积累,必要的时候需要自己进行定义。同时脚本针对HTTP,DNS,TLS,SMB等协议记录的关键字段也进行了解析,如上图2。至于如何判断这些字段是否存在问题,以及根据这些字段能够得出哪些更为丰富的信息,后续的文章会进行介绍。

关于如何获取eve log文件,可以参考文章,这里。需要注意的事,在dalton的默认容器中有很多的信息是不默认记录的,如果想要记录更多的信息需要修改suricata.yaml中的默认配置,如下图6是记录文件md5值的配置信息,见这里。
在这里插入图片描述
图6
可以根据实际的需要修改yaml文件字段,生成eve log之后,对应修改lua,解析对应的字段即可。

需要注意的是入侵检测的规则主要针对的威胁形式为具备某种检测点的攻击技术点,例如SQL注入,命令执行,反序列化等。针对IOC,恶意文件传输,恶意证书等指标类的威胁特征,虽然也可以编写IDS规则,但是一般采用威胁情报碰撞的形式进行检测更为的方便,主要原因在于IOC的特征数量大,变换较为迅速,将会在下一篇文章中进行介绍。

本文为CSDN村中少年原创文章,未经允许不得转载,博主链接这里。

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

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

相关文章

21天学会C++:Day7----auto关键字

CSDN的uu们,大家好。这里是C入门的第七讲。 座右铭:前路坎坷,披荆斩棘,扶摇直上。 博客主页: 姬如祎 收录专栏:C专题 目录 1. 知识引入 2. auto的使用 2.1 auto与指针和引用结合起来使用 2.2 在同一…

区分序列/UIO/特征集示例

区分序列/UIO/特征集示例 从确定性有限状态机进行测试:检查状态 概述 让我们假设我们有一个状态集 S 的 FSM M。还假设我们知道通过转换 t 达到的当前状态是 s 或 s0。 我们如何确定 t 到达了哪个状态? 分离状态 输入序列 w 将两个状态 s 和 s0 分开&…

C++进阶之继承

文章目录 前言一、继承的概念及定义1.继承概念2.继承格式与访问限定符3.继承基类与派生类的访问关系变化4.总结 二、基类和派生类对象赋值转换基本概念与规则 三、继承中的作用域四、派生类的默认成员函数五、继承与友元六、继承与静态成员六、复杂的菱形继承及菱形虚拟继承七、…

图论试题2020

n-m 2 16 Pk(Kn)k(k-1)…(k-n1)。 C:A2对角线元素aii2等于对应顶点vi的度数,所以对角线元素之和等于边数的两倍。 A的所有特征值的平方和等于A2的对角线元素之和。 B 完全图没有顶点隔,实际上也只有以完全图为生成子图的图没有顶点隔。 连通…

Qt6 C++基础入门1 定时器与QTimer

定时器 定时器图片流水灯案例 实现效果:构建一个界面,点击开始按钮轮流播放文件夹下图片,点击停止按钮停止播放 构建页面,上部是一个没有内容的 label 下面是开始和暂停按钮,各自的名称分别为 startBtn 和 stopBtn 先保…

6.事件绑定

目录 1 事件对象的属性 2 事件绑定方式 3 在事件中赋值 4 事件传参 1 事件对象的属性 target是触发该事件源头的组件,currentTarget是当前事件所绑定的组件,比如现在有一个父组件包着子组件,你给父组件绑定事件,由于事件…

ps磨皮插件专用智能磨皮插件Portraiture4

Portraiture是一款智能磨皮插件,为Photoshop和Lightroom添加一键磨皮美化功能,快速对照片中皮肤、头发、眉毛等部位进行美化,无需手动调整,大大提高P图效率。全新4版本,升级AI算法,并独家支持多人及全身模式…

从0到1深入剖析微服务架构,阿里人十年经验浓缩成一份笔记

前言 数字化经济的快速发展和云计算给底层IT系统带来的巨大变革正是当下微服务架构快速发展的时代背景。Gartner预计,从2018年到2022年,PaaS将成为未来的主流平台交付模式,而PaaS平台需要更加灵活的云原生应用架构做技术支撑,微服…

图论与算法(3)图的深度优先遍历

1. 遍历的意义 1.1 图的遍历 图的遍历是指按照一定规则访问图中的所有顶点,以便获取图的信息或执行特定操作。常见的图遍历算法包括深度优先搜索(DFS)和广度优先搜索(BFS)。 深度优先搜索(DFS&#xff0…

UART串口通信实验

不管是单片机开发还是嵌入式 Linux 开发,串口都是最常用到的外设。 可以通过串口将开发板与电脑相连,然后在电脑上通过串口调试助手来调试程序。 还有很多模块,比如蓝牙、GPS、GPRS等都使用串口与主控进行通信。 UART简介 串口全称串行接口…

vb6 Webview2微软Edge Chromium内核执行JS取网页数据测速

微软Edge Chromium内核执行JS获取网页数据测试 ExcuteScript eval(document.body.innerHTML) from : https://www.163.com 采集的网页HTM字符串占用字节空间1.2MB ExcuteScript回调事件中取得JS执行结果,用时 54 毫秒 其中JSON转字符13.5209毫秒 jSON数据长度: 增…

ChatGPT更新说明(20230524)

原文传送门:ChatGPT — Release Notes 更新说明(5月24日) 简要:iOS应用在更多国家可用,Alpha测试中的共享链接,Bing插件,iOS上的历史记录禁用 ChatGPT iOS应用在更多国家可用 好消息&#xf…

Elasticsearch:如何使用集群级别的分片分配过滤(不包括节点)安全地停用节点

当你想停用 Elasticsearch 中的节点时,通常的过程不是直接销毁节点。 如果你这样做,那么你就有数据丢失的风险,这不是你想要对应该是可靠的数据库做的事情。 这样做的问题是,节点很可能会通过 Elasticsearch 处理的恰当命名的分片…

Character.AI成为新晋AI聊天应用爆款;谷歌推出 Google Slides AI 图像生成

🦉 AI新闻 🚀 Character.AI:首周下载量超越ChatGPT,成为新晋AI聊天应用爆款 摘要:Character.AI是一款受欢迎的人工智能聊天应用,用户可以自由创建AI角色,并与它们聊天。该应用于2023年5月23日…

C#,码海拾贝(32)——计算“实对称三对角阵的全部特征值与特征向量的”之C#源代码,《C#数值计算算法编程》源代码升级改进版

using System; namespace Zhou.CSharp.Algorithm { /// <summary> /// 矩阵类 /// 作者&#xff1a;周长发 /// 改进&#xff1a;深度混淆 /// https://blog.csdn.net/beijinghorn /// </summary> public partial class Matrix {…

【Mysql】 表的约束

文章目录 【Mysql】 表的约束空属性默认值列描述zerofill主键自增长唯一键外键综合案例 【Mysql】 表的约束 上一个博客记录的是mysql中的类型&#xff0c;这篇博客记录的是mysql中的表的约束&#xff1b;即列字段对插入数据的约束 空属性 俩个值&#xff1a; null (默认) 和…

Vue3 + ElementPlus实战学习——模拟简单的联系人列表管理后台

文章目录 &#x1f4cb;前言&#x1f3af;demo 介绍&#x1f3af;功能分析&#x1f9e9;数据的展示与分页功能&#x1f9e9;编辑功能&#x1f9e9;删除功能 &#x1f3af;部分代码分析&#x1f3af;完整代码&#x1f4dd;最后 &#x1f4cb;前言 这篇文章介绍一下基于 Vue3 和…

DataSpell第一次安装使用教程

官网&#xff1a; Download DataSpell: The IDE for Data Scientists (jetbrains.com) 双击.exe文件开始安装 安装过程就一直点击下一步就好&#xff0c;遇到方框需要勾选的全部勾上。 注意尽量别安装在C盘&#xff0c;我安装在了D盘。 获取jihuoma&#xff1a;(484条消息)…

MySql学习1:安装

前言 学习教程&#xff1a;黑马程序员 MySQL数据库入门到精通&#xff0c;从mysql安装到mysql高级、mysql优化全囊括 目前的打算是跟着教程学习基础部分&#xff0c;进阶和运维部分以后可能会学习。 安装 关于如何安装mysql可以跟着视频里的操作&#xff0c;但是对于我这种…

盘点一个AI你画我猜的小工具

点击上方“Python爬虫与数据挖掘”&#xff0c;进行关注 回复“书籍”即可获赠Python从入门到进阶共10本电子书 今 日 鸡 汤 寻声暗问弹者谁&#xff0c;琵琶声停欲语迟。 大家好&#xff0c;我是Python进阶者。 一、前言 前几天在【ChatGPT&AI破局俱乐部】知识星球发现了一…