44.网络游戏逆向分析与漏洞攻防-角色管理功能通信分析-角色创建服务器反馈数据包分析

免责声明:内容仅供学习参考,请合法利用知识,禁止进行违法犯罪活动!

如果看不懂、不知道现在做的什么,那就跟着做完看效果

现在的代码都是依据数据包来写的,如果看不懂代码,就说明没看懂数据包

内容参考于: 易道云信息技术研究院VIP课

上一个内容:43.角色创建功能的数据包初步分析

码云地址(master 分支):https://gitee.com/dye_your_fingers/titan

码云版本号:073c2dd3c5f53f237037dd1cea1b1d4c456291ff

代码下载地址,在 titan 目录下,文件名为:titan-角色创建服务器反馈数据包分析.zip

链接:https://pan.baidu.com/s/1W-JpUcGOWbSJmMdmtMzYZg

提取码:q9n5

--来自百度网盘超级会员V4的分享

HOOK引擎,文件名为:黑兔sdk升级版.zip

链接:https://pan.baidu.com/s/1IB-Zs6hi3yU8LC2f-8hIEw

提取码:78h8

--来自百度网盘超级会员V4的分享

以 43.角色创建功能的数据包初步分析 它的代码为基础进行修改

创建角色

点击下图红框里的东西,也就是创建角色,会给服务端发送 03 01 00 00 00数据包

  

发送了03 01 00 00 00数据包之后,服务端会返回一个数据包,也就是下图里的内容,下图红框里是字符串,出了1-3字节,其余的都是字符串,05是数据包头,01当做short类型解读也即是读1字节数据

接下来使用程序把字符串解读出来,上方数据包解读翻译的内容如下图,看不出是什么来,它就先不分析了

分析阵营选择发送的数据包,如下图

对它的分析,然后0A开头的数据包,进入游戏之后会有很多,下图中的00 0A 00 00第一个00是我自己补的,为了看起来方便,之后见到00 0A 00 00也是这个意思

如下图进入游戏之后,基本上发送的数据包都是0A开头

随便找的一个0A开头的数据包,可以满足上方拆分的结果

下图应该是移动时发送的数据包,因为有多个浮点数

然后这些0A开头的数据包,前23字节都是固定的结构

只有下图红框位置的数据会根据后方数据的个数而变,但总之结构还是固定的,所以前23字节可以用结构体处理,为了内存对齐结构体定义时应该是24字节,要在0A前面补一个0

然后经过多个数据包的分析,下图红框的02 0F位置的东西应该是操作类型,就是用来表示这是一个什么操作

发送聊天数据的数据包,它是02 01每次长度后面好像跟的都是02

长度后面的数据,为了后续方便使用,现在把它们称作为数据参数,长度称为数据参数的个数,如下图

当前的代码搭建了,解析数据参数的框架

总结:现在得到的东西,下方的数据参数总结,分别在上方的某个图中可以看到真实数据

数据参数07开头是wchar_t*类型,也就是宽字节字符串,07开头后面的数据是字符串的长度

数据参数06开头是char*类型,也就是单字节字符串,06开头后面的数据是字符串的长度

数据参数04开头是浮点数,也就是float

数据参数03开头是一个数字,也就是long long类型

数据参数02开头是一个数字,也就是int类型

GameWinSock.h文件的修改:新加 I_SEND_CUSTOM宏、S_CREATEROLE_START宏

#pragma once#define I_LOGIN 0x2 // 登录
#define I_CREATEROLE_START 0x3 // 创建角色发送数据包的头
#define I_DELROLE 0x6 // 删除角色发送数据包的头
#define I_SEND_CUSTOM 0x0A // 进入游戏之后大部分数据包的头#define S_TIPS 0x3 // 服务端返回错误的数据包头
#define S_LOGINOK 0x4 // 登录成功的返回数据包的头
// 创建角色服务端反馈的数据(发送03 01 00 00 00这个数据包服务端返回的数据包头)
#define S_CREATEROLE_START 0x05class GameWinSock
{typedef bool(GameWinSock::* PROC)(char*, unsigned);
public:unsigned* vatble;// 虚函数表unsigned un[17];unsigned RecvPoint; // 游戏recv之后调用处理一个数据包函数时的eax,这里偏移是0x48
public:static PROC _OnConnect;static PROC _OnSend;static PROC _OnRecv;bool OnConnect(char* ip, unsigned port);bool OnSend(char* buff, unsigned len);bool OnRecving(char* buff, unsigned len);bool OnRecv(char* buff, unsigned len);void Init();// end为了避免超长解读void AnlyBuff(char* start, char* end, char index = 0);
};

GameWinSock.cpp文件的修改:新加 OnSendCustom函数、OnSvrStartCreateRole函数

#include "pch.h"
#include "GameWinSock.h"
#include "extern_all.h"
#include "NetClass.h"
#include "EnCode.h"typedef bool(*DealProc)(char*&, unsigned&);DealProc SendDealProc[0x100];
DealProc RecvDealProc[0x100];GameWinSock::PROC GameWinSock::_OnConnect{};
GameWinSock::PROC GameWinSock::_OnSend{};
GameWinSock::PROC GameWinSock::_OnRecv{};bool DeafaultDeal(char*&, unsigned&) { return true; }// 登录数据包的处理
bool Onlogin(char*& buff, unsigned& len) {PDATALOGIN _data = (PDATALOGIN)(buff + 1);char* _id = _data->Id;_data = (PDATALOGIN)(buff + 1 + _data->lenId - 0x10);char* _pass = _data->Pass;Client->Onlogin(_id, _pass);/* 修改账号密码len = sizeof(DATA_LOGIN) + 1;buff = new char[len];DATA_LOGIN data;PDATALOGIN _data = &data;buff[0] = 0x2;CStringA _id = "";// 补充账号CStringA _pass = "";// 补充密码memcpy(_data->Id, _id.GetBuffer(), _id.GetLength());memcpy(_data->Pass, _pass.GetBuffer(), _pass.GetLength());memcpy(buff + 1, _data, len - 1);*//* 监控登录数据PDATALOGIN _data = (PDATALOGIN)buff;CStringA _id = _data->Id;_data = (PDATALOGIN)(buff + _data->lenId - 0x10);CStringA _pass = _data->Pass;CStringA _tmp;// 请求登录 账号[% s]密码[% s] 这个内容别人在逆向的时候就会看到// 所以这种东西需要自己搞个编码来代替它_tmp.Format("请求登录 账号[%s]密码[%s]", _id, _pass);
#ifdef  Anlyanly->SendData(TTYPE::I_DIS, 1, _tmp.GetBuffer(), _tmp.GetAllocLength());
#endif*//*返回false,游戏无法发送数据包原因看调用此此函数的位置 OnSend 函数(if (SendDealProc[buff[0]]((buff + 1), len - 1)))*/return true;
}bool OnTips(char*& buff, unsigned& len) {int* code = (int*)&buff[1];return Client->Tips(code[0]);
}bool OnDelRole(char*& buff, unsigned& len) {PDATADELROLE p = (PDATADELROLE)buff;return Client->OnDelRole((wchar_t*)(p->buff), p->len);// 返回值改为false将拦截发送的删除角色数据包// 详情看注册 OnDelRole 函数的位置,Init函数// return true;
}bool OnloginOk(char*& buff, unsigned& len) {PDATALOGINOK _p = (PDATALOGINOK)&buff[1];ROLE_DATA* roleDatas = nullptr;if (_p->RoleCount > 0) {char* buffStart = buff + 1 + sizeof(DATA_LOGIN_OK);WinSock->AnlyBuff(buffStart, buff + len);roleDatas = new ROLE_DATA[_p->RoleCount];for (int i = 0; i < _p->RoleCount; i++){roleDatas[i].byte = buffStart;roleDatas[i].un = buffStart;roleDatas[i].un1 = buffStart;roleDatas[i].name = buffStart;roleDatas[i].infos = buffStart;roleDatas[i].un2 = buffStart;roleDatas[i].un3 = buffStart;}Client->loginok(roleDatas, _p->RoleCount);}return true;
}bool OnStartCreateRole(char*& buff, unsigned& len){// 申请进入创建角色界面int* code = (int*)&buff[1];return Client->OnStartCreateRole(code[0]);
}bool OnSendCustom(char*& buff, unsigned& len) {PNET_SEND_HEAD head = (PNET_SEND_HEAD)(buff - 1);int icount = head->count;char* buffStart = (char*)head + sizeof(NET_SEND_HEAD);if (buffStart[0] != 0x02) {#ifdef  Anlyanly->SendData(TTYPE::I_DIS, I_SEND_CUSTOM, "SEND_CUSTOM 发现异常数据", 25);
#endifreturn true;}// 解析一个长度后面的数据EnCode coder(buffStart, len, 1);GINT* intCoder = (GINT*)&coder;// 得到数据int opcode = intCoder->value();
}bool OnSvrStartCreateRole(char*& buff, unsigned& len) {short* _st = (short*)&buff[1];wchar_t* _txt = (wchar_t*)&buff[3];
#ifdef  AnlyCString txt;CStringA txtA;txt.Format(L"code:%d\r\n%s", _st[0], _txt);txtA = txt;//AfxMessageBox(txtA);anly->SendData(TTYPE::I_DIS, S_CREATEROLE_START, txtA.GetBuffer(), txt.GetAllocLength() + 1);
#endifreturn Client->OnScrStartCreateRole(_st[0], _txt);
}// 这个函数拦截了游戏的连接
bool GameWinSock::OnConnect(char* ip, unsigned port)
{
#ifdef  Anly// 长度24的原因,它是宽字节要,一个文字要2个字节,一共是10个文字加上结尾的0是11个// 所以 11 乘以2,然后再加2 anly->SendData(TTYPE::I_LOG, 0, L"服务器正在连接。。。", 24);
#endif// this是ecx,HOOK的点已经有ecx了WinSock = this;bool b = (this->*_OnConnect)(ip, port);// 下方注释的代码时为了防止多次注入,导致虚函数地址不恢复问题导致死循环,通过一次性HOOK也能解决/*unsigned* vtable = (unsigned*)this;vtable = (unsigned*)vtable[0];union {unsigned value;bool(GameWinSock::* _proc)(char*, unsigned);} vproc;vproc._proc = _OnConnect;DWORD oldPro, backProc;VirtualProtect(vtable, 0x10x00, PAGE_EXECUTE_READWRITE, &oldPro);vtable[0x34 / 4] = vproc.value;VirtualProtect(vtable, 0x10x00, oldPro, &backProc);*/return b;
}bool GameWinSock::OnSend(char* buff, unsigned len)
{/*这里就可以监控游戏发送的数据了*/#ifdef  Anlyanly->SendData(TTYPE::I_SEND, buff[0], buff, len);
#endif/*数据包的头只有一字节所以它的取值范围就是0x0-0xFF*/if (SendDealProc[buff[0]]((buff), len)) {// 执行失败不让游戏发送数据包return (this->*_OnSend)(buff, len);}else {// 发送失败屏蔽消息return true;// 屏蔽消息}}bool GameWinSock::OnRecving(char* buff, unsigned len)
{// MessageBoxA(0, "11111111111111", "0", MB_OK);/*监控游戏接收的数据包*/
#ifdef  Anlyanly->SendData(TTYPE::I_RECV, buff[0], buff, len);
#endifreturn RecvDealProc[buff[0]](buff, len);
}bool GameWinSock::OnRecv(char* buff, unsigned len)
{
//#ifdef  Anly
//	anly->SendData(1, buff, len);
//#endifreturn (this->*_OnRecv)(buff, len);
}void GameWinSock::Init()
{for (int i = 0; i < 0x100; i++) {SendDealProc[i] = &DeafaultDeal;RecvDealProc[i] = &DeafaultDeal;}// 注册登录数据包处理函数// SendDealProc[I_LOGIN] = &Onlogin;SendDealProc[I_DELROLE] = &OnDelRole;SendDealProc[I_CREATEROLE_START] = &OnStartCreateRole;SendDealProc[I_SEND_CUSTOM] = &OnSendCustom;// 注册数据登录失败数据包处理函数RecvDealProc[S_TIPS] = &OnTips;RecvDealProc[S_LOGINOK] = &OnloginOk;RecvDealProc[S_CREATEROLE_START] = &OnSvrStartCreateRole;
}// 它会生成一个结构体,详情看效果图
void GameWinSock::AnlyBuff(char* start, char* end, char index)
{
#ifdef  AnlyCStringA txt;CStringA tmp;CString utmp;EnCode _coder;GBYTE* _bytecoder;GSHORT* _shortcoder;GINT* _intcoder;GFLOAT* _floatcoder;GDOUBLE* _doublecoder;GCHAR* _asccoder;GUTF16* _utfcoder;GINT64* _int64coder;CStringA _opname = data_desc[_coder.index][_coder.op].name;while (start < end) {_coder.Init(start, index);// _opname.MakeLower()是变为小写字母,会影响 _opname它的值// 所以又写了一边 data_desc[_coder.index][_coder.op].nametmp.Format("%s %s;//", data_desc[_coder.index][_coder.op].name, _opname.MakeLower());txt = txt + tmp;if (_coder.index == 0) {switch (_coder.op){case 1:_shortcoder = (GSHORT*)&_coder;tmp.Format("%d\r\n", _shortcoder->value());txt = txt + tmp;break;case 2:_intcoder = (GINT*)&_coder;tmp.Format("%d\r\n", _intcoder->value());txt = txt + tmp;break;case 4:_floatcoder = (GFLOAT*)&_coder;tmp.Format("%d\r\n", _floatcoder->value());txt = txt + tmp;break;case 6:_bytecoder = (GBYTE*)&_coder;tmp.Format("%d\r\n", _bytecoder->value());txt = txt + tmp;break;case 7:_utfcoder = (GUTF16*)&_coder;utmp.Format(L"[%s]\r\n", _utfcoder->value());tmp = utmp;txt = txt + tmp;break;case 3:case 5:case 8:_int64coder = (GINT64*)&_coder;tmp.Format("%.i64d\r\n", _int64coder->value());txt = txt + tmp;break;default:break;}}} anly->SendData(TTYPE::I_DIS, 0, txt.GetBuffer(), txt.GetAllocLength() + 1);
#endif//		char* buffStart = buff + 1 + sizeof(DATA_LOGIN_OK);
//		char* buffEnd = buff + len;
//		while (buffStart < buffEnd) {
//			/*
//				由于 EnCode 的入参带了& 所以可以在EnCode里直接对buffStart或len进行操作
//				也就是说只要入参带了&在函数里的操作会影响它原本的值
//				比如 
//				char*a = 0;
//				aaa(a)
//				aaa(char*&AA){
//					AA = new char[10]; // 它就会把a的值改成new char[10]
//				}
//			*/
//			EnCode* _coder = new EnCode(buffStart, len);
//			tmp.Format("%s:",data_desc[_coder->index][_coder->op].name);
//			txt = txt + tmp;
//
//			if (_coder->op == 0x7) {
//				utmp = (wchar_t*)_coder->pointer;
//				tmp = utmp;
//				txt = txt + tmp;
//			}else
//			if (_coder->op == 0x3) {
//				tmp.Format("[%f]", _coder->fval);
//				txt = txt + tmp;
//			}
//			else {
//				tmp.Format("[%d]", _coder->val);
//				txt = txt + tmp;
//			}
//		}
//
//#ifdef  Anly
//		// 长度24的原因,它是宽字节要,一个文字要2个字节,一共是10个文字加上结尾的0是11个
//		// 所以 11 乘以2,然后再加2 
//		anly->SendData(TTYPE::I_DIS, 0, txt.GetBuffer(), txt.GetAllocLength() + 1);
//#endif
}

NetClass.h文件的修改:新加NET_SEND_HEAD结构体

#pragma once
#include "EnCode.h"
// 发送数据标准头
typedef struct NET_SEND_HEAD {// 为了内存对齐,当前结构不可能超过255,所以这样写是没问题的char len = sizeof(NET_SEND_HEAD) - 1;char op = 0x0A;short unst = 0;int unnt[4]{};short unst1 = 0;short count = 0;
}*PNET_SEND_HEAD;// 申请创建角色结构体
typedef struct NET_CREATEROLE_START {char un[0x02];// 用来做内存对齐char len = sizeof(NET_CREATEROLE_START) - 3;// 用来做内存对齐char op = 0x03;int code = 0x01;
}*PNET_CREATEROLE_START;// 删除角色数据结构
typedef struct DATA_DELROLE {char op;char buff[0x1F];int len;int un[8] = { 0x01 , 0x03};}*PDATADELROLE;/*数据包还原结构体要注意内存对齐,如果数据不满4字节,它字段会补齐比如结构体里有一个char变量,它是1字节,在内存里它可能会为了内存对齐让它变成4字节,所以这要注意
*/
// 登录数据
typedef struct DATA_LOGIN {int op = 0x0300;char buff[0x10]{};int lenId = 0x10;/*这个是登录的账号,它可能会变成0x20或更长,现在默认让它0x10读的时候以长度为准就好了*/char Id[0x10]{};int lenPass = 0x10;/*这个是登录的密码,它可能会变成0x20或更长,现在默认让它0x10读的时候以长度为准就好了*/char Pass[0x10]{};int lenCode = 0x10;char Code[0x10]{};int eop = 0x01;
}*PDATALOGIN;
typedef struct DATA_LOGIN_OK {// 登录成功数据包头int un[8] = { 0, 0, 0x76B, 0x0C, 0x1E,0, 0, 0 };int index = 0;int RoleCount = 0;
}*PDATALOGINOK;
typedef class ROLE_DATA {// 登录成功数据包
public:GBYTE byte;GINT un;GINT un1;GUTF16 name;GUTF16 infos;GINT un2;GINT64 un3;
}*PROLEDATA;

NetClient.h文件的修改:新加 OnScrStartCreateRole函数声明

#pragma once
#include "NetClass.h"
class NetClient // 监视客户端每一个操作
{
private:PROLEDATA roles;unsigned rolecount;bool logined = false;bool DelRole(wchar_t* rolename, unsigned _len);
public:/*模拟登陆的方法Id是账号Pass是密码它要基于发送的方法实现,因为我们没有连接socket的操作*/bool login(const char* Id, const char* Pass);bool DelRole(wchar_t* rolename);bool StartCreateRole();// 用于创建角色// 根据角色名字获取一个登录成功数据包(选择角色列表里的一个数据)PROLEDATA GetRoleByName(wchar_t* rolename);
public:// 用于拦截游戏删除角色功能bool virtual OnDelRole(wchar_t* rolename, unsigned _len);// 用于拦截游戏登录功能void virtual Onlogin(const char* Id, const char*Pass);// 用于拦截游戏创建角色功能bool virtual OnStartCreateRole(int code);
public:// 处理失败,参数是错误码bool virtual Tips(int code);void virtual loginok(ROLE_DATA* _roles, int count);bool virtual OnScrStartCreateRole(short code,wchar_t* _txt);
};

NetClient.cpp文件的修改:新加 OnScrStartCreateRole函数

#include "pch.h"
#include "NetClient.h"
#include "extern_all.h"bool NetClient::login(const char* Id, const char* Pass)
{const int bufflen = sizeof(DATA_LOGIN) + 1;char buff[bufflen];DATA_LOGIN data;// 有些操作系统这样写会报错,因为内存不对齐,现在Windows下没事//PDATALOGIN _data = (PDATALOGIN)(buff + 1);// 这样写就能解决内存对齐问题PDATALOGIN _data =&data;int len = strlen(Id);memcpy(_data->Id, Id, len);len = strlen(Pass);memcpy(_data->Pass, Pass, len);memcpy(buff+1, _data, sizeof(DATA_LOGIN));buff[0] = I_LOGIN;return  WinSock->OnSend(buff, sizeof(buff));}bool NetClient::DelRole(wchar_t* rolename)
{PROLEDATA _role = GetRoleByName(rolename);if (_role == nullptr) {return false;}else {return DelRole(rolename, _role->name.lenth);}return false;
}bool NetClient::StartCreateRole()
{NET_CREATEROLE_START _data;return WinSock->OnSend(&_data.op, _data.len);
}PROLEDATA NetClient::GetRoleByName(wchar_t* rolename)
{//PROLEDATA result = nullptr;for (int i = 0; i < rolecount; i++){// StrCmpW判断两个字符串是否相同// 比较时区分大小写,如果字符串相同返回0if (StrCmpW(roles[i].name.value(), rolename) == 0) {return &roles[i];}}return nullptr;
}bool NetClient::DelRole(wchar_t* rolename, unsigned _len)
{DATA_DELROLE _data;_data.op = 0x06;_data.len = _len;memcpy(_data.buff, rolename, _len);return WinSock->OnSend((char*)&_data, sizeof(DATA_DELROLE) - 1);
}bool NetClient::OnDelRole(wchar_t* rolename, unsigned _len)
{AfxMessageBox(rolename);return true;
}void NetClient::Onlogin(const char* Id, const char* Pass)
{/*const int bufflen = sizeof(DATA_LOGIN) + 1;char buff[bufflen];DATA_LOGIN data;// 有些操作系统这样写会报错,因为内存不对齐,现在Windows下没事//PDATALOGIN _data = (PDATALOGIN)(buff + 1);// 这样写就能解决内存对齐问题PDATALOGIN _data =&data;int len = strlen(Id);memcpy(_data->Id, Id, len);len = strlen(Pass);memcpy(_data->Pass, Pass, len);memcpy(buff+1, _data, sizeof(DATA_LOGIN));buff[0] = I_LOGIN;return  WinSock->OnSend(buff, sizeof(buff));*/
}bool NetClient::OnStartCreateRole(int code)
{return true;
}bool NetClient::Tips(int code)
{
#ifdef  AnlyCString txt;if (code == 51001) {txt = L"登陆失败,易道云通行证不存在!";}else if (code == 51002) {txt = L"登录失败,密码错误!";}else {txt.Format(L"未知登录错误:%d", code);}anly->SendData(TTYPE::I_LOG, 0, txt.GetBuffer(), txt.GetLength()*2);
#endifreturn true;
}void NetClient::loginok(ROLE_DATA* _roles, int count)
{logined = true;if(roles) delete[] roles;roles = _roles;rolecount = count;
}bool NetClient::OnScrStartCreateRole(short code, wchar_t* _txt)
{return true;
}

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

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

相关文章

转让北京装饰装修二级和建筑幕墙施工二级流程和条件

我公司可以帮您快速办理北京市各类建筑姿质申请&#xff0c;也有现成的姿质转让&#xff0c;新申请建筑姿质要求比较高&#xff0c;但是对于企业来说&#xff0c;承接模板脚手架工程也是需要具备姿质的&#xff0c;该姿质也就是模板脚手架姿质&#xff0c;那么对于企业想要申请…

基于Java+SpringBoot+Vue幼儿园管理系统(源码+文档+部署+讲解)

一.系统概述 随着信息时代的来临&#xff0c;过去的传统管理方式缺点逐渐暴露&#xff0c;对过去的传统管理方式的缺点进行分析&#xff0c;采取计算机方式构建幼儿园管理系统。本文通过课题背景、课题目的及意义相关技术&#xff0c;提出了一种教 学生信息、学生考勤、健康记录…

Fecify 商品标签功能

关于商品标签 商品标签是指商家可以在展示商品时&#xff0c;自己创建一个自定义标签&#xff0c;可自定义某个关键词或短语。这样顾客在浏览商城时&#xff0c;只需要通过标签就能看到更直观的展示信息。 商品标签可以按照用户的属性、行为、偏好等进行分类&#xff0c;标签要…

传输大咖22|如何利用ProtoBuf实现高效的数据传输?

在今日信息技术日新月异的时代&#xff0c;数据传输的速度与安全性无疑成为了软件开发中的重中之重。无论是微服务架构下的服务间交流&#xff0c;还是客户端与服务器间的数据互动&#xff0c;寻求一种既高效又稳妥的数据传输方式已成为共识。尽管传统的数据格式&#xff0c;如…

水务行业如何实现数字化转型实现智能化管理

水务行业在当下已经迈入了新的发展阶段&#xff0c;行业内增大了信息化、数字化的探索&#xff0c;尤其是智能化技术出现以后&#xff0c;智能水务概念的提出使得水务数字化转型成为可能。但我国水务行业经历了漫长的发展时期&#xff0c;在很长一段时间内因为存在发展思路、技…

骨传导耳机怎么选?这五款骨传导耳机性能好、配置高,跟着买不出错!

如今&#xff0c;骨传导耳机作为一种创意十足的蓝牙耳机&#xff0c;正在逐渐走入千家万户&#xff0c;成为最受欢迎的耳机款式。然而&#xff0c;随着骨传导耳机的热度增加&#xff0c;市面上开始出现各式各样的骨传导耳机品牌&#xff0c;面对琳琅满目的骨传导耳机品牌&#…

基于SpringBoot的“垃圾分类网站”的设计与实现(源码+数据库+文档+PPT)

基于SpringBoot的“垃圾分类网站”的设计与实现&#xff08;源码数据库文档PPT) 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SpringBoot 工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 系统功能结构图 系统功能界面图 用户登录、用户注…

方案分享 | 针对ETC系统的OBE-SAM模块设计方案

ETC&#xff08;Electrical Toll Collection&#xff09;不停车收费是目前世界上最先进的路桥收费方式。通过安装在车辆挡风玻璃上的车载单元与安装在收费站 ETC 车道上的路侧单元之间的微波专用短程通讯&#xff0c;利用计算机联网技术与银行进行后台结算处理&#xff0c;从而…

2024软件测试工具测评,总有一款适合你!

在软件开发周期中&#xff0c;测试是确保产品质量的关键环节。随着企业对于软件质量的要求日益提升&#xff0c;测试人员面临着前所未有的挑战&#xff0c;“工欲善其事必先利其器”&#xff0c;选择一款高效、实用的软件测试工具&#xff0c;不仅能够提升测试效率&#xff0c;…

ElasticSearch分词检索

1. 倒排索引&#xff1a;表示一种数据结构&#xff0c;分词词条与文档id集合的隐射关系 2. 它跟关系型数据库是一种互补的关系&#xff0c;因为关系型数据库支持事务操作&#xff0c;满足ACID原则 3. 索引库的文档字段只允许新增不允许修改 1.创建索引库 put /索引库名称2.1 …

吴恩达2022机器学习专项课程(一) 5.8 学习率的选择

问题预览/关键词 梯度下降不正常工作的曲线是什么样子&#xff1f;如何调试学习率来检测代码中的bug&#xff1f;学习率过小的后果是&#xff1f;如何选择学习率&#xff1f; 笔记 1.曲线错误的样子 曲线没有逐步递减&#xff0c;由于学习率选择过大或代码有bug。 2.调试…

LeetCode-279. 完全平方数【广度优先搜索 数学 动态规划】

LeetCode-279. 完全平方数【广度优先搜索 数学 动态规划】 题目描述&#xff1a;解题思路一&#xff1a;Python 动态规划五部曲&#xff08;完全平方数就是物品&#xff08;可以无限件使用&#xff09;&#xff0c;凑个正整数n就是背包&#xff0c;问凑满这个背包最少有多少物品…

在图片上画出mask和pred

画出论文中《Variance-aware attention U-Net for multi-organ segmentation》的图1&#xff0c;也就是在原图上画出mask和pred的位置。 新建一个文件夹 然后运行代码&#xff1a; import cv2 import os from os.path import splitext####第一次&#xff1a;把GT&#xff08…

【文件IO】JavaIO详解

一.文件的相关概念 什么是文件? 文件是计算机中存储信息的基本单位。文件通常指的是存储在计算机或其他数字存储设备上的一段信息的集合&#xff0c;这些信息可以是文本、图片、音频、视频等不同格式的数据。 文件路径: 文件的路径可以分为两类 相对路径:先指定一个"当前…

武汉星起航:成功挂牌,董事长张振邦:将持续为创业者提供支持

2023年10月30日&#xff0c;对于武汉星起航电子商务有限公司来说&#xff0c;是一个值得铭记的历史时刻。这一天&#xff0c;公司在上海股权托管交易中心成功挂牌展示&#xff0c;这不仅是公司发展历程中的一个重要里程碑&#xff0c;更是对其不懈努力和卓越成就的肯定。 武汉…

求三角形面积(C语言)

一、运行结果&#xff1b; 二、源代码&#xff1b; # define _CRT_SECURE_NO_WARNINGS # include <stdio.h> # include <math.h>int main() {//初始化变量值&#xff1b;double a, b, c, s, area;//赋值&#xff1b;a 3.67;b 5.43;c 6.21;//运算求s&#xff1b…

阿里云ECS服务器上如何白嫖一年的腾讯云的SSL证书(详细保姆教程)

有的时候&#xff0c;我们在阿里云上有一个ECS备案的服务器&#xff0c;而且也有域名&#xff0c;但是现在很多场景下是需要Https协议的&#xff0c;如果用宝塔搭建网站系统的话&#xff0c;就需要一个SSL证书&#xff0c;目前阿里云上SSL证书购买要么是付费的&#xff0c;要么…

Leetcode算法训练日记 | day21

一、二叉搜索树的最小绝对差 1.题目 Leetcode&#xff1a;第 530 题 给你一个二叉搜索树的根节点 root &#xff0c;返回 树中任意两不同节点值之间的最小差值 。 差值是一个正数&#xff0c;其数值等于两值之差的绝对值。 示例 1&#xff1a; 输入&#xff1a;root [4,2,…

mapv修改源码实现图标和管道到统一页面显示,图标和管道和点击

一、效果图 二、背景 map 地图添加marker&#xff0c;是操作的dom&#xff0c;而mapv是使用的canvas方式&#xff0c;所以性能要好 三、Mapv和MapVGL的区别 百度地图 JavaScript API GL快速升级 和mapVGL的使用 Mapv 是一款基于百度地图的大数据可视化开源库&#xff0c;可以…

【Linux】socket编程2

欢迎来到Cefler的博客&#x1f601; &#x1f54c;博客主页&#xff1a;折纸花满衣 &#x1f3e0;个人专栏&#xff1a;题目解析 目录 &#x1f449;&#x1f3fb;客户端代码Makefile(生成目标文件)UdpClient.cc(客户端代码)服务端代码部分优化1&#xff08;接受客户端时显示客…