<input id="eiiko"></input>
<menu id="eiiko"><blockquote id="eiiko"></blockquote></menu>
  • <bdo id="eiiko"><samp id="eiiko"></samp></bdo>
  • Windows内核扩展机制研究

    2019-03-26 42522人围观 ,发现 1 个不明物体 系统安全

    近期,我自己写了一个内核模式驱动器,跟我平时写的很多代码一样,这个驱动器也存在Bug。 确切来说,这个驱动器可以防止其他驱动器正常加载,并导致系统崩溃。

    ?#23548;?#19978;,很多启动器会默认它们的初始化进程(DriverEntry)能够成功执行,因此在出现问题时它们是无法正确处理的。j00ru曾在几年前发布的博文中提过几种案例,而且其中有很多都跟目前的Windows版本有关,而我只对其中的一个驱动器比较?#34892;?#36259;。这里最吸引我的还是“Windows内核主机扩展机制?#20445;?#26412;文的主要内容也是关于它的。

    这个驱动器是Bam.sys(Background Activity Moderator)- Windows 10 v1709(RS3)中新引入的一种驱动器。当它的DriverEntry中途出现问题时,调用栈会导致系统发生崩溃:

    从崩溃信息中我们可以看到,Bam.sys注册了一个进程创建回调,并且在卸载之前忘记注销它了。接下来,当一个进程被创建/终止时,系统会尝试调用这个回调,并引用旧指针,然后导致系统崩溃。

    有趣的并不是系统发生崩溃,而是Bam.sys到?#36164;?#22914;何注册这个回调的。一般来说,进程创建回调是通过nt!PsSetCreateProcessNotifyRoutine(Ex)注册?#27169;?#23427;会向nt!PspCreateProcessNotifyRoutine数组中添加这个回调。接下来,当进程被创建/终止时,nt!PspCallProcessNotifyRoutines会迭代循环这个数组,并调用所有已注册的回调。然而,如果我们在WinDbg中运行“!wdbgark.wa_systemcb /typeprocess“的话,我们将会看到这个数组中并不存在Bam.sys所使用的回调:

    原来,Bam.sys使用了另一种机制来注册它的回调:

    4.png

    5.png

    查看nt!PspCallProcessNotifyRoutines后我们发现,其中有一个指向nt!PspBamExtensionHost的显示引用。它会使用这种“扩展主机”来检索所谓的“扩展表?#20445;?#24182;调用扩展表中的第一个函数,即bam!BampCreateProcessCallback。

    在IDA?#20889;?#24320;Bam.sys,我们可以直接看到bam!BampCreateProcessCallback,并搜索它的xrefs。幸好它只有一个,就在bam!BampRegisterKernelExtension中:

    我怀疑Bam!BampCreateProcessCallback并没有通过常规的回调注册机制来进行注册。?#23548;?#19978;,它被存在了一个名叫Bam!BampKernelCalloutTable的函数表中,而这个表会和其他参数一起传递给nt!ExRegisterExtension函数(文档中未记载)。

    唯一一个我?#19994;?#30340;跟这个函数相关的就是ntosifs.h头文件,其中包含了nt!ExRegisterExtension函数的原型以及_EX_EXTENSION_REGISTRATION_1结构体。

    nt!ExRegisterExtension函数原型和_EX_EXTENSION_REGISTRATION_1:

    NTKERNELAPINTSTATUS ExRegisterExtension (

        _Outptr_ PEX_EXTENSION *Extension,

        _In_ ULONG RegistrationVersion,

        _In_ PVOID RegistrationInfo

    );

     

    typedefstruct _EX_EXTENSION_REGISTRATION_1 {

        USHORT ExtensionId;

        USHORT ExtensionVersion;

        USHORT FunctionCount;

        VOID *FunctionTable;

        PVOID *HostInterface;

        PVOID DriverObject;

    }EX_EXTENSION_REGISTRATION_1, *PEX_EXTENSION_REGISTRATION_1;

    注意:nt!ExRegisterExtension的伪代码可在附录B中?#19994;健?/p>

    我们可以看到,nt!ExRegisterExtension会搜索名称匹配RegistrationInfo的主机。那么问题来了,这些主机是从哪儿来的呢?

    在初始化的过程中,NTOS会多次调用nt!ExRegisterHost。?#30475;?#35843;用时,它都会传递一个结构体来识别列表中预定义的驱动器(完整列表可在附录A中?#19994;劍?#27604;如说,Bam.sys会使用下列调用来初始化一台主机:

    7.png

    nt!ExRegisterHost会分配一个type_HOST_LIST_ENTRY结构体,并使用调用方提供的参数来进行初始化,然后将其添加到nt!ExpHostList结尾。_HOST_LIST_ENTRY结构体文档未记载,它的大致结构如下:

    struct_HOST_LIST_ENTRY

    {

        _LIST_ENTRY List;

        DWORD RefCount;

        USHORT ExtensionId;

        USHORT ExtensionVersion;

        USHORT FunctionCount; // number ofcallbacks that the extension

                              // contains

        POOL_TYPE PoolType;   // where this host is allocated

        PVOID HostInterface; // table of unexportednt functions,

                             // to be used by thedriver to which

                             // this extensionbelongs

        PVOID FunctionAddress; // optional, rarelyused.

                               // This callback iscalled before

                               // and after anextension for this

                               // host isregistered / unregistered

        PVOID ArgForFunction; // will be sent tothe function saved here

        _EX_RUNDOWN_REF RundownRef;

        _EX_PUSH_LOCK Lock;

        PVOID FunctionTable; // a table of thecallbacks that the

                             // driver “registers”

        DWORD Flags;         // Only uses one bit.

                             // Not sure about itsmeaning.

    }HOST_LIST_ENTRY, *PHOST_LIST_ENTRY;

    当其中一个预定义的驱动器加载时,它会使用nt!ExRegisterExtension注册一个扩展,并提供一个RegistrationInfo结构体,其中包括一个函数表。这个函数表会被置入匹配主机的FunctionTable成员中。这些函数随后会被NTOS在特定的场景下调用,这也就是所谓的回调了。

    之前我们看到了nt!ExRegisterExtension的部?#27490;?#33021;会被设置为RegistrationInfo->HostInterface(包含一个全?#30452;?#37327;),并指向主机结构体中的某些数据。

    每一个注册了扩展的驱动器?#21152;?#19968;个NTOS设置的初始化主机,这种主机包含一个HostInterface,它指向HTOS函数预定一个一个表,不同的驱动器会收到不同的HostInterface,但?#34892;?#21017;不会收到。

    比如说,Bam.sys收到的就是下面这个HostInterface:

    ?#23548;?#19978;,这里所谓的“内核扩展机制”即使一种双向通信端口:驱动器提供一个“回调”列表,以供在不同场景下调用,然后在各自内部使用场景中接收不同的函数集。

    下面给出的是Bam.sys的回调样本:

    BampCreateProcessCallback

    BampSetThrottleStateCallback

    BampGetThrottleStateCallback

    BampSetUserSettings

    BampGetUserSettingsHandle

    负责Bam.sys初始化的主机预先“知道”它需要接收包含上面这5个函数的函数表。这些函数必须按照上述顺序提供,因此这里是通过索引来调用的。我们可以看到,nt!PspBamExtensionHost->FunctionTable[4]调?#20204;?#20917;:

    总而言之,Windows?#20889;?#22312;一种机制,可以用来“扩展”NTOS,具体过程是先注册某些回调函数,然后接收驱动可以使用的未导出函数。我并不清楚这个知识点是否能发挥?#23548;首?#29992;,但觉得这方面内容比较有趣,?#26723;?#19982;大家分享。希望可以给大家提供帮助,?#34892;?#38405;读!

    附录A-NTOS初始化的扩展主机

    10.png

    附录B-函数伪代码

    ExRegisterExtension.c:

    NTSTATUSExRegisterExtension(_Outptr_ PEX_EXTENSION *Extension, _In_ ULONGRegistrationVersion, _In_ PREGISTRATION_INFO RegistrationInfo)

    {

           // Validate that version is ok and thatFunctionTable is not sent without FunctionCount or vise-versa.

           if ( (RegistrationVersion &0xFFFF0000 != 0x10000) || (RegistrationInfo->FunctionTable == nullptr&& RegistrationInfo->FunctionCount != 0) )

           {

                  return STATUS_INVALID_PARAMETER;

           }

     

           // Skipping over some lock-related stuff,

          

           // Find the host with the matchingversion and id.

           PHOST_LIST_ENTRY pHostListEntry;

           pHostListEntry =ExpFindHost(RegistrationInfo->ExtensionId,RegistrationInfo->ExtensionVersion);

     

           // More lock-related stuff.     

     

           if (!pHostListEntry)

           {

                  return STATUS_NOT_FOUND;

           }

     

           // Verify that the FunctionCount in thehost doesn't exceed the FunctionCount supplied by the caller.

           if (RegistrationInfo->FunctionCount< pHostListEntry->FunctionCount)

           {

           ExpDereferenceHost(pHostListEntry);

                  return STATUS_INVALID_PARAMETER;

           }

     

           // Check that the number of functions inFunctionTable matches the amount in FunctionCount.

           PVOID FunctionTable =RegistrationInfo->FunctionTable;

           for (int i = 0; i <RegistrationInfo->FunctionCount; i++)

           {     

                  if (RegistrationInfo->FunctionTable[i] == nullptr )

                  {

                  ExpDereferenceHost(pHostListEntry);

                         returnSTATUS_ACCESS_DENIED;

                  }

           }

     

           // skipping over some more lock-relatedstuff

     

           // Check if there is already an extensionregistered for this host.

           if (pHostListEntry->FunctionTable !=nullptr || FlagOn(pHostListEntry->Flags, 1) )

           {

                  // There is something related tolocks here

           ExpDereferenceHost(pHostListEntry);

                  returnSTATUS_OBJECT_NAME_COLLISION;

           }

     

           // If there is a callback function forthis host, call it before registering the extension, with 0 as the firstparameter.

           if (pHostListEntry->FunctionAddress)

           {

           pHostListEntry->FunctionAddress(0,pHostListEntry->ArgForFunction);

           }

     

           // Set the FunctionTable in the host tothe table supplied by the caller, or to MmBadPointer if a table wasn'tsupplied.

           if (RegistrationInfo->FunctionTable ==nullptr)

           {

                  pHostListEntry->FunctionTable= nt!MmBadPointer;

           }

           else

           {

                  pHostListEntry->FunctionTable= RegistrationInfo->FunctionTable;

           }

     

           pHostListEntry->RundownRef = 0;

     

           // If there is a callback function forthis host, call it after registering the extension, with 1 as the firstparameter.

           if (pHostListEntry->FunctionAddress)

           {

           pHostListEntry->FunctionAddress(1,pHostListEntry->ArgForFunction);

           }

     

           // Here there is some more lock-relatedstuff

     

           // Set the HostTable of the callingdriver to the table of functions listed in the host.

           if (RegistrationInfo->HostTable !=nullptr)

           {

           *(PVOID)RegistrationInfo->HostTable= pHostListEntry->hostInterface;

           }

     

           // Return the initialized host to thecaller in the output Extension parameter.

           *Extension = pHostListEntry;

           return STATUS_SUCCESS;

    }

    ExRegisterHost.c:

    NTSTATUSExRegisterHost(_Out_ PHOST_LIST_ENTRY ExtensionHost, _In_ ULONG Unused, _In_PHOST_INFORMATION HostInformation)

    {

           NTSTATUS Status = STATUS_SUCCESS;

     

           // Allocate memory for a newHOST_LIST_ENTRY

           PHOST_LIST_ENTRY p =ExAllocatePoolWithTag(HostInformation->PoolType, 0x60, 'HExE');

           if (p == nullptr)

           {

                  returnSTATUS_INSUFFICIENT_RESOURCES;

           }

     

           //

           // Initialize a new HOST_LIST_ENTRY

           //

           p->Flags &= 0xFE;

           p->RefCount = 1;

           p->FunctionTable = 0;

           p->ExtensionId =HostInformation->ExtensionId;

           p->ExtensionVersion =HostInformation->ExtensionVersion;

           p->hostInterface =HostInformation->hostInterface;

           p->FunctionAddress =HostInformation->FunctionAddress;

           p->ArgForFunction =HostInformation->ArgForFunction;                    

           p->Lock = 0;                   

           p->RundownRef = 0;             

     

           // Search for an existing listEntry withthe same version and id.

           PHOST_LIST_ENTRY listEntry = ExpFindHost(HostInformation->ExtensionId,HostInformation->ExtensionVersion);

           if (listEntry)

           {

                  Status =STATUS_OBJECT_NAME_COLLISION;

                  ExpDereferenceHost(p);

                  ExpDereferenceHost(listEntry);

           }

           else

           {

                  // Insert the new HOST_LIST_ENTRYto the end of ExpHostList.

                  if ( *lastHostListEntry !=&firstHostListEntry )

                  {

                        __fastfail();

                  }

     

                  firstHostListEntry->Prev =&p;

                  p->Next = firstHostListEntry;

                  lastHostListEntry = p;

     

                  ExtensionHost = p;

           }

          

           return Status;

    }

    ExpFindHost.c:

    PHOST_LIST_ENTRYExpFindHost(USHORT ExtensionId, USHORT ExtensionVersion)

    {

           PHOST_LIST_ENTRY entry;

           for (entry == ExpHostList; ; entry =entry->Next)

           {

                  if (entry == &ExpHostList)

                  {

                         return 0;

                  }

                 

                  if ( *(entry->ExtensionId) ==ExtensionId && *(entry->ExtensionVersion) == ExtensionVersion )

                  {

                         break;

                  }

           }

    InterlockedIncrement(entry->RefCount);

           return entry;

    }

    ExpDereferenceHost.c:

    voidExpDereferenceHost(PHOST_LIST_ENTRY Host)

    {

          if (InterlockedExchangeAdd(Host.RefCount, 0xFFFFFFFF) == 1 )

           {

                      ExFreePoolWithTag(Host, 0);

           }

    }

    附录C-结构体定义

    struct_HOST_INFORMATION

    {

        USHORT ExtensionId;

        USHORT ExtensionVersion;

        DWORD FunctionCount;

        POOL_TYPE PoolType;

        PVOID HostInterface;

        PVOID FunctionAddress;

        PVOID ArgForFunction;

        PVOID unk;

    }HOST_INFORMATION, *PHOST_INFORMATION;

     

    struct_HOST_LIST_ENTRY

    {

        _LIST_ENTRY List;

        DWORD RefCount;

        USHORT ExtensionId;

        USHORT ExtensionVersion;

        USHORT FunctionCount; // number ofcallbacks that the

                              // extension contains

        POOL_TYPE PoolType;   // where this host is allocated

        PVOID HostInterface;  // table of unexported nt functions,

                              // to be used by thedriver to which

                              // this extensionbelongs

        PVOID FunctionAddress; // optional, rarelyused.

                               // This callback iscalled before and   

                               // after anextension for this host

                               // is registered /unregistered

        PVOID ArgForFunction; // will be sent tothe function saved here

        _EX_RUNDOWN_REF RundownRef;

        _EX_PUSH_LOCK Lock;

        PVOID FunctionTable;    // a table of the callbacks that

                                // the driver“registers”

    DWORDFlags;                // Only uses oneflag.

                                // Not sure about its meaning.

    }HOST_LIST_ENTRY, *PHOST_LIST_ENTRY;;

     

    struct_EX_EXTENSION_REGISTRATION_1

    {

        USHORT ExtensionId;

        USHORT ExtensionVersion;

        USHORT FunctionCount;

        PVOID FunctionTable;

        PVOID *HostTable;

        PVOID DriverObject;

    }EX_EXTENSION_REGISTRATION_1,*PEX_EXTENSION_REGISTRATION_1;

    * 参考来源:medium,FB小编Alpha_h4ck编译,转载请注明来自FreeBuf.COM

    发表评论

    已有 1 条评论

    取消
    Loading...
    css.php 天津11选5开奖
    <input id="eiiko"></input>
    <menu id="eiiko"><blockquote id="eiiko"></blockquote></menu>
  • <bdo id="eiiko"><samp id="eiiko"></samp></bdo>
  • <input id="eiiko"></input>
    <menu id="eiiko"><blockquote id="eiiko"></blockquote></menu>
  • <bdo id="eiiko"><samp id="eiiko"></samp></bdo>