目录
前言
一、实现功能
二、附加功能
三、开发环境
四、数据库简单字段设计
五、代码架构
六、软件界面
七、功能架构
八、部分功能截图
九、相关实现细节概要
9.1 获取文件信息
9.2 清理电脑垃圾信息
9.2.1 回收站
9.2.2 清理指定数据下的文件
9.3 数据库与网络运用
9.3.1 数据库
9.3.2 网络应用
9.4 注册表操作
9.4.1 软件卸载
9.4.1 管理启动项
9.5 遍历服务,启动,关闭服务
9.6.1 获取服务控制管理器的句柄
9.6.2 枚举所有服务
9.6.3 遍历服务
前言
接下来我们来实现一个MFC版本QQ安全卫士的仿造品,我们需要掌握的前置知识点如下:
(1)熟悉windows原理,学会windowsAPI解析PE文件
(2)熟悉windows的权限管理和hook注入的相关知识
(3)理解MFC界面编程和python网络编程
(4)python结合mysql开发
还不会的同学请关注本专栏,尽快学习前面的章节知识点,下面开始介绍本项目。
一、实现功能
1. 任务管理器功能:能够全面遍历进程、线程以及模块,同时对桌面窗口进行有效管理,方便用户清晰掌握系统运行状态。
2. 系统资源监控与优化:精准监测CPU和内存的使用率,并具备内存优化功能,提升系统整体运行效率。
3. 垃圾清理功能:涵盖系统垃圾、浏览器缓存垃圾以及VS开发环境产生的垃圾文件清理,释放磁盘空间。
4. 服务管理功能:可遍历系统内的所有服务,并支持启动和关闭特定服务,满足用户对系统服务的定制化管理需求。
5. 杀毒功能模块:
- MD5查杀:通过对比文件MD5值识别病毒文件。
- 全路径查杀:依据文件路径特征进行病毒排查。
- 白名单查杀:基于白名单机制,识别并处理未在名单内的进程。
6. 数据库与网络应用:
- 云查杀:利用网络连接服务器,实现云端病毒查杀。
- 提交样本:支持用户提交可疑文件样本,助力病毒库更新。
- 更新病毒库:及时从服务器获取最新病毒信息,保障查杀能力。
7. 进程保护功能:对关键进程进行防护,防止其被恶意终止或篡改。
8. PE文件解析:深入分析PE文件结构,辅助病毒检测和系统维护。
二、附加功能
1. 老板键功能:设置便捷老板键,快速隐藏特定窗口,保护隐私。
2. 系统控制功能:提供关机、重启、休眠等系统操作快捷入口。
3. 文件信息查看:可查询文件的创建时间、修改时间以及MD5值,方便用户了解文件属性。
4. 启动项与软件管理:能够管理系统启动项,同时支持软件卸载操作。
5. 内存优化小火箭及界面优化:以直观的小火箭形式展示内存优化功能,注重界面友好度设计,采用自定义控件提升用户体验。
三、开发环境
- 操作系统:Windows 10
- 数据库:MySQL 5.6
- Python版本:Python 3.6.2 32bit
- 开发工具:VS 2015或更高版本
四、数据库简单字段设计
- 病毒库:包含ID和MD5字段,用于存储病毒文件的唯一标识和对应的MD5值。
- 黑名单库:同样设置ID和MD5字段,记录黑名单中的文件标识及MD5信息。
- 白名单库:通过ID和MD5字段,保存白名单内文件的相关信息。
- 用户表备用:预留用户表,可用于后续扩展用户相关功能 。
五、代码架构
客户端在相应窗口位置发送消息,在特定位置接收消息,开启线程发送更新消息,线程维护,同时保留socket统一接收接口。
六、软件界面
七、功能架构
八、部分功能截图
解析PE文件信息
PE头
区段信息
目录信息
导入表
导出表
资源表
重定位表
延迟加载表
TLS表
集成任务管理器
遍历进程线程模块
遍历文件信息
遍历窗口信息
VS工程垃圾清理
老板键
关机重启等
九、相关实现细节概要
关于任务管理器的实现细节请看上一节,下面介绍本项目剩余部分细节实现。
9.1 获取文件信息
(1)获取文件的创建时间、修改时间和文件大小
要是我们知道了文件的路径,就可以用FindFirstFile()函数来获取单个文件的信息。用这个函数能得到文件的好多信息,这些信息都存放在一个结构体里。下面是这个结构体的原型和注解:
typedef struct _WIN32_FIND_DATAW {DWORD dwFileAttributes; //文件属性FILETIME ftCreationTime; //文件创建时间FILETIME ftLastAccessTime; //文件最后的访问时间FILETIME ftLastWriteTime; //文件最后写入的时间DWORD nFileSizeHigh; //文件字节数的高32位DWORD nFileSizeLow; //文件字节数低32位DWORD dwReserved0; //保留DWORD dwReserved1; //保留_Field_z_WCHAR cFileName[MAX_PATH ];//文件名_Field_z_WCHAR cAlternateFileName[14 ];
#ifdef _MACDWORD dwFileType;DWORD dwCreatorType;WORD WFinderFlags;
#endif
}WIN32_FIND_DATAW,*PWIN32_FIND_DATAW,*LPWIN32_FIND_DATAW;
由于我们只打算获取一个文件的信息,所以文件路径不用加通配符,也不用调用FindNextFile()函数去逐个查找文件。具体做法如下:
WIN32_FIND_DATA stcFData={0};
HANDLE hFind =FindFirstFile(L"c:\\123.exe",&stcFData);
if(hFind ==INVALID_HANDLE_VALUE)return FALSE;
最后,就可以使用stcFData结构体中的内容了。
(2)获取文件的MD5值
没有现成的API能直接算出文件的MD5值,不过网上有开源代码可以做这个计算。
9.2 清理电脑垃圾信息
9.2.1 回收站
(1)相关函数及结构体
typedef struct {DWORD cbSize; //本结构体的大小int64 i64Size; //回收站的大小int64 i64NumItems;//回收站中文件的数量
}SHQUERYRBINFO,*LPSHQUERYRBINFO;
- 获取回收站信息
HRESULT SHQueryRecycleBin(LPCTSTR pszRootPath, //文件或文件夹的路径LPSHQUERYRBINFO pSHQueryRBInfo //返回SHQUERYRBINFO结构体信息
);
- 清空回收站信息
- `pszRootPath`:这个参数是回收站在根驱动器里的路径地址。它可以是用字符串表示的驱动器、文件夹和子文件夹的名称,也能是空字符串或者NULL。要是为空,就意味着会清空所有驱动器上的回收站。
- `dwFlags`:这是清空回收站时的一些操作设置。
- `SHERB_NOCONFIRMATION`:删除时不会弹出确认删除的对话框。
- `SHERB_NOPROGRESSUI`:删除时不显示删除进度条。
- `SHERB_NOSOUND`:删除完成后不会有提示音。
(2)思路
1. 初始化SHQUERYRBINFO结构体
SHQUERYRBINFO RecycleBinInfo ={};
RecycleBinInfo.cbSize =sizeof (RecycleBinInfo);
2. 查询回收站信息
SHQueryRecycleBin(NULL,&RecycleBinInfo);
//SHQueryRecycleBin第一参数为要查询回收站的盘符或者文件夹
3. 清空回收站
SHEmptyRecycleBin(NULL,NULL,SHERB_NOCONFIRMATION | SHERB_NOPROGRESSUI | SHERB_NOSOUND);
9.2.2 清理指定数据下的文件
(1)相关函数及结构体
typedef struct _WIN32_FIND_DATA {DWORD dwFileAttributes; //文件属性FILETIME ftCreationTime; //文件创建时间FILETIME ftLastAccessTime; //文件最后一次访问时间FILETIME ftLastWriteTime; //文件最后一次修改时间DWORD nFileSizeHigh; //文件长度(高32位)DWORD nFileSizeLow; //文件长度(低32位)DWORD dwReserved0; //系统保留DWORD dwReserved1; //系统保留TCHAR cFileName[MAX_PATH]; //文件名TCHAR cAlternateFileName[14]; //类似于短路径
}WIN32_FIND_DATA,*PWIN32_FIND_DATA,*LPWIN32_FIND_DATA;typedef struct _WIN32_FILE_ATTRIBUTE_DATA {DWORD dwFileAttributes; //文件系统文件或目录的属性信息。FILETIME ftCreationTime; //创建文件和文件夹的时间FILETIME ftLastAccessTime; //文件最后一次访问时间FILETIME ftLastWriteTime; //文件最后一次修改时间DWORD nFileSizeHigh; //文件长度(高32位)DWORD nFileSizeLow; //文件长度(低32位)
}WIN32_FILE_ATTRIBUTE_DATA,*LPWIN32_FILE_ATTRIBUTE_DATA;
1. 查找第一个文件
HANDLE WINAPI FindFirstFile(LPCTSTR lpFileName, //要查询的文件路径LPWIN32_FIND_DATA lpFindFileData //指向文件信息结构体
);
2. 获取文件属性
BOOL GetFileAttributesEx(LPCTSTR lpFileName, //文件路径GET_FILEEX_INFO_LEVELS fInfoLevelId,//指定要获取的属性信息的设置LPVOID lpFileInformation //指向WIN32_FIND_DATA结构体。
);
也可以通过FindFirstFile函数返回的结构体信息确定文件的属性
3. 获取文件的后缀名
PTSTR PathFindExtension(PTSTR pszPath //文件的路径
);
4. 获取下一个文件
BOOL FindNextFile(HANDLE hFindFile, //FindFirstFile的返回值LPWIN32_FIND_DATA lpFindFileData //指向文件信息结构体
);
(2)思路
实现删除C盘根目录下指定类型的文件
LARGE_INTEGER EnumFoldAndDeleteFile(WCHAR *lpFoldPath,WCHAR *1pFileType)
{//1. 初始化路径字符串WCHAR szFoldPath[MAX_PATH]=L"C:\\";wcscat_s(szFoldPath, L"*");//在路径字符串追加通配符L"\\*"//2.获取第一个文件的信息ULARGE_INTEGER qwFileTotalSize ={}; //文件总大小WIN32_FIND_DATA w32FindData ={};HANDLE hFindFile =FindFirstFile(szFoldPath,&w32FindData);//3.循环遍历获取当前目录中的文件信息do{//去掉两个特殊目录if((!wcscmp(w32FindData.cFileName,L"."))|| (!wcscmp(w32FindData.cFileName,L"..")))continue;if(w32FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY){//文件夹,继续删除文件夹里面的内容WCHAR szSubfoldPath[MAX_PATH];StringCchPrintf(szSubfoldPath,MAX_PATH,L"%s\\",lpFoldPath);//追加反斜杠StringCchCat(szSubfoldPath,MAX_PATH,w32FindData.cFileName);//子目录名qwFileTotalSize =EnumFoldAndDeleteFile(szSubfoldPath,1pFileType);}else{//获取文件的后缀WCHAR szFullPath[MAX_PATH];StringCchPrintf(szFullPath, MAX_PATH, L"%s\\%s", lpFoldPath, w32FindData.cFileName);LPWSTR FileSuffix=PathFindExtension(szFullPath);if((!lstrcmp(FileSuffix,L".tlog")) || (!lstrcmp(FileSuffix, L".obj"))|| (!lstrcmp(FileSuffix, L".log"))|| (!lstrcmp(FileSuffix,L".pch")) || (!lstrcmp(FileSuffix,L".ilk"))|| (!lstrcmp(FileSuffix, L".pdb"))){ULARGE_INTEGER fileSize; //记录文件的大小fileSize.HighPart =w32FindData.nFileSizeHigh;fileSize.LowPart =w32FindData.nFileSizeLow;qwFileTotalSize.QuadPart +=fileSize.QuadPart;DeleteFile(szFullPath); //删除文件}}}while(FindNextFile(hFindFile,&w32FindData));return qwFileTotalSize;
}
要是想获取文件大小,由于WIN32_FIND_DATA结构体里文件长度分高位和低位,我们可以借助另一个结构体把它们拼接起来得到完整的文件大小,最后直接用fileSize.QuadPart就行。
像系统临时文件:C:\Windows\Temp
用户临时文件:C:\Users\kevin\AppData\Local\Temp
Windows错误报告:C:\Users\kevin\AppData\Local\Microsoft\Windows\WER\ReportQueue
至于其他的垃圾信息路径,就得自己去收集了,可以参考电脑管家垃圾清理功能里涉及的文件路径。接下来讲讲9.3杀毒功能,包括MD5查杀、全路径查杀和白名单查杀。
(1)相关函数及结构体
HANDLE WINAPI CreateFile(_In_ LPCTSTR lpFileName, //要打开的文件名_In_ DWORD dwDesiredAccess, //打开方式_In_ DWORD dwShareMode, //是否共享读_In_opt LPSECURITY_ATTRIBUTES lpSecurityAttributes,//安全设置_In_ DWORD dwCreationDisposition,//操作文件属性_In_ DWORD dwFlagsAndAttributes,//文件本身的属性_In_opt_ HANDLE hTemplateFile //另一个文件句柄,模板文件
);BOOL WriteFile(HANDLE hFile, //文件句柄LPCVOID lpBuffer, //要写入的数据缓冲区DWORD nNumberOfBytesToWrite, //要写入缓冲区的大小LPDWORD lpNumberOfBytesWritten,//实际写入文件的字节数LPOVERLAPPED lpOverlapped //指向OVERLAPPED结构体
);BOOL ReadFile(HANDLE hFile,LPVOID lpBuffer,DWORD nNumberOfBytesToRead,LPDWORD lpNumberOfBytesRead,LPOVERLAPPED lpOverlapped
);
(2)思路
1. 将MD5值手动保存在文件中(.txt)
2. 打开文件
HANDLE hFile =CreateFile(L"5.txt", //名为"5.txt"GENERIC_READ, //以读方式打开0, //不能被共享NULL, //默认安全描述子,不能被继承OPEN_EXISTING, //只打开以存在的文件FILE_ATTRIBUTE_NORMAL, //常规文件属性NULL); //无模板
3. 读取文件中的信息
char ch[100]={};
DWORD dwReads;
ReadFile(hFile,ch,100,&dwReads,NULL);
4. 关闭文件句柄
CloseHandle(hFile);
- MD5查杀:先在一个文件里随意存上几个软件或者文件的MD5值,之后获取需要检测文件的MD5值,再把它和之前存好的MD5值对比,以此判断文件是否安全。
- 全路径查杀:采用逐个查找文件的方式,把找到的每个文件的MD5值和文件路径都进行分析比较。打个比方,要是在C盘根目录下发现有QQ.exe这个文件,就判定它是病毒,这算是一种很基础的文件查杀手段 。
- 白名单查杀:挨个检查进程,看看正在运行的进程是不是在自己预先设定好的白名单里面,如果不在,就把这个进程关掉。
9.3 数据库与网络运用
(云杀毒,从服务器获取查杀的信息)
9.3.1 数据库
(1)病毒信息存于数据库思路
1. 创建数据库
create database 病毒数据库
2. 创建病毒数据表
create table 病毒信息表
(NAME nchar(50), --病毒名MD5 nchar(50)not null, --病毒MD5值
)
3. 插入病毒数据(手动在数据库引擎中添加)
Insert Into病毒信息表(NAME,MD5)
values('QQScLauncher.exe','9800BDO8FCF2F15E74B88F3359CC0420')
Insert Into病毒信息表(NAME,MD5)
values('QQScLauncher.exe','9800BD08FCF2F15E74B88F3359CC0420')
4. 用ADO编程的办法来添加和查找数据:要是想知道具体怎么查找和添加,去看上课时发的关于ADO的资料,那里边讲得很详细。
注意:你可以自己在http://www.virscan.org这个网站上找几个你觉得是病毒的信息,然后保存下来。之后在数据库里要是能查到这些信息,那就说明对应的文件是病毒。
5. 创建两个账户,它们的权限不一样,一个账户只有读取数据的权限,另一个账户既能读取又能写入数据,这就是权限管理。
9.3.2 网络应用
(1)数据库在服务器上运行,我们从服务器获取病毒信息:
1. 在服务器那边,用ADO来连接自身服务器,就是把对数据库的各种访问操作都放在服务端进行。接着选一种网络模型(比如消息模型),用来接收客户端发过来的信息。
2. 在客户端这边,先获取文件的MD5值,然后把它发给服务端。服务端查完相关结果后,再把对应的信息返回给客户端。关于网络通信的具体内容,参考课上的资料(比如消息模型部分)。
(2)提交样本信息:
1. 客户端向服务端发送数据,服务端要判断收到的数据,到底是提交的样本信息,还是查询信息。判断方式有:
- 在网络编程里,通过数据名字前面加特殊符号的方法来区分。
- 把客户端要发的数据定义成结构体(结构体里包含数据类型、数据长度、结构体自身长度等信息),这样每次发送的数据就是结构体指针。服务端也用同样的结构体来接收数据。这里有个问题,当发送的数据太长时,没办法一次接收完。这时候结构体里的数据长度就很关键了。用recv接收数据后,会返回当前实际收到的数据长度,如果这个长度小于结构体里保存的数据长度,那就得再接收一次,并且把新收到的数据存到之前数据缓冲区的后面。
例子:
客户端:
//发给服务器的数据包,包括消息头,大小和MD5值
struct MessageContent
{DWORD dwMessageType; //消息类型ULARGE_INTEGER ulFileSize; //MD5值的数据大小char szMD5[36]; //MD5值
};
MessageContent msg;
send(socket,(char *)&msg,sizeof (msg),0);
服务端:用`recv()`函数接收到数据后,把它转成MessageContent类型就行。接着对收到的数据做判断,然后根据判断结果操作数据库,进行增加、删除、修改、查询这些操作。
9.4 注册表操作
(启动项和软件卸载)
9.4.1 软件卸载
(1)相关结构体和函数
1. 打开子句键句柄
LONG RegOpenKeyEx(_In_ HKEY hKey, //根键句柄_In_opt_ LPCTSTR lpSubKey, //子键路径_Reserved_ DWORD ul0ptions, //保留NULL_In_ REGSAM samDesired,//打开键句柄的权限_0ut_ PHKEY phkResult //子键句柄
);
2. 枚举获取Uninstall文件夹下的子文件名称
LONG RegEnumKeyEx(_In_ HKEY hKey, //RegOpenKeyEx获取到的子键句柄_In_ DWORD dwIndex, //指定被枚举键下子键的索引,从0开始_0ut_ LPTSTR lpName, //保存子键的名称_Inout LPDWORD lpcName, //指向szKeyName内存的大小_Reserved LPDWORD lpReserved,//保留 必须为0_Inout_ LPTSTR lpClass, //DWORD变量地址,用于获取子键的类型_Inout_opt_ LPDWORD lpcClass, //指向一块内存,用于获取子键值数据_Out_opt_ PFILETIME lpftLastWriteTime //上一个参数所向的内存大小
);
3. 获取注册表项指定值的名称和数据类型
LONG RegQueryValueEx(_In_ HKEY hKey, //子键句柄_In_opt_ LPCTSTR lpValueName,//要查询的键值名称_Reserved LPDWORD lpReserved,//保留 必须为0_0ut_opt LPDWORD lpType, //键值的类型_0ut_opt_ LPBYTE lpData, //保存得到的结果_Inout_opt_LPDWORD lpcbData ); //缓冲的大小每次查询时都初始化一下
(2)思路
软件卸载信息有两处,通常系统版本为x64则遍历x64子键
- 主键:HKEY_LOCAL_MACHINE
- 32位下:
子键:Software\Microsoft\Windows\CurrentVersion\Uninstall
- 64位下:
子键:Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall
//自定义结构体
typedef struct _SOFTINFO
{WCHAR szSoftName[50]; //软件名称WCHAR szSoftVer[50]; //软件版本号WCHAR szSoftDate[20]; //软件安装日期WCHAR szSoftSize[MAX_PATH]; //软件大小WCHAR strSoftInsPath[MAX_PATH]; //软件安装路径WCHAR strSoftUniPath[MAX_PATH]; //软件卸件路径WCHAR strSoftVenRel[50]; //软件发布厂商WCHAR strSoftIco[MAX_PATH]; //软件图标路径
}SOFTINFO,*PSOFTINFO;vector<SOFTINFO>m_vectSoftInfo; //保存软件相关信息
HKEY RootKey =HKEY_LOCAL_MACHINE; //主键
//子键名称(x86)
LPCTSTR lpSubKey =
L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall";
HKEY hkResult =0; //接收将要打开的键的句柄//1.打开一个己经存在的注册表键
LONG lReturn =RegOpenKeyEx(RootKey, lpSubKey, 0,KEY_ENUMERATE_SUB_KEYS|KEY_QUERY_VALUE, //指定打开键句柄的权限&hkResult); //子键句柄//2.循环遍历Uninstall目录下的子键
while(true)
{DWORD dwIndex =0;DWORD dwKeyLen =255;WCHAR szNewKeyName[MAX_PATH]={}; //注册表项名称LONG lReturn =RegEnumKeyEx(hkResult, //子键句柄dwIndex, //指定被枚举键下子键的索引,从0开始szNewKeyName, //指向一块内存保存子键的名称&dwKeyLen, //指向szKeyName内存的大小0, //保留 必须为0NULL, //用于获取子键值的类型NULL, //指向一块内存,用于获取子键值数据NULL); //上一个参数所向的内存大小//2.1通过得到的子键名称重新组合成新的子键路径WCHAR strMidReg[MAX_PATH]={};swprintf_s(strMidReg,L"%s%s%s",lpSubKey,L"\\",szNewKeyName);//2.2打开新的子键,获取其句柄HKEY hkValueKey =0; //子键句柄RegOpenKeyEx(RootKey,strMidReg,0,KEY_QUERY_VALUE,&hkValueKey);//2.3获取键值DWORD dwNameLen =255; //指向szBuffer内存的大小//获取软件名称RegQueryValueEx(hkValueKey,L"DisplayName",0,&dwType,(LPBYTE)m_vectSoftInfo[0].szSoftName,&dwNameLen);dwNameLen =255; //如果没有重新赋值下一次将获取不到信息//拆卸路径RegQueryValueEx(hkValueKey,L"UninstallString",0,&dwType,(LPBYTE)m_vectSoftInfo[0].strSoftUniPath,&dwNameLen);dwNameLen =255;dwIndex++; //子键的索引
}
最后一步卸载软件只需要将得到的拆卸路径以OpenFile打开即可。
9.4.1 管理启动项
(1)相关函数
1. 删除键值
LONG WINAPI RegDeleteValue(_In_ HKEY hKey, //打开要删除键值所属的子健_In_opt_ LPCTSTR lpValueName //键值名
);
2. 枚举键值
LONG WINAPI RegEnumValue(_In_ HKEY hKey, //主键_In_ DWORD dwIndex, //索引,第1个_0ut_ LPTSTR lpValueName, //键值的名称_Inout LPDWORD lpcchValueName,//键值的大小_Reserved LPDWORD lpReserved, //保留_0ut_opt_ LPDWORD lpType, //类型_0ut_opt_ LPBYTE lpData, //保存数据_Inout_opt LPDWORD lpcbData //lpData的长度
);
(2)思路
注册表启动项在HKEY_CURRENT_USER和HKEY_LOCAL_MACHINE下面的:
- X86:
- L"Software\\Microsoft\\Windows\\CurrentVersion\\Run",
- L"Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer\\Run",
- L"Software\\Microsoft\\Windows\\CurrentVersion\\RunOnce",
- L"Software\\Microsoft\\Windows\\CurrentVersion\\RunServices0nce",
- L"Software\\Microsoft\\Windows\\CurrentVersion\\RunServices",
- L"Software\\Microsoft\\Windows\\CurrentVersion\\RunOnceEx",
- X64:
- L"Software\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Run"
- L"Software\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\RunOnce"
//HKEY_CURRENT_USER下的指定键值
WCHAR RegBootStartItemInHKCU[REG_BOOT_START_ITEM_HKCU][MAX_PATH]={L"Software \\Microsoft\\WindowsNT\\CurrentVersion\\Windows",L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon",L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon",L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon",L"Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System"
};
获取启动程序相关信息:前面的操作和拆卸软件的一样,先枚举有子键,再枚举子键内的值。
DWORD dwIndex =0;
RegEnumValue(hSubKey,dwIndex,szKeyValueName,&dwValueNameLen,0,NULL,(PBYTE)szValue,&dwValueLen);
1. 打开要删除键值所属的子健
HKEY hSubkey;
RegOpenKeyEx(hKey,lpSubkey,0,KEY_SET_VALUE,&hSubkey);
2. 删除指定键值
RegDeleteValue(hSubkey,lpKeyValueName);
9.5 遍历服务,启动,关闭服务
在Windows系统里,有个服务控制管理器,它存着电脑上所有服务的信息和状态。只要能操作这个服务控制管理器,就能获取所有服务的信息和状态,还能加载、启动、停止、关闭服务。下面是操作服务控制管理器的方法:
9.6.1 获取服务控制管理器的句柄
操作服务控制管理器,就跟读写文件一样,都得有个句柄。下面是获取服务控制管理器句柄的API原型和注解:
SC_HANDLE OpenSCManagerW(LPCWSTR lpMachineName, //主机名LPCWSTR lpDatabaseName, //数据库名DWORD dwDesiredAccess //访问方式
);
- `lpMachineName`:这是主机名,它可以是远程主机的名字。要是填NULL,就表示打开本地主机。
- `lpDatabaseName`:这个参数要设置成SERVICES_ACTIVE_DATABASE,如果填NULL,系统会自动把它设成这个宏的值。
- `dwDesiredAccess`:这是访问权限,有下面这些宏可以用:
- `SC_MANAGER_ALL_ACCESS`:拥有所有权限。
- `SC_MANAGER_CREATE_SERVICE`:创建服务的权限,使用CreateService()函数时必须要有这个权限。
- `SC_MANAGER_CONNECT`:连接服务控制管理器的权限。
- `SC_MANAGER_ENUMERATE_SERVICE`:枚举所有服务的权限,使用EnumServicesStatus()函数和EnumServicesStatusEx()函数时需要这个权限。
更多详细信息可以自己去查MSDN(在函数名上按F1就能查到)。
9.6.2 枚举所有服务
如果想获取所有服务的信息,那么可以使用枚举服务的API。枚举服务的API原型和注解如下:
BOOL EnumServicesStatusEx(SC_HANDLE hSCManager, //SCM数据库句柄SC_ENUM_TYPE InfoLevel, //要返回的属性DWORD dwServiceType, //服务类型DWORD dwServiceState, //服务器状态LPBYTE lpServices, //接收信息缓冲区DWORD cbBufSize, //缓冲区大小LPDWORD pcbBytesNeeded, //需要的大小LPDWORD lpServicesReturned, //缓冲区中的服务个数LPDWORD lpResumeHandle, //NULLLPCTSTR pszGroupName //NULL
);
- `hSCManager`:这个是OpenSCManagerW函数的返回值。
- `InfoLevel`:一般填SC_ENUM_PROCESS_INFO。
- `dwServiceType`:这是要枚举的服务类型,一般填SERVICE_WIN32,有下面这些宏可以选:
- `SERVICE_DRIVER`:驱动服务。
- `SERVICE_FILE_SYSTEM_DRIVER`:文件系统驱动服务。
- `SERVICE_KERNEL_DRIVER`:内核驱动服务。
- `SERVICE_WIN32`:应用进程的服务,相当于下面两个宏的组合。
- `SERVICE_WIN32_OWN_PROCESS`
- `SERVICE_WIN32_SHARE_PROCESS`
- `lpServices`:用来存枚举出来的服务信息的缓冲区。
- `cbBufSize`:缓冲区的实际大小。
- `pcbBytesNeeded`:需要的缓冲区大小。
- `lpServicesReturned`:获取到的服务信息数量。
这个函数的用法和枚举模块的函数用法差不多。通常先传一个无效的缓冲区去调用这个函数,得到所需缓冲区的字节数,然后申请动态内存,再调用一次这个函数,把有效的缓冲区传进去,这样就能得到所有服务的信息了。
其实,这个缓冲区是一个ENUM_SERVICE_STATUSW结构体的数组,`lpServicesReturned`说的就是这个数组里元素的个数。
9.6.3 遍历服务
下面是获取服务信息的示例:
//1.打开远程计算机服务控制管理器
SC_HANDLE hSCM=OpenSCManagerW(NULL,NULL,SC_MANAGER_ALL_ACCESS);//2.第一次调用,获取需要的内存大小
DWORD dwServiceNum =0;
DWORD dwSize =0;
EnumServicesStatusEx(hSCM,SC_ENUM_PROCESS_INFO,SERVICE_WIN32,SERVICE_STATE_ALL, // 所有服务状态NULL, // 缓冲区0, //缓冲区大小&dwSize, //需要的大小&dwServiceNum, // 缓冲区中的服务个数NULL,NULL
);//3. 申请需要的内存,第二次调用
LPENUM_SERVICE_STATUS_PROCESS pEnumSerice =(LPENUM_SERVICE_STATUS_PROCESS)new char[dwSize];//4 . 第二次枚举
bool bStatus =FALSE;
bStatus=EnumServicesStatusEx(hSCM,SC_ENUM_PROCESS_INFO,SERVICE_WIN32,SERVICE_STATE_ALL, // 所有服务状态(PBYTE)pEnumSerice, // 缓冲区dwSize, //缓冲区大小&dwSize, //需要的大小&dwServiceNum, // 缓冲区中的服务个数NULL,NULL
);//5 . 遍历信息
for(DWORD i=0;i< dwServiceNum;i ++) {//获取基础信息// 服 务 名pEnumSerice[i].lpServiceName;//服务状态有已停止,正在运行,正在暂停//(根据得到的值手动输出字符串,具体值的含义可以在该结构体上按F1 查 MSDN)pEnumSerice[i].ServiceStatus.dwCurrentState;//服务类型//有文件系统驱动服务,驱动服务,独立进程服务//更多详细的信息查MSDNpEnumSerice[i].ServiceStatus.dwServiceType;//6 . 获取服务的详细信息这里将只给出函数// 打 开 服 务SC_HANDLE hService = OpenService( hSCM, // 服务控制管理器的句柄pEnumSerice[i].lpServiceName, // 服务名SERVICE_QUERY_CONFIG // 打开权限);//第一次调用获取需要的缓冲区大小QueryServiceConfig(hService,NULL,0,&dwSize);//分配内存LPQUERY_SERVICE_CONFIG pServiceConfig = \(LPQUERY_SERVICE_CONFIG)new char[dwSize];//第二次调用,获取信息QueryServiceConfig(hService,pServiceConfig,dwSize,&dwSize);//通过上面获取到的结构体信息具体得到想要的值//获取启动类型pServiceConfig->dwStartType;//类型有:"自启动","手动启动,"已禁用"//获取路径信息pServiceConfig->lpBinaryPathName;
}
项目代码:https://download.csdn.net/download/linshantang/90591342