首页
社区
课程
招聘
[旧帖] [原创]CFG机制原理 0.00雪花
发表于: 2015-5-22 22:24 2681

[旧帖] [原创]CFG机制原理 0.00雪花

2015-5-22 22:24
2681
对Control Flow Guard 这个缓解机制比较感兴趣,所以自己调试了一下。
首先CFG只有在Windows8.1 Update3之后才有。我用的是win8.1 x64
CFG主要是在间接调用函数前对要调用的函数到Bitmap中检查,成功调用,失败直接蓝屏。这样可以增加漏洞利用的难度。

首先在下断点。
kd> bp nt!PspPrepareSystemDllInitBlock

断点触发后查看,通过下面的栈回溯可以看出是在创建进程时初始化SystemDllInitBlock这个全局变量的。

kd> k
Child-SP          RetAddr           Call Site
ffffd000`21855a70 fffff802`a44d957f nt!PspPrepareSystemDllInitBlock+0x36
ffffd000`21855ab0 fffff802`a4415980 nt!PspSetupUserProcessAddressSpace+0x13b
ffffd000`21855b50 fffff802`a44da5cf nt!PspAllocateProcess+0xa80
ffffd000`21855f20 fffff802`a41e34b3 nt!NtCreateUserProcess+0x55b
ffffd000`21856a90 00007ffc`8e8d70fa nt!KiSystemServiceCopyEnd+0x13
000000f0`a2cfdf48 00000000`00000000 ntdll!NtCreateUserProcess+0xa

查看nt!PspPrepareSystemDllInitBlock看看其中的代码是怎么样的。

一开始就有调用了nt!MmGetCfgBitMapInformation
这个函数的作用后面会提到其实就是获取bitmap的长度和地址

 

 同时还调用了nt!ExGenRandom

产生一个随机数 放到LdrSystemDllInitBlock+0x50应该是系统会进行检测可能是为了防止被篡改。

接下来看看上面提到的nt!MmGetCfgBitMapInformation函数。
 nt!MmGetCfgBitMapInformation返回的值放在rax中就是bitmap的开始地址nt!MmGetCfgBitMapInformation。第二个参数是一个out参数,放入的是bitmap的长度。放入的Bitmap地址是0000020000000000(Byte)即2TB(在x64环境下是2TB)

   fffff801`3bc10750 48897760        mov     qword ptr [rdi+60h],rsi     这里在SystemDllInitBlock+0x60处放入Bitmap地址
   fffff801`3bc10754 488b442448      mov     rax,qword ptr [rsp+48h]
   fffff801`3bc10759 48894768        mov     qword ptr [rdi+68h],rax   这里在SystemDllInitBlock+0x68处放入长度

接下来再次下断点
 kd> bp ntdll!LdrpCfgProcessLoadConfig
这个函数主要是检查进程是否支持CFG,同时将设置检查函数LdrpValidateUserCallTarget

kd> k
Child-SP          RetAddr           Call Site
000000fb`76bdf218 00007ffc`8e890463 ntdll!LdrpCfgProcessLoadConfig
000000fb`76bdf220 00007ffc`8e909208 ntdll!LdrpProcessMappedModule+0x1cf
000000fb`76bdf280 00007ffc`8e8ec0e4 ntdll!LdrpInitializeProcess+0x1020
000000fb`76bdf5a0 00007ffc`8e866eda ntdll!_LdrpInitialize+0x851b8
000000fb`76bdf610 00000000`00000000 ntdll!LdrInitializeThunk+0xe

0033:00007ffa`0395782a 41f7809000000000100000 test dword ptr [r8+90h],1000h  [r8+90h]是Guard Flag 
0033:00007ffa`03957835 7476            je      ntdll!LdrpCfgProcessLoadConfig+0xb1 (00007ffa`039578ad)

0033:00007ffa`03957849 488b4930        mov     rcx,qword ptr [rcx+30h]  rcx指向IMAGE_NT_HEADER rcx+0x30指向ImageBase 
0033:00007ffa`0395784d 4c8d4d38        lea     r9,[rbp+38h]
0033:00007ffa`03957851 41b80d000000    mov     r8d,0Dh
0033:00007ffa`03957857 b201            mov     dl,1
0033:00007ffa`03957859 e87202fdff      call    ntdll!RtlImageDirectoryEntryToData (00007ffa`03927ad0)  这个函数是要获取Delay Import Directory的虚拟地址

0033:00007ffc`0ec1785e 4885c0          test    rax,rax
0033:00007ffc`0ec17861 744a            je      ntdll!LdrpCfgProcessLoadConfig+0xb1 (00007ffc`0ec178ad)
0033:00007ffc`0ec17863 0fb74e14        movzx   ecx,word ptr [rsi+14h]
0033:00007ffc`0ec17867 440fb74606      movzx   r8d,word ptr [rsi+6]
0033:00007ffc`0ec1786c 488d5618        lea     rdx,[rsi+18h]
0033:00007ffc`0ec17870 4803d1          add     rdx,rcx
0033:00007ffc`0ec17873 33c9            xor     ecx,ecx
0033:00007ffc`0ec17875 4585c0          test    r8d,r8d
0033:00007ffc`0ec17878 7433            je      ntdll!LdrpCfgProcessLoadConfig+0xb1 (00007ffc`0ec178ad)
0033:00007ffc`0ec1787a 448b480c        mov     r9d,dword ptr [rax+0Ch]
0033:00007ffc`0ec1787e 418bc1          mov     eax,r9d
0033:00007ffc`0ec17881 2b420c          sub     eax,dword ptr [rdx+0Ch]  VirtualAddress
0033:00007ffc`0ec17884 3b4208          cmp     eax,dword ptr [rdx+8]     VirtualSize
0033:00007ffc`0ec17887 720d            jb      ntdll!LdrpCfgProcessLoadConfig+0x9a (00007ffc`0ec17896)
0033:00007ffc`0ec17889 ffc1            inc     ecx
0033:00007ffc`0ec1788b 4883c228        add     rdx,28h     刚好是一个节区头的长度(IMAGE_SECTION_HEADER)这里是循环
0033:00007ffc`0ec1788f 413bc8          cmp     ecx,r8d
0033:00007ffc`0ec17892 72ea            jb      ntdll!LdrpCfgProcessLoadConfig+0x82 (00007ffc`0ec1787e)

cmp     qword ptr [ntdll!LdrSystemDllInitBlock+0x60 (00007ffc`0ecf0280)],0 比较地址是否为NULL是则直接结束

0033:00007ffc`0ec178bb b800400000      mov     eax,4000h看以前的资料0x4000是保留的可能就是这里是最新的IMAGE_DLLCHARACTERISTICS_GUARD_CF flag
0033:00007ffc`0ec178c0 6685465e        test    word ptr [rsi+5Eh],ax   option header DLLCharacstics
0033:00007ffc`0ec178c4 0f84a2000000    je      ntdll!LdrpCfgProcessLoadConfig+0x170 (00007ffc`0ec1796c)
0033:00007ffc`0ec178ca f7839000000000010000 test dword ptr [rbx+90h],100h   Guard Flags
0033:00007ffc`0ec178d4 0f8492000000    je      ntdll!LdrpCfgProcessLoadConfig+0x170 (00007ffc`0ec1796c)
0033:00007ffc`0ec178da 488b5b70        mov     rbx,qword ptr [rbx+70h] Guard CF address of check-function pointer
0033:00007ffc`0ec178de 4885db          test    rbx,rbx
0033:00007ffc`0ec178e1 0f8485000000    je      ntdll!LdrpCfgProcessLoadConfig+0x170 (00007ffc`0ec1796c)

。。。。。。。。。。

0033:00007ffa`039578da 488b5b70        mov     rbx,qword ptr [rbx+70h] ds:002b:00007ffa`038f9190={ntdll!_guard_check_icall_fptr (00007ffa`03a301d0)}

这里就是接下来放入LdrpValidateUserCallTarget验证函数的地方

..........................................

0033:00007ffc`0ec1793c 488d3d5da20100  lea     rdi,[ntdll!LdrpValidateUserCallTarget (00007ffc`0ec31ba0)]
0033:00007ffc`0ec17943 4c8d45f0        lea     r8,[rbp-10h]
0033:00007ffc`0ec17947 48893b          mov     qword ptr [rbx],rdi将LdrpValidateUserCallTarget放入

最后看看是LdrpValidateUserCallTarget是如何检测的
kd> bp ntdll!LdrpValidateUserCallTarget

kd> k
Child-SP          RetAddr           Call Site
00000058`856ff108 00007ffc`0ebcdcea ntdll!LdrpValidateUserCallTarget
00000058`856ff110 00007ffc`0ebcd9bb ntdll!LdrpCallInitRoutine+0x42
00000058`856ff170 00007ffc`0ebca73e ntdll!LdrpInitializeThread+0x15b
00000058`856ff250 00007ffc`0ebca65e ntdll!_LdrpInitialize+0x8e
00000058`856ff2c0 00000000`00000000 ntdll!LdrInitializeThunk+0xe

0033:00007ffc`0ec31ba0 488b15d9e60b00  mov     rdx,qword ptr[ntdll!LdrSystemDllInitBlock+0x60  (00007ffc`0ecf0280)] ds:002b:00007ffc`0ecf0280=00007df5ff3d0000
0033:00007ffc`0ec31ba7 488bc1          mov     rax,rcx
0033:00007ffc`0ec31baa 48c1e809        shr     rax,9将传入的地址除以512
0033:00007ffc`0ec31bae 488b14c2        mov     rdx,qword ptr [rdx+rax*8]然后以此为偏移量获取相应的8字节
0033:00007ffc`0ec31bb2 488bc1          mov     rax,rcx
0033:00007ffc`0ec31bb5 48c1e803        shr     rax,3
0033:00007ffc`0ec31bb9 f6c10f          test    cl,0Fh
0033:00007ffc`0ec31bbc 7507            jne     ntdll!LdrpValidateUserCallTarget+0x25 (00007ffc`0ec31bc5)
0033:00007ffc`0ec31bbe 480fa3c2        bt      rdx,rax  这里检测相应的bit是否置位
0033:00007ffc`0ec31bc2 730c            jae     ntdll!LdrpValidateUserCallTarget+0x30 (00007ffc`0ec31bd0)
0033:00007ffc`0ec31bc4 c3              ret
0033:00007ffc`0ec31bc5 4883c801        or      rax,1  没有以16对齐的情况按8字节对齐后查找
0033:00007ffc`0ec31bc9 480fa3c2        bt      rdx,rax 这里也是检测相应的bit位是否置位
0033:00007ffc`0ec31bcd 7301            jae     ntdll!LdrpValidateUserCallTarget+0x30 (00007ffc`0ec31bd0)
0033:00007ffc`0ec31bcf c3              ret
0033:00007ffc`0ec31bd0 488bd1          mov     rdx,rcx  如果间接调用的函数被改,直接异常
0033:00007ffc`0ec31bd3 b90a000000      mov     ecx,0Ah
0033:00007ffc`0ec31bd8 cd29            int     29h
0033:00007ffc`0ec31bda c3              ret

可以看到开启了CFG的进程虚拟尺寸为2TB
参考资料:http://www.powerofcommunity.net/poc2014/mj0011.pdf

http://blogs.msdn.com/b/vcblog/archive/2014/12/08/visual-studio-2015-preview-work-in-progress-security-feature.aspx

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

收藏
免费 0
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//