OS hacking: Making some kernel memory read-only after booting finishes

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
well hello friends welcome back to os hacking today we are gonna implement a technique that i heard about in linux so uh apparently they have this thing that they call a read only after init memory and it's a technique where they identify data in the kernel that only needs to be written during the initial boot process and then once the system is up and running um they never want to write to that data again and what you can do with that information is that you can collect all of those things in one place and then once the system is booted you can make all that memory read only and i thought this sounded like a fantastic mechanism that we could also implement right and that's what we're going to do today so basically the the first thing i had in mind for this was our idt which is our um x86 interrupt descriptor table because uh once the system has booted we um we never want to change any of the entries in the idt but it is currently in just it's just uh in the kernel data section right so it's just writable um kernel memory so um that's sort of the first target but then really any global variable for example that we never want to write to again after the system has booted is a good candidate for this kind of thing so um i think what we're going to do is we're going to put these in their own section first off so like ro after init something like that um i'm missing a parent and um actually let's make this a macro read only after init and then yes and then you can just put this in front of anything so we'll do both the descriptor table pointer and the idt itself and probably we can move this up to somewhere higher we probably have various things like in in various places in the kernel like um like here like the pty 0 the first tty pointer once we have actually created t2y0 we never need to write to this pointer again the fact that the pointer is writable you could argue is [Music] a little bit of a security hole like any writable memory could be thought of as a security hole if it doesn't need to be writable so this is just a way that we can tighten things up um so the trick now of course is to um how do we actually make these variables read only after init so now that we have this attribute on them it means that we're going to put them in their own elf section so we'll recompile the kernel and then we'll take a look at the elf sections so if we look at the sections in the kernel we can now see that we have a brand new ro after init prog bits section so this is just a section containing all of the variables that i just marked with that attribute and it has hex 80c bytes in it already so uh i guess like just over two kilobytes of stuff and um by itself this is not super useful but um if we go and look at the linker script of the kernel we could actually put these um things into their own um into their own section so let's see i guess what we'll do is something here like we will align them so that they're four kilobyte aligned because we want it to be aligned on a page boundary um because that means that we can we know that like we're not mixing it with any other data so nothing else that shouldn't become read-only becomes read-only and then here we will simply say aro after init and um let's put a marker here also uh and marker after and these two markers uh we'll be able to access them from c plus so that we know uh where the section begins and ends and then at runtime we can actually go and update the page tables for only this section so um we kind of do some stuff like that already for making data of the kernel non-executable in memory manager somewhere let's see nx somewhere yeah so we have memory manager protect kernel image which we run after after booting up once we have paging and protection everything up and running then we go and we mark all of the remark all of the text and read only data segments as non-writable and we also mark the data bss and heap segments as non-executable so i think we're going to do something similar here but we're going to make [Music] we're going to disable writing to the ro data arrow after init section but we'll do this a bit later so we'll put it in something so protect arrow after init um memory oh good read only yeah and then [Music] and then since we have those markers we can just say start fro after init and um end of after init but of course we also have to um just x turn them here so that so that c plus plus is aware that they exist start of arrow after init and if our afternoon okay and then when do we call this thing um so first off we need to actually um we need to actually call it from somewhere we need to do what am i thinking we need to we need to add it to the memory manager header first of all okay and then we want to be calling this puppy from init and the kernel so when is a good time to do this like should we do this right before we start the scheduler or we can do it even a little bit later it seems to me that it's okay to delay this until uh maybe like until we launch system server because that's like when the kernel init actually kind of ends in the user space and it takes over so i think we'll do that um so we're having it stage two which is the multitasking part of initialization we're still creating devices and stuff here so i think it's okay to let that keep going but then let's see so here is where we launch system server um so maybe here after we load the kernel symbol table then we will say memory manager protect read only after knit memory um like that um note oops oops oops um read only after init um everything marked read only after init becomes non-writable after this point yes okay so let's see if it boots with this change because it is very possible that we will actually try to write to some of these things um but i don't think we should so let's figure out what actually happens if we try to boot okay we have oh we need to we need to be holding some lock in um protect read only after memory sure we can do that um scoped spin lock the smm lock sure fine okay so um that was it was a little too easy i wonder if it actually worked so let's print out the addresses here that we are actually marking as non-writable so the message line um we'll just do something simple like this right like just print out the the address so did we print something around it okay this address right here all right fair enough so i guess if something were to try to write to these things let's see what happens if we try to do that so we have to um i guess we can just go so we marked um tty 0 here as read only after knit so let's try to write to it actually what happens if we just try to set it to null right boom colonel panic crash in ring zero um oh wait wait wait wait wait so i'm just assigning an old putter to it and then de-referencing it so i'm not sure that this actually worked um let's see the message fine let's print out the address let's print out the address of dty0 and see and let's make something that we don't need to use um so we make something like um i don't know foo foo bar um and then and then let's assign to it so like fubar is three okay so everything actually boots um so dty zero is inside of that thing so auto after init i feel like that's not taking effect why is that um oh wait wait we need to actually flush the tlb for that address otherwise this um cpu is not going to care so t.o.b how about we just flush the entire teal we after we're done with this hmm i would have thought we would do that normally i guess it's better to only flush that address so what are the arguments then page directory and virtual address sure so like that okay so now we do get a kernel panic great so we were just missing a tlb flush that is cool and then we have fault at an invalid address sure so it would be super neat if the crash message could even tell us that like hey this is you're writing to ro after init memory right so let's teach the grass crash message thingy about that um so where would that be we want to be at essentially where this message is coming from um page fault um so where are we handle page fault here is where we would be because there's no corresponding region for this so we could do something earlier actually before we call memory manager we can do this directly in the page fault handler so oh we need that range information start of arrow after init so let's put those here and then wait i think why does this code look so weird oh wait it doesn't look weird i just uh assumed this was um i thought this was like this but it's not okay never mind [Music] so before calling mm we will simply check if fault address is within the auto after init section then we can um panic panic um right uh attempt to write uh into arrow read only after init section yeah so that would be a very helpful error message i think um undefined reference to oh wait wait i i need to put them outside of the kernel namespace okay colonel panic sure but we're seeing a crash in ring zero um page fault in ring zero [Music] hmm oh wait wait i have to um take the address of these things yeah that's how we get them okay so finally attempt to write into read-only after init section that's excellent and then it tells you exactly where this happens so at cpu cpp272 we are panicking um oh right yeah but um i guess it would be best if we would show the actual fault location as well so [Music] we should really dump before panicking okay so colonel panic and then here you get to see um what actually happened right so if you take this you need to learn that it's at initcbp306 yes assigning to foobar okay so it works cool okay so that was a little bit messy but we got it working [Music] so basically now let's just get rid of that foo bar thing so now we have a way to decorate any variable in the kernel with this special attribute and then once we call protect read-only after init then these variables will no longer be writable so i think that is very very interesting so let's find something else to put it on like uh scheduler for example we could put this on like a bunch of things here i guess like the finalizer thread once we create the finalizer thread you're never gonna write to that variable just read same thing here we're not gonna overwrite this i'm not gonna overwrite this pointer um the ready queues we're not going to overwrite those i think yeah [Music] um i think because we don't ever write to this and initialize yeah and i'm sure we can find some more things um let's see if it works with these ones we just put no still boots that's cool so if we look in kernel process cpp so we can find some stuff here like the this one is oh um read only after in it the global process table we never need to overwrite that pointer that would be um a problem if somebody did so let's make it let's make all of these read-only afternoon it this is super neato um yeah i'm not gonna like comb through everything right now but we'll take these obvious ones that we just uh that we can just see and i think that's pretty neat i wonder how big that section ended up now so wait where did it even go oh i guess it got merged into the bss because i told the linker to do that um so we could probably make its own thing so something like this yeah so now we can see it as its own section here and it's 8 34 hex um so 100 bytes of stuff um yeah and then as i said we should just we should just like look around and see if we can find more stuff like this and of course this is only um this is only one way of doing it so now we're only doing it to uh globals right like kernel data variables that are just sitting around in the global scope but we could also imagine doing the same thing for um something uh more dynamically allocated so we could you could imagine having a dynamic allocator that gives you memory and then at some point you tell it okay so all that memory that you gave me now make it read only because we have a lot of dynamic dynamically allocated stuff as well so um that would be an interesting expansion of this concept but just as a start i think this is really neat uh and i'm particularly happy that we can protect the interrupt descriptor table this way because um if if you were to get right access to the kernel then you could certainly create a lot of trouble by by writing into the idt and now that is at least right protected so you would have to gain enough control to unwrite protected which would be i guess more difficult so yeah very neat very neat and it doesn't this doesn't feel like it costs us um that much complexity like if if something is ambiguous like whether or not it should be readily after in it we can just let it be writable so we'll see we'll see how this works out but i think this is a very cool mechanism so i'm glad that we are implementing it so let's um commit some stuff so i guess we'll start with just the macro and the linker changes and um the stuff that actually makes it um non-writable okay kernel add a mechanism to mark um variables to make some variables read only after init um after boot something like that [Music] um memory let's say okay so uh you can now use the read only after init macro when declaring a variable and it will and we will put it in a special arrow after init section in the kernel um all the memory pages in that section will then be marked read only or um data in that section remains writable during the boot uh and init process and uh is then is later marked or is that marked read only just before passing control to system server just before launching the system server this is based on a concept from uh based on an idea from the linux kernel yes so i don't know what they call it exactly over there i just uh heard about it in a talk by case cook but um this is sort of my understanding of the concept so um i hope i i hope i understood it correctly but but even either way like this seems like a very reasonable thing to be doing so colonel um mark a bunch of things or you know what let's let's make this bisect friendly oh and we should have that panic thing here colonel uh print a helpful panic message if uh crashing due to read only after init yes this part should be in that commit as well i realized okay mark the x86 idt uh let's read only after init we never um need to modify the interrupt descriptor table after init after um finishing initialization so let's make it an error to do so indeed [Music] um mark a a handful of things in process cpp read only after init yeah we'll just do this this will be uh unexciting uh messages but wait why didn't i combine those [Music] um okay and thread cpp oops i'm just doing it this way so that um if we're bisecting this then we'll we'll find them quickly um yeah tvp is read only after and sure i'm surprised that we didn't have more stuff here that could be read only after a net i mean there probably is a whole bunch of things it's just um also a lot of stuff that could make use of this pattern but would need to be um split apart a little bit um like per cpu data like once we have booted all the cpus for example then um so we could mark some of their data as read only after in it that's epic yeah so like you could do like that right anyway i'm not going to sit here and go through everything so i think this was sufficient for what we wanted to do here and we'll continue to to do more more stuff is readily after knit but it's a cool start i think and see that we can still browse the web still works that's good um yeah so i think this will be the end of the video so if you made it this far then i thank you for watching for hanging out and i hope that you saw something interesting here i think uh this was a pretty neat little mechanism and it turned out pretty good so just yet another thing that we do to make the system a little more robust a little more um resistant to attacks um because um that just never ends like the game goes on so anyway thanks for checking out and uh i will see you next time bye
Info
Channel: Andreas Kling
Views: 4,502
Rating: undefined out of 5
Keywords: os hacking, os development, c++, c++ development, operating system development, operating system programming, c++ programming, serenity os, serenity, serenity operating system, hobby os, hobby operating system, programming stream, os programming, c++ kernel, osdev, alternative operating systems, operating systems, linux, unix, bsd, infosec, exploitation, browser exploit, pwn, security, hacking
Id: X6s3XBWe2XU
Channel Id: undefined
Length: 34min 23sec (2063 seconds)
Published: Sun Feb 14 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.