导语:如果你是软件开发人员,又希望自己开发的软件安全性高一点,那么当前的Windows 10企业内部预览版(10.0.16253)中就有一个功能可以做到。
如果你是软件开发人员,又希望自己开发的软件安全性高一点,那么当前的Windows 10企业内部预览版(10.0.16253)中就有一个功能可以做到, 当然我无法保证未来版本中它是否还会存在,但我想这可能对你非常有用。
它的位置在“设置 – > Windows Defender – > Windows Defender安全中心 – >应用程序和浏览器控制 – >漏洞保护设置”中,你可以为整个系统或特定程序启用自定义漏洞利用设置。其中有很多不同的保护特性并且许多都可以在审计模式下打开。在审计模式下,它不是在出现情况时终止进程,而是将事件写入事件日志。对于管理员,这就意味着其会允许软件继续运行,同时能够使我们意识到这一情况是何时发生的。对于希望通过了解这些情况以此来改进产品的人来说,它其实是提供了一个启用安全功能的附加好处,即无需重新编译(在某些情况下),就能告诉你确切的程序代码在当它在运行时遇到问题的位置。在版本10.0.16253中,可以进行审计的保护特性包括:
任意代码保护 - 防止非图像支持的执行代码和代码页修改(例如VirtualAlloc / VirtualProtect创建/修改的代码) 阻止低完整性图像 阻止远程图像 阻止不受信任的字体 代码完整性守护者 禁用Win32k系统调用 不允许子进程 导出地址过滤 - 将功能修补到另一个功能的一个常见方法中的一个步骤 导入地址过滤 - 将功能修补到另一个功能的一个常见方法中的一个步骤 模拟执行 验证API调用(CallerCheck) 验证图像依赖完整性 验证堆栈完整性
要充分利用此功能,我们需要安装Windows Performance Toolkit。它是Windows SDK安装程序的安装选项之一。当您启用了您感兴趣的设置后,打开管理命令提示符并浏览到Windows Performance Toolkit目录(通常为Program Files(x86) Windows Kits {Version} Windows Performance Toolkit)。您可以通过运行以下两个命令(在替换文件名所需的任何路径之后)启动并开始收集上述漏洞保护设置的跟踪以及解析堆栈跟踪所需的数据:
xperf - “PROC_THREAD + LOADER”-f“wdeg_klogger.etl” xperf -start“WDEG” - “Microsoft-Windows-Security-Mitigations:0xFFFFFFFFFFFFFF:0xFF:'stack'”-f“wdeg_unmerged.etl”
在您运行任何想要收集的方案之后,您可以停止跟踪并将数据与以下内容合并(再次替换文件名所需的任何路径):
xperf -stop -stop“WDEG”-d“wdeg_merged.etl”
然后,您可以删除创建的前两个文件,因为此时您将拥有第三个所需的所有数据。您可以在Windows Performance Analyzer(WPA)中打开生成的etl文件,并查看数据。如果你不熟悉这个,这里有一个示例预设文件来查看这种数据:
<?xml version="1.0" encoding="utf-8"?> <WpaProfileContainer xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" Version="2" xmlns="http://tempuri.org/SerializableElement.xsd"> <Content xsi:type="WpaProfile2"> <Sessions /> <Views /> <ModifiedGraphs> <GraphSchema Guid="04f69f98-176e-4d1c-b44e-97f734996ab8"> <ModifiedPresets> <Preset Name="EG Stacks" BarGraphIntervalCount="50" IsThreadActivityTable="false" LeftFrozenColumnCount="5" RightFrozenColumnCount="9" KeyColumnCount="4" GraphColumnCount="10" InitialFilterQuery="[Event Name]:~="Microsoft-Windows-Security-Miti"" InitialFilterShouldKeep="true" GraphFilterTopValue="0" GraphFilterThresholdValue="0" HelpText="{}{rtf1ansiansicpg1252uc1htmautspdeff2{fonttbl{f0fcharset0 Times New Roman;}{f2fcharset0 Segoe UI;}}{colortblred0green0blue0;red255green255blue255;}lochhichdbchpardplainltrparitap0{lang1033fs18f2cf0 cf0ql{f2 {ltrch Groups all the events by provider, task, and opcode.}li0ri0sa0sb0fi0qlpar} } }"> <Columns> <Column Name="Provider Name" Guid="8b4c40f8-0d99-437d-86ab-56ec200137dc" Width="200" IsVisible="true" SortPriority="18" /> <Column Name="Task Name" Guid="511777f7-30ef-4e86-bd0b-0facaf23a0d3" Width="80" IsVisible="true" SortPriority="19" /> <Column Name="Event Name" Guid="90fe0b49-e3bb-440f-b829-5813c42108a1" Width="100" IsVisible="false" SortPriority="0" /> <Column Name="Stack" Guid="26eed6bf-f07d-4bb0-a36f-43a7d3a68828" Width="247" IsVisible="true" SortPriority="0"> <StackOptionsParameter /> </Column> <Column Name="Message" Guid="0734f1a4-fbd9-4ff6-aec0-cf43875c8253" Width="100" IsVisible="true" SortPriority="0" /> <Column Name="Process" Guid="7ee6a5ff-1faf-428a-a7c2-7d2cb2b5cf26" Width="150" IsVisible="true" SortPriority="28" /> <Column Name="ThreadId" Guid="edf01e5d-3644-4dbc-ab9d-f8954e6db6ea" Width="50" IsVisible="true" SortPriority="27" /> <Column Name="Time" Guid="bbfc990a-b6c9-4dcd-858b-f040ab4a1efe" Width="80" IsVisible="true" SortPriority="29" /> </Columns> <MetadataEntries> <MetadataEntry Guid="edf01e5d-3644-4dbc-ab9d-f8954e6db6ea" Name="ThreadId" ColumnMetadata="EndThreadId" /> <MetadataEntry Guid="bbfc990a-b6c9-4dcd-858b-f040ab4a1efe" Name="Time" ColumnMetadata="StartTime" /> <MetadataEntry Guid="bbfc990a-b6c9-4dcd-858b-f040ab4a1efe" Name="Time" ColumnMetadata="EndTime" /> </MetadataEntries> <HighlightEntries /> </Preset> </ModifiedPresets> <PersistedPresets /> </GraphSchema> </ModifiedGraphs> </Content> </WpaProfileContainer>
您可以使用wpaPreset文件扩展名保存它,在WPA中加载数据,转到我的预设(在WPA的较新版本中的文件 – >窗口 – >我的存在),选择导入,浏览到保存预设的任何地方,选择它,并双击它以显示视图。另外,您还需要加载符号,以充分利用这一点。您可以在WPA中在File-> Configure Symbols下设置符号; 您需要将其指向msdl.microsoft.com/download/symbols上的Microsoft符号服务器以及您的符号服务器(或您没有符号服务器设置的符号文件的位置)。您可以在加载跟踪时自动配置WPA加载符号,但也可以通过转到文件 – >加载符号来手动加载符号。一旦完成,您将能够轻松地查看堆栈跟踪。它就会像下面这样:
以下是在创建上述过程中我为x64控制台应用程序编译的示例代码:
#include <Windows.h>#include <iostream>using namespace std;void* CreateCodeInVirtualMemory(BOOL writable) { BYTE code[3] = { 0x33, 0xc0, 0xc3 }; LPVOID result = VirtualAlloc(NULL, sizeof(code), MEM_COMMIT | MEM_RESERVE, writable ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE); if (result) { memcpy(result, code, sizeof(code)); } else cout << "VirtualAllocEx failed with error " << GetLastError() << endl; return result; }void CreateCodeInVirtualMemoryAndExecute(BOOL useWritableMemory) { LPTHREAD_START_ROUTINE addr = (LPTHREAD_START_ROUTINE)CreateCodeInVirtualMemory(useWritableMemory); if (addr) { DWORD result = addr(NULL); cout << "Code at 0x" << hex << (void*)addr << " returned " << result << endl; } else cout << "NULL address was not executed" << endl; }void ExecuteIllegalMemory() { CreateCodeInVirtualMemoryAndExecute(FALSE); } void PrintOptions() { cout << "Enter one of the following options:" << endl; cout << "1 - Execute Memory Not Marked As Executable" << endl; cout << "2 - Create Code in Virtual Memory" << endl; cout << "3 - Create Code in Virtual Memory and Execute" << endl; cout << "0 - Exit" << endl; }void DecisionLoop() { while (true) { int selection; PrintOptions(); cin >> selection; switch (selection) { case 0: return; case 1: ExecuteIllegalMemory(); break; case 2: CreateCodeInVirtualMemory(TRUE); break; case 3: CreateCodeInVirtualMemoryAndExecute(TRUE); break; default: cout << "Invalid input" << endl; } } }int main() { DecisionLoop(); return 0; }
在这里我没有去对它的应用做更多的介绍,但我相信我的抛砖引玉一定能够让更多的人对这一功能产生兴趣。
对于在事件查看器中的应用程序和服务日志中找到的大多数内容,您也可以以相同的方式完成此类操作——单击特定日志的属性,它将具有Provider-Name-Parts / LogName形式的名称。您只需要在xperf命令中使用“Provider-Name-Parts”部分即可。