首页
社区
课程
招聘
[原创]内核中获取DNS解析的源头发起方进程方案
发表于: 3天前 232

[原创]内核中获取DNS解析的源头发起方进程方案

3天前
232

论坛里好多朋友一直在询问如何在内核中比较准确的获取DNS实际发起方的进程,这里给大家抛砖引玉介绍一下我自己的方案,这个方案也有失败几率但是与成功率相比那就忽略了。整个方案的核心思路是在WFP层获取发送的DNS包(目标端口是53,且是UDP协议,基本认为是DNS请求包,然后在做自己的包解析确认是不是),明确了是发送的DNS包之后,接下来就是重点了:借助ALPC线程枚举,找到在等待ALPC应答的线程,通过线程反推PID,获取目标进程。在这个WFP层获取的发包PID不是我们想要的进程pid,这个PID一般都是svchost的进程,就是下面这个:

实际上应用程序在发起DNS解析时,是通过本地ALPC将相关信息发送给了这个DNS解析与缓存服务,随后统一由这个进程进行解析,如果缓存里有了,就发送新的请求了,如果缓存里没有就执行一下UDP请求。在我们的实际业务场景中(网络检测与拦截)我们需要获得的是真正发起这个DNS请求的进程,而不是这个svchost的代理进程。先看下效果,再贴代码:

逻辑图如下所示(逻辑图描述了整个方案的实现思路):

核心代码与结构定义如下(代码是从业务中抠出来的,进行了删减,参考核心实现 GetKernelDNS_Service 即可):

typedef struct _ALPC_PORT ALPC_PORT, * PALPC_PORT;
typedef struct _KALPC_MESSAGE KALPC_MESSAGE, * PKALPC_MESSAGE;

typedef struct _ALPC_HANDLE_ENTRY {
    PVOID	Object;
}ALPC_HANDLE_ENTRY, * PALPC_HANDLE_ENTRY;

typedef struct _ALPC_HANDLE_TABLE {
    PALPC_HANDLE_ENTRY	Handles;
    UINT16	TotalHandles;
    UINT16	Flags;
    EX_PUSH_LOCK Lock;
}ALPC_HANDLE_TABLE, * PALPC_HANDLE_TABLE;


typedef struct _KALPC_MESSAGE_ATTRIBUTES {
    PVOID	ClientContext;		// + 0x000
    PVOID	ServerContext;		// + 0x008
    PVOID	PortContext;		// + 0x010
    PVOID	CancelPortContext;	// + 0x018
    PVOID	SecurityData;		// + 0x020
    PVOID	View;				// + 0x028
    PVOID	HandleData;			// + 0x030
}KALPC_MESSAGE_ATTRIBUTES, * PKALPC_MESSAGE_ATTRIBUTES;

typedef struct _ALPC_COMMUNICATION_INFO {
    PALPC_PORT	ConnectionPort;
    PALPC_PORT	ServerCommunicationPort;
    PALPC_PORT	ClientCommunicationPort;
    LIST_ENTRY	CommunicationList;
    ALPC_HANDLE_TABLE	HandleTable;
}ALPC_COMMUNICATION_INFO, * PALPC_COMMUNICATION_INFO;


typedef struct _KALPC_MESSAGE {
    LIST_ENTRY	Entry;					// + 0x000
    PVOID		ExtensionBuffer;		// + 0x010
    ULONG64		ExtensionBufferSize;	// + 0x018

    //	union X{							// + 0x020
    PVOID	QuotaProcess;			// + 0x020
    //		PVOID	QuptaBlock;				// + 0x020
    //	};
    ULONG	SequenceNo;					// + 0x028
    ULONG	u1;							// + 0x02C
    PVOID	CancelSequencePort;			// + 0x030
    PVOID	CancelQueuePort;			// + 0x038
    PVOID	CancelSequenceNo;			// + 0x040
    LIST_ENTRY	CancelListEntry;		// + 0x048
    PETHREAD	WaitingThread;			// + 0x058
    PVOID	Reserve;					// + 0x060
    PALPC_PORT	PortQueue;				// + 0x068
    PALPC_PORT	OwnerPort;				// + 0x070
    KALPC_MESSAGE_ATTRIBUTES MessageAttributes;		// + 0x078
    PVOID		DataUserVa;				// + 0x0B0
    PVOID		DataSystemVa;			// + 0x0B8
    PALPC_COMMUNICATION_INFO CommunicationInfo;	// + 0x0C0
    PALPC_PORT	ConnectionPort;			// + 0x0C8
    PETHREAD	ServerThread;			// + 0x0D0
    PORT_MESSAGE PortMessage;			// + 0x0D8
}KALPC_MESSAGE, * PKALPC_MESSAGE;


typedef struct _ALPC_COMPLETION_LIST {
    LIST_ENTRY	Entry;			// + 0x000
    PEPROCESS	OwnerProcess;	// + 0x010
    PMDL		Mdl;			// + 0x018
    PVOID		UserVa;			// + 0x020
    PVOID		UserLimit;		// + 0x028
    PVOID		DataUserVa;		// + 0x030
    PVOID		SystemVa;		// + 0x038
    UINT64		TotalSize;		// + 0x040
    PVOID		Header;			// + 0x048
    PVOID		List;			// + 0x050
    UINT64		ListSize;		// + 0x058
    PVOID		Bitmap;			// + 0x060
    PVOID		BitmapSize;		// + 0x068
    PVOID		Data;			// + 0x070
    UINT64		DataSize;		// + 0x078
    UINT32		BitmapLimit;	// + 0x080
    UINT32		BitmapNextHint;	// + 0x084
    UINT32		ConcurrencyCount;	// + 0x088
    UINT32		AttributeFlags;	// + 0x08C
    UINT32		AttributeSize;	// + 0x090
}ALPC_COMPLETION_LIST, * PALPC_COMPLETION_LIST;
typedef struct _ALPC_PORT_WIN10 {
    LIST_ENTRY	PortListEntry;					// + 0x000
    PVOID		CommunicationInfo;				// + 0x010
    PEPROCESS	OwnerProcess;					// + 0x018
    PVOID		CompletionPort;					// + 0x020
    PVOID		CompletionKey;					// + 0x028
    PVOID		CompletionPacketLookaside;		// + 0x030
    PVOID		PortContext;					// + 0x038
    SECURITY_CLIENT_CONTEXT StaticSecurity;		// + 0x040
    EX_PUSH_LOCK	IncomingQueueLock;			// + 0x088
    LIST_ENTRY		MainQueue;					// + 0x090
    LIST_ENTRY		LargeMessageQueue;			// + 0x0A0
    EX_PUSH_LOCK	PendingQueueLock;			// + 0x0B0
    LIST_ENTRY		PendingQueue;				// + 0x0B8
    EX_PUSH_LOCK	DirectQueueLock;			// + 0x0C8
    LIST_ENTRY		DirectQueue;				// + 0x0D0
    EX_PUSH_LOCK	WaitQueueLock;				// + 0x0E0
    LIST_ENTRY		WaitQueue;					// + 0x0E8

    union
    {
        KSEMAPHORE* Semaphore;					// + 0x0F8
        KEVENT* DummyEvent;					    // + 0x0F8
    };

    ALPC_PORT_ATTRIBUTES PortAttributes;        // + 0x100
    EX_PUSH_LOCK	ResourceListLock;           // + 0x148
    LIST_ENTRY		ResourceListHead;           // + 0x150
    EX_PUSH_LOCK	PortObjectLock;             // + 0x160
    ALPC_COMPLETION_LIST* CompletionList;		// + 0x168
    PVOID			CallbackObject;             // + 0x170
    VOID* CallbackContext;			            // + 0x178
    LIST_ENTRY		CanceledQueue;				// + 0x180
    LONG			SequenceNo;				    // + 0x190
    LONG			ReferenceNo;				// + 0x194
    PVOID			ReferenceNoWait;			// + 0x198
    union
    {
        struct
        {
            ULONG Initialized : 1;                                            //0x1a0
            ULONG Type : 2;                                                   //0x1a0
            ULONG ConnectionPending : 1;                                      //0x1a0
            ULONG ConnectionRefused : 1;                                      //0x1a0
            ULONG Disconnected : 1;                                           //0x1a0
            ULONG Closed : 1;                                                 //0x1a0
            ULONG NoFlushOnClose : 1;                                         //0x1a0
            ULONG ReturnExtendedInfo : 1;                                     //0x1a0
            ULONG Waitable : 1;                                               //0x1a0
            ULONG DynamicSecurity : 1;                                        //0x1a0
            ULONG Wow64CompletionList : 1;                                    //0x1a0
            ULONG Lpc : 1;                                                    //0x1a0
            ULONG LpcToLpc : 1;                                               //0x1a0
            ULONG HasCompletionList : 1;                                      //0x1a0
            ULONG HadCompletionList : 1;                                      //0x1a0
            ULONG EnableCompletionList : 1;                                   //0x1a0
        } s1;                                                               //0x1a0
        ULONG State;                                                        //0x1a0
    } u1;                                                                   //0x1a0
    PVOID TargetQueuePort;                                     //0x1a8  PALPC_PORT_10
    PVOID TargetSequencePort;                                  //0x1b0  PALPC_PORT_10
    KALPC_MESSAGE* CachedMessage;                                   //0x1b8
    ULONG MainQueueLength;                                                  //0x1c0
    ULONG LargeMessageQueueLength;                                          //0x1c4
    ULONG PendingQueueLength;                                               //0x1c8
    ULONG DirectQueueLength;                                                //0x1cc
    ULONG CanceledQueueLength;                                              //0x1d0
    ULONG WaitQueueLength;                                                  //0x1d4
}_ALPC_PORT_10, * PALPC_PORT_10;
typedef struct _KALPC_MESSAGE_WIN10 {
    LIST_ENTRY	ListEntry;				// + 0x000
    PALPC_PORT	PortQueue;				// + 0x010
    PALPC_PORT	OwnerPort;				// + 0x018
    PETHREAD	WaitingThread;			// + 0x020
}KALPC_MESSAGE_10, * PKALPC_MESSAGE_10;



//辅助工具函数

//内存相关
PVOID kMalloc(SIZE_T size)
{
    PVOID pTmp = (PVOID)ExAllocatePoolWithTag(NonPagedPool, size, 'pmt9');
    if (pTmp)
    {
        RtlZeroMemory(pTmp, size);
    }
    return pTmp;
}
VOID kFree(PVOID pMem)
{
    if (pMem)
    {
        ExFreePoolWithTag(pMem, '0GAT');
        pMem = 0;
    }
}

BOOLEAN StringUnicodeToAnsi(PUNICODE_STRING pUnicode, char* szAnsiBuf, int nABuflen)
{
    ANSI_STRING ansiBuf;
    BOOLEAN		bResult = FALSE;
    ansiBuf.Buffer = (char*)kMalloc(PAGE_SIZE);
    ansiBuf.MaximumLength = PAGE_SIZE;
    int iLen = pUnicode->Length;
    wchar_t* cBuf = pUnicode->Buffer;
    //  LOG("unicode info %d %p\n", iLen, cBuf);
    if (MmIsAddressValid((PVOID)pUnicode) && MmIsAddressValid((PVOID)pUnicode->Buffer) && MmIsAddressValid(szAnsiBuf))
    {
        auto Status = RtlUnicodeStringToAnsiString(&ansiBuf, (PUNICODE_STRING)pUnicode, FALSE);
        if (NT_SUCCESS(Status))
        {
            if (ansiBuf.Length < nABuflen)
            {
                memcpy(szAnsiBuf, ansiBuf.Buffer, ansiBuf.Length);
                bResult = TRUE;
            }
        }
    }
    kFree(ansiBuf.Buffer);
    return bResult;
}

//获取使用DNS服务的进程信息
BOOLEAN GetKernelDNSServiceProc(char* szProcessName, int nLen, int& iPid)
{
    BOOLEAN bFind = FALSE;

    PVOID			PortObject = NULL;
    PEPROCESS		pProcess = NULL;

    //Win10
    PKALPC_MESSAGE_10 pAlpcMessage10 = NULL;
    PALPC_PORT_10 pAlpcPort10 = NULL;

    UNICODE_STRING	objName;
    NTSTATUS		Status;
    RtlZeroMemory(szProcessName, nLen);

    //
    //	ntsvcs是services.exe的服务管理端
    //  
    RtlInitUnicodeString(&objName, L"\\RPC Control\\DNSResolver");

    Status = ObReferenceObjectByName(
        &objName, OBJ_CASE_INSENSITIVE, NULL, 0,
        *LpcPortObjectType, KernelMode, NULL, (PVOID*)&PortObject);

    if (!NT_SUCCESS(Status))
    {
        DbgPrint("ObReferenceObjectByName failed:%x\n", Status);
        return FALSE;
    }


    //
    //	从ntsvcs端口的pending列表尾部找到等待服务返回结果的客户端线程
    //
    __try
    {
        if (true)//自己做系统判断与其他条件判断
        {
            pAlpcPort10 = (PALPC_PORT_10)PortObject;
            if (!IsListEmpty(&pAlpcPort10->PendingQueue))
            {
                pAlpcMessage10 = (PKALPC_MESSAGE_10)pAlpcPort10->PendingQueue.Blink;
                if (MmIsAddressValid(pAlpcMessage10->WaitingThread))
                {
                    PALPC_PORT_10 pTarAlpcPort = (PALPC_PORT_10)pAlpcMessage10->OwnerPort;//OwnerPort 是发起方的APLC对象
                    pProcess = IoThreadToProcess(pAlpcMessage10->WaitingThread);

                    if (MmIsAddressValid(pProcess))
                    {
                        iPid = (int)PsGetProcessId(pProcess);
                        auto uTid = (ULONG32)PsGetThreadId(pAlpcMessage10->WaitingThread);

						// 获取进程名
                        if (pProcess)
                        {
                            WCHAR* buffer = NULL;
                            PFILE_OBJECT FileObject = NULL;
                            if (NT_SUCCESS(PsReferenceProcessFilePointer((PEPROCESS)pProcess, &FileObject)))
                            {
                                char cName[512] = { 0 };
                                StringUnicodeToAnsi(&FileObject->FileName, cName, sizeof(cName));

                                strcpy(szProcessName, cName);
                                if (FileObject)
                                {
                                    ObDereferenceObject(FileObject);
                                }
                            }
                        }
                        bFind = TRUE;
                    }
                }
            }
        }
       
    }
    __except (EXCEPTION_EXECUTE_HANDLER)
    {
        DbgPrint("---> 域名解析信息 exception----\n");
    }

    if (pAlpcPort10)
        ObDereferenceObject(pAlpcPort10);

    return bFind;
}

对于其他同类型发起方进程的获取,其实现的方法类似,例如:驱动加载方进程的获取(这玩意在MiniFilter的IRP_MJ_ACQUIRE_FOR_SECTION_SYNCHRONIZATION中获取或者是在ImageNotifyCallBack中获取的时候默认拿到的进程是services.exe,也不是真实的进程)。


本文旨在分享自己的技术实现手段,帮助其他存在疑问的朋友,同时抛砖引玉,看看其他朋友有没有什么更好的方案来做补充。


[培训]Windows内核深度攻防:从Hook技术到Rootkit实战!

最后于 3天前 被沧海浮萍_编辑 ,原因:
收藏
免费 2
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回