cfg如何工作
reference:
- https://www.powerofcommunity.net/poc2014/mj0011.pdf
- https://windows-internals.com/understanding-a-new-mitigation-module-tampering-protection/
在阅读upx源码的时候发现默认不支持开启了cfg的pe文件,比如尝试给notepad.exe加壳就会出现报错
看一下cfg是如何实现的
cfg可以在vs的编译选项中开启
这里有一个使用了间接调用的示例代码
开启cfg
关闭cfg
开启了cfg的pe相比关闭cfg的pe中会多出来这些东西
开启cfg
关闭cfg
这里面有如下三个关键部分
- cfg check function pointer,指向一个空函数
- cfg function table,内核会用到这个数据
- cfg flag
check function pointer你在ida或者pebear中看他是一个空函数,但是它实际上会在pe加载的时候由LdrpCfgProcessLoadConfig函数设置为下面这个函数
不过我们实际测试的话,发现并没有直接调用check function pointer,而是调用的__guard_dispatch_icall_fptr
0:000> dqs test+0020280 l100007ff6`4bd40280 00007ffb`79f1cd00 ntdll!LdrpDispatchUserCallTarget
其实check(LdrpValidateUserCallTarget)函数和dispatch(LdrpDispatchUserCallTarget)函数的实际代码是一样的
module tamper protection
references:
这个东西可以预防process hollowing技术
在eprocess中可以看到这个标志位
1: kd> dx @$curprocess.KernelObject.MitigationFlagsValues
@$curprocess.KernelObject.MitigationFlagsValues [Type: <unnamed-tag>]
[+0x000 ( 0: 0)] ControlFlowGuardEnabled : 0x1 [Type: unsigned long]
[+0x000 ( 1: 1)] ControlFlowGuardExportSuppressionEnabled : 0x0 [Type: unsigned long]
[+0x000 ( 2: 2)] ControlFlowGuardStrict : 0x0 [Type: unsigned long]
[+0x000 ( 3: 3)] DisallowStrippedImages : 0x0 [Type: unsigned long]
[+0x000 ( 4: 4)] ForceRelocateImages : 0x0 [Type: unsigned long]
[+0x000 ( 5: 5)] HighEntropyASLREnabled : 0x1 [Type: unsigned long]
[+0x000 ( 6: 6)] StackRandomizationDisabled : 0x0 [Type: unsigned long]
[+0x000 ( 7: 7)] ExtensionPointDisable : 0x0 [Type: unsigned long]
[+0x000 ( 8: 8)] DisableDynamicCode : 0x0 [Type: unsigned long]
[+0x000 ( 9: 9)] DisableDynamicCodeAllowOptOut : 0x0 [Type: unsigned long]
[+0x000 (10:10)] DisableDynamicCodeAllowRemoteDowngrade : 0x0 [Type: unsigned long]
[+0x000 (11:11)] AuditDisableDynamicCode : 0x0 [Type: unsigned long]
[+0x000 (12:12)] DisallowWin32kSystemCalls : 0x0 [Type: unsigned long]
[+0x000 (13:13)] AuditDisallowWin32kSystemCalls : 0x0 [Type: unsigned long]
[+0x000 (14:14)] EnableFilteredWin32kAPIs : 0x0 [Type: unsigned long]
[+0x000 (15:15)] AuditFilteredWin32kAPIs : 0x0 [Type: unsigned long]
[+0x000 (16:16)] DisableNonSystemFonts : 0x0 [Type: unsigned long]
[+0x000 (17:17)] AuditNonSystemFontLoading : 0x0 [Type: unsigned long]
[+0x000 (18:18)] PreferSystem32Images : 0x0 [Type: unsigned long]
[+0x000 (19:19)] ProhibitRemoteImageMap : 0x0 [Type: unsigned long]
[+0x000 (20:20)] AuditProhibitRemoteImageMap : 0x0 [Type: unsigned long]
[+0x000 (21:21)] ProhibitLowILImageMap : 0x0 [Type: unsigned long]
[+0x000 (22:22)] AuditProhibitLowILImageMap : 0x0 [Type: unsigned long]
[+0x000 (23:23)] SignatureMitigationOptIn : 0x0 [Type: unsigned long]
[+0x000 (24:24)] AuditBlockNonMicrosoftBinaries : 0x0 [Type: unsigned long]
[+0x000 (25:25)] AuditBlockNonMicrosoftBinariesAllowStore : 0x0 [Type: unsigned long]
[+0x000 (26:26)] LoaderIntegrityContinuityEnabled : 0x0 [Type: unsigned long]
[+0x000 (27:27)] AuditLoaderIntegrityContinuity : 0x0 [Type: unsigned long]
[+0x000 (28:28)] EnableModuleTamperingProtection : 0x0 [Type: unsigned long]
[+0x000 (29:29)] EnableModuleTamperingProtectionNoInherit : 0x0 [Type: unsigned long]
[+0x000 (30:30)] RestrictIndirectBranchPrediction : 0x1 [Type: unsigned long]
[+0x000 (31:31)] IsolateSecurityDomain : 0x0 [Type: unsigned long]
28和29bit
在nt内核的PspApplyMitigationOptions函数中设置这两个标志位
这两个bit的检查位置在ntdll的LdrpGetImportDescriptorForSnap函数中
就是在这里检查的
需要设置一下这个内存位置的类型,它实际上是_PS_SYSTEM_DLL_INIT_BLOCK结构体,这个符号可以在combase.dll中找到
0:000> dt _PS_SYSTEM_DLL_INIT_BLOCK
combase!_PS_SYSTEM_DLL_INIT_BLOCK
+0x000 Size : Uint4B
+0x008 SystemDllWowRelocation : Uint8B
+0x010 SystemDllNativeRelocation : Uint8B
+0x018 Wow64SharedInformation : [16] Uint8B
+0x098 RngData : Uint4B
+0x09c Flags : Uint4B
+0x09c CfgOverride : Pos 0, 1 Bit
+0x09c Reserved : Pos 1, 31 Bits
+0x0a0 MitigationOptionsMap : _PS_MITIGATION_OPTIONS_MAP
+0x0b8 CfgBitMap : Uint8B
+0x0c0 CfgBitMapSize : Uint8B
+0x0c8 Wow64CfgBitMap : Uint8B
+0x0d0 Wow64CfgBitMapSize : Uint8B
+0x0d8 MitigationAuditOptionsMap : _PS_MITIGATION_AUDIT_OPTIONS_MAP
我们把这个内容保存成头文件,然后加载到ntdll的ida项目中
struct _PS_MITIGATION_OPTIONS_MAP
{
unsigned __int64 Map[3];
};
typedef _PS_MITIGATION_OPTIONS_MAP *PPS_MITIGATION_OPTIONS_MAP;
struct _PS_MITIGATION_AUDIT_OPTIONS_MAP
{
unsigned __int64 Map[3];
};
typedef _PS_MITIGATION_AUDIT_OPTIONS_MAP *PPS_MITIGATION_AUDIT_OPTIONS_MAP;
struct _PS_SYSTEM_DLL_INIT_BLOCK
{
unsigned int Size;
unsigned __int64 SystemDllWowRelocation;
unsigned __int64 SystemDllNativeRelocation;
unsigned __int64 Wow64SharedInformation[16];
unsigned int RngData;
unsigned int ___u5;
_PS_MITIGATION_OPTIONS_MAP MitigationOptionsMap;
unsigned __int64 CfgBitMap;
unsigned __int64 CfgBitMapSize;
unsigned __int64 Wow64CfgBitMap;
unsigned __int64 Wow64CfgBitMapSize;
_PS_MITIGATION_AUDIT_OPTIONS_MAP MitigationAuditOptionsMap;
};
typedef const _PS_SYSTEM_DLL_INIT_BLOCK *PCPS_SYSTEM_DLL_INIT_BLOCK;
然后直接ctrl+G输入LdrSystemDllInitBlock跳转过去,设置类型为_PS_SYSTEM_DLL_INIT_BLOCK
然后在伪代码中选中qword_18019b3b8按y再回车即可
MitigationOptionsMap就是一个包含3个8bytes的结构体而已,每个8bytes存储着不同的mitigation相关的一些flag
这里的>>44 & 3检查的就是PROCESS_CREATION_MITIGATION_POLICY2_MODULE_TAMPERING_PROTECTION_MASK标志位
WinBase.h中定义了这个flag
#define PROCESS_CREATION_MITIGATION_POLICY2_MODULE_TAMPERING_PROTECTION_MASK (0x00000003ui64 << 12)
这个东西在bitmap[1]的高dword上,所以实际上是12+32=44bit,因此上面右移44bit很合理
原作者说在createprocess指定PROCESS_CREATION_MITIGATION_POLICY2_MODULE_TAMPERING_PROTECTION_MASK就可以,但是我实际测试发现并没有生效
我只能手动从nt内核给他开启
windwos自带的会开启这个选项的是C:\Windows\System32\SystemSettingsAdminFlows.exe,犹appinfo服务创建,他这个选项是在PspInheritMitigationOptions的下面这个位置开启的
1: kd> k
# Child-SP RetAddr Call Site
00 ffffe08e`a5253a10 fffff803`71501b23 nt!PspInheritMitigationOptions+0xb3
01 ffffe08e`a5253a80 fffff803`71504348 nt!PspAllocateProcess+0x16d7
02 ffffe08e`a52542b0 fffff803`712274e5 nt!NtCreateUserProcess+0x778
03 ffffe08e`a5254a70 00007fff`e78d0dc4 nt!KiSystemServiceCopyEnd+0x25
我让chatgpt分析了一下PspInheritMitigationOptions函数,它其实就是把前两个参数整合到第三个参数里面
前两个都是mitigationmap结构体,也就是长度为3的8bytes数组
经过回溯可以知道mitigation选项来自于ntcreateuserprocess的第11个参数
经过一番探索,最终知道这个mitigation是通过ntcreateuserprocess的a11 PS_ATTRIBUTE_LIST传进来的
通过在PspBuildCreateProcessContext函数par @$ra可以知道是在PS_ATTRIBUTE结构体的Attribute字段为0000000000020010 的时候终止循环的
结合内存值我们就可以确定20010就是mitigation属性,长度为18,实际上就是_PS_MITIGATION_OPTIONS_MAP
那么现在我们就来看一下这个属性是如何构造出来的
referecen:
使用这个代码可以创建出开启了tamper保护的进程
其实就是手动构造PPS_ATTRIBUTE_LIST
然后还有一点需要注意的就是,我在测试过程中发现这个注册表路径会在PspAllocateProcess函数的这个地方被检查
我们测试的时候,尽量不要使用出现这里面的二进制程序
下面我们要测试一下这个mitigation是怎么跑到用户模式的那个_PS_SYSTEM_DLL_INIT_BLOCK里面的
LdrSystemDllInitBlock的初始化
references :
这个东西是在内核的PspSetupUserProcessAddressSpace函数中通过调用PspPrepareSystemDllInitBlock来初始化的
在调用PspPrepareSystemDllInitBlock函数之前,会先切换到目标进程的空间中
在这个函数中,poi(PsNtdllExports)就是ntdll的base
然后我们的tamper在用户模式检查的时候出现在相对于LdrSystemDllInitBlock 0xa8的位置,这里是oword操作,直接包含了
v15来自于
这个就是前面创建进程时PspBuildCreateProcessContext函数的第四个参数,0x150偏移就是mitigation map
这样就完成了对LdrSystemDllInitBlock的初始化
tamper protection的检查
现在我们再来看一下这个东西是怎么检查的
我测来测去,发现这个mitigation根本就没有用,process hollowing还是可以正常工作的