首页
论坛
课程
招聘
EMET下EAF机制分析以及模拟实现
2022-9-5 20:51 10524

EMET下EAF机制分析以及模拟实现

2022-9-5 20:51
10524

EAF机制分析报告

简单分析了一下EAF机制,主要是参考了一下github上面的项目然后自己重写了一个EMET模拟程序,这是EAF的分析部分。第一次发帖,有写的不对的地方请大家多多包涵。
代码的话看一下伪代码和实现流程就好了。因为具体实现的时候是和其他机制一起写的,所以看起来有点奇怪。

EMET概述

​ Enhanced Migigation Experience Toolkit(增强的缓解体验工具包)。EMET试图缓解漏洞攻击的影响,通过引入以下这些保护措施来实现缓解攻击的影响:

 

​ 数据执行保护(DEP)、结构化异常处理程序覆盖保护(SEHOP) 、空页面保护(NullPage) 、堆喷射保护(HeapSpray) 、导出地址表访问过滤(EAF)导出地址表访问过滤增强版(EAF+) 、强制地址空间布局随机化(MandatoryASLR) 、由低而上的地址空间布局随机化(BottomUpASLR) 、Load Library 保护(LoadLib) 、内存保护(MemProt) 、ROP 调用者检查(Caller) 、ROP 模拟执行流(SimExecFlow)、堆栈支点(StackPivot) 、减少攻击面(ASR)

EAF机制

概述

​ Export Address Table Access Filtering(导出地址表访问过滤),可以对访问导出地址表(EAT)的调用代码设置规则。

 

​ 为了调用 API,shellcode 需要找到API加载的地址。通常shellcode会遍历所有已加载模块的导出地址表,寻找包含有用api的模块。通常涉及kernel32.dll,ntdll.dll 或 kernelbase.dll三个模块。EAF机制限制对Export Address Table (EAT)的读访问,一旦shellcode访问EAT,操作将被堵塞。

实现流程

1.调用AddVectoredExceptionHandler注册异常处理函数

 

2.获得三个重要模块(kernel32.dll,ntdll.dll,kernelbase.dll)EAT地址

 

3.在上述获得的地址处加上内存断点

 

​ 内存断点:调用VirtualProtect将关注的模块相应地址处加上页面保护属性PAGE_GUARD

 

4.当有shellcode访问被保护地址时,引发STATUS_GUARD_PAGE_VIOLATION (0x80000001)

 

​ STATUS_GUARD_PAGE_VIOLATION异常:如果程序尝试访问保护页中的地址,系统将引发STATUS_GUARD_PAGE_VIOLATION (0x80000001) 异常。系统还会清除PAGE_GUARD修饰符,从而删除内存页的防护页状态

 

5.进入异常处理函数,在异常处理函数中获取发生异常的指令的地址,获得该地址所在的模块名,若不在白名单内,即为违规操作,调用TerminateProcess结束进程

 

6.若在白名单内,设置EFALG寄存器的TF标识,返回继续执行,进入单步调试异常,恢复该页面的PAGE_GUARD属性

 

图片描述

详细分析

1.注册异常处理函数

1
AddVectoredExceptionHandler(0, EAFVectoredHandler);

2.获取模块的EAT地址

1
2
3
4
5
6
7
8
9
10
11
12
13
void * GetModuleEAT(DWORD_PTR ModuleBase)
{
    IMAGE_DOS_HEADER* ImageDosHeader = NULL;
    IMAGE_OPTIONAL_HEADER*  ImageOptionalHeader = NULL;
    PIMAGE_EXPORT_DIRECTORY ImageExportDirectory;
    PULONG  AddressOfFunctions;
    ImageDosHeader = (IMAGE_DOS_HEADER *)ModuleBase;
    ImageOptionalHeader = (IMAGE_OPTIONAL_HEADER*)((BYTE*)ModuleBase + ImageDosHeader->e_lfanew + 24);
    ImageExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((BYTE*)ModuleBase + ImageOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
 
    AddressOfFunctions = (ULONG*)((BYTE*)ModuleBase + ImageExportDirectory->AddressOfFunctions);
    return (void*)AddressOfFunctions;
}

3.添加内存断点

 

​ 调用VirtualProtect将关注的模块相应地址处加上页面保护属性PAGE_GUARD

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
DWORD EAF() {
    DWORD dwRet = 0;
    EnterCriticalSection(&g_CriSec);
 
    for (int i = 0; i < 3; i++) {
        DWORD_PTR ModuleBase = (DWORD_PTR)GetModuleHandle(LPCTSTR(g_Info.SystemDllInfo[i].dwModuleName));
        g_Info.SystemDllInfo[i].dwModuleBase = ModuleBase;
        g_Info.SystemDllInfo[i].dwEATAddr = GetModuleEAT(ModuleBase);
        g_Info.SystemDllInfo[i].dwModuleSize = GetModuleSize(ModuleBase);
        g_Info.SystemDllInfo[i].dwPageAddrOfEAT = g_Info.SystemDllInfo[i].dwEATAddr & 0xFFFFF000;
        g_Info.SystemDllInfo[i].dwSize = 0x1000;
 
        MEMORY_BASIC_INFORMATION mbi;
        VirtualQuery((PVOID)g_Info.SystemDllInfo[i].dwEATAddr, &mbi, sizeof(MEMORY_BASIC_INFORMATION));
        if (mbi.State == MEM_COMMIT)
        {
            if (!(mbi.Protect & PAGE_GUARD))
            {
                DWORD NewProtect = 0;
                DWORD OldProtect = 0;
                DWORD dwSize = g_Info.SystemDllInfo[i].dwSize;
                PVOID dwBaseAddress = (PVOID)g_Info.SystemDllInfo[i].dwPageAddrOfEAT;
                NewProtect = mbi.Protect | PAGE_GUARD;
                g_Info.SystemDllInfo[i].dwProtect = NewProtect;
                dwRet = pNtProtectVirtualMemory(GetCurrentProcess(), &dwBaseAddress, &dwSize, NewProtect, &OldProtect);   
            }
        }
    }
    LeaveCriticalSection(&g_CriSec);
    return dwRet;
}

4.shellcode访问被保护页面,引发异常

 

异常处理函数流程:

 

​ 获得发生异常的指令的地址,判断该地址是否在某个模块内,若不在,即为违规操作,调用TerminateProcess结束进程

 

​ 若在白名单内,设置EFALG寄存器的TF标识,返回继续执行,进入单步调试异常,恢复该页面的PAGE_GUARD属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
if (pExceptionRecord->ExceptionCode == STATUS_GUARD_PAGE_VIOLATION)
    {
        if (g_Info.EAF)
        {
            int nIndexDll = 0;
            DWORD dwEip = dwCurEip;
            ULONG_PTR uTargetAddress = pExceptionRecord->ExceptionInformation[1];//不可访问数据的虚拟地址
 
 
            //判断发生异常处的堆栈指针(EBP,ESP)和当前线程是否一致,不一致直接退出进程
            CheckStack(pExceptionInfo);
 
            if (GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (LPCSTR)dwEip, &AttckModuleHandle))
            {
                if (!AttckModuleHandle)
                {
                    ErrorReport();
                }
                else
                {
                    GetModuleName(AttckModuleHandle, AttackModuleName);
                    if (ModuleInWhiteList(AttackModuleName) == FALSE) {
                        ErrorReport();
                    }
 
                }
            }
            else
            {
                ErrorReport();
            }
            GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (LPCTSTR)uTargetAddress, &TargetModuleHandle);
            GetModuleName(TargetModuleHandle, TargetModuleName);
 
            for (nIndexDll = 0; nIndexDll < 13; nIndexDll++)
            {
                if (strcmp((const char *)(g_Info.SystemDllInfo[nIndexDll].dwModuleName), TargetModuleName) == 0)
                {
                    g_Info.SystemDllInfo[nIndexDll].dwNoGuard = 1;
                    break;
                }
 
            }
 
            //如果发生异常处地址在整个模块内,设置该模块的寄存器信息
            if (g_Info.SystemDllInfo[nIndexDll].dwNoGuard)
            {
                pContextRecord->EFlags |= 0x100;
            }
            return EXCEPTION_CONTINUE_EXECUTION;
        }
        return EXCEPTION_CONTINUE_SEARCH;
    }
}
//此处EAF和EAF+一起判断
for (int i = 0; i < 13; i++)
{
    if (g_Info.SystemDllInfo[i].dwNoGuard)
    {
        if (g_Info.EAF)
        {
            g_Info.SystemDllInfo[i].dwNoGuard = 0;
            SIZE_T ProtectSize = 0x1000;
            PVOID pProtectEATAddr;
            PVOID pProtectMZAddr;
            PVOID pProtectPEAddr;
            DWORD OldProtect = 0;
            if (i < 3)
            {
                pProtectEATAddr = (PVOID)g_Info.SystemDllInfo[i].dwPageAddrOfEAT;
                pNtProtectVirtualMemory(GetCurrentProcess(), &pProtectEATAddr, &ProtectSize, g_Info.SystemDllInfo[i].dwProtect, &OldProtect);
            }
            else
            {
                pProtectMZAddr = (PVOID)g_Info.SystemDllInfo[i].dwModuleBase;
                pProtectPEAddr = (PVOID)(IMAGE_NT_HEADERS*)((BYTE*)g_Info.SystemDllInfo[i].dwModuleBase + ((IMAGE_DOS_HEADER*)g_Info.SystemDllInfo[i].dwModuleBase)->e_lfanew);
                pNtProtectVirtualMemory(GetCurrentProcess(), &pProtectMZAddr, &ProtectSize, g_Info.SystemDllInfo[i].dwProtect, &OldProtect);
                pNtProtectVirtualMemory(GetCurrentProcess(), &pProtectPEAddr, &ProtectSize, g_Info.SystemDllInfo[i].dwProtect, &OldProtect);
 
            }
            return EXCEPTION_CONTINUE_EXECUTION;
 
        }
    }
}

代码测试

1.在设置好断点以后写入测试代码,作用为读访问ntdll.dll的EAT地址 测试代码如下

1
2
3
DWORD_PTR ModuleBase = (DWORD_PTR)GetModuleHandleA("ntdll.dll");
DWORD Test = *(DWORD*)(GetModuleEAT(ModuleBase));
Test = *(DWORD*)(GetModuleEAT(ModuleBase));

2.运行程序,断点跟踪进入异常处理函数
图片描述

 

3.当前访问ntdll.dll EAT地址的模块不在白名单内,调用TerminateProcess结束进程
图片描述

 

4.若在白名单内,设置EFLAG寄存器的TF位
图片描述

 

5.进入单步调试异常恢复PAGE_GUARD
图片描述

 

6.可见白名单访问受保护页面后引发0x80000001,然后又进入0x80000004异常
图片描述

EAF+机制

概述

相比EAF机制增加了更多检测点,代码逻辑一样。增加功能如下:

 

​ 可检测堆栈寄存器(ESP/EBP)是否超出访问范围,检测对特定模块的DOS_HEADER/NT_HEADERS的内存读取访问。

 

增加检测模块

1
2
3
4
5
6
7
8
9
10
mshtml.dll
flash*.ocx
jscript*.ocx
vbscript.dll
vgx.dll
mozjs.dll
xul.dll
acrord32.dll
acrofx32.dll
acroform.api

实现流程

1.调用AddVectoredExceptionHandler注册异常处理函数

 

2.对LoadLibrary系列函数进行InlineHook,当应用程序加载特定模块时进行拦截,获取加载模块的模块信息

 

3.在上述获得的模块DOS_HEADER/NT_HEADERS处添加内存断点:

 

​ 调用VirtualProtect将关注的模块相应地址处加上页面保护属性PAGE_GUARD

 

4.当有shellcode访问被保护页面时,引发STATUS_GUARD_PAGE_VIOLATION (0x80000001) 异常

 

5.进入异常处理函数,在异常处理函数中获取发生异常的指令的地址,获得该地址所在的模块名,若不在白名单内,即为违规操作,调用TerminateProcess结束进程

 

6.若在白名单内,设置EFALG寄存器的TF标识,返回继续执行,进入单步调试异常,恢复该页面的PAGE_GUARD属性
图片描述

详细分析

1.注册异常处理函数

1
AddVectoredExceptionHandler(0, EAFPlusVectoredHandler);

2.对LoadLibrary系列函数进行InlineHook,当应用程序加载特定模块时进行拦截,获取加载模块的模块信息,添加内存断点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
void EAF_PLUS(UNION_HOOKEDFUNCINFO::PEAFP_INFO pMemProt, HMODULE hModuleBase) {
    char szFileName[0x1000];
    if (pMemProt->dwType == 4 && hModuleBase != NULL) {
 
        if (GetModuleFileNameA(hModuleBase, szFileName, 0x1000)) {
            //获得加载的文件名
            char *pszCurLoadingFileName = strrchr(szFileName, '\\');
            if (pszCurLoadingFileName != NULL) {
                pszCurLoadingFileName += 1;
            }
            else {
                pszCurLoadingFileName = szFileName;
            }
 
            for (int i = 3; i < 13; i++) {
                DWORD dwModuleName = g_Info.SystemDllInfo[i].dwModuleName;
                if (dwModuleName == 0) {
                    break;
                }
                //如果当前加载模块是要关注的模块
                if (MatchStr(pszCurLoadingFileName, (PCSTR)dwModuleName)) {
                    DWORD dwModuleSize = GetModuleSize((DWORD_PTR)hModuleBase);
                    MEMORY_BASIC_INFORMATION mbi = { 0 };
                    if (dwModuleSize != 0 && VirtualQuery(hModuleBase, &mbi, sizeof(mbi)) != 0) {
                        EnterCriticalSection(&g_CriSec);
                        //在此赋值,模块基地址初始化为0,在此进行比较交换,在此进行赋值
                        if (InterlockedCompareExchange((volatile ULONG*)&(g_Info.SystemDllInfo[i].dwModuleBase), (ULONG64)hModuleBase, 0) == 0) {
                            DWORD dwNewProtect = mbi.Protect | PAGE_GUARD;
                            DWORD dwSize = 0x1000;
                            DWORD dwOldProtect = 0;
                            PVOID BaseAddress = (PVOID)hModuleBase;
                            PVOID PEAddress = (PVOID)(IMAGE_NT_HEADERS*)((BYTE*)hModuleBase + ((IMAGE_DOS_HEADER*)hModuleBase)->e_lfanew);
                            g_Info.SystemDllInfo[i].dwEATAddr = GetModuleEAT((DWORD_PTR)hModuleBase);
                            g_Info.SystemDllInfo[i].dwModuleSize = GetModuleSize((DWORD_PTR)hModuleBase);
                            g_Info.SystemDllInfo[i].dwPageAddrOfEAT = g_Info.SystemDllInfo[i].dwEATAddr & 0xFFFF000;
                            g_Info.SystemDllInfo[i].dwProtect = dwNewProtect;
                            g_Info.SystemDllInfo[i].dwSize = dwSize;
 
                            //为MZ头加上保护
                            pNtProtectVirtualMemory(GetCurrentProcess(), &BaseAddress, &dwSize, dwNewProtect, &dwOldProtect);
                            //为PE头加上保护
                            pNtProtectVirtualMemory(GetCurrentProcess(), &PEAddress, &dwSize, dwNewProtect, &dwOldProtect);
                        }
                        LeaveCriticalSection(&g_CriSec);
                    }
                    return;
 
                }
            }
        }
    }
    return;
}

3.shellcode访问被保护页面,引发异常

 

异常处理函数流程:

 

​ 获得发生异常的指令的地址,判断该地址是否在某个模块内,若不在,即为违规操作,调用TerminateProcess结束进程

 

​ 若在白名单内,设置EFALG寄存器的TF标识,返回继续执行,进入单步调试异常,恢复该页面的PAGE_GUARD属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
if (pExceptionRecord->ExceptionCode == STATUS_GUARD_PAGE_VIOLATION)
    {
        if (g_Info.EAF)
        {
            int nIndexDll = 0;
            DWORD dwEip = dwCurEip;
            ULONG_PTR uTargetAddress = pExceptionRecord->ExceptionInformation[1];//不可访问数据的虚拟地址
 
 
            //判断发生异常处的堆栈指针(EBP,ESP)和当前线程是否一致,不一致直接退出进程
            CheckStack(pExceptionInfo);
 
            if (GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (LPCSTR)dwEip, &AttckModuleHandle))
            {
                if (!AttckModuleHandle)
                {
                    ErrorReport();
                }
                else
                {
                    GetModuleName(AttckModuleHandle, AttackModuleName);
                    if (ModuleInWhiteList(AttackModuleName) == FALSE) {
                        ErrorReport();
                    }
 
                }
            }
            else
            {
                ErrorReport();
            }
            GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (LPCTSTR)uTargetAddress, &TargetModuleHandle);
            GetModuleName(TargetModuleHandle, TargetModuleName);
 
            for (nIndexDll = 0; nIndexDll < 13; nIndexDll++)
            {
                if (strcmp((const char *)(g_Info.SystemDllInfo[nIndexDll].dwModuleName), TargetModuleName) == 0)
                {
                    g_Info.SystemDllInfo[nIndexDll].dwNoGuard = 1;
                    break;
                }
 
            }
 
            //如果发生异常处地址在整个模块内,设置该模块的寄存器信息
            if (g_Info.SystemDllInfo[nIndexDll].dwNoGuard)
            {
                pContextRecord->EFlags |= 0x100;
            }
            return EXCEPTION_CONTINUE_EXECUTION;
        }
        return EXCEPTION_CONTINUE_SEARCH;
    }
}
//此处EAF和EAF+一起判断
for (int i = 0; i < 13; i++)
{
    if (g_Info.SystemDllInfo[i].dwNoGuard)
    {
        if (g_Info.EAF)
        {
            g_Info.SystemDllInfo[i].dwNoGuard = 0;
            SIZE_T ProtectSize = 0x1000;
            PVOID pProtectEATAddr;
            PVOID pProtectMZAddr;
            PVOID pProtectPEAddr;
            DWORD OldProtect = 0;
            if (i < 3)
            {
                pProtectEATAddr = (PVOID)g_Info.SystemDllInfo[i].dwPageAddrOfEAT;
                pNtProtectVirtualMemory(GetCurrentProcess(), &pProtectEATAddr, &ProtectSize, g_Info.SystemDllInfo[i].dwProtect, &OldProtect);
            }
            else
            {
                pProtectMZAddr = (PVOID)g_Info.SystemDllInfo[i].dwModuleBase;
                pProtectPEAddr = (PVOID)(IMAGE_NT_HEADERS*)((BYTE*)g_Info.SystemDllInfo[i].dwModuleBase + ((IMAGE_DOS_HEADER*)g_Info.SystemDllInfo[i].dwModuleBase)->e_lfanew);
                pNtProtectVirtualMemory(GetCurrentProcess(), &pProtectMZAddr, &ProtectSize, g_Info.SystemDllInfo[i].dwProtect, &OldProtect);
                pNtProtectVirtualMemory(GetCurrentProcess(), &pProtectPEAddr, &ProtectSize, g_Info.SystemDllInfo[i].dwProtect, &OldProtect);
 
            }
            return EXCEPTION_CONTINUE_EXECUTION;
 
        }
    }
}

代码测试

1.在设置好断点以后写入测试代码,作用为读访问mshtml.dll的基地址 测试代码如下

1
2
HMODULE ModuleBase = LoadLibraryA("mshtml.dll");
DWORD Test = *(DWORD*)ModuleBase;

2.加载目标模块时,进入EAFPlus初始化函数
图片描述

 

3.获得加载模块的信息,添加内存断点
图片描述

 

4.当前访问mshtml.dll 基地址的模块不在白名单内,调用TerminateProcess结束进程
图片描述

 

5.若在白名单内,设置TF标志位
图片描述

 

6.进入单步调试异常恢复PAGE_GUARD
图片描述

 

7.可见白名单访问受保护页面后引发0x80000001,然后又进入0x80000004异常
图片描述

参考项目

GitHub - sheri31/EMET_Simulator: Simulate EMET

 

GitHub - codingtest/EMET: reversed emet tool

 

Maxwell/MemGuard.cpp at db4d8b189ff2f39f7c5235bb6a1a9974f7532a8c · endgameinc/Maxwell · GitHub


看雪2022 KCTF 秋季赛 防守篇规则,征题截止日期11月12日!(iPhone 14等你拿!)

收藏
点赞4
打赏
分享
最新回复 (2)
雪    币: 300
活跃值: 活跃值 (1556)
能力值: ( LV5,RANK:75 )
在线值:
发帖
回帖
粉丝
囧囧 活跃值 2022-9-7 10:42
2
0
感谢分享~
雪    币: 209
活跃值: 活跃值 (20)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
shelky 活跃值 2022-9-9 11:28
3
0
游客
登录 | 注册 方可回帖
返回