The opinions expressed in this blog are my own and do not represent those of my current employer

Microsoft did nice work related to callback mechanism, to avoid nasty patching across kernel, and support monitoring in clean way. Currently we can use, among others, for example callbacks on loading new image, process, thread, opening & duplicating handles, dropping files etc. For monitoring network communication you can attach to some device drivers, which is cleaner than hooking, but still does not cover as much as i want to. And there comes ALPC, because even resolving host comes through, and when you are able to recognize it ..

-> everyone like ALPC. And especially applications with network communication, because as was said at training, even gethostbyname ends up by calling some ALPC! So I think it is really good point to start at some object responsible for communication

OB_OPERATION_HANDLE_CREATE – A new process handle or thread handle was or will be opened.

OB_OPERATION_HANDLE_DUPLICATE – A process handle or thread handle was or will be duplicated.

Thats basically means, that we are theoretically able to get called at two mentioned HANDLE operations. Thats good, but wants to get more .. after some digging of nt!_ALPC_PORT it is possible to spot nice structure :

One good candidate to deeper look is NtAlpcSetInformation which call AlpcpInitializeCompletionList and it ends by calling IoAllocateMiniCompletionPacket – and this last routine can sound pretty familiar now!

OK, but whats happening there ? It is another callback mechanism – *CompletionIo*, already described in Windows internals 6th edition, Part2 (I/O Completion Ports). And this callback mechanism is setup-ed by default as you have already seen to call nt!AlpcpLookasidePacketCallbackRoutine.

It is obvious that it is possible to intercept mechanism by rewriting this callback, but this is not what we want to do … When we look at this default function, we can see how this callback mechanism work.

nt!IoSetIoCompletionEx2 ends in nt!IoSetIoCompletionEx, and nt!AlpcpDeferredFreeCompletionPacketLookaside ends by calling nt!IoFreeMiniCompletionPacket per packet in queue.

init : nt!IoAllocateMiniCompletionPacket

registering : nt!IoSetIoCompletionEx

free : nt!IoFreeMiniCompletionPacket

C++

1

2

3

4

5

6

7

8

9

10

11

12

EXTERN_C

NTKERNELAPI

void

NTAPI IoSetIoCompletionEx(

__inout void*completitionPort,

__in constvoid*keyContext,

__in constvoid*apcContext,

__in ULONG_PTR ioStatus,

__in ULONG_PTR ioStatusInformation,

__in boolallocPacketInfo,

__in constvoid*ioMiniCoompletitionPacketUser

);

and now how to setup own Callback packet :

C++

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

std::unique_ptr<void,decltype(&IoFreeMiniCompletionPacket)>m_pPacket(

IoAllocateMiniCompletionPacket(MiniPacketCallbackInterceptor,this),

IoFreeMiniCompletionPacket);

...

bool

StartIntercepting(

__in _ALPC_PORT*alpcPort,

__in void*keyContext

)

{

...

IoSetIoCompletionEx(

alpcPort->CompletionPort,

keyContext,

nullptr,

NULL,

NULL,

FALSE,

m_pPacket);

...

So now almost done, but one essential thing is missing – alpc port itself to attach .. and there exist some approaches how to find it :

!alpc /lpp

kdexts.dll do it somehow, so here is the approach :

… unfortunately nt!AlpcpPortList is not exported symbol, but its location is inside this ‘structure’ :

one member of this structure which can be found quite easly is nt!AlpcPortObjectType, which is not directly exported, but fortunately for us nt!LpcPortObjectType is alias to it!
And there is also another way to get it (not so comfortable) – querying it :

to successfully locate this structure, and port list itself, inside of ntoskrnl image just add additional checks of predicable values of some members of structure alongside with equality of value for nt!AlpcPortObjectType

ObFiltering

Another option *should* be an official option, but in reallity …

Ob Filters, and registering on nt!AlpcPortObjectType, mechanism is ready to use and it is already implemented in kernel! But you have some obstacles :

And you can be even more specific while monitoring, because that communication with svchost, or other generic service, is not information bomb at all, but you can look at it through service names, which can be useful far more!

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

service:PlugPlay

service:Power

service:DcomLaunch

service:RpcEptMapper

service:RpcSs

service:eventlog

service:AudioEndpointBuilder

service:MMCSS

service:AudioSrv

service:CscService

service:gpsvc

service:ProfSvc

service:Themes

service:EventSystem

service:SENS

service:UxSms

service:SamSs

service:lmhosts

service:nsi

service:Dhcp

service:Dnscache

service:ShellHWDetection

service:Schedule

service:Spooler

service:BFE

service:MpsSvc

service:LanmanWorkstation

service:CryptSvc

service:DPS

service:FDResPub

service:NlaSvc

service:PcaSvc

service:SysMain

service:TrkWks

service:Winmgmt

service:iphlpsvc

service:LanmanServer

service:netprofm

service:WdiServiceHost

service:WPDBusEnum

service:WdiSystemHost

service:WinHttpAutoProxySvc

service:Browser

service:WSearch

service:Netman

service:WMPNetworkSvc

service:fdPHost

service:HomeGroupProvider

service:SSDPSRV

service:BITS

Article about resolving service name by its id (SubProcessTag) you can find here, and also is written more concrete example of implementation here, and even more you can find it in process hacker as well. This method was designed for user mode, but in kernel you are by creation, so lets say it is more straighforward to resolve this information

Seems that IoCompletion callbacks can be really helpful mechanism. It works just on ports that use the I/O completion port type, not on all ALPC ports, but for network monitoring purposes seems it is fair enough :)

Another limitation is that ‘limited’ usage of Ob Filters on AlpcPorts. It is quite nice feature but limited so much … I hope filtering will support at least nt!AlpcPortObjectType soon!

At the end of this post, I would like to thank to Alex Ionescu for reviewing this article, and for that nice syscan win-internals training!