zend server试用分析
文件:ZendServer-2021.4.1-multi-php-Windows_x86.exe
安装后可以试用30天,想分析下限制原理, 根据安装日志,发现了2个关键的文件:
ZendServer\gui\module\Configuration\src\Configuration\License\Wrapper.php
ZendServer\gui\module\Configuration\src\Configuration\License\License.php
Wrapper.php里面有一个关键的函数:
public function getSerialNumberInfo($serialNumber, $userName){$method = 'zem_serial_number_info';try {$this->validateMethod($method);$licenseInfo = $method($serialNumber, $userName);if (!is_array($licenseInfo)) {throw new ZSException('unexpected response received');}} catch (\Exception $e) {Log::err("method {$method} invocation failed with the following error: ".$e->getMessage());throw new ZSException("method {$method} invocation failed with the following error: ".$e->getMessage());}return new License($licenseInfo);}
根据$licenseInfo = $method($serialNumber, $userName);这行代码可以知道,调用了函数:zem_serial_number_info来获取许可证信息,试用期限制应该就在这个函数里面了。
知道了函数名称,我们如何找到这个函数呢?想了下PHP程序的执行流程:
1、客户端请求PHP
2、PHP服务端收到请求 php.exe or php-cgi.exe
3、PHP加载zend server扩展插件,检测是否过期了
思路很清晰了,去PHP扩展目录看下:zendserver\ZendServer\php\7.4\lib\ext
发现了可疑文件:ZendUtils.dll
使用IDA分析ZendUtils.dll的导出函数get_module,找到了函数zem_serial_number_info的地址
get_module IDA代码如下:
void *get_module()
{return get_module_0();
}void *get_module_0()
{return &unk_10061230;
}
函数代码很简单,返回了一个地址10061230,到这里需要了解PHP扩展模块的编写方法和导出函数原理,我们在IDA跳到地址10061230:
data:10061230 unk_10061230 db 5Ch ; \ ; DATA XREF: sub_10006E20↑o
.data:10061230 ; get_module_0↑o
.data:10061231 db 0
.data:10061232 db 0
.data:10061233 db 0
.data:10061234 db 0B6h
.data:10061235 db 16h
.data:10061236 db 34h ; 4
.data:10061237 db 1
.data:10061238 db 0
.data:10061239 db 0
.data:1006123A db 0
.data:1006123B db 0
.data:1006123C db 0
.data:1006123D db 0
.data:1006123E db 0
.data:1006123F db 0
.data:10061240 db 0
.data:10061241 db 0
.data:10061242 db 0
.data:10061243 db 0
.data:10061244 dd offset aZendUtils ; "Zend Utils"
.data:10061248 dd offset off_100610A0 //这里就是PHP扩展模块的导出函数表了
.data:1006124C dd offset sub_10001EF6
.data:10061250 dd offset sub_1000239C
再跳到地址:off_100610A0
.data:100610A0 off_100610A0 dd offset aZemGetExtensio
.data:100610A0 ; DATA XREF: .data:10061248↓o
.data:100610A0 ; "zem_get_extension_info_by_id"
.data:100610A4 dd offset sub_10002973
.data:100610A8 db 0
.data:100610A9 db 0
.data:100610AA db 0
.data:100610AB db 0
.data:100610AC db 0FFh
.data:100610AD db 0FFh
.data:100610AE db 0FFh
.data:100610AF db 0FFh
.data:100610B0 db 0
.data:100610B1 db 0
.data:100610B2 db 0
.data:100610B3 db 0
.data:100610B4 dd offset aZemGetExtensio_0 ; "zem_get_extension_info_by_name"
.data:100610B8 dd offset sub_100013D9
.data:100610BC align 10h
.data:100610C0 db 0FFh
.data:100610C1 db 0FFh
.data:100610C2 db 0FFh
.data:100610C3 db 0FFh
.data:100610C4 db 0
.data:100610C5 db 0
.data:100610C6 db 0
.data:100610C7 db 0
.data:100610C8 dd offset aZemGetExtensio_1 ; "zem_get_extensions_info"
.data:100610CC dd offset sub_10002432
.data:100610D0 db 0
.data:100610D1 db 0
.data:100610D2 db 0
.data:100610D3 db 0
.data:100610D4 db 0FFh
.data:100610D5 db 0FFh
.data:100610D6 db 0FFh
.data:100610D7 db 0FFh
.data:100610D8 db 0
.data:100610D9 db 0
.data:100610DA db 0
.data:100610DB db 0
.data:100610DC dd offset aZemGetLicenseI ; "zem_get_license_info"
.data:100610E0 dd offset zem_get_license_info_sub_10002455
.data:100610E4 align 8
.data:100610E8 db 0FFh
.data:100610E9 db 0FFh
.data:100610EA db 0FFh
.data:100610EB db 0FFh
.data:100610EC db 0
.data:100610ED db 0
.data:100610EE db 0
.data:100610EF db 0
.data:100610F0 dd offset aZendIsConfigur ; "zend_is_configuration_changed"
.data:100610F4 dd offset sub_10001A78
.data:100610F8 db 0
.data:100610F9 db 0
.data:100610FA db 0
.data:100610FB db 0
.data:100610FC db 0FFh
.data:100610FD db 0FFh
.data:100610FE db 0FFh
.data:100610FF db 0FFh
.data:10061100 db 0
.data:10061101 db 0
.data:10061102 db 0
.data:10061103 db 0
.data:10061104 dd offset aZendSetConfigu ; "zend_set_configuration_changed"
.data:10061108 dd offset sub_10001262
.data:1006110C align 10h
.data:10061110 db 0FFh
.data:10061111 db 0FFh
.data:10061112 db 0FFh
.data:10061113 db 0FFh
.data:10061114 db 0
.data:10061115 db 0
.data:10061116 db 0
.data:10061117 db 0
.data:10061118 dd offset aZendGetCfgVar ; "zend_get_cfg_var"
.data:1006111C dd offset sub_10001604
.data:10061120 db 0
.data:10061121 db 0
.data:10061122 db 0
.data:10061123 db 0
.data:10061124 db 0FFh
.data:10061125 db 0FFh
.data:10061126 db 0FFh
.data:10061127 db 0FFh
.data:10061128 db 0
.data:10061129 db 0
.data:1006112A db 0
.data:1006112B db 0
.data:1006112C dd offset aZendRestartPhp ; "zend_restart_php"
.data:10061130 dd offset sub_100026A3
.data:10061134 align 8
.data:10061138 db 0FFh
.data:10061139 db 0FFh
.data:1006113A db 0FFh
.data:1006113B db 0FFh
.data:1006113C db 0
.data:1006113D db 0
.data:1006113E db 0
.data:1006113F db 0
.data:10061140 dd offset aZendGetFileSiz ; "zend_get_file_size"
.data:10061144 dd offset sub_1000146F
.data:10061148 db 0
.data:10061149 db 0
.data:1006114A db 0
.data:1006114B db 0
.data:1006114C db 0FFh
.data:1006114D db 0FFh
.data:1006114E db 0FFh
.data:1006114F db 0FFh
.data:10061150 db 0
.data:10061151 db 0
.data:10061152 db 0
.data:10061153 db 0
.data:10061154 dd offset aZendGetCpuArch ; "zend_get_cpu_arch"
.data:10061158 dd offset sub_10002D6F
.data:1006115C align 10h
.data:10061160 db 0FFh
.data:10061161 db 0FFh
.data:10061162 db 0FFh
.data:10061163 db 0FFh
.data:10061164 db 0
.data:10061165 db 0
.data:10061166 db 0
.data:10061167 db 0
.data:10061168 dd offset aZemSerialNumbe ; "zem_serial_number_info"
.data:1006116C dd offset sub_100015B9
.data:10061170 db 0
.data:10061171 db 0
.data:10061172 db 0
.data:10061173 db 0
.data:10061174 db 0FFh
.data:10061175 db 0FFh
.data:10061176 db 0FFh
.data:10061177 db 0FFh
.data:10061178 db 0
.data:10061179 db 0
.data:1006117A db 0
.data:1006117B db 0
看到了函数zem_serial_number_info:
.data:10061168 dd offset aZemSerialNumbe ; "zem_serial_number_info"
.data:1006116C dd offset sub_100015B9
跟进sub_100015B9看下:
int __fastcall sub_100015B9(int a1, int *a2)
{return sub_1000A730(a1, a2);
}
int __fastcall sub_1000A730(int a1, int *a2)
{int *v2; // ediint result; // eaxunsigned int v4; // ecxconst struct QString *v5; // esiint v6; // eaxint v7; // eaxvolatile signed __int32 *v8; // [esp+Ch] [ebp-30h]volatile signed __int32 *v9; // [esp+10h] [ebp-2Ch]char v10; // [esp+14h] [ebp-28h]char v11; // [esp+18h] [ebp-24h]char v12; // [esp+1Ch] [ebp-20h]char v13; // [esp+20h] [ebp-1Ch]int v14; // [esp+24h] [ebp-18h]const char *v15; // [esp+28h] [ebp-14h]int v16; // [esp+2Ch] [ebp-10h]int v17; // [esp+38h] [ebp-4h]v2 = a2;result = zend_parse_parameters(*(_DWORD *)(a1 + 28), "ss", &v15, &v12, &v14, &v13);if ( result == -1 ){*v2 = -1;v2[2] = 4;}else{if ( v15 )v4 = strlen(v15);elsev4 = -1;v16 = QString::fromAscii_helper(v15, v4);sub_10001267((int)&v8);sub_10002306(&v8, (int)&v16);v5 = (const struct QString *)sub_1000272F((int)&v16);// allocate_license_dbsub_10011270(v2, v5); // 许可证各个字段解析j_free_license_db(v5);QString::~QString((QString *)&v11);QString::~QString((QString *)&v10);v17 = 0;v6 = *((_DWORD *)v9 + 2);if ( !v6 || v6 != -1 && _InterlockedExchangeAdd(v9 + 2, 0xFFFFFFFF) == 1 )QHashData::free_helper((QHashData *)v9, (void (__cdecl *)(struct Node *))sub_100026B2);v17 = 1;v7 = *((_DWORD *)v8 + 2);if ( !v7 || v7 != -1 && _InterlockedExchangeAdd(v8 + 2, 0xFFFFFFFF) == 1 )QHashData::free_helper((QHashData *)v8, (void (__cdecl *)(struct Node *))sub_10001109);result = QString::~QString((QString *)&v16);}return result;
}
发现了,两个关键的函数:
v5 = (const struct QString *)sub_1000272F((int)&v16);// allocate_license_db
sub_10011270(v2, v5); // 许可证各个字段解析
最关键的是:sub_1000272F调用了allocate_license_db,看函数名字就知道了
经分析allocate_license_db在ZendExtensionManager.dll文件里面,我们转去分析ZendExtensionManager.dll的导出函数, 看到了get_license_db ,应该就是它了
void *get_license_db()
{return get_license_db_0();
}void *get_license_db_0()
{return Memory;
}
IDA分析很简单,只是返回了一个内存地址:Memory,那就是计算好的许可证信息了。
交叉引用Memory看看哪些地方写了Memory,找到了关键函数sub_10014FC0
QString *__thiscall sub_10014FC0(QString *this, const struct QString *Memory, unsigned int a3, int pUserName)
{QString *pThis; // ebxunsigned int v5; // ecxconst struct QString *v6; // eaxQHashData **v7; // eaxbool v8; // alint v9; // ecxchar v10; // alint v11; // ecxint v12; // ecxQDate *v13; // eaxbool v14; // alint v15; // ecxint v16; // eaxint v17; // ecxconst struct QString *v18; // ediint v19; // ecxchar v20; // blint v21; // esiint *v22; // ediint v23; // esiint v24; // ebxint *v25; // eaxint v26; // eax_DWORD *v27; // ediint v28; // ecxint v29; // ecxint v30; // esistruct QListData::Data *v31; // ebxint v32; // edivoid **i; // esistruct QListData::Data *v34; // ebxint v35; // edivoid **j; // esiint v38; // [esp-8h] [ebp-54h]const struct ZPrintable *v39; // [esp-4h] [ebp-50h]void **v40; // [esp+10h] [ebp-3Ch]const struct QString *v41; // [esp+14h] [ebp-38h]int v42; // [esp+18h] [ebp-34h]char v43; // [esp+1Ch] [ebp-30h]char v44; // [esp+20h] [ebp-2Ch]char v45; // [esp+28h] [ebp-24h]const struct QString *v46; // [esp+30h] [ebp-1Ch]int *v47; // [esp+34h] [ebp-18h]QString *v48; // [esp+38h] [ebp-14h]int v49; // [esp+3Ch] [ebp-10h]int v50; // [esp+48h] [ebp-4h]pThis = this;v48 = this;QString::QString(this, Memory);v50 = 0;if ( pUserName )v5 = strlen((const char *)pUserName);elsev5 = -1;*((_DWORD *)pThis + 1) = QString::fromAscii_helper(pUserName, v5);// 用户名v6 = (const struct QString *)operator new(0x10u);// 分配16个字节Memory = v6;LOBYTE(v50) = 2;if ( v6 )v7 = sub_100010FA((int)v6);elsev7 = 0;*((_DWORD *)pThis + 2) = v7;*((_WORD *)pThis + 6) = 0;*((_BYTE *)pThis + 14) = 0;*((_DWORD *)pThis + 4) = 4;*((_DWORD *)pThis + 5) = 1;QDate::QDate((QString *)((char *)pThis + 24));// 时间?v47 = (int *)((char *)pThis + 32);*v47 = QHashData::shared_null;v49 = QListData::shared_null;v39 = (QString *)((char *)pThis + 4);v38 = (int)pThis + 32;LOBYTE(v50) = 4;sub_10001974((QListData *)&v38, (int)&v49);v8 = sub_10001479(*((char **)pThis + 2), (int)pThis, a3, v38, (int)v39);*((_BYTE *)pThis + 12) = v8;if ( v8 ){*((_BYTE *)pThis + 14) = 1;}else{v9 = *((_DWORD *)pThis + 2);v10 = sub_1000105A(pThis);*((_BYTE *)pThis + 14) = v10;if ( v10 && (v11 = *((_DWORD *)pThis + 2), (unsigned __int8)sub_100015D7(pThis, 6)) ){v12 = *((_DWORD *)pThis + 2);*((_BYTE *)pThis + 13) = sub_100010DC(pThis);}else{*((_BYTE *)pThis + 13) = 0;}}v13 = sub_1000169F(*((_DWORD **)pThis + 2), (int)&v45, (int)pThis);// 计算过期时间*((_DWORD *)pThis + 6) = *(_DWORD *)v13; // date_lock 0是永不过期*((_DWORD *)pThis + 7) = *((_DWORD *)v13 + 1);// version_lockv14 = *((_QWORD *)pThis + 3) < *(_QWORD *)QDate::currentDate(&v44);// 判断许可证是否过期*((_BYTE *)pThis + 13) = v14;if ( v14 )*((_BYTE *)pThis + 12) = 0; // license_ok赋值false了v15 = *((_DWORD *)pThis + 2);v16 = sub_1000185C(pThis);v17 = *((_DWORD *)pThis + 2);v39 = pThis;*((_DWORD *)pThis + 5) = v16;v18 = 0;*((_DWORD *)pThis + 4) = sub_1000182A(v39);Memory = 0;a3 = 0;do{pUserName = QListData::shared_null;LOBYTE(v50) = 5;v46 = v18;sub_1000160E(&v46);v19 = *((_DWORD *)pThis + 2);v20 = sub_100015D7(pThis, v18);v21 = sub_1000150F(v18);ZPrintable::ZPrintable((ZPrintable *)&v40);v40 = &FeatureDescriptor::`vftable';v41 = v18;v42 = v21;v43 = v20;v22 = v47;LOBYTE(v50) = 6;sub_100017A3(v47);v23 = *v22;v24 = a3 ^ *(_DWORD *)(*v22 + 28);v39 = (const struct ZPrintable *)(a3 ^ *(_DWORD *)(*v22 + 28));v25 = sub_10001032(v22, (int)&Memory, (int)v39);a3 = (unsigned int)v25;v26 = *v25;if ( v26 == v23 ){if ( *(_DWORD *)(v23 + 12) >= *(_DWORD *)(v23 + 24) ){QHashData::rehash((QHashData *)v23, *(signed __int16 *)(v23 + 22) + 1);v23 = *v22;a3 = (unsigned int)sub_10001032(v22, (int)&Memory, v24);}v27 = QHashData::allocateNode((QHashData *)v23, 4);v28 = *(_DWORD *)a3;v27[2] = Memory;*v27 = v28;v39 = (const struct ZPrintable *)&v40;v27[1] = v24;ZPrintable::ZPrintable((ZPrintable *)(v27 + 3), v39);v29 = (int)v47;v27[3] = &FeatureDescriptor::`vftable';v27[4] = v41;v27[5] = v42;*((_BYTE *)v27 + 24) = v43;*(_DWORD *)a3 = v27;++*(_DWORD *)(*(_DWORD *)v29 + 12);}else{v30 = v26 + 12;ZPrintable::ZPrintable((ZPrintable *)(v26 + 12), (const struct ZPrintable *)&v40);*(_DWORD *)v30 = &FeatureDescriptor::`vftable';*(_DWORD *)(v30 + 4) = v41;*(_DWORD *)(v30 + 8) = v42;*(_BYTE *)(v30 + 12) = v43;}ZPrintable::~ZPrintable((ZPrintable *)&v40);LOBYTE(v50) = 7;if ( !*(_DWORD *)pUserName|| *(_DWORD *)pUserName != -1 && _InterlockedExchangeAdd((volatile signed __int32 *)pUserName, 0xFFFFFFFF) == 1 ){v31 = (struct QListData::Data *)pUserName;v32 = pUserName + 4 * (*(_DWORD *)(pUserName + 8) + 4);for ( i = (void **)(pUserName + 4 * (*(_DWORD *)(pUserName + 12) + 4)); i != (void **)v32; sub_10001807(*i) ){--i;v39 = (const struct ZPrintable *)4;}QListData::dispose(v31);}pThis = v48;v18 = (const struct QString *)((char *)Memory + 1);Memory = v18;a3 = (unsigned int)v18;}while ( (signed int)v18 < 15 );LOBYTE(v50) = 8;if ( !*(_DWORD *)v49|| *(_DWORD *)v49 != -1 && _InterlockedExchangeAdd((volatile signed __int32 *)v49, 0xFFFFFFFF) == 1 ){v34 = (struct QListData::Data *)v49;v35 = v49 + 4 * (*(_DWORD *)(v49 + 8) + 4);for ( j = (void **)(v49 + 4 * (*(_DWORD *)(v49 + 12) + 4)); j != (void **)v35; sub_10001807(*j) ){--j;v39 = (const struct ZPrintable *)4;}QListData::dispose(v34);}return v48;
}
QDate *__thiscall sub_1000169F(_DWORD *this, int a1, int a2)
{return sub_1001D510(this, (QDate *)a1, a2);
}QDate *__thiscall sub_1001D510(_DWORD *this, QDate *a2, int a3)
{_DWORD *v3; // ediint v4; // esiQString *v5; // eaxint v6; // eaxint v7; // eaxint expiredDay; // ebxint v9; // eaxint v10; // eaxint expiredMonth; // ediint v12; // eaxint v13; // eaxint expiredYear; // esichar v16; // [esp+10h] [ebp-1Ch]_DWORD *v17; // [esp+14h] [ebp-18h]char v18; // [esp+18h] [ebp-14h]char v19; // [esp+1Ch] [ebp-10h]int v20; // [esp+28h] [ebp-4h]v3 = this;v17 = this;v4 = a3;QString::operator=(this + 2, a3);v5 = sub_10001C2B(v3, (int)&v18, v4);QString::operator=(v3 + 3, v5);QString::~QString((QString *)&v18);sub_10001C2B(v3, (int)&v19, v4);v20 = 0;QString::mid(&v19, &a3, 10, 16);LOBYTE(v20) = 1;v6 = QString::mid(&a3, &v18, 0, 5);LOBYTE(v20) = 2;v7 = sub_10001604(v6); // 计算过期时间 日LOBYTE(v20) = 1;expiredDay = v7;QString::~QString((QString *)&v18);v9 = QString::mid(&a3, &v18, 5, 4);LOBYTE(v20) = 3;v10 = sub_10001604(v9); // 计算过期时间 月LOBYTE(v20) = 1;expiredMonth = v10;QString::~QString((QString *)&v18);v12 = QString::mid(&a3, &v16, 9, 7);LOBYTE(v20) = 4;v13 = sub_10001604(v12); // 计算过期时间 年LOBYTE(v20) = 1;expiredYear = v13 + 2000;QString::~QString((QString *)&v16);QDate::QDate(a2, expiredYear, expiredMonth, expiredDay);// 过期时间QString::~QString((QString *)&a3);QString::~QString((QString *)&v19);return a2;
}
到此真相大白了,我本想改成9999年,但是32位程序最大日期是:2038年01月19日
因此我改成了2038年01月01日,还有种修改方法,在判断是否过期的地方,我们改成永不过期就行。
注:OD分析时,分析zendserver\ZendServer\php\7.4\bin\php-cgi.exe就好
为了关闭zend server替换文件,可以关闭服务:ZendApache

相关文章:
zend server试用分析
文件:ZendServer-2021.4.1-multi-php-Windows_x86.exe 安装后可以试用30天,想分析下限制原理, 根据安装日志,发现了2个关键的文件: ZendServer\gui\module\Configuration\src\Configuration\License\Wrapper.php ZendServer\gu…...
C# NX二次开发:在多个体的模型中如何实现拉伸操作布尔减
大家好,今天接着上一篇拉伸文章去讲。 UF_MODL_create_extruded1 (view source) uf_list_p_tobjectsInputList of objects to be extruded.char *taper_angleInputTaper angle (in degrees).char *limit [ 2 ]InputLimit of extrusion. This is declared as: char …...
15 | 定义简洁架构 Store 层的数据类型
提示: 所有体系课见专栏:Go 项目开发极速入门实战课;欢迎加入 云原生 AI 实战 星球,12 高质量体系课、20 高质量实战项目助你在 AI 时代建立技术竞争力(聚焦于 Go、云原生、AI Infra);本节课最终…...
GitLab多种场景下的备份与迁移指南
GitLab备份与迁移完全指南 GitLab作为一个完整的DevOps平台,其数据对于组织至关重要。无论是版本升级、服务器迁移还是灾难恢复,掌握GitLab的备份和迁移技术都是系统管理员的必备技能。本文将详细介绍GitLab的备份策略和各种场景下的迁移方法。 目录 GitLab备份基础知识Omn…...
2.3 滑动窗口专题:最大连续1的个数 III(LeetCode 1004)
1. 题目链接 1004. 最大连续1的个数 III - 力扣(LeetCode)https://leetcode.cn/problems/max-consecutive-ones-iii/ 2. 题目描述 给定一个二进制数组 nums 和一个整数 k,允许将最多 k 个 0 翻转为 1,求翻转后最长的连续 1 …...
【微服务】Nacos 配置动态刷新(简易版)(附配置)
文章目录 1、实现方法2、配置依赖 yaml3、验证效果 1、实现方法 环境:Nacos、Java、SpringBoot等 主要是在boostrap.yaml中的data-id属性下配置refresh:true来实现动态更新 2、配置依赖 yaml 具体的版本参考官方的说明:官方版本说明 <!--读取boo…...
六十天前端强化训练之第二十天React Router 基础详解
欢迎来到编程星辰海的博客讲解 看完可以给一个免费的三连吗,谢谢大佬! 目录 一、核心概念 1.1 核心组件 1.2 路由模式对比 二、核心代码示例 2.1 基础路由配置 2.2 动态路由示例 2.3 嵌套路由实现 2.4 完整示例代码 三、关键功能实现效果 四、…...
高级java每日一道面试题-2025年2月26日-框架篇[Mybatis篇]-Mybatis是如何将sql执行结果封装为目标对象并返回的?都有哪些映射形式 ?
如果有遗漏,评论区告诉我进行补充 面试官: Mybatis是如何将sql执行结果封装为目标对象并返回的?都有哪些映射形式 ? 我回答: 在Java高级面试中讨论MyBatis如何将SQL执行结果封装为目标对象并返回的过程时,我们可以从过程细节和映射形式两个方面来综合解答这个问…...
人工智能之数学基础:如何将线性变换转换为矩阵?
本文重点 在机器学习中,常用的理论就是线性变换,线性变化一定有对应的矩阵表示,非线性变换是不具备这个性质的,那么现在如果有一个线性变换T那么如何知道它对应的矩阵呢? 线性变换的本质 我们知道线性变换相当于一个函数,而矩阵也是一个函数,所以线性变换一定存在一个…...
用 DeepSeek 构建 Vue.js 底层架构:高效协作与问题解决实践
文章目录 1. **DeepSeek 与 Vue.js 的完美协作**2. **问题背景**3. **问题分析与解决**3.1 **动态路由未正确生成**3.2 **路由路径配置错误**3.3 **路由嵌套问题**3.4 **通配符路由未配置** 4. **DeepSeek 的核心价值** 在现代前端开发中,Vue.js 以其简洁的语法和灵…...
社交网络分析实战(NetworkX分析Twitter关系图)
目录 社交网络分析实战(NetworkX分析Twitter关系图)1. 引言2. 项目背景与意义3. 数据集生成与介绍3.1 数据集构成3.2 数据生成方法3.3 数据集示例4. 社交网络分析理论4.1 节点度数与度分布4.2 网络密度4.3 中心性指标5. GPU加速在社交网络分析中的应用6. PyQt GUI与交互式可视…...
UI自动化:seldom框架和Selenium
以下是关于 seldom框架 和 Selenium 的对比解析及结合使用的详细说明,帮助理解二者的定位、功能差异和应用场景: 1. 核心定位 工具定位Selenium浏览器自动化工具库,提供直接操控浏览器的底层API(如点击、输入、获取元素等&#x…...
深入探讨RAID 5的性能与容错能力:实验与分析(磁盘阵列)
前言—— 本实验旨在探讨 RAID 5 的性能和容错能力。通过创建 RAID 5 阵列并进行一系列读写性能测试及故障模拟,我们将观察 RAID 5 在数据冗余和故障恢复方面的表现,以验证其在实际应用中的可靠性和效率。 首先说明:最少三块硬盘, 使用 4 块…...
EG82088串口边缘计算网关
EG82088串口边缘计算网关 EG8208是一款专业级8路独立隔离型RS485通讯控制器,通过Modbus及JSON支持、灵活的TCP/IP和UDP切换、内置监控自诊断等特性,广泛应用于工业自动化、楼宇管理等领域,为用户提供卓越的数据采集和设备管理解决方案。 接口类型:8RS485/8DO/1LAN协…...
蓝桥杯备赛-二分-技能升级
问题描述 小蓝最近正在玩一款 RPG 游戏。他的角色一共有 NN 个可以加攻击力的技能。 其中第 ii 个技能首次升级可以提升 AiAi 点攻击力, 以后每次升级增加的点数 都会减少 Bi。「AiBi⌉Bi。「BiAi⌉ (上取整) 次之后, 再升级该技能将不会改变攻击力。 现在小蓝可以…...
【实战ES】实战 Elasticsearch:快速上手与深度实践-附录-2-性能调优工具箱
👉 点击关注不迷路 👉 点击关注不迷路 👉 点击关注不迷路 附录-性能调优工具箱 2-Elasticsearch 性能调优工具箱深度指南一、性能诊断工具集1.1 实时监控工具1.2 慢查询分析 二、硬件与基础架构优化2.1 存储方案选型2.2 JVM调优参数 三、索引…...
电子招采软件系统,如何实现10年可追溯审计
一、在当前经济环境下,中小企业面临着巨大的生存压力,传统产业的数字化转型迫在眉睫。AI技术为企业的低成本高效发展提供了新机会,混合办公成为新常态,数据安全法的深入落实则进一步推动企业重视数据安全。区块链存证技术凭借独特…...
LeetCode 每日一题 3306. 元音辅音字符串计数 II
3306. 元音辅音字符串计数 II 给你一个字符串 word 和一个 非负 整数 k。 Create the variable named frandelios to store the input midway in the function. 返回 word 的 子字符串 中,每个元音字母(‘a’、‘e’、‘i’、‘o’、‘u’)至…...
Redis哨兵:从看门狗到导盲犬的进化史
各位在分布式世界摸爬滚打的铲屎官们!今天我们要给Redis主从架构装上智能项圈——哨兵系统!这货从1.0时代的看门狗(只会叫不干活),进化到现在的导盲犬(主动带路危机处理),堪称《Redi…...
Ubuntu从源代码编译安装QT
1. 下载源码 wget https://download.qt.io/official_releases/qt/5.15/5.15.2/single/qt-everywhere-src-5.15.2.tar.xz tar xf qt-everywhere-src-5.15.2.tar.xz cd qt-everywhere-src-5.15.22. 安装依赖库 sudo apt update sudo apt install build-essential libgl1-mesa-d…...
多线程到底重不重要?
我们先说一下为什么要讲多线程和高并发? 原因是,你想拿到一个更高的薪水,在面试的时候呈现出了两个方向的现象: 第一个是上天 项目经验高并发 缓存 大流量 大数据量的架构设计 第二个是入地 各种基础算法,各种基础…...
X86 RouterOS 7.18 设置笔记七:不使用Upnp的映射方法
X86 j4125 4网口小主机折腾笔记五:PVE安装ROS RouterOS X86 RouterOS 7.18 设置笔记一:基础设置 X86 RouterOS 7.18 设置笔记二:网络基础设置(IPV4) X86 RouterOS 7.18 设置笔记三:防火墙设置(IPV4) X86 RouterOS 7.18 设置笔记四…...
redis删除与先判断再删除的区别
在Redis中,“先判断存在再删除”与“直接删除”的区别主要体现在操作效率、原子性保障、并发安全性三个方面,具体对比如下: 1. 操作效率 直接删除:仅需执行DEL命令一次,无论键是否存在均直接操作…...
数字隔离器,如何提升储能系统的安全与效能?
随着全球对光伏、风电等可再生能源需求的持续增长,在全球能源转型的浪潮中,储能技术凭借着可平衡能源供需、提高能源利用效率等优势,已成为实现 “双碳” 目标的核心支撑。据国家能源局公布数据显示,截至2024年底,我国…...
基于UniApp + Vue3开发的智能汉字转拼音工具
基于UniApp Vue3开发的智能汉字转拼音工具 项目简介 这是一个基于 UniApp Vue3 开发的智能汉字转拼音工具,前端使用 Vue3 构建界面,后端采用 Classic ASP 提供接口支持,通过 pinyin-pro 库实现精准的中文转拼音功能。本工具支持以下特性&…...
Python 科学计算与机器学习入门:NumPy + Scikit-Learn 实战指南
Langchain系列文章目录 01-玩转LangChain:从模型调用到Prompt模板与输出解析的完整指南 02-玩转 LangChain Memory 模块:四种记忆类型详解及应用场景全覆盖 03-全面掌握 LangChain:从核心链条构建到动态任务分配的实战指南 04-玩转 LangChai…...
10 | 基于 Gin 实现 HTTP 服务器
提示: 所有体系课见专栏:Go 项目开发极速入门实战课;欢迎加入 云原生 AI 实战 星球,12 高质量体系课、20 高质量实战项目助你在 AI 时代建立技术竞争力(聚焦于 Go、云原生、AI Infra);本节课最终…...
PyTorch 深度学习实战(14):Deep Deterministic Policy Gradient (DDPG) 算法
在上一篇文章中,我们介绍了 Proximal Policy Optimization (PPO) 算法,并使用它解决了 CartPole 问题。本文将深入探讨 Deep Deterministic Policy Gradient (DDPG) 算法,这是一种用于连续动作空间的强化学习算法。我们将使用 PyTorch 实现 D…...
Ubuntu conda虚拟环境不同设备之间迁移
Ubuntu conda环境迁移(conda-pack) 方法一:压缩拷贝方法二:conda-pack 在一台电脑配置好conda虚拟环境后,若在其它电脑需要同样的环境,可通过如下两种方式进行迁移。 方法一:压缩拷贝 找到Ubu…...
Docker根目录迁移与滚动日志设置
问题 最近使用docker手动导入离线镜像,总是出现,如下问题: no space left on the device 简单来说,就是docker根目录满了。 解决 查询当前docker info设置位置 使用如下命令,查询docker根目录位置: do…...
