Windows Shim Engine,即Windows 兼容性模式实现引擎,在exe文件的属性对话框中有一个兼容性选项卡,用户可设置此exe程序完美工作的系统版本,Windows会尝试模拟老的系统环境运行此程序。
那Windows是如何模拟的呢?Windows认为老程序出问题的原因在于它们调用的API上,因新版本的Windows更新API,或者加入新的flag,或者取消老的API功能等等因素,如果老的程序在新版本的Win上不正确的使用了老API(如ChangeDisplayConfig等),则会出现错误或无法达到预期的效果,导致后续的一连串错误发生。所以兼容性模式引擎的核心原理就十分简单了——修复那些有问题的API调用。
如何修复?不外乎Hook。
如何Hook?很多应用程序可不会有HotPatch这种预留的东西,所以兼容性模式引擎使用的是比较安全通用的IAT Hook。
而出问题的API多数在用户模式,所以兼容性引擎核心也运行在R3层。
本文的研究基本上完全在Win8进行,对Win7有一定相通性,不过据我所知,Shim Engine从XP到Win8一直在变动,所以这篇文章仅仅只能是参考而已。
ReactOS有XP的Shim Engine实现源代码,Google搜索LdrpLoadShimEngine即可看到。
1)兼容性模式引擎Dll的载入
兼容性模式引擎的核心Dll有2个,分别为ntdll.dll和apphelp.dll,其中ntdll扮演着统辖全局的工作,apphelp则负责Sdb解包及逻辑判断和为功能实现核心做跳板,其他的诸如AcLayers.dll则为功能实现引擎。
随便让一个程序开启兼容性,OD载入,让其停在LdrInitializeThunk,我把启动过程Dump出来,就像下面:
LdrInitializeThunk
LdrpInitialize
_LdrpInitialize
_LdrpInitializeProcess
_LdrpInitializeNlsInfo(RtlInitNlsTables\RtlResetRtlTranslations)
_LdrpInitializeExecutionOptions
_RtlpInitDeferredCriticalSection
RtlInitializeBitMap(Fls)
RtlInitializeBitMap(Tls)
RtlInitializeBitMap(TlsExpansion)
RtlInitializeCriticalSectionEx(for RtlAcquirePebLock)
_RtlInitializeHeapManager(use NtGlobalFlags)
RtlCreateHeap
RtlAllocateActivationContextStack
RtlInitializeSListHead(for Etw)
_TpInitializePackage
RtlReleaseMemoryStream
RtlpInitEnvironmentBlock
RtlpInitParameterBlock
ZwOpenDirectoryObject(use _LdrpKnownDllDirectoryHandle)
ZwOpenSymbolicLinkObject
ZwQuerySymbolicLinkObject(use _LdrpKnownDllPath)
ZwClose
_LdrpInitializeDllPath
_LdrpInitializeLoadContext
LdrpAllocateDataTableEntry
LdrpProcessMappedModule
RtlpInitCurrentDir
LdrpAllocateTls
LdrLoadDll(_LdrpKernel32DllName)
LdrGetProcedureAddress(_Kernel32ThreadInitThunkFunction)
LdrGetProcedureAddress(TermsrvGetWindowsDirectoryW)
LdrGetProcedureAddress(BaseQueryModuleData)
LdrpCodeAuthzInitialize
ZwQueryInformationProcess(ProcessExecuteFlags)
[B]SbObtainTraceHandle(Query pShimData)->LdrpInitShimEngine[/B]
LdrpAcquireLoaderLock(_LdrpModuleEnumLock\_LdrpLoaderLock)
LdrpPrepareModuleForExecution(->Load IAT Modules)
LdrpReleaseLoaderLock
kernel32!_IsSystemLUID
kernel32!_IsTSAppCompatEnabled
LdrpInitializePerUserWindowsDirectory(->TermsrvGetWindowsDirectoryW)
LdrpAcquireLoaderLock
LdrpReleaseLoaderLock
LdrpReleaseDllPath
ZwTestAlert
SE_InitializeEngine -> SE核心初始化,这个函数最先被ntdll执行
NTSTATUS WINAPI SE_InitializeEngine(PUNICODE_STRING pusCoreDllFile,PUNICODE_STRING pusExecuteFileName,PVOID pShimData)
这个必须返回STATUS_SUCCESS
SE_InstallBeforeInit
BOOL WINAPI SE_InstallBeforeInit(PUNICODE_STRING pusExecuteFileName,PVOID pShimData) 这个必须返回TRUE
VOID WINAPI SE_InstallBeforeInit() //WIN8
SE_InstallAfterInit -> 引擎初始化完成后这个函数会被调用
BOOL WINAPI SE_InstallAfterInit(PUNICODE_STRING pusExecuteFileName,PVOID pShimData)
这个必须返回TRUE
SE_ShimDllLoaded -> hModule == AcLayers
VOID WINAPI SE_ShimDllLoaded(HMODULE hModule)
SE_DllLoaded -> Dll载入通知,同LdrRegisterDllNotification
VOID WINAPI SE_DllLoaded(PLDR_DATA_TABLE_ENTRY pLdrModuleLoaded)
SE_DllUnloaded -> Dll卸载通知
VOID WINAPI SE_DllUnloaded(PLDR_DATA_TABLE_ENTRY pLdrModuleUnload)
SE_LdrEntryRemoved
VOID WINAPI SE_LdrEntryRemoved(PLDR_DATA_TABLE_ENTRY pLdrEntryRemoved);
SE_ProcessDying
VOID WINAPI SE_ProcessDying();
SE_LdrResolveDllName -> 经常被调用
VOID WINAPI SE_LdrResolveDllName(PUNICODE_STRING pusUnknown,PVOID pvModuleDataUnk,PUNICODE_STRING pusModuleFileName)
SE_GetProcAddressLoad -> WIN7才有
VOID WINAPI SE_GetProcAddressLoad(PLDR_DATA_TABLE_ENTRY pLdrEntry)
SE_GetProcAddressForCaller -> 经常被调用
VOID WINAPI SE_GetProcAddressForCaller(PVOID pvUnknown0,PVOID pvUnknown1,PVOID pfnCallProcAddr,ULONG_PTR ulZero,PVOID pfnReturnToAddr)
ApphelpCheckModule
BOOL WINAPI ApphelpCheckModule(PUNICODE_STRING pusModuleName,PVOID pvUnknown1,PVOID pvUnknown2,PVOID pvUnknown3,PVOID pvUnknown4,PVOID pvUnknown5,PVOID pvUnknown6)
SE_InitializeEngine
HANDLE WINAPI SdbInitDatabaseEx(DWORD dwZero0,DWORD dwZero1,DWORD dwFlags); dwFlags == 0x14C
BOOL WINAPI SdbUnpackAppCompatData(HANDLE hInitData,LPWSTR lpszExeFile,PVOID pShimData,PVOID pvUnpackData);
VOID WINAPI SdbReleaseDatabase(HANDLE hInitData);
apphelp._SepSdbProcessShim@28->SdbGetDllPath
Private Declare Function SdbSetPermLayerKeys& Lib "apphelp" (ByVal lpszExeFile&, ByVal lpszSystemMode&, ByVal flags&)
SdbSetPermLayerKeys StrPtr("C:\1.exe"), StrPtr("~ WINXPSP3"), 0
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!