/ Windows / 258浏览

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,以及是否还有其他类型?
Windows是如何区分互联网下载文件和本地文件的
Windows是如何区分互联网下载文件和本地文件的
如何低成本的获取到应用卡顿情况
如何低成本的获取到应用卡顿情况
【译】ETW 堆跟踪 – 每个分配都被记录
【译】ETW 堆跟踪 – 每个分配都被记录
【译】Wait Analysis – 寻找空闲时间
【译】Wait Analysis – 寻找空闲时间
如何通过 ETW Provider 来记录应用日志
如何通过 ETW Provider 来记录应用日志
如何通过 C++ 实时监听 ETW 事件
如何通过 C++ 实时监听 ETW 事件

0

  1. This post has no comment yet

发表回复

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