关于x64/Win10中Shim机制的一次调试分析过程

有阵子给windbg写jscript插件,无意中在x64/Win10上发现一件事,执行notepad不从nt!PspProcessOpen()过,mspaint则过。跟bluerust说起这事,他瞎猜了一个说法,是不是因为mspaint有Shim机制介入,进而导致这种区别。为什么有这种区别,到了也没去深究,不是刚需。但他说的Shim机制,对于一向孤陋寡闻的我,有点意思。

Alex Ionescu早年写过一个未完的系列,我觉得就那么回事吧。

Secrets of the Application Compatilibity Database (SDB) Part 1 – Alex Ionescu [2007-05-20]
http://www.alex-ionescu.com/?p=39

Secrets of the Application Compatilibity Database (SDB) Part 2 – Alex Ionescu [2007-05-21]
http://www.alex-ionescu.com/?p=40

Secrets of the Application Compatilibity Database (SDB) Part 3 – Alex Ionescu [2007-05-26]
http://www.alex-ionescu.com/?p=41

Secrets of the Application Compatilibity Database (SDB) Part 4 – Alex Ionescu [2007-06-17]
http://www.alex-ionescu.com/?p=43

————————————————————————-

有个中国同胞就Shim机制做过逆向分析:

shimeng – wedday [2008-08-26]
http://wedday.blogspot.jp/2008/08/shimeng.html
(讲了32位的Shim机制,没讲64位的)

shimeng注入 – wedday [2008-08-27]
http://wedday.blogspot.jp/2008/08/include-ntifs.html

————————————————————————–

看得出来,是个高手,年代久远,无从联系,不知此君还有新的个人主页否?

在这个领域遍历了一圈,比较精彩的几篇是:

Leveraging the Application Compatibility Cache in Forensic Investigations – Andrew Davis, Associate Consultant, Mandiant [2012-04-17]
https://www.fireeye.com/content/dam/fireeye-www/services/freeware/shimcache-whitepaper.pdf
https://github.com/mandiant/ShimCacheParser

Shim Shady: Live Investigations of the Application Compatibility Cache – Fred House, Claudiu Teodorescu, Andrew Davis [2015-10-27]
https://www.fireeye.com/blog/threat-research/2015/10/shim_shady_live_inv.html
https://www.fireeye.com/blog/threat-research/2015/10/shim_shady_live_inv/shim-shady-part-2.html

Malicious Application Compatibility Shims – Sean Pierce [2015-11-03]
https://www.blackhat.com/docs/eu-15/materials/eu-15-Pierce-Defending-Against-Malicious-Application-Compatibility-Shims-wp.pdf

————————————————————————–

既然声称遍历了一圈,看过的肯定远比这些多,不过没必要在此一一列举,有兴趣、
有毅力者自己放狗。顺便说一句,Sysinternals的autoruns至今没有检查Shim机制相
关的东西。

下面是一些关于x64/Win10中Shim机制的调试分析过程。

无意中发现mspaint.exe会加载AcGenral.dll。

cdb.exe -noinh -snul -hd -o -xe ld:ntdll mspaint.exe

拦截对AcGenral.dll的加载:

sxe ld:AcGenral

命中时调用栈回溯如下:

ntdll!NtMapViewOfSection+0x14
ntdll!LdrpMinimalMapModule+0xde
ntdll!LdrpMapDllWithSectionHandle+0x14
ntdll!LdrpMapDllNtFileName+0x18a
ntdll!LdrpMapDllSearchPath+0x1de
ntdll!LdrpProcessWork+0x66
ntdll!LdrpLoadDllInternal+0x13e
ntdll!LdrpLoadDll+0x107
ntdll!LdrpLoadShimEngine+0x194
ntdll!LdrpInitShimEngine+0x157
ntdll!LdrpInitializeProcess+0x1cda
ntdll!_LdrpInitialize+0x4e393
ntdll!LdrpInitialize+0x3b
ntdll!LdrInitializeThunk+0xe

在其中注意到:

LdrpLoadShimEngine
LdrpInitShimEngine

顾名思义,初始化、加载Shim引擎。逆向分析其主调函数:

LdrpInitializeProcess ( PCONTEXT Context, ... )
{
...
/*
* 保存5个关键函数的前16字节
*/
LdrpCaptureCriticalThunks();
...
/*
* 检查EXE的IFEO
*/
LdrpInitializeExecutionOptions();
...
/*
* FLG_SHOW_LDR_SNAPS(2)
*/
if ( 0 != ( Peb->NtGlobalFlag & FLG_SHOW_LDR_SNAPS ) )
{
LdrpDebugFlags |= 1;
}
...
if ( !UseWOW64 )
{
/*
* > dt ntdll!_PEB pShimData
* +0x2d8 pShimData : Ptr64 Void
*/
ShimData = PEB->pShimData;
}
...
if ( 0 != ( Peb->NtGlobalFlag & FLG_SHOW_LDR_SNAPS ) )
{
LdrpDebugFlags |= 1;
}
...
if ( ShimData != NULL && ... )
{
PEB->AppCompatInfo = NULL;
/*
* 初始化SHIM引擎
*/
LdrpInitShimEngine( ShimData );
}
...
AppCompatFlags = PEB->AppCompatFlags.LowPart;
/*
* 可以通过AppCompatFlags间接将LoaderThreads置1
*/
if ( AppCompatFlags & 0x10000000 )
{
PEB->ProcessParameters->LoaderThreads = 1;
}
LdrpEnableParallelLoading( PEB->ProcessParameters->LoaderThreads );
...
LdrInitState = 2;
if ( PEB->BeingDebugged )
{
/*
* "初始化断点"所在
*/
LdrpDoDebuggerBreak();
}
...
LdrpProcessInitContextRecord = Context;
LdrpDrainWorkQueue( 0 );
/*
* 加锁,用到了ntdll!LdrpLoaderLock
*/
LdrpAcquireLoaderLock();
LdrpInitializeGraphRecurse( LdrpImageEntry->DdagNode, x, x );
/*
* 释放锁
*/
LdrpReleaseLoaderLock(...);
LdrpFreeLoadContextOfNode( LdrpImageEntry->DdagNode, x );
LdrpDropLastInProgressCount(...);
LdrpProcessInitContextRecord = NULL;
...
if ( LdrpImageEntry->TlsIndex )
{
...
RtlActivateActivationContextUnsafeFast( x, LdrpImageEntry->EntryPointActivationContext );
/*
* EXE的TLS在此得到处理
*/
LdrpCallTlsInitializers( DLL_PROCESS_ATTACH, LdrpImageEntry );
RtlDeactivateActivationContextUnsafeFast( x );
}
/*
* 布尔变量
*
* db ntdll!g_ShimsEnabled l 1
*/
if ( g_ShimsEnabled && ... )
{
g_ShimsEnabled = FALSE;
/*
* lmfc a @@masm(poi(ntdll!g_pShimEngineModule))
*
* 比如apphelp.dll的基址
*/
LdrUnloadDll( g_pShimEngineModule );
/*
* 一旦从ibp离开,该变量很快被清成NULL
*/
g_pShimEngineModule = NULL;
}
if ( PEB->PostProcessInitRoutine != NULL )
{
PEB->PostProcessInitRoutine();
}
...
}

————————————————————————–

如果只是关心Shim机制,没必要看这一大堆,只要知道为了加载Shim引擎,
PEB->pShimData不得为NULL。起初以为是ntdll.dll初始化该值,后来确认
ntdll.dll被映射到进程空间时,该值已经就绪。

dq @$peb+@@(#FIELD_OFFSET(nt!_PEB,pShimData)) l 1
dt ntdll!_PEB pShimData @$peb
dx @$peb->pShimData

进一步调试发现,”sxe cpr”命中时,PEB已经存在,跟ntdll是否映射无关,只是不
能以符号形式访问PEB,只能用数字偏移。PEB->pShimData已经有值,由于缺少
ntdll.pdb,只能使用:

dq @$peb+0x2d8 l 1

为了知道PEB->pShimData何时被赋值、被赋何值,可能需要调试父进程的代码。当时有点犯傻,第一反应是用kd进行内核态调试,拦截nt!PspAllocateProcess(),从EPROCESS中找PEB,对PEB->pShimData设数据断点。

NTSTATUS PspAllocateProcess
(
/*
* 对应父进程
*/
PEPROCESS ParentProcess, // rcx
KPROCESSOR_MODE AccessMode, // rdx
POBJECT_ATTRIBUTES ObjectAttributes, // r8
unsigned char Protection, // r9
unsigned char SignatureLevel, // rsp+0x28
unsigned char SectionSignatureLevel, // rsp+0x30
PSECTION Section, // rsp+0x38
PTOKEN Token, // rsp+0x40
ULONG Flags, // rsp+0x48
a10, // rsp+0x50
a11, // rsp+0x58
/*
* JobMemberLevel != 0
*/
BOOLEAN a12, // rsp+0x60
a13, // rsp+0x68
PVOID *a14, // rsp+0x70
/*
* 由此输出新分配、创建的EPROCESS
*/
PEPROCESS *Process // rsp+0x78 out
);

————————————————————————–

该函数第1形参ParentProcess对应父进程,可以从中获取父进程的名字,可以针对父进程名设置条件断点,比如当父进程是cdb时断在nt!PspAllocateProcess()。

Hit:[cdb.exe]
...
nt!PspAllocateProcess:
fffff801`6ef25a7c 4c8bdc mov r11,rsp

kd> r $t0=poi(@rsp+0x78)
kd> dp @$t0 l 1
fffff50f`80994f10 00000000`00000000
kd> g poi(@rsp)
...
nt!NtCreateUserProcess+0x723:
fffff801`6ef1f657 8bf0 mov esi,eax

“g poi(@rsp)”之后,子进程的EPROCESS已经生成,检查确认子进程是mspaint:

kd> dp @$t0 l 1
fffff50f`80994f10 ffffdf00`660d4400
kd> r $t1=poi(@$t0)
kd> !process @$t1 0
PROCESS ffffdf00660d4400
SessionId: 1 Cid: 2754 Peb: d6286a7000 ParentCid: 23d0
DirBase: 7b7aa000 ObjectTable: ffffa40019946980 HandleCount: 0.
Image: mspaint.exe

检查确认父进程(当前进程)是cdb:

kd> !process -1 0
PROCESS ffffdf00697ed5c0
SessionId: 1 Cid: 23d0 Peb: 951eb82000 ParentCid: 2e90
DirBase: 4766d000 ObjectTable: ffffa400176c6540 HandleCount: 100.
Image: cdb.exe

刷新用户态符号,查看调用栈回溯:

kd> .reload /user
kd> kpn
# Child-SP RetAddr Call Site
00 fffff50f`80994e50 fffff801`6ebf8d53 nt!NtCreateUserProcess+0x723
01 fffff50f`80995a90 00007ffd`66631654 nt!KiSystemServiceCopyEnd+0x13
02 00000095`1e92e968 00007ffd`638006df ntdll!NtCreateUserProcess+0x14
03 00000095`1e92e970 00007ffd`637fd7a6 KERNELBASE!CreateProcessInternalW+0x1b3f
04 00000095`1e92f5d0 00007ffd`6623e4e3 KERNELBASE!CreateProcessW+0x66
05 00000095`1e92f640 00007ffd`4157caa2 KERNEL32!CreateProcessWStub+0x53
06 00000095`1e92f6a0 00007ffd`4139e43a dbgeng!LiveUserDebugServices::CreateProcessW+0x262
07 00000095`1e92f810 00007ffd`4135e054 dbgeng!LiveUserTargetInfo::StartCreateProcess+0x10a
08 00000095`1e92f870 00007ffd`4135df27 dbgeng!DebugClient::CreateProcessAndAttach2Wide+0x114
09 00000095`1e92f930 00007ff7`fdd653b9 dbgeng!DebugClient::CreateProcessAndAttachWide+0x77
0a 00000095`1e92f9b0 00007ff7`fdd6a3c3 cdb!InitializeSession+0x5d5
0b 00000095`1e92fa70 00007ff7`fdd6e359 cdb!wmain+0x3ef
0c 00000095`1e92fd10 00007ffd`66241fe4 cdb!std::_Winerror_map+0x299
0d 00000095`1e92fd50 00007ffd`665fef91 KERNEL32!BaseThreadInitThunk+0x14
0e 00000095`1e92fd80 00000000`00000000 ntdll!RtlUserThreadStart+0x21

检查子进程的PEB:

dq @$t1+@@(#FIELD_OFFSET(nt!_EPROCESS,Peb)) l 1
dt nt!_EPROCESS Peb @$t1
dx -r0 ((nt!_EPROCESS *)@$t1)->Peb
? @@c++(((nt!_EPROCESS *)@$t1)->Peb)

kd> r $t2=@@c++(((nt!_EPROCESS *)@$t1)->Peb)
kd> ? @$t2
Evaluate expression: 919801065472 = 000000d6`286a7000

现在$t1、$t2依次对应mspaint的EPROCESS、PEB。

kd> .process /p /r @$t1
kd> r $t3=@$t2+@@(#FIELD_OFFSET(nt!_PEB,pShimData))
kd> dq @$t3 l 1
000000d6`286a72d8 00000000`00000000

此时mspaint的PEB->pShimData尚未初始化,对之设数据断点:

kd> ba w1 @$t3
kd> g

数据断点命中:

nt!memcpy+0x2f:
fffff801`6ebf9f6f 75ef jne nt!memcpy+0x20 (fffff801`6ebf9f60) [br=0]
kd> kpn
# Child-SP RetAddr Call Site
00 fffff50f`809954f8 fffff801`6ef9abff nt!memcpy+0x2f
01 fffff50f`80995500 fffff801`6ef9a630 nt!MmCopyVirtualMemory+0x53f
02 fffff50f`809959d0 fffff801`6effa1d7 nt!MiReadWriteVirtualMemory+0x160
03 fffff50f`80995a50 fffff801`6ebf8d53 nt!NtWriteVirtualMemory+0x1b
04 fffff50f`80995a90 00007ffd`66630584 nt!KiSystemServiceCopyEnd+0x13
05 00000095`1e92e968 00000000`00000000 0x00007ffd`66630584
kd> !process -1 0
PROCESS ffffdf00660d4400
SessionId: 1 Cid: 2754 Peb: d6286a7000 ParentCid: 23d0
DirBase: 7b7aa000 ObjectTable: ffffa40019946980 HandleCount: 0.
Image: mspaint.exe

kd> dq @$t3 l 1
000000d6`286a72d8 0000024f`ea7f0000

此时mspaint的PEB->pShimData已被初始化。

kd> .reload /user
Loading User Symbols
PEB is paged out (Peb.Ldr = 000000d6`286a7018). Type ".hh dbgerr001" for details

在mspaint进程空间,但”.reload /user”失败,可以用”!vad”解决之:

kd> !vad 7ffd`66630584 1

VAD @ ffffdf0069588960
Start VPN 7ffd66590 End VPN 7ffd6676f Control Area ffffdf006701a980
FirstProtoPte ffffa40014e23010 LastPte ffffa40014e23f08 Commit Charge c (0n12)
Secured.Flink 0 Blink 0 Banked/Extend 0
File Offset 0
ImageMap ViewShare EXECUTE_WRITECOPY

ControlArea @ ffffdf006701a980
Segment ffffa40014d2ab40 Flink ffffdf00695889c0 Blink ffffdf0066ff7830
Section Ref 2 Pfn Ref 167 Mapped Views 51
User Ref 53 WaitForDel 0 Flush Count ac30
File Object ffffdf0067000900 ModWriteCount 0 System Views 9987
WritableRefs c0001e
Flags (a0) Image File

\Windows\System32\ntdll.dll

Segment @ ffffa40014d2ab40
ControlArea ffffdf006701a980 BasedAddress 00007ffd66590000
Total Ptes 1e0
Segment Size 1e0000 Committed 0
Image Commit c Image Info ffffa40014d2ab88
ProtoPtes ffffa40014e23010
Flags (c2822000) DebugSymbolsLoaded ProtectionMask

Reload command: .reload ntdll.dll=00007ffd`66590000,1e0000

kd> .reload ntdll.dll=00007ffd`66590000,1e0000
kd> lmu
start end module name
00007ffd`66590000 00007ffd`66770000 ntdll (pdb symbols)

再次查看调用栈回溯:

kd> kpn
# Child-SP RetAddr Call Site
00 fffff50f`809954f8 fffff801`6ef9abff nt!memcpy+0x2f
01 fffff50f`80995500 fffff801`6ef9a630 nt!MmCopyVirtualMemory+0x53f
02 fffff50f`809959d0 fffff801`6effa1d7 nt!MiReadWriteVirtualMemory+0x160
03 fffff50f`80995a50 fffff801`6ebf8d53 nt!NtWriteVirtualMemory+0x1b
04 fffff50f`80995a90 00007ffd`66630584 nt!KiSystemServiceCopyEnd+0x13
05 00000095`1e92e968 00000000`00000000 ntdll!NtWriteVirtualMemory+0x14

像这种到了5号栈帧就突然结束的调用栈回溯,一般涉及Attach操作:

kd> x nt!K*AttachProcess
fffff801`6eb7d880 nt!KeStackAttachProcess (void)
fffff801`6eb7d7d0 nt!KeAttachProcess (void)
fffff801`6eb7d950 nt!KiAttachProcess (void)
fffff801`6eb39540 nt!KiStackAttachProcess (void)

此时应该换种方式查看调用栈回溯:

kd> !thread -p -1 0xf
PROCESS ffffdf00660d4400
SessionId: 1 Cid: 2754 Peb: d6286a7000 ParentCid: 23d0
DirBase: 7b7aa000 ObjectTable: ffffa40019946980 HandleCount: 0.
Image: mspaint.exe

THREAD ffffdf0068d85700 Cid 23d0.11d8 Teb: 000000951eb83000 Win32Thread: ffffdf0068a1dbc0 RUNNING on processor 0
Not impersonating
Owning Process ffffdf00697ed5c0 Image: cdb.exe
Attached Process ffffdf00660d4400 Image: mspaint.exe
Wait Start TickCount 61160402 Ticks: 0
Context Switch Count 61 IdealProcessor: 0
UserTime 00:00:00.015
KernelTime 00:00:00.015
Win32 Start Address 0x00007ff7fdd6e3d0
Stack Init fffff50f80995c90 Current fffff50f809954e0
Base fffff50f80996000 Limit fffff50f80990000 Call 0000000000000000
Priority 14 BasePriority 13 PriorityDecrement 0 IoPriority 2 PagePriority 5
Child-SP RetAddr Call Site
fffff50f`809954f8 fffff801`6ef9abff nt!memcpy+0x2f
fffff50f`80995500 fffff801`6ef9a630 nt!MmCopyVirtualMemory+0x53f
fffff50f`809959d0 fffff801`6effa1d7 nt!MiReadWriteVirtualMemory+0x160
fffff50f`80995a50 fffff801`6ebf8d53 nt!NtWriteVirtualMemory+0x1b
fffff50f`80995a90 00007ffd`66630584 nt!KiSystemServiceCopyEnd+0x13 (TrapFrame @ fffff50f`80995b00)
00000095`1e92e968 00000000`00000000 ntdll!NtWriteVirtualMemory+0x14

上述显示表明,父进程是cdb,然后Attach到子进程mspaint。

kd> bp nt!NtWriteVirtualMemory+0x1b
kd> g

断点命中时果然已切回cdb:

kd> !thread -p -1 0x1f
PROCESS ffffdf00697ed5c0
SessionId: 1 Cid: 23d0 Peb: 951eb82000 ParentCid: 2e90
DirBase: 4766d000 ObjectTable: ffffa400176c6540 HandleCount: 109.
Image: cdb.exe

THREAD ffffdf0068d85700 Cid 23d0.11d8 Teb: 000000951eb83000 Win32Thread: ffffdf0068a1dbc0 RUNNING on processor 0
Not impersonating
DeviceMap ffffa400160f9e40
Owning Process ffffdf00697ed5c0 Image: cdb.exe
Attached Process N/A Image: N/A
Wait Start TickCount 61160402 Ticks: 0
Context Switch Count 61 IdealProcessor: 0
UserTime 00:00:00.015
KernelTime 00:00:00.015
Win32 Start Address cdb!wmainCRTStartup (0x00007ff7fdd6e3d0)
Stack Init fffff50f80995c90 Current fffff50f809954e0
Base fffff50f80996000 Limit fffff50f80990000 Call 0000000000000000
Priority 14 BasePriority 13 PriorityDecrement 0 IoPriority 2 PagePriority 5
Child-SP RetAddr Call Site
fffff50f`80995a50 fffff801`6ebf8d53 nt!NtWriteVirtualMemory+0x1b
fffff50f`80995a90 00007ffd`66630584 nt!KiSystemServiceCopyEnd+0x13 (TrapFrame @ fffff50f`80995b00)
00000095`1e92e968 00007ffd`6380196f ntdll!NtWriteVirtualMemory+0x14
00000095`1e92e970 00007ffd`637fd7a6 KERNELBASE!CreateProcessInternalW+0x2dcf
00000095`1e92f5d0 00007ffd`6623e4e3 KERNELBASE!CreateProcessW+0x66
00000095`1e92f640 00007ffd`4157caa2 KERNEL32!CreateProcessWStub+0x53
00000095`1e92f6a0 00007ffd`4139e43a dbgeng!LiveUserDebugServices::CreateProcessW+0x262
00000095`1e92f810 00007ffd`4135e054 dbgeng!LiveUserTargetInfo::StartCreateProcess+0x10a
00000095`1e92f870 00007ffd`4135df27 dbgeng!DebugClient::CreateProcessAndAttach2Wide+0x114
00000095`1e92f930 00007ff7`fdd653b9 dbgeng!DebugClient::CreateProcessAndAttachWide+0x77
00000095`1e92f9b0 00007ff7`fdd6a3c3 cdb!InitializeSession+0x5d5
00000095`1e92fa70 00007ff7`fdd6e359 cdb!wmain+0x3ef
00000095`1e92fd10 00007ffd`66241fe4 cdb!std::_Winerror_map+0x299
00000095`1e92fd50 00007ffd`665fef91 KERNEL32!BaseThreadInitThunk+0x14
00000095`1e92fd80 00000000`00000000 ntdll!RtlUserThreadStart+0x21

至此知道:

KERNELBASE!CreateProcessW+0x61
KERNELBASE!CreateProcessInternalW+0x1b39
ntdll!NtCreateUserProcess
nt!NtCreateUserProcess // cdb创建mspaint
KERNELBASE!CreateProcessInternalW+0x2dc9
ntdll!NtWriteVirtualMemory
nt!NtWriteVirtualMemory // cdb更新mspaint的PEB->pShimData

为了搞清楚SHIM初始化,必须逆向分析KERNELBASE!CreateProcessInternalW()。这里一定要记住,子进程的PEB->pShimData是由父进程填充的,假设用cdb调试mspaint,”sxe cpr”断下来时mspaint的PEB->pShimData已被填充,无法调试这个填充过程。可
以用kd调试cmd,也可以用cdb附加到cmd,然后在cmd里启动mspaint。

前面拦截nt!PspAllocateProcess(),根本目的是拦截子进程PEB的创建,进一步逆向
找到:

NTSTATUS MmCreatePeb
(
PEPROCESS TargetProcess, // rcx
PINITIAL_PEB InitialPeb, // rdx
PPEB *Base // r8 out
PVOID a4 // r9 out
);

————————————————————————–

可以直接拦截nt!MmCreatePeb(),对之设置条件断点,当目标进程是mspaint时断在

nt!MmCreatePeb():

Hit:[mspaint.exe]
...
nt!MmCreatePeb:
fffff801`6ef5e1d0 4c8bdc mov r11,rsp
kd> !thread -p -1 0x1f
PROCESS ffffdf0068f73080
SessionId: 1 Cid: 14a8 Peb: 8e7f1f4000 ParentCid: 0f38
DirBase: 473cd000 ObjectTable: ffffa400191eb6c0 HandleCount: 236.
Image: cmd.exe

THREAD ffffdf006873c080 Cid 14a8.3108 Teb: 0000008e7f1f5000 Win32Thread: ffffdf0067c90b00 RUNNING on processor 0
Not impersonating
DeviceMap ffffa400160f9e40
Owning Process ffffdf0068f73080 Image: cmd.exe
Attached Process N/A Image: N/A
Wait Start TickCount 61303344 Ticks: 1 (0:00:00:00.015)
Context Switch Count 392 IdealProcessor: 0
UserTime 00:00:00.031
KernelTime 00:00:00.109
Win32 Start Address cmd!mainCRTStartup (0x00007ff689ec6b00)
Stack Init fffff50f7febcc90 Current fffff50f7febc480
Base fffff50f7febd000 Limit fffff50f7feb7000 Call 0000000000000000
Priority 10 BasePriority 8 PriorityDecrement 2 IoPriority 2 PagePriority 5
Child-SP RetAddr Call Site
fffff50f`7febb9b8 fffff801`6ef2698c nt!MmCreatePeb
fffff50f`7febb9c0 fffff801`6ef1f657 nt!PspAllocateProcess+0xf10
fffff50f`7febbe50 fffff801`6ebf8d53 nt!NtCreateUserProcess+0x723
fffff50f`7febca90 00007ffd`66631654 nt!KiSystemServiceCopyEnd+0x13 (TrapFrame @ fffff50f`7febcb00)
0000008e`7efae438 00007ffd`638006df ntdll!NtCreateUserProcess+0x14
0000008e`7efae440 00007ffd`637fd7a6 KERNELBASE!CreateProcessInternalW+0x1b3f
0000008e`7efaf0a0 00007ffd`6623e4e3 KERNELBASE!CreateProcessW+0x66
0000008e`7efaf110 00007ff6`89ebaec8 KERNEL32!CreateProcessWStub+0x53
0000008e`7efaf170 00007ff6`89ec39e3 cmd!ExecPgm+0x244
0000008e`7efaf3b0 00007ff6`89ebfb9e cmd!ECWork+0xa3
0000008e`7efaf620 00007ff6`89ebff35 cmd!FindFixAndRun+0x3de
0000008e`7efafac0 00007ff6`89ecd0d1 cmd!Dispatch+0xa5
0000008e`7efafb50 00007ff6`89ec6a89 cmd!_chkstk+0x5241
0000008e`7efafbf0 00007ffd`66241fe4 cmd!wil::details_abi::ProcessLocalStorage<wil::details_abi::ProcessLocalData>::~ProcessLocalStorage<wil::details_abi::ProcessLocalData>+0x289
0000008e`7efafc30 00007ffd`665fef91 KERNEL32!BaseThreadInitThunk+0x14
0000008e`7efafc60 00000000`00000000 ntdll!RtlUserThreadStart+0x21

kd调试经常需要”.reload /user”,还是用cdb附加到cmd调试比较方便。

在KERNELBASE!CreateProcessInternalW()中看到若干与SHIM相关的导入函数,来自
kernel32.dll。”x KERNEL32!*AppCompat*”可以看到更多相关符号,比如:

KERNEL32!BasepQueryAppCompat
KERNEL32!BasepGetAppCompatData
KERNEL32!BasepInitAppCompatData
KERNEL32!IsTSAppCompatEnabled
KERNEL32!IsRegTSAppCompatEnabled
KERNEL32!BasepCheckAppCompat
KERNEL32!BaseGenerateAppCompatData

设断:

bp KERNEL32!BasepQueryAppCompat
bp KERNEL32!BasepGetAppCompatData
bp KERNEL32!BasepInitAppCompatData
bp KERNEL32!IsTSAppCompatEnabled
bp KERNEL32!IsRegTSAppCompatEnabled
bp KERNEL32!BasepCheckAppCompat
bp KERNEL32!BaseGenerateAppCompatData

mspaint启动过程中依次看到如下几种调用栈回溯:

KERNEL32!BasepQueryAppCompat
KERNELBASE!CreateProcessInternalW+0x27b3
KERNELBASE!CreateProcessW+0x66
KERNEL32!CreateProcessWStub+0x53
cmd!ExecPgm+0x244

KERNEL32!BasepGetAppCompatData
KERNELBASE!CreateProcessInternalW+0x2d03
KERNELBASE!CreateProcessW+0x66
KERNEL32!CreateProcessWStub+0x53
cmd!ExecPgm+0x244

KERNEL32!BaseGenerateAppCompatData
KERNEL32!BasepGetAppCompatData+0x391
KERNELBASE!CreateProcessInternalW+0x2d03
KERNELBASE!CreateProcessW+0x66
KERNEL32!CreateProcessWStub+0x53
cmd!ExecPgm+0x244

mspaint、notepad、calc都会触发BasepQueryAppCompat、BasepGetAppCompatData,但notepad、calc不会触发BaseGenerateAppCompatData,这与mspaint不同。

前面已知父进程的KERNELBASE!CreateProcessInternalW()会调用ntdll!NtWriteVirtualMemory()去写子进程的PEB->pShimData,检查父进程附近代码
得知:

KERNELBASE!CreateProcessW+0x61
KERNELBASE!CreateProcessInternalW+0x1b39
ntdll!NtCreateUserProcess // cmd创建mspaint
KERNELBASE!CreateProcessInternalW+0x2d53
ntdll!NtAllocateVirtualMemory // cmd在mspaint进程空间分配内存,将来用于存放SHIM数据
KERNELBASE!CreateProcessInternalW+0x2d8d
ntdll!NtWriteVirtualMemory // cmd向mspaint进程空间写入SHIM数据
KERNELBASE!CreateProcessInternalW+0x2dc9
ntdll!NtWriteVirtualMemory // cmd更新mspaint的PEB->pShimData

实测mspaint、notepad、calc都有上述流程。对比mspaint、notepad、calc的SHIM数
据,发现它们都是0xfb8字节,可以解码其中一部分:

b8 0f 00 00 // +0x0 长度域,包含自身
ab ed 0d ac // +0x4 固定值,猜是magic
01 00 00 00 // +0x8 mspaint有值,notepad、calc是0
64 86 // +0xc 固定值
00 00 // +0xe
c4 19 13 00 // +0x10 mspaint有值,notepad、calc是0
...
00 00 01 00 // +0x50 mspaint有值,notepad、calc是0
...
01 00 00 00 // +0xb8 mspaint有值,notepad、calc是0
...
01 08 00 00 // +0xd0 mspaint有值,notepad、calc是0
01 00 00 00 // +0xd4 mspaint有值,notepad、calc是0
//
// 11111111-1111-1111-1111-111111111111
//
11 11 11 11 11 11 11 11 // +0xd8 DBID,参mspaint.xml
11 11 11 11 11 11 11 11
...
41 70 70 68 65 6c 70 44 // +0x1d8 初始值为0,ibp时填成"ApphelpDebug"
65 62 75 67 00
...
00 04 00 00 // +0x224
00 10 00 00 // +0x228
00 00 00 00 // +0x22c
02 00 00 00 // +0x230
73 68 69 6d 65 6e 67 73 // +0x234 初始值为0,ibp时填成"shimengstate"
74 61 74 65 00
...
//
// .writemem mspaint.xml 0x029421db8320 l 0x554
//
20 83 db 21 94 02 00 00 // +0x274 buf=0x029421db8320,一个指针
54 05 00 00 // +0x27c buflen=0x554,上面这个指针指向的内存的字节数
...
02 00 00 00 // +0x28c
53 68 69 6d 44 65 62 75 // +0x290 初始值为0,ibp时填成"ShimDebugLog"
67 4c 6f 67 00
...
//
// 50 52 49 4e 54 2c 4e 6f-74 69 66 79 46 6e 2c 33 PRINT,NotifyFn,3
// 37 32 2c 47 65 6e 65 72-61 6c 20 50 75 72 70 6f 72,General Purpo
// 73 65 20 53 68 69 6d 73-20 69 6e 69 74 69 61 6c se Shims initial
// 69 7a 65 64 2e 0d 0a ized...
//
// "PRINT,NotifyFn,372,General Purpose Shims initialized.\r\n"
//
00 3e dc 21 94 02 00 00 // +0x2d0 buf=0x029421dc3e00,一个指针
37 00 00 00 // +0x2d8 buflen=0x37,上面这个指针指向的内存的字节数
00 10 00 00 // +0x2dc
00 10 00 00 // +0x2e0
00 00 00 00 // +0x2e4
02 00 00 00 // +0x2e8
...
03 00 00 00 00 00 00 00 // +0x5d8 初始值为1,ibp时为3
01 00 00 00 00 00 00 00 // +0x5e0 固定值
...
00 00 0a 00 // +0x5f4 固定值
03 00 00 00 // +0x5f8 固定值
...
//
// !pde.guid
// 8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a Win10
// e2011457-1546-43c5-a5fe-008deee3d3f0 Vista
//
12 7a 0f 8e b3 bf e8 4f // +0x608 MaxSupportedOS GUID
b9 a5 48 fd 50 a1 5a 9a
57 14 01 e2 46 15 c5 43 // +0x618 MinSupportedOS GUID
a5 fe 00 8d ee e3 d3 f0 // MinSupportedOS初始值同MaxSupportedOS,ibp时发生变化
01 00 00 00 // +0x628 固定值
...
a8 14 00 00 // +0x930 父进程PID,0x14a8(5288)
43 00 3a 00 5c 00 57 00 // +0x934 父进程路径,C:\Windows\System32\cmd.exe
69 00 6e 00 64 00 6f 00
77 00 73 00 5c 00 53 00
79 00 73 00 74 00 65 00
6d 00 33 00 32 00 5c 00
63 00 6d 00 64 00 2e 00
65 00 78 00 65 00 00 00
...
00 94 66 00 // +0xf3c 文件大小(dir查看),0x669400(6722560)
70 31 67 00 // +0xf40 PE校验和,0x673170
...
01 00 00 00 // +0xf50 固定值
00 00 00 00 // +0xf54
01 00 00 00 // +0xf58
00 00 00 00 // +0xf5c
...
00 00 ff ff // +0xf98 mspaint有值,notepad、calc是0
...
03 00 00 00 // +0xfa0 mspaint有值,notepad、calc是0

————————————————————————–

mspaint.xml如下:

<?xml version="1.0" encoding="utf-8"?>
<MATCHED_ENTRIES>
<EXE NAME="mspaint.exe" ID="{b4f8a960-7b73-4a56-93bf-779951036962}" DBID="{11111111-1111-1111-1111-111111111111}"/>
</MATCHED_ENTRIES>
<SHIMENGSTATE PID="5220" FILENAME="C:\WINDOWS\system32\mspaint.exe" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:schemas.microsoft.com/appcompat/2010/03/shimengstate EngineState.xsd" xmlns="urn:schemas.microsoft.com/appcompat/2010/03/shimengstate">
<INEX MODE="EXCLUDE_SYSTEM32">
<INCLUDE MODULE="OLE32.dll"/>
<INCLUDE MODULE="OLEAUT32.dll"/>
<INCLUDE MODULE="MSVCRT.dll"/>
<INCLUDE MODULE="MSVCIRT.dll"/>
<INCLUDE MODULE="MSVCRT20.dll"/>
<INCLUDE MODULE="MSVCRT40.dll"/>
<INCLUDE MODULE="MFC40.dll"/>
<INCLUDE MODULE="MFC42.dll"/>
<INCLUDE MODULE="MFCSUBS.dll"/>
<INCLUDE MODULE="MFC42ENU.DLL"/>
<INCLUDE MODULE="MFCN42D.DLL"/>
<INCLUDE MODULE="MFCD42D.DLL"/>
<INCLUDE MODULE="MFCO42D.DLL"/>
<INCLUDE MODULE="MFC42D.DLL"/>
<INCLUDE MODULE="KERNEL32.DLL"/>
<EXCLUDE MODULE="BLACKBOX.DLL"/>
<EXCLUDE MODULE="FWCWSP.dll"/>
<EXCLUDE MODULE="FWCWSP64.dll"/>
</INEX>
<FIXES>
<SHIM NAME="SYSTEM">
</SHIM>
<SHIM NAME="ConfigureDownlevelInput" COMMAND_LINE="-HorizontalScrollMode 2">
</SHIM>
</FIXES>
</SHIMENGSTATE>

————————————————————————–

用Process Explorer搜索apphelp.dll,发现如下进程也加载了它:

dwm.exe
winlogon.exe
svchost.exe (要进一步检查对应的服务)
RuntimeBroker.exe
explorer.exe
cmd.exe
cdb.exe

检查cmd的SHIM数据,对比mspaint、cmd:

+0x1d8 "ApphelpDebug"
+0x234 "shimengstate"->"PcaCliTrace"
+0x274 0x029421db8320->0x01efc4007c50,一个指针,指向"TRACE..."
+0x27c 0x554->0x4e,buflen
+0x290 "ShimDebugLog"->"PcaCliDebug"
+0x930 父进程PID,0x14a8(5288)->0xf38(3896)
+0x934 "C:\Windows\System32\cmd.exe"->"C:\WINDOWS\Explorer.EXE"
+0xf3c 文件大小(dir查看)
+0xf40 PE校验和

> da /c 64 0x01efc4007c50
000001ef`c4007c50 "TRACE,0000,2600,PcaClient,Excluded,C:\WINDOWS\system32\mmc.exe,SFC protected.."
> db 0x01efc4007c50 l 0x4e
000001ef`c4007c50 54 52 41 43 45 2c 30 30-30 30 2c 32 36 30 30 2c TRACE,0000,2600,
000001ef`c4007c60 50 63 61 43 6c 69 65 6e-74 2c 45 78 63 6c 75 64 PcaClient,Exclud
000001ef`c4007c70 65 64 2c 43 3a 5c 57 49-4e 44 4f 57 53 5c 73 79 ed,C:\WINDOWS\sy
000001ef`c4007c80 73 74 65 6d 33 32 5c 6d-6d 63 2e 65 78 65 2c 53 stem32\mmc.exe,S
000001ef`c4007c90 46 43 20 70 72 6f 74 65-63 74 65 64 0d 0a FC protected..

至此可以自定义几个用于IDA逆向分析的结构:

#pragma pack(push,1)

struct SomeStruct_0
{
unsigned int unknown_0; // +0x0
unsigned int unknown_4; // +0x4
unsigned int unknown_8; // +0x8
unsigned int unknown_c; // +0xc
unsigned int unknown_10; // +0x10
unsigned int unknown_14; // +0x14
unsigned int unknown_18; // +0x18
unsigned int unknown_1c; // +0x1c
unsigned int unknown_20; // +0x20
unsigned int unknown_24; // +0x24
unsigned int unknown_28; // +0x28
unsigned int unknown_2c; // +0x2c
unsigned int unknown_30; // +0x30
unsigned int unknown_34; // +0x34
unsigned int unknown_38; // +0x38
unsigned int unknown_3c; // +0x3c
unsigned int unknown_40; // +0x40
unsigned int unknown_44; // +0x44
/*
* 11111111-1111-1111-1111-111111111111
*/
GUID DBID; // +0x48 参mspaint.xml
unsigned int unknown_58; // +0x58
unsigned int unknown_5c; // +0x5c
unsigned int unknown_60; // +0x60
unsigned int unknown_64; // +0x64
unsigned int unknown_68; // +0x68
unsigned int unknown_6c; // +0x6c
unsigned int unknown_70; // +0x70
unsigned int unknown_74; // +0x74
unsigned int unknown_78; // +0x78
unsigned int unknown_7c; // +0x7c
// +0x80
};

struct SomeStruct_1
{
SomeStruct_0 struct_0; // +0x0
SomeStruct_0 struct_80; // +0x80
SomeStruct_0 struct_100; // +0x100
unsigned char unknown_180[0x48]; // +0x180
// +0x1c8
};

struct ShimData
{
unsigned int ShimDataLen; // +0x0 长度域,包含自身,固定值,0xFB8
unsigned int magic; // +0x4 固定值,0xAC0DEDAB
unsigned int unknown_8; // +0x8
/*
* ? wo(@@c++(@$peb->ImageBaseAddress)+dwo(@@c++(@$peb->ImageBaseAddress)+0x3c)+0x4)
* dt ntdll!_IMAGE_DOS_HEADER e_lfanew
* dt ntdll!_IMAGE_NT_HEADERS64 FileHeader.Machine
*/
unsigned short int Machine; // +0xc 0x8664(34404)表示x64
unsigned short int unknown_e; // +0xe
SomeStruct_1 struct_10; // +0x10
unsigned char str_1d8[0x40]; // +0x1d8 ibp时填成"ApphelpDebug"
unsigned int unknown_218; // +0x218
unsigned int unknown_21c; // +0x21c
unsigned int unknown_220; // +0x220
unsigned int unknown_224; // +0x224
unsigned int unknown_228; // +0x228
unsigned int unknown_22c; // +0x22c
unsigned int unknown_230; // +0x230
unsigned char str_234[0x40]; // +0x234 ibp时填成"shimengstate"
unsigned char *buf_274; // +0x274
unsigned int buflen_27c; // +0x27c
unsigned char unknown_280[0xc]; // +0x280
unsigned int unknown_28c; // +0x28c
unsigned char str_290[0x40]; // +0x290 ibp时填成"ShimDebugLog"
unsigned char *buf_2d0; // +0x2d0
unsigned int buflen_2d8; // +0x2d8
unsigned int unknown_2dc; // +0x2dc
unsigned int unknown_2e0; // +0x2e0
unsigned int unknown_2e4; // +0x2e4
unsigned int unknown_2e8; // +0x2e8
unsigned char unknown_2ec[0x2ec]; // +0x2ec
unsigned int unknown_5d8; // +0x5d8
unsigned int unknown_5dc; // +0x5dc
unsigned int unknown_5e0; // +0x5e0
unsigned char unknown_5e4[0x10]; // +0x5e4
unsigned int unknown_5f4; // +0x5f4
unsigned int unknown_5f8; // +0x5f8
unsigned char unknown_5fc[0xc]; // +0x5fc
/*
* !pde.guid
* 8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a Win10
* e2011457-1546-43c5-a5fe-008deee3d3f0 Vista
*/
GUID MaxSupportedOS; // +0x608
GUID MinSupportedOS; // +0x618
unsigned int unknown_628; // +0x628
unsigned char unknown_62c[0x304]; // +0x62c
unsigned int ParentPid; // +0x930 父进程PID
wchar_t ParentPath[0x104]; // +0x934 父进程路径
unsigned char unknown_b3c[0x400]; // +0xb3c
unsigned int FileSize; // +0xf3c 文件大小(dir查看)
unsigned int PESum; // +0xf40 PE校验和
unsigned int boolean_f44; // +0xf44
unsigned int unknown_f48; // +0xf48
unsigned int unknown_f4c; // +0xf4c
unsigned int unknown_f50; // +0xf50
unsigned int boolean_f54; // +0xf54
unsigned int unknown_f58; // +0xf58
unsigned int unknown_f5c; // +0xf5c
unsigned char unknown_f60[0x38]; // +0xf60
unsigned int unknown_f98; // +0xf98
unsigned int unknown_f9c; // +0xf9c
unsigned int unknown_fa0; // +0xfa0
unsigned char unknown_fa4[0x14]; // +0xfa4
// +0xfb8
};

#pragma pack(pop)

————————————————————————–

写入子进程的SHIM数据由父进程调用KERNEL32!BasepGetAppCompatData()产生:

void BasepGetAppCompatData
(
a1, // rcx
a2, // rdx
a3, // r8
a4, // r9
a5, // rsp+0x28
a6, // rsp+0x30
/*
* ? wo(@@c++(@$peb->ImageBaseAddress)+dwo(@@c++(@$peb->ImageBaseAddress)+0x3c)+0x4)
* dt ntdll!_IMAGE_DOS_HEADER e_lfanew
* dt ntdll!_IMAGE_NT_HEADERS64 FileHeader.Machine
*/
unsigned short int Machine, // rsp+0x38
a8, // rsp+0x40
a9, // rsp+0x48
/*
* ? poi(poi(@rsp+0x50))
* ? dwo(poi(@rsp+0x58))
*/
SomeStruct_1 **a10, // rsp+0x50 in
/*
* *all==sizeof(SomeStruct_1)
*/
unsigned int *a11, // rsp+0x58 in
unsigned char **out, // rsp+0x60 out
unsigned int *outlen // rsp+0x68 out
);

————————————————————————–

BasepGetAppCompatData()对如下三个EXE做了特别对待:

wwahost.exe
microsoftedge.exe
microsoftedgecp.exe

用wt命令对比分析mspaint、notepad在KERNEL32!BasepGetAppCompatData()中的流程,确认mspaint的*a10、*a11有值,notepad则无,这些数据将来会复制到ShimData中。
BasepGetAppCompatData()的*a10等于NULL时,导致notepad不会调用
BaseGenerateAppCompatData()。需要进一步分析CreateProcessInternalW(),以了解BasepGetAppCompatData()的*a10何时不为NULL。*a10是由BasepQueryAppCompat()
填充的:

void BasepQueryAppCompat
(
a1, // rcx
a2, // rdx
a3, // r8
a4, // r9
HANDLE ProcessHandle, // rsp+0x28
a6, // rsp+0x30
a7, // rsp+0x38
a8, // rsp+0x40
/*
* 对应BasepGetAppCompatData()的a10
*/
SomeStruct_1 **x9, // rsp+0x48
/*
* 对应BasepGetAppCompatData()的a11
*
* sizeof(SomeStruct_1)==0x1c8
*/
unsigned int *x10, // rsp+0x50
a11, // rsp+0x58
a12, // rsp+0x60
a13, // rsp+0x68
a14, // rsp+0x70
a15, // rsp+0x78
a16, // rsp+0x80
a17, // rsp+0x88
a18 // rsp+0x90
);

————————————————————————–

顺着BasepQueryAppCompat()找到CompatCacheLookupAndWriteToProcess(),mspaint和notepad在后者的流程会分叉。

分析CompatCacheLookupAndWriteToProcess(),越来越混乱。至此仍然无法解释为什么mspaint有Shim机制介入,而notepad没有,这个配置到底在哪里?

观察到mspaint会加载apphelp.dll、映射sysmain.sdb,猜测cmd在创建mspaint时也会映射sysmain.sdb,也就是说mspaint实际涉及2次sysmain.sdb。猜测x64/Win10不分32、64位,只有一份sysmain.sdb,AppPatch64子目录是个摆设。

监控cmd对sysmain.sdb的映射,看到调用栈回溯:

Hit:[C:\Windows\apppatch\sysmain.sdb]

rax=0000000000000000 rbx=0000000000000000 rcx=00007ffd66630344
rdx=0000000000000000 rsi=000001f959c82f00 rdi=000001f959c82f68
rip=00007ffd60e77f17 rsp=000000c933b7e0f0 rbp=000000c933b7e169
r8=000000c933b7e0e8 r9=000000c933b7e169 r10=0000000000000000
r11=0000000000000246 r12=0000000000000001 r13=0000000000000001
r14=0000000000000000 r15=0000000000000000
iopl=0 nv up ei pl zr na po nc
cs=0033 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246
apphelp!RtlFileMapMapView+0x137:
00007ffd`60e77f17 8bd8 mov ebx,eax
> kpn
# Child-SP RetAddr Call Site
00 000000c9`33b7e0f0 00007ffd`60e77ca9 apphelp!RtlFileMapMapView+0x137
01 000000c9`33b7e1d0 00007ffd`60e77c04 apphelp!AslFileMappingEnsureMappedAs+0x35
02 000000c9`33b7e210 00007ffd`60e77ac8 apphelp!SdbOpenDatabaseEx+0x8c
03 000000c9`33b7e260 00007ffd`60e763f8 apphelp!SdbInitDatabaseEx+0xa8
04 000000c9`33b7e500 00007ffd`66237e6f apphelp!ApphelpCreateAppcompatData+0xa8
05 000000c9`33b7e550 00007ffd`66237891 KERNEL32!BaseGenerateAppCompatData+0x5f
06 000000c9`33b7e5a0 00007ffd`638018a3 KERNEL32!BasepGetAppCompatData+0x391
07 000000c9`33b7e670 00007ffd`637fd7a6 KERNELBASE!CreateProcessInternalW+0x2d03
08 000000c9`33b7f2d0 00007ffd`6623e4e3 KERNELBASE!CreateProcessW+0x66
09 000000c9`33b7f340 00007ff6`89ebaec8 KERNEL32!CreateProcessWStub+0x53
0a 000000c9`33b7f3a0 00007ff6`89ec39e3 cmd!ExecPgm+0x244
> u 7ffd`60e77f17-6 l 1
apphelp!RtlFileMapMapView+0x131:
00007ffd`60e77f11 ff15811e0400 call qword ptr [apphelp!_imp_ZwMapViewOfSection (00007ffd`60eb9d98)]
> dps apphelp!_imp_ZwMapViewOfSection l 1
00007ffd`60eb9d98 00007ffd`66630330 ntdll!NtMapViewOfSection

cmd创建mspaint时果然先映射了一次sysmain.sdb。

至此知道:

KERNELBASE!CreateProcessW+0x61
KERNELBASE!CreateProcessInternalW+0x1b39
ntdll!NtCreateUserProcess // cmd创建mspaint
KERNELBASE!CreateProcessInternalW+0x2cfd
KERNEL32!BasepGetAppCompatData+0x38c
KERNEL32!BaseGenerateAppCompatData+0x59 // notepad不会过此
apphelp!ApphelpCreateAppcompatData+0xa3
apphelp!SdbInitDatabaseEx+0xa3
apphelp!SdbOpenDatabaseEx+0x87 // cmd打开sysmain.sdb
KERNELBASE!CreateProcessInternalW+0x2d53
ntdll!NtAllocateVirtualMemory // cmd在mspaint进程空间分配内存,将来用于存放SHIM数据
KERNELBASE!CreateProcessInternalW+0x2d8d
ntdll!NtWriteVirtualMemory // cmd向mspaint进程空间写入SHIM数据
KERNELBASE!CreateProcessInternalW+0x2dc9
ntdll!NtWriteVirtualMemory // cmd更新mspaint的PEB->pShimData

尽管确认cmd创建mspaint时,自己会映射一次sysmain.sdb,但这不是最早的分叉点。
CompatCacheLookupAndWriteToProcess()所暗示的”Compatibility Cache”是什么东西,从哪里来的?

放狗搜”Windows Compatibility Cache”,找到这些内容:

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\AppCompatCache]
"AppCompatCache"=hex:...
"CacheMainSdb"=hex:...
"SdbTime"=hex:...

————————————————————————–

这3个键值都是REG_BINARY类型。

C:\Windows\appcompat\Programs\Amcache.hve
C:\Windows\AppCompat\Programs\RecentFileCache.bcf

$ sc qc ahcache

SERVICE_NAME: ahcache
TYPE : 1 KERNEL_DRIVER
START_TYPE : 1 SYSTEM_START
ERROR_CONTROL : 1 NORMAL
BINARY_PATH_NAME : system32\DRIVERS\ahcache.sys
LOAD_ORDER_GROUP :
TAG : 0
DISPLAY_NAME : Application Compatibility Cache
DEPENDENCIES :
SERVICE_START_NAME :

$ sc qc PcaSvc

SERVICE_NAME: PcaSvc
TYPE : 20 WIN32_SHARE_PROCESS
START_TYPE : 3 DEMAND_START
ERROR_CONTROL : 1 NORMAL
BINARY_PATH_NAME : C:\WINDOWS\system32\svchost.exe -k LocalSystemNetworkRestricted -p
LOAD_ORDER_GROUP :
TAG : 0
DISPLAY_NAME : Program Compatibility Assistant Service
DEPENDENCIES : RpcSs
SERVICE_START_NAME : LocalSystem

kd> x ahcache!AhcCacheHandle

看了一圈,估计是这个意思。一个程序是否需要Shim机制介入,归根结底还是sysmain.sdb在起作用。如果某程序第一次执行时被判定需要Shim机制介入,会在”Compatibility Cache”里缓存相应信息。当系统重启、关闭时,”Compatibility Cache”里缓存的信息被序列化后存入AppCompatCache子键下。程序
执行时,先在”Compatibility Cache”里找,找不到了再去sysmain.sdb里找,以此判定是否需要Shim机制介入。

我用下面这个工具看sysmain.sdb:

Windows Shim Database (SDB) Parser (shims) – David Tomczak
https://tzworks.net/prototype_page.php?proto_id=33

在其中找到:

<exe name="mspaint.exe" vendor="Microsoft Corporation" exeid="b4f8a960-7b73-4a56-93bf-779951036962" mitigation_os="set" runtimeplatform="0x00000003" url_id="0x00000001">
<app appname="Microsoft Paint" appid="ffb6c136-4a53-4669-ae3e-ca70a77b4044" />
<matchingfile name="mspaint.exe" companyname="Microsoft Corporation" productname="Microsoft* Windows* Operating System" filedescription="Paint" />
<shimref name="ConfigureDownlevelInput" shimtagid="256fc" commandline="-HorizontalScrollMode 2">
<shim name="ConfigureDownlevelInput" dllfile="AcGenral.dll" fixid="42ea0dd9-bce3-4bd8-8056-381edbbb2d0c" general="set" description_rcid="0x00000000">
<include module="ninput.dll" />
</shim>
</shimref>
</exe>

————————————————————————–

与之前从内存中转储得来的mspaint.xml做个对比,关键内容相符合。

最初没有特别明确的目标,只是想用调试器看看,如果能找到一些关键点,即使当下不深究,将来需要时也可以提纲挈领式地继续。后来工作安排有变动,不打算看Shim机制了,但已经有过一些调试分析,不忍舍弃,留于此间。本文未做Shim机制科普,最后也无明确的指导性结论,但个人觉得有一些通用逆向分析思路藏于其中。这些思考过程不是最优的,但这种基于当下知识储备展开的逐步迭代、推进,显得格外真实,
供读者借鉴。

Spread the word. Share this post!

Meet The Author

C/ASM程序员

Leave Comment