1.15 自实现GetProcAddress
在正常情况下,要想使用GetProcAddress函数,需要首先调用LoadLibraryA函数获取到kernel32.dll动态链接库的内存地址,接着在调用GetProcAddress函数时传入模块基址以及模块中函数名即可动态获取到特定函数的内存地址,但在有时这个函数会被保护起来,导致我们无法直接调用该函数获取到特定函数的内存地址,此时就需要自己编写实现LoadLibrary以及GetProcAddress函数,该功能的实现需要依赖于PEB线程环境块,通过线程环境块可遍历出kernel32.dll模块的入口地址,接着就可以在该模块中寻找GetProcAddress函数入口地址,当找到该入口地址后即可直接调用实现动态定位功能。
首先通过PEB/TEB找到自身进程的所有载入模块数据,获取TEB也就是线程环境块。在编程的时候TEB始终保存在寄存器 FS 中。
0:000> !teb
TEB at 00680000ExceptionList: 008ff904StackBase: 00900000StackLimit: 008fc000RpcHandle: 00000000Tls Storage: 0068002cPEB Address: 0067d0000:000> dt _teb 00680000
ntdll!_TEB+0x000 NtTib : _NT_TIB+0x01c EnvironmentPointer : (null) +0x020 ClientId : _CLIENT_ID+0x028 ActiveRpcHandle : (null) +0x02c ThreadLocalStoragePointer : 0x0068002c Void+0x030 ProcessEnvironmentBlock : 0x0067d000 _PEB // 偏移为30,PEB
从该命令的输出可以看出,PEB 结构体的地址位于 TEB 结构体偏移0x30 的位置,该位置保存的地址是 0x0067d000。也就是说,PEB 的地址是 0x0067d000,通过该地址来解析 PEB并获得 LDR结构。
0:000> dt nt!_peb 0x0067d000
ntdll!_PEB+0x000 InheritedAddressSpace : 0 ''+0x001 ReadImageFileExecOptions : 0 ''+0x002 BeingDebugged : 0x1 ''+0x003 BitField : 0x4 ''+0x003 ImageUsesLargePages : 0y0+0x003 IsProtectedProcess : 0y0+0x003 IsImageDynamicallyRelocated : 0y1+0x003 SkipPatchingUser32Forwarders : 0y0+0x003 IsPackagedProcess : 0y0+0x003 IsAppContainer : 0y0+0x003 IsProtectedProcessLight : 0y0+0x003 IsLongPathAwareProcess : 0y0+0x004 Mutant : 0xffffffff Void+0x008 ImageBaseAddress : 0x00f30000 Void+0x00c Ldr : 0x774c0c40 _PEB_LDR_DATA // LDR
从如上输出结果可以看出,LDR 在 PEB 结构体偏移的 0x0C 处,该地址保存的地址是 0x774c0c40 通过该地址来解析 LDR 结构体。WinDBG 输出如下内容:
0:000> dt _peb_ldr_data 0x774c0c40
ntdll!_PEB_LDR_DATA+0x000 Length : 0x30+0x004 Initialized : 0x1 ''+0x008 SsHandle : (null) +0x00c InLoadOrderModuleList : _LIST_ENTRY [ 0x9e3208 - 0x9e5678 ]+0x014 InMemoryOrderModuleList : _LIST_ENTRY [ 0x9e3210 - 0x9e5680 ]+0x01c InInitializationOrderModuleList : _LIST_ENTRY [ 0x9e3110 - 0x9e35f8 ]+0x024 EntryInProgress : (null) +0x028 ShutdownInProgress : 0 ''+0x02c ShutdownThreadId : (null) 0:000> dt _LIST_ENTRY
ntdll!_LIST_ENTRY+0x000 Flink : Ptr32 _LIST_ENTRY+0x004 Blink : Ptr32 _LIST_ENTRY
现在来手动遍历第一条链表,输入命令0x9e3208:在链表偏移 0x18 的位置是模块的映射地址,即 ImageBase;在链表
偏移 0x28 的位置是模块的路径及名称的地址;在链表偏移 0x30 的位置是模块名称的地址。
0:000> dd 0x9e3208
009e3208 009e3100 774c0c4c 009e3108 774c0c54
009e3218 00000000 00000000 00f30000 00f315bb
009e3228 00007000 00180016 009e1fd4 00120010
009e3238 009e1fda 000022cc 0000ffff 774c0b080:000> du 009e1fd4
009e1fd4 "C:\main.exe"
0:000> du 009e1fda
009e1fda "main.exe"
读者可自行验证,如下所示的确是模块的名称。既然是链表,就来下一条链表的信息,009e3100保存着下一个链表结构。依次遍历就是了。
0:000> dd 009e3100
009e3100 009e35e8 009e3208 009e35f0 009e3210
009e3110 009e39b8 774c0c5c 773a0000 00000000
009e3120 0019c000 003c003a 009e2fe0 001400120:000> du 009e2fe0
009e2fe0 "C:\Windows\SYSTEM32\ntdll.dll"
上述地址009e3100介绍的结构,是微软保留结构,只能从网上找到一个结构定义,然后自行看着解析就好了。
typedef struct _LDR_DATA_TABLE_ENTRY
{PVOID Reserved1[2];LIST_ENTRY InMemoryOrderLinks;PVOID Reserved2[2];PVOID DllBase;PVOID EntryPoint;PVOID Reserved3;UNICODE_STRING FullDllName;BYTE Reserved4[8];PVOID Reserved5[3];union {ULONG CheckSum;PVOID Reserved6;};ULONG TimeDateStamp;
} LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;
根据如上流程,想要得到kernel32.dll模块的入口地址,我们可以进行这几步,首先得到TEB地址,并在该地址中寻找PEB线程环境块,并在该环境块内得到LDR结构,在该结构中获取第二条链表地址,输出该链表中的0x10以及0x20即可得到当前模块的基地址,以及完整的模块路径信息,该功能的实现分为32位于64位,如下代码则是实现代码。
#include <iostream>
#include <Windows.h>// 将不同的节压缩为单一的节
#pragma comment(linker, "/merge:.data=.text")
#pragma comment(linker, "/merge:.rdata=.text")
#pragma comment(linker, "/section:.text,RWE")// 得到32位模式下kernel32.dll地址
DWORD GetModuleKernel32()
{DWORD *PEB = NULL, *Ldr = NULL, *Flink = NULL, *p = NULL;DWORD *BaseAddress = NULL, *FullDllName = NULL;__asm{mov eax, fs:[0x30] // FS保存着TEBmov PEB, eax // +30定位到PEB}// 得到LDRLdr = *((DWORD **)((unsigned char *)PEB + 0x0c));// 在LDR基础上找到第二条链表Flink = *((DWORD **)((unsigned char *)Ldr + 0x14));p = Flink;p = *((DWORD **)p);// 计数器int count = 0;while (Flink != p){BaseAddress = *((DWORD **)((unsigned char *)p + 0x10));FullDllName = *((DWORD **)((unsigned char *)p + 0x20));if (BaseAddress == 0)break;// printf("镜像基址 = %08x \r\n 模块路径 = %S \r\n", BaseAddress, (unsigned char *)FullDllName);// 第二个模块是kernel32.dllif (count == 1){// printf("address =%x \n", BaseAddress);return reinterpret_cast<DWORD>(BaseAddress);}p = *((DWORD **)p);count = count + 1;}// 未找到Kernel32模块return 0;
}// 获取64位模式下的kernel32.dll基址
ULONGLONG GetModuleKernel64()
{ULONGLONG dwKernel32Addr = 0;// 获取TEB的地址_TEB* pTeb = NtCurrentTeb();// 获取PEB的地址PULONGLONG pPeb = (PULONGLONG)*(PULONGLONG)((ULONGLONG)pTeb + 0x60);// 获取PEB_LDR_DATA结构的地址PULONGLONG pLdr = (PULONGLONG)*(PULONGLONG)((ULONGLONG)pPeb + 0x18);// 模块初始化链表的头指针InInitializationOrderModuleListPULONGLONG pInLoadOrderModuleList = (PULONGLONG)((ULONGLONG)pLdr + 0x10);// 获取链表中第一个模块信息,exe模块PULONGLONG pModuleExe = (PULONGLONG)*pInLoadOrderModuleList;//printf("EXE Base = > %X \n", pModuleExe[6]);// 获取链表中第二个模块信息,ntdll模块PULONGLONG pModuleNtdll = (PULONGLONG)*pModuleExe;//printf("Ntdll Base = > %X \n", pModuleNtdll[6]);// 获取链表中第三个模块信息,Kernel32模块PULONGLONG pModuleKernel32 = (PULONGLONG)*pModuleNtdll;//printf("Kernel32 Base = > %X \n", pModuleKernel32[6]);// 获取kernel32基址dwKernel32Addr = pModuleKernel32[6];return dwKernel32Addr;
}int main(int argc, char *argv[])
{// 输出32位kernel32DWORD kernel32BaseAddress = GetModuleKernel32();std::cout << "kernel32 = " << std::hex << kernel32BaseAddress << std::endl;// 输出64位kernel32ULONGLONG kernel64BaseAddress = GetModuleKernel64();std::cout << "kernel64 = " << std::hex << kernel32BaseAddress << std::endl;system("pause");return 0;
}
如上代码中分别实现了32位于64位两种获取内存模块基址GetModuleKernel32用于获取32位模式,GetModuleKernel64则用于获取64位内存基址,读者可自行调用两种模式,输出如下图所示;

我们通过调用GetModuleKernel32()函数读入kernel32.dll模块入口地址后,则下一步就可以通过循环,遍历该模块的导出表并寻找到GetProcAddress导出函数地址,找到该导出函数内存地址后,则可以通过kernel32模块基址加上dwFunAddrOffset相对偏移,获取到该函数的内存地址,此时通过函数指针就可以将该函数地址读入到内存指针内。
// 封装基地址获取功能
ULONGLONG MyGetProcAddress()
{// 获取32位基址ULONGLONG dwBase = GetModuleKernel32();// 获取64位基址// ULONGLONG dwBase = GetModuleKernel64();// 获取DOS头PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)dwBase;// 获取32位NT头PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(dwBase + pDos->e_lfanew);// 获取64位NT头// PIMAGE_NT_HEADERS64 pNt = (PIMAGE_NT_HEADERS64)(dwBase + pDos->e_lfanew);// 获取数据目录表PIMAGE_DATA_DIRECTORY pExportDir = pNt->OptionalHeader.DataDirectory;pExportDir = &(pExportDir[IMAGE_DIRECTORY_ENTRY_EXPORT]);DWORD dwOffset = pExportDir->VirtualAddress;// 获取导出表信息结构PIMAGE_EXPORT_DIRECTORY pExport = (PIMAGE_EXPORT_DIRECTORY)(dwBase + dwOffset);DWORD dwFunCount = pExport->NumberOfFunctions;DWORD dwFunNameCount = pExport->NumberOfNames;DWORD dwModOffset = pExport->Name;// 获取导出地址表PDWORD pEAT = (PDWORD)(dwBase + pExport->AddressOfFunctions);// 获取导出名称表PDWORD pENT = (PDWORD)(dwBase + pExport->AddressOfNames);// 获取导出序号表PWORD pEIT = (PWORD)(dwBase + pExport->AddressOfNameOrdinals);for (DWORD dwOrdinal = 0; dwOrdinal < dwFunCount; dwOrdinal++){if (!pEAT[dwOrdinal]){continue;}// 获取序号DWORD dwID = pExport->Base + dwOrdinal;// 获取导出函数地址ULONGLONG dwFunAddrOffset = pEAT[dwOrdinal];for (DWORD dwIndex = 0; dwIndex < dwFunNameCount; dwIndex++){// 在序号表中查找函数的序号if (pEIT[dwIndex] == dwOrdinal){// 根据序号索引到函数名称表中的名字ULONGLONG dwNameOffset = pENT[dwIndex];char* pFunName = (char*)((ULONGLONG)dwBase + dwNameOffset);if (!strcmp(pFunName, "GetProcAddress")){// 根据函数名称返回函数地址return dwBase + dwFunAddrOffset;}}}}return 0;
}// 定义名称指针
typedef ULONGLONG(WINAPI *fnGetProcAddress)(_In_ HMODULE hModule, _In_ LPCSTR lpProcName);
typedef HMODULE(WINAPI *fnLoadLibraryA)(_In_ LPCSTR lpLibFileName);int main(int argc, char *argv[])
{DWORD kernel32BaseAddress = GetModuleKernel32();if (kernel32BaseAddress == 0){return 0;}// 获取kernel32基址/获取GetProcAddress的基址fnGetProcAddress pfnGetProcAddress = (fnGetProcAddress)MyGetProcAddress();std::cout << pfnGetProcAddress << std::endl;// 获取Kernel32核心API地址fnLoadLibraryA pfnLoadLibraryA = (fnLoadLibraryA)pfnGetProcAddress((HMODULE)kernel32BaseAddress, "LoadLibraryA");printf("自定义读入LoadLibrary = %x \n", pfnLoadLibraryA);system("pause");return 0;
}
输出效果如下图所示,我们即可读入fnLoadLibraryA函数的内存地址;

上述代码的使用也很简单,当我们能够得到GetProcAddress的内存地址后,就可以使用该内存地址动态定位到任意一个函数地址,我们通过得到LoadLibrary函数地址,与GetModuleHandleA函数地址,通过两个函数就可以定位到Windows系统内任意一个函数,我们以调用MessageBox弹窗为例,动态输出一个弹窗,该调用方式如下所示。
// 定义名称指针
typedef ULONGLONG(WINAPI *fnGetProcAddress)(_In_ HMODULE hModule, _In_ LPCSTR lpProcName);
typedef HMODULE(WINAPI *fnLoadLibraryA)(_In_ LPCSTR lpLibFileName);
typedef int(WINAPI *fnMessageBox)(HWND hWnd, LPSTR lpText, LPSTR lpCaption, UINT uType);
typedef HMODULE(WINAPI *fnGetModuleHandleA)(_In_opt_ LPCSTR lpModuleName);
typedef BOOL(WINAPI *fnVirtualProtect)(_In_ LPVOID lpAddress, _In_ SIZE_T dwSize, _In_ DWORD flNewProtect, _Out_ PDWORD lpflOldProtect);
typedef void(WINAPI *fnExitProcess)(_In_ UINT uExitCode);int main(int argc, char * argv[])
{// 获取kernel32基址 / 获取GetProcAddress的基址fnGetProcAddress pfnGetProcAddress = (fnGetProcAddress)MyGetProcAddress();ULONGLONG dwBase = GetModuleKernel32();printf("fnGetProcAddress = %x \n", pfnGetProcAddress);printf("GetKernel32Addr = %x \n", dwBase);// 获取Kernel32核心API地址fnLoadLibraryA pfnLoadLibraryA = (fnLoadLibraryA)pfnGetProcAddress((HMODULE)dwBase, "LoadLibraryA");printf("pfnLoadLibraryA = %x \n", pfnLoadLibraryA);fnGetModuleHandleA pfnGetModuleHandleA = (fnGetModuleHandleA)pfnGetProcAddress((HMODULE)dwBase, "GetModuleHandleA");printf("pfnGetModuleHandleA = %x \n", pfnGetModuleHandleA);fnVirtualProtect pfnVirtualProtect = (fnVirtualProtect)pfnGetProcAddress((HMODULE)dwBase, "VirtualProtect");printf("pfnVirtualProtect = %x \n", pfnVirtualProtect);// 有了核心API之后,即可获取到User32.dll的基地址pfnLoadLibraryA("User32.dll");HMODULE hUser32 = (HMODULE)pfnGetModuleHandleA("User32.dll");fnMessageBox pfnMessageBoxA = (fnMessageBox)pfnGetProcAddress(hUser32, "MessageBoxA");printf("User32 = > %x \t MessageBox = > %x \n", hUser32, pfnMessageBoxA);HMODULE hKernel32 = (HMODULE)pfnGetModuleHandleA("kernel32.dll");fnExitProcess pfnExitProcess = (fnExitProcess)pfnGetProcAddress(hKernel32, "ExitProcess");printf("Kernel32 = > %x \t ExitProcess = > %x \n", hKernel32, pfnExitProcess);// 弹出信息框int nRet = pfnMessageBoxA(NULL, "hello lyshark", "MsgBox", MB_YESNO);if (nRet == IDYES){printf("你点击了YES \n");}system("pause");pfnExitProcess(0);return 0;
}
运行上述代码,通过动态调用的方式获取到MessageBox函数内存地址,并将该内存放入到pfnMessageBoxA指针内,最后直接调用该指针即可输出如下图所示的效果图;

相关文章:
1.15 自实现GetProcAddress
在正常情况下,要想使用GetProcAddress函数,需要首先调用LoadLibraryA函数获取到kernel32.dll动态链接库的内存地址,接着在调用GetProcAddress函数时传入模块基址以及模块中函数名即可动态获取到特定函数的内存地址,但在有时这个函…...
总结ADX指标交易的好处
股神巴菲特从一个穷小子变成世界富豪,而闻名世界。anzo capital昂首资本以为这辈子再也不会和巴菲特产生任何交集,直到我看了巴菲特的发家史,才发现原来我们都使用过ADX指标盈利过,下面anzo capital昂首资本就总结一下使用ADX指…...
ConsoleApplication815项目(直接加载+VEH Hook Load)
上线图 ConsoleApplication815.cpp #include <iostream> #include<Windows.h> #include "detours.h" #include "detver.h" #pragma comment(lib,"detours.lib")#pragma warning(disable:4996)LPVOID Beacon_address; SIZE_T Beacon…...
事务(SQL)
事务概述 事务是一组操作的集合,他是一个不可分割的工作单位,事务会把所有的操作作为一个整体一起向西永提交或撤销操作请求。这组操作,要么全部执行成功,要么全部执行失败。 事务操作 查看/设置事务提交方式 -- 查看/设置事务…...
原型,原型链,继承(圣杯模式)
经典模式和圣杯模式区别 经典模式和圣杯模式都是用于解决构造函数继承和原型继承的问题,但它们在实现继承的方式上有所不同。 经典模式是通过将子类的原型对象设置为父类的实例来实现继承,然后将子类的构造函数设置为子类本身。这样子类既可以继承父类…...
远程方法调用中间件Dubbo在spring项目中的使用
Dubbo是一个分布式服务框架,它可以帮助我们快速开发和提供高性能、高可靠性的分布式服务,同时提供服务治理、容错、负载均衡等功能。 使用Dubbo可以分为以下步骤: 引入Dubbo依赖:在项目的pom.xml文件中引入Dubbo的依赖。编写服务…...
MFC -- Date Time Picker 控件使用
当前环境:VS2015 Windows 10 //(一)使用普通函数, 获取当前时间CString strCurrentTime; COleDateTime m_time COleDateTime::GetCurrentTime(); strCurrentTime m_time.Format(_T("%Y-%m-%d %H:%M:%S")); SetDlgIt…...
zookeeper 集群
zookeeper 集群 1、zookeeper 集群说明 initLimit 是Zookeeper用它来限定集群中的Zookeeper服务器连接到Leader的时限 syncLimit 限制了follower服务器与leader服务器之间请求和应答之间的时限 服务器名称与地址:集群信息(服务器编号,服务器…...
stable diffusion实践操作-随机种子seed
系列文章目录 stable diffusion实践操作 文章目录 系列文章目录前言一、seed是什么?二、使用步骤1.多批次随机生成多张图片2.提取图片seed3. 根据seed 再次培养4 seed使用4.1 复原别人图4.1 轻微修改4.2 固定某个人物-修改背景 三、差异随机种子1. webUI位置2. 什么…...
大数据可视化大屏实战项目(4)物流数据云看台(包括动态登陆页面)—HTML+CSS+JS【源码在文末】(可用于比赛项目或者作业参考中)
大数据可视化大屏实战项目(4)物流数据云看台(包括动态登陆页面)—HTMLCSSJS【源码在文末】(可用于比赛项目或者作业参考中🐕🐕🐕) 一,项目概览 ☞☞☞☞☞☞…...
在ubuntu下远程链接仓库gitte/github
后期适当加点图片,提高可读性。 本教程是最基础的连接教程,设计git的操作也仅仅局限于push/pull,如果想全面了解,可以参考廖雪峰git教程 在Ubuntu上初始化本地Git仓库并链接到远程Gitee仓库(github同理),需要按照以下步…...
一些自己整理的工具实用参数
工具实用参数 sqlmap -u: 指定需要测试的目标URL(格式:http://www.example.com/test.php?id1) --cookie: 设置需要发送的 HTTP Cookie,例如:--cookie"sid123456;PHPSESSID654321" --threads:…...
C# Timer定时器
C# Timer定时器 Timer定时器定时器主要用到的就是Timer的Tick事件,另外还要设置时间间隔: 下面这个实力演示了每隔一秒,picturebox中的图片来回切换,每隔一秒,文本框中显示当前时间。 using System; using System.Co…...
oracle怎么删除表索引
Oracle是目前常用的企业级关系型数据库管理系统,用于存储和管理大量数据。在Oracle中,表索引是用于提高查询效率的重要组成部分,但也有时候需要删除表索引。本文将介绍如何在Oracle中删除表索引。 一、查看表索引 在删除表索引之前ÿ…...
【Tkinter系列13/15】标准化外观和选项数据库
27. 标准化外观和选项数据库 可以轻松地将颜色、字体和其他选项应用于 小部件,当您创建它们时。然而 如果您希望很多小部件具有相同的 背景颜色或字体,指定每个都很乏味 每次选项,以及 让用户覆盖您的选择是很好的 他们最喜欢的配色方案、字…...
springboot 集成dubbo
上一篇我们一起认识了Dubbo与RPC,今天我们就来一起学习如何使用Dubbo,并将Dubbo集成到Spring Boot的项目中。我们来看下今天要使用到的软件及版本: 软件 版本 说明 Java 11 Spring Boot 2.7.13 Spring Boot 3.0版本开始,最…...
基于YOLOV8模型和CCPD数据集的车牌目标检测系统(PyTorch+Pyside6+YOLOv8模型)
摘要:基于YOLOV8模型和CCPD数据集的车牌目标检测系统可用于日常生活中检测与定位车牌目标,利用深度学习算法可实现图片、视频、摄像头等方式的目标检测,另外本系统还支持图片、视频等格式的结果可视化与结果导出。本系统采用YOLOv8目标检测算…...
net user安全隐患
net user test 123456 /add #添加一个test账户,密码是123456net user test /delete #删除test账户windows administrator权限下删除其他账户,难道不需要知道该账户的密码吗? 以前没太注意,但是这算不算是一个漏洞呢? 另外&#…...
深入剖析Kubernetes之资源模型和GPU 管理
文章目录 资源模型QoS 模型GPU 管理 资源模型 在 Kubernetes 里,Pod 是最小的原子调度单位。这也就意味着,所有跟调度和资源管理相关的属性都应该是属于 Pod 对象的字段。而这其中最重要的部分,就是 Pod 的 CPU 和内存配置,如下所…...
Springboot整合HBase
Springboot整合HBase数据库 1、添加依赖 <!-- Spring Boot HBase 依赖 --> <dependency><groupId>com.spring4all</groupId><artifactId>spring-boot-starter-hbase</artifactId> </dependency> <dependency><groupId>…...
【WiFi帧结构】
文章目录 帧结构MAC头部管理帧 帧结构 Wi-Fi的帧分为三部分组成:MAC头部frame bodyFCS,其中MAC是固定格式的,frame body是可变长度。 MAC头部有frame control,duration,address1,address2,addre…...
理解 MCP 工作流:使用 Ollama 和 LangChain 构建本地 MCP 客户端
🌟 什么是 MCP? 模型控制协议 (MCP) 是一种创新的协议,旨在无缝连接 AI 模型与应用程序。 MCP 是一个开源协议,它标准化了我们的 LLM 应用程序连接所需工具和数据源并与之协作的方式。 可以把它想象成你的 AI 模型 和想要使用它…...
测试markdown--肇兴
day1: 1、去程:7:04 --11:32高铁 高铁右转上售票大厅2楼,穿过候车厅下一楼,上大巴车 ¥10/人 **2、到达:**12点多到达寨子,买门票,美团/抖音:¥78人 3、中饭&a…...
Qt Http Server模块功能及架构
Qt Http Server 是 Qt 6.0 中引入的一个新模块,它提供了一个轻量级的 HTTP 服务器实现,主要用于构建基于 HTTP 的应用程序和服务。 功能介绍: 主要功能 HTTP服务器功能: 支持 HTTP/1.1 协议 简单的请求/响应处理模型 支持 GET…...
【单片机期末】单片机系统设计
主要内容:系统状态机,系统时基,系统需求分析,系统构建,系统状态流图 一、题目要求 二、绘制系统状态流图 题目:根据上述描述绘制系统状态流图,注明状态转移条件及方向。 三、利用定时器产生时…...
C# 类和继承(抽象类)
抽象类 抽象类是指设计为被继承的类。抽象类只能被用作其他类的基类。 不能创建抽象类的实例。抽象类使用abstract修饰符声明。 抽象类可以包含抽象成员或普通的非抽象成员。抽象类的成员可以是抽象成员和普通带 实现的成员的任意组合。抽象类自己可以派生自另一个抽象类。例…...
【配置 YOLOX 用于按目录分类的图片数据集】
现在的图标点选越来越多,如何一步解决,采用 YOLOX 目标检测模式则可以轻松解决 要在 YOLOX 中使用按目录分类的图片数据集(每个目录代表一个类别,目录下是该类别的所有图片),你需要进行以下配置步骤&#x…...
ElasticSearch搜索引擎之倒排索引及其底层算法
文章目录 一、搜索引擎1、什么是搜索引擎?2、搜索引擎的分类3、常用的搜索引擎4、搜索引擎的特点二、倒排索引1、简介2、为什么倒排索引不用B+树1.创建时间长,文件大。2.其次,树深,IO次数可怕。3.索引可能会失效。4.精准度差。三. 倒排索引四、算法1、Term Index的算法2、 …...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个生活电费的缴纳和查询小程序
一、项目初始化与配置 1. 创建项目 ohpm init harmony/utility-payment-app 2. 配置权限 // module.json5 {"requestPermissions": [{"name": "ohos.permission.INTERNET"},{"name": "ohos.permission.GET_NETWORK_INFO"…...
今日科技热点速览
🔥 今日科技热点速览 🎮 任天堂Switch 2 正式发售 任天堂新一代游戏主机 Switch 2 今日正式上线发售,主打更强图形性能与沉浸式体验,支持多模态交互,受到全球玩家热捧 。 🤖 人工智能持续突破 DeepSeek-R1&…...
