最近由于项目原因,需要实现一个驱动的自我保护,即禁止其他人使用sc stop servicename的方式关停我的驱动,因此就有了这一篇文章
我准备通过研究卡巴斯基的代码来看看他是如何实现自我保护的
可以看到卡巴这里直接显示SC对自己的服务无效,我就是想要达到这种效果
首先使用windbg调试sc.exe
把sc.exe拖到ida里面,查找关键函数调用,首先看WMAIN函数,然后再进入Woker函数
在Worker函数中可以看到SC的命令行选项之一query
再往下看就能看到stop选项,那么我们现在就进入SendControlToService函数
根据实际动态调试,我们会在这个地方失败,该函数返回值为0表示失败
这个函数是一个导入函数,他是一个动态加载的函数
我们需要在WINDBG中通过动态调试来找到这个Helper2函数的地址
定位到该函数实际上就是sechost!ControlService
那么现在我们把sechost.dll拖到IDA中,看一下这个函数
可以看到最终就是发送了一个RPC来对服务进行控制的,那么现在问题来了,我们要怎么才能找到这个RPC服务的服务端进程是谁呢?以及是该进程中的哪个函数负责处理的呢?
要想解答这个问题,我们就需要逆向一下这个函数来看看他是如何工作的,妈的这个好难分析啊
通过跟踪RPCMSG结构体,最终可以定位到发送位置为NtAlpcSendWaitReceivePort,调用栈如下:
0:000> k
# Child-SP RetAddr Call Site
00 000000e5`8627ed90 00007ffb`17f63fc1 RPCRT4+0x46e6b
01 000000e5`8627ee40 00007ffb`17f792a7 RPCRT4+0x43fc1
02 000000e5`8627ee90 00007ffb`17f217c0 RPCRT4+0x592a7
03 000000e5`8627eec0 00007ffb`17f224bf RPCRT4+0x17c0
04 000000e5`8627f4e0 00007ffb`1790e964 RPCRT4+0x24bf
05 000000e5`8627f510 00007ff6`781f9741 sechost+0xe964
06 000000e5`8627f560 00007ff6`781f3d88 sc+0x9741
07 000000e5`8627f630 00007ff6`781f1074 sc+0x3d88
08 000000e5`8627f790 00007ff6`781f210d sc+0x1074
09 000000e5`8627f7c0 00007ffb`175a7614 sc+0x210d
0a 000000e5`8627f800 00007ffb`182c26a1 KERNEL32!BaseThreadInitThunk+0x14
0b 000000e5`8627f830 00000000`00000000 ntdll!RtlUserThreadStart+0x21
该函数对应的系统调用为
根据字段名称可以推测这个应该就是接收方的ALPC_PORT
查看该PORT的OwnerProcess
可以看到,接收方的进程为services.exe,那么我们只需要在nt!AlpcpReceiveMessage
下断点,并限定进程为services.exe即可
可以看到他的ALPC PORT的名称为NTSVCS
把services.exe拖到IDA里面去看
这里有一个RPC的示例项目,可以帮助我们更好的理解windows的RPC是如何工作的
根据示例项目中的RPCServer.exe的逆向分析:
提供给客户端调用的函数是SendReverseShell
,那么我们就看这个函数的交叉引用
最终定位到红框部分
再来查看红框部分的交叉引用,定位到这个地方
最后这个东西的引用位置是RpcServerRegisterIf2
函数的第一个参数
那么我们照猫画虎来分析services.exe,我们前面已经知道他的ALPC的名称叫做NTSVCS,那么就可以定位到这个地方
根据前面对RPCServer.exe的分析,那么下图中的unk_140082380
就是关键位置
最终我们定位到如下远程调用函数列表
根据函数名可知,我们要的函数就是RControlService
现在我们就可以继续分析了
但是我没有办法调试services.exe进程,即使摘掉了他的PPL保护我还是没办法调试
内核调试器中使用/p
下的断点也无法被触发
最终我还是使用了内核调试的方式来对services.exe进程进行的调试,首先,我在内核调试器中定义如下断点:
bp /p ffff928a5717b0c0 ntopenfile
其中ffff928a5717b0c0
为services.exe进程的EPROCESS地址
经过漫长的等待之后,断点终于触发,得到如下调用栈:
kd> k
# Child-SP RetAddr Call Site
00 ffff8600`60a29a88 fffff803`85611d05 nt!NtOpenFile
01 ffff8600`60a29a90 00007ff8`7a86dbc4 nt!KiSystemServiceCopyEnd+0x25
02 000000b2`4237cf18 00007ff8`7a828b5e ntdll!NtOpenFile+0x14
03 000000b2`4237cf20 00007ff8`7a8289d2 ntdll!LdrpMapResourceFile+0x112
04 000000b2`4237d030 00007ff8`7a80533c ntdll!LdrMapAndVerifyResourceFile+0x9a
05 000000b2`4237d0b0 00007ff8`7a8058ac ntdll!LdrLoadAlternateResourceModuleEx+0x49c
06 000000b2`4237dbc0 00007ff8`7a803d98 ntdll!LdrpLoadResourceFromAlternativeModule+0x1ec
07 000000b2`4237dd30 00007ff8`7a823c45 ntdll!LdrpSearchResourceSection_U+0x1cc
08 000000b2`4237dea0 00007ff8`7846bc09 ntdll!RtlLoadString+0x105
09 000000b2`4237df60 00007ff8`7846919b KERNELBASE!LoadStringByReference+0x499
0a 000000b2`4237e300 00007ff6`4f59b855 KERNELBASE!RegLoadMUIStringW+0x18b
0b 000000b2`4237e390 00007ff6`4f59b548 services!ScRegLoadMUIString+0x81
0c 000000b2`4237e830 00007ff6`4f59b617 services!ScReadStringIndirect+0x64
0d 000000b2`4237e890 00007ff6`4f59b169 services!ScReadAndTranslateString+0x83
0e 000000b2`4237e900 00007ff6`4f59b047 services!CServiceRecord::ReadDisplayNameFromRegistry+0x79
0f 000000b2`4237e940 00007ff6`4f59adf3 services!CServiceRecord::ReadDisplayName+0x17
10 000000b2`4237e970 00007ff6`4f599d1a services!CServiceRecord::GetJITReadDisplayName+0x2b
11 000000b2`4237e9a0 00007ff6`4f58988f services!CWin32ServiceRecord::SendControl+0xca
12 000000b2`4237ece0 00007ff6`4f593939 services!CWin32ServiceRecord::StartInternal+0xcbf
13 000000b2`4237eec0 00007ff6`4f5935ca services!CServiceRecord::Start+0x99
14 000000b2`4237ef20 00007ff6`4f592b18 services!ScStartMarkedServicesInServiceSet+0x182
15 000000b2`4237efb0 00007ff6`4f5924fa services!ScStartServicesInStartList+0x200
16 000000b2`4237f080 00007ff6`4f586101 services!ScStartServiceAndDependencies+0x1d6
17 000000b2`4237f150 00007ff8`79c9a2d3 services!RStartServiceW+0x101
18 000000b2`4237f1d0 00007ff8`79c325a7 RPCRT4!Invoke+0x73
19 000000b2`4237f230 00007ff8`79c80c3a RPCRT4!NdrStubCall2+0x3f7
1a 000000b2`4237f580 00007ff8`79c7b128 RPCRT4!NdrServerCall2+0x1a
1b 000000b2`4237f5b0 00007ff8`79c58146 RPCRT4!DispatchToStubInCNoAvrf+0x18
1c 000000b2`4237f600 00007ff8`79c57a98 RPCRT4!RPC_INTERFACE::DispatchToStubWorker+0x1a6
1d 000000b2`4237f6e0 00007ff8`79c650af RPCRT4!RPC_INTERFACE::DispatchToStub+0xf8
1e 000000b2`4237f750 00007ff8`79c644b8 RPCRT4!LRPC_SCALL::DispatchRequest+0x31f
1f 000000b2`4237f820 00007ff8`79c63aa1 RPCRT4!LRPC_SCALL::HandleRequest+0x7f8
20 000000b2`4237f930 00007ff8`79c6350e RPCRT4!LRPC_ADDRESS::HandleRequest+0x341
21 000000b2`4237f9d0 00007ff8`79c67b62 RPCRT4!LRPC_ADDRESS::ProcessIO+0x89e
22 000000b2`4237fb10 00007ff8`7a7f0330 RPCRT4!LrpcIoComplete+0xc2
23 000000b2`4237fbb0 00007ff8`7a81d566 ntdll!TppAlpcpExecuteCallback+0x260
24 000000b2`4237fc30 00007ff8`78fc7374 ntdll!TppWorkerThread+0x456
25 000000b2`4237ff30 00007ff8`7a81cc91 KERNEL32!BaseThreadInitThunk+0x14
26 000000b2`4237ff60 00000000`00000000 ntdll!RtlUserThreadStart+0x21
从这个地方我们可以判断出来,services.exe进程中的远程函数都是通过RPCRT4!Invoke
来调用的LL
17 000000b2`4237f150 00007ff8`79c9a2d3 services!RStartServiceW+0x101
18 000000b2`4237f1d0 00007ff8`79c325a7 RPCRT4!Invoke+0x73
准确来说,就是这个地方:LLLL
然后我们在这个地方下断点,当r10
为RControlService
我们断下来就行了