D

Deleted member 65228

Guest
#1
Hello everyone.


Introduction


Data Execution Prevention (DEP) is a memory protection feature which helps prevent exploitation of software. It is not only enforced from a software-level but also hardware-level usually. Unless a process marks a location in memory as executable, code will be unable to execute (e.g. if it is an location in memory which has not been explicitly marked example). There are differences between software-level and hardware-level enforced DEP.

There is a good article provided by Microsoft which can teach you the basics about what it is for and how it may work, you can find them here:
https://support.microsoft.com/en-gb...-the-data-execution-prevention-dep-feature-in


Further explanation


In this tutorial I will outline how you can enable DEP from software-level (forced), however it will not be "permanent". This means that an attacker could potentially disable DEP also from software-level after you have enabled it using this technique. The best thing you can do is keep DEP enabled at the time of compilation (e.g. Linker Options in Visual Studio -> Yes (/NXCOMPAT) ). This will lead to the DEP being enforced (as far as I am aware, from a hardware-level as well).

There is a Native API function called NtSetInformationProcess/ZwSetInformationProcess. Thankfully, it can be invoked from user-mode due to NTDLL (NTDLL.DLL) which is a system call wrapper component embedded into the user-mode side of Windows. To keep things short and understandable, user-mode programs invoke routines which are actually present in kernel-mode and not user-mode memory for various things (without this, programs would not work properly on Windows), and this is achieved through a system call. The system call provides a transition from user-mode to kernel-mode so a kernel-mode routine can be signalled where it will perform checks to see which function from the Native API is requesting to be called and if appropriate (and if its address can be found within the System Service Dispatch Table, comparing from the value within the EAX register) the call will be made and the NTSTATUS (LONG) return status will be sent all the way back down (and when a Win32 API function internally calls an NTDLL export for a system call, error codes are translated and set to be retrieved by GetLastError).

ntoskrnl.exe exports NtSetInformationProcess and NTDLL exports a function stub for it too. When the function is called either internally by the Win32 or manually through static/dynamic import on NTDLL (e.g. acquiring the address via GetProcAddress -> LdrGetProcedureAddress or manually through scanning the Export Address Table for NTDLL), a system call will be performed. On older versions of Windows, other routines may be called to handle things (on Windows XP an interrupt is used which would be int 21h) however on modern versions of Windows for x64, instructions like SYSCALL (0x0F, 0x05) are used. X86-X64 processes will pass through the WOW64 emulation layer (move into 64-bit world by changing the segment selector from 0x22 to 0x33 -> now the 64-bit NTDLL function stub is called and the system call happens and then it gets reverted back). The system call invokes a kernel-mode routine which will perform checks and extract the function ID from the EAX register -> look up with the System Service Dispatch Table (SSDT) to acquire the address of where the function is present in memory within ntoskrnl.exe -> called.

The function type-definition for NtSetInformationProcess:
Code:
typedef NTSTATUS(NTAPI *pNtSetInformationProcess)(
    HANDLE ProcessHandle,
    PROCESS_INFORMATION_CLASS ProcessInformationClass,
    PVOID ProcessInformation,
    ULONG ProcessInformationLength
    );
Along with NtSetInformationProcess, there is an undocumented list of class IDs to be used with this function (within an enum type definition) - Tutorial - SYSTEM_INFORMATION_CLASS & PROCESSINFOLASS . This is passed in for the second parameter of the function.

Code:
typedef enum _PROCESSINFOCLASS {
    ProcessBasicInformation = 0,
    ProcessQuotaLimits = 1,
    ProcessIoCounters = 2,
    ProcessVmCounters = 3,
    ProcessTimes = 4,
    ProcessBasePriority = 5,
    ProcessRaisePriority = 6,
    ProcessDebugPort = 7,
    ProcessExceptionPort = 8,
    ProcessAccessToken = 9,
    ProcessLdrInformation = 10,
    ProcessLdtSize = 11,
    ProcessDefaultHardErrorMode = 12,
    ProcessIoPortHandlers = 13,
    ProcessPooledUsageAndLimits = 14,
    ProcessWorkingSetWatch = 15,
    ProcessUserModeIOPL = 16,
    ProcessEnableAlignmentFaultFixup = 17,
    ProcessPriorityClass = 18,
    ProcessWx86Information = 19,
    ProcessHandleCount = 20,
    ProcessAffinityMask = 21,
    ProcessPriorityBoost = 22,
    ProcessDeviceMap = 23,
    ProcessSessionInformation = 24,
    ProcessForegroundInformation = 25,
    ProcessWow64Information = 26,
    ProcessImageFileName = 27,
    ProcessLUIDDeviceMapsEnabled = 28,
    ProcessBreakOnTermination = 29,
    ProcessDebugObjectHandle = 30,
    ProcessDebugFlags = 31,
    ProcessHandleTracing = 32,
    ProcessIoPriority = 33,
    ProcessExecuteFlags = 34,
    ProcessTlsInformation = 35,
    ProcessCookie = 36,
    ProcessImageInformation = 37,
    ProcessCycleTime = 38,
    ProcessPagePriority = 39,
    ProcessInstrumentationCallback = 40,
    ProcessThreadStackAllocation = 41,
    ProcessWorkingSetWatchEx = 42,
    ProcessImageFileNameWin32 = 43,
    ProcessImageFileMapping = 44,
    ProcessAffinityUpdateMode = 45,
    ProcessMemoryAllocationMode = 46,
    ProcessGroupInformation = 47,
    ProcessTokenVirtualizationEnabled = 48,
    ProcessConsoleHostProcess = 49,
    ProcessWindowInformation = 50,
    MaxProcessInfoClass    // always last one so no need to add a value manually
} PROCESSINFOCLASS;
The class identifier which is an interest right now for enabling DEP would be ProcessExecuteFlags (Class ID of 34). We can use NtSetInformationProcess to change the value for ProcessExecuteFlags. To disable DEP you need to specify 0x00000000 (0) and to enable DEP you need to specify 0x00000001 (1).

When you do this, internally the Windows Kernel will update the data within a kernel-mode only structure for the process, called KPROCESS (KPROCESS* is PKPROCESS - the link provided is to Nirsoft documentation and may be outdated now since it is from Vista however it is still valid for this explanation).

Code:
typedef struct _KPROCESS
{
    DISPATCHER_HEADER Header;
    LIST_ENTRY ProfileListHead;
    ULONG DirectoryTableBase;
    ULONG Unused0;
    KGDTENTRY LdtDescriptor;
    KIDTENTRY Int21Descriptor;
    WORD IopmOffset;
    UCHAR Iopl;
    UCHAR Unused;
    ULONG ActiveProcessors;
    ULONG KernelTime;
    ULONG UserTime;
    LIST_ENTRY ReadyListHead;
    SINGLE_LIST_ENTRY SwapListEntry;
    PVOID VdmTrapcHandler;
    LIST_ENTRY ThreadListHead;
    ULONG ProcessLock;
    ULONG Affinity;
    union
    {
         ULONG AutoAlignment: 1;
         ULONG DisableBoost: 1;
         ULONG DisableQuantum: 1;
         ULONG ReservedFlags: 29;
         LONG ProcessFlags;
    };
    CHAR BasePriority;
    CHAR QuantumReset;
    UCHAR State;
    UCHAR ThreadSeed;
    UCHAR PowerState;
    UCHAR IdealNode;
    UCHAR Visited;
    union
    {
         KEXECUTE_OPTIONS Flags;
         UCHAR ExecuteOptions;
    };
    ULONG StackCount;
    LIST_ENTRY ProcessListEntry;
    UINT64 CycleTime;
} KPROCESS, *PKPROCESS;
There is a structure used for Flags within this structure called KEXECUTE_OPTIONS.

Code:
typedef struct _KEXECUTE_OPTIONS
{
    ULONG ExecuteDisable: 1;
    ULONG ExecuteEnable: 1;
    ULONG DisableThunkEmulation: 1;
    ULONG Permanent: 1;
    ULONG ExecuteDispatchEnable: 1;
    ULONG ImageDispatchEnable: 1;
    ULONG Spare: 2;
} KEXECUTE_OPTIONS, *PKEXECUTE_OPTIONS;
This should be where updates are performed in the end. The limitations of enforcing it from user-mode this way is that it can be disabled by an attacker afterwards (potentially) and you cannot remotely apply it for other processes (only your own - you can however inject code into another process and then call NtSetInformationProcess to enable it).

If you are going to enforce it for running programs which do not have it enabled then it would be worth considering if using kernel-mode for the task is possible or not, because enforcing it from kernel-mode by modifying the flags within the kernel-mode structure is much more secure and efficient (there won't be a need to inject into other running processes to enable it for them).


I have written a source-code example in C++ (C-style) on how you can enable DEP for your own process at run-time (remember the limitations with this though) in user-mode.


stdafx.h
Code:
#pragma once
#include <Windows.h>
#include <iostream>

#include "ntdef.h"
#include "dep.h"
ntdef.h
Code:
#pragma once
#include "stdafx.h"

typedef _Return_type_success_(return >= 0) LONG NTSTATUS;
typedef NTSTATUS *PNTSTATUS;

#define STATUS_SUCCESS 0x00000000
#define STATUS_UNSUCCESSFUL 0xC0000001

#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)

#define NtCurrentProcess() ((HANDLE)-1)

typedef enum _PROCESSINFOCLASS {
    ProcessBasicInformation = 0,
    ProcessQuotaLimits = 1,
    ProcessIoCounters = 2,
    ProcessVmCounters = 3,
    ProcessTimes = 4,
    ProcessBasePriority = 5,
    ProcessRaisePriority = 6,
    ProcessDebugPort = 7,
    ProcessExceptionPort = 8,
    ProcessAccessToken = 9,
    ProcessLdrInformation = 10,
    ProcessLdtSize = 11,
    ProcessDefaultHardErrorMode = 12,
    ProcessIoPortHandlers = 13,
    ProcessPooledUsageAndLimits = 14,
    ProcessWorkingSetWatch = 15,
    ProcessUserModeIOPL = 16,
    ProcessEnableAlignmentFaultFixup = 17,
    ProcessPriorityClass = 18,
    ProcessWx86Information = 19,
    ProcessHandleCount = 20,
    ProcessAffinityMask = 21,
    ProcessPriorityBoost = 22,
    ProcessDeviceMap = 23,
    ProcessSessionInformation = 24,
    ProcessForegroundInformation = 25,
    ProcessWow64Information = 26,
    ProcessImageFileName = 27,
    ProcessLUIDDeviceMapsEnabled = 28,
    ProcessBreakOnTermination = 29,
    ProcessDebugObjectHandle = 30,
    ProcessDebugFlags = 31,
    ProcessHandleTracing = 32,
    ProcessIoPriority = 33,
    ProcessExecuteFlags = 34,
    ProcessTlsInformation = 35,
    ProcessCookie = 36,
    ProcessImageInformation = 37,
    ProcessCycleTime = 38,
    ProcessPagePriority = 39,
    ProcessInstrumentationCallback = 40,
    ProcessThreadStackAllocation = 41,
    ProcessWorkingSetWatchEx = 42,
    ProcessImageFileNameWin32 = 43,
    ProcessImageFileMapping = 44,
    ProcessAffinityUpdateMode = 45,
    ProcessMemoryAllocationMode = 46,
    ProcessGroupInformation = 47,
    ProcessTokenVirtualizationEnabled = 48,
    ProcessConsoleHostProcess = 49,
    ProcessWindowInformation = 50,
    MaxProcessInfoClass    // always last one so no need to add a value manually
} PROCESSINFOCLASS;

typedef NTSTATUS(NTAPI *pNtSetInformationProcess)(
    HANDLE ProcessHandle,
    PROCESS_INFORMATION_CLASS ProcessInformationClass,
    PVOID ProcessInformation,
    ULONG ProcessInformationLength
    );
dep.h
Code:
#pragma once
#include "stdafx.h"

#define DEP_DISABLE 0x00000000
#define DEP_ENABLE 0x00000001

BOOL EnableDep(
    BOOL Enable
);
main.cpp
Code:
#include "stdafx.h"
using namespace std;

int main()
{
    if (EnableDep(TRUE))
    {
        cout << "DEP has been enabled!\n";
    }
    else
    {
        cout << "DEP cannot be enabled\n";
    }

    getchar();
    return 0;
}
dep.cpp
Code:
#include "stdafx.h"

pNtSetInformationProcess fNtSetInformationProcess = (pNtSetInformationProcess)GetProcAddress(GetModuleHandle("NTDLL"),
    "NtSetInformationProcess");

NTSTATUS WrapperEnableDep(
    BOOL Enable
)
{
    NTSTATUS NtStatus = STATUS_SUCCESS;
    ULONG Flags = DEP_DISABLE;

    if (Enable)
    {
        Flags = DEP_ENABLE;
    }

    if (!fNtSetInformationProcess)
    {
        return STATUS_UNSUCCESSFUL;
    }

    NtStatus = fNtSetInformationProcess(NtCurrentProcess(),
        (PROCESS_INFORMATION_CLASS)ProcessExecuteFlags,
        &Flags,
        sizeof(ULONG));

    return NtStatus;
}

BOOL EnableDep(
    BOOL Enable
)
{
    return WrapperEnableDep(Enable) == STATUS_SUCCESS ?
        TRUE : FALSE;
}

Example


















On modern versions of Windows, there are actually Win32-level APIs implemented by Microsoft and documented over on MSDN regarding protection policies for processes, and they cover a lot more than just DEP. I suggest you do some research if you are interested; Google Chrome makes use of a lot of these mitigations. I believe this is linked to their sandbox container; they used to use NtSetInformationProcess for backwards compatibility (checks to ensure it was enabled even from user-mode should it have been disabled for one reason or another - they probably still have this implemented since older versions of Windows don't have access to the newer APIs for this).





In my opinion, you should always keep DEP enabled (ASLR is very important too and should be used) if you plan on developing and releasing good software. The last thing you want is to release software which is not as secure as it can currently be, because this opens up more holes for attackers.

Thank you for reading. :)
 

Attachments

Last edited by a moderator:

tim one

Level 21
AV-Tester
Verified
Joined
Jul 31, 2014
Messages
1,071
Operating System
Windows 10
Antivirus
F-Secure
#2
Thanks to @Opcode for the good read! :)

Yes DEP is implemented to prevent the execution of instructions in certain memory areas, thanks to attacks such as buffer overflow. Just mentioning also ASLR (Address Space Layout Randomization), which seeks to block the execution of code in known memory locations, allocating the addresses in a random manner.

Perhaps many of us know this technology but after reading your post surely we know it better (including myself) ;)
 
D

Deleted member 65228

Guest
#3
Perhaps many of us know this technology but after reading your post surely we know it better (including myself) ;)
Thank you! I am still learning a lot about it too, I just thought that it couldn't hurt to share my current findings :)

About ASLR, in the Optional Header for the PE -> DllCharacteristics -> IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE -> determines whether ASLR will be enabled or not. There's also a registry key which can be used to force ASLR for all software as far as I know.

In my opinion, both DEP and ASLR should be enabled in linker options before compilation for good, secure software. It is very useful to help prevent exploitation, if it is disabled then it makes the job easier for attackers because restrictions won't be in place for where code can execute from in memory (e.g. non-specified executable locations) and hard-coded addresses are easier than dynamically acquiring them... Unless there is a good reason then it should be enabled IMO.

Windows has a force option for DEP now on the latest Creators Update but I am yet to test it out. Apparently enabling it can break some software very easily which does not surprise me really. I prefer the approach third-party Anti-Exploits take like Malwarebytes Anti-Exploit where it can enable for specific applications (forced if it isn't on - such as Microsoft Office, Java, Media players, etc.).

:) :)
 
D

Deleted member 65228

Guest
#4
Update.

I briefly mentioned that there are Win32-level API functions which can be used to enforce mitigation policies which have been available since Windows 8 however I did not provide much information about it - the function of interest would be SetProcessMitigationPolicy (KERNEL32.DLL). GetProcessMitigationPolicy (KERNEL32) can be used to retrieve information on enforced policies not only for your own process, but another process as well.

I've decided to make a quick example to share with you on how to enable Data Execution Prevention (DEP) at run-time and have it set permanently via usage of the function SetProcessMitigationPolicy.

Code:
#include <Windows.h>
#include <iostream>
using namespace std;

BOOL EnableDep()
{
    PROCESS_MITIGATION_DEP_POLICY ProcessDepPolicy = { 0 };

    ProcessDepPolicy.Enable = TRUE;
    ProcessDepPolicy.Permanent = TRUE;

    return SetProcessMitigationPolicy(ProcessDEPPolicy,
        &ProcessDepPolicy,
        sizeof(ProcessDepPolicy));
}

int main()
{
    if (EnableDep())
    {
        cout << "Enabled\n";
    }
 
    getchar();
    return 0;
}
If you check the documentation you'll find out more about the function and what is supported: SetProcessMitigationPolicy function (Windows)

Below is a list of what policies it supports:
Code:
ProcessDEPPolicy
ProcessASLRPolicy
ProcessImageLoadPolicy
ProcessStrictHandleCheckPolicy
ProcessSystemCallDisablePolicy
ProcessMitigationOptionsMask
ProcessExtensionPointDisablePolicy
ProcessControlFlowGuardPolicy
ProcessSignaturePolicy
ProcessFontDisablePolicy
ProcessImageLoadPolicy
Below is another example I've made which demonstrates using the function for more than just enforcing DEP. Be prepared for messy demo code.

Code:
#include <Windows.h>
#include <iostream>
using namespace std;

BOOL EnableDep()
{
    PROCESS_MITIGATION_DEP_POLICY ProcessDepInfo = { 0 };

    ProcessDepInfo.Enable = TRUE;
    ProcessDepInfo.Permanent = TRUE;

    return SetProcessMitigationPolicy(ProcessDEPPolicy,
        &ProcessDepInfo,
        sizeof(ProcessDepInfo));
}

BOOL EnableAslr()
{
    PROCESS_MITIGATION_ASLR_POLICY ProcessAslrInfo = { 0 };

    ProcessAslrInfo.EnableForceRelocateImages = TRUE;

    return SetProcessMitigationPolicy(ProcessASLRPolicy,
        &ProcessAslrInfo,
        sizeof(ProcessAslrInfo));
}

BOOL EnableDynamicCodeBlock()
{
    PROCESS_MITIGATION_DYNAMIC_CODE_POLICY ProcessDynamicCodeInfo = { 0 };

    ProcessDynamicCodeInfo.ProhibitDynamicCode = TRUE;

    return SetProcessMitigationPolicy(ProcessDynamicCodePolicy,
        &ProcessDynamicCodeInfo,
        sizeof(ProcessDynamicCodeInfo));
}

BOOL EnableSignatureDllPolicy()
{
    PROCESS_MITIGATION_BINARY_SIGNATURE_POLICY ProcessSignatureImageInfo = { 0 };

    ProcessSignatureImageInfo.MicrosoftSignedOnly = TRUE;

    return SetProcessMitigationPolicy(ProcessSignaturePolicy,
        &ProcessSignatureImageInfo,
        sizeof(ProcessSignatureImageInfo));
}

BOOL EnableImageLoadPolicy()
{
    PROCESS_MITIGATION_IMAGE_LOAD_POLICY ProcessImageLoadInfo = { 0 };

    ProcessImageLoadInfo.NoRemoteImages = TRUE;

    return SetProcessMitigationPolicy(ProcessImageLoadPolicy,
        &ProcessImageLoadInfo,
        sizeof(ProcessImageLoadInfo));
}

BOOL EnablePolicies(
    BOOL bEnableDep,
    BOOL bEnableAslr,
    BOOL bEnableDynamicCodeBlock,
    BOOL bEnableSignaturePolicy,
    BOOL bEnableImageLoadPolicy
)
{
    BOOL PolicyStatus[5] = {
        TRUE,
        TRUE,
        TRUE,
        TRUE,
        TRUE
    };

    if (bEnableDep)
    {
        PolicyStatus[0] = EnableDep();
    }

    if (bEnableAslr)
    {
        PolicyStatus[1] = EnableAslr();
    }

    if (bEnableDynamicCodeBlock)
    {
        PolicyStatus[2] = EnableDynamicCodeBlock();
    }

    if (bEnableSignaturePolicy)
    {
        PolicyStatus[3] = EnableSignatureDllPolicy();
    }

    if (bEnableImageLoadPolicy)
    {
        PolicyStatus[4] = EnableImageLoadPolicy();
    }

    for (BOOL &bStatus : PolicyStatus)
    {
        if (!bStatus)
        {
            return FALSE;
        }
    }

    return TRUE;
}

VOID DisplayPolicies(
    HANDLE ProcessHandle
)
{
    PROCESS_MITIGATION_DEP_POLICY ProcessDepInfo = { 0 };
    PROCESS_MITIGATION_ASLR_POLICY ProcessAslrInfo = { 0 };
    PROCESS_MITIGATION_DYNAMIC_CODE_POLICY ProcessDynamicCodeInfo = { 0 };
    PROCESS_MITIGATION_BINARY_SIGNATURE_POLICY ProcessSignatureImageInfo = { 0 };
    PROCESS_MITIGATION_IMAGE_LOAD_POLICY ProcessImageLoadInfo = { 0 };

    BOOL EnabledStatus[5] = {
        FALSE,
        FALSE,
        FALSE,
        FALSE,
        FALSE
    };

    if (GetProcessMitigationPolicy(ProcessHandle,
        ProcessDEPPolicy,
        &ProcessDepInfo,
        sizeof(ProcessDepInfo)))
    {
        EnabledStatus[0] = (BOOL)ProcessDepInfo.Enable;
    }

    if (GetProcessMitigationPolicy(ProcessHandle,
        ProcessASLRPolicy,
        &ProcessAslrInfo,
        sizeof(ProcessAslrInfo)))
    {
        if (ProcessAslrInfo.EnableForceRelocateImages ||
            ProcessAslrInfo.EnableBottomUpRandomization ||
            ProcessAslrInfo.EnableHighEntropy)
        {
            EnabledStatus[1] = TRUE;
        }
    }

    if (GetProcessMitigationPolicy(ProcessHandle,
        ProcessDynamicCodePolicy,
        &ProcessDynamicCodeInfo,
        sizeof(ProcessDynamicCodeInfo)))
    {
        if (ProcessDynamicCodeInfo.ProhibitDynamicCode)
        {
            EnabledStatus[2] = TRUE;
        }
    }

    if (GetProcessMitigationPolicy(ProcessHandle,
        ProcessSignaturePolicy,
        &ProcessSignatureImageInfo,
        sizeof(ProcessSignatureImageInfo)))
    {
        if (ProcessSignatureImageInfo.MicrosoftSignedOnly ||
            ProcessSignatureImageInfo.StoreSignedOnly ||
            ProcessSignatureImageInfo.MitigationOptIn)
        {
            EnabledStatus[3] = TRUE;
        }
    }

    if (GetProcessMitigationPolicy(ProcessHandle,
        ProcessImageLoadPolicy,
        &ProcessImageLoadInfo,
        sizeof(ProcessImageLoadInfo)))
    {
        if (ProcessImageLoadInfo.NoRemoteImages ||
            ProcessImageLoadInfo.PreferSystem32Images ||
            ProcessImageLoadInfo.NoLowMandatoryLabelImages)
        {
            EnabledStatus[4] = TRUE;
        }
    }

    if (EnabledStatus[0])
    {
        cout << "DEP: Enabled\n";
    }

    if (EnabledStatus[1])
    {
        cout << "ASLR: Enabled\n";
    }

    if (EnabledStatus[2])
    {
        cout << "Dynamic Code Policy: Enabled\n";
    }

    if (EnabledStatus[3])
    {
        cout << "Image Signature Policy: Enabled\n";
    }
 
    if (EnabledStatus[4])
    {
        cout << "Image Load Policy: Enabled\n";
    }
}

int main()
{
    BOOL bStatus =     EnablePolicies(
        TRUE,    // DEP
        TRUE,    // ASLR
        TRUE,    // Dynamic code block
        TRUE,    // Signature restrictions for image loading
        TRUE);    // Image location loading

    if (bStatus)
    {
        cout << "All requested policies were successfully enabled\n";
    }
    else
    {
        DisplayPolicies(GetCurrentProcess());
    }

    getchar();
    return 0;
}
The above demonstration code I've written will enable: DEP (permanently); ASLR (force relocation); dynamic code prohibition; signature restrictions for future-loaded images (e.g. DLLs); and image loading restrictions (such as location of the image -> e.g. remote location). If enabling a policy fails then it'll output which ones were enabled. All of it is experimental and not 100%, you can change configuration more than I did and for the policy checking more can be added for checks.



Address Space Layout Randomisation (ASLR) cannot be fully enabled after the process has started up which is why enabling it via linker options before compilation is important. However, you can still enforce relocation for future-loaded images at run-time.

Dynamic Code Policy prevents generation of new executable code or attempts to modify the executable code in memory. I do not know much about it but used the documentation presented at Microsoft Developer Network (MSDN) to add it into the demonstration briefly.

Process Signature Policy prevents images from being loaded into the process depending on the configuration for this policy. You can restrict image loading to those only signed by Microsoft, and/or from the Windows Store. You can also however, prevent unloading of images for the same. Just to be clear, by "image" I am referring to DLLs (for example).

Process Image Load Policy prevents images from being loaded into the process depending on their location. For example purposes, I enabled it to prevent images in a remote location.

While you cannot perform modifications to enforced policies for other processes by default, you can still do it via code injection. Bear in mind that once some settings are enabled, they cannot be disabled afterwards.

It is important to understand how the code works and to read the documentation provided by Microsoft (available on the link presented nearer the start of this post) before attempting to use anything because the above demonstration code may break your software, it depends on the circumstances. It is experimental and demonstration-based only.

The new Exploit Protection feature in the latest version of Windows 10 (Fall Creators Update (FCU)) most likely uses these APIs, or just enforces it at a deeper level. We already know that DEP enable state is really controlled based on flags within a kernel-mode structure from the original post. Support for all of this has been around since Windows 8, and even prior to Windows 8 it was still possible to enforce DEP/ASLR for specific programs via registry modifications. Features like DEP should still be able to be permanently disabled via bcdedit.exe (disable NX), too.

Thanks for reading the update.
 
D

Deleted member 65228

Guest
#5
Another quick update.

In the previous update I provided a demonstration on how to use SetProcessMitigationPolicy and GetProcessMitigationPolicy however this update is for anyone interested in understanding how these functions work (a bit lower-level).

When you call SetProcessMitigationPolicy, a call to NtSetInformationProcess is performed. When you call GetProcessMitigationPolicy, a call to NtQueryInformationProcess is performed. We already know that the class for ProcessExecuteFlags is set/queried for Data Execution Prevention (DEP), but what about all the other supported mitigations (such as Address Space Layout Randomisation (ASLR)? You can check if NtSetInformationProcess/NtQueryInformationProcess is called or not by setting a break-point on them before using the Win32 API functions.

It turns out that there are new class IDs for the undocumented PROCESSINFOCLASS enum type-definition, specifically for these policies. The class ID is 52 (ProcessMitigationPolicy). Below is the updated enum type-definition.

PROCESSINFOCLASS
Code:
typedef enum _PROCESSINFOCLASS {
    ProcessBasicInformation = 0,
    ProcessQuotaLimits = 1,
    ProcessIoCounters = 2,
    ProcessVmCounters = 3,
    ProcessTimes = 4,
    ProcessBasePriority = 5,
    ProcessRaisePriority = 6,
    ProcessDebugPort = 7,
    ProcessExceptionPort = 8,
    ProcessAccessToken = 9,
    ProcessLdrInformation = 10,
    ProcessLdtSize = 11,
    ProcessDefaultHardErrorMode = 12,
    ProcessIoPortHandlers = 13,
    ProcessPooledUsageAndLimits = 14,
    ProcessWorkingSetWatch = 15,
    ProcessUserModeIOPL = 16,
    ProcessEnableAlignmentFaultFixup = 17,
    ProcessPriorityClass = 18,
    ProcessWx86Information = 19,
    ProcessHandleCount = 20,
    ProcessAffinityMask = 21,
    ProcessPriorityBoost = 22,
    ProcessDeviceMap = 23,
    ProcessSessionInformation = 24,
    ProcessForegroundInformation = 25,
    ProcessWow64Information = 26,
    ProcessImageFileName = 27,
    ProcessLUIDDeviceMapsEnabled = 28,
    ProcessBreakOnTermination = 29,
    ProcessDebugObjectHandle = 30,
    ProcessDebugFlags = 31,
    ProcessHandleTracing = 32,
    ProcessIoPriority = 33,
    ProcessExecuteFlags = 34,
    ProcessTlsInformation = 35,
    ProcessCookie = 36,
    ProcessImageInformation = 37,
    ProcessCycleTime = 38,
    ProcessPagePriority = 39,
    ProcessInstrumentationCallback = 40,
    ProcessThreadStackAllocation = 41,
    ProcessWorkingSetWatchEx = 42,
    ProcessImageFileNameWin32 = 43,
    ProcessImageFileMapping = 44,
    ProcessAffinityUpdateMode = 45,
    ProcessMemoryAllocationMode = 46,
    ProcessGroupInformation = 47,
    ProcessTokenVirtualizationEnabled = 48,
    ProcessConsoleHostProcess = 49,
    ProcessWindowInformation = 50,
    ProcessHandleInformation = 51,
    ProcessMitigationPolicy = 52,
    ProcessDynamicFunctionTableInformation = 53,
    ProcessHandleCheckingMode = 54,
    ProcessKeepAliveCount = 55,
    ProcessRevokeFileHandles = 56,
    MaxProcessInfoClass    // always last one so no need to add a value manually
} PROCESSINFOCLASS;
If you go back to the documentation over at Microsoft Developer Network (MSDN) you will see that the function SetProcessMitigationPolicy takes in a parameter with the data-type PROCESS_MITIGATION_POLICY as the first parameter and PVOID (void*) as the second parameter. The third parameter is just for size calculation. The first parameter is supposed to use a value from the PROCESS_MITIGATION_POLICY enum type-definition (where a number represents the target policy) and the second parameter is supposed to point to a structure containing the configuration data for the policy.

PROCESS_MITIGATION_POLICY
Code:
typedef enum _PROCESS_MITIGATION_POLICY {
  ProcessDEPPolicy                    = 0,
  ProcessASLRPolicy                   = 1,
  ProcessDynamicCodePolicy            = 2,
  ProcessStrictHandleCheckPolicy      = 3,
  ProcessSystemCallDisablePolicy      = 4,
  ProcessMitigationOptionsMask        = 5,
  ProcessExtensionPointDisablePolicy  = 6,
  ProcessControlFlowGuardPolicy       = 7,
  ProcessSignaturePolicy              = 8,
  ProcessFontDisablePolicy            = 9,
  ProcessImageLoadPolicy              = 10,
  MaxProcessMitigationPolicy          = 11
} PROCESS_MITIGATION_POLICY, *PPROCESS_MITIGATION_POLICY;
All we need to do is make a custom structure definition which will hold two entries: one for the PROCESS_MITIGATION_POLICY and another for the flags (used for the configuration). You can even see that the flags are in DWORD form - 0 represents FALSE and 1 represents TRUE (FALSE/TRUE are just defined integer values).

Code:
typedef struct _PROCESS_MITIGATION_INFO {
    PROCESS_MITIGATION_POLICY ProcessMitigationPolicy;
    DWORD dwFlags;
}PROCESS_MITIGATION_INFO, *PPROCESS_MITIGATION_INFO;
We can fill in the data for this structure before passing it with a call to NtSetInformationProcess.

I've written an update to the original code example which now supports using the Native API to enable the same as in the Win32 source code example based on if a TRUE/FALSE is passed in for the bNativeAPI parameter (for demonstration purposes).

stdafx.h
Code:
#pragma once
#include <Windows.h>
#include <iostream>

#include "ntdef.h"
#include "policies.h"
ntdef.h
Code:
#pragma once
#include "stdafx.h"

typedef LONG NTSTATUS;
typedef NTSTATUS *PNTSTATUS;

#define STATUS_SUCCESS 0x00000000
#define STATUS_UNSUCCESSFUL 0xC0000001

#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)

#define NtCurrentProcess() ((HANDLE)-1)

typedef enum _PROCESSINFOCLASS {
    ProcessBasicInformation = 0,
    ProcessQuotaLimits = 1,
    ProcessIoCounters = 2,
    ProcessVmCounters = 3,
    ProcessTimes = 4,
    ProcessBasePriority = 5,
    ProcessRaisePriority = 6,
    ProcessDebugPort = 7,
    ProcessExceptionPort = 8,
    ProcessAccessToken = 9,
    ProcessLdrInformation = 10,
    ProcessLdtSize = 11,
    ProcessDefaultHardErrorMode = 12,
    ProcessIoPortHandlers = 13,
    ProcessPooledUsageAndLimits = 14,
    ProcessWorkingSetWatch = 15,
    ProcessUserModeIOPL = 16,
    ProcessEnableAlignmentFaultFixup = 17,
    ProcessPriorityClass = 18,
    ProcessWx86Information = 19,
    ProcessHandleCount = 20,
    ProcessAffinityMask = 21,
    ProcessPriorityBoost = 22,
    ProcessDeviceMap = 23,
    ProcessSessionInformation = 24,
    ProcessForegroundInformation = 25,
    ProcessWow64Information = 26,
    ProcessImageFileName = 27,
    ProcessLUIDDeviceMapsEnabled = 28,
    ProcessBreakOnTermination = 29,
    ProcessDebugObjectHandle = 30,
    ProcessDebugFlags = 31,
    ProcessHandleTracing = 32,
    ProcessIoPriority = 33,
    ProcessExecuteFlags = 34,
    ProcessTlsInformation = 35,
    ProcessCookie = 36,
    ProcessImageInformation = 37,
    ProcessCycleTime = 38,
    ProcessPagePriority = 39,
    ProcessInstrumentationCallback = 40,
    ProcessThreadStackAllocation = 41,
    ProcessWorkingSetWatchEx = 42,
    ProcessImageFileNameWin32 = 43,
    ProcessImageFileMapping = 44,
    ProcessAffinityUpdateMode = 45,
    ProcessMemoryAllocationMode = 46,
    ProcessGroupInformation = 47,
    ProcessTokenVirtualizationEnabled = 48,
    ProcessConsoleHostProcess = 49,
    ProcessWindowInformation = 50,
    ProcessHandleInformation = 51,
    ProcessMitigationPolicy = 52,
    ProcessDynamicFunctionTableInformation = 53,
    ProcessHandleCheckingMode = 54,
    ProcessKeepAliveCount = 55,
    ProcessRevokeFileHandles = 56,
    MaxProcessInfoClass    // always last one so no need to add a value manually
} PROCESSINFOCLASS;

typedef struct _PROCESS_MITIGATION_INFO {
    PROCESS_MITIGATION_POLICY ProcessMitigationPolicy;
    DWORD dwFlags;
}PROCESS_MITIGATION_INFO, *PPROCESS_MITIGATION_INFO;

typedef NTSTATUS(NTAPI *pNtSetInformationProcess)(
    HANDLE ProcessHandle,
    PROCESS_INFORMATION_CLASS ProcessInformationClass,
    PVOID ProcessInformation,
    ULONG ProcessInformationLength
    );
policies.h
Code:
#pragma once
#include "stdafx.h"

#define IndexAslr 0
#define IndexDynamicCode 1
#define IndexSignatureCheck 2
#define IndexImageLoadCheck 3

VOID WrapperEnablePolicies(
    BOOL bNativeAPI
);
main.cpp
Code:
#include "ntdef.h"
using namespace std;

int main()
{
    WrapperEnablePolicies(TRUE);
    getchar();
    return 0;
}
policies.cpp
Code:
#include "stdafx.h"
using namespace std;

pNtSetInformationProcess fNtSetInformationProcess = (pNtSetInformationProcess)GetProcAddress(GetModuleHandle("NTDLL"),
    "NtSetInformationProcess");

PROCESS_MITIGATION_INFO ReturnMitigationInfo(
    INT MitigationId
)
{
    PROCESS_MITIGATION_INFO ProcessMitigationInfo;

    switch (MitigationId)
    {
    case IndexAslr:
        ProcessMitigationInfo.ProcessMitigationPolicy = ProcessASLRPolicy;
        ProcessMitigationInfo.dwFlags = FALSE |                // EnableBottomUpRandomization
            TRUE |                                            // EnableForceRelocateImages
            FALSE |                                            // EnableHighEntropy
            FALSE |                                            // DisallowStrippedImages
            FALSE;                                            // ReservedFlags

        break;
    case IndexDynamicCode:
        ProcessMitigationInfo.ProcessMitigationPolicy = ProcessDynamicCodePolicy;
        ProcessMitigationInfo.dwFlags = TRUE |                // ProhibitDynamicCode
            FALSE |                                            // AllowThreadOptOut
            FALSE |                                            // AllowRemoteDowngrade
            FALSE;                                            // ReservedFlags

        break;
    case IndexSignatureCheck:
        ProcessMitigationInfo.ProcessMitigationPolicy = ProcessSignaturePolicy;
        ProcessMitigationInfo.dwFlags = TRUE |                // MicrosoftSignedOnly
            FALSE |                                            // StoreSignedOnly
            FALSE |                                            // MitigationOptIn 
            FALSE;                                            // ReservedFlags

        break;
    case IndexImageLoadCheck:
        ProcessMitigationInfo.ProcessMitigationPolicy = ProcessImageLoadPolicy;
        ProcessMitigationInfo.dwFlags = TRUE |                // NoRemoteImages
            FALSE |                                            // NoLowMandatoryLabelImages
            FALSE |                                            // PreferSystem32Images
            FALSE;                                            // ReservedFlags

        break;
    }

    return ProcessMitigationInfo;
}

BOOL EnableDep(
    BOOL bNativeAPI
)
{
    PROCESS_MITIGATION_DEP_POLICY ProcessDepInfo = { 0 };

    if (bNativeAPI)
    {
        ULONG Flags = 1;

        return fNtSetInformationProcess(NtCurrentProcess(),
            (PROCESS_INFORMATION_CLASS)ProcessExecuteFlags,
            &Flags,
            sizeof(Flags)) == STATUS_SUCCESS ? TRUE : FALSE;
    }

    ProcessDepInfo.Enable = TRUE;
    ProcessDepInfo.Permanent = TRUE;

    return SetProcessMitigationPolicy(ProcessDEPPolicy,
        &ProcessDepInfo,
        sizeof(ProcessDepInfo));
}

BOOL EnableAslr(
    BOOL bNativeAPI
)
{
    PROCESS_MITIGATION_ASLR_POLICY ProcessAslrInfo = { 0 };

    if (bNativeAPI)
    {
        PROCESS_MITIGATION_INFO MitigationInfo = ReturnMitigationInfo(0);

        return fNtSetInformationProcess(NtCurrentProcess(),
            (PROCESS_INFORMATION_CLASS)ProcessMitigationPolicy,
            &MitigationInfo,
            sizeof(MitigationInfo)) == STATUS_SUCCESS ? TRUE : FALSE;
    }

    ProcessAslrInfo.EnableForceRelocateImages = TRUE;

    return SetProcessMitigationPolicy(ProcessASLRPolicy,
        &ProcessAslrInfo,
        sizeof(ProcessAslrInfo));
}

BOOL EnableDynamicCodeBlock(
    BOOL bNativeAPI
)
{
    PROCESS_MITIGATION_DYNAMIC_CODE_POLICY ProcessDynamicCodeInfo = { 0 };

    if (bNativeAPI)
    {
        PROCESS_MITIGATION_INFO MitigationInfo = ReturnMitigationInfo(1);

        return fNtSetInformationProcess(NtCurrentProcess(),
            (PROCESS_INFORMATION_CLASS)ProcessMitigationPolicy,
            &MitigationInfo,
            sizeof(MitigationInfo)) == STATUS_SUCCESS ? TRUE : FALSE;
    }

    ProcessDynamicCodeInfo.ProhibitDynamicCode = TRUE;

    return SetProcessMitigationPolicy(ProcessDynamicCodePolicy,
        &ProcessDynamicCodeInfo,
        sizeof(ProcessDynamicCodeInfo));
}

BOOL EnableSignatureDllPolicy(
    BOOL bNativeAPI
)
{
    PROCESS_MITIGATION_BINARY_SIGNATURE_POLICY ProcessSignatureImageInfo = { 0 };

    if (bNativeAPI)
    {
        PROCESS_MITIGATION_INFO MitigationInfo = ReturnMitigationInfo(2);

        return fNtSetInformationProcess(NtCurrentProcess(),
            (PROCESS_INFORMATION_CLASS)ProcessMitigationPolicy,
            &MitigationInfo,
            sizeof(MitigationInfo)) == STATUS_SUCCESS ? TRUE : FALSE;
    }

    ProcessSignatureImageInfo.MicrosoftSignedOnly = TRUE;

    return SetProcessMitigationPolicy(ProcessSignaturePolicy,
        &ProcessSignatureImageInfo,
        sizeof(ProcessSignatureImageInfo));
}

BOOL EnableImageLoadPolicy(
    BOOL bNativeAPI
)
{
    PROCESS_MITIGATION_IMAGE_LOAD_POLICY ProcessImageLoadInfo = { 0 };

    if (bNativeAPI)
    {
        PROCESS_MITIGATION_INFO MitigationInfo = ReturnMitigationInfo(3);

        return fNtSetInformationProcess(NtCurrentProcess(),
            (PROCESS_INFORMATION_CLASS)ProcessMitigationPolicy,
            &MitigationInfo,
            sizeof(MitigationInfo)) == STATUS_SUCCESS ? TRUE : FALSE;
    }

    ProcessImageLoadInfo.NoRemoteImages = TRUE;

    return SetProcessMitigationPolicy(ProcessImageLoadPolicy,
        &ProcessImageLoadInfo,
        sizeof(ProcessImageLoadInfo));
}

BOOL EnablePolicies(
    BOOL bEnableDep,
    BOOL bEnableAslr,
    BOOL bEnableDynamicCodeBlock,
    BOOL bEnableSignaturePolicy,
    BOOL bEnableImageLoadPolicy,
    BOOL bNativeAPI
)
{
    BOOL PolicyStatus[5] = {
        TRUE,
        TRUE,
        TRUE,
        TRUE,
        TRUE
    };

    if (bEnableDep)
    {
        PolicyStatus[0] = EnableDep(bNativeAPI);
    }

    if (bEnableAslr)
    {
        PolicyStatus[1] = EnableAslr(bNativeAPI);
    }

    if (bEnableDynamicCodeBlock)
    {
        PolicyStatus[2] = EnableDynamicCodeBlock(bNativeAPI);
    }

    if (bEnableSignaturePolicy)
    {
        PolicyStatus[3] = EnableSignatureDllPolicy(bNativeAPI);
    }

    if (bEnableImageLoadPolicy)
    {
        PolicyStatus[4] = EnableImageLoadPolicy(bNativeAPI);
    }

    for (BOOL &bStatus : PolicyStatus)
    {
        if (!bStatus)
        {
            return FALSE;
        }
    }

    return TRUE;
}

VOID DisplayPolicies(
    HANDLE ProcessHandle
)
{
    PROCESS_MITIGATION_DEP_POLICY ProcessDepInfo = { 0 };
    PROCESS_MITIGATION_ASLR_POLICY ProcessAslrInfo = { 0 };
    PROCESS_MITIGATION_DYNAMIC_CODE_POLICY ProcessDynamicCodeInfo = { 0 };
    PROCESS_MITIGATION_BINARY_SIGNATURE_POLICY ProcessSignatureImageInfo = { 0 };
    PROCESS_MITIGATION_IMAGE_LOAD_POLICY ProcessImageLoadInfo = { 0 };

    BOOL EnabledStatus[5] = {
        FALSE,
        FALSE,
        FALSE,
        FALSE,
        FALSE
    };

    if (GetProcessMitigationPolicy(ProcessHandle,
        ProcessDEPPolicy,
        &ProcessDepInfo,
        sizeof(ProcessDepInfo)))
    {
        EnabledStatus[0] = (BOOL)ProcessDepInfo.Enable;
    }

    if (GetProcessMitigationPolicy(ProcessHandle,
        ProcessASLRPolicy,
        &ProcessAslrInfo,
        sizeof(ProcessAslrInfo)))
    {
        if (ProcessAslrInfo.EnableForceRelocateImages ||
            ProcessAslrInfo.EnableBottomUpRandomization ||
            ProcessAslrInfo.EnableHighEntropy)
        {
            EnabledStatus[1] = TRUE;
        }
    }

    if (GetProcessMitigationPolicy(ProcessHandle,
        ProcessDynamicCodePolicy,
        &ProcessDynamicCodeInfo,
        sizeof(ProcessDynamicCodeInfo)))
    {
        if (ProcessDynamicCodeInfo.ProhibitDynamicCode)
        {
            EnabledStatus[2] = TRUE;
        }
    }

    if (GetProcessMitigationPolicy(ProcessHandle,
        ProcessSignaturePolicy,
        &ProcessSignatureImageInfo,
        sizeof(ProcessSignatureImageInfo)))
    {
        if (ProcessSignatureImageInfo.MicrosoftSignedOnly ||
            ProcessSignatureImageInfo.StoreSignedOnly ||
            ProcessSignatureImageInfo.MitigationOptIn)
        {
            EnabledStatus[3] = TRUE;
        }
    }

    if (GetProcessMitigationPolicy(ProcessHandle,
        ProcessImageLoadPolicy,
        &ProcessImageLoadInfo,
        sizeof(ProcessImageLoadInfo)))
    {
        if (ProcessImageLoadInfo.NoRemoteImages ||
            ProcessImageLoadInfo.PreferSystem32Images ||
            ProcessImageLoadInfo.NoLowMandatoryLabelImages)
        {
            EnabledStatus[4] = TRUE;
        }
    }

    if (EnabledStatus[0])
    {
        cout << "DEP: Enabled\n";
    }

    if (EnabledStatus[1])
    {
        cout << "ASLR: Enabled\n";
    }

    if (EnabledStatus[2])
    {
        cout << "Dynamic Code Policy: Enabled\n";
    }

    if (EnabledStatus[3])
    {
        cout << "Image Signature Policy: Enabled\n";
    }

    if (EnabledStatus[4])
    {
        cout << "Image Load Policy: Enabled\n";
    }
}

VOID WrapperEnablePolicies(
    BOOL bNativeAPI
)
{
    BOOL bStatus = EnablePolicies(
        TRUE,    // DEP
        TRUE,    // ASLR
        TRUE,    // Dynamic code block
        TRUE,    // Signature restrictions for image loading
        TRUE,    // Image location loading
        bNativeAPI);    // Native API?

    if (bStatus)
    {
        cout << "All requested policies were successfully enabled\n";
    }
    else
    {
        DisplayPolicies(GetCurrentProcess());
    }
}

Thanks for reading.