
[Music] start again I used to play CTF I'm busy now I used to work at Intel on system software fuzzing until they paid me to leave the company uh so I'm not here representing any employers which is kind of fun um I do hobby hacking I do side projects all that good stuff and uh I'm not a Windows person so uh until like last week uh when I when I started working on this stuff I had like touched no windows internals or anything like that so it was entirely new to me which was kind of fun um and uh you can find me on most websites at Nova facing if you're interested in that um all right
so I was working on a fuzzer that's what I was here last year to talk about it fuzzes ufi it fuzzes Windows kernel it fuzzes Linux kernel um and uh it's going great I was using it I was finding some bugs and stuff um and uh I gave it to some some engineers and they were able to find some bugs and stuff too especially like on the UEFI and Linux kernel side uh which was great you know built on a simulator so decently fast we're talking like five executions per second that's actually quite good um unfortunately and uh everybody was everybody was pretty happy with it um except when it came time for me to use
it on a bunch of projects when I said ooh I kind of just want to work on the fuzzer I don't really want to actually go try and find any bugs that's not really my thing right now um and so I you know tried to continue working on the foser uh so what I did was I just handed handed the tools to all of the developers and said you guys have your your you know Windows kernel applications and drivers and stuff uh go ahead and find your own bugs and they said uh we don't really like that uh because that's how uh I was given everybody information on coverage for their Windows kernel drivers now like
technically you can get everything you need out of that it's got program counters and uh it tells you what the program counter is at every branch and if you look really hard you can say all right this one comes before this one so we must have this control flow uh but nobody really likes doing that especially not when you're fuzzing with like a 100 harnesses on a super complicated driver it's just not very fun it turns out that there's a good way to do that and it's a coverage visualization so if you've ever done like uh development on Windows or Linux and run a program and needed to do like performance testing on it you've
probably seen something like that graph on the right that's a flame graph it just tells you like this function executes for x% of your runtime of your program so uh you know if your decoding function or your sorting function is running in like o of n cubed time maybe you should make that a little more efficient uh cuz you were really lazy when you wrote it the first time this one on the left is a little uglier so that's actually the output of uh clangs coverage metrics and it tells you how many times each branch executed uh but that's kind of ugly it's not actually in my opinion like it's a level above the Json but it's not like three levels
above the Json to where most people would want to look at it so we kind of want to aim for something more like the one on the right a little bit more gooey based a little bit easier for people who are used to working in visual studio and things like that to use um so we have a few options we can use lvms uh profile data profile raw whatever the format is uh that you're using and that's submitted very easily just run a couple of uh you compile with a specific flag you uh run your program it outputs a couple of files and uh then you run this lvm coverage show which will give you
that kind of nice output on the right it tells you how many times each line of code executed it'll highlight ones in red if they didn't execute it all so that's the kind of information we're after uh but we're trying to support everybody with this so uh not everybody is super comfortable in the command line not everybody uh is able to use lvm for their software especially on Windows that's a big thing A lot of people are restricted to using Microsoft's compilers so um this isn't a perfect solution but it's getting close um one problem with this as well if we're trying to build this format into tools is that this format has no specification
uh you have to just go into the lvm code base and read it all and say all right what's this weird binary format that tells me what the coverage is it's not like incredibly complicated it's like numbers and lines and stuff uh but it's not super maintainable if every time lvm changes it you need to go and look at the diff of a bunch of source code instead of just looking at a change log on a document so that's not super great um but it does have all the information we want so uh basically what we're after is number of times each line executes whether a line executed at all how many times each branch was taken or not taken
in our program that includes like loops and if statements and stuff like that and then uh lvm has this interesting one which is modified condition decision coverage which tells you um for all of the like separate predicates in an if statement or a while condition uh how many of those actually influenced the decision of the branch and it turns out this is not that useful for development purposes but it's very useful for security testing purposes because you can tell um what variables in the program are not getting flipped or are getting flipped to affect your control flow so if you can say all right this particular variable is never getting modified um to influence the direction
of this Branch maybe we need to add some fuzzing or a test case that does modify it and see what happens um so that one's super useful uh but not a whole lot of uh documentation about it so uh there's another tool for the GCC Suite of stuff and obviously this won't really help us on Windows because nobody serious unfortunately is using like msys 2 for their development at work um but they have a couple things that are a little bit nicer than lvm system um very similar stuff almost exactly the same compile command to add the instrumentation that you need to Output this data you run the program again it produces a couple of uh binary files and
then you run this gcov format which actually in this case produces a readable file but it's basically just the text of the code output so we're slightly better than lvm in that in that respect we know what the format looks like and it's probably not going to change just code with a couple of annotations on it um but we're still not uh quite to a format that we can really use an output so uh it turns out there's a tool that actually runs after gcov uh called elov I don't know what the L stands for somebody knows feel free to raise your hand um but basically what this does is it's mostly intended for translating the gcov data to HTML and uh
we're getting back to our key objective of reaching a guy where uh basically will translate this to HTML and then you can just view it as a web page and you get a pretty output of all of your code with everything highlighted nobody has to run anything in a terminal it's all platform independent you just send somebody something they open in Chrome uh so super nice you just have a a very simple text format that's over here on the right and then uh down at the bottom is the annotations of what all of those records mean uh but it tells us almost all of the same stuff as the lvm and uh GCC coverage data tells us all of the
branches we hit all of the lines we hit how many times we took each branch uh how many functions have been covered how many functions are not covered um the only thing it's missing is that mcdc uh stat which theoretically you could add to this but it's not in the in the specification um and theoretically you can also calculate from this if you work really hard so I basically said all right that'll do we'll use that um and our next task is to integrate into our our fuzzing tool well it's executing and have it be able to observe what's happening when the program runs and output this format so that people can view this instead of anything in their
terminal um so same example as before but this is a a web page that you can bundle up and send to somebody uh tells you all of the line coverage function coverage not pictured here is like for each directory it'll give you a list of functions as well and then that's recursive so if you have a codebase super huge codebase you can navigate through it easily figure out what your uh spots all of your fuzzers are missing are figure out what spots they're hitting uh maybe variables that aren't covered and so forth so um shop this around to a few a few Windows developers they said this looks great please do it so um that sounds good um but we kind of
need something to work with this format the the official codebase for it is written in Pearl which I personally don't really like or understand or know in any way um so so I wrote a little library for rust which I do like um and is also the language the fuzzer I was working with was written in and uh we were good to go that took like four hours it's such a simple text format that it uh was pretty low effort so we've got a library to work with elov files in memory and then write them out to disk and then translate them to HTML so uh basically what the fuzzer knows is the program counter of every
Edge and uh in the in the program when you're executing if you hit a a conditional statement you have an edge and uh one Edge goes to usually the next instruction and one Edge goes somewhere else so if you have an if statement it'll have the the body of the if statement or you'll jump down to below the body of that if statement um it kind of depends per Assembly Language but in general that's what the fuzzer knows and that's basically it it just gets that uh program counter stream and also the information of whether this this program counter is an edge so to go from program counter to a source line of code in this
obviously once we've compiled the program and stuff we don't have Source lines necessarily um so we need to find an offset which is the program counter minus the base of the object loaded in memory so that'll be your Elf file or your PE file whatever the case is and we use debug info uh either from the program or separately which is usually the case on Microsoft platforms um to figure out what that uh Base address and offset is so we know uh what address we're at in the code debug info store is usually just a mapping of address to symbol or like base of address and size usually is what it actually is and then you can figure
out what the offset is so the way we get that on a running Windows system that's running in a virtual machine is via virtual machine introspection basically we reach in using whatever mechanism our hypervisor or simulator in this case gives us and we ask for some memory contents or the value of a register and then we uh use that a bunch of times to figure out where the uh internal Windows structures are and then we use that to figure out where all of the objects that we have the debug info for but we don't have their Base address are located so let's talk about how to do that so in the simulator that I was
working with and in most like hypervisors or system uh emulators like Kimu uh you'll be able to read virtual memory you'll be able to read sometimes but not always physical memory um because that physical memory is simulated or emulated and you'll be able to usually read registers now uh with Kimu sometimes you can't read all registers uh things like virtual machine extensions and all of the registers for that are usually not emulated they're um passed down to the host operating system through KVM or something like that so you don't necessarily have the ability to read all registers but for our purpose we'll just assume that you can um and on the simulator this fuzzer I
was working on was built on we could also read msrs or model specific registers so on Intel and AMD platforms there's like thousands of these model specific registers and they hold super specific stuff like uh since the last time you read this register how many uh system management mode interrupts happened so things that you don't frequently need access to but the system has some value in storing that data and sometimes Intel sees value in allowing you to know what that register's value means um so that's basically model specific registers some of them are super commonly used uh so we'll talk about one in particular that's used for this so we kind of have a road map uh
for what we need to do using this capability first we need to find the kernel because uh if we don't know where the kernel is we're going to have a really hard time finding all of its structures and we're mostly worried about kernel drivers for this and kind of less so user space processes um user space processes coverage is kind of already solved like we looked at with those compile flags and msvc has its own way to do that even though it's a little bit less uh easy to to access so first we need to find the kernel um and I did not know how to do this all I knew was how to read some
memory same thing as the simulator knew so uh I started by looking at some existing tools a lot of people here probably maybe show of hands know about volatility okay decent number so uh volatility is a memory forensics tool so if you capture an image of the Ram from a running system you can use volatility to basically ask questions about it like uh was some malware active in this system when I dumped the ram um or what were all of the command line uh programs that were running and what were their arguments when I dump the ram of this system so to do that it does a very similar thing to what we need to do it
SC through memory it finds the kernel it analyzes all of the structures of the kernel to find all of the information that it's able to give you um for the command line program example it'll scan through memory until it finds the list of processes that's currently loaded it'll Traverse through those processes and each process in Windows has a um member of its uh internal structure that says here was the command line arguments to this program so just read those dump it out and now volatility has told you what the command line AR are so um I looked there first because I took a memory forensics class in college and I knew about it and uh basically the way
volatility finds the kernel is by scanning the entire dump of RAM for just like a pretty simple signature of what the kernel is supposed to look like and for my purposes this was not good because the simulator I was working on uh took like two or three minutes to scan through all of memory and making it worse the simulator let you put an arbitrary amount of RAM into the system so like you could have a petabyte of ram in your virtual in your virtual simulated system which would take a very very I didn't try this uh but like 16 gigabytes took 2 minutes so you can kind of do the math on how long a petabyte
would probably take um but I needed it to be able to work for any amount of ram so this was not going to be good um but the the thing I learned from that is kind of what the signature of the kernel looks like and how to tell like if you have an address uh is this address the base of the kernel we'll talk a little bit about that later but the next thing I checked was lib VMI which was uh written by one of my co-workers and friends at Intel and uh basically this does the same thing uh and I was a little bit miffed at this because lib VMI is supposed to be used
online it's on a running system but limi is designed to be used in a hypervisor like Zen or KVM where reading memory is actually pretty fast there's no emulation behind the memory it's just go look at a physical address on the system you're already on that's a pretty quick operation and so they can kind of get away with doing that um so that still didn't work but I compared the implementations of like checking whether a certain address is the kernel between limi and volatility and I said all right these are pretty similar so this must be the right way to do it so uh again a little bit of information gained even though uh this didn't really work for my
purposes and then there's a third way to do this which uh everybody online if you search this problem will say suggest that you use which is to just call nquery system information which is a function in ntdll on Windows which will just give you the Base address of the kernel for free which uh usually isn't something that you get access to on Linux so I was a little surprised about that um there's some permission stuff involved you know you can't do this from every process you need uh certain privileges but uh that's cool the only problem is since I am the simulator kind of in this situation I'm not actually allowed to run code on the system that's
being simulated so this still didn't work but another piece of information gained a you can do this which is kind of crazy um and B this uh this function will give you a lot more than just the Base address of the kernel it'll give you like process offsets um process information where certain things are located um and so kind of just learning from all of the options this is like a very large call it has a lot of parameters um and so just kind of learning the vocab of what all of the internal structures of windows were from the documentation for this function was also super helpful for just Googling uh stuff to complete the rest of this project
so so I didn't know anything about Windows I learned a little bit about like what the kernel maybe looks like I didn't really know what a PE file looked like so like learning the learning the signatures for that was good um so where do we go next I've kind of asked the security Community by reading through their GitHub HS uh I've kind of asked the security Community by messaging my co-workers but it turned out that like he had some copyright thing on lib VMI so he couldn't really tell me much about it at work because he didn't want to lose his copyright so lots of lots of silly roadblocks you know um it turns out that game hackers love the windows
kernel um and if you've ever played Call of Duty you probably know this because there are just a million hackers everywhere and not of course not all of them are Colonel hackers most of them just downloaded stuff that they paid like $35 on for online to cheat in video games which I still don't understand uh but the the people who made those exploits know a lot about the kernel because they need to circumvent anti-che um anti-che for those of you who don't know runs with like the maximum system permissions usually on Windows it can read everything you're doing it can write to any process um it can download every picture you posted of your dog
from online if it wanted to I don't know uh who decided it was a good idea to trust game developers who you know very frequently have bugs where you like walk through walls or uh roll through the ground in Mario 64 if you've watched the half a press video um but uh yeah I guess we do that and it turns out that people who want to circumvent those anti-che things uh know a lot about the kernel because those uh those kernel level privileges that the anti-che has need to be targeted by the game hackers so they can get around them and so game hackers know a lot about like internals of windows all of the structures and
stuff so there's this website unknowncheats.me uh where people will just post full exploit code for like various versions of windows it's pretty fantastic and they usually uh either won't attach any comments to it or all of the comments will need to be Google translated but you can get some good information out of this stuff um so a like all of the structs that they use I didn't know any of these but they they'll Define all of the structs in their code and the reason they do this is because these structs are not uh the in any header file that Microsoft publishes sometimes they're in uh pdb files that Microsoft publishes which is debug info for Windows um and
there's nobody really knows whether that's intentional or not but some of these struct structs have been unintentionally published some of them have been reverse engineered and just kind of understood over time um but those structs kind of coming from the the cheats websites were super duper useful for understanding like where all of the pointers that I needed to get to the process list and the and the kernel module list which are really the two things we're after uh were located so uh there's also a couple other good places that I found to get info so this guy Jeff Chapel I have no idea who he is I didn't read the bio I just went straight
for the colel struck documentation part of his website um but it's like probably some of the best documentation I've ever seen so even if you don't care about like uh Windows structures if you just want to learn about how to write documentation for structures for program I highly recommend checking out this website it's like mind-blowingly good and he claims that he's never worked for Microsoft he doesn't know anything about these internal structs other than just like public knowledge and stuff and he just works from that but uh basically he describes every field in detail its entire history of what it's ever done uh for just about every Windows kernel struct that I needed to use for this so
um thank you Jeff if you're in the room and then finally uh the one thing Jeff doesn't do is publish like a machine readable specification of all of these structures across time um I didn't really care about like working for Windows 7 or Windows XP or anything like that but I really wanted to work for every version of Windows 10 and 11 uh just because we have lots of machines running those at the moment and uh usually if you're testing kernel modules you're probably on like the latest couple years of releases um although I realized the other day Windows 10 was released in like 2015 so that's almost a decade which is kind of crazy um so
there's this project virgilia project I forget where they got the name but it is on the homepage because I remember reading about it and basically what they do is they've taken all of those accidentally or on purpose we don't know published uh pdb files from Microsoft and they've extracted all of the structure definitions from those ingested them into yaml which was an interesting choice I don't know why they chose it but they're all in yaml um and post it on their website which is super helpful because uh the first thing I tried to do before I discovered this this website was download an ISO for every version of Windows ever spoiler alert some of them are very hard to find
um unpack all of them which is like 30 terabytes I have that much storage but I had to clear out some Linux isos to make room for the windows isos um and then extract all of the pdbs Myself by downloading them and that's actually where I got stuck because that is a lot of uh bandwidth and it was going to take like two months to download all of it which was not really cool with me cuz I started writing this talk like 3 weeks ago um so anyway I downloaded the yl um so the first thing I had to do was translate this yaml I did not want to work with it I don't really like yaml
personally I don't know how everybody in the room feels if you're a kubernetes person you're probably either like really cool with it or really not cool with it depending on which like side of the kubernetes spectrum you're on um but I like writing rust as I mentioned earlier and uh right now they're in this weird like C definition inl file so the first thing I did was translate all of that to C from the rest because it was in C it was just also in yaml um and then of course if you try and compile that c it's not going to work because you have a bunch of structs defined later which are part of structs defined
earlier um and it turns out there's a very simple way to solve that you just topologically sort all of the types which actually is where the yaml came in handy and I suspect Maybe why they used it um so I have to give them a little bit of props even though I'm still confused uh after that you've got C that compiles with maybe like one or two manual tweaks because there's some typos but uh I was actually pleasantly surprised that I was able to just compile this C code like with GCC on Linux uh from like automatically extracted Windows struct it was it's pretty cool um and then of course uh because rust is awesome we just have a
tool that you can run that'll translate that all to rust you don't need to do any manual work so that's kind of what that looks like here's the yaml on the left very ugly here's the C in the middle okay there's the rest on the right side which is beautiful all right so I mentioned that I I needed every version of Windows earlier and the reason that I do is because um every time you have a new version of Windows like for whatever reason and I'm not sure why they did this uh all of these fields get shifted resized reordered um and this will be like between build versions so like Windows uh 2121 has like three or four builds and
all of them the structures that you need to do this to are like completely different um and so in order to resolve that in the in the simulator you need to be able to figure out what version of Windows you're looking at so that you know what version of the struct you're looking at so that you know what the heck you're looking at so uh first thing we need to do is check the windows version and thankfully this is very easy um and Microsoft I really hope never changes it so there's a structure and this one doesn't change that much which is good like fields at the bottom of it change but the fields we care about have
never moved which is great and I learned that from Jeff Chapel's website so one of the reasons you should check it out uh there's this stru called K user shared data and it's always mapped at the exact same virtual address in every address space while Windows is running so any process or in kernel mode you read this address you're getting K user shared data and from that you can get the major version which is right now always 10 the minor version which is right now always zero and the build number which is the thing you actually care about um and that'll tell you which version of Windows 10 or 11 uh major version is 10 for 10 and 11 which is a
little confusing um that you're looking at and I figured that out by just running a bunch of Windows systems and looking at this struct and all of them because people said stuff online uh but I I've been burned before by just reading unknown sheets. me and running code from it so I just wanted to make sure so we read that struct we get the we get the build numbers and now we know what version of every other struct we're going to be looking at so we build that into our system next thing we need to do now that we know like what the kernel structures are and what their fields are and how big they are is we need to
actually find the kernel base and so to do this I think I already mentioned we don't want to scan through all of memory we could but it'll take a very long time and it might take a very very long time um so what we want to do instead is find some code pointer into the windows kernel and we want to just scan backwards in memory and I have a diagram of this we want to just scan backwards in memory until we find the base of the kernel using those uh signatures that I extracted from Liv VMI and volatility 3 and didn't write myself until I realized that I needed a couple more checks uh because oh it's not on this slide sorry
we'll get there soon all right so there's two ways we can do this one is the interrupt descriptor table so on all and this is x86 specific so I apologize to the arm enthusiasts in the room but I haven't looked at arm Windows yet um but uh there's this thing called the interrupt descriptor table which tells the processor anytime an interrupt happens you go to this table and depending on what the interrupt was there's a bunch of function pointers for you to run depending on what the interrupt is so uh General protection fault you run this uh this particular function which handles that or divide by zero error you run this other function and you can set
these all to the same function which will effectively give you like a single entry point for handling interrupts or you can set them all to different functions so that you just uh handle them one by one depending on what that interrupt is now uh this interrupt descriptor table as of like one of the windows 10 builds that I'm forgetting it's like 19 something um Windows has introduced a feature called kernel virtual address shadow kavas um and basically what that does is they're trying to move any pointers that used to be into the code section of the windows kernel into a different section in memory which is um sometimes adjacent to the kernel and sometimes not adjacent to
the kernel um although as we'll see it kind of doesn't matter um for our purposes but for like finding Rob gadgets and stuff it does matter and that uh new code section is just called kvas code and that's um like I said either located adjacent to the kernel or kind of near it but separated by some some memory range so the second way uh which actually doesn't work anymore and that's why we're not going to use it but it's it's worth mentioning is the MSR i32 kernel GS base always points to a specific structure called the kpcr and that kpcr contains a uh bigger structure it's just tacked onto the end of it actually called the kpr RCB kernel
processor control block and that contains a pointer to the current kernel thread and so of course if you can access the kernel thread you would think there's probably like a reference the entry point of whatever that kernel thread program is um and that used to be true and is no longer true there's now no code pointers from that K thread into actual code of the kernel so you have some like uh data from the kernel but you don't have a code pointer anymore which is unfortunate cuz that's actually a really nice way to do it um because it'll just give you a direct pointer into a known place in the kernel um and now we just have to find some known
place in some memory that's not the kernel and try and find the kernel based on it luckily for us it turns out that's uh not actually that hard and this is kind of what either of those processes look like if we uh take the IDT R which is the interrupt descriptor table register um the base field of that register basically just straight up points to the interrupt descriptor table wherever it is in memory so that's very easy to find um that'll work un like K any simulator essentially has to have that ID and then the second method using the i32 kernel GS Bas MSR um that'll point to the kpcr and then the IDT base
field of that structure points to the same place as the IDR so if you were in a situation where you could only read msrs but you couldn't read the IDR you could at least still find the IDT from that first method or sorry from the from the second method if you could only read msrs which I don't think anybody else will ever end up in that scenario but if you do now you know how to do this all right so once we found the uh first entry of the interrupt descriptor table that's going to be divide by zero actually is the first entry of that um so we'll take the code pointer to the Handler for that which is a a kernel
system call called Ki divide error fault and this used to be in the kernel it would just call straight into the kernel code um at that at that location now all uh handlers externally entry points into the kernel are in this kavas code section which has like a swap GS instruction to like change the change the page mappings and then it'll jump to the actual Handler in the kernel so unfortunately that means we' have to like disassemble that Handler to figure out uh what it's what its code pattern is that it's jumping to in the kernel we would need to use like we'd need to single single step over that swap GS instruction to get a new page mapping so
that we could translate the addresses correctly to find the kernel it's just kind of a mess um and so what we do instead is we just walk backwards in memory because we know there's going to be some memory between kis code and the kernel code but it's not going to be massive and and so we can pay a small performance penalty for a lot of simplification of this process by just doing what's called a scan back and uh the Unknown cheats people love this there's like a hundred different code examples of how to do this and they all do it the same way and seemingly don't realize that 100 other people have posted the same code um so on versions
below 19,000 pages are 4 kilabytes aligned um and on above 19,000 they're 2 megabytes aligned which I think is large page PES but not huge Pages if I'm remembering correctly um so we start we have a start that's our KS code pointer we have a step and now we need an end so this is kind of the tricky part is making sure that we'll go far enough with our scan but we're not going to like for some reason if we've picked a bad code address we're not going to like go all the way to the zero address because then our system would walk backwards for like 20 minutes and then it would say all right we reached null so clearly is an
error um we we probably want to fail a little faster than that so our stop address we basically just um truncate our our start address to like the nearest gigabyte and assume that'll probably be good enough and so far I haven't seen it fail on any amount of ram so I I think that does work so yep once the once the page we look at is the kernel base then we're good we found the kernel so uh that's really the hard part but how do we know oh yeah here's the code for that um probably nobody cares about this and I it was like 11: p.m. when I put this in the in the slide deck but it's pretty simple so
we just uh take a build number for the kernel and then decide get the IDT base scan backwards and check if page is Kernel and that's kind of the part that I took out of uh volatility and lib VMI so we basically just do a bunch of checks and if any of them fail then it's not the kernel and if we get to the bottom then we're good um so first we make sure it's a it's a PE executable that's kind of the first check and then if it is a PE executable we make sure it's also an NT executable because it turns out that uh Microsoft for one reason or another I don't know
if this is on purpose or not has put a bunch of like magic bites for PE executables just kind of littered through the kernel at various places and so you'll run across something and it'll say MZ and you say all right great I found the kernel and then you try and use that and everything is total garbage um because that MZ bite is not actually the kernel so you need to do a bunch more checks to make sure that you're not running across like uh either a fake header or an unintentionally fake header I'm not sure which is the case so once we know this is a a PE we know it's a NT version PE so pe32 plus
uh then we make sure that it has an optional header um we make sure the export header of the optional header exists and it has like valid side that's a good sanity check um and that's the point where there's no longer any fake stuff so there there are some fake structures that will satisfy the magic check for the Dos header the NT header the optional header but there aren't any that will satisfy that export header check so once I got to that point um I didn't see any false positives pop up which I thought was kind of interesting that seems like a lot of steps to set stuff up if you're not doing something intentionally but I'm making no
accusations uh so so after that then we find the name field of that export directory which just tells other programs like what this program is um and we just make sure it's called OS kernel. exe and if that's the case then you found the kernel or you can be pretty sure that you have um unless somebody is really messing with you we've got our kernel Base address now we don't know anything about what's in that kernel yet uh we just know what the base is and we know what the version number is uh but we don't know where the structures are and we don't know what they're called so we need to get some debug info both for the colonel and for
all of the uh applications and drivers running on the system so we need to grab pdbs and luckily Microsoft very helpfully has a what's called a symbol server so if you know some specific information about either a dll a driver or an executable you can just download if Microsoft has it and is willing to give it to you you can just download the debug info for that file super helpful helps a lot for debugging and really helps us out for this because we couldn't do it without that information uh so kind of similar processes before we read the PE structure um and pees have something called a debug directory and some of those debug directories are
in this code view 7.0 format which apparently is pdb um Microsoft actually has an open- Source pdb specification repo on GitHub which is archived and not being worked on uh so maybe download that before it goes away I guess I don't know what the archive means um then once we find that uh code view 7.0 information there is a header for for that uh specification that says like here's the file signature for code View and then here's a guid or a globally unique identifier which is just a string of numbers essentially um which uniquely identifies that file and then there's what's called a file age I don't know what this means it's just a zero or a
one so I guess it's like not old or it is um and then there's a file name so if we have all of that information we can just download the pdb information for that executable and then we can also just download a copy of the executable too which is useful because it's actually not uh super efficient to copy executables out of a simulation and you don't always necessarily want to do that it's sometimes better to just download the fresh copy of the executable from the symbol server as well um and we can do that by knowing the ex file name which for for the kernel we actually don't know uh until we check that um
export header because for different uh distributions of Windows like iot core and arm uh there can actually be different kernel executable names um that you might run into so you actually do need to check this you can't just assume it's in TOS kernel. exe all right so once we know that information we can download the pdb file which has all of our debug information and we can download the executable fresh from the symbol server and this is what that process looks like all right so we've got executable image we've got some headers those headers have information um this will probably be easier looked at like after the talk if you're really curious about it hopefully it'll be visible on
YouTube but the things highlighted in red that time date stamp size of image uh for the file header and then the signature age and file name for that uh code view signature are the only things we need in order to get the debug information for our kernel and that'll actually be true of all of the drivers and the uh executables running on the system we just do this exact same process once we find the bases of all of those PE images loaded in memory uh so we didn't actually need to do any extra work to find uh the pdbs for all of those things either all right so next thing we do once we have the once we have the kernel
uh p PB we can use the Base address that we already found for the kernel and the pdb info to uh figure out where in memory various structures in the kernel are so one of those is the list of processes very useful and the other one is the list of Kernel modules also very useful those are actually the only uh symbols that I needed to use but if you wanted to determine more stuff about the kernel from this like uh I was only interested in getting base addresses for things for coverage purposes but if you wanted to like analyze a system for malware uh and maybe like find kernel modules which are not in the list and figure out
maybe if you have a rootkit running on your system uh you might want to use more symbols in the kernel to figure out extra information beyond what I've done so uh a little bit about paging basically when a process is running it's called paged in and everything in that process is mapped and then when you switch to another process uh the stuff in that process gets mapped and most but not necessarily all of the stuff from the first process will be paged out so if you try and access its memory location there'll be either nothing there or it'll be something different because your current process has something else mapped um so because that's the case um there's some
information uh specifically like the file names for processes that are paged out which are not actually mapped um for for those paged out processes and so it's kind of hard to build like a full list of of the file names and all of the information um and grab all of the uh stuff that we need to download pdbs for all of the paged out processes so what I did is basically just said all right let's just grab it for the current page in process and then every time there's a change in the page Maps uh which is a right to control register 3 on x86 um we'll just refresh and we'll just grab the new current process um unless it's
the same as the old one in which case we can kind of optimize and not worry about it all right so to get a processes information basically what we do is we uh grab the and this is for the current process so we'll grab that kpcr kernel processor control structure uh address like we talked about earlier so we actually need this MSR whether we uh use the initial IDT method to find the kernel pointer or not um there's a couple other options for grabbing this address we could also use a symbol in the kernel which is the loed processor list for some reason I had some trouble using the in order process links from the konel to
find all of the processes wasn't able to like replicate it with other VMI systems on older windows so I don't know if something has changed in new windows and that's not an option anymore but uh I didn't explore it any further so you know you can go home and double check my work um but yeah so basically we read this MSR grabb the kpcr and therefore the kpcb RCB grabb the uh current K thread which is the kernel thread uh information for a process that's running that K thread has a pointer to What's called the E process which is like all the process information like here's its priority here's its process identifier here's its name here's um importantly
all of the modules that are loaded in that process so there's a linked list for each uh process that has all of the modules loaded into it and that's like if you have a exe and it loads 50 dlls all 50 of those dlls will be in that list list so that's this one I actually think the graph is a little easier to look at so we go from register to kpcr to kthread to eess um we need to grab that uh field directory table base because that's actually the base of the page table for the current process so we need that actually to do the address translations um so once we have that Dess trans address translation
information we can grab all of the rest of these uh Fields using that address translation because otherwise we would just get like garbage because it's not paged in or mapped um so we grab the file name we grab What's called the PEB and then over there on the right you can see that Arrow points back to itself that's just a link list of all of the modules for the process that gives us our file name our like P pdb goid information that we need to download the debug info for that module um and we need to do that for every process once we see it um in order to have like full coverage of it because very frequently
the actual executable is going to be pretty small and most of your functionality will be in dlls all right so once we've done that we'll have like the dll Base address for all of the modules loaded in the system we'll have the entry point we'll have the image size and from all of this uh we can sorry I'm kind of running out of time so I'm going a little faster um from all of this we can build lookup trees so basically for all of our modules we take the debug info and we get all of our symbols from all of those symbols we have a like Base address of that symbol and a size and so from that
we can construct a range which we can use whenever we have a program counter during our execution remember that's all our fuzzer really knows is program counters we have that program counter we can figure out uh what range we're in of addresses which will allow us to look up the symbol and from that symbol uh in pdbs each symbol has a list of lines um and those lines also have base and size and we can look up our uh address in those ranges of lines as well so just using our program counter we can now say what line of code are we on uh what function are we in and that's actually all of the information that we need to
construct that elov coverage uh which will give us that really nice uh displayed graph uh or listing of code for our users um so I used interval for this which is a nice data structure for range lookups it's really great for a lot of like reverse engineering program analysis type stuff where you're doing similar like PC to something lookups uh because they're almost always ranges and basically what this is It's Like A specialized B tree where you have uh like link list lookups um and you get really efficient queries uh which is nice because querying is most of what we're going to be doing we only build the tree once every cr3 right but we
have uh a query every like instruction that we execute so it's a ton of queries so log of n is really good where um n is just the number of intervals which is not actually that big you'll have like maybe a couple thousand lines that you need to look up over all right so from there we correlate the coverage back from the PC all we need to do is subtract the PC from The Base address that we know we're looking at um or we can do what I actually did which is just store the lookups as PCS instead of uh instead of offsets from base addresses of program objects and that way you can just directly go all right PC what's the
range I'm in give me the line and symbol coverage we build that into our uh fuzzer every time the the system sees a right to cr3 we re reload those ranges and uh then we write that out after fuzzing is done to elov and we have HTML coverage of all of our uh kernel code that we've been fuzzing so Google search kind of okay uh you really need to know your Search terms and so it's really nice to find a forum or a Discord or somewhere where people can tell you like here's some actual information instead of AI generated garbage and then once you have that you can actually find the stuff that you're looking for um GitHub search
which I kind of use to search through like volatility and live VMI libraries is like currently amazing um make sure you log in and then if you learn how to use the search terms on GitHub search you'll be able to find like almost anything instantly um ask game hackers stuff they love to talk about the cool hacks that they've done and they'll just walk you through everything even if they didn't post code with comments and uh yeah be lazy don't reverse engineer anything I didn't reverse engineer windows at all for this project and uh I don't really think anybody should do it because so much information about Windows is public that unless you're like really looking for exploits I don't
think there's any reason to try so yep that's it questions
I think we have like 10 minutes right oh all right well uh I have a flight to catch but if you want to follow me on stuff you can send me messages later and I'm happy to help with any of this uh related things but otherwise thank you oh [Music]
[Music]