Process Mitigation Policies & ACG

Anti-Virus and EDR solutions often inject DLL’s into the address space of an application to perform user-land function hooking.

For instance, using WinDBG we can see the application being debugged has been injected with an Anti-Virus vendors DLL;

0:000> lm
start             end                 module name
00007ff7`ee800000 00007ff7`ee838000   notepad    (pdb symbols)          C:\ProgramData\Dbg\sym\notepad.pdb\67D551E7B9BB3B68E823F5B998BD94531\notepad.pdb
00007ffa`5da10000 00007ffa`5db6b000   atcuf64    (deferred)             
00007ffa`5db70000 00007ffa`5dc3f000   bdhkm64    (deferred)             
00007ffa`9c190000 00007ffa`9c42a000   COMCTL32   (deferred)     

0:004> lmDvmbdhkm32
Browse full module list
start    end        module name
6af50000 6affc000   bdhkm32    (deferred)             
    Image path: C:\Program Files\Bitdefender\Bitdefender Security\bdhkm\dlls_266184808945032704\bdhkm32.dll
    Image name: bdhkm32.dll
    Browse all global symbols  functions  data
    Timestamp:        Wed Sep 28 10:04:35 2022 (63340E23)
    CheckSum:         000BA952
    ImageSize:        000AC000
    File version:     1.7.229.0
    Product version:  1.0.0.0
    File flags:       0 (Mask 3F)
    File OS:          40004 NT Win32
    File type:        0.0 Unknown
    File date:        00000000.00000000
    Translations:     0409.04b0
    Information from resource tables:
        CompanyName:      BitDefender S.R.L. Bucharest, ROMANIA
        ProductName:      BitDefender® AntiVirus
        InternalName:     BDHKM32.DLL
        OriginalFilename: BDHKM32.DLL
        ProductVersion:   1
        FileVersion:      1.7.229.0 #0x83df3e0
        FileDescription:  BitDefender Hooking DLL
        LegalCopyright:   © BitDefender S.R.L. All rights reserved.        

The injected DLL’s will be used to monitor the target process, so it’s in our interests to remove them. Two potential methods of doing this are using process mitigation policies and Arbitrary Code Guard (ACG).

Process Mitigation: Binary Signature Policies

One process mitigation policy of interest is binary signature policies. Microsoft provide the following definition for binary signature policies;

The policy of a process that can restrict image loading to those images that are either signed by Microsoft, by the Windows Store, or by Microsoft, the Windows Store and the Windows Hardware Quality Labs (WHQL). The lpBuffer parameter points to a PROCESS_MITIGATION_BINARY_SIGNATURE_POLICY structure that specifies the signature policy flags.

Executables with this policy set cannot be injected into unless the injected DLL has been signed by Microsoft. To start a process with a binary signature policy, you just need to call CreateProcess with the relevant ProcThreadAttribute. Similar steps were covered in the PPID Spoofing article.

#include <iostream>
#include <Windows.h>

int main()
{
	PROCESS_INFORMATION pi;
	STARTUPINFOEXA si;
	SIZE_T attributeSize;
	
	InitializeProcThreadAttributeList(NULL, 1, 0, &attributeSize);
	PPROC_THREAD_ATTRIBUTE_LIST attributes = (PPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, attributeSize);
	InitializeProcThreadAttributeList(attributes, 1, 0, &attributeSize);

	DWORD64 policy = PROCESS_CREATION_MITIGATION_POLICY_BLOCK_NON_MICROSOFT_BINARIES_ALWAYS_ON;
	UpdateProcThreadAttribute(attributes, 0, PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY, &policy, sizeof(DWORD64), NULL, NULL);
	si.lpAttributeList = attributes;

	CreateProcessA(NULL, (LPSTR)"notepad", NULL, NULL, TRUE, EXTENDED_STARTUPINFO_PRESENT, NULL, NULL, &si.StartupInfo, &pi);
	HeapFree(GetProcessHeap(), HEAP_ZERO_MEMORY, attributes);

	return 0;
}

The Get-ProcessMitigation PowerShell cmdlet can be used to determine if the policy has been set correctly;

Get-ProcessMitigation -Id 8240

ProcessName                      : Notepad
Source                           : Running Process
Id                               : 8240

BinarySignature:
    MicrosoftSignedOnly                : ON
    AllowStoreSignedBinaries           : OFF
    AuditMicrosoftSignedOnly           : OFF
    AuditStoreSigned                   : OFF

This may be useful to bypass some solutions, although most AV vendors use a valid Microsoft signature to prevent this from working;

Arbitrary Code Guard (ACG)

ACG is a security technology that prevents an executable from loading external code. This is done by prohibiting the execute flag from being set during memory allocation operations.

With administrative permissions on a system, you can use the following PowerShell command to set the mitigation policy on a process;

Set-ProcessMitigation -name notepad.exe -enable BlockDynamicCode

Similarly we can confirm it’s been configured using the Get-ProcessMitigation cmdlet;

Get-ProcessMitigation -name notepad.exe

ProcessName                      : notepad.exe
Source                           : Registry
Id                               : 0

DynamicCode:
    BlockDynamicCode                   : ON
    AllowThreadsToOptOut               : NOTSET
    Audit                              : NOTSET
    Override DynamicCode               : False

Connecting to the application with a debugger shows our Anti-Virus DLL is no longer being injected 🙂

0:007> lm
start             end                 module name
00007ff7`b87c0000 00007ff7`b87f8000   notepad    (pdb symbols)          C:\ProgramData\Dbg\sym\notepad.pdb\67D551E7B9BB3B68E823F5B998BD94531\notepad.pdb
00007ffa`82b10000 00007ffa`82bed000   efswrt     (deferred)             
00007ffa`84840000 00007ffa`848a6000   oleacc     (deferred)             
00007ffa`8e1c0000 00007ffa`8e26c000   TextShaping   (deferred)             
00007ffa`919c0000 00007ffa`91ab4000   MrmCoreR   (deferred)             
00007ffa`936a0000 00007ffa`93799000   textinputframework   (deferred)             
00007ffa`953b0000 00007ffa`953cd000   MPR        (deferred)             
00007ffa`96e10000 00007ffa`97010000   twinapi_appcore   (deferred)             
00007ffa`97cf0000 00007ffa`97e44000   wintypes   (deferred)             
00007ffa`98e10000 00007ffa`9916e000   CoreUIComponents   (deferred)             

ACG is trivial to implement using the SetProcessMitigationPolicy function;

#include <iostream>
#include <Windows.h>

int main()
{
	PROCESS_MITIGATION_DYNAMIC_CODE_POLICY codePolicy = {};
	codePolicy.ProhibitDynamicCode = 1;

	SetProcessMitigationPolicy(ProcessDynamicCodePolicy, &codePolicy, sizeof(codePolicy));

    getchar();
    
    printf("Done");

    return 0;

}

However, after running this code we find that the AV Vendor DLL is back!

0:001> lm
start    end        module name
007e0000 00800000   ACG      C (no symbols)           
754f0000 75612000   atcuf32    (deferred)             
75620000 756cc000   bdhkm32    (deferred)             
75a10000 75b00000   KERNEL32   (pdb symbols)          C:\ProgramData\Dbg\sym\wkernel32.pdb\5BAE423055358D71E7EF8F4360C760F61\wkernel32.pdb
77020000 77239000   KERNELBASE   (deferred)             
77810000 779b4000   ntdll      (pdb symbols)          C:\ProgramData\Dbg\sym\wntdll.pdb\F9A68320E338E9BBE5189F90856B444A1\wntdll.pdb

ACG Prevents a process from marking it’s own memory as executable, such as commonly found in the first stage of a ROP chain – but it doesn’t prevent remote processes from doing the same thing.

Interestingly, setting the policy with Set-ProcessMitigation does stop the AV DLL being injected. It would make sense that enabling the policy after the process has spawned would not be effective, since the DLL has already been injected by that point. Unfortunately there doesn’t appear to be a way to set the Process Mitigation policy when calling CreateProcess either.

In Conclusion

Signature Policies and ACG are unlikely to help preventing DLL injection by Anti-Virus vendors.

This leaves the following options;

  • Reload the hooked DLL’s from disk
  • Modify the injected DLL’s to make sure they don’t take action
  • Attempt direct system calls to bypass the hooks