EDR Series : How EDR Hooks API Calls (Part-1)
EDR is a hot topic in the present world. In this blog post we will do the technical analysis of hooking APIs by McAfee EDR.
One thing that we all know is most of the EDR hooks in the user-land (mostly ntdll.dll). In this post we’ll talk about how EDR hooks APIs in those DLLs. We’re looking into the McAfee EDR in this post. So, the hooking engine of other EDR might be different from the McAfee EDR.
NOTE: To understand all the functions flow path please refer to the Injection flow diagram at the end of this blog.
Now, let’s get to the point. In the following image we can see 3 dlls which are related to the McAfee are injected into the process.
- mfedeeprem32.dll :- McAfee Deep remediation Injected
- mfehcinj.dll :- McAfee HookCore Injected Environment
- mfehcthe.dll :- McAfee HookCore Thin Hook Environment
At this time, we know 3 McAfee dlls are injected into the process. Now the question that arises are “Which dll was loaded first?”, “Are they loaded separately?”, “Is one of the DLL is responsible for loading all other DLLs?” etc. To find out the answers to all of these questions we’ll start by opening our process with x64dbg.
Inside x64dbg both “DLL Entry” and “DLL Load” are enabled which will break during DLL loading and DLL entry point.
Now if we reload the x64dbg it’ll start to break on dll load. We’ll continue the execution until we find one of the dll from McAfee. After a few breaks we’ll see that the mfehcinj.dll is loaded, which means mfehcinj.dll is the first dll that gets loaded into the process.
If we look into the DLL exports, we can see 4 Export functions. The one we’re interested is in Initialize function.
In Initialize function we can see there’s a call to ThinHookInterface which basically loads the “mfehcthe.dll” and try to get the function address “GetThinHookInterface” from “mfehcthe.dll” libaray and do some operations.
Inside the ThinHookInterface call we can see “mfehcthe.dll” is loaded using LoadLibraryA API call. Then the address of “GetThinHookInterface()” function is resolved and called.
After the call to GetThinHookInterface() function it’s checking whether the returned value is zero or not. If the returned value is not zero it’ll go to the loc_1000724D and a few steps down it’s calling the address sub_10006A60 (resolve_ldr_x_addr).
If we go inside the function resolve_ldr_x_addr we can see that it’s loading 2 dlls from ntdll i.e., “LdrLoadDll” and “LdrResolveDelayLoadedAPI” with the function sub_100088A0 (GetProcAddress_Dyn).
Actually, this GetProcAddress_Dyn function is allocating the local memory and putting some data there along with the resolved function address.
At this point we’re confused like why it’s putting the resolve address in the heap because these resolved function addresses were never called till now. Now after a few operations we got back to the Initialize function. Few steps down we can see the function sub_10003550 (mfedeeprem32_and_loading_proc_address) is being called. In short, this function loads the dll mfedeeprem32.dll and resolves other function addresses.
If we further dig into the function sub_10003550 and move here and there we find a call to a function sub_10002A90. This function is actually quite large. It does lots of stuff like creating named pipe, loading libraries, resolving function addresses etc. and also there are lots of nested function calls.
However, we are only interested in 2 things here:
- Library that is loaded
- How other functions are resolved and moved to the heap
Here we can see at loc_1002ED1 “mfedeeprem32.dll” is getting loaded with LoadLibraryW.
After setting up the named pipes and its configuration, it’ll call one of the functions in mfedeeprem32.dll i.e., mfedeeprem32.sub_1000A380. This function actually resolves a few functions address and put them in a heap.
- mfedeeprem32.sub_1000A380 calls mfedeeprem32.sub_1000A150
- mfedeeprem32.sub_1000A150 functions has a loop implemented which loops until all the functions are resolved
- mfedeeprem32.sub_1000A150 calls the function mfehcinj.sub_10003B40
mfehcinj.sub_10003B40 function calls the same function as the GetProcAddress_Dyn (sub_100088A0) to resolve the function address and store it in a heap.
These are all the resolve API function Addresses including the first 2 resolved APIs.
As we can see in the above table 19 APIs addresses were resolved and stored in the local memory. After this we’ll again come back to the Initialize function. Few moments later we found a function sub_10005490 and this is a root function from where the hooking begins to start.
There are few nested functions which you can look into the API hook Initialization graph for more details.
Inside sub_10005490 there’s a call to a function from mfehcthe.dll i.e., mfehcthe.sub_10004FC0.
If we further dig into the function mfehcthe.sub_10004FC0 we’ll reach the function mfehcthe.sub_10001650 where 2 functions are moved into the memory which are related to the actual EDR hooked functions. We’ll prove this later.
Inside the function mfehcthe.sub_10001650 we can see that the memory is allocated and the memset is executed with bytes 0xcc.
As we mentioned above 2 functions are copied into the newly allocated memory using memcpy function.
These 2 are the EDR hooked function we’ll confirm this very soon. Before that in function mfehcthe.sub_100068F0 another function will be written to the memory and will be at offset 0x660 also, this is the exact function where hooked jumps point to and after this this function will call above 2 functions.
These 2 are the EDR hooked function we’ll confirm this very soon. Before that in function mfehcthe.sub_100068F0 another function will be written to the memory and will be at offset 0x660 also, this is the exact function where hooked jumps point to and after this this function will call above 2 functions.
Let’s suppose the mini_shellcode_1 is at address 0xBB0000 the hooked jumps pointed function/codes will be at 0x660 i.e., BB0660
We can confirm this in the debugger as well:
Till now only the codes part of the EDR hook functions are moved into the memory however, patches are still not applied to the user-land dlls before syscall or Functions. The jump patches need to be applied so that it’ll jump to the EDR hook function/codes which check let’s say suspicious behavior before doing actual syscall or executing the Functions.
After that inside the function mfehcthe.sub_10006180 actual patches are applied.
Inside the function mfehcthe.sub_10006180 we can see the call to the WriteProcessMemory in which BaseAddress is the function address to be hooked and the buffer is a jump byte to be applied which is of size 5 bytes. And the BaseAddress is retrieved from the address where it was resolved and saved before which means there are 19 BaseAddress are stored in the memory. And jump patches are calculated just before the call to mfehcthe.sub_10004FC0.
If we closely look into the debugger, we can confirm that the base address is of the “LdrLoadDll” function and the bytes are the jump hooks.
Also, we can confirm that 5 bytes will be overwritten and the starting byte is “e9” which is “jmp” instruction.
We can confirm that before writing the patches everything is normal.
After the patches are applied, we can see the jump pointing to the address 0xBB0660. Now it’s confirmed that the address that it points to after applying the hook is 0xBB0660.
Now one by one all the 19 functions will be hooked.
In conclusion, 19 functions which are to be hooked to their addresses are resolved and placed in the local memory. Then 2 functions are copied in the memory which are the actual codes that checks everything coming to that API. And again, another function is copied to the memory at offset 0x660.
These all 3 functions are copied into the same memory region. And about the 3rd function which is moved into the offset 0x660, in there few things are dynamically resolved before moving into the memory such as the base address of the 1st function (mini_shellcode_1).
The 2nd function will be called inside the 1st function. Now everything is set in the memory the hook jump bytes are calculated and applied to all those 19 functions and the jump will point to the address in memory at offset 0x660 where the hook codes are present.
Hang tight for other series of this blog !
Author : John Sherchan, Red Team Security Researcher at CyberWarFare Labs
Yash Bharadwaj, CTO CyberWarFare Labs
Follow us on social media :
Stay connected with news and updates!
Join our mailing list to receive the latest news and updates of cutting-edge cyber security research from our team.
Don’t worry, your information will not be shared.