当前位置: 首页 > news >正文

Wireshark自定义Lua插件

背景:

常见的抓包工具有tcpdump和wireshark,二者可基于网卡进行抓包:tcpdump用于Linux环境抓包,而wireshark用于windows环境。抓包后需借助包分析工具对数据进行解析,将不可读的二进制数转换为可读的数据结构。
wireshark不仅可以作为抓包工具,还可以作为包解析工具。Wireshark针对常见协议都提供了对应的解析插件,
如: TCP、UDP、HTTP、SIP等;同时提供了自定义插件机制,用户可以基于此解析自定义消息。至于插件,wireshark支持C语言插件和Lua插件,Lua作为脚本不需要编译,方便调试,速度相对C语言较慢。由于抓包时可以根据条件过滤,且一般数据包分析在本地进行,这部分性能优势相对于Lua脚本的方便性可以忽略。
因此,本文的主体内容是介绍如何在Wireshark中开发自定义插件解析消息。

1.插件配置方式

1.1 配置protobuf加载路径

根据Wireshark->Preferences->Protocols路径进入配置页面(Windows中路径为 “编辑->首选项->Protocols” ):
在这里插入图片描述
勾选Load .proto files on startup选项,然后点击Edit按钮开始配置。添加proto文件所在文件夹,勾选"load all files"选项。
经过上述配置,已经为wireshark指定了查找proto文件的路径,后续在lua脚本中可直接使用proto文件。

1.2 配置lua脚本路径

根据Wireshark->About Wireshark->Folders路径进入配置页面(windows下路径为: 帮助->关于->文件夹):
在这里插入图片描述
添加或者查看个人Lua插件的存放位置,后面开发的插件需要存放到这个路径下才会生效。添加或者修改lua插件后,需要重新加载lua插件:"分析->重新载入Lua插件"或者通过快捷键Ctrl+Shift+L.

1.3 Lua console调试工具

在"Tools->Lua console"页面可以编写和执行Lua脚本,可用于调试:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-W2T20zdx-1717753458062)(C:\Users\0216001379\AppData\Roaming\Typora\typora-user-images\1716602725265.png)]
说明:调试工具是开发Lua插件的关键,结合快捷键Ctrl+Shift+L,通过打印提示信息,可以快速定位和发现问题。

2.wireshark关于Lua API介绍

Lua语法请参考: Lua使用方式介绍

Lua API参考自: https://mika-s.github.io/wireshark/lua/dissector/2017/11/04/creating-a-wireshark-dissector-in-lua-1.html

这部分介绍wireshark为Lua脚本的API,以及根据如何使用这些API实现自定义Lua插件。介绍Lua API前,有必要对Wireshark页面进行介绍:
在这里插入图片描述

需要关注上图红色标注的区域,包括:column区、tree区、data区,后续API会操作这些区域。

2.1 定义协议

seong_protocol = Proto("seong",  "seong description")seong_protocol.dissector = function(buffer, pinfo, tree)local subtree = tree:add(seong_protocol, buffer(),"Seong Message Data"); 
endDissectorTable.get("tcp.port"):add(9003, seong_protocol)

将上述Lua插件注册到环境后,可使用自定义的seong协议过滤消息,Wireshark页面显示如下:
在这里插入图片描述
定义协议需要三个步骤:定义Proto协议对象,为Proto对象添加解码器方法,将Proto对象与对应的端口进行绑定。
[1] 定义协议对象

seong_protocol = Proto("seong",  "seong description")

Proto作为构造参数用于创建Proto对象,接收两个参数,协议名称和协议描述。

[2] 为协议对象添加解析器
解析器函数:

seong_protocol.dissector = function(buffer, pinfo, tree)local subtree = tree:add(seong_protocol, buffer(),"Seong Message Data"); 
end

函数包括三个入参:
(1) buffer为二进制消息数据,可以通过类似buffer(0,2)方式从消息中截取字节数组;
(2) pinfo为数据包的元数据对象,包括消息大小、源地址/目标地址、大小、时间戳等信息;
(3) tree为协议树节点对象,数据结构会被渲染在tree区。

tree.add方法:
解析器内部的tree:add(seong_protocol, buffer(),"Seong Message Data")功能是: 在tree区域添加一个子树(并返回子树的引用)。其中第一个参数是必选的,后面两个参数是可选的:
(1) 协议参数
tree区域中,每层协议对应一个子树,即每个子tree需要与指定的协议绑定,此时需要为seong协议创建一个子树:

local subtree = tree:add(seong_protocol, nil, nil);

后续通过操作subtree对象,为seong协议子树添加显示数据。

(2) 数据参数
当传递为nil和buffer或者buffer(0,2) 时,鼠标选中seong协议时,关联的data区域不同:
在这里插入图片描述
(3) 描述信息
当描述信息为nil时,wireshark会选择使用协议的描述信息展示。

[3] 将协议与端口绑定
将协议与端口绑定后,Wireshark会自动将该端口上的消息使用绑定的协议解析:

DissectorTable.get("tcp.port"):add(9003, seong_protocol)

此时,TCP协议的9003端口的消息使用seong插件解析。

2.2 修改Column区

在过滤窗口,通过seong过滤后,可以得到TCP-9003端口的消息,显示的Protocol协议仍未TCP,应该修改为seong. 在解析器内部添加语句pinfo.columns.protocol:set(seong_protocol.name),得到:

seong_protocol = Proto("seong",  "seong description")seong_protocol.dissector = function(buffer, pinfo, tree)local subtree = tree:add(seong_protocol, buffer(),"Seong Message Data");pinfo.columns.protocol:set(seong_protocol.name); 
endDissectorTable.get("tcp.port"):add(9003, seong_protocol)

Wireshark显示为:
在这里插入图片描述
消息的协议名称修改为了seong.
除了protocol外,还可以通过pinfo.columns对象修改columns区域的其他字段的内容, 如修改info消息:
pinfo.columns.info:set("此时充值VIP可观看");
在这里插入图片描述

2.3 修改Tree区

Tree区为重点区域,自定义插件的核心功能是为了在这个区域直观地展示消息的内容。解析器的重点职责是从二进制数据中解析消息,并将消息作为字段添加到tree上,从而在Tree区域展示。
以下结合两种方式,其中通过Proto对象的fields属性方式是官方文档的推荐方式;直接操作tree对象方式是个人探索所得,相对比较简单(可能有坑)。

2.3.1 Proto对象的fields属性方式

先给出案例:

seong_protocol = Proto("seong",  "seong description")message_length = ProtoField.int32("message_length", "Message-Length", base.DEC)
seong_protocol.fields = {message_length}seong_protocol.dissector = function(buffer, pinfo, tree)local subtree = tree:add(seong_protocol, buffer(),"Seong Message Data");pinfo.columns.protocol:set(seong_protocol.name);subtree:add(message_length, 123456)
endDissectorTable.get("tcp.port"):add(9003, seong_protocol)

与之前的lua脚本区域在于新增了Proto.fields相关的逻辑:
[1] 声明字段类型
message_length = ProtoField.int32(“message_length”, “Message-Length”, base.DEC)
创建一个属性,属性名称为message_length,描述为Message-Length(显示使用), 为十进制的整数。
[2] 字段添加到协议对象中

seong_protocol.fields = {message_length}

[3] 为message_length赋值,并添加到tree中

subtree:add(message_length, 123456)

此时, wireshark显示如下:
在这里插入图片描述

2.3.2 直接操作tree对象

通过subtree:add(字符串)方法直接将字符串设置到tree对象上:

seong_protocol = Proto("seong",  "seong description")seong_protocol.dissector = function(buffer, pinfo, tree)local subtree = tree:add(seong_protocol, buffer(),"Seong Message Data");pinfo.columns.protocol:set(seong_protocol.name);subtree:add("Message-Length: " .. 11223344)
endDissectorTable.get("tcp.port"):add(9003, seong_protocol)

在这里插入图片描述

2.3.3 简单案例

假设消息中前两个字节表示有效的数据长度:

seong_protocol = Proto("seong",  "seong description")seong_protocol.dissector = function(buffer, pinfo, tree)local subtree = tree:add(seong_protocol, buffer(),"Seong Message Data");pinfo.columns.protocol:set(seong_protocol.name);subtree:add("Message-Length: " .. buffer(0,2))
endDissectorTable.get("tcp.port"):add(9003, seong_protocol)

其中,buffer(0,2)从二进制消息中提取前两个字节; subtree:add方法调用时,可以关联data区域:
subtree:add("Message-Length: " .. buffer(0,2)) 修改为
subtree:add(buffer(0,2), "Message-Length: " .. buffer(0,2)):
显示如下:
在这里插入图片描述

3.案例

3.1 protobuf文件准备

Person.proto文件:

syntax = "proto2";option java_package = "com.seong";option java_outer_classname = "TestProtoMsg";message Person {required int32 id = 1;required string name = 2;required bool isMale = 3;repeated Address address = 4;
};message Address {required string country = 1;optional string location = 2;
};

编译后,生成com.seong.TestProtoMsg类,内部有Person和Address两个内部类,生成的Java类将在服务端和客户端程序中使用。然后将Person.proto文件放到1.1章节中配置的protobuf加载路径下。

3.2 Java服务端和客户端单例

使用Netty构建一个服务端(监听端口为9999)与客户端, 二者之间通过TCP-Protobuf通信,消息格式如下:
在这里插入图片描述
首部固定为AAAA(2字节),消息大类为BB(1字节), 消息子类为CC(1字节),消息长度表示PB消息体的长度(2字节),PB消息内容为5.1中Person.proto文件的protobuf消息。

关于Netty相关代码这里不进行介绍,请参考IO系列-netty相关的文章。

客户端:

(1) 客户端Netty模板代码:

public class Application {public static void main(String[] args) throws Exception {new Application().start();}public void start() throws Exception {EventLoopGroup group = new NioEventLoopGroup();try {Bootstrap b = new Bootstrap();b.group(group).channel(NioSocketChannel.class).handler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch) {ch.pipeline().addLast(new PersonProtoBufEncoder());}});ChannelFuture f = b.connect("localhost", 9999).sync();f.channel().writeAndFlush(buildPersonMsg());f.channel().closeFuture().sync();} finally {group.shutdownGracefully();}}
}

(2) 构造消息: 根据PB定义构造案例消息

private TestProtoMsg.Person buildPersonMsg() {TestProtoMsg.Person.Builder personBuilder = TestProtoMsg.Person.newBuilder();personBuilder.setId(1960001001);personBuilder.setName("ue001");personBuilder.setIsMale(false);TestProtoMsg.Address.Builder addressBuilder = TestProtoMsg.Address.newBuilder();addressBuilder.setCountry("zh-CN");addressBuilder.setLocation("NanJing");personBuilder.addAddress(addressBuilder.build());return personBuilder.setId(1).build();
}

(3) Protobuf编码器:将TestProtoMsg.Person对象编码为二进制数据,然后发送给服务端

public class PersonProtoBufEncoder extends MessageToByteEncoder<TestProtoMsg.Person> {private static final int TYPE = 4;private static final int LENGTH = 4;@Overrideprotected void encode(ChannelHandlerContext ctx, TestProtoMsg.Person person, ByteBuf byteBuf) {byte[] playLoadBytes = person.toByteArray();int playLoadLen = playLoadBytes.length;ByteBuf msgBuffer = Unpooled.buffer(TYPE + LENGTH + playLoadLen);msgBuffer.writeBytes(new byte[] {(byte)0xAA, (byte)0xAA});msgBuffer.writeBytes(new byte[] {(byte)0xBB, (byte)0xCC});msgBuffer.writeInt(playLoadLen);msgBuffer.writeBytes(playLoadBytes);byteBuf.writeBytes(msgBuffer);}
}

服务端:

(1) 服务端Netty模板代码:

public class Application {public static void main(String[] args) throws Exception {new Application().start(9999);}public void start(int port) throws Exception {EventLoopGroup bossGroup = new NioEventLoopGroup();EventLoopGroup workerGroup = new NioEventLoopGroup();try {ServerBootstrap b = new ServerBootstrap();b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).handler(new LoggingHandler(LogLevel.INFO)).childHandler(new ChannelInitializer<SocketChannel>() {@Overridepublic void initChannel(SocketChannel ch) {ch.pipeline().addLast(new PersonProtoBufDecoder());ch.pipeline().addLast(new PersonProtoServerHandler());}}).option(ChannelOption.SO_BACKLOG, 128).childOption(ChannelOption.SO_KEEPALIVE, true);ChannelFuture f = b.bind(port).sync();f.channel().closeFuture().sync();} finally {workerGroup.shutdownGracefully();bossGroup.shutdownGracefully();}}
}

(2) 解码器: 将来自客户端的二进制数据解码为TestProtoMsg.Person对象

public class PersonProtoBufDecoder extends ByteToMessageDecoder {private static final int TYPE_HEAD = 4;private static final int LENGTH_HEAD = 4;private static final int HEAD_LEN = TYPE_HEAD + LENGTH_HEAD;@Overrideprotected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception {byte[] msgBytes = new byte[byteBuf.readableBytes()];byteBuf.readBytes(msgBytes);int msgLen = msgBytes.length;if (msgLen <= HEAD_LEN) {return;}byte[] bodyMsg = new byte[msgLen - HEAD_LEN];System.arraycopy(msgBytes, HEAD_LEN, bodyMsg, 0, msgLen - HEAD_LEN);TestProtoMsg.Person person = TestProtoMsg.Person.parseFrom(bodyMsg);list.add(person);}
}

(3) 解码后的消息处理: 打印TestProtoMsg.Person对象

@Slf4j
public class PersonProtoServerHandler extends ChannelInboundHandlerAdapter {@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) {if (!(msg instanceof TestProtoMsg.Person)) {ctx.fireChannelRead(msg);return;}LOGGER.info("Receive from client, msg is {}.", msg);}
}

运行结果如下所示:

17:17:42.874 [nioEventLoopGroup-3-1] INFO com.seong.PersonProtoServerHandler - Receive from client, msg is id: 1
name: "ue001"
isMale: false
address {country: "zh-CN"location: "NanJing"
}
.

3.3 抓包分析

通过wireshark或者tcpdump可进行抓包,这里对端口进行过滤(9999):
在这里插入图片描述
可以看到客户端与服务端的通信数据包已被获取,为二进制数据,没有可读性。

3.4 Lua脚本定义协议

-- 自定义协议:Proto构造函数有两个参数:名称和描述
seong_protocol = Proto("seong",  "seong Message")
-- 添加一个字段,用于在数据树中显示
message_length = ProtoField.int32("seong.message_length", "PB-Message-Length", base.DEC)
seong_protocol.fields = {message_length}-- 自定义协议的解析器
seong_protocol.dissector = function(buffer, pinfo, tree)-- 消息长度为0,直接返回local length = buffer:len();if length == 0 then return end;-- 添加子树,显示为Seong Message Datalocal subtree = tree:add(seong_protocol, buffer(),"Seong Message Data"); -- 消息树中添加PB-Message-Length信息local msgLen = buffer(4,4):uint()subtree:add(message_length, msgLen)-- 消息树中添加HEAD,Main-Type,Sub-Type数据local headFlag = "" .. buffer(0,2)local mainType = "" .. buffer(2,1)local subType = "" .. buffer(3,1)subtree:add(buffer(0,2),"HEAD: " .. headFlag)subtree:add(buffer(2,1),"Main-Type: " .. mainType)subtree:add(buffer(3,1),"Sub-Type: " .. subType)-- 调用wireshark内置的protobuf解析器sipProtoType = "Person";pinfo.private["pb_msg_type"] = "message," .. sipProtoTypelocal protobuf_dissector = Dissector.get("protobuf");local result = pcall(Dissector.call, protobuf_dissector, buffer(8, msgLen):tvb(), pinfo, subtree)pinfo.columns.protocol:set(seong_protocol.name)
end--注册协议到指定的端口
local tcp_port = DissectorTable.get("tcp.port"):add(9999, seong_protocol)
3.5 查看协议

在这里插入图片描述
此时二进制数据已经通过树区域进行了展示, 与服务端解码后的消息保持一致。

3.6 扩展

本文中涉及的protobuf文件只有一个,实际上系统间的消息类型数以百计,因此需要对上述Lua脚本进行扩展以具备更好的通用性。
注意到定义消息时,添加了消息大类和消息子类两个冗余字段,可通过这两个字段与protobuf之间建立映射关系,即这两个消息确定了消息类型和解码方式。

local function matchedProtoType(mainType, subType)print("mainType:" .. mainType .. "subType:" .. subType)if mainType == "bb" thenif subType == "cc" thenreturn "Person";endelsereturn nilendreturn nil
end

定义一个函数,根据mainType和subType返回protobuf消息类型,相应地,Lua脚本中解析器的定义进行如下修改(将硬编码的Person类型改为通过matchedProtoType获取):

idslds_protocol.dissector = function(buffer, pinfo, tree)-- ...--sipProtoType = "Person";sipProtoType = matchedProtoType(mainType,subType)-- ...
end

本文主要介绍如何在wireshark中介绍自定义Lua插件,因此扩展这一部分不进行详细描述。后续在IO系列-Netty应用相关的文章中将介绍一个通过Netty实现子网穿越的案例;之后结合该案例对Lua插件的应用进行完整的阐述。

相关文章:

Wireshark自定义Lua插件

背景&#xff1a; 常见的抓包工具有tcpdump和wireshark&#xff0c;二者可基于网卡进行抓包&#xff1a;tcpdump用于Linux环境抓包&#xff0c;而wireshark用于windows环境。抓包后需借助包分析工具对数据进行解析&#xff0c;将不可读的二进制数转换为可读的数据结构。 wires…...

商城项目【尚品汇】07分布式锁-2 Redisson篇

文章目录 1 Redisson功能介绍2 Redisson在Springboot中快速入门&#xff08;代码&#xff09;2.1 导入依赖2.2 Redisson配置2.3 将自定义锁setnx换成Redisson实现&#xff08;可重入锁&#xff09; 3 可重入锁原理3.1 自定义分布式锁setnx为什么不可以重入3.2 redisson为什么可…...

Adobe Illustrator 矢量图设计软件下载安装,Illustrator 轻松创建各种矢量图形

Adobe Illustrator&#xff0c;它不仅仅是一个简单的图形编辑工具&#xff0c;更是一个拥有丰富功能和强大性能的设计利器。 在这款软件中&#xff0c;用户可以通过各种精心设计的工具&#xff0c;轻松创建和编辑基于矢量路径的图形文件。这些矢量图形不仅具有高度的可编辑性&a…...

Nvidia/算能 +FPGA+AI大算力边缘计算盒子:中国舰船研究院

中国舰船研究院又称中国船舶重工集团公司第七研究院&#xff0c;隶属于中国船舶重工集团公司&#xff0c;是专门从事舰船研究、设计、开发的科学技术研究机构&#xff0c;是中国船舶重工集团公司的军品技术研究中心、科技开发中心&#xff1b;主要从事舰船武器装备发展战略研究…...

双网卡配置IP和路由总结

1.在网络适配器属性IPv4中设置默认网关&#xff08;记网关地址为A&#xff09;&#xff0c;将会在本地路由表中新增一条记录&#xff1a; 网络号子网掩码网关地址0.0.0.00.0.0.0A 2.如果有两个网卡&#xff08;假设一个连接内网&#xff0c;一个连接互联网&#xff09;&#…...

【纯血鸿蒙】——自适应布局如何实现?

界面级一多能力有 2 类&#xff1a; 自适应布局: 略微调整界面结构 响应式布局&#xff1a;比较大的界面调整 本文章先主要讲解自适应布局&#xff0c;响应式布局再后面文章再细讲。话不多说&#xff0c;开始了。 自适应布局 针对常见的开发场景&#xff0c;方舟开发框架提…...

Qt5学习笔记(一):Qt Widgets Application项目初探

笔者长期使用MFC开发Windows GUI软件。随着软件向Linux平台迁移的趋势越发明朗&#xff0c;GUI程序的跨平台需求也越来越多。因此笔者计划重新抓一下Qt来实现跨平台GUI程序的实现。 0x01. 看看Qt Widgets Application项目结构 打开Qt5&#xff0c;点击“ New”按钮新建项目。…...

Linux网络编程:数据链路层协议

目录 前言&#xff1a; 1.以太网 1.1.以太网帧格式 1.2.MTU&#xff08;最大传输单元&#xff09; 1.2.1.IP协议和MTU 1.2.2.UDP协议和MTU 1.2.3.TCP协议和MTU 2.ARP协议&#xff08;地址解析协议&#xff09; 2.1.ARP在局域网通信的角色 2.2.ARP报文格式 2.3.ARP报文…...

企业估值的三种方法

估值模型三剑客—DCF、P/E、EV /EBITDA 三种主要估值模型的优缺点: DCF 优点&#xff1a;通过对自由现金流的折现计算&#xff0c;反映了公司内在价值的本质&#xff0c;是最重要与最合理的估值方法。 缺点&#xff1a;未来自由现金流的估计不准确&#xff0c;受折现率影响…...

比亚迪正式签约国际皮划艇联合会和中国皮划艇协会,助推龙舟入奥新阶段

6月5日&#xff0c;比亚迪与国际皮划艇联合会、中国皮划艇协会在深圳共同签署合作协议&#xff0c;国际皮划艇联合会主席托马斯科涅茨科&#xff0c;国际皮划艇联合会秘书长理查德派蒂特&#xff0c;中国皮划艇协会秘书长张茵&#xff0c;比亚迪品牌及公关处总经理李云飞&#…...

宏集Panorama SCADA:个性化定制,满足多元角色需求

前言 在考虑不同人员在企业中的职能和职责时&#xff0c;他们对于SCADA系统的需求可能因其角色和工作职责的不同而有所差异。在SCADA系统的设计和实施过程中&#xff0c;必须充分考虑和解决这种差异性。 为了满足不同人员的需求, 宏集Panorama SCADA平台具备灵活的功能和定制…...

聪明人社交的基本顺序:千万别搞反了,越早明白越好

聪明人社交的基本顺序&#xff1a;千万别搞反了&#xff0c;越早明白越好 国学文化 德鲁克博雅管理 2024-03-27 17:00 作者&#xff1a;方小格 来源&#xff1a;国学文化&#xff08;gxwh001&#xff09; 导语 比一个好的圈子更重要的&#xff0c;是自己优质的能力。 唐诗宋…...

图片和PDF展示预览、并支持下载

需求 展示图片和PDF类型&#xff0c;并且点击图片或者PDF可以预览 第一步&#xff1a;遍历所有的图片和PDF列表 <div v-for"(data,index) in parerFont(item.fileInfo)" :key"index" class"data-list-item"><downloadCard :file-inf…...

图论第5天

127.单词接龙 需要cout看一下过程。 #include <iostream> #include <queue> #include <stack> #include <unordered_map> #include <unordered_set> #include <vector> using namespace ::std;class Solution { public:int ladderLength(…...

Java开发-面试题-0004-HashMap 和 Hashtable的区别

Java开发-面试题-0004-HashMap 和 Hashtable的区别 更多内容欢迎关注我&#xff08;持续更新中&#xff0c;欢迎Star✨&#xff09; Github&#xff1a;CodeZeng1998/Java-Developer-Work-Note 技术公众号&#xff1a;CodeZeng1998&#xff08;纯纯技术文&#xff09; 生活…...

Swift 序列(Sequence)排序面面俱到 - 从过去到现在(一)

概览 在任何语言中对序列(或集合)元素的排序无疑是一种司空见惯的常规操作,在 Swift 语言里自然也不例外。序列排序看似简单,实则“暗藏玄机”。 要想真正掌握 Swift 语言中对排序的“各种姿势”,我们还得从长计议。不如就先从最简单的排序基本功开始聊起吧。 在本篇博…...

redis 04 redis结构

1.客户端...

【原创】springboot+mysql农业园区管理系统设计与实现

个人主页&#xff1a;程序猿小小杨 个人简介&#xff1a;从事开发多年&#xff0c;Java、Php、Python、前端开发均有涉猎 博客内容&#xff1a;Java项目实战、项目演示、技术分享 文末有作者名片&#xff0c;希望和大家一起共同进步&#xff0c;你只管努力&#xff0c;剩下的交…...

web前端 孙俏:深度探索与实战之路

web前端 孙俏&#xff1a;深度探索与实战之路 在这个数字化、信息化的时代&#xff0c;web前端技术以其独特的魅力&#xff0c;吸引着越来越多的开发者投身其中。今天&#xff0c;我们将跟随孙俏的脚步&#xff0c;一同探索web前端的深度与广度&#xff0c;揭开其神秘的面纱。…...

opencv实战小结-银行卡号识别

实战1-银行卡号识别 项目来源&#xff1a;opencv入门 项目目的&#xff1a;识别传入的银行卡照片中的卡号 难点&#xff1a;银行卡上会有一些干扰项&#xff0c;如何排除这些干扰项&#xff0c;并且打印正确的号码是一个问题 最终效果如上图 实现这样的功能需要以下几个步骤…...

Windows API 开发桌面应用程序,在窗口按下鼠标左键不放可以拖图,并且拖图期间鼠标图标变成手掌

在Windows API中&#xff0c;要实现鼠标左键按下并拖动以移动窗口中的某个图形&#xff0c;并且同时改变鼠标图标为“手掌”形状&#xff08;这通常指的是“拖动”或“移动”的图标&#xff09;&#xff0c;你需要执行几个步骤。 以下是一个基本的步骤指南&#xff0c;用于在W…...

Docker的网络管理

文章目录 一、Docker容器之间的通信1、直接互联&#xff08;默认Bridge网络&#xff09;1.1、Docker安装后默认的网络配置1.2、创建容器后的网络配置1.2.1、首先创建一个容器1.2.2、ip a 列出网卡变化信息1.2.3、查看新建容器后的桥接状态 1.3、容器内安装常见的工具1.4、容器间…...

【数据结构】平衡二叉树左旋右旋与红黑树

平衡二叉树左旋右旋与红黑树 平衡二叉树 定义 平衡二叉树是二叉搜索树的一种特殊形式。二叉搜索树&#xff08;Binary Search Tree&#xff0c;BST&#xff09;是一种具有以下性质的二叉树&#xff1a; 对于树中的每个节点&#xff0c;其左子树中的所有节点都小于该节点的值…...

2024蓝桥杯初赛决赛pwn题全解

蓝桥杯初赛决赛pwn题解 初赛第一题第二题 决赛getting_startedbabyheap 初赛 第一题 有system函数&#xff0c;并且能在bss上读入字符 而且存在栈溢出&#xff0c;只要过掉check函数即可 check函数中&#xff0c;主要是对system常规获取权限的参数&#xff0c;进行了过滤&…...

大模型多轮问答的两种方式

前言 大模型的多轮问答难点就是在于如何精确识别用户最新的提问的真实意图&#xff0c;而在常见的使用大模型进行多轮对话方式中&#xff0c;我接触到的只有两种方式&#xff1a; 一种是简单地直接使用 user 和 assistant 两个角色将一问一答的会话内容喂给大模型&#xff0c…...

【无标题】1877A

足球锦标赛中有 n支球队。每对队伍匹配一次。每场比赛结束后&#xff0c;Pak Chanek收到两个整数作为比赛结果&#xff0c;即两队在比赛中得分的数量。一支球队的效率等于本队每场比赛的总进球数减去对手每场比赛的总进球数。 比赛结束后&#xff0c;Pak Dengklek会计算每支球…...

直播美颜工具解析:美颜SDK核心技术与性能优化方法

本篇文章&#xff0c;小编将深入解析直播美颜SDK的核心技术及其性能优化方法&#xff0c;以期为开发者提供有价值的参考。 一、美颜SDK核心技术 1.实时人脸检测与识别 美颜SDK的核心技术之一是实时人脸检测与识别。这项技术基于深度学习算法&#xff0c;能够快速、准确地识别…...

YOLOv10开源,高效轻量实时端到端目标检测新标准,速度提升46%

前言 实时目标检测在自动驾驶、机器人导航、物体追踪等领域应用广泛&#xff0c;近年来&#xff0c;YOLO 系列模型凭借其高效的性能和实时性&#xff0c;成为了该领域的主流方法。但传统的 YOLO 模型通常采用非极大值抑制 (NMS) 进行后处理&#xff0c;这会增加推理延迟&#…...

如何解决访问网站时IP被限制的问题?

在互联网上&#xff0c;用户可能会面临一个令人困扰的问题——当尝试访问某个特定的网站时&#xff0c;却发现自己的IP地址被该网站屏蔽。 IP地址被网站屏蔽是一个相对常见的现象&#xff0c;而导致这种情况的原因多种多样&#xff0c;包括恶意行为、违规访问等。本文将解释IP地…...

springboot城市美发管理系统的设计与实现-计算机毕业设计源码71715

摘 要 信息化社会内需要与之针对性的信息获取途径&#xff0c;但是途径的扩展基本上为人们所努力的方向&#xff0c;由于站在的角度存在偏差&#xff0c;人们经常能够获得不同类型信息&#xff0c;这也是技术最为难以攻克的课题。针对城市美发管理系统等问题&#xff0c;对城市…...

附近做网站/网络推广项目外包公司

神经网络的结构设计有3个主流的高级技巧&#xff1a;1&#xff0c;高低融合 (将高层次特征与低层次特征融合&#xff0c;提升特征维度的丰富性和多样性&#xff0c;像人一样同时考虑整体和细节)2&#xff0c;权值共享 (一个权值矩阵参与多个不同的计算&#xff0c;降低参数规模…...

做网站后有人抢注品牌关键字/seo站长综合查询

应用篇&#xff1a;qt写入版本号应用篇&#xff1a;qt写入版本号创建.rc文件在工程文件中载入.rc文件转载需注明出处应用篇&#xff1a;qt写入版本号 在软件开发及交付的过程中&#xff0c;常常需要对已经发布的软件进行技术维护和bug追踪&#xff0c;而这个恰恰依赖于工程师良…...

flask做大型网站开发/自助建站的优势

1、多选题&#xff1a; 可以运行R语言的操作系统包括&#xff08; &#xff09;&#xff1a; 选项&#xff1a; A:Linux B:Windows C:Mac OS D:Android 答案: 【Linux;Windows;Mac OS 】 2、单选题&#xff1a; &#xff08; &#xff09;可以命令将R语言升级到最新版本。…...

微网站独立域名/西安sem竞价托管

1 从file中选择 2...

网站建设公司上海做网站公司哪家好/国内永久免费的云服务器

类型检查和自动转换 类型判断 is/!is表达式 if (obj is String) {print(obj.length) }if (obj !is String) { // same as !(obj is String)print("Not a String") } else {print(obj.length) }智能转换 kotlin具有自动转换类型的功能&#xff0c;在is/!is表达式,会根…...

成都装饰公司网站建设/百度seo规则

&#xff08;一&#xff09;String类&#xff1a; Java使用String类来创建和操作字符串&#xff1b;String str"hello World"; 注意&#xff1a;String类是不可变的&#xff0c;一旦创建了String对象&#xff0c;他的值就无法改变了&#xff1b; 常用方法&#xf…...