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