Hey! I have two kernel drivers that communicate with each other, and a callback mechanism where one driver calls into another through a global pointer. Looking at my compiled driver, I can see the indirect call is wrapped with a CFG check, which made me wonder how KCFG works in that regard, I mean, I’m aware there’s a bitmap of addresses considered valid targets for indirect calls. But is it per module? Or global to kernel? If it’s per module, how would it know about a valid address in another driver? Isn’t it being built by the compiler?
Why would it not work exactly the same way as user mode programs that link to dlls that implement callback interfaces?
So it’s global to the kernel? Because in UM it’s managed per process right?
Yes it is global to the kernel as ntoskrnl + all the drivers form one dynamically linked program in a single address space. The indirect call checkers are imported by your driver from the kernel (a bit like in UM programs the checkers are imported from ntdll.dll).
Yeah, that makes sense, but I think the way it works in kernel mode might be a bit different. In user mode, things like EXEs and DLLs can share CFG info more easily. But for drivers, I’m not sure if the kernel shares that CFG data between them. Like, how would it know a function in another driver is safe to call? Maybe someone knows if the kernel merges that info when loading drivers?
That info is part of the kernel's CFG bitmap, and drivers are just modules loaded into the kernel's address space, why would the CFG info not be shared? I am way oversimplifying things here but EXE & DLL in user mode are analogous to NTOSKRNL & SYS in kernel mode. The CFG implementation is very similar between the two.
The section named "Contemporary Mitigation #1: CFG/kCFG" in this post: The State of Exploit Development: Part 1 | CrowdStrike gives a high level overview of usermode and kernel mode CFG.