/ Windows / 125浏览

Windows 任务管理器是怎么启用/禁用应用自启动的

不想看分析过程的,可以直接跳转到最后的汇总。

背景

通常我们要增加一个自启动,会在 SOFTWARE\Microsoft\Windows\CurrentVersion\Run 中添加一个项。如果需要禁用,很正常的思路是从次注册表中移除,然后保存到另外一个地方,待下次启动后,再把这一项添加回来(autoruns 就是这么干的)

但这样会有个问题,当被禁用(从注册表中移除)后,就相当于又被移除出去了,这样在任务管理器中就无法看到这个应用,而不是显示此应用被禁用。

看了下任务管理器是怎么做的,发现其并没有移除原注册表项,也实现了禁用的效果。

接下来就来研究下它是怎么实现的。

分析

开始上工具分析:

首先我们先用 Process Monitor 来看看禁用/启用自启动项的时候,任务管理器干了啥。

Process Monitor 初步分析

Process Monitor 可以在 https://live.sysinternals.com/ 中下载到。

打开 Process Monitor,并在过滤器中选择【Process Name】,并选择【taskmgr.exe】,如下图所示:

然后我们就可以在任务管理器上禁止一个应用,然后马上停止收集。这里以向日葵(SunloginClient.exe)为例。就可以看到如下的信息:

我们可以关注到HKLMSOFTWAREMicrosoftWindowsCurrentVersionExplorerStartupApprovedRunSunloginClient 这个注册表项。

再开启、禁用。发现此项确实有被修改。

注册表项值

从 Process Monitor 和 Regedit 中我们可以看到,这个项是一个二进制的值。这就有点麻烦了。

我们分别收集启用和禁用情况下的值:

启用时:

禁用时:

很明显,我们还是没法知道这些值都代表什么意思。

于是,用上我们的搜索大法:

发现一个相对比较有用的回答:

链接:

到这里,我们其实只了解了前两位的值有什么用。但后面的值,是没有信息的。

于是,我们就需要用逆向的手段来做进一步的分析了。

主要可以从静态分析和动态分析来入手。这里以静态分析为例:

IDA Pro

静态分析上,我们可以通过 ida pro 来进行分析。

首先我们需要找到突破口,根据之前的注册表,尝试搜索【StartupApproved】,如下图:

于是就搜到了 ,跳转到那块区域:

从上图中,我们可以看到,其被引用在 StartupDB::StartupApproval::Initialize ,微软居然还提供了 pdb,太棒了。

于是我们就可以在函数界面,搜索下 StartupDB::StartupApproval 相关的方法了。

可以看到,里面包含 SetBlockedItemInfoGetBlockedItemInfo 。点进去看看:

SetBlockedItemInfo 为例,可以看到,基本没啥逻辑,就是写了一下注册表。

于是,接下来找到调用它的人。通过 xref 可以看到,主要就是 WdcStartupMonitor::DisableItemWdcStartupMonitor::EnableItem 。跳转到 WdcStartupMonitor::DisableItem

因为我们主要看看其逻辑,所以我们直接看伪代码即可。按 F5 转到 伪代码界面(当然,这里也是可以将汇编代码发给 Chatgpt,让它帮忙解释下是什么意思。也很好)

由于代码比较多,这里我们看看重点代码:

从代码中可以看到:

SystemTimeAsFileTime 主要分两部分:

  1. SystemTimeAsFileTime[0] :低 32 位就是上述提到的低两位(上述,02 为开启,03 为禁用)
  2. SystemTimeAsFileTime[1] :高 64 位是 FileTime,也就是当前的系统时间

总共 12 位,也可以通过 SetBlockedItemInfo 的伪代码来验证。

于是,我们基本搞清楚了注册表中的信息是什么。

验证

参考代码如下:

Zeus::WinRegistry reg(item->rootKey, item->regPath, false);

TaskmgrAutorunsRegData data;
std::vector<char> val;

try
{
    if (isEnable && !IsEnableType(item->type)) {
        data.type = item->type - 1;
        memset(&data.fileTime, 0, sizeof(FILETIME));
    } else if (!isEnable && IsEnableType(item->type) ) {
        data.type = item->type | 1;
        GetSystemTimeAsFileTime(&data.fileTime);
    }
    else {
        return true;
    }

    char* charData = reinterpret_cast<char*>(&data);

    for (int i = 0; i < sizeof(TaskmgrAutorunsRegData); i++) {
        val.push_back(charData[i]);
    }
    reg.SetBinary(item->name, val);

    return true;
}

// TaskmgrAutorunsRegData 定义
struct TaskmgrAutorunsRegData {
    // 01-禁用启动,具有管理员权限的用户可以启用
    // 02-已启用启动,具有管理员权限的用户可以禁用
    // 08-已启用启动,用户无法禁用(灰色显示)
    // 09-禁用启动,用户无法启用(灰色显示)
    int32_t type;
    FILETIME fileTime;
};

修改 SunloginClient.exe 的自启动后,可以通过任务管理器查看到禁用时间。

重启后,也不会自己启动。

于是,我们就达成目的了。

汇总

获取自启动列表

主要在如下注册表中枚举:

  • "SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\StartupApproved\Run”
  • "SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\StartupApproved\Run32”
  • "SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\StartupApproved\StartupFolder”

包含 HKEY_LOCAL_MACHINE HKEY_CURRENT_USER 两个。

判断特定项是否开启自启动

获取 TaskmgrAutorunsRegData 的 type 是 2 或者是 8

设置特定项目的自启动状态

Zeus::WinRegistry reg(item->rootKey, item->regPath, false);

TaskmgrAutorunsRegData data;
std::vector<char> val;

try
{
    if (isEnable && !IsEnableType(item->type)) {
        data.type = item->type - 1;
        memset(&data.fileTime, 0, sizeof(FILETIME));
    } else if (!isEnable && IsEnableType(item->type) ) {
        data.type = item->type | 1;
        GetSystemTimeAsFileTime(&data.fileTime);
    }
    else {
        return true;
    }

    char* charData = reinterpret_cast<char*>(&data);

    for (int i = 0; i < sizeof(TaskmgrAutorunsRegData); i++) {
        val.push_back(charData[i]);
    }
    reg.SetBinary(item->name, val);

    return true;
}

// TaskmgrAutorunsRegData 定义
struct TaskmgrAutorunsRegData {
    // 01-禁用启动,具有管理员权限的用户可以启用
    // 02-已启用启动,具有管理员权限的用户可以禁用
    // 08-已启用启动,用户无法禁用(灰色显示)
    // 09-禁用启动,用户无法启用(灰色显示)
    int32_t type;
    FILETIME fileTime;
};

后续

后续研究还有几个方向:

  1. 这里只分析了注册表的自启动应用,还有 UWP 应用,怎么枚举,怎么设置?
  2. 这里只分析了,基于注册表有值的情况下的修改。系统是怎么判断什么时候是 2,什么时候是 8,以及是否还有其他类型?
如何通过 C++ 实时监听 ETW 事件
如何通过 C++ 实时监听 ETW 事件
【译】调查并确定 Windows 运行速度变慢问题
【译】调查并确定 Windows 运行速度变慢问题
【译】丢失的 WPA 文档 —— 磁盘使用
【译】丢失的 WPA 文档 —— 磁盘使用
【译】丢失的 WPA 文档 —— CPU 调度
【译】丢失的 WPA 文档 —— CPU 调度
【译】丢失的 WPA 文档 —— CPU 采样
【译】丢失的 WPA 文档 —— CPU 采样
如何通过 PDH(Performance Data Helper) 获取性能计数器的值

0

  1. This post has no comment yet

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注