Windows软件调试初探-特殊过程调用

跨越空间、跨越特权级别、跨越线程、跨越进程或跨越机器的过程调用统称为特殊过程调用。

异步过程调用APC

例如异步文件操作引发的APC:

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
void CGeAPCDlg::D4D(LPCTSTR szFormat, ...) {
TCHAR szMsg[MAX_PATH] = { 0 };
va_list va;
va_start(va, szFormat);
_vsntprintf_s(szMsg, MAX_PATH, MAX_PATH, szFormat, va);
OutputDebugString(szMsg);
this->m_ListInfo.AddString(szMsg);
return;
};
VOID WINAPI GeCompletedWriteRoutine(DWORD dwErr, DWORD cbWritten, LPOVERLAPPED lpOverLap) {
CGeAPCDlg* pDlg = (CGeAPCDlg*)((LPGEOVERLAP)lpOverLap)->pUserData;
BOOL fRead = FALSE;
pDlg->D4D(_T("Complete Routine is called with %d bytes written."), cbWritten);
return;
};
HRESULT CGeAPCDlg::WriteLong(LPCTSTR szFileName, int nBytes) {
if (m_hFile == INVALID_HANDLE_VALUE) {
m_hFile = CreateFile(szFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (m_hFile == INVALID_HANDLE_VALUE) {
D4D(_T("Failed to create file %s, gle %d"), szFileName, GetLastError());
return E_FAIL;
};
m_lpBuffer = (LPBYTE)malloc(nBytes);
memset(&m_Overlapped, 0, sizeof(m_Overlapped));
m_Overlapped.pUserData = this;
}
else
m_Overlapped.oOverlap.Offset += nBytes;
BOOL bWrite = WriteFileEx(m_hFile, m_lpBuffer, nBytes, (LPOVERLAPPED)&m_Overlapped, (LPOVERLAPPED_COMPLETION_ROUTINE)GeCompletedWriteRoutine);
D4D(_T("Writing %d bytes returned %d, gle=%d"), nBytes, bWrite, GetLastError());
return bWrite ? S_OK : E_FAIL;
};
void CGeAPCDlg::OnBnClickedWriteLong() {
WriteLong(_T("geapc.bin"), 0x200000);
return;
};
void CGeAPCDlg::OnBnClickedAlertable() {
SleepEx(1, TRUE);
return;
};
void Papcfunc(ULONG_PTR lpParameter) {
MessageBox(NULL, _T("I am called by APC"), _T("GeAPC"), MB_OK);
return;
};
void CGeAPCDlg::OnBnClickedQueueapc() {
DWORD dwRet = QueueUserAPC(Papcfunc, GetCurrentThread(), (ULONG_PTR)this);
D4D(_T("QueueUserAPC returned 0x%x, gle = %d"), dwRet, GetLastError());
return;
};

当I/O管理器完成I/O请求时如果该请求是异步的,则创建一个APC对象,用KeInsertQueueApc放入内核队列。当NTFS完成程序提交的写操作时,用I/O管理器IofCompleteRequest报告完成IRP。当I.O管理器内部函数IopCompleteRequest检查到对应IRP操作有关联用户态完成函数时,常见APC。

1
2
3
4
5
6
7
8
9
10
0: kd> k
# Child-SP RetAddr Call Site
00 fffff806`8087edd8 fffff806`7d0c33ad nt!KiInsertQueueApc
01 fffff806`8087ede0 fffff806`7d0c2e97 nt!IopfCompleteRequest+0x4fd
02 fffff806`8087eed0 fffff806`80dd5056 nt!IofCompleteRequest+0x17
03 fffff806`8087ef00 00000000`00000018 0xfffff806`80dd5056
04 fffff806`8087ef08 fffff806`8087ef70 0x18
05 fffff806`8087ef10 ffffba8d`296e3010 0xfffff806`8087ef70
06 fffff806`8087ef18 ffffba8d`240f4000 0xffffba8d`296e3010
07 fffff806`8087ef20 00000000`00000000 0xffffba8d`240f4000

通过进程EPROCESS查看指定进程APC队列:

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
1: kd> !apc proc ffffba8d253d0080
Process ffffba8d253d0080 GeAPC.exe
Thread ffffba8d24b97080 ApcStateIndex 0 ApcListHead ffffba8d24b97128 [USER]
KAPC @ ffffba8d28bf1a18
Type 12
KernelRoutine fffff8067d4935c0 nt!IopUserCompletion+0
RundownRoutine fffff8067d4935c0 nt!IopUserCompletion+0
KAPC @ ffffba8d28783a18
Type 12
KernelRoutine fffff8067d4935c0 nt!IopUserCompletion+0
RundownRoutine fffff8067d4935c0 nt!IopUserCompletion+0
1: kd> !thread ffffba8d24b97080
THREAD ffffba8d24b97080 Cid 0950.03bc Teb: 0000006107e9c000 Win32Thread: ffffba8d25df3db0 WAIT: (WrUserRequest) UserMode Non-Alertable
ffffba8d25e91500 QueueObject
Not impersonating
DeviceMap ffffe582ed824650
Owning Process ffffba8d253d0080 Image: GeAPC.exe
Attached Process N/A Image: N/A
Wait Start TickCount 412661 Ticks: 235 (0:00:00:03.671)
Context Switch Count 5210 IdealProcessor: 1
UserTime 00:00:00.031
KernelTime 00:00:00.203
Win32 Start Address 0x00007ff7b6f714e2
Stack Init ffffcf8675edfc90 Current ffffcf8675edef50
Base ffffcf8675ee0000 Limit ffffcf8675eda000 Call 0000000000000000
Priority 12 BasePriority 8 PriorityDecrement 2 IoPriority 2 PagePriority 5
Child-SP RetAddr : Args to Child : Call Site
ffffcf86`75edef90 fffff806`7d0dfe60 : fffff806`78f73180 00000000`ffffffff 00000000`00000000 00000000`00000000 : nt!KiSwapContext+0x76
ffffcf86`75edf0d0 fffff806`7d0df38f : 00000000`00000000 00000000`00000001 ffffcf86`75edf290 ffffba8d`00000000 : nt!KiSwapThread+0x500
ffffcf86`75edf180 fffff806`7d0dec33 : 00000000`00000000 00000000`00000000 00000000`00000000 ffffba8d`24b971c0 : nt!KiCommitThreadWait+0x14f
ffffcf86`75edf220 fffff806`7d0be66b : ffffba8d`25e91500 fffff806`0000000d ffffba8d`00000001 00000000`00000000 : nt!KeWaitForSingleObject+0x233
ffffcf86`75edf310 ffff85c8`8b05ce42 : ffff8593`029618e0 ffff8593`029618e0 00000000`00000000 00000000`00000000 : nt!KeWaitForMultipleObjects+0x45b
ffffcf86`75edf420 ffff8593`029618e0 : ffff8593`029618e0 00000000`00000000 00000000`00000000 00000000`00000001 : 0xffff85c8`8b05ce42
ffffcf86`75edf428 ffff8593`029618e0 : 00000000`00000000 00000000`00000000 00000000`00000001 00000000`00000000 : 0xffff8593`029618e0
ffffcf86`75edf430 00000000`00000000 : 00000000`00000000 00000000`00000001 00000000`00000000 00000000`00000000 : 0xffff8593`029618e0

可见该线程处于等待状态但不可接警状态,因为该进程0号线程又进入消息循环,用NtUserGetMessage等待窗口消息。只有线程处于可接警状态才可向其投递APC,进入可接警状态方法之一是用SleepEx,第二个参数指定TRUE。SleepEx内部用内核服务NtDelayExecution。该服务返回时内核KiSystemServiceExit检查是否需要投递APC,需要且处于可接警状态时用KiDeliverApc投递APC。

1
2
3
4
5
6
7
8
0: kd> k
# Child-SP RetAddr Call Site
00 ffffcf86`748ae738 fffff806`7d1f9570 nt!KiDeliverApc
01 ffffcf86`748ae740 fffff806`7d3fccd8 nt!KiApcInterrupt+0x2f0
02 ffffcf86`748ae8d0 fffff806`7d1fcbd8 nt!PspUserThreadStartup+0x48
03 ffffcf86`748ae9c0 fffff806`7d1fcb40 nt!KiStartUserThread+0x28
04 ffffcf86`748aeb00 00007ffd`bcc7cea0 nt!KiStartUserThreadReturn
05 0000009b`1e57fe08 00000000`00000000 0x00007ffd`bcc7cea0

ntdll!KiUserApcDispatch负责用户空间分发APC,并调用OVERLAPPED结构指定的回调函数。

此外,还有用Windows API之QueueUserAPC让指定线程执行指定函数。目标线程可以是当前进程或其他进程中的,目标线程也可以是发起线程。每调用一次将把一个APC插入队列,用SleepEx等系统将投递APC并调用pfnAPC函数。

中断请求级别IRQL

例如当操作系统内核执行某些原子操作时不希望被中断,解决方法是屏蔽CPU中断,但会有一些问题:若关闭中断后代码存在瑕疵如执行很慢或死循环,其他CPU需要与该CPU通过IPI中断通信,导致连锁反应并可导致系统瘫痪;若系统发送重要事件如电源故障或硬件错误等,内核无法及时跳过去处理。解决这俩问题的方法是引入IRQL,当有优先级更高的事件发生则CPU跳过去执行,当有优先级更低的事件发生就不要打扰CPU。

每个优先级用一个数字表示,从0开始最低,数字越大优先级越高。

IRQL x86 AMD64 IA64 描述
PASSIVE_LEVEL/LOW_LEVEL 0 0 0 被动级别,用户或内核普通操作
APC_LEVEL 1 1 1 APC和处理页错误
DISPATCH_LEVEL 2 2 2 线程调度器和DPC
CMC_LEVEL 3 可纠正机器检查异常
DIRQL 3~26 3~11 4~11 处理硬件设备中断
PC_LEVEL 12 性能计数器
PROFILE_LEVEL 27 15 15 性能分析定时器,废弃
SYNCH_LEVEL 27 13 13 跨处理器的代码和指令流同步
CLOCK_LEVEL 13 13 时钟定时器
CLOCK2_LEVEL 28 x86硬件时钟定时器
IPI_LEVEL 29 14 14 处理器间中断
POWER_LEVEL 30 14 15 电源故障
HIGH_LEVEL 31 15 15 机器检查异常、蓝屏和转储等重要操作

HIGH_LEVEL时CPU不再响应任何硬件中断请求,NMI除外。

如蓝屏停止码IRQL_NOT_DISPATCH_LEVEL表示当前IRQL不是要求的DISPATCH_LEVEL。IRQL_GT_ZERO_AT_SYSTEM_SERVICE表示CPU执行系统服务后即将返回用户空间时IRQL还大于0。IRQL_NOT_LESS_OR_EQUAL表示IRQL没有小于等于要求值。

每个CPU有一个KPCR结构,其中有个IRQL:

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
51
52
53
54
2: kd> !pcr
KPCR for Processor 2 at ffff9e811d72a000:
Major 1 Minor 1
NtTib.ExceptionList: ffff9e811d739fb0
NtTib.StackBase: ffff9e811d738000
NtTib.StackLimit: 0000000002c6f5e8
NtTib.SubSystemTib: ffff9e811d72a000
NtTib.Version: 000000001d72a180
NtTib.UserPointer: ffff9e811d72a870
NtTib.SelfTib: 00000000007f1000

SelfPcr: 0000000000000000
Prcb: ffff9e811d72a180
Irql: 0000000000000000
IRR: 0000000000000000
IDR: 0000000000000000
InterruptMode: 0000000000000000
IDT: 0000000000000000
GDT: 0000000000000000
TSS: 0000000000000000

CurrentThread: ffff9e811d735140
NextThread: 0000000000000000
IdleThread: ffff9e811d735140

DpcQueue: Unable to read nt!_KDPC_DATA.DpcListHead.Flink @ ffff9e811d72d240
2: kd> dt nt!_KPCR ffff9e811d72a000
+0x000 NtTib : _NT_TIB
+0x000 GdtBase : 0xffff9e81`1d739fb0 _KGDTENTRY64
+0x008 TssBase : 0xffff9e81`1d738000 _KTSS64
+0x010 UserRsp : 0x2c6f5e8
+0x018 Self : 0xffff9e81`1d72a000 _KPCR
+0x020 CurrentPrcb : 0xffff9e81`1d72a180 _KPRCB
+0x028 LockArray : 0xffff9e81`1d72a870 _KSPIN_LOCK_QUEUE
+0x030 Used_Self : 0x00000000`007f1000 Void
+0x038 IdtBase : 0xffff9e81`1d737000 _KIDTENTRY64
+0x040 Unused : [2] 0
+0x050 Irql : 0 ''
+0x051 SecondLevelCacheAssociativity : 0xc ''
+0x052 ObsoleteNumber : 0x2 ''
+0x053 Fill0 : 0 ''
+0x054 Unused0 : [3] 0
+0x060 MajorVersion : 1
+0x062 MinorVersion : 1
+0x064 StallScaleFactor : 0x973
+0x068 Unused1 : [3] (null)
+0x080 KernelReserved : [15] 0
+0x0bc SecondLevelCacheSize : 0x2400000
+0x0c0 HalReserved : [16] 0x90321000
+0x100 Unused2 : 0
+0x108 KdVersionBlock : (null)
+0x110 Unused3 : (null)
+0x118 PcrAlign1 : [24] 0
+0x180 Prcb : _KPRCB

优先级控制涉及底层硬件知识,这里略去。

如果有老六代码把IRQL 升上去不降下来则导致系统严重故障。如KeWaitForSingleObject在高IRQL上等待可能导致系统级别死锁,所以只能在PASSIVE_LEVEL调用。

延迟过程调用DPC

高IRQL时操作要求简单短小,确实需要执行较长操作则用延迟过程调用DPC。典型使用场景是设备驱动程序的中断处理函数ISR,一般在DIRQL级别运行,不宜复杂操作,否则应使用DPC。一般用DDI的KeInsertQueueDpc向某个CPU的DPC队列插入DPC,WDF封装了更简单好使的WdfInterruptQueueDpcForIsr。用!dpcs观察DPC队列,每个DPC对象一个KDPC结构,其中DeferredRoutine指向关联的回调函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
0: kd> dt _KDPC
nt!_KDPC
+0x000 TargetInfoAsUlong : Uint4B
+0x000 Type : UChar
+0x001 Importance : UChar
+0x002 Number : Uint2B
+0x008 DpcListEntry : _SINGLE_LIST_ENTRY
+0x010 ProcessorHistory : Uint8B
+0x018 DeferredRoutine : Ptr64 void
+0x020 DeferredContext : Ptr64 Void
+0x028 SystemArgument1 : Ptr64 Void
+0x030 SystemArgument2 : Ptr64 Void
+0x038 DpcData : Ptr64 Void

当IRQL降到DISPATCH_LEVEL或以下时清理DPC队列,可能发生在CPU空闲线程中。一个典型的DPC与APC配合过程为:

步骤 操作 IRQL
1 应用程序发起异步I/O操作,如读数据 0
2 设备驱动程序收到请求后向硬件下发操作指令 0
3 硬件准备好数据后,通过中断通知驱动程序
4 驱动程序ISR简单处理后,向DPC队列插入一个DPC DIRQL
5 DPC回调函数被执行,对数据做处理后,插入APC通知应用程序 2
6 系统投递APC,应用程序回调函数被调用,得到通知和数据 1/0

在DISPATCH_LEVEL执行DPC操作不当,称为黏滞在DPC,会导致系统挂死。当发生这种情况,系统中DPC看门狗触发系统蓝屏,停止码DPC_WATCHDOG_VIOLATION。总结来说,APC是当另一个线程方便时做某件事,DPC是让CPU从危险IRQL降到不危险时完成刚才不方便做的事。

本地过程调用LPC

本地过程调用(或轻量级过程调用、或本地跨进程通信)LPC是一种基于端口和消息的通信机制。Windows Vista引入ALPC,现在LPC一般指ALPC。一般LPC通信过程如下:

  • 服务端用NtCreatePort创建服务端口。
  • 服务器端用NtListenPort监听端口。
  • 客户端用NtConnectPort连接端口。
  • 服务器端用NtAcceptConnectPort接受连接
  • 服务器端用NtCompleteConnectPort完成连接。
  • 客户端用NtRequestWaitReplyPort发送消息和等待回复。
  • 服务器端用NtReplyWaitReceivePort接收数据和发送回复。

例如列出指定进程所有LPC端口如下。上半部分“SmApiPort”为服务器端,下半部分“SbApiPort”是作为客户端连接的服务器端口。虽然名字一样,但输入不同的进程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
0: kd> !process 0 0 smss.exe
PROCESS ffffba8d23d50040
SessionId: none Cid: 010c Peb: 6eac984000 ParentCid: 0004
DirBase: 0eaf0000 ObjectTable: ffffe582e9bf1b40 HandleCount: 53.
Image: smss.exe
0: kd> !alpc /lpp ffffba8d23d50040

Ports created by the process ffffba8d23d50040:

ffffba8d23d56ac0('SmApiPort') 0, 3 connections
ffffba8d23da2a40 0 ->ffffba8d23de8480 0 ffffba8d241ca140('csrss.exe')
ffffba8d241ead80 0 ->ffffba8d241ebd80 0 ffffba8d24b32400('csrss.exe')
ffffba8d23b4bba0 0 ->ffffba8d240f0560 0 ffffba8d252e1400('svchost.exe')

Ports the process ffffba8d23d50040 is connected to:

ffffba8d23d52650 0 -> ffffba8d24167de0 ('SbApiPort') 0 ffffba8d241ca140 ('csrss.exe')
ffffba8d241e9d80 0 -> ffffba8d24b52df0 ('SbApiPort') 0 ffffba8d24b32400 ('csrss.exe')

端口名前地址为LPC端口对象,详细如下。上半部分为LPC对象基本属性,下半部分第一个列表是端口服务的服务例程,目前即SMSS提供会话服务的工作线程,后面几个队列都为空。

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
0: kd> !alpc /p ffffba8d23d56ac0
Port ffffba8d23d56ac0
Type : ALPC_CONNECTION_PORT
CommunicationInfo : ffffe582e9c95bf0
ConnectionPort : ffffba8d23d56ac0 (SmApiPort), Connections
ClientCommunicationPort : 0000000000000000
ServerCommunicationPort : 0000000000000000
OwnerProcess : ffffba8d23d50040 (smss.exe), Connections
SequenceNo : 0x00000005 (5)
CompletionPort : ffffba8d23b8a340
CompletionList : 0000000000000000
ConnectionPending : No
ConnectionRefused : No
Disconnected : No
Closed : No
FlushOnClose : Yes
ReturnExtendedInfo : No
Waitable : No
Security : Static
Wow64CompletionList : No

1 thread(s) are registered with port IO completion object:
THREAD ffffba8d23c55080 Cid 010c.011c Teb: 0000006eac987000 Win32Thread: 0000000000000000 WAIT
Main queue is empty.
Direct message queue is empty.
Large message queue is empty.
Pending queue is empty.
Canceled queue is empty.

!alpc /m观察每个消息详情。WaitingThread为正等待该消息的客户线程。

远程过程调用RPC

上文LPC是Windows系统内部使用,不公开,间接使用方式使用公开的RPC编程接口。PCR定位是为Windows平台提供一套强大的跨进程通信机制,让构建分布式计算软件更容易。RPC基本思想是技能提供强大的远程调用能力,又不损失本地调用的语义简洁性。RPC技术目标是让开发者像调用本地函数一样调用远程函数。开发者不必关心底层通信细节,按设计好的函数原型发起调用即可调用本地或远程的函数实现。

客户进程调用某个函数时,调用的是这个函数的桩函数。桩函数接到调用后负责收集参数,并把参数翻译为网络数据表示NDR。封装好参数后桩函数用RPC客户端运行时函数,通过传输层把数据发给服务器端。桩函数举例如下,NdrClientCall2是RPC运行时中重要函数,供客户端发起调用。

1
2
3
void HelloProc(unsigned char* pszString) {
NdrClientCall2((PMIDL_STUB_DESC)&hello_StubDesc, (PFORMAT_STRING)&__MIDL_ProcFormatString.Format[0], pszString);
};

服务器端运行时函数收到数据包后,调用服务端桩函数,服务器桩函数把NDR数据解开,转为普通形式参数,然后调用函数真正实现。若有返回值或返回类型的参数,服务器桩函数把返回数据打包,用服务器端RPC运行时函数将其发回客户端。

RPC子系统服务RpcSs是不允许禁止的。微软在实现RPC技术时也在开发组件对象模型COM和DCOM,因此RPC与COM和DCOM技术之间有很多交叉和复用,很多开发工具和基础设施也是共享的。如RPC中也用COM和DCOM中使用的IDL语言描述接口;RpcSs中既有对RPC的全局支持,也有对COM和DCOM的关键支持。

RPC服务器端启动后监听若干个端点,客户端启动后通过端点与服务器端建立联系。每个端点有固定传输层,传输层协议选择有命名管道、TCP、UDP、IPX、SPX、LPC等。传输层上有3中RPC协议可使用:

  • 面向连接的协议,全称面向连接的网络计算架构NCACN。
  • 数据报文协议,全称数据报文网络计算架构NCADG。
  • 本地的远程过程调用,全称本地远程过程调用网络计算架构NCALPRC。

使用RPC时,协议串ProtoSeq为一个标识不同RPC协议和传输层协议组合的固定字符串,例如ncacn_nb_tcp标识面向连接的RPC协议与NetBIOS TCP。系统服务EP Mapper用于匹配RPC端点,该服务与RPC子系统服务一样都是RPCSS.dll。

RPC设施工作时,用蜂巢存储各种类型RPC对象。每个蜂巢有自己的ID,一般为“x.y”形式。RPC运行时专门维护一个蜂巢堆嘞分配和释放蜂巢,如分配蜂巢用RPCRT4!CellHeap::AllocateCell。蜂巢堆上存储的RPC对象可以有:

RPC对象 属性 说明
端点 ProtseqType 该端点协议串类型
Status 端点状态,已分配allocated、活跃active、不活跃inactive
EndpointName 端点名称前28字符
线程 Status 线程状态,processing、dispatched、allocated、idle
LastUpdateTime 上次更新时间,即系统启动后毫秒数
TID 线程ID
连接 Flags 是否为互斥模式;认证级别;认证服务状态
LastTransmitFragmentSize 通过这个连接传输的上一个数据片大小
Endpoint 所属端点的蜂巢ID
LastSendTime 上次发送时间
LastReceiveTime 上次接收时间
服务器端调用SCALL Status 调用状态,已分配allocated、活跃active(RPC运行时正在处理)、已分发dispatched(服务函数已经调用但没返回)
ProcNum 过程序号,即调用函数在接口中序号,从0开始
InterfaceUUIDStart 接口第一个DWORD
ServicingTID 服务线程的蜂巢ID,该字段只在调用状态为活跃或已分配时有效
CallFlags 是否为被缓存的调用、异步调用、管道调用、LRPC或OSF调用
LastUpdateTime 上次更新时间
PID 调用者进程ID、仅LRPC时有效
TID 调用者线程ID、仅LPRC时有效
客户端调用CCALL ProcNum 被调用方法的过程序号
ServicingThread 发起调用的工作线程的蜂巢ID
IfStart 接口UUID第一个DWORD
EndPoint 该调用的服务器端点名字前12个字符
ProtocalSequence 协议串
LastUpdateTime 上次更新时间
TargetServer 服务器名称前24个字符

用用户态调试,打印出客户端调用列表如下。其中PID为当前客户端进程ID。CELL ID为该客户端调用对象蜂巢ID。PNO为调用过程序号。IFSTART为接口UUID起始部分。TIDNUMBER为调用线程ID。CALLID为本次调用ID。LASTIME为上次更新时间,可用getdbgcell将其翻译为秒数。ENDPOINT为该调用端点名称。有了端点名称即可获取到服务端信息。ST为端点状态,1活跃、0已分配。PROTSEQ为协议串,如NMP命名管道。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
0:000> !rpcexts.getclientcallinfo
Searching for call info ...
PID CELL ID PNO IFSTART TIDNUMBER CALLID LASTTIME PS CLTNUMBER ENDPOINT
------------------------------------------------------------------------------
0:000> !getendpointinfo \pipe\hello
Searching for endpoint info ...
PID CELL ID ST PROTSEQ ENDPOINT
-------------------------------------------------------------
0:000> !getendpointinfo
Searching for endpoint info ...
PID CELL ID ST PROTSEQ ENDPOINT
-------------------------------------------------------------
1328 0000.0002 01 LRPC LRPC-fa5191924afa47b302
104c 0000.0002 01 LRPC OLE5C2ED26E4B174DC263DB5874
0ca8 0000.0001 01 LRPC OLEA15241225E257BFB65312878
0:000> !rpcexts.getcallinfo 0 0 0 1328
Searching for call info ...
PID CELL ID ST PNO IFSTART THRDCELL CALLFLAG CALLID LASTTIME CONN/CLN
----------------------------------------------------------------------------
1328 0000.0003 00 000 00000000 0000.0000 0000000a 00000000 021e984c 0000.0000

还可以用!rpcexts.getdbgcell <PID> 0.4获取详细信息,其中Status为状态,Last update time为上次更新时间。!rpcexts.rpctime获取当前时间。