 Okay, guess we'll go ahead and get started. I am Julian Grisard. I'm a graduate student at Georgia Tech and studying computer security and today I'm going to give you a talk on search code recovery from current level root kit installations. And this is on Linux based systems. So first off the latest slides tools and you know our spine architecture work is available online. The links to this is on your CD and and So definitely download the latest mental eye changes. All right, so first off some background on root kits. What does a root kit do? So a root kit is a tool kit that a attacker will use to retain access to your system and hide activity that he's doing on your system. A root kit doesn't give you access to a system if you don't have prior access. You have to exploit the system by some known vulnerability or something you figured out or social engineering or whatever. And then you will a attacker will usually install a root kit in order to come back into the system at a later point in case the password changes or as vulnerabilities patched. So how many people have personally had experience with a root kit installed on a system they administered. We got some people, wow, we got lots of people out there. Interesting. And so what what is the recommended thing to do when you get a root kit installed on your system? That's right, format and reinstall. So I'm actually going against that grain, you know, I'm saying okay, maybe there's more efficient ways to recover from a root kit. I mean, what if you got a server that every minute of downtime costs millions of dollars? I mean, how can you format and reinstall that system? So we're gonna look at different ways to recover from a root kit. So first off, I want to give you guys some background on operating systems because what a root kit does is it replaces parts of your operating system. So it's important to understand how an operating system works in order to understand the design of root kits. So this is a monolithic operating system. That's just a fancy term for this particular architecture. This is Linux looks like this. Windows, all the windows look like this. Most systems look like this. So you've got two parts to an operating system. You've got the kernel space and this is the code that runs right on top of your hardware, controls all your devices, schedules your processes, does your file access, network stack, these kinds of things. And you have user space. User space is where the programs that you really care about run. So P1 could be your word processor, P2 could be a LS, Bash command, and so on and so forth. Now I want to give you a contrasted operating system design that I'll come back to you later. But I just want to give you a preview and compare and contrast to current OSes. This is the micro kernel operating system design. The difference here is you have a very thin layer for the kernel space. You have minimal, I mean, only code you have in the kernel is the minimum amount of code needed to guarantee isolation among processes and that kind of thing. All the drivers, video, disk, hardware, network, all this stuff is pushed into user space sitting alongside your user space processes. And each bubble here is actually guaranteed to be isolated by the micro kernel. So what guarantees this isolation? Well, on the Intel x86 architecture, a lot of you have probably heard of this ring architecture where you have level 0, 1, 2, 3. And it turns out modern operating systems only use two levels. They use level 0 for the kernel, which gives you unlimited access to the hardware. If you have a program running at level 0, you can do anything to any bit and any where in the computer. And the user space processes run at level 3, which is shown in yellow here. And if you want access, they don't have direct access to the hardware. If you want access to the hardware, you have to make a system call. Ask the kernel to do it for you. So I want to show you just real quick how this is implemented in the hardware. There's a 16-bit code segment register on Intel piniums and x86 processors. And the lower two bits of that register specify what privilege level you're running at, which ring you're in. And this is some test code to test and see which ring you're in. And so I'm going to demo this code running real quick, just a quick demo here. So I have a Linux box running over here to my right. I'm going to log into that. All right, so I have this Git CPL program that gets your current privilege level. And it returns 3 because I'm running a user space process forking off the command line. Now let's check out the kernel. All right, so I inserted a kernel module called, well, it's way too big on there. And I inserted a kernel module called Git CPL, which all it does is, right here, all it does is pops the CS register from into a variable and ends it with 3 to get the ring level. And if you look at the bar log syslog, you can see that, in fact, the kernel is running at ring 0. I'm going to get my next demo set up and we'll go back to the presentation. And so these slides here are just showing that as well. All right, so if you're looking at the operating system and you want to make a user level root kit, how do you do it? Well, you attack files on the hard disk shown in red here. And so you replace ps, netstat, ls, top, password. These kinds of files will, you replace those with malicious files that hide your activities. Hide files, hide network connections, these kinds of things. And so when you replace those files, they get loaded into your processes, the green bubbles up there. So that is pretty much the user level root kit. It's pretty easy to detect. You just run drip wire, check your system against your known good state. And it's not too difficult. We know how to do that. Definitely drip wire has its issues, but there's a way to deal with those. Now kernel level root kit attacks, they modify the running kernel code and the running data structures, which is this block shown here in red. So anything in there is suspect to being attacked by a kernel level root kit. And all kernel is, is a running program on your computer. So in essence, these kernel level root kits are modifying code on the fly and redirecting that code to malicious code. In particular, the system calls is the gateway from user space to kernel. And that's a good target and the virtual file system. And I'll go over those two examples. So first I want to go over a system call and why, you know, that is the most popular attack target for kernel level root kits. So it's important to understand how a system call works in order to, a, detect the system call has been tampered with and, b, recover from it. So what happens, what do you use a system call for? Okay, say you're a user process and you want to read files from a hard disk. Then you can't do that directly because you don't have access to the hardware. So you issue a system call to the kernel, to open, read the constants of those files and so on and so forth. So for instance, let's say we have a sys read system call. What happens? Well, the first thing that happens is your user space process issues an int 0x80 instruction, which says a software interrupt, which indexes into 0x80, 80th entry of the interrupt descriptor table. And that's shown right here. So as soon as you issue that interrupt, you go over to the kernel. The kernel looks up where the IDT is in memory, looks up that 0x80th entry and executes the code pointed to by that entry. The code pointed to by that entry is the system call handler. And the system call handler is responsible for setting up parameters of your system call, generic parameters. It works, that's the first thing that's called for any system call. And then it calls the system call you specifically requested. So you see this pointer coming out of the code in the system call handler, which points to the system call table. So it references, let's say for sys read, you look up this entry number three in the system call table over here. And that redirects you down here to the sys read system call. And the sys read then takes the parameters given to it by system call handler and executes your request. So what's the attack points here? Anything in this chain is an attack point. You could redirect the IDT entry, you could redirect the system call handler reference to the system call table. You could redirect individual system calls. You could overwrite the system call with jump instruction to go somewhere else. All these are susceptible. So how do you recover from this system call attack? Well, here's the algorithm. It's basically installing a new system call table into the running kernel. This is exactly what a root kit will do, in fact. But we're going to use a good system call table that has known good state. So the best place to get that is to keep a copy of your kernel on another disk on safe storage. And you can rip out these system calls using what I did with modified GDB to get that code out. And so you, it's pretty simple. You just, well, it's somewhat simple and it's kernel stuff. But basically you copy each system call into kernel memory and then you create a new system call table pointing to all those system calls. And then you can set the handler to point to that system call table. And there's, you can go full blown. You can, if I go back to this picture, you can set this pointer right here, which is on the left for you guys. And you can point that to a new system call handler if you want. And that way you'll know the whole chain is correct. Okay. So it's a little, just want to give you guys some details on, it's not quite that simple. You can't just copy stuff over. When you copy code around in memory, x86 code has these call instructions with relative offset parameters. So you have to adjust them, basically relink them into the kernel. So you could recompile the code if you know where it's going to be loaded. But I chose just to recompute those relative offsets as I was inserting each system call into memory. And you adjust the relative offset based on where it's placed in memory because you allocate dynamic memory for it. And you compare that to where it was originally in memory and you just subtract the difference. Of course it's a little bit more confusing with the way the Intel architecture works, but that's the gist of it. So another thing is there's multiple ways into the kernel. I'll just give you some background. This is another tidbit of kernel level rootkit stuff is you can insert a kernel module into the kernel and muck with a system call table. And that's one way to penetrate the kernel. Another way you can get into the kernel is through the dev kmem device file. This device file gives you access to the kernel memory as a file. So if you're a user space process, you can open that dev kmem like it's a regular file. You can seek to it and you pass it a kernel memory address and it seeks that memory and then you can read that data or write to it. Has anybody familiar with the socket rootkit from those people that have had installations on their computers? We got some people. So there's a few more. Okay, so if you want to install your rootkit from dev kmem without doing a kernel module, there's a couple of things that are tricky. First, how do you find the system call table? You don't have the symbols. And two, how do you allocate memory to insert new code into the kernel? All right, so first I'll just go over how you find the system call handler right quick. This is some code pulled out of socket. First thing you do is you pop the IDT register into memory. And what that is is that tells you the address. Let's go back a few slides real quick. That tells you where this table is in memory. All right, so now you know where that table is. So then you read the 0x 80th entry of that table. And you can compute where the system call handler is in memory. And then you can parse the system call handler to find out where the system call table is. All right, so how do you get dynamically allocated memory from the kernel from user space? If you write a kernel module, you just use kmalik. But you don't have access to that code from user space. Well, the way socket does it is sets up kmalik as a system call. So there's a number of unused system calls in the system call tabler. Suck it takes over one of those system calls and points it to kmalik. So now you can issue system call 180 or something like that and pass it a size memory allocation size and you'll get kernel memory returns. Okay, so now I want to do my next demo. So the scenario here is I've already got root access to this machine, so I'm wanting to install my root kit to retain access and to hide some files. And this folder right here, I have some bank accounts I want to store on this computer and don't worry, these are not legit, at least I hope not. Okay, so we've got this data that I don't want to store on my own computer, but I want to store it on this machine that I've compromised so I can trade it with somebody else. Something like that. So then I've already downloaded my suck it root kit onto the system and I'm going to install it and it's done. So now that directory that I was just showing you up here, this hidden info.sk12 is now hidden. The way this works is anything that ends in .sk12 will be hidden. For instance, if I touch a file called hide me, you can see that it's there. If I move hide me to hide me .sk12, it's gone. So now I'm the administrator of this computer and I believe I may have a root kit on my system. So what I'm going to do is try and install a new system call table to see if there's any differences in the file system. Okay, so this takes a little bit of time because it's not optimized. It's good for demo though because I can explain a few things. So what you see here is each little block of code, it's copying a system call into kernel memory. It issues a K malloc for each one of these, copies it in and then you see these asterisks, asterisks, asterisks found, dot, dot, dot. It's finding these call instructions and it's recomputing where they need to be relinked to and setting that. So right now we're at about 130 system calls that have been copied into memory. Turns out Linux has 255-ish system calls. And so we'll be through this here in a second. Does anybody have any questions so far? We've got a few more minutes. From what I've found, I mean, that's a possibility that they have absolute calls but you could relink those in as well. From what I've found, they don't have absolute calls. Excellent, excellent question. I will get to that very shortly. All right, so the new system call table has been installed. Now notice these hidden files come back up. So as you can see, all suck it was doing was redirecting some system calls. So we install a known good system call table into memory. All of a sudden these files come back up. Does anybody have any other questions while we've got a stopping point in the presentation? How does it catch it? Right, let's go back to that figure. So you're saying this code right here is being overwritten or something like that. Okay, so first off, the tools that I was just showing you do not do root kit detection. Well, some of them do but the recover root kit itself does not. I do have the ability to, you know, read any system calls or I could dump this code for sysread to a file and see if it's been overwritten. For instance, to answer your question, that would be the way to do it. It's changing memory. So the way the recovery tools work is right now it actually just replaces this pointer to a new system call table. And so that's going to shoot over here into memory somewhere. You've got a new system call table over here. You've got a new sysread over here. So even if this sysread right here was overwritten, that's not a problem because we aren't depending on that. That's a good question. Okay, so back to our gentleman over here's question. Okay, so it turns out if you use devkmem to modify the kernel as I do with my recovery tools or even if you use a kernel module to do the recovery, either one of these approaches relies on system calls to do the recovery. So there's an obvious problem here. If you've installed a root kit that's redirected system calls and you're depending on those system calls that's redirected, then you may be depending on a code that's going to prevent your recovery tools from working. Does that make sense to everybody? It's a little bit confusing. Okay, so how do we deal with this? Well, I propose we need a new system architecture and let me take a minute to explain the vision I have here which goes much beyond root kits and much beyond anything like that. I've got this vision of intrusion recovery system. So this is like, you know, you've got your intrusion detection system. You've got your intrusion prevention system. But bottom line is we still have compromises every day. It's still a problem for the foreseeable feature. So we need to think about ways to more quickly recover from compromises given that they're going to be a problem. Systems are so complex. So that's why I've come up with this notion of an intrusion recovery system which has a new system architecture that's not currently used in widely available OSs and it's capable of verifying the integrity of the running system and repairing that state if it has some problems. And it has, you know, obviously there's got to be some kind of, this has got to be the top most secure piece of your system because you'll rely on it for, you know, your system running good. So you have to have some kind of way to store a known good state for your system. Because if you store the known good state in your system that is accessible by route, then that can be changed. So this new architecture supports these kinds of things. It supports what I call a state hold which is kind of like a stronghold but it stores your state instead. And it does these other things. And I'll go over, this is my research right now so it's certainly in development and I'm looking for any kind of feedback you guys have. It's probably got some problems but I'll show you what I've got and we'll see what you say. So back to the beginning, I showed you, you've got the monolithic operating systems and you've got these microkernel operating systems. Well, I'm going to go back to this microkernel operating system. It turns out this looks really good for security applications. If you look at the microkernel, it has about 20,000 lines of code that run the kernel. Compare that to the Linux kernel which has on the order of 4 million. So we can hopefully better guess or better rely on the microkernel for correctness. Matter of fact, there's some research microkernels out there that try and formally prove that the microkernel is correct. These system calls to the microkernel, there's only, you know, 14 or so, just depends on the implementation, maybe on the order of 10. So you've got a small number of system calls that do basic stuff, you do IPCs, you do interprocess communication, you do thread management, that kind of thing, that's it actually. All the hardware access, these kinds of things are pushed to user level. So based on this microkernel design, I've laid out this spine architecture for the intrusion recovery system. At the base of the architecture, you have the microkernel which I'm using L4 fiasco implementation which is developed by a university in Dresden, Germany. And on top of that runs the guest kernel, the L4 Linux implementation runs on top of that. So you can run a full blown version of Linux on top of the microkernel. So you're probably more familiar with the term virtual machine than you are microkernel. Turns out these are very highly related topics. Essentially the microkernel does the same thing for us. And so we have this guest kernel which is L4 Linux running on top of it. At the next level, you've got your user processes and those run just like they do on Linux. You don't even have to modify them. And then over here on the first, you know, only fiasco runs in kernel mode. So only fiasco runs in that ring zero and everything else runs in ring three. That means even if you have root access on the kernel machine, you're not guaranteed access to all bits in the computer system. So on the right here, I have at each level a component of the intrusion recovery system. I call them L0, L1, L2, L3. And L0 is just the hardware. There's really no support for that right now, just accept the ring stuff. Turns out Intel and IBM, these kinds of companies, are very interested in hardware support for virtual machines. So in the future, this will definitely be huge. L1 is a component I have running at the microkernel. And the importance of this component is it's able to verify that the guest kernel and the L2 component are operating correctly. And similarly for L2 to L3. And why do I have these various levels in the system? Well, it turns out if you're looking at the entire Linux running system, the entire Linux operating system from L1, you're going to have a hard time interpreting all the data structures, interpreting what's good, what's bad. So it's much easier to run components in each level. In order to understand the semantics of that level. And the vertical hierarchy is a way of guaranteeing that those components are not chosen or stopped or bypassed, these kinds of things. So just, there's a lot more details here. I just want to give you a few. I'll just show you the memory real quick. How does the memory work, for instance? So down here at the bottom, you have the microkernel L1, which owns all the memory. And it can allocate some of the physical, by memory, I mean the RAM. It can allocate some of the RAM to the guest kernel, which is shown in red here. And you can see that portions of the memory are still owned by the microkernel. And the Linux guest kernel cannot touch that memory. And then in turn, the guest kernel, the Linux kernel, can map some of its memory to user space processes. All right, so this is the system. How do we attack it? So you can attack any of these things in user space. If you have read access, you have access to all of these components highlighted in red here. So again, the L2 and L3 components of the intrusion recovery system are suspect to attack. And that's why we need L1 to verify that they're operating correctly. So right now, I'm still working on algorithms to determine when L2 and L3 are operating correctly. So far, I only have monitoring the PC instruction counter and verifying the hash of the code at L2 to verify and check that the program kind of runs in that code periodically. If anybody's got some ideas on other ways to verify, I'd definitely like to talk to you. Okay, so now I want to look at another kernel-level rootkit target. So it turns out these same kernel-level rootkits will work on this new spine architecture. They will attack the guest kernel instead of the host kernel. So I want to go through another target to give you guys some more background on how kernel-level rootkits work. This target is the virtual file system. Now, the virtual file system in Linux is a wrapper that gives you access to all file systems in Linux. And so you can insert your own virtual file systems. If you have an ext3-formatted partition, then you insert that in the VFS and you don't have to readjust your system calls to point to that. Everything goes through the virtual file system. Let me show you how it works. So first off, a system call is issued, let's say I'll go over the PROC file system in this example. Let me back up a minute. So you have lots of different file systems in Linux. You have your file system for your hard disk. You have remote disk storage stored on another network drive. You have the PROC file system, which is a file system built by the kernel and gives you information from the kernel, such as thread or task IDs, and so on and so forth. So if you want to... Remember, if you want to get access to the file system, any file system, you have to issue a system call. So in this example, I want to read the contents of the PROC directory to see what processes are running on my system. So first, my program will issue a sysreadder that sysreader indexes into the virtual file system, which then... And you've told it to open up this PROC, some directory in PROC. So the virtual file system knows, based on the path you gave it, where it needs to go to service your request. For PROC, that's a special file system. It indexes into its root structure. And there's a lot of pointers in that root structure. The f underscore op stands for file operations, I believe. And so for the PROC, you look up what are the file operations for that file system. And so there's a fop structure, which is standard. And for each file system, you have to implement a number of function pointers. In this case, readder is the one I'm going to highlight. So you've indexed all the way in here to readder. And finally, you get to the PROC readder code. And so that'll then return information back to your system call and then return that information back to your user program. Okay, so what's the attack points here? Again, it's the same basic idea. Any time you've got a function pointer, any time you've got a reference, any code along the path can be redirected. So what's the recovery algorithm for this? Well, now's when we sort of step back and make it more general. So generally, the recovery methods for kernel-level rootkits is consistency checking on the data structures in the kernel and the code running. And you can store a copy of this, what it should look like in your protected spine architecture. And you can use that to check and see if it's been modified in a manner that's against your policy and if so, then you repair it back to its known good state. And the IRS can do this automatically. And that's sort of the vision is you have this system that's constantly monitoring your computer for malicious root cancellations. If that happens, it undoes the damage from it and furthermore have longer term goals of, okay, so yeah, you've got rootkits installed, you undo this damage, the attackers still got in your system somehow. You also have to figure out how you got out, kick them out, you know, change passwords. It's more than just repairing the system. It's a lot more complicated. All right, so let's go for another demo. Now, for the purposes of this demo, I'm not using the automated repair of the system because you wouldn't be able to see that anything happened. But one thing I want to show you real quick is I am in fact running the spine architecture now. You can see them running the Linux 2.6.11-L4 kernel, which is the way it works for the Linux kernel is you port the Linux kernel to the L4 architecture as opposed to the i3886, the alpha, so on and so forth. So in fact, we can go in here and check out our getCPL again to see if our privilege levels have changed. And so the user process is still running at ring3, which is expected. And let's see what the kernel is now running at. And so can everybody see this in the background? Should I call it out for you guys? Move it up. Sorry about that. Is that better? Thanks. Okay, so what I've just done, let me scroll back up for you guys then. So right here I just did the getCPL and it returned ring3, running in ring3. And I have just inserted the getCPL into the running kernel and you can tell the var log syslog and see that it was printed out as running in ring3 now. So now the kernel is running in ring3. If anybody's out there familiar with user mode Linux, it's a very similar concept to that, but it's much, much faster. In terms of performance, it'll take a slight venture. Just in case you guys are wondering, you figure about a 10% performance penalty for running in this architecture. So it runs about 90% the speed of Linux running directly on your hardware. Okay, so for the second demo, I'm going to install the Door Next Generation rootkit. And this rootkit works on the Linux 2.6 kernel. And what this rootkit does is exactly what I was just showing you with the virtual file system. It redirects the virtual file system to malicious structures so it can hide files and processes. Okay, so I've just loaded the rootkit. Notice first off that my DoorNG directory that was just in is now hidden. That's part of the configuration. It hides whatever your rootkit directory is. And then I'm going to launch this rootme binary, which doesn't really do anything, but it runs. And you can see right here in the process list that rootme is running. Now I've got to scroll back up here and see what my directory was. So I'm going to go back into the DoorNG directory. I'm tabbing over right now and that's another system call. It doesn't find that there's an DoorNG directory. So I have to type the full path. And there's a user-level utility to go with this DoorNG rootkit, which can hide processes for you. So I want to hide this process 644. So now rootme is gone. All right, so now I'm going to put on my system administrator hat again. And I suspect that a rootkit has been installed. This could be done automatically with the intrusion recovery system, but again for the purposes of this demo, I will do it manually. And so what I'm going to do is a similar algorithm to the system call table repair. I am going to, but this time I've made it a little bit simpler and I just fixed the pointers back to where they're supposed to be. Okay, so I've just repaired the proc file system. And now you can see the rootme file is back, our process is back. And you can see the DoorNG directory is back. So in fact we've been able to recover from these kernel-level rootkits. Okay, let's go back to wrap up things here. All right, so I've shown you the ability to recover from some kernel-level rootkits and presented you with an architecture that's perhaps more robust and you can somewhat rely on for this recovery. So I mean definitely there's some limitations to this system. I'm still developing it now, so any feedback you guys have would be great. Some of the limitations, you know, first off, how can you trust this microkernel? What if the attacker installs a rootkit into the microkernel? Well, you know, that's a hard question, but it's relying on the smallness of the microkernel to be correct to prevent that capability. You know, one of the things that some of you guys are probably thinking about already is what about direct memory access where you program a controller to overwrite the microkernel? Well, that's definitely a problem right now. That's the limitation. And future Intel architectures that support better isolation that hopefully will not be a problem. So, and finally, you know, just like everything else with security, there's no be-all and-all solution. However, I believe this IRS can make your systems more reliable and save us a lot more time and trouble. So thanks to a lot of people that have helped me out. You know, there's lots more people on here than I have listed. These people in particular have been helpful. I got some links for you guys if you want to find out some more information about what I've been talking about, various architectures, system projects here, you know, check root kit, our research. And this time, we've got a few starter questions if anybody wants to comment on. And if not, if you've got your own questions, that's fine. We've got about five minutes or so. And also, after the talk, I will be heading out that door over there. So if you guys want to catch me, definitely link up with me outside. So first off, let me just ask these three questions and I'll take either answer these comments to these questions or your own questions. So I already asked this question, how many people have personally dealt with the recovery of root kit? If anybody has a testimony, we might share that in a minute. Let me read all three of these and then I'll start calling on people. Secondly is I would definitely be interested to see if anybody's seen any root kits that use DMA, Direct Memory Access. I've seen a lot of talk about it. Maybe I just have not seen the public talk about it or public implementation. And finally, if anybody has not followed the conventional wisdom of wiping their system clean and reinstalling, I'd be interested to hear that. So this time I'll open it up for questions and comments over there. Okay, so the question was with the L4 architecture, can you run multiple instances of your kernel? And the answer is yes. The L4 architecture is in fact pretty much a virtual machine architecture so you can run multiple instances of Linux. System pipes and log files, I have not addressed that yet. Good question. Certainly, but I mean, so it's suspicious, but it's kind of like running a virtual machine, you know. You can tell when you're running on VMware. But legitimate systems run on there. So that definitely would be able to tell that it's not a normal system. Absolutely. Actually, a lot of the tools that I have written for the system call stuff gives you all kinds of information on the system call table. So for instance, you can check out each individual system call with these tools and that is much more surgical. Definitely. Good comment. Question here. So the question was, this system would be useful just for detection, correct? Absolutely. Why am I doing intrusion recovery system as well? Because I'm a research student and we've got to do new and exciting things. So absolutely, and lots of people have talked about using similar architectures for intrusion detection. That's an excellent idea. That'll be the way it works 10 years from now. We've got guys in the back, people over here. Oh yeah, absolutely. As a matter of fact, I was going to do a demo on that for this presentation but didn't have enough time. Recovery from user level root kits, I've done some work on that. It's pretty much tying a trip wire or aid kind of thing to a recovery program which just checks your file system periodically and replaces the binaries back to normal. That's absolutely a part of the system because you need the full system. We've got a question way back there in the back and I'll come back up here. That's an excellent question. So Zen is another very similar architecture. In fact, I've got a link to that in the slides. Zen is very hot right now. It's a virtual machine architecture open source. I believe it's England. People are doing that. And it's very fast. As a matter of fact, Zen and L4 compete. I am going to compare Zen to L4 and my thesis to see which one would be better. It's just an implementation difference. Either one is certainly suitable. I just chose L4. I've got a question up here and then I'll quit after this guy. The question is what's the more background on this microkernel? The reason I chose L4 fiasco implementation is basically it's one of the more mature L4 implementations developed by a university at Dresden. The team there has been really good at helping me out. Any kind of questions I've got. They've got a big team there. Lots of questions. They've got the mailing list and everything. Comparing it to other... There's also two or three other implementations I could have used. I happen to know the guys a little bit at Dresden. That's really the only reason. That's a good question. I'm going to wrap up and let the next speaker get in. I'll be out here in the hall if you guys have got any more questions.