【Day 24】上百种 Provider 任意选,这样的 ETW 你喜欢吗 - ETW 监控 Process

环境

  • Windows 10 21H1
  • Visual Studio 2019

前情提要

【Day 23】为美好的 Windows 献上 ETW - Event Tracing for Windows 提到对於蓝队而言,ETW 是个有效监控程序行为的方式。最後也有使用 Windows 内建工具 - logman 进行 Process 的监控,然而 logman 只是单纯将事件写入档案,无法弹性的分析抓取的事件,因此这一篇将会介绍如何自己写程序利用 ETW 抓取事件并进行分析。

在讲解 ETW 时,有说 ETW 是由 Controller、Consumer、Provider 三个角色组成,这篇使用的 Provider 是 Windows 内建的 Microsoft-Windows-Kernel-Process。

准备工作

首先,Microsoft 有开发一个开源专案 - KrabsETW,可以很方便的建立 Session 收取事件。这篇主要会使用这个专案写 POC。

当然这个专案的原理也是呼叫 Windows API,不过 KrabsETW 将这个流程简化并统整,如果有想要直接学习原始的 API 怎麽用的,可以参考 MSDN 的范例

不常碰 Windows 开发的朋友可以先建一下 vcpkg 的环境。这篇会用到的 Library 有两个,分别是刚刚提到的 KrabsETW 和 JSON。

vcpkg.exe install nlohmann-json:x64-windows
vcpkg.exe install krabsetw:x64-windows

任务讲解

这次使用的 Provider 是 Microsoft-Windows-Kernel-Process,GUID 为 {22FB2CD6-0E7B-422B-A0C7-2FAD1FD0E716}。用上一篇提到的工具 - logman 看一下详细资讯。

# logman query providers Microsoft-Windows-Kernel-Process

Provider                                 GUID
-------------------------------------------------------------------------------
Microsoft-Windows-Kernel-Process         {22FB2CD6-0E7B-422B-A0C7-2FAD1FD0E716}

Value               Keyword              Description
-------------------------------------------------------------------------------
0x0000000000000010  WINEVENT_KEYWORD_PROCESS
0x0000000000000020  WINEVENT_KEYWORD_THREAD
0x0000000000000040  WINEVENT_KEYWORD_IMAGE
0x0000000000000080  WINEVENT_KEYWORD_CPU_PRIORITY
0x0000000000000100  WINEVENT_KEYWORD_OTHER_PRIORITY
0x0000000000000200  WINEVENT_KEYWORD_PROCESS_FREEZE
0x0000000000000400  WINEVENT_KEYWORD_JOB
0x0000000000000800  WINEVENT_KEYWORD_ENABLE_PROCESS_TRACING_CALLBACKS
0x0000000000001000  WINEVENT_KEYWORD_JOB_IO
0x0000000000002000  WINEVENT_KEYWORD_WORK_ON_BEHALF
0x0000000000004000  WINEVENT_KEYWORD_JOB_SILO
0x8000000000000000  Microsoft-Windows-Kernel-Process/Analytic

Value               Level                Description
-------------------------------------------------------------------------------
0x04                win:Informational    Information

PID                 Image
-------------------------------------------------------------------------------
0x00000000


The command completed successfully.

一个 Provider 可能会提供多种事件,每个事件又有对应的 Event ID。每个 Event ID 的版本会提供的资料栏位已经有人整理在 Windows10EtwEvents。这一篇的目标会放在 Event ID 1 的事件,也就是 ProcessStart。

实作

实作流程

  1. 建立 Session
  2. 设定 Provider 资讯
  3. 设置 Callback 处理事件,其中会把栏位 Parse 好
  4. 设定 Session 从目标 Provider 抓取事件
  5. 启用 Session

POC

程序专案可以参考我的 GitHub zeze-zeze/2021iThome

#include <krabs.hpp>
#include <nlohmann/json.hpp>
#include <iostream>
#include <vector>

// ws2s: 将型别 wstring 转 string
std::string ws2s(const std::wstring& wstr) {
	const std::string str(wstr.begin(), wstr.end());
	return str;
}

// byte2uint32: 将型别 vector<Byte> 转为 uint32
uint32_t byte2uint32(std::vector<BYTE> v) {
    uint32_t res = 0;
    for (int i = (int)v.size() - 1; i >= 0; i--) {
        res <<= 8;
        res += (uint32_t)v[i];
    }
    return res;
}

// byte2uint64: 将型别 vector<BYTE> 转为 uint64
uint64_t byte2uint64(std::vector<BYTE> v) {
    uint64_t res = 0;
    for (int i = 7; i >= 0; i--) {
        res <<= 8;
        res += (uint64_t)v[i];
    }
    return res;
}

// callback: 当 Session 从 Provider 拿到任何事件时会呼叫
void callback(const EVENT_RECORD& record, const krabs::trace_context& trace_context) {
    // 取得事件模型并 Parse
    krabs::schema schema(record, trace_context.schema_locator);
    krabs::parser parser(schema);
    
    // 如果是 ProcessStart 事件才处理
    if (schema.event_id() == 1) {
        // 将事件中的每个栏位 Parse 後转成需要的型别并存放到 json 中
        nlohmann::json data;
        data["ProcessID"] = byte2uint32(parser.parse<krabs::binary>(L"ProcessID").bytes());
        data["CreateTime"] = byte2uint64(parser.parse<krabs::binary>(L"CreateTime").bytes());
        data["ParentProcessID"] = byte2uint32(parser.parse<krabs::binary>(L"ParentProcessID").bytes());
        data["SessionID"] = byte2uint32(parser.parse<krabs::binary>(L"SessionID").bytes());
        data["ImageName"] = ws2s(parser.parse<std::wstring>(L"ImageName"));
        
        // 将 json 资料印出,参数 4 让印出来的资料比较容易看
        std::cout << data.dump(4) << std::endl;
    }
}

int main(int argc, const char* argv[]) {
    // 1. 建立 Session
    krabs::user_trace session(L"ETW_example");
    
    // 2. 设定 Provider 资讯
    krabs::provider<> provider(L"Microsoft-Windows-Kernel-Process");
    provider.any(0x10); // 这是上一篇讲解过的 flags
    
    // 3. 设置 Callback 处理事件
    provider.add_on_event_callback(callback);

    // 4. 设定 Session 从目标 Provider 抓取事件
    session.enable(provider);

    // 5. 启用 Session
    session.start();
}

实际测试

在执行 POC 後,Cmd 应该会挂着,将执行的 Process 资讯印出来。以执行 explorer.exe 为例,执行结果应大致如下,实际输出会根据系统状态不同而改变

{
    "CreateTime": 132744674520123800,
    "ImageName": "\\Device\\HarddiskVolume5\\Windows\\explorer.exe",
    "ParentProcessID": 17640,
    "ProcessID": 14236,
    "SessionID": 2
}

参考资料


<<:  Day30完赛心得!

>>:  Day 25 去识别化定义规划实作

Re: 新手让网页 act 起来: Day01 - 一起来认识 React 吧!

前言 哈喽!大家好,开赛前先来简单的自我介绍。前阵子因为公司专案的关系,开始接触 React,想要藉...

Proxmox VE 安装虚拟机:Windows 10 (一)

当我们把几个必备的基础前置作业处理完成後,可以来开始准备建立第一个虚拟机客体,正式体验 Proxm...

day 7 - grpc server 初始 proto测试

我有一个放着很多测试程序的资料夹,每次要用新的套件或测试逻辑的时候, 为了要排除其他影响因素, 我会...

Teachable Machine (TM)

前面有提到TinyML 然後我就看到这个了 Teachable Machine(TM) 这网页可以收...

Day 23 | 使用ManoMotion制作打地鼠游戏Part1 - 手部侦测及地鼠设定

在上一篇文章介绍了ManoMotion的安装与介绍,今天我们要使用ManoMotion来制作打地鼠游...