首页
论坛
课程
招聘
[原创]win10 1909逆向(ALPC通信原理浅析)
2021-6-25 14:48 8489

[原创]win10 1909逆向(ALPC通信原理浅析)

2021-6-25 14:48
8489

0x0 前言


       参考书籍《深入解析 Windows 操作系统》第六版,网上资料少且部分错误,另外很多内核书籍是以 WRK的LPC 原型来讲解,但 Vista 后LPC代码被移除,仅保留 LPC 的接 口,参考意义不大。


       逆向ALPC通信原理仅作为我入职公司的压力测试,所以逆向时间有限,由于 ALPC 设计较复杂,目前仅以 ALPC 简单的通信为基础逆向得到下面的观点,如果有错误,那我也没有法子,哇哈哈。


0x1 基础

      Windows 需要一种机制来安全的在一个或多个进程之间传输数据,或者允许内核中的服务 和用户模式下的客户之间传输数据,从而实现了一种称为高级本地过程调用(advanced local procedure callALPC 的内部 IPC 机制,这是一种高速的、可伸缩的、安全的消息传递 设施,可用于传递任意大小的消息。

         ALPC 是一种内部机制,所以第三方开发人员无法使用,但应用于 windows 的各个部分,例 如本地模式 RPC 和内核模式 RPC 都是调用的 ALPC。 

         ALPC_PORT 端口可以代表四种意思(索引有相应的意义):

    端口主要分两类:连接端口和通信端口。 

          0。未连接通信端口:这是一个未命名的端口,自己和自己通信。

         1。服务器连接端口:这是一个命名的端口,也是服务器的连接请求点。客户通过此端口连接到服务器。

         2。客户端通信端口:这是一个未命名的端口,客户线程利用这个端口与一个特定的服务器进行通信。

         3。服务器通信端口:这是一个未命名的端口,服务器利用此端口与一个特定的客户进行通信,针对每个活动的客户,服务器都有一个这样的端口。 

    初始化 PortObject AlpcpInitializePort(PortObject, Type, 0)这里进行了设置,里面这里的 INDEX 会是对应的 TYPE,内部会根据这个进行大量的判断


数据传输内部实现: 

1.0~512 字节以内:直接放入 Port_Message 结构体的末尾。 

2.512 字节~64K 以内: 512 字节以内,直接放在 Port_Message 结构体的末尾, 512 字节 ~64K 字节,申请一块 Datalen-0x200 的空间放入 Message->ExtensionBuffer 里。 

3.64K 以上:返回 Port_Message->Reserve。【未触发,暂未逆】 

消息队列:(红色部分是根据自己逆向推测

MainQueue:主队列,消息已经被发送,客户正在出来该消息。

PendingQueue:待处理消息,消息已经被发送,调用者正在等待应答,但是应答尚未被发 出,会从 WaitQueue 里的线程池里选择一条线程来执行。

LargeMessageQueue:大消息队列,消息已经被发送,但是调用者的缓冲区太小因而不能接 受该消息。调用者获得另一次机会来申请一个更大的缓冲区,并再次请求该消息的负荷数据。

CanceledQueue:已取消的队列,原本发送给该端口对象的消息,但是此后已被取消。 

WaitQueue:等待队列,它并没有把消息链接起来,相反,它把所有正在等待某个消息的线 程链接起来。 

DirectQueue:直接队列。由某个具体工作线程的处理的。


0x2 BLOB对象详解

目前 ALPC 所使用的 BLOB 类型 + 资源类型: 

连接 BLOBAlpcConnectionType _ALPC_COMMUNICATION_INFO

区域 BLOBAlpcRegionType _KALPC_REGION 

视图 BLOBAlpcViewType _KALPC_VIEW

安全 BLOBAlpcSecurityType _KALPC_SECURITY_DATA 

内存 BLOBAlpcSectionType _KALPC_SECTION 消息 

BLOBAlpcMessageType _KALPC_MESSAGE 

保留 BLOBAlpcReserveType _KALPC_RESERVE 

句柄 BLOBAlpcHandleDataType _KALPC_HANDLE_DATA


通过全局变量 AlpcpRegisteredTypes,可以知道总共注册了多少个 ALPC 类型

AlpcConnectionType 这些属于 BLOB_TYPE 类型:

例如: 

CommResources=AlpcpAllocateBlob(&AlpcConnectionType, 0x48, 1) 

根据 BlobType 用不同的方 法创建 BLOB0x48 就是_ALPC_COMMUNICATION_INFO 的长度。 

最后形成的结构体类似句柄+资源: 

typedef struct _COMMBLOB

     _BLOB blob; _

    ALPC_COMMUNICATION_INFO alpcCommInfo; 

}COMMBLOB,*PCOMMBLOB


BLOB 结构体:

ResourceId =BLOB_ID

--------------------------------------------------------------------------------------------------------------------------------- ---------------------------------------------------------

例如:这里是创建了连接 

BLOB NewAlpcCommunicationInfo=AlpcpAllocateBlob(&AlpcConnectionType, 0x48, 1) 

AlpcConnectionType=BLOB_TYPE NewAlpcCommunicationInfo=_ALPC_COMMUNICATION_INFO

0x3 ALPC初始化(AlpcpInitSystem)

windows 在启动阶段调用 LpcInitSystem()来初始化 ALPC 里面主要实现三个功能:

 

1windows vista 后移除了所有的 LPC,但为了兼容性的问题,LPC 包装了 ALPC 的调用。


2. 通过 AlpcpInitSystem,创建_ALPC_PORT 的对象类型并加入到 ObTypeIndexTable 表中。

3.通过 AlpcpInitSystem,创建Alpc 全局消息句柄表。Windows 会根据消息数量动态的创建 AlpcpSecondaryMessageTables 数组,数组里面都是 HANDLE_TABLE 

【在插入消息时,句柄 ID>=0x4000000 时就会进入 AlpcpSecondaryMessageTables 数组 Windows 句柄表是三层结构:512*512*256=0x4000000,这就是由来。 

而私有句柄表和全局句柄表是 OBJECT+权限,所以是 512*512*256,而消息句柄表没有权 限,但也沿用了前者的模式】


0x4 服务端创建端口(NtAlpcCreatePort)

原型: 

NTSYSCALLAPI NTSTATUS NTAPI NtAlpcCreatePort( 

_Out_ PHANDLE PortHandle, 

_In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, 

_In_opt_ PALPC_PORT_ATTRIBUTES PortAttributes

);

NtAlpcCreatePort-->AlpcpCreateConnectionPort 

1.AlpcpCreateConnectionPort->AlpcpCreatePort 来创建一个命名的服务端连接端口,让客户端可以通过这个连接。 

    AlpcpCreatePort(ObjectAttributes, PreviousMode, &ServerConnectObject)

2.AlpcpCreateConnectionPort->AlpcpInitializePort 来初始化Object 结构体。 

AlpcpInitializePort(pServerConnectObject, 2, SynchronizationType) 

初始化各个链表: 

MainQueue:主队列,消息已经被发送,客户正在处理该消息。 

PendingQueue:待处理消息,消息已经被发送,调用者正在等待应答,但是应答尚未被发 出。

LargeMessageQueue:大消息队列,消息已经被发送,但是调用者的缓冲区太小因而不能接 受该消息。调用者获得另一次机会来申请一个更大的缓冲区,并再次请求该消息的负荷数 据。

CanceledQueue:已取消的队列,原本发送给该端口对象的消息,但是此后已被取消。 

WaitQueue:等待队列,它并没有把消息链接起来,相反,它把所有正在等待某个消息的线程链接起来。 

DirectQueue:直接队列。 

初始化事件或者信号量:

将当前的ALPC_PORT 加入全局的AlpcpPortList 链表 

3.创建连接 BLOB 和连接消息

ServerAlpcConnectCommInfo = AlpcpAllocateBlob(&AlpcConnectionType, 0x48, 1) 注 意 : ConnectionType 内部是调用 ExallocatePoolWithTag 来申请内存 

pServerConnectObject->CommunicationInfo = ServerAlpcConnectCommInfo 

pServerConnectObject->CommunicationInfo->ConnectionPort = pServerConnectObject;

pServerConnectObject->CommunicationInfo->ServerCommunicationPort = 0; 

pServerConnectObject->CommunicationInfo->CloseMessage = 0; 

ServerAlpcConnectCommInfo->ClientCommunicationPort = 0;

InitializeListHead(&ServerAlpcCommInfo->CommunicationList) 

AlpcInitializeHandleTable(&ServerAlpcConnectCommInfo->HandleTable); //初始化句柄表

ObInsertObjectEx(pServerConnectObject, 0, 0x1F0001u, 0, 0, 0, &Handle); //将当前 object 对象插入到Server 端的私有句柄表里。


0x4 服务端允许连接端口(NtAlpcAcceptConnectPort)

注意:服务端创建端口后,会调用 NtAlpcSendWaitReceivePort 等待客户端发送连接信息, 并在这里kewaitforsingleobject,等待客户端发送连接信息后,客户端调用 KeReleaseSemaphoreEx 释放信号量,服务端这时候会得到消息ID,这时候调用 NtAlpcAcceptConnectPort 来决定是否来创建通信端口。 

由于 NtAlpcSendWaitReceivePort 实现较为复杂,暂不将此函数实现过程写出。


原型:

NTSYSCALLAPI NTSTATUS NTAPI NtAlpcAcceptConnectPort( 

_Out_ PHANDLE PortHandle, 

_In_ HANDLE ConnectionPortHandle,

_In_ ULONG Flags, 

_In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, 

_In_opt_ PALPC_PORT_ATTRIBUTES PortAttributes, 

_In_opt_ PVOID PortContext, 

_In_reads_bytes_(ConnectionRequest->u1.s1.TotalLength) PPORT_MESSAGE ConnectionRequest, 

_Inout_opt_ PALPC_MESSAGE_ATTRIBUTES ConnectionMessageAttributes,

_In_ BOOLEAN AcceptConnection 

);

1.AlpcpReferencePortByHandle(pServerPortHandle,1u,PreviousMode, &ServerConnectAlpcObject);// 通过句柄得到 Server 连接端口对象

2.AlpcpLookupMessage(pServerAlpcObject, MessageId, CallbackId, ignore, &OutMessage);// messageId 查找 kalpc_message 

1> 通 过 MessageId 的 大 小 找 到 消 息 句 柄 表 是 : AlpcMessageTable 或 者 AlpcpSecondaryMessageTables 数组里寻找。

2>通过 MessageId AlpcMessageTable 表里找到相应的消息。 

ExpLookupHandleTableEntry(pAlpcMessageTable, MessageId)

这时候得到了加密后的 ALPC_MESSAGE,解密是: sar 0x10(算术右移 0x10)就是 Message

pClientAlpcCommInfo=ConnectMessage->OwerPort->CommunicationInfo // ConnectionMessage 得到客户端的通信信息。 

3. 创 建 服 务 器 通 信 端 口 AlpcpCreatePort(pObjectAttributes, vPreviousMode, &ServerCommPortObject)

4. 初始化服务器通信端口 AlpcpInitializePort(ServerCommPortObject, 3, 0)

pServerCommPortObject->CommunicationInfo=pClientAlpcCommInfo; //在这里让服务端通信端口和客户端 

pClientAlpcCommInfo->ServerCommunicationPort =ServerCommPortObject

ConnectMessage->OwerPort->TargetQueuePort=pServerConnectAlpcObject;

ConnectMessage->OwerPort->TargetSequencePort=pServerCommPortObject;

pServerCommPortObject->TargetQueuePort = OwnerPort; //注意:OwnerPort 就是客户端通讯端口 

pServerCommPortObject->TargetSequencePort = OwnerPort;


5.AlpcpSetupMessageDataForDeferredCopy

如果数据<=0x200,则 Message->DataUserVa=ConnectionRequest; 

6. ObInsertObjectEx(pServerCommPortObject, 0, 0x1F0001, 0, 0, 0, &Handle) //ServerCommPortObject 插入Server 端句柄表

7. 修改收到的 Message 的内容,分发到客户端实现回复通知。 

DispatchContext.PortObject = pServerCommPortObject; 

DispatchContext.Message = ConnectMessage; 

DispatchContext.Flags = 0x10000; 

AlpcpDispatchMessage(&DispatchContext) 

这个函数实现的很复杂,简单来说: 

1. 当前的 Message 的环境是 Server 端,修改成 Client 端环境属性

2。从ServerConnectPort 待处理消息队列里移除消息,并将 Message.PortQueue=0; 

3.if ( Message->DataUserVa )

   AlpcpCaptureMessageDataSafe(Message); 

// 512 字节以内,直接放在 Message+1 

// 512 字节~64K 字节,申请一块 Datalen-0x200 的空间前 512 个字节放置到 Message+1,余字节放入 Message->ExtensionBuffer 里 

// 64K 以上,直接返回 Message->Reserve Message->DataUserVa = 0; // 这里不需要了,清 0

5. 通信消息加入到 ClientCommunicationPort->MainQueue ,并且 ++ClientCommunicationPort->MainQueueLength,这代表服务端会回复一个消息告知客户端已连上。



0x4 客户端连接端口(NtAlpcConnectPortEx)

原型: 

NTSYSCALLAPI NTSTATUS NTAPI NtAlpcConnectPortEx( _Out_ PHANDLE PortHandle,

_In_ POBJECT_ATTRIBUTES ConnectionPortObjectAttributes, 

_In_opt_ POBJECT_ATTRIBUTES ClientPortObjectAttributes, 

_In_opt_ PALPC_PORT_ATTRIBUTES PortAttributes,

_In_ ULONG Flags,

_In_opt_ PSECURITY_DESCRIPTOR ServerSecurityRequirements, 

_Inout_updates_bytes_to_opt_(*BufferLength, *BufferLength) PPORT_MESSAGE ConnectionMessage, 

_Inout_opt_ PSIZE_T BufferLength, 

_Inout_opt_ PALPC_MESSAGE_ATTRIBUTES OutMessageAttributes,

_Inout_opt_ PALPC_MESSAGE_ATTRIBUTES InMessageAttributes,

_In_opt_ PLARGE_INTEGER Timeout

)

NtAlpcConnectPort-->AlpcpConnectPort 

1AlpcpConnectPort-->AlpcpCreateClientPort 创建未命名的客户端通信端口。 

1>通过要连接的 ObjectName,得到要 Server 端的 PortObject

ObReferenceObjectByName(ObjectName, 0, 0, 1, AlpcPortObjectType, PreviousMode, 0, &ServerConnectPortObject); 

2>创建客户端对象,并让 Server 端和 Client 端产生联系。 

AlpcpCreatePort(ObjectAttributes, PreviousMode, &ClientCommObject) //创建客户端通信端口 

AlpcpInitializePort(ClientCommObject, 2, SynchronizationType) //客户端通信端口初始化 

pClientAlpcCommInfo = AlpcpAllocateBlob(&AlpcConnectionType, 0x48, 1); // 创建客户端连接信息

pClientCommObject->CommunicationInfo=pClientAlpcCommInfo; 

pClientAlpcCommInfo->CloseMessage=0;

pClientAlpcCommInfo->ServerCommunicationPort=0; //服务通信端口,这里会在Server 允许 连接后由服务端填写服务端Port

pClientAlpcCommInfo->ClientCommunicationPort =pClientCommObject; //客户通信端口 

pClientAlpcCommInfo->ConnectionPort=pServerConnectPortObject//需要连接的服务端端口 

InsertTailList(&ServerConnectPortObject->CommunicationInfo- >CommunicationList,&pClientAlpcCommInfo->CommunicationList) 

AlpcInitializeHandleTable(&pClientAlpcCommInfo->HandleTable);// 创建句柄表


3>创建关闭消息,并将消息加入到AlpcMessageTable 全局消息句柄表里 

AlpcpAllocateMessage(pCloseMessage, 0x30, 1) 

实现原理: 

1AlpcpAllocateBlob(&AlpcMessageType, Size, TRUE);注意:AlpcMessageType 内部是调用 AlpcpAllocateMessageFunction 来 申请内存 , 并且将创建出来的Message放进AlpcMessageTable 全局消息表中 

2pAlpcMessage->PortMessage.MessageId = Handle

上面函数创建的句柄 ID 等等,后面忽略。

4>pClientCommObject->OwnerProcess=PsGetCurrentProcess(); 

5>ClientObject 对象插入Client 进程的私有句柄表,得到HANDLE 

ObInsertObjectEx(pClientCommObject, 0, 0x1F0001, 0, 0, 0, &Handle);

pClientCommObject->PortContext = Handle; 

2AlpcpConnectPort-->AlpcpProcessConnectionRequest (分发连接请求

1>AlpcpFormatConnectionRequest 通过各种属性创建一个连接消息NewConnectionMessage。 

AlpcDispatchContext.PortObject = pClientCommObject;

AlpcDispatchContext.Message = NewConnectionMessage; 

AlpcDispatchContext.Flags = Flags; 

2>AlpcpDispatchConnectionRequest(&AlpcDispatchContext) 分发连接消息继续设置连接信息NewConnectionMessage ,并设置AlpcDisPatchContext, 最后调用AlpcCompleteDispatchMessage 来完成分发。

3AlpcCompleteDispatchMessage(&AlpcDispatchContext)

1>AlpcpInsertMessagePendingQueue(ServerConnectPortObject, NewConnectionMessage) 

是:

InsertTailList(&ServerConnectPortObject- >PendingQueue,&NewConnectionMessage) 

++ServerConnectPortObject->PendingQueueLength; 

2>KeReleaseSemaphoreEx(Ethread->AlpcWaitSemaphore....) 释放信号量让ServerConnectPortObject kewaitforsingleobject 接着执行。注:pServerPortObject->WaitQueue 里面放的是 Ethread->AlpcWaitListEntry


0x5 消息发送和接受(以后发)

完。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。



[注意] 欢迎加入看雪团队!base上海,招聘安全工程师、逆向工程师多个坑位等你投递!

收藏
点赞6
打赏
分享
最新回复 (9)
雪    币: 424
活跃值: 活跃值 (413)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
青丝梦 活跃值 2021-6-25 14:50
2
0
豆总  yyds
雪    币: 1061
活跃值: 活跃值 (1190)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
灵幻空间 活跃值 2021-6-25 14:53
3
0
牛皮!
雪    币: 72
活跃值: 活跃值 (1725)
能力值: ( LV6,RANK:84 )
在线值:
发帖
回帖
粉丝
还我六千雪币 活跃值 2021-6-25 15:02
4
0
牛皮!
雪    币: 10475
活跃值: 活跃值 (4184)
能力值: ( LV9,RANK:270 )
在线值:
发帖
回帖
粉丝
hzqst 活跃值 3 2021-6-25 15:22
5
0
好耶
雪    币: 3845
活跃值: 活跃值 (4415)
能力值: ( LV9,RANK:150 )
在线值:
发帖
回帖
粉丝
学技术打豆豆 活跃值 1 2021-6-25 16:54
6
0
hzqst 好耶
雪    币: 1061
活跃值: 活跃值 (1190)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
灵幻空间 活跃值 2021-6-29 00:29
7
0
hzqst 好耶
我看到什么
雪    币: 106
活跃值: 活跃值 (408)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
lifeandi 活跃值 2021-7-20 14:12
8
0
yyds
雪    币: 189
活跃值: 活跃值 (122)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
sculida 活跃值 2021-9-17 15:18
9
0
谢谢!
雪    币: 506
活跃值: 活跃值 (567)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
pushmop 活跃值 2021-9-17 16:08
10
0
牛批
游客
登录 | 注册 方可回帖
返回