|
原文链接:windows ALPC内核拦截的方法
前言ALPC这玩意挺复杂的,现在的安全软件应该都是在r3下hook services来实现的,比如
@FaEry
https://bbs.pediy.com/thread-251158.htm写的
但是r3下挂会遇到PPL问题
https://bbs.pediy.com/thread-250446.htm
所以才有了今天的文章
内核可以的挂钩方法直接内联hook -PG
找指针改地址,做DKOM -安全软件不应该这样做
由于ALPC_PORT是object回调,因此挂object 的情况下,win7可以,win8以上直接PG并且只能拦得到创建但是不能拦得到ALPC交换的过程
IO挂钩 -本文的方法
最开始我是自己逆向ALPC时候偶然发现的,后来搜了一下外网,老外
http://www.zer0mem.sk/?p=542
也找到了一个一样的方法,不过他的研究比我深入一点
为什么能挂上在
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
|
|