【WIN】Windows自启动项查询(附代码)

您所在的位置:网站首页 怎样查看系统启动项 【WIN】Windows自启动项查询(附代码)

【WIN】Windows自启动项查询(附代码)

2024-02-25 00:54| 来源: 网络整理| 查看: 265

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

整理一下 Windows 自启动项的存放位置

Windows 自启动项查询整理

目前有很多产品 都可以获取系统自启动项 如360安全卫士、autoRun、Dism++等等,在使用上各有不同,Windows的自启动项主要有以下几项:

自启动文件夹 注册表启动项 系统服务 系统驱动程序 任务计划 动态链接库 映像劫持

本文主要提供一下上述几项的信息和查询方式,另外需要补充的是,对于查询结果处理的准确性可结合多方面进行校验,这边罗列一下可以提供校验的工具:

【系统】。Windows提供了可以查看自启动软件(msconfig),服务(services.msc),定时任务的工具(管理工具)的工具,包括注册表也可以用来使用,以上这几个是系统提供的可供查询的路径。 【AutoRun】。这是一个软件,可以自行去找相关资源使用,这个软件很强大。可以参考这个去实现查询功能,里边很多设计的点都可以借鉴。 1. 自启动文件夹

自启动文件夹指的是Win系统上有专门的文件夹来存放自启动项,直接通过资源管理器查看即可:

1. %USERPROFILE%\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup 2. %ProgramData%\Microsoft\Windows\Start Menu\Programs\Startup

直接复制上述路径粘贴到资源管理器即可打开,如下所示两个路径下的应用程序也会是Win系统的启动项 在这里插入图片描述 如果是通过C++去查询,则需要使用函数去获取自启动项的绝对路径,使用 ExpandEnvironmentStrings可以获取环境变量所指向的绝对路径。

如果获取的是快捷方式,可以看看我的这篇文章:

【WIN】【C/C++】获取快捷方式指向的位置

【WIN】【C/C++】获取文件版本号

【WIN】【C++】遍历文件夹下所有文件

2. 通过注册表启动项

注册表作为Windows一个核心,自然也少不了保存相关项来存储自启动程序,注册表下的自启动项路径主要有以下 8 个部分,其中可能有重复项,需要自己去过滤重复项:

1. HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run 2. HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Run 3. HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer\Run 4. HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer\Run 5. HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce 6. HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce 7. HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnceEx 8. HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnceEx 此外,64位操作系统中,还存在一个重定向到32位的映射路径,如下: 9. HKLM\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Run

注册表项相对较多,可以提供一个统一的接口,逐个调用获取每个注册表路径下的启动项就好。

接口的大致过程分为三个步骤:

RegOpenKeyEx()进入目标路径 使用while循环和RegEnumValue遍历当前注册表路径 使用RegClose关闭句柄

百度【枚举注册表子项 | RegEnumValue】能查到较多相关demo,既然都写了,那就贡献一个我自己写的接口:

其中获取文件属性的代码我注释掉了,如有需要,请到这里👇获取源码

【WIN】查询文件信息(公司、版本、版权、描述、厂商等)_欧恩意的博客-CSDN博客

typedef struct _AUTO_RUNS_ST { WCHAR szName[MAX_PATH]; //软件名称 WCHAR szPath[MAX_PATH]; //软件路径 WCHAR szVersion[MAX_PATH]; //软件版本 WCHAR szCompany[MAX_PATH]; //软件公司 WCHAR szDesp[MAX_PATH * 4]; //软件描述 WCHAR szCmdLine[MAX_PATH]; //启动命令行 }AUTO_RUNS_ST, *AUTO_RUNS_ST; /* * [in]Htype: 注册表句柄 * [in]lpSubKey: 子路径 如:“SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run” * [in]bIs64KEY: 被查询机器是否是64bit * [out]vecAutoItems: 查询结果的输出结构体 */ bool EnumAutoRun( __in HKEY Htype, __in LPCTSTR lpSubKey, __in bool bIs64KEY, __out std::vector &vecAutoItems ) { bool bRet = FALSE; DWORD dwSamDesire = 0; // 1、自启动项查询 DWORD dwIndex = 0; HKEY hKey = HKEY_LOCAL_MACHINE; DWORD dwSizeValueName = MAX_PATH; DWORD dwSizeofKeyName = MAX_PATH; BYTE szValueName[MAX_PATH] = { 0 }; TCHAR szKeyName[MAX_PATH] = { 0 }; long nError = 0; size_t nEnd; if ( bIs64KEY ) { dwSamDesire = bIs64KEY ? (KEY_READ | KEY_WOW64_64KEY) : (KEY_READ | KEY_WOW64_32KEY); } if (RegOpenKeyExW(Htype, lpSubKey, 0, dwSamDesire, &hKey)) { printf(_T("EnumAutoRun RegOpenKeyExW Failed!Error code(%d)"), GetLastError()); goto _EXIT_; } // 2、遍历:根据得到的软件路径查询相关信息 do { dwSizeValueName = MAX_PATH; dwSizeofKeyName = MAX_PATH; AUTO_RUNS_ST stuAutoRun; memset(stuAutoRun, 0, sizeof(stuAutoRun)); nError = RegEnumValueW(hKey, dwIndex++, szKeyName, &dwSizeofKeyName, NULL, &dwType, szValueName, &dwSizeValueName); if (nError == ERROR_SUCCESS) { // 转string->截取字符串 -> 得到程序路径 std::wstring strFilePath = (wchar_t*)szValueName; // 先处理带‘"’的字符串 int nReplace = strFilePath.find_first_of(L"\"");//here k3=1 while(nReplace != strFilePath.npos) //hint: here "string::npos"means find failed { strFilePath = strFilePath.erase(nReplace, 1); nReplace = (int)strFilePath.find_first_of(L"\"", nReplace+1); } // 转小写 transform(strFilePath.begin(),strFilePath.end(),strFilePath.begin(), ::towlower); nEnd = strFilePath.find(L"exe"); std::wstring wsNewFilePath; // 2.2 保存 路径 if(nEnd != wstring::npos) { wsNewFilePath = strFilePath.substr(0, nEnd + 3); } _tcsncpy_s(stuAutoRun.szPath, ArraySize(stuAutoRun.szPath), wsNewFilePath.c_str(), ArraySize(stuAutoRun.szPath) - 1); printf(_T("EnumAutoRun RegOpenKeyExW:%s->%s!"), szKeyName, wsNewFilePath.c_str()); // 2.3 保存 程序名和启动命令行 _tcsncpy_s(stuAutoRun.szName, ArraySize(stuAutoRun.szName), szKeyName, ArraySize(stuAutoRun.szVersion)-1); _tcsncpy_s(stuAutoRun.szCmdLine, ArraySize(stuAutoRun.szCmdLine), (wchar_t*)szValueName, ArraySize(stuAutoRun.szVersion)-1); // 查询对应软件信息,如有需要 以‘///’注释的行取消注释即可 /// std::wstring wsVersion = L" "; /// std::wstring wsCompany = L" "; /// std::wstring wsDesp = L" "; /// FileInfoUtils::GetFileVersion(wsNewFilePath,wsVersion); /// FileInfoUtils::GetCompanyName(wsNewFilePath,wsCompany); /// FileInfoUtils::GetFileDescription(wsNewFilePath,wsDesp); // 过滤Microsoft项,这里是过滤微软的部分启动项,根据公司和版权写的一个hideMicroSoftEntry,可自行实现 /* if(hideMicroSoftEntry(wsCompany) && wsNewFilePath != L"cmd.exe") // 排除cmd.exe { bRet = TRUE; continue; } */ // 2.4 保存路径信息 /// _tcsncpy_s(stuAutoRun.szVersion, ArraySize(stuAutoRun.szVersion), wsVersion.c_str(), ArraySize(stuAutoRun.szVersion)-1); /// _tcsncpy_s(stuAutoRun.szCompany, ArraySize(stuAutoRun.szCompany), wsCompany.c_str(), ArraySize(stuAutoRun.szVersion)-1); /// _tcsncpy_s(stuAutoRun.szDesp, ArraySize(stuAutoRun.szDesp), wsDesp.c_str(), ArraySize(stuAutoRun.szVersion)-1); wsNewFilePath.clear(); /// if( _tcslen(stuAutoRun.szName) != 0 || _tcslen(stuAutoRun.szCmdLine) != 0) /// { /// vecAutoItems.push_back(stuAutoRun); /// } } } while (nError != ERROR_NO_MORE_ITEMS); bRet = TRUE; _EXIT_: return bRet; } 3. 通过系统服务(services)

Windows系统中,可以使用服务管理器(SCM,Service Control Manager,对应的进程为services.msc)查看、修改系统服务信息(Win+R输入services.msc打开服务管理器)。如下图所示: 在这里插入图片描述 除此之外,在注册表项中也能查看到系统当前的服务有哪些。注册表路径为:HKLM\\System\\CurrentControlSet\\Services,不但能查看到服务都有哪些,同时也能查看到服务所在路径,启动状态,类型等信息: 注册表下服务信息实例 相对而言,注册表和SCM所展示的重点不同,注册表下的更详细,SCM更贴近用户使用。

对【自启动服务项】来说,查询方式有两种,一种是根据注册表使用前文所提到的注册表项的方式遍历HKLM\\System\\CurrentControlSet\\Services注册表路径,另一种方式就是使用 Win API 提供的接口查询系统服务:

第一种方法前文已说过,读者适当修改即可,这里只罗列第二种方法的逻辑:

使用注册表

特别需要补充的是,在查询注册表项是,会查询到大量的svchost的服务项,这一原因主要是因为windows操作系统中,有一类特殊的服务。它们在运行时,任务管理器中不会显示它们的进程信息,真正的进程内容都被保存到了dll文件中,也就是保存到了一个进程的进程空间中去了,而这个dll则是通过svchost.exe加载的。

另外一个需要注意的就是对查询到的键值项ImagePath需要进一步处理,因为他可能是文件路径、也可能是环境变量路径、也可能是命令行,我们要做的就是提取其中的路径进而得到争取的进程所在路径。

关于svchost的相关说明,可以看👇这篇文章。

【WIN】svchost与共享进程服务

void queryServices(){ HKEY hkeyOuter=nullptr; const TCHAR * const rootMenu=_T("SYSTEM\\CurrentControlSet\\Services\\"); long openResult=::RegOpenKeyEx( HKEY_LOCAL_MACHINE, //根键 rootMenu, // 子键路径 0, &hkeyOuter ); int i=0; // 这里可直接使用前文的 EnumAutoRun remnant = RegEnumKeyEx(hkeyOuter,i,innerFile,&cbMaxSubKey,NULL,NULL,NULL,NULL); //遍历目标文件夹下的每个子文件夹 while (remnant!=ERROR_NO_MORE_ITEMS) { if(RegOpenKeyEx(HKEY_LOCAL_MACHINE,ultraPath,0,KEY_READ,&hkeyInner)==ERROR_SUCCESS){ //查询ImagePath key_log=RegQueryValueEx(hkeyInner,_T("ImagePath"),NULL,&dwType,(BYTE*)&keyData,&dwSize); //查询启动项优先级 long forStatus=RegQueryValueEx(hkeyInner,_T("Start"),NULL,&dwType2,(BYTE*)&statusData,&dwSize2); if(key_log==ERROR_SUCCESS&&forStatus==ERROR_SUCCESS){ if(wcsstr(keyData, L".exe") != NULL&&statusDataGetTasks(NULL, &pTaskCollection); 6. 遍历pTaskCollection中的每个计划任务,并输出其相关信息 for (LONG i = 0; i < numTasks; i++) { IRegisteredTask* pRegisteredTask = NULL; pTaskCollection->get_Item(_variant_t(i + 1), &pRegisteredTask); BSTR taskName = NULL; //任务名称 pRegisteredTask->get_Name(&taskName); TASK_STATE taskState; //运行状态 pRegisteredTask->get_State(&taskState); ITaskDefinition *itaskdef = NULL; pRegisteredTask->get_Definition(&itaskdef); IRegistrationInfo *regInfo = NULL; itaskdef->get_RegistrationInfo(®Info); BSTR taskInfo = NULL, author = NULL; regInfo->get_Description(&taskInfo); //任务描述 regInfo->get_Author(&author); //任务发行者 }

以上就是自启动项计划任务项的相关说明和查询步骤。

6. 通过知名动态链接库

关于dll的相关说明可以自行查阅,在这一部分,我们把dll可以视为是exe的一个部分来处理。也可以查阅《Windows核心编程》一书中第四部分的内容

动态链接库的危险性在于:它使得进程可以调用不属于其可执行代码的函数。函数的可执行代码位于一个 DLL 文件中,该 DLL 包含一个或多个已被编译、链接并与使用它们的进程分开存储的函数。DLL 还有助于共享数据和资源。多个应用程序可同时访问内存中单个 DLL 副本的内容。

相应的,木马编写者可以将攻击函数存放在一个dll文件中,而木马的入口exe文件只是调用有威胁的dll文件,本身没有任何的攻击函数,那么木马的exe文件就可能绕过安全工具的检查。由上述分析易知,动态链接库存在巨大隐患,我们需要查看这些dll文件并分析其安全性。

在注册表中搜索关键字KnownDLL可以找到系统中常用的动态链接库都有哪些,其注册表路径为:HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs 在这里,我们同样可以使用前文的EnumAutoRun接口去查询该路径下的内容,稍作改动即可:

void queryKnownDlls(){ HKEY hkey=nullptr; long openResult=RegOpenKeyExW( //进入注册表文件夹HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Session Manager\\KnownDLLs", 0, KEY_QUERY_VALUE, &hkey ); //遍历所有项目该文件下所有注册表值(其实就是dll文件) long traverseResult=RegEnumValue( hkey,dwIndex,valueName, &len,0,&keyType, keyData,&dataLen ); while (traverseResult==ERROR_SUCCESS) { //随着index不断增大,我们就能遍历每个项目 //value name 记录项目名称,keydata记录数据,keyType记录类型 dwIndex++; traverseResult=RegEnumValue( hkey,dwIndex,valueName, &len,0,&keyType, keyData,&dataLen ); RegCloseKey(hkey); } 7. 通过劫持劫持

映像劫持(Image Hijack,旧称Image File Execution Options,IFEO),是为一些在默认系统环境中运行时可能引发错误的程序执行体提供特殊的环境设定。由于这个项主要是用来调试程序用的,对一般用户意义不大。默认是只有管理员和local system有权读写修改。

映像劫持同样是存储在注册表中,注册表路径为:HKLM\SOFTWARE\Classes\htmlfile\shell\open\command ,依然是通过注册表查询的方式获取其信息。

void queryImageHijack(){ HKEY hkey=nullptr; //进入注册表的目标文件夹HKLM\SOFTWARE\Classes\htmlfile\shell\open\command long openResult=RegOpenKeyExW( HKEY_LOCAL_MACHINE, L"SOFTWARE\\Classes\\htmlfile\\shell\\open\\command", 0, KEY_QUERY_VALUE, &hkey ); //遍历所有项目 long traverseResult=RegEnumValue( hkey,dwIndex,valueName, &len,0,&keyType, keyData,&dataLen ); int row=0; while (traverseResult==ERROR_SUCCESS) { //value name 记录项目名称,keydata记录数据,keyType记录类型 dwIndex++; traverseResult=RegEnumValue( //index不断增加,就能遍历文件夹下的所有项目 hkey,dwIndex,valueName, &len,0,&keyType, keyData,&dataLen ); } RegCloseKey(hkey); }


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3