windows ALPC内核拦截的方法
2021-12-24 16:9:27 Author: www.freebuf.com(查看原文) 阅读量:22 收藏

前言

ALPC挺复杂的,现在的安全软件应该都是在r3下hook services来实现的,比如@FaEry 写的

但是r3下挂会遇到PPL问题,所以才有了今天的文章。

内核可以的挂钩方法

直接内联hook -PG,找指针改地址,做DKOM -安全软件不应该这样做。

由于ALPC_PORT是object回调,因此挂object 的情况下,win7可以,win8以上直接PG并且只能拦得到创

建但是不能拦得到ALPC交换的过程。

IO挂钩 -本文的方法

最开始我是自己逆向ALPC时候偶然发现的,后来搜了一下外网,老外也找到了一个一样的方法,早知道就先搜搜… …

为什么能挂上

以下可以看到

NtAlpcSetInformation->AlpcpInitializeCompletionList->AlpcpAllocateCompletionPacketLookaside

这是一个IO回调,可以通过设置微软允许的IOComplateCallback来做一些操作

怎么挂钩

首先我们要知道内核里面有个全局的双向链表是叫做nt!alpclist 根据zeromem的说法是

虽然这玩意不公开,但是有个公开结构AlpcPortObjectType是跟他在一起的,定位到AlpcPortObjectType再定位到它就行

请注意这是一个坑,因为这个方法win7好使,仅限于win8之前,win8后这玩意就成这样的布局了:

因此我们再也无法通过公开的AlpcPortObjectType再拿到信息了,只能找特征码:

\x48\xCC\xCC\xCC\xCC\xCC\xCC\x48\xCC\xCC\xCC\xCC\xCC\xCC\x48\xCC\xCC\xCC\xCC\xCC\xCC\x48\xCC\xCC\xCC\xCC\xCC\xCC\xCC自己找,别抄.....

那个alpclistlock也是一样,特征码处理

挂钩

我们需要干什么:

1. 遍历alpclist
2. 找到这个ALPC是否是我们的需要挂钩的进程(比如services.exe)
3. 设置IO回调挂钩

首先记得锁一下:

if (_interlockedbittestandset64((__int64*)&gAlpcpPortListLock, 0))
        ExfAcquirePushLockShared((PULONG_PTR)&gAlpcpPortListLock);

然后是遍历这个双向链表

PLIST_ENTRY pLink = NULL;
    for (pLink = gAlpcpPortList->Flink; pLink != (PLIST_ENTRY)&gAlpcpPortList->Flink; pLink = pLink->Flink)
    {

        _ALPC_PORT* alpcPort = CONTAINING_RECORD(pLink, _ALPC_PORT, PortListEntry);

其中ALPC_PORT结构每个版本都在变

struct _ALPC_PORT
{
    struct _LIST_ENTRY PortListEntry;                                       //0x0
    struct _ALPC_COMMUNICATION_INFO* CommunicationInfo;                     //0x10
    struct _EPROCESS* OwnerProcess;                                         //0x18
    VOID* CompletionPort;                                                   //0x20
    VOID* CompletionKey;                                                    //0x28
    struct _ALPC_COMPLETION_PACKET_LOOKASIDE* CompletionPacketLookaside;    //0x30
    VOID* PortContext;                                                      //0x38
    struct _SECURITY_CLIENT_CONTEXT StaticSecurity;                         //0x40
    struct _LIST_ENTRY MainQueue;                                           //0x88
    struct _LIST_ENTRY PendingQueue;                                        //0x98
    struct _LIST_ENTRY LargeMessageQueue;                                   //0xa8
    struct _LIST_ENTRY WaitQueue;                                           //0xb8
    union
    {
        struct _KSEMAPHORE* Semaphore;                                      //0xc8
        struct _KEVENT* DummyEvent;                                         //0xc8
    };
    struct _ALPC_PORT_ATTRIBUTES PortAttributes;                            //0xd0
    struct _EX_PUSH_LOCK Lock;                                              //0x118
    struct _EX_PUSH_LOCK ResourceListLock;                                  //0x120
    struct _LIST_ENTRY ResourceListHead;                                    //0x128
    struct _ALPC_COMPLETION_LIST* CompletionList;                           //0x138
    struct _ALPC_MESSAGE_ZONE* MessageZone;                                 //0x140
    struct _CALLBACK_OBJECT* CallbackObject;                                //0x148
    VOID* CallbackContext;                                                  //0x150
    struct _LIST_ENTRY CanceledQueue;                                       //0x158
    volatile LONG SequenceNo;                                               //0x168
    union
    {
        struct
        {
            ULONG Initialized : 1;                                            //0x16c
            ULONG Type : 2;                                                   //0x16c
            ULONG ConnectionPending : 1;                                      //0x16c
            ULONG ConnectionRefused : 1;                                      //0x16c
            ULONG Disconnected : 1;                                           //0x16c
            ULONG Closed : 1;                                                 //0x16c
            ULONG NoFlushOnClose : 1;                                         //0x16c
            ULONG ReturnExtendedInfo : 1;                                     //0x16c
            ULONG Waitable : 1;                                               //0x16c
            ULONG DynamicSecurity : 1;                                        //0x16c
            ULONG Wow64CompletionList : 1;                                    //0x16c
            ULONG Lpc : 1;                                                    //0x16c
            ULONG LpcToLpc : 1;                                               //0x16c
            ULONG HasCompletionList : 1;                                      //0x16c
            ULONG HadCompletionList : 1;                                      //0x16c
            ULONG EnableCompletionList : 1;                                   //0x16c
        } s1;                                                               //0x16c
        ULONG State;                                                        //0x16c
    } u1;                                                                   //0x16c
    struct _ALPC_PORT* TargetQueuePort;                                     //0x170
    struct _ALPC_PORT* TargetSequencePort;                                  //0x178
    struct _KALPC_MESSAGE* volatile CachedMessage;                          //0x180
    ULONG MainQueueLength;                                                  //0x188
    ULONG PendingQueueLength;                                               //0x18c
    ULONG LargeMessageQueueLength;                                          //0x190
    ULONG CanceledQueueLength;                                              //0x194
    ULONG WaitQueueLength;                                                  //0x198
};

之后判断进程合法性:

if (!alpcPort->OwnerProcess ||
            PsGetProcessId((PEPROCESS)alpcPort->OwnerProcess) != TargetPrcessId ||
            !alpcPort->CompletionPort)
            continue;

然后就是设置回调了

void* IoMiniCompletPtr = IoAllocateMiniCompletionPacket(ALPC_NotifyCallback, alpcPort);
        if (IoMiniCompletPtr == NULL)
        {
            __debugbreak();
            return;
        }
        IoSetIoCompletionEx(
            alpcPort->CompletionPort,
            alpcPort->CompletionKey,
            nullptr,
            NULL,
            NULL,
            FALSE,
            IoMiniCompletPtr);

这个IoAllocateMiniCompletionPacket和他的结构我是自己IDA逆向推导出来的,因为没公开,没任何可靠的信息

struct _IO_MINI_COMPLETION_PACKET_USER
{
    struct _LIST_ENTRY ListEntry;                                           //0x0
    ULONG PacketType;                                                       //0x10
    VOID* KeyContext;                                                       //0x18
    VOID* ApcContext;                                                       //0x20
    LONG IoStatus;                                                          //0x28
    ULONGLONG IoStatusInformation;                                          //0x30
    VOID(*MiniPacketCallback)(struct _IO_MINI_COMPLETION_PACKET_USER* arg1, VOID* arg2); //0x38
    VOID* Context;                                                          //0x40
    UCHAR Allocated;                                                        //0x48
};
/*
这两玩意是我IDA逆向看的,微软也没说
PAGE:00000001402C807C                                     IoAllocateMiniCompletionPacket proc near
PAGE:00000001402C807C                                                     ; CODE XREF: AlpcpAllocateCompletionPacketLookaside+82↑p
PAGE:00000001402C807C                                                     ; NtCreateWorkerFactory+179↓p
PAGE:00000001402C807C                                                     ; DATA XREF: ...
PAGE:00000001402C807C
PAGE:00000001402C807C                                     arg_0           = qword ptr  8
PAGE:00000001402C807C
PAGE:00000001402C807C 48 89 5C 24 08                                      mov     [rsp+arg_0], rbx
PAGE:00000001402C8081 57                                                  push    rdi
PAGE:00000001402C8082 48 83 EC 20                                         sub     rsp, 20h
PAGE:00000001402C8086 48 8B DA                                            mov     rbx, rdx
PAGE:00000001402C8089 33 D2                                               xor     edx, edx
PAGE:00000001402C808B 48 8B F9                                            mov     rdi, rcx
PAGE:00000001402C808E 8D 4A 03                                            lea     ecx, [rdx+3]
PAGE:00000001402C8091 E8 AA 9C 03 00                                      call    IopAllocateMiniCompletionPacket
PAGE:00000001402C8096 48 85 C0                                            test    rax, rax ; 【rax = 分配的io包结构】
PAGE:00000001402C8099 74 0C                                               jz      short loc_1402C80A7
PAGE:00000001402C809B 48 89 78 38                                         mov     [rax+38h], rdi ; 【0x38 = _IO_MINI_COMPLETION_PACKET_USER->MiniPacketCallback】
PAGE:00000001402C809F 48 89 58 40                                         mov     [rax+40h], rbx ; 【0x40 = _IO_MINI_COMPLETION_PACKET_USER->Context】
PAGE:00000001402C80A3 C6 40 48 01                                         mov     byte ptr [rax+48h], 1
PAGE:00000001402C80A7
PAGE:00000001402C80A7                                     loc_1402C80A7:  ; CODE XREF: IoAllocateMiniCompletionPacket+1D↑j
PAGE:00000001402C80A7 48 8B 5C 24 30                                      mov     rbx, [rsp+28h+arg_0]
PAGE:00000001402C80AC 48 83 C4 20                                         add     rsp, 20h
PAGE:00000001402C80B0 5F                                                  pop     rdi
PAGE:00000001402C80B1 C3                                                  retn
PAGE:00000001402C80B1                                     IoAllocateMiniCompletionPacket endp
rax = 一个指针
*/
typedef void(__fastcall* MINIPACKETCALLBACK)(
    __in _IO_MINI_COMPLETION_PACKET_USER* miniPacket,
    __inout void* context
    );
extern "C" {
    NTKERNELAPI
        void*
        NTAPI IoAllocateMiniCompletionPacket(
            __in MINIPACKETCALLBACK miniPacketCallback,
            __in const void* context
        );

    NTKERNELAPI
        void
        NTAPI IoSetIoCompletionEx(
            __inout void* completitionPort,
            __in const void* keyContext,
            __in const void* apcContext,
            __in ULONG_PTR ioStatus,
            __in ULONG_PTR ioStatusInformation,
            __in bool allocPacketInfo,
            __in const void* ioMiniCoompletitionPacketUser
        );

    NTSYSAPI
        NTSTATUS
        NTAPI
        ZwQuerySystemInformation(
            IN SYSTEM_INFORMATION_CLASS SystemInformationClass,
            OUT PVOID SystemInformation,
            IN ULONG SystemInformationLength,
            OUT PULONG ReturnLength OPTIONAL
        );

    NTSYSAPI PIMAGE_NT_HEADERS NTAPI RtlImageNtHeader(
        _In_ PVOID Base
    );
    NTKERNELAPI
        void*
        NTAPI IoFreeMiniCompletionPacket(
            __in const void* miniPacket
        );
}

挂钩后,我们顺利得到信息

解码ALPC_PORT信息

ALPC这个只是一个标准协议,每个不同的服务比如 创建服务与创建账号与搜索系统信息等的具体内容都是不同的,要自己手动解码。但是在这里如zeroman所说,这个keycontext带了一个叫做SubProcessTag的东西,这个东西就对应这个RPC所带的服务(其实不是ALPC传过来的),就可以拿到基本的,某进程发了RPC给某进程,并且RPC是关于xxx服务的,但是服务的具体信息比如名字啥的。

具体解码禁止抄作业
其他的参考:
https://bbs.pediy.com/thread-268225.htm#msg_header_h1_7
https://blog.csdn.net/weixin_43787608/article/details/84555474
https://bbs.pediy.com/thread-251158.htm
http://www.zer0mem.sk/?p=542


文章来源: https://www.freebuf.com/articles/317365.html
如有侵权请联系:admin#unsafe.sh