Windows Notification Facility (WNF)组件的详细介绍

栏目: Android · 发布时间: 5年前

内容简介:前言关于Windows Notification Facility (WNF)组件,或许我和你一样,从来不知道这个组件,并且互联网上关于该组件的信息也很少。能查到的也只是如下信息:Windows Notification Facility(WNF)是一个不是很知名的内核组件,在整个系统中的主要任务就是用于通知,相当于通知中心。它既可以在内核模式中使用,也可以在用户空间 (USER-LAND)中使用,其中包含一组导出的但显然没有文档化的API函数和相关的数据结构。应用程序可以订阅特定类型的事件(由State

前言

关于Windows Notification Facility (WNF)组件,或许我和你一样,从来不知道这个组件,并且互联网上关于该组件的信息也很少。能查到的也只是如下信息:

14. Reverse engineer the following Windows kernel functions.
    The result of this exercise will be used in the next exercise.

    • ExSubscribeWnfStateChange
    • ExQueryWnfStateData

15. Write a driver that gets notified when an application is using the microphone.
    Print its pid.

Hints:
    • check contentDeliveryManager_Utilities.dll for the WNF StateName.
    • some interesting info is available here:

http://redplait.blogspot.com/2017/08/wnf-ids-from-perfntcdll.html

Windows Notification Facility(WNF)是一个不是很知名的内核组件,在整个系统中的主要任务就是用于通知,相当于通知中心。它既可以在内核模式中使用,也可以在用户空间 (USER-LAND)中使用,其中包含一组导出的但显然没有文档化的API函数和相关的数据结构。应用程序可以订阅特定类型的事件(由StateName标识),以便在每次发生状态更改时进行通知(可以与StateData关联)。另一方面,发布通知的组件负责提供与通知一起发送的数据并触发事件。

应该注意的是,WNF状态名称可以实例化(Scope)到单个进程,silo(Windows容器)或整个设备中。例如,如果应用程序在silo内运行,则只会通知其自身容器内发生的SiloScope内的事件。

数据结构

WNF中涉及很多数据结构,以下就是它们在内存中关系的简化图。

Windows Notification Facility (WNF)组件的详细介绍

WNF_NAME_INSTANCE结构在内存中表示事件或WNF状态名称的实例,这些结构在二叉树中排序,并链接到事件发生的scope,scope属性(使用 scope 属性,可以将数据单元格与表头单元格联系起来。)确定组件能够查看或访问的信息,它们还支持为相同的状态名实例化不同的数据。

有五种可能的scope类型,定义如下。

typedef enum _WNF_DATA_SCOPE{
  WnfDataScopeSystem = 0x0,
  WnfDataScopeSession = 0x1,
  WnfDataScopeUser = 0x2,
  WnfDataScopeProcess = 0x3,
  WnfDataScopeMachine = 0x4,} WNF_DATA_SCOPE;

使用WNF_SCOPE_INSTANCE结构标识的作用域按类型存储在双向链接列表中,并且它们的表头保存在特定于silo的WNF_SCOPE_MAP中。

当组件订阅WNF状态名称时,将创建新的WNF_SUBSCRIPTION结构并将其添加到属于关联的WNF_NAME_INSTANCE的链接列表中。如果通知订阅者使用的是低级API(例如下面描述的API),则会在WNF_SUBSCRIPTION中添加回调,并在需要通知组件时调用。

WNF_PROCESS_CONTEXT对象会跟踪特定通知订阅者进程涉及的所有不同结构。它还存储用于通知进程的KEVENT。可以通过EPROCESS对象访问此上下文,也可以通过抓取nt!ExpWnfProcessesListHead指向的双向链表来访问此上下文,你可以在下面找到这些连接的示意图。

Windows Notification Facility (WNF)组件的详细介绍

0x906指的是什么呢?它与WNF使用的所有结构都有一个描述结构类型和大小的小头(Windows文件系统相关数据结构中常见的情况)有关。

typedef struct _WNF_CONTEXT_HEADER{
    CSHORT NodeTypeCode;
    CSHORT NodeByteSize;} WNF_CONTEXT_HEADER, *PWNF_CONTEXT_HEADER;

这个头文件在调试时非常方便,因为很容易发现内存中的对象,下面是一些WNF结构的节点类型代码。

#define WNF_SCOPE_MAP_CODE          ((CSHORT)0x901)
#define WNF_SCOPE_INSTANCE_CODE     ((CSHORT)0x902)
#define WNF_NAME_INSTANCE_CODE      ((CSHORT)0x903)
#define WNF_STATE_DATA_CODE         ((CSHORT)0x904)
#define WNF_SUBSCRIPTION_CODE       ((CSHORT)0x905)
#define WNF_PROCESS_CONTEXT_CODE    ((CSHORT)0x906)

反转函数

掌握了这些背景知识后,让我们开始学习如何使用该组件,第一部分实际上是反转下列函数,以便了解其目的:

·ExSubscribeWnfStateChange

· ExQueryWnfStateData

ExSubscribeWnfStateChange

NTSTATUSExSubscribeWnfStateChange (
    _Out_ptr_ PWNF_SUBSCRIPTION* Subscription,
    _In_ PWNF_STATE_NAME StateName,
    _In_ ULONG DeliveryOption,
    _In_ WNF_CHANGE_STAMP CurrentChangeStamp,
    _In_ PWNF_CALLBACK Callback,
    _In_opt_ PVOID CallbackContext
    );

ExSubscribeWnfStateChange允许在WNF引擎中注册新的订阅,它将StateName作为参数之一,指定我们感兴趣的事件类型,并在触发通知时调用回调函数。它还会返回一个新的订阅指针,该指针可用于查询与通知关联的数据。

在内部,此函数仅将执行流转移到private counterpart(ExpWnfSubscribeWnfStateChange),由它处理所有进程。

由于WNF状态名称以不透明格式存储,因此ExpWnfSubscribeWnfStateChange首先使用ExpCaptureWnfStateName解码ID的“清洁”版本。

这个“清洁”的WNF状态名称可以解码如下:

#define WNF_XOR_KEY 0x41C64E6DA3BC0074

ClearStateName = StateName ^ WNF_XOR_KEY;
Version = ClearStateName & 0xf;
LifeTime = (ClearStateName >> 4) & 0x3;
DataScope = (ClearStateName >> 6) & 0xf;
IsPermanent = (ClearStateName >> 0xa) & 0x1;
Unique = ClearStateName >> 0xb;

要是格式再正规一点,解码的结构则如下所示:

typedef struct _WNF_STATE_NAME_INTERNAL
{
    ULONG64 Version : 4;
    ULONG64 Lifetime : 2;
    ULONG64 DataScope : 4;
    ULONG64 IsPermanent : 1;
    ULONG64 Unique : 53;
} WNF_STATE_NAME_INTERNAL, *PWNF_STATE_NAME_INTERNAL;

然后,ExpWnfSubscribeWnfStateChange调用ExpWnfResolveScopeInstance。后者检索Server Silo Globals(或者在没有服务器silo的情况下为nt!PspHostSiloGlobals)并通过几个结构来查找名称实例所属的WNF_SCOPE_INSTANCE。如果此Scope实例(Scope Instance)不存在,则会创建该Scope并将其添加到相应的WNF_SCOPE_MAP列表中,如下所示。

Windows Notification Facility (WNF)组件的详细介绍

在此Scope实例结构中,ExpWnfSubscribeWnfStateChange搜索(使用ExpWnfLookupNameInstance)与给定WNF状态名称匹配的WNF_NAME_INSTANCE。

Windows Notification Facility (WNF)组件的详细介绍

如果没有找到匹配项,则使用ExpWnfCreateNameInstance创建一个新的WNF_NAME_INSTANCE,此新实例将添加到以WNF_SCOPE_INSTANCE为根的二叉树中。

该函数的下一步是调用ExpWnfSubscribeNameInstance来创建新的订阅对象。如上所述,此对象将保存引擎所需的所有信息以触发通知。

最后,ExpWnfSubscribeWnfStateChange调用ExpWnfNotifySubscription将新的订阅插入等待队列(pending queue)并触发通知。

ExQueryWnfStateData

NTSTATUS
ExQueryWnfStateData (
    _In_ PWNF_SUBSCRIPTION Subscription,
    _Out_ PWNF_CHANGE_STAMP ChangeStamp,
    _Out_ PVOID OutputBuffer,
    _Out_ OPULONG OutputBufferSize
);

这个函数非常简单,因为它只执行两个操作。首先,它使用ExpWnfAcquireSubscriptionNameInstance从订阅中检索WNF_NAME_INSTANCE。然后,它使用ExpWnfReadStateData读取存储在其中的数据,并尝试将其复制到缓冲区中。如果缓冲区太小,它将只写入OuputBufferSize所需的大小,并返回STATUS_BUFFER_TOO_SMALL。

为了记录,所有WNF状态名称都将其数据存储在WNF_STATE_DATA结构下的内存中。该结构包含各种元数据,例如数据大小和它被更新的次数(称为ChangeStamp)。指向WNF_STATE_DATA的指针直接保存在WNF_NAME_INSTANCE中,如下所示。

Windows Notification Facility (WNF)组件的详细介绍

另外,我还可以将WNF状态名称标记为持久性(persistent),这意味着数据(和更改标记)将在重新启动时被保留(显然是通过使用辅助数据存储)。有关详细信息,请 点此 了解。

编码过程

在我掌握了函数的反转后,就应该能够注册一个新的订阅,并通知任何其他合法的应用使用WNF。

但是,此时我仍然缺少一个元素,就是找到microphone类输入所需的WNF状态名。

本文,我只会详细介绍与WNF交互相关的驱动程序。如果你对Windows上的驱动程序开发感兴趣,你可能需要查看Windows驱动程序 工具 包文档及其示例。

寻找合适的WNF状态名

至于如何检索WNF状态名称,我也是阅读了此 博客 ,并了解了库名称(contentDeliveryManager_Utilities.dll)。

在这篇文章中,Redplait定义了WNF使用的几个状态名。不幸的是,我正在寻找的那个状态名却没有被列出。然而,这仍然给了我一个很好的启发,因为我最起码知道了WNF状态名是什么样的。

经过摸索,找到我们正在寻找的WNF状态名的一个简单的方法就是对contentDeliveryManager_Utilities.dll中的其中一个WNF状态名称实施grep,以此找到其他关联的ID,幸运的是,这个方法非常有效!通过对IDA中匹配模式的交叉引用,我们可以得到DLL中引用的WNF状态名的完整列表。这个列表中的每个条目都自带其名称和描述,这对我们来说非常方便! (此列表由GetWellKnownWnfStateByName使用)。

Windows Notification Facility (WNF)组件的详细介绍

我们现在只需要找一个特定的microphone类既可:

.rdata:00000001800E3680  dq offset WNF_AUDC_CAPTURE
.rdata:00000001800E3688  dq offset aWnf_audc_captu ; "WNF_AUDC_CAPTURE"
.rdata:00000001800E3690  dq offset aReportsTheNu_0 ; "Reports the number of, and process ids "...
// “Reports the number of, and process ids of all applications currently capturing audio.
//  Returns a WNF_CAPTURE_STREAM_EVENT_HEADER data structure”

应该注意的是,Windows性能分析器附带的“perf_nt_c.dll“库也可以使用相同的表。

订阅事件

要订阅一个新事件,我们只需要在我们的驱动程序中使用我们从上面找到的WNF状态名称调用ExSubscribeWnfStateChange既可。此函数虽然已导出,但还未在任何标题中进行定义,因此我们必须通过粘贴上面的定义来对它作出说明。请注意,ntoskrnl.lib包含导入库存根,所以不需要手动检索它的地址。

此时,唯一需要做的就是使用正确的参数调用函数:

NTSTATUSCallExSubscribeWnfStateChange (
    VOID
    ){
    PWNF_SUBSCRIPTION wnfSubscription= NULL;
    WNF_STATE_NATE stateName;
    NTSTATUS status;

    stateName.Data = 0x2821B2CA3BC4075; // WNF_AUDC_CAPTURE

    status = ExSubscribeWnfStateChange(&wnfSubscription, &stateName, 0x1, NULL, &notifCallback, NULL);
    if (NT_SUCCESS(status)) DbgPrint("Subscription address: %p\n", Subscription_addr);

    return status;}

回调机制的定义

正如我们之前看到的,ExSubscribeWnfStateChange在其参数中包含一个回调机制,每次触发事件时都会调用该回调,此回调将用于获取和处理与通知相关的事件数据。

回调原型如下所示:

NTSTATUSnotifCallback (
    _In_ PWNF_SUBSCRIPTION Subscription,
    _In_ PWNF_STATE_NAME StateName,
    _In_ ULONG SubscribedEventSet,
    _In_ WNF_CHANGE_STAMP ChangeStamp,
    _In_opt_ PWNF_TYPE_ID TypeId,
    _In_opt_ PVOID CallbackContext
    );

要获得回调中的数据,我们必须调用ExQueryWnfStateDataName。同样,这个函数是导出的,但没有在任何标头中定义,所以我们必须自己定义。

NTSTATUSExQueryWnfStateData (
    _In_ PWNF_SUBSCRIPTION Subscription,
    _Out_ PWNF_CHANGE_STAMP CurrentChangeStamp,
    _Out_writes_bytes_to_opt_(*OutputBufferSize, *OutputBufferSize) PVOID OutputBuffer,
    _Inout_ PULONG OutputBufferSize
    );[...]

我们需要调用此API两次,一次是为了获得为数据分配缓冲区所需的大小,另一次是为了实际检索数据。

NTSTATUSnotifCallback(...){
    NTSTATUS status = STATUS_SUCCESS;
    ULONG bufferSize = 0x0;
    PVOID pStateData;
    WNF_CHANGE_STAMP changeStamp = 0;

    status = ExQueryWnfStateDataFunc(Subscription, &changeStamp, NULL, &bufferSize);
    if (status != STATUS_BUFFER_TOO_SMALL) goto Exit;

    pStateData = ExAllocatePoolWithTag(PagedPool, bufferSize, 'LULZ');
    if (pStateData == NULL) {
        status = STATUS_UNSUCCESSFUL;
        goto Exit;
    }

    status = ExQueryWnfStateDataFunc(Subscription, &changeStamp, pStateData, &bufferSize);
    if (NT_SUCCESS(status)) DbgPrint("## Data processed: %S\n", pStateData);

    [...] // do stuff with the dataExit:
    if (pStateData != nullptr) ExFreePoolWithTag(pStateData, 'LULZ');
    return status;}

卸载驱动程序时记得将其清理干净

如果你盲目的尝试上面的代码,你得到将是一个死机的蓝屏状态,这是我在第一次尝试练习并卸载我的驱动程序时所得到的结果。请注意,我们需要提前删除订阅。

为此,我们可以在驱动程序卸载例程中调用ExUnsubscribeWnfStateChange,并确保将PWNF_SUBSCRIPTION wnfSubscription设置为全局变量。

PVOIDExUnsubscribeWnfStateChange (
    _In_ PWNF_SUBSCRIPTION Subscription
    );VOIDDriverUnload (
    _In_ PDRIVER_OBJECT DriverObject
    ){
   [...]
   ExUnsubscribeWnfStateChange(g_WnfSubscription);}

失败的实践尝试

我们现在要做的就是在驱动程序中,启用Cortana,用它来触发事件。等了一会儿,触发还是没有响应,因为我忘记了我的虚拟机上没有声卡,可能是我无法启动任何声音相关应用程序的原因吧。最重要的是,由于我的主机配置,我根本无法让它工作。

尽管如此,为了确保我的驱动程序正常工作,我必须选择另一个事件,并由于我的主机配置,我根本无法让它工作并开始使用WNF_SHEL_DESKTOP_APPLICATION_STARTED。每当桌面应用程序启动时,都会发出此通知。作为反应,它只输出已启动的应用程序名称。有了这个WNF状态名,就很容易得到一些结果。

Windows Notification Facility (WNF)组件的详细介绍

与WNF保持同步

我在上文展示了一种查找WNF名称的简单方法,只需在其中一个包含该表的DLL中搜索IDA中的名称,更可靠和可扩展的方法是通过解析DLL来查找表并转储它来检索WNF状态名。虽然这不是我在练习中使用的方法,但是为了能够轻松跟上WNF状态名称(添加/删除/修改)的变化,我制作了一个脚本。此脚本可用于区分两个DLL并快速获取表中的差异,以及从单个DLL转储表数据。除此之外,他也可以很容易的适用于其他C和 Python 程序。

$ python .\StateNamediffer.py -h
usage: StateNamediffer.py [-h] [-dump | -diff] [-v] [-o OUTPUT] [-py]
file1 [file2]

Stupid little script to dump or diff wnf name table from dll

positional arguments:
    file1
    file2 Only used when diffing

optional arguments:
    -h, --help show this help message and exit
    -dump Dump the table into a file
    -diff Diff two tables and dump the difference into a file
    -v, --verbose Print the description of the keys
    -o OUTPUT, --output OUTPUT Output file (Default: output.txt)

输出示例:

typedef struct _WNF_NAME{
    PCHAR Name;
    ULONG64 Value;} WNF_NAME, *PWNF_NAME;WNF_NAME g_WellKnownWnfNames[] ={
    {"WNF_A2A_APPURIHANDLER_INSTALLED", 0x41877c2ca3bc0875}, // An app implementing windows.AppUriHandler contract has been installed
    {"WNF_AAD_DEVICE_REGISTRATION_STATUS_CHANGE", 0x41820f2ca3bc0875}, // This event is signalled when device changes status of registration in Azure Active Directory.
    {"WNF_AA_CURATED_TILE_COLLECTION_STATUS", 0x41c60f2ca3bc1075}, // Curate tile collection for all allowed apps for current AssignedAccess account has been created
    {"WNF_AA_LOCKDOWN_CHANGED", 0x41c60f2ca3bc0875}, // Mobile lockdown configuration has been changed
    [...]}

总结

经过这次的实践,我终于有机会深入研究一个我根本不知道的内核组件,该组件非常有趣。我学到了很多东西,并且非常喜欢弄清楚如何使用WNF的过程。


以上所述就是小编给大家介绍的《Windows Notification Facility (WNF)组件的详细介绍》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Essential ActionScript 3.0

Essential ActionScript 3.0

Colin Moock / Adobe Dev Library / June 22, 2007 / $34.64

ActionScript 3.0 is a huge upgrade to Flash's programming language. The enhancements to ActionScript's performance, feature set, ease of use, cleanliness, and sophistication are considerable. Essentia......一起来看看 《Essential ActionScript 3.0》 这本书的介绍吧!

html转js在线工具
html转js在线工具

html转js在线工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具