-
-
[原创]内核中获取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实战!