← All talks

Callback objects — Everything you didn't know you wanted to hook in the kernel

BSides Delhi · 202044:491.3K viewsPublished 2020-11Watch on YouTube ↗
Speakers
Tags
CategoryTechnical
StyleTalk
About this talk
Callback objects - Everything you didn’t know you wanted to hook in the kernel Whether you’re an attacker, trying to persist and gather information while avoiding detection, or a defender, trying to monitor everything that’s running on a box and staying a step ahead of the attackers, everyone wants to know what’s happening on a machine. Windows has a mechanism that can fulfill all these needs and isn’t getting the attention it deserves – callback objects. Used by many kernel components and easy to access for 3rd-party drivers, these objects can supply valuable information about various kernel events, internal AV communication, and more. Even Patch Guard has its own callback! As helpful as they are, they are mostly ignored by security solutions, making them a great place for rootkits to hide in, where no one is looking. CallbackObjectAnalyzer: https://github.com/yardenshafir/CallbackObjectAnalyzer Slides: https://github.com/yardenshafir/CallbackObjectAnalyzer/tree/main/Slides Yarden Shafir Yarden Shafir started dancing at the age of 7, and later joined a rhythmic gymnastics team and competed during her teenage years. After her military service, she practiced pole dancing and fell in love with acrobatics. In recent years she performs aerial arts for the circus, trains whenever possible, and teaches lyra and silks in Israel, while also having a rich background of Windows Internals research originally at Sentinel One, followed by her current role as a Software Engineer at CrowdStrike working on various EDR capabilities and EPP features.
Show transcript [en]

uh this is by yardan shafir hello yarden hello how are you i'm very well thank you how are you and and uh where are you calling in from i'm good i'm calling in from greece right now ah lovely excellent and is is that your artwork on the walls behind you or is that no no this is not mine this is good anyway uh we are well we're in order to try and stay on track we're going to crack straight on you are going to talk to us about uh and since i'm definitely not going to know what this is about uh call back objects everything you didn't know you wanted to hook in the kernel

um so yarden please take it away and i'll be monitoring for questions and get back to you at the end yes let me share my slides okay let me know if that worked i think that's worked i will leave you to get on with your presentation thank you very much thank you uh okay so yeah like i said uh amiodan and we're gonna talk about callback objects is um an internal communication mechanism in the windows kernel that you're probably gonna want to use or look at for your next rootkit or adr software so first of all about me um my name is joden i'm a software engineer at crowdstrike previously it's center one uh doing windows security research and

development form of defense purposes i'm also an instructor in onesider teaching windows internals classes together with nesco and i'm also a circus artist instructor and performer mostly working in israel teaching aerial teaching performing aerial arts i do a lot of things i'm a pastry chef and during quarantine i mostly taught my mom how to unmute zoom almost by herself so that's probably my greatest achievement so far and what about callback objects we might sound familiar to a lot of you because the windows kernel has a lot of things that have the name callback in them there's a lot of user mode combat kernel mode callbacks some of you might know the process and thread and load image

notifications which are also called callbacks those are all different things for example process and thread on notifications are legacy callbacks which are a different thing in which the kernel notifies different drivers about certain events happening in the system now callback objects are a slightly different thing they're an actual object like a file like process like a symbolic link they're one of about 70 different object types in the system and they're named when you create a callback you can choose a name for it or not you don't have to and that's how other drivers in the system are going to recognize it and the thing that makes callback objects different from a few other communication mechanisms in the kernel

is that anyone can create a callback anyone can register for one and anyone can notify one unlike other mechanisms in which the colonel exports some functionality some options some call back like here you can register to receive notifications about processes with callback objects anyone can use them in the kernel any way they want there are certain callbacks that the kernel makes available like power state um you know if the system is going to shut down or restart or go to sleep and a few other things but you can also create your own callback object for example if your product has two or three or four different drivers that want a way to pass information to

each other you can create internal callback objects have all your drivers or some of them register for those and any of them can notify the others to pass any kind of information you want callbacks when notified receive three arguments two of them controlled by whoever is notifying it so that's as long as all sides as long as all sides of the communication agree on which arguments these are which types they're in you can have your own internal communication mechanism inside the kernel but it's not private anyone can register to any callback almost um we'll see how that's not true and how it's still kinda true anyway so this is roughly what that mechanism looks like you have

a few drivers one of them creates a callback with excrete callback creates a new callback object that they said can have or not have a name and other drivers can register for that callback to register for they first need to find it they do it again with excreate callback which create which gives them pointer a handle to the object and then they can register functions they can register functions that will be notified when someone wants to notify the callback and then after there are registered drivers anyone any of the registered drivers can notify the callback and anyone who registered a function will be called thing is you can technically you can notify a callback without being

registered to it you just need to have the handle to the callback now you're not supposed to have the handle without registering because you're supposed to receive it when you register callback function or create the callback but you're in the kernel you have access to basically everything so you can kind of get the handle anyway by yourself here is some simple code um a simple poc code to show how to create and register a notification function for callback in this case it's the kernel's image verification uh info callback that's that's one that's provided by the windows kernel starting in windows 8.1 and that's a that's a fun callback that gives you for every newly loaded driver

information about its hash its signature its certification um so for an edr tool that's a fun one to use as you can see like with almost any object we need to initialize the object attributes part of which are the name of the callback we want then we call excreate callback to get object and then we use that object to register our own function in this case they called it image verification callback function and then every time a new driver is going to be loaded kernel is going to notify our callback and any other callback and any other function that was registered for this call

fortunately the internal structures that are used for uh call backstation are not public they're not documented and they're not in the symbols which makes callback objects a bit harder and more annoying to work with um especially if you want to debug them because you can't use public symbols for that um but the structures are pretty well known anyway we have two main structures that we need to know there is the one on the right that's the callback object itself that's what you're creating and receiving when you call ex create callback it has a signature which is not very interesting and a lock and then it has two linked lists first thing i like linked lists because

that's the easiest way to get access to information that you might not normally have access to and you want to find something windows kernel if it's not exported or some official documented thing you might have a hard time finding it that's why we have makes like binary scanning trying to match specific bytes signatures but if something is linked in the list it's enough to have one part of the list to have all of it you can just walk the list so i like lists in this case we have two the last field here is the callback list which links all the callback objects in the system in a single list which means once you have one callback object

you can find all of them and the second is the list of registered callbacks because when you register a function to be notified by a callback you don't just register a function you register you create a callback registration structure that's what that's what gets internally created and we'll look at that in a second and those are all connected by a linked list which makes sense girl needs to have a way to get all the registered functions and notify them one by one so alex there's an easy way to do that and the last field in the callback object that's interesting is allow multiple callbacks like the name suggests that's um boolean value that says allow do you want to allow

more than one callback function to be registered here or not that's that field is being specified when the callback is first created and if it's set to false when you register callback function the kernel will check if there's already someone registered for this callback and if there is it won't let you register no that's not a very major limit because the callback is in the kernel your driver is in the kernel you can go and catch the list yourself and just add your function there anyway but you're not supposed to so now when you create when you register a function for a callback behind the scenes there's a callback registration structure that's created again that has the linked list

connecting all the callback registration structures it has the pointer to the callback function and it has a pointer to something called a callback context now that callback context is see if i have that in my example i didn't use it um that callback context is a pointer that you supply when you register for a callback and whenever your callback whenever the callback is notified every function that's listed in the list is going to be called and receive as one of the arguments the callback context that it registered it's not something like the caller it's not something that whoever notifies the callback determines it's not something that the kernel determines whoever registered the callback will receive

its own callback context when the callback is notified on first look that might not make much sense because why would you register an argument that you're gonna receive later actually that's um the logic behind is that this is a pretty um rough way to implement classes but kernel is is not built with classes it's basically c there's no there's no complicated development concepts there there's no classes in the kernel but that's a rough way to implement a class where the whenever the callback object it would is notified would receive the class instance basically it's a way for drivers to have some internal global data structure that they want to have available in a lot of cases including in their

callback in their callback function but without saving it as a global that anyone can easily find so that's that and why is that interesting because that means that those callback contexts when used can will probably be pointers to pretty large internal data structures where we might have information that we're interested in but we'll have a pretty hard time finding otherwise because it's usually full memory it can be anywhere it doesn't usually have something that makes it easy to identify to find if you need to go and dig inside the driver for pointers but the callback context is an easy way to find a pointer into it a lot of drivers don't use that callback context later we'll

dump some callbacks and see and mostly it's null but when it's not you can sometimes find some interesting things there here we basically have the same thing in diagram form we have the two lists we have a list that connects all the callback objects together and in each one of them we have the internal list connecting all the callback registration entries inside the callback each callback registration is one driver that registered a function to be notified when the callback is notified as we can see here if we get one of them any of them a callback object or a callback registration we have all of them we can find every registered function in every callback object in the

system means even if there's callback objects that we can't easily find for example unnamed ones that we can't index by name we still have a way to find them now who actually uses those callbacks first the windows kernel itself exports but not exports exposes some callbacks like a very popular one pretty well known one is the power state callback i mentioned earlier um there is a processor add callback there's license there's a license licensing data callback and like we saw the image verification one that's pretty useful for edr software to to know what drivers are loaded one thing i have to add is that callback objects are unlike some other parts of the system callback object

objects are just notifications for example being notified that a driver is being loaded doesn't give you the option of blocking it or changing it in any way it just sends you message saying hey this thing is get is being loaded here's its information good luck uh so there's a few of those there's some interesting callbacks in the kernel that some of them are used pretty often for example win32k has some callbacks that uses mostly anonymous callbacks for internal 132k communication and for whoever is interested in win32k 132k exploitation and bugs and finding more ways in there that might be an interesting direction to look at other than that there are security products that use them for internal

communication security products that have more than one driver and want an easy way to synchronize between them and pass information might use that for example windows defender has a few different drivers and uses callback objects for communication between them and it's actually a fun example to look at that we'll look at in a bit more detail later and another example that's that's pretty interesting is patch card patch card has a callback object that is used to initialize a pointer to a function that does patch or periodic checks and we'll look at that a bit more later too now i'm mentioning all these callbacks but how can we actually find them these callbacks because they're not just

the when just the kernels callbacks because anyone can use them each of you will have different callbacks on your system based on what products run run there like is windows defender running or do you have some other security product or some other things that use callbacks so a few ways to see what callbacks you have one of them is using winobgx that's a pretty easy one just looking for callbacks with window gx the only limitation is that it's only going to show named callbacks it won't show you the unnamed one so you'll only see ones that have a name another option is to use wind bag and run bang object uh slash callbacks i'll show this later when i show the

demos uh but that again that will only show the named callbacks you can also use winback to write a script a dx script or javascript to a javascript script to manually parse the callback objects and then you will have all of them not just the named ones the part that makes it difficult is again the internal structures are not documented and not in the symbols so you'll have a bit of a hard time doing that it is possible but it will be slightly difficult to it's that difficult to work with undocumented structures in one bag third option which i'll show later is using just using c writing either debugger api which is what i did or

write a driver that will parse the callback list both are valid options i i wrote a tool to do that um a debugger api tool that i'll share later and then if we look at the list this this way ex screenshot is from my machine and we can see stuff like that first one the one with the with all the weird named number that's the patch card callback actually then we have all sorts of callbacks from my audio and video cards there's a few of them here we see the power state callback we see the image verification callback and at the end we see three wd callbacks those are the windows defender ones the one that windows defender uses for

its internal communication now we've seen this mechanism we see what it does we see how we can legitimately use it how can we abuse this mechanism there's all sorts of fun ways like i said this mechanism is can used for internal communication but it is not private as long as a callback is not defined with don't allow multiple callbacks anyone can register to it you cannot specify who is allowed and who is not allowed even if technically a callback is not allowed to have multiple callbacks you can go and patch them and patch the memory yourself you can find pointers and hook um communication by replacing the pointer with your own function or just adding another one to the list

and listening into callbacks that might have interesting information that we want to follow for example the windows defender callbacks you can also remove remove functions from those lists which will probably break something depending on what you remove and from which callbacks you can use the callback context to find pointers to interesting structures interesting internal structures used by drivers that you that you care about and you can also notify those callbacks yourself with possibly false information which good with security products which could lead some to some interesting results there

a couple of the examples that i mentioned that we'll look at are windows defender and patch card so windows defender a very rough um very rough design it has is windows defender on windows 10 has three drivers first one is the filter driver which does love the work it registers for the processing thread notifications it monitors file system does all sorts of things that one has one callback the second one is the network driver which communicates with the management system and that one has a second callback and the last one is the boot or elam driver um for anyone who's not familiar elam is a microsoft program in which it allows certain av vendors to load their driver

very early earlier than most drivers are allowed to load and what those drivers are allowed to do is pretty much one thing register for windows callback another callback object which will give them which will notify them every time a boot driver is loaded with with information about the hash with some basic information about the driver this one allows those allows the boot drive allows the av driver to use um an allow list or block list to decide if it wants to block those drivers that's meant to allow some av drivers to block early loaded malicious drivers like root kits that will be loaded in the system

so windows defender has a boot drive has a has an elam driver that is loaded very early in the system for this callback and has an internal callback object of its own that it uses to communicate with the other with the filter driver so what does this callback do um one thing to know about elon drivers is that they have to unload early too they're allowed to load very early see all the boot drivers load uh block or not block them and then they need to unload they can't keep running so what the windows defender driver does is that it receives those driver notifications and it saves the information about the drivers in an internal data structure then when the

filter driver loads right before the boot driver needs to unload the filter driver registers for the windows defender boot callback and notifies it telling the boot driver hey send me all the driver information that you kept so it can use it for whichever detection logics or notifications or whatever it wants to do with them and the boot driver sends back the all the info that internal data structure with all the information about the early loaded drivers so that's one callback which is if you manage to load early enough before the boot driver is unloaded you can notify that callback and receive all that information to us and when you know how to parse it second callback is the filter driver

callback that's the call we had it here that's the last one on the list w the the boot driver callback is the wdeb notification callback and the filter driver has the it creates the wd process notification callback

so like the like it sounds from the name the wd process notification callback is a way for windows defender to notify other drivers about processes um starting in the system because what happens is the filter driver registers for the legacy process notification callbacks the one where the kernel notifies drivers about every loaded process so when your defender receives that notification that a new process has been loaded it does some analysis gets some information about the driver and then it wants to pass that information to the network driver so it can pass it on to the management server to the centralized server so when a windows defender is notified about a new process it gets whatever information needs it

saves it in its own internal structure and then it uses the windows defender callback object to notify the network driver hey there is a new process in the system here is the information about it now what's fun about this callback is that something that you haven't mentioned before callback objects are not very well known and as far as i know they're not really monitored by anyone i don't know any security products that monitor them uh tools like when check don't dump callback objects as far as i'm aware so they're not really looked at but for example the call legacy called accept legacy process notification callback is a much more well-known one and a lot of products are looking at it

seeing who is registered there where does it point does it point to driver memory does it point to non-driver memory if you register there you're a lot more exposed but you can do something interesting if you have windows defender running and you want your rootkit to get cross notifications but not be super out there about it you can let the windows defender register for pros notifications let it receive the data process the data and then register your own driver to the windows defender callback so whenever a new proxy is loaded windows defender will notify its own callback and as a result will notify your driver too and not only do you get to sort of hide and not

and not be super visible because you're not registered to the system's pros notification callback you also get slightly more processed information and kind of letting windows defender do some of the work for you the only downside here is that windows defender uses an internal data structure for its information so you'll need to figure out how this data structure is built and how to parse it properly once you have that you've got everything and that's actually the most interesting callback that windows defender has the third one is the network drivers callback which is only actually called once it's called when the network driver starts because it starts a lot later than the filter drivers then the filter

driver when the network driver starts it registers for this callback object that the filter driver creates to notify the filter driver hey i'm up you can start sending me information and then immediately the filter driver sends it the information about all the processes that were loaded before the fill before the network driver started so it's only actually notified once you don't really get different information than you do with the process with the process callback um you just get a more you get information about more processes but you don't get actually different information that's just an internal synchronization mechanism for them

so the second callback object that seems pretty exciting is the patch card callback it has this very unclear name because it's not going to be called patchguard notification callback and that one is also only called once it's not called to trigger patch card checks it is called to initialize a pointer to the function that performs patch card checks um the kernel notifies this callback once when initialization of the system ends when um phase zero ends up the system wants to make patch cards start running to enable patch card because everything is where it's supposed to be everything is initialized now it wants patch guard to start monitoring so enables patch card and as part of

that it notifies that callback which has one registered function to it the function is in ms-sec-flt and it notifies it with a pointer to the function that will perform random random patch card checks to tell i'm a second faulty this is the function that you need to call to do patch card checks now the interesting thing about this is registering for this callback is not very useful because you're going to register too late um if you somehow manage to have your driver running before phase 0 of uh system initialization ends there are more interesting things you can do than registering for the patch card callback so registering for it is not going to be very useful because it won't be called

by the time your driver is up but looking at the look at the way it's implemented the function in msc flt is implemented in such a way that it initializes its own global with the pointer that it received but never checks if the pointer is if the global is already initialized which means if you notify this callback again you can replace the pointer you would expect it to bug check because hey the pointer is already initialized who's trying to change it and why but it doesn't um so you can notify the callback again and see what happens now my guess is that on newer systems with hyperguard this pointer uh will probably be protected and you will probably

not be able to change it but it might be worth checking might be worth seeing what's there and also if if ms flt is still registered for the function by the time your driver is up probably won't be you can walk the registered callback registration structures to see who's registered receive the pointer to the callback function do some simple binary parsing there it's not a very large function and find the global which has the pointer to the patch card to the patch card function the patch card um monitoring function which is fun for some path for some patch card research uh if you want to do some patch card research um obviously this is this is a very

rough description of callback objects we only kind of scrape the surface here there's a lot more interesting callbacks to look at i would look at security products i would maybe look at some other third-party products that might be using them there is a base for some very interesting research here that can go in a lot of different directions but what doesn't really exist is an easy way to investigate callbacks like public tools or whatever so let me unshare and share uh all that i wrote for this okay so i wrote this simple tool that analyzes a crash dump or takes a crash damper of its own or connects to a kernel debugger and dumps all the callbacks

and registered functions that are in it so we'll let it run and while it runs we can look at things so the first ones on the list are as you'd expect the kernel ones there's enlightenment state set system time now here's the power state one we see it's popular there is stuff registered here that you'd expect to see in a power state callback there's acpi there's hal there's a lot of other drivers here registered for that one now here we start seeing different callbacks we see different here's for example one of the win32k callbacks we can see that it's unnamed when 32k uses anonymous callbacks for its port signaling um here's our few who chose the name

null for the callbacks those are i think my graphic um my graphics drivers [Music] here's the dam driver which always has some interesting stuff in this on this machine i don't have windows defender running so we won't see so we won't see that and just because we have a couple more minutes i'll show how we can also dump callbacks with windbag but only named ones [Music]

so simple we run bank object

and give it a minute because always a bit slow uh internally it just looks for all objects that have that part of the that have slash callback as part of the name now while it's running right

never remember if it's one or two so while we're letting it run uh anyone has any questions

so we don't have any questions yet uh i'm afraid jardin i think uh i think you've blown them away with your technical expertise and um you need some time to process exactly exactly and i can tell you as an ex-ciso one of the great advantages of being a cso is you get this very high level view of everything that's going on security wise in the organization the downside of being an ex of being a cso is this with this very high level view of the organization i didn't understand a word of what you just said but but i do know i do know that there are many many people in the audience who will have found this incredibly useful

um and folks if um if you're still processing not quite ready to ask yarden a question just yet you can still use the slack channels um yard and you'll be on slack available to ask any answer any questions that sent your way please continue the conversation uh afterwards um uh having someone like yardan with such a deep level of technical expertise here is invaluable to you please take full advantage and um really really uh um you know make the most of it uh let me just check time we do have uh a couple of questions now typically they've literally just appeared um so let me just check to see do we have time we've got four minutes to answer

questions so let me yes thank you for getting the photo do you have any interesting structures in the object context that you've seen yeah i'm assuming you mean the callback context there um so yes if i'll sec i'll reshare the output of the tool i wrote i'm going to it's on github i'm going to publish it later um and i'll send the link okay so yeah we have your text out here yeah we have the callback context output here you can use it to see which functions have initialized a callback context and which one's not um like here we see that dom doesn't and one specific one that i know if you're interested in graphics

drivers almost all the graphics and audio drivers do use a callback context so there are probably some interesting stuff there they're all pointing to very large structures and the windows defender driver uses a callback context for all of its callbacks and that points to the internal windows defender global data structure that always has interesting information of all sorts of kinds depends on exactly what you're interested in but you're probably going to find it there okay excellent and i think we do have time for this second question what should you look for when window defender is stuff and admin cannot start it anymore i'm not really sure what the question here is uh when windows defender is stopped

maybe uh an admin i'm not sure

windows defender technical support i don't really do okay and in which case let's go on to this final question uh what is the difference in the image loader callback object and the regular load image notify routine uh there's actually no image loader callback object the image load the load image notify routine is the legacy callback that notifies about newly loaded image as far as i know there is no image loader callback object if you mean the image verification information the difference is that the image verification callback is only for kernel mode images it only notifies about drivers and it supplies information that's actually a public one all the structures are public because it's meant to be used by edr

software and that gives you information for example about the who signed the image uh it gives you it gives you some information that you don't get from the load image notification but they're used for separate purposes because one is only for kernel mode images and image load and the legacy image load notify routine is for both kernel and user mode i hope that answers your question uh yarden thank you very much one of the huge downsides of a virtual event is that you don't get a round of applause at the end so please take mine or your presentation um the um please hang around on the slack channels afterwards folks please um ask questions there yard and you'll

be posting links to the github that you mentioned yes i'll be i'll be posting it there um and on twitter and even after a slack channel is not relevant it's easy to find me feel free to ask questions if you think of any later fantastic do you have a twitter handle or anything that people should know about i do it's you then sofia the same name that's on the slide so very easy to find okay excellent yarden thank you very much