鱼C论坛

 找回密码
 立即注册
查看: 8656|回复: 78

[开源]简易安全卫士的实现

[复制链接]
发表于 2018-1-26 12:39:47 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
   前段时间在学习核心编程,同时也利用所学知识做了个安全卫士的项目,现在拿出来跟大家分享一下,非常适合新手学习!
        这也算是我在15PB阶段性学习的一个小总结,在此提别感谢学习中帮助过我的老师们!

        下面进入正题:
        开发环境:Visual Studio 2013、Win7 x64、MFC
        项目周期:6天
【 功能模块目录】:
        1.病毒查杀:MD5查杀、白名单查杀、单个文件和全路径查杀,可使用本地病毒库或网络病毒库(网络病毒库自己搭建服务器通信)。
        2.垃圾清理:系统垃圾、浏览器垃圾、注册表的清理。
        3.电脑加速:简单的内存优化,释放内存。
        4.软件管理:显示本机上所安装的软件,并可以进行卸载操作。
        5.启动项管理:显示本机的启动项,并可以进行删除和添加操作。
        6.服务管理:显示本机的所有服务,列举服务状态、启动类型、服务类型等信息,可进行启动和停止服务操作。

        附几张图项目效果图预览一下:
5.jpg 4.jpg 3.jpg 2.jpg 1.jpg


本项目用的是MFC框架开发,按钮基本都是用的自绘,图片素材是我从一个外国的资源网站下载来然后自己PS的颜色。
        自我感觉界面还不错,总比原生的MFC界面强吧哈哈!
项目框架:

        【首页信息】是主窗口,其他各个模块都是主窗口的子窗口,各个模块的功能都是独立的,每个子窗口都已自己的类。需要的时候,各个模块会启用多线程处理。模块之间的切换,其实就是现实当前选择的功能模块窗口,隐藏其他的子窗口。
        每个模块可以分开来单独看,模块之间的联系不大。
1.【病毒查杀】模块

        首先要做的就是确定用户扫描的选项,比如,是单个文件查杀还是全路径查杀,是用本地病毒库还是用云端病毒库,这些信息我都会设置一个BOOL值,在开始扫描之前将他们赋值,然后在扫描的时候只需判断这些选项的BOOL值来做出不同的扫描方案。
        接下来就是按照选项进行扫描了,当进行单个文件扫描的时候,只需弹出一个选择单个文件的CFileDialog保存其路径即可;如果是全路径扫描,则需要保存用户所选要扫描的路径并保存。
        然后就是开始扫描文件了,我觉得管它叫遍历文件更准确,如果是单个文件扫描的话,文件路径之前已经保存,就不需要遍历,直接通过绝对地址对文件进行病毒判断就行;如果是全路径扫描,就需要遍历指定文件加下的所有文件了。具体的功能是通过两个API实现的:FindFirstFile( )和FindNextFile( ),首先调用FindFirstFile( ),获取遍历出来的第一个文件的路径,然后再循环执行FindNextFile( )寻找下一个文件,直到FindNextFile( )返回FALSE就跳出循环,遍历结束。需要注意的是,当遍历到的路径是文件夹的时候,需要将这个文件夹的路径当做参数传入本函数,也就是递归执行此函数,这样就不会遗漏一个路径下所有的文件了。
        还有一点就是,在执行扫描部分的代码的时候,最好另起一个线程,这样就不至于在扫描的时候主界面会卡在那不动。
        具体的代码实现如下:

  1. void Scam_All(LPCTSTR szPath)
  2. {
  3.   WIN32_FIND_DATA wfd;
  4.   HANDLE hFind;
  5.   CString sFullPath;
  6.   CString sFindFilter;
  7.   DWORD dwAttributes = 0;
  8.   sFindFilter = szPath;
  9.   sFindFilter += TEXT("\\*.*");
  10.   if ((hFind = FindFirstFile(sFindFilter, &wfd)) == INVALID_HANDLE_VALUE)
  11.     return;
  12.   do
  13.   {
  14.     if (_tcscmp(wfd.cFileName, TEXT(".")) == 0 ||
  15.       _tcscmp(wfd.cFileName, TEXT("..")) == 0)
  16.     {
  17.       continue;
  18.     }
  19.     //获取完整路径名
  20.     sFullPath = szPath;
  21.     sFullPath += TEXT("\");
  22.     sFullPath += wfd.cFileName;
  23.     //如果当前路径是文件夹,则需要递归文件夹中的文件
  24.     if (wfd.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)
  25.     {
  26.       Scam_All(sFullPath);
  27.     }
  28.     //否则,这是一个文件
  29.     else
  30.     {
  31.       //输出正在搜索的文件路径界面,让用户直观的看到正在扫描的文件路径
  32.       m_szStatus = _T("正在扫描 : ")+sFullPath;
  33.       SendMessage(WM_MYUPDATEDATA, FALSE);
  34.       //判断是否是病毒
  35.       if (IsMD5)   
  36.       {
  37.         //MD5查杀
  38.         //如果是病毒 将文件信息保存到病毒Vector
  39.         if (MD5Scan(sFullPath))
  40.         {
  41.           //插入List中显示
  42.           m_List_Vir.InsertItem(0, wfd.cFileName);  //文件名
  43.           m_List_Vir.SetItemText(0, 1, sFullPath);  //文件路径
  44.           m_List_Vir.SetItemText(0, 2,        //文件大小
  45.              GetShowSize(wfd.nFileSizeLow));
  46.           //保存病毒信息到Vector容器
  47.           VIRINFO VirInfo;
  48.           _tcscpy_s(VirInfo.szVirName, wfd.cFileName);
  49.           _tcscpy_s(VirInfo.szVirPath, sFullPath);
  50.           _tcscpy_s(VirInfo.szVirSize, GetShowSize(wfd.nFileSizeLow));
  51.           m_vecVirInfo.push_back(VirInfo);
  52.         }
  53.       }
  54.       else
  55.       {
  56.         //白名单查杀
  57.         //如果是病毒 将文件信息保存到病毒Vector
  58.         if (WhiteScan(sFullPath))
  59.         {
  60.           //插入List中显示
  61.           m_List_Vir.InsertItem(0, wfd.cFileName);  //文件名
  62.           m_List_Vir.SetItemText(0, 1, sFullPath);  //文件路径
  63.           m_List_Vir.SetItemText(0, 2,        //文件大小
  64.             GetShowSize(wfd.nFileSizeLow));
  65.           //保存病毒信息到Vector容器
  66.           VIRINFO VirInfo;
  67.           _tcscpy_s(VirInfo.szVirName, wfd.cFileName);
  68.           _tcscpy_s(VirInfo.szVirPath, sFullPath);
  69.           _tcscpy_s(VirInfo.szVirSize, GetShowSize(wfd.nFileSizeLow));
  70.           m_vecVirInfo.push_back(VirInfo);
  71.         }
  72.       }
  73.     }
  74.   } while (FindNextFile(hFind, &wfd));
  75.   FindClose(hFind);
  76. }
复制代码


在遍历文件的时候,当获取到文件的完整路径的时候,就可以对其进行病毒检测了。我这里仅仅是象征性的使用了两种检测方式,MD5对比查杀和白名单查杀(当然,真正的病毒查杀肯定不会这么简单),将要杀的文件路径当做参数传递给检测病毒的函数,MD5扫描函数是:MD5Scan(sFullPath),白名单扫描函数是:WhiteScan(sFullPath),如果判断是病毒的话返回TRUE,这时就可以保存这个路径,并将这个“病毒文件”信息输出到List控件中。
这两个函数的代码如下:

  1. BOOL MD5Scan(LPCTSTR szPath)
  2. {
  3.   //LPCTSTR 转CHAR*
  4.   int num = WideCharToMultiByte(CP_OEMCP, NULL, szPath, -1, NULL, 0, NULL, FALSE);
  5.   char *pchar = new char[num];
  6.   WideCharToMultiByte(CP_OEMCP, NULL, szPath, -1, pchar, num, NULL, FALSE);
  7.   //获取MD5值
  8.   char* md5 = md5FileValue(pchar);
  9.   //CHAR* 转LPCTSTR
  10.   num = MultiByteToWideChar(0, 0, md5, -1, NULL, 0);
  11.   wchar_t *wide = new wchar_t[num];
  12.   MultiByteToWideChar(0, 0, md5, -1, wide, num);
  13.   m_szMD5 = wide;
  14.   delete[]pchar;
  15.   delete[]wide;
  16.    
  17.   //获取文件MD5信息完毕
  18.   //判断是本地查杀还是云端查杀
  19.   if (IsLocal)
  20.   {
  21.     //本地MD5查杀    与m_LocalMD5作对比
  22.     for (DWORD i = 0; i < m_LocalMD5.size();i++)
  23.     {
  24.       if (m_LocalMD5[i] == m_szMD5)
  25.       {
  26.         //是病毒 返回真
  27.         return TRUE;
  28.       }
  29.     }
  30.   }
  31.   else
  32.   {  
  33.     //云端MD5查杀    与m_ServerMD5作对比
  34.     //本地MD5查杀    与m_LocalMD5作对比
  35.     for (DWORD i = 0; i < m_ServerMD5.size(); i++)
  36.     {
  37.       if (m_ServerMD5[i] == m_szMD5)
  38.       {
  39.         //是病毒 返回真
  40.         return TRUE;
  41.       }
  42.     }
  43.   }
  44.   return FALSE;
  45. }
复制代码
  1. BOOL WhiteScan(LPCTSTR szPath)
  2. {
  3.   //判断是本地查杀还是云端查杀
  4.   if (IsLocal)
  5.   {
  6.     //本地白名单查杀    与m_LocalWhite作对比
  7.     for (DWORD i = 0; i < m_LocalWhite.size(); i++)
  8.     {
  9.       if (m_LocalWhite[i] == szPath)
  10.       {
  11.         //是病毒 返回真
  12.         return FALSE;
  13.       }
  14.     }
  15.   }
  16.   else
  17.   {
  18.     //云端MD5查杀    与m_ServerWhite作对比
  19.     for (DWORD i = 0; i < m_ServerWhite.size(); i++)
  20.     {
  21.       if (m_ServerWhite[i] == szPath)
  22.       {
  23.         //是病毒 返回真
  24.         return FALSE;
  25.       }
  26.     }
  27.   }
  28.   return TRUE;
  29. }
复制代码


MD5查杀对比的时候,需要获取一下这个文件的MD5值,这里用到了一个MD5类的函数,这个类我用的是网上开源的代码(源码中包含)。
        MD5数据库和白名单查杀数据库,是在扫描开始前根据用户选项,然后从本地或云端将数据库信息载入内存的,本地数据库我用的是ini配置文件(象征性表示一下),云端查杀数据库是通过TCP通信下载的云端数据库信息。TCP服务端的代码在源码中有,但由于服务端还需要连接一个MySQL数据库,这样测试起来比较麻烦,所以建议只测试本地查杀功能,反正这个病毒查杀只是个象征性的对比,仅仅用于展现杀毒模块的功能、给大家提供个这个框架而已。
        判断该文件为病毒以后,将该文件的信息保存在一个vector容器成员变量中(m_vecVirInfo),在删除的时候,遍历这个vector,然后依次删除即可。
        删除病毒代码:
  1. void DeleteVir()
  2. {
  3.   //先判断病毒Vector是否为空,若为空,则提示没有可以清除的文件
  4.   if (m_vecVirInfo.size() == 0)
  5.   {
  6.     return;
  7.   }
  8.   //删除病毒
  9.   for (DWORD i = 0; i < m_vecVirInfo.size();i++)
  10.   {
  11.     DeleteFile(m_vecVirInfo[i].szVirPath);   
  12.   }
  13.   m_vecVirInfo.clear();
  14.   m_List_Vir.DeleteAllItems();
  15.   m_szStatus = _T("病 毒 清 除 完 毕 !");
  16.   UpdateData(FALSE);
  17. }
复制代码

   注:本地病毒库位置为exe文件所在目录下:   .\LocalVirBase\Vir.ini

2.【清理垃圾】模块
  清理方式主要有两种,一种是删除磁盘上的文件,另一种是删除注册表项。像清理系统临时垃圾、清理浏览器垃圾这样的功能,是通过第一种清理方式实现的;而像清理最近文档记录、文件查找记录则是通过第二种清理方式实现的。
        第一种清理方式的具体操作:
        以清理Interner临时文件为例,首先需要获取Interner临时文件的路径,由于每台电脑这个路径或许会有所不同,所以不能写死,这时候利用一个Windows API可以轻松的获取到你想要获取的各种系统路径:
  1. BOOL SHGetSpecialFolderPath(
  2.         HWND   hwndOwner,
  3.   _Out_ LPTSTR lpszPath,
  4.   _In_  int    csidl,
  5.   _In_  BOOL   fCreate
  6. );
复制代码


第一个参数传NULL,最后一个参数传FALSE。
        第二个参数是保存路径的缓冲区。
        第三个参数是一个宏,传入不同的宏可以获取不同的路径,例如传入 CSIDL_INTERNET_CACHE ,获取的就是Interner临时文件的路径。
        这些宏在MSDN上有给出,数量很多,基本可以获取到你想要获取的所有系统路径。
        SHGetSpecialFolderPath 介绍:
        https://msdn.microsoft.com/en-us/library/windows/desktop/bb762204(v=vs.85).aspx
        第三个参数宏:
        https://msdn.microsoft.com/en-us/library/windows/desktop/bb762494(v=vs.85).aspx
        获取完需要清理的路径以后就需要扫描或清理此路径下的文件了,扫描和清理功能我用的是同一个函数,区别就是定义了一个BOOL值 IsScanFile ,如果为TRUE,就只是扫描垃圾文件,并将垃圾文件显示出来,然后计算总的垃圾文件大小;如果IsScan为FALSE,在扫描的同时就会进行删除。
        这个扫描垃圾文件的函数同【病毒查杀】模块中的扫描函数类似,都是传入一个路径参数,然后遍历该路径下的所有文件,原理大同小异,唯一不同的就是对文件的处理而已,所以这里扫描和清理垃圾文件的代码我就不贴出来了,具体的也可以在源码中找到。

        第二种清理方式的具体操作:
        以清理运行记录为例,删除注册表键值之前,同样需要获取要删除注册表键值的路径。我没有找到获取某些指定注册表项路径的API,所以我就用了最笨的办法,手动写死要删除的注册表路径。保存运行记录的注册表位置为:"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\RunMRU",删除该注册表的API为:SHDeleteKey( ),代码实现如下:

  1. VOID CleanRunHistory()
  2. {
  3.   if (IsScanFile)  
  4.     return;  //如果是正在扫描垃圾文件,则直接返回,不进行清理操作。
  5.   SHDeleteKey(HKEY_CURRENT_USER,
  6.     TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\RunMRU"));
  7. }
复制代码

要想丰富项目的清理功能,很多时候就需要自己搜集垃圾文件的路径或注册表键值,拓展一下可以是清理某些指定软件的垃圾文件,搜集这些信息都是体力活。我这个项目只是清理了部分系统的垃圾,清理其他软件的垃圾原理类似。
3.【电脑加速】模块
   上面两大模块功能已经讲完了,剩下的几个模块就简单了许多。
        电脑加速模块的功能类似于很多优化软件的内存优化,所谓的优化其实就是腾出更多的可用内存空间。
        用到的两个函数:
  1. BOOL WINAPI SetProcessWorkingSetSize(
  2.   _In_ HANDLE hProcess,
  3.   _In_ SIZE_T dwMinimumWorkingSetSize,
  4.   _In_ SIZE_T dwMaximumWorkingSetSize
  5. );
复制代码
  1. BOOL WINAPI EmptyWorkingSet( _In_ HANDLE hProcess);
复制代码

关于SetProcessWorkingSetSize,如果第二个参数和第三个参数都传“-1”的话,MSDN上的解释如下:
        If both dwMinimumWorkingSetSize and dwMaximumWorkingSetSize have the value (SIZE_T)–1, the function removes as many pages as possible from the working set of the specified process.
        直译为尽可能多的将指定进程的页面从工作区中移除,我的理解就是将进程的页面置换到虚拟内存(磁盘)中,这样就可以腾出更多的内存空间了。
        另外一个函数EmptyWorkingSet,这是个宏,MSDN的解释也是Removes as many pages as possible from the working set of the specified process.所以这两个函数的功能貌似是一样的,不知道这两个函数的效果是否会叠加,反正对于一个进程,我都会调用一下这两个函数。、
        
        调用上面两个函数需要传入进程句柄,也就是说同时只能优化一个进程的内存空间,那么我们就还需要遍历整个系统的所有进程,然后获取每个进程的句柄,将该句柄当做参数传入上面两个函数,就会达到优化所有进程内存空间的效果。
        遍历进程并进行优化的代码如下:
  1. void ClearMemory()
  2. {
  3.   HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
  4.   if (hProcessSnap == INVALID_HANDLE_VALUE)
  5.   {
  6.     return;
  7.   }
  8.    
  9.   PROCESSENTRY32 ProcessInfo;         
  10.   ProcessInfo.dwSize = sizeof(ProcessInfo);  
  11.   int count = 0;
  12.   //获取系统中第一个进程的信息  
  13.   BOOL Status = Process32First(hProcessSnap, &ProcessInfo);
  14.   while (Status)
  15.   {
  16.     HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, TRUE,
  17.       ProcessInfo.th32ProcessID);
  18.     if (hProcess)
  19.     {
  20.       //设置进程工作区大小
  21.       SetProcessWorkingSetSize(hProcess, -1, -1);
  22.       //尽可能多的将指定进程的页面从工作区移除
  23.       EmptyWorkingSet(hProcess);
  24.       CloseHandle(hProcess);
  25.     }
  26.     //获取下一个进程的信息  
  27.     Status = Process32Next(hProcessSnap, &ProcessInfo);
  28.   }
  29. }
复制代码

        执行完上面这个函数以后,为了直观的显示优化后所腾出的内存空间,可以通过一个获取当前内存信息的代码计算,在执行清理之前计算一下当前的内存使用量,在执行完清理之后再计算一下内存的使用量,两个计算结果相减就可以得出本次优化后所腾出的内存空间。获取当前内存使用量的代码如下:
  1. m_szStatus = _T("正在清理内存中... ...");
  2.   UpdateData(FALSE);
  3.   //获取清理前的内存信息
  4.   MEMORYSTATUSEX memStatus = { sizeof(MEMORYSTATUSEX) };
  5.   GlobalMemoryStatusEx(&memStatus);
  6.   DOUBLE preUsedMem = (DOUBLE)(memStatus.ullTotalPhys - memStatus.ullAvailPhys) / 1024 / 1024;
  7.   //开始清理内存
  8.   ClearMemory();
  9.   //获取清理后的内存信息
  10.   GlobalMemoryStatusEx(&memStatus);
  11.   DOUBLE afterUsedMem = (DOUBLE)(memStatus.ullTotalPhys - memStatus.ullAvailPhys) / 1024 / 1024;
  12.   //输出清理信息
  13.   m_szStatus.Format(_T("内存清理完毕!本次清理 : %.2f MB"), preUsedMem - afterUsedMem);
复制代码


        我自己关于内存优化的看法:
        首先从一种暴力清理内存的方式展开说。上面的内存清理方式,所腾出的可用内存空间并不明显,于是我从网上找到了另一种暴力清理方式,原理就是用一个进程大量的申请内存空间,瞬间填满你的内存,这时候系统就会调用其本身的置换操作,将其他进程的内存空间置换到虚拟内存(磁盘)中,该进程再突然释放所申请的大量空间,然后内存就会瞬间腾出很大的空间。我在我的4GB内存电脑上实测了这种内存清理方法,效果要好于我上面所说的清理方式,我所说的“效果好”其实是指的所腾出的可用内存空间大,虽然你看上去会有很大的空闲内存可用,但你会发现当你优化完,再去执行其他的程序时,会出现明显的卡顿,在卡顿的过程中,你的内存使用量又涨上去了。
         上面所说的卡顿现象,我觉得是进程又将自己原有的内存信息从磁盘中读入内存,这会给系统带来很大的开销,因为需要大量IO操作,而磁盘的读写速度与内存完全不是一个量级的,总之我觉得这种极端暴力的内存优化着实鸡肋。从这个极端地例子可以得出结论,并不是“清理”的内存越多系统就越快,反而清理过多了会拖慢系统的运行,因为在内存中的速度那么快,干嘛要把它置换到磁盘中呢?
        综上所述,所谓的内存优化其效果是仁者见仁智者见智,就我个人而言觉得这个功能并不实用,对于小内存的电脑来说优化其实是在拖慢系统,而对于大内存的电脑来说又没有什么必要。
       So,优化内存最有效的方式就是——插内存条。。。


4.【软件管理】模块

  获取安装软件的信息,其实就是枚举注册表项,查询注册表键值然后保存。
        定义一个保存软件信息的结构体(自己定义的):
  1. typedef struct _SOFTINFO
  2. {
  3.   TCHAR szSoftName[50];        //软件名称
  4.   TCHAR szSoftVer[50];        //软件版本号
  5.   TCHAR strSoftVenRel[50];      //软件发布厂商
  6.   TCHAR szSoftData[20];        //软件安装日期
  7.   TCHAR strSoftInsPath[MAX_PATH];    //软件安装路径
  8.   TCHAR strSoftUniPath[MAX_PATH];    //软件卸载路径
  9. }SOFTINFO, *PSOFTINFO;
复制代码


32位和64位系统的查询位置也略有不同,可以自己定义两个宏,根据判断是否为64位进行选择。具体实现代码如下:
  1. #define Win32PATH _T("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall")
  2. #define Win64PATH _T("SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall")
  3. void GetSoftwareInfo()
  4. {
  5.   m_Dlg5_List.DeleteAllItems();
  6.   m_vecSoftInfo.clear();
  7.   SOFTINFO stcSoftInfo = {0};  // 软件信息结构体
  8.   HKEY RootKey;                // 主键
  9.   LPCTSTR lpSubKey;            // 子键名称
  10.   HKEY hkResult;               // 将要打开键的句柄
  11.   HKEY hkRKey;
  12.   LONG lReturn;                // 记录读取注册表是否成功
  13.   CString strBuffer;
  14.   CString strMidReg;
  15.   DWORD index = 0;
  16.   TCHAR szKeyName[255] = { 0 };        // 注册表项名称
  17.   DWORD dwKeyLen = 255;
  18.   DWORD dwNameLen = 255;
  19.   DWORD dwType = 0;
  20.   RootKey = HKEY_LOCAL_MACHINE;
  21.   lpSubKey = Is64() ? Win64PATH : Win32PATH;
  22.   lReturn = RegOpenKeyEx(RootKey, lpSubKey, 0,
  23.     KEY_ALL_ACCESS, &hkResult);
  24.   if (lReturn == ERROR_SUCCESS)
  25.   {
  26.     DWORD index = 0;
  27.     DWORD ListIndex = 0;
  28.     while (ERROR_NO_MORE_ITEMS !=
  29.       RegEnumKeyEx(hkResult, index, szKeyName, &dwKeyLen,
  30.       0, NULL, NULL, NULL))
  31.     {
  32.       strBuffer.Format(_T("%s"), szKeyName);
  33.       if (!strBuffer.IsEmpty())
  34.       {
  35.         strMidReg = (CString)lpSubKey + _T("\") + strBuffer;
  36.         if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, strMidReg,
  37.           0, KEY_ALL_ACCESS, &hkRKey) == ERROR_SUCCESS)
  38.         {
  39.           RegQueryValueEx(hkRKey, _T("DisplayName"),
  40.             0, &dwType, (LPBYTE)stcSoftInfo.szSoftName, &dwNameLen);
  41.           dwNameLen = 255;
  42.           RegQueryValueEx(hkRKey, _T("DisplayVersion"),
  43.             0, &dwType, (LPBYTE)stcSoftInfo.szSoftVer, &dwNameLen);
  44.           dwNameLen = 255;
  45.           RegQueryValueEx(hkRKey, _T("InstallLocation"),
  46.             0, &dwType, (LPBYTE)stcSoftInfo.strSoftInsPath, &dwNameLen);
  47.           dwNameLen = 255;
  48.           RegQueryValueEx(hkRKey, _T("Publisher"),
  49.             0, &dwType, (LPBYTE)stcSoftInfo.strSoftVenRel, &dwNameLen);
  50.           dwNameLen = 255;
  51.           RegQueryValueEx(hkRKey, _T("UninstallString"),
  52.             0, &dwType, (LPBYTE)stcSoftInfo.strSoftUniPath, &dwNameLen);
  53.           dwNameLen = 255;
  54.           RegQueryValueEx(hkRKey, _T("InstallDate"),
  55.             0, &dwType, (LPBYTE)stcSoftInfo.szSoftData, &dwNameLen);
  56.           if (stcSoftInfo.szSoftData[0])
  57.           {
  58.             stcSoftInfo.szSoftData[9] = stcSoftInfo.szSoftData[7];
  59.             stcSoftInfo.szSoftData[8] = stcSoftInfo.szSoftData[6];
  60.             stcSoftInfo.szSoftData[7] = '-';
  61.             stcSoftInfo.szSoftData[6] = stcSoftInfo.szSoftData[5];
  62.             stcSoftInfo.szSoftData[5] = stcSoftInfo.szSoftData[4];
  63.             stcSoftInfo.szSoftData[4] = '-';
  64.           }
  65.           dwNameLen = 255;
  66.           //保存数据
  67.           if (stcSoftInfo.szSoftName[0]!='\0')
  68.           {
  69.             m_vecSoftInfo.push_back(stcSoftInfo);
  70.             m_Dlg5_List.AddItem(ListIndex++, 4,
  71.               stcSoftInfo.szSoftName,
  72.               stcSoftInfo.strSoftVenRel,
  73.               stcSoftInfo.szSoftData,
  74.               stcSoftInfo.szSoftVer);
  75.           }
  76.           index++;
  77.         }
  78.         dwKeyLen = 255;
  79.         memset(szKeyName, 0, 255);
  80.         memset(&stcSoftInfo, 0, sizeof(SOFTINFO));
  81.       }
  82.     }
  83.     RegCloseKey(hkResult);
  84.   }
  85. }
复制代码

卸载软件功能用到了上面软件信息结构体中的一个变量:TCHAR strSoftUniPath[MAX_PATH]。
        这个变量保存了软件的卸载指令。
        之前所遍历出来的软件信息是保存在一个vector容器m_vecSoftInfo中,删除软件时通过下标标识要卸载的软件,卸载代码如下:

  1. ShellExecute(NULL, _T("open"),
  2.     m_vecSoftInfo[m_Select].strSoftUniPath, NULL, NULL, SW_SHOWNORMAL);
复制代码


5.【启动项】模块

由于获取启动项信息也是通过遍历注册表实现的,代码类似于获取软件信息的函数,不同之处就在一要查询的注册表位置不同,所以在这里就不再贴出代码,具体实现在源码中也可以找到。
        我所获取的注册表信息有两处:
        "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run"
        "SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Run"
        用到的启动项信息结构体(自己定义的):
  1. typedef struct _BOOTINFO
  2. {
  3.   TCHAR szBootName[50];      //启动项
  4.   TCHAR szBootOrder[255];      //命令
  5.   TCHAR szHKEY[50];        //主键信息
  6.   TCHAR szBootPos[255];      //删除时要用到的位置
  7.   TCHAR szBootPos_All[255];    //List中要显示的
  8. }BOOTINFO, *PBOOTINFO;
复制代码



        其中TCHAR szBootPos[255]是删除时要用到的变量,该变量保存了删除该注册表键值的路径
        删除启动项的代码如下:

  1. void DeleteBoot()
  2. {
  3.   HKEY RootKey=NULL;    //根键
  4.   HKEY hKey;
  5.   //确定要删除的根键
  6.   strTemp = m_vecBootInfo[m_Select].szHKEY;
  7.   if (strTemp == _T("HKEY_CURRENT_USER"))
  8.   {
  9.     RootKey = HKEY_CURRENT_USER;
  10.   }
  11.   if (strTemp == _T("HKEY_LOCAL_MACHINE"))
  12.   {
  13.     RootKey = HKEY_LOCAL_MACHINE;
  14.   }
  15.   if (RootKey==NULL)
  16.   {
  17.     return;
  18.   }
  19.   //确定要删除的Run子键路径
  20.   LPCTSTR lpRun = m_vecBootInfo[m_Select].szBootPos;
  21.   //打开启动项Key   
  22.   long lRet = RegOpenKeyEx(RootKey, lpRun, 0, KEY_WRITE, &hKey);
  23.   if (lRet == ERROR_SUCCESS)
  24.   {
  25.     //获取要删除启动项的名字
  26.     strTemp = m_vecBootInfo[m_Select].szBootName;
  27.     //删除此启动项
  28.     lRet = RegDeleteValue(hKey, strTemp);
  29.     //关闭注册表   
  30.     RegCloseKey(hKey);
  31.     if (lRet != ERROR_SUCCESS)
  32.     {
  33.       AfxMessageBox(_T("删除启动项成功!"));
  34.     }
  35.     else{
  36.       AfxMessageBox(_T("删除启动项成功!"));
  37.       GetBootInfo();
  38.     }
  39.   }
  40. }
复制代码


添加启动项的操作需要用到RegSetValueEx这个API,代码实现如下:
  1. void AddBoot()
  2. {
  3.   //打开文件代码
  4.   TCHAR FilePathName[MAX_PATH] = {0};
  5.   CString FileName;
  6.   CFileDialog dlg(TRUE, //TRUE为OPEN对话框,FALSE为SAVE AS对话框
  7.     NULL,
  8.     NULL,
  9.     OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,
  10.     (LPCTSTR)_TEXT("All Files (*.*)|*.*||"),
  11.     NULL);
  12.   if (dlg.DoModal() == IDOK)
  13.   {
  14.     //文件名保存在了FilePathName里
  15.     _tcscpy_s(FilePathName, dlg.GetPathName());
  16.   }
  17.   else
  18.   {
  19.     return;
  20.   }
  21.   //打开启动项Key
  22.   HKEY hKey;
  23.   LPCTSTR lpRun = _T("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run");
  24.   long lRet = RegOpenKeyEx( HKEY_CURRENT_USER, lpRun, 0, KEY_WRITE, &hKey);
  25.   if (lRet == ERROR_SUCCESS)
  26.   {
  27.     //获取路径长度
  28.     DWORD dwRet = _tcslen(FilePathName);
  29.     //添加启动项信息
  30.     FileName = dlg.GetFileTitle();
  31.     int n = sizeof(TCHAR);
  32.     lRet = RegSetValueEx(hKey, FileName, 0, REG_SZ,
  33.       (BYTE *)FilePathName, dwRet*sizeof(TCHAR));
  34.     //关闭注册表   
  35.     RegCloseKey(hKey);
  36.     if (lRet != ERROR_SUCCESS)
  37.     {
  38.       AfxMessageBox(_T("添加启动项失败!"));
  39.     }
  40.     else
  41.     {
  42.       AfxMessageBox(_T("添加启动项成功!"));
  43.       GetBootInfo();
  44.     }
  45.   }
  46. }
复制代码


6.【系统服务】模块
最后再说一下遍历系统服务信息,此模块功能需要有管理员权限才能实现,要不然服务模块的信息遍历不出来(当然你也可以自己添加UAC提权代码)。
        调用EnumServicesStatus这个API可以遍历服务信息,该函数会给你返回一个保存所有服务信息的缓冲区,你需要通过一个循环依次取出每一个服务的具体信息。了解了这个流程以后,看懂代码应该不算困难了:
  1. void GetServiceInfo()
  2. {
  3.   //1.打开远程计算机服务控制管理器
  4.   m_Dlg7_List.DeleteAllItems();
  5.   m_vecSerInfo.clear();
  6.   SC_HANDLE hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
  7.   //2.第一次调用,获取需要的内存大小
  8.   DWORD dwServiceNum = 0;
  9.   DWORD dwSize = 0;
  10.   EnumServicesStatus(
  11.     hSCM,
  12.     SERVICE_WIN32,
  13.     SERVICE_STATE_ALL,  //所有服务状态
  14.     NULL,        //缓冲区
  15.     0,          //缓冲区大小
  16.     &dwSize,      //需要的大小
  17.     &dwServiceNum,    //缓冲区中的服务个数
  18.     NULL);
  19.   //3.申请需要的内存,第二次调用
  20.   LPENUM_SERVICE_STATUS lpEnumService =
  21.     (LPENUM_SERVICE_STATUS)LocalAlloc(LPTR, dwSize);
  22.   //4.第二次枚举
  23.   EnumServicesStatus(
  24.     hSCM,
  25.     SERVICE_WIN32,
  26.     SERVICE_STATE_ALL,  //所有服务状态
  27.     lpEnumService,    //缓冲区
  28.     dwSize,        //缓冲区大小
  29.     &dwSize,      //需要的大小
  30.     &dwServiceNum,    //缓冲区中的服务个数
  31.     NULL);
  32.   //5.遍历信息
  33.   SC_HANDLE hService = nullptr;
  34.   for (DWORD i = 0; i < dwServiceNum; i++)
  35.   {
  36.     //获取基础信息
  37.     //1.服务名称
  38.     //2.服务描述
  39.     //3.服务状态(根据得到的值手动输出字符串)
  40.     //“已停止” “正在运行" "正在暂停"...
  41.     m_vecSerInfo.push_back(lpEnumService[i]);
  42.     //获取更多信息
  43.     //1.打开服务
  44.     hService = OpenService(hSCM,
  45.       lpEnumService[i].lpServiceName, SERVICE_QUERY_CONFIG);
  46.     // 2.第一次调用获取需要的缓冲区大小
  47.     QueryServiceConfig(hService, NULL, 0, &dwSize);
  48.     //分配内存
  49.     LPQUERY_SERVICE_CONFIG pServiceConfig =
  50.       (LPQUERY_SERVICE_CONFIG)LocalAlloc(LPTR, dwSize);
  51.     //3.第二次调用,获取信息
  52.     QueryServiceConfig(hService, pServiceConfig, dwSize, &dwSize);
  53.     //获取服务信息完毕,开始插入List
  54.     m_Dlg7_List.AddItem(i, 5,
  55.       m_vecSerInfo[i].lpServiceName,
  56.       m_vecSerInfo[i].lpDisplayName,
  57.       m_vecSerInfo[i].ServiceStatus.dwCurrentState,
  58.       pServiceConfig->dwStartType,
  59.       m_vecSerInfo[i].ServiceStatus.dwServiceType);
  60.     LocalFree(pServiceConfig);
  61.   }
  62.   LocalFree(lpEnumService);
  63.   CloseServiceHandle(hSCM);
  64.   CloseServiceHandle(hService);
  65. }
复制代码



剩下的就是启动、停止服务的功能了,也是通过API实现的,实现起来比较容易:
  1. //启动服务
  2.   SC_HANDLE hSCM = OpenSCManager(      //打开服务控制管理器
  3.     NULL, NULL, SC_MANAGER_ALL_ACCESS);
  4.   SC_HANDLE hService = OpenService(hSCM,  //打开服务
  5.     m_ServiceName, SERVICE_START);
  6.   StartService(hService, 0, 0);      //启动服务
复制代码

  1. //停止服务
  2.   SC_HANDLE hSCM = OpenSCManager(      //打开服务控制管理器
  3.     NULL, NULL, SC_MANAGER_ALL_ACCESS);
  4.   SC_HANDLE hService = OpenService(hSCM,  //打开服务
  5.     m_ServiceName, SERVICE_STOP);
  6.   SERVICE_STATUS status;
  7.   ControlService(hService,        //结束服务
  8.     SERVICE_CONTROL_STOP,
  9.     &status);  
复制代码


   至此,6个模块的功能就介绍完了!
结语:

        本项目基本都是通过调用ring3层的API实现的,通过MFC框架完成界面部分,已经完成了一个安全卫士的建议框架,部分功能还有具有一定实用性的,源码中也有一定的注释,很适合新手学习。大神轻喷啊。。。
        由于项目时间有限,网络监控模块没有写,只预留的按钮。哈哈 有点偷懒了。。。
        第一次发帖,讲的有点乱,编程半年的小菜,代码风格也不是很好,大家将就看吧。
        好了,就到这吧,最后附上源码。
源码下载:
游客,如果您要查看本帖隐藏内容请回复

点评

我很赞同!: 5.0
我很赞同!: 5
厉害呀  发表于 2023-7-2 17:41

评分

参与人数 1荣誉 +5 鱼币 +5 贡献 +3 收起 理由
高山 + 5 + 5 + 3 鱼C有你更精彩^_^

查看全部评分

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2018-4-23 18:20:53 | 显示全部楼层
厉害了,我的哥
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2018-4-23 20:55:52 | 显示全部楼层
huif
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2018-4-23 22:33:26 | 显示全部楼层
膜拜,学习下
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2018-4-26 21:42:47 | 显示全部楼层
膜拜
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2018-5-1 14:06:48 | 显示全部楼层
66666666
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2018-5-2 14:14:04 | 显示全部楼层
真棒,看看
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2018-5-5 16:53:13 | 显示全部楼层
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2018-5-6 20:06:42 | 显示全部楼层
666学习一下
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2018-5-18 08:15:25 From FishC Mobile | 显示全部楼层
忽然发现做一个会员真好
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2018-5-18 08:18:32 From FishC Mobile | 显示全部楼层
我这显示压缩包异常,是我的问题吗
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2018-5-21 10:22:47 | 显示全部楼层
谢谢楼主分享
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2018-5-22 18:07:22 | 显示全部楼层
太强了
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2018-5-23 15:55:18 | 显示全部楼层
大神,膜拜膜拜
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2018-5-29 10:07:41 From FishC Mobile | 显示全部楼层
给跪下了
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2018-6-4 20:38:10 | 显示全部楼层
好久没来了,非常感谢分享!学习了
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2018-6-8 10:51:45 | 显示全部楼层

膜拜,学习下
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2018-6-14 20:11:08 | 显示全部楼层
膜拜大神,谢谢分享,66666
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2018-6-18 22:25:49 From FishC Mobile | 显示全部楼层
666
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2018-7-4 16:25:46 | 显示全部楼层
牛逼啊,膜拜下大神~~~
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-3-28 18:48

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表