1 00:00:00,000 --> 00:00:04,560 It will just be a minute more as we get self-paced running and things like that in our presentation. 2 00:00:06,300 --> 00:00:10,700 The presentation was originally developed for an hour and 30 minutes. 3 00:00:10,800 --> 00:00:16,180 However, this time slot is an hour and 50, I mean 50 minutes and we've already filled like 7. 4 00:00:16,700 --> 00:00:22,040 Therefore, we may run a little bit late if we need to leave early. 5 00:00:23,900 --> 00:00:31,480 However, I will go through the first 20 slides in about 2 minutes because we don't have time to get them in. 6 00:00:31,520 --> 00:00:36,860 And Sherry does the interesting slides, so I will introduce her soon. 7 00:00:37,000 --> 00:00:38,740 My name is Jamie Butler. 8 00:00:38,740 --> 00:00:40,400 I'm from Rootkid.com. 9 00:00:40,400 --> 00:00:42,080 This is Sherry Sparks. 10 00:00:42,140 --> 00:00:47,780 And we're developing a project called Shadow Walker and it's to hide rootkits. 11 00:00:48,300 --> 00:00:53,580 You can find a description of this talk probably later on Rootkid.com. 12 00:00:53,580 --> 00:00:54,340 It's in the source code. 13 00:00:54,340 --> 00:00:55,840 I believe Sherry has proposed it. 14 00:00:55,840 --> 00:00:58,580 Also, it will be in FRAG 63. 15 00:00:59,200 --> 00:01:04,160 If it hasn't appeared online already, probably in the next week or two it will be there. 16 00:01:04,180 --> 00:01:12,020 If you don't understand much about rootkits or you are new to the Windows world of rootkits, you can go to Rootkid.com. 17 00:01:12,020 --> 00:01:16,300 We post what's there for free and there's a lot of source code for free. 18 00:01:16,640 --> 00:01:19,120 Nothing there is for free. 19 00:01:19,120 --> 00:01:26,580 Or you can apply for a rootkit supporting the Windows kernel by Greg Kogel and myself. 20 00:01:27,100 --> 00:01:28,980 So we'll get started with Shadow Walker. 21 00:01:32,820 --> 00:01:34,540 So a quick flow through. 22 00:01:34,620 --> 00:01:37,300 Rootkits, they don't propagate themselves. 23 00:01:37,300 --> 00:01:38,100 They're not like a virus. 24 00:01:38,100 --> 00:01:39,760 They're just here to provide stuff. 25 00:01:39,860 --> 00:01:43,960 So the attacker gets on the box and they try to maintain presence. 26 00:01:44,040 --> 00:01:48,160 They don't want to keep using their zero day to come back all the time to the machine. 27 00:01:48,160 --> 00:01:49,920 So they install a rootkit. 28 00:01:50,060 --> 00:01:56,440 A rootkit is there to hide their presence and also to give them future access at a later date. 29 00:01:56,760 --> 00:02:03,840 Rootkits can also be used for law enforcement for things like a sneak and peek or a properly executed search warrant. 30 00:02:04,640 --> 00:02:11,320 There's two levels of protection within the Windows and Linux operating system. 31 00:02:11,320 --> 00:02:14,660 There's basically two rings, ring 0 and ring 3. 32 00:02:14,660 --> 00:02:18,460 Ring 3 is user land processes. 33 00:02:18,600 --> 00:02:20,260 They are unprivileged. 34 00:02:20,260 --> 00:02:21,960 Ring 0 is kernel land. 35 00:02:22,420 --> 00:02:27,760 Once you're in ring 0, your kernel rootkit can do whatever you want. 36 00:02:28,820 --> 00:02:39,980 Operating system provides basically an intermediary between upper level user processes and things that they want to query the operating system for. 37 00:02:39,980 --> 00:02:42,420 So I want to open a port. 38 00:02:42,420 --> 00:02:44,340 The operating system does that for you. 39 00:02:44,340 --> 00:02:47,360 And then hands it back to the user land process. 40 00:02:47,360 --> 00:02:53,860 They also provide an interface between user land processes and the lower hardware. 41 00:02:54,180 --> 00:03:01,320 So if you have a ring 0 kernel rootkit, you are just like the operating system, you are a man in the middle. 42 00:03:03,700 --> 00:03:09,800 Some of the different OS components that a rootkit may want to attack would be the IOManager. 43 00:03:09,800 --> 00:03:11,680 You can install keyloggers. 44 00:03:11,820 --> 00:03:16,920 Sharing has a project on rootkit.com that's a keyboard sniffer. 45 00:03:17,400 --> 00:03:20,040 And you can also attack the file system. 46 00:03:20,060 --> 00:03:26,700 You can return file filter driver and interface between every single access of the hard drive. 47 00:03:26,700 --> 00:03:34,560 The object manager has things like processes and threads that it uses to keep bookkeeping. 48 00:03:34,620 --> 00:03:40,400 And you can attack the object manager in order to hide your processes and threads. 49 00:03:42,420 --> 00:03:44,900 That's enough of that configuration manager. 50 00:03:44,900 --> 00:03:46,640 You can use that to hide registries. 51 00:03:46,760 --> 00:03:49,500 So first generation of rootkits, what were they? 52 00:03:49,500 --> 00:03:55,320 These were simple infringement replacement programs on the hard drive. 53 00:03:57,060 --> 00:04:04,360 They may include the Unix login program, LS, ES, so on and so forth. 54 00:04:05,040 --> 00:04:07,160 Second generation rootkits. 55 00:04:07,160 --> 00:04:09,720 These started to be more sophisticated. 56 00:04:10,220 --> 00:04:16,740 These modified static tables within the kernel are within the upper levels of the operating system themselves. 57 00:04:16,740 --> 00:04:25,600 So you can replace the import address table of a PE file and alter the execution path of any command. 58 00:04:25,600 --> 00:04:36,900 You can also alter the system call table within the kernel and ring zero and change what function actually gets executed within the kernel when the user requests it. 59 00:04:38,840 --> 00:04:41,560 Third generation rootkits. 60 00:04:41,560 --> 00:04:48,260 We term these... they use DCOM, direct kernel object manipulation. 61 00:04:48,540 --> 00:04:53,940 These actually don't hook anything because hooks are a bit easier to detect. 62 00:04:53,940 --> 00:04:58,380 Third generation actually manipulate the data structures in memory. 63 00:04:58,380 --> 00:05:04,900 So there's no code... you don't alter the code at all, you just alter the data structures themselves. 64 00:05:05,000 --> 00:05:07,260 This is almost undetectable. 65 00:05:07,260 --> 00:05:10,880 Data structures come and go very frequently and they're constantly changing. 66 00:05:10,880 --> 00:05:12,880 That's why they're in the data structure. 67 00:05:14,900 --> 00:05:17,460 This is very hard to detect. 68 00:05:17,460 --> 00:05:20,960 And we'll talk about how you can use this to hide processes and threads if you wanted. 69 00:05:21,080 --> 00:05:27,980 Or you can use it to hide drivers, escalate process privileges using tokens, etc. 70 00:05:29,060 --> 00:05:37,200 So real quick, about two years ago at one of the black ads, and all the slides are online, you can see process hiding with DCOM. 71 00:05:37,200 --> 00:05:40,080 I also mentioned a little bit last year when I talked about Python. 72 00:05:40,960 --> 00:05:47,460 What the kernel uses to keep track of all the processes is this list of active processes. 73 00:05:47,880 --> 00:05:53,220 Every e-process block you see there represents an individual process in the system. 74 00:05:53,500 --> 00:05:56,760 This individual process could be your little hacker program. 75 00:05:56,960 --> 00:06:00,860 It's doing things and you don't want the system administrator to know you're there. 76 00:06:01,580 --> 00:06:06,880 If you simply unlink... at the bottom of the slide you see that there's a series of links. 77 00:06:06,880 --> 00:06:13,760 If you simply unlink a process that you want to have hidden, it will disappear when you try to list it out. 78 00:06:14,100 --> 00:06:17,900 So when you run task manager, etc., it won't be there. 79 00:06:20,420 --> 00:06:24,740 Now, current rootkit detection methods, there's basically four types. 80 00:06:24,740 --> 00:06:26,680 You don't have time to go into all these. 81 00:06:26,820 --> 00:06:31,640 But just quickly, behavioral, integrity, signature-based, and difference-based. 82 00:06:32,560 --> 00:06:36,720 Some of the AB products are basically an example of signature-based. 83 00:06:36,740 --> 00:06:44,820 Integrity checkers, such as tripwire, check some of the disk files so that if you replace it with a fresh one, you'll be found. 84 00:06:44,960 --> 00:06:47,440 Difference-based approaches is kind of a new thing. 85 00:06:48,080 --> 00:06:49,060 Microsoft's doing it. 86 00:06:49,060 --> 00:06:50,620 F-Secure's doing it. 87 00:06:51,100 --> 00:06:51,980 Sysinternals. 88 00:06:52,320 --> 00:06:58,200 What this is, is it does some kind of low-level scan within the kernel by following structures directly. 89 00:06:58,200 --> 00:07:02,540 It tries to get a list of things like files and processes, registry keys, etc. 90 00:07:02,840 --> 00:07:05,060 Then it does an upper-level API call. 91 00:07:05,060 --> 00:07:07,500 If there's a difference, it knows something's up. 92 00:07:10,580 --> 00:07:11,880 Behavioral detection. 93 00:07:12,420 --> 00:07:15,800 In this, you try to detect the presence, the effects of rootkit. 94 00:07:15,800 --> 00:07:22,920 So you might want to look for alteration in the execution path, or what is actually executed within the program. 95 00:07:22,920 --> 00:07:25,600 Some of the system calls, or the ordering, etc. 96 00:07:26,080 --> 00:07:28,780 We don't have time to go into more of that. 97 00:07:28,800 --> 00:07:30,080 Integrity checking, exactly. 98 00:07:30,080 --> 00:07:31,300 It's a CRC. 99 00:07:31,380 --> 00:07:33,340 This could be on-disk or in-memory. 100 00:07:33,380 --> 00:07:35,960 However, hardly anyone is doing it in-memory. 101 00:07:37,260 --> 00:07:45,380 Signature-based scan memory or on-disk for a particular set of bytes that represents the mal code. 102 00:07:47,440 --> 00:07:52,180 So, we have a really powerful way to hide rootkit, or hide processes, etc. 103 00:07:52,180 --> 00:07:55,100 within the kernel by modifying all these data structures. 104 00:07:55,840 --> 00:08:08,340 However, if you look for our driver, because we are in the kernel, so we have a kernel-level driver, you can just do a simple scan, like a signature scan, for the presence of the malware. 105 00:08:08,860 --> 00:08:11,020 And that's not very good. 106 00:08:11,360 --> 00:08:21,520 So what we want to do is to try to shake it up, so even if you scan memory, you can't detect all that decomp stuff that's in here in the fu-rootkit. 107 00:08:22,520 --> 00:08:26,720 You want to make it so you can't detect our rootkit within the kernel. 108 00:08:27,140 --> 00:08:34,360 So, this is a problem that viruses have faced for a long time, and they use things like polymorphism and stuff to get around it. 109 00:08:34,640 --> 00:08:42,100 However, in the kernel, it gets more problematic to do really good polymorphism code. 110 00:08:42,100 --> 00:08:50,280 Also, no one's doing it other than a holy father and his hacker defender. 111 00:08:50,480 --> 00:08:52,680 And you have to get him to handle that. 112 00:08:53,020 --> 00:08:55,840 And I think it's like 40 heroes in the county does it. 113 00:08:56,380 --> 00:09:03,460 So, really quickly, we're going to walk through how we deploy this in memory and share it with people. 114 00:09:09,910 --> 00:09:11,270 Thank you, Jamie. 115 00:09:11,270 --> 00:09:19,370 I'm going to be discussing the technical implementation of ShadowWalker, which is our rootkit hiding technology. 116 00:09:19,690 --> 00:09:28,350 Like Jamie said, polymorphism has not really been seen a lot in rootkits so far, but it's not really an optimal approach for a rootkit. 117 00:09:29,130 --> 00:09:31,330 Viruses need to hide their code. 118 00:09:31,610 --> 00:09:37,890 Rootkits not only need to hide their code, but they also need to hide changes to operating system components. 119 00:09:38,870 --> 00:09:49,250 Polymorphism is not necessarily an optimal solution for that when you consider that you have the memory-based equivalence of, say, tripwire or other types of integrity checkers. 120 00:09:49,870 --> 00:10:01,150 So, basically, what we wanted to do is we wanted to figure out a way to hide in memory such that we actually hook the memory of the system without actually changing the code at all. 121 00:10:01,730 --> 00:10:10,090 And that gave rise to ShadowWalker, which we're coining kind of as a prototype for the fourth generation of rootkit technology. 122 00:10:11,570 --> 00:10:16,930 Basically, the alternative to polymorphism is virtual memory subversion. 123 00:10:18,550 --> 00:10:33,030 Basically, what we're going to be showing you today is a proof-of-concept demonstration that a rootkit is capable of transparently controlling the view of memory seen by user applications, kernel drivers, and the operating system itself. 124 00:10:34,730 --> 00:10:39,950 Now, the cool thing about this technology is the fact that there's a minimal performance impact. 125 00:10:40,270 --> 00:10:50,570 Because we exploit some features in the hardware, that means that basically our memory hook engine hiding a rootkit is virtually undetectable. 126 00:10:50,570 --> 00:10:53,910 It has no slowdown or system performance hit. 127 00:10:53,910 --> 00:11:03,210 Obviously, this makes this type of technology attractive to not only a rootkit, but also to maybe a virus, or a worm, or spyware applications. 128 00:11:06,250 --> 00:11:09,930 Just a few implications of virtual memory subversion. 129 00:11:10,030 --> 00:11:16,690 For a long time, rootkit scanners relied upon the integrity of the operating system API. 130 00:11:16,910 --> 00:11:34,030 Like Jamie mentioned, ever since the second generation of rootkits, rootkits have been hooking the API, and upper-level processes can no longer trust the results returned to them by the API, either the user-level API or the kernel-level API, depending upon where the rootkit is hooking. 131 00:11:34,030 --> 00:11:38,250 So basically, rootkit scanners have gotten a little bit smarter. 132 00:11:38,250 --> 00:11:44,650 They're starting to realize that they can't rely upon operating system APIs anymore. 133 00:11:44,650 --> 00:11:53,230 But even those rootkit scanners that don't rely upon operating system APIs, they still rely upon the integrity of the virtual memory system. 134 00:11:53,810 --> 00:12:02,250 So therefore, when a security scanner does a read of a certain memory address, it expects to see the data that's actually stored at that address. 135 00:12:02,250 --> 00:12:09,250 What we're proposing is actually to return some data that is, in fact, not actually stored at that address. 136 00:12:09,530 --> 00:12:12,950 So basically, there are two implications here. 137 00:12:13,010 --> 00:12:23,730 If we can control a scanner's memory reads, then we can fool signature scanners and potentially make a known rootkit or virus code immune to an in-memory signature scan. 138 00:12:23,730 --> 00:12:36,350 Because basically, the scanner will access the range of memory where the rootkit code is stored, and it will be returned data which is a clean copy or does not have the rootkit code. 139 00:12:36,570 --> 00:12:39,310 We can also fool integrity checkers. 140 00:12:39,310 --> 00:12:48,330 Those kind of checkers, like Jamie Butler, presented a talk on a tool called Vice, which basically attempts to detect hooks. 141 00:12:48,330 --> 00:13:00,010 Hooks typically work by replacing the beginning of the function prolog in an API with a jump instruction to a stub, which is the rootkit code. 142 00:13:00,010 --> 00:13:10,510 So basically, under normal situations, you would not expect to see the first instruction in an API to be a jump instruction. 143 00:13:10,510 --> 00:13:16,090 The normal code would be such stuff as stack setup code, allocation of local variables. 144 00:13:16,390 --> 00:13:22,430 So basically, Jamie uses heuristics like this to determine if functions are hooked. 145 00:13:22,430 --> 00:13:41,690 Basically, the implications for this is that if we are subverting that particular function in memory, the scanner would perform a read, and it would receive a clean copy of that API function code, yet the actual code that it's executing is actually the hook code. 146 00:13:44,440 --> 00:13:50,520 Since we're going to be talking about the subversion of virtual memory, we're going to need to do a little bit of a review. 147 00:13:51,100 --> 00:14:01,100 This is stuff that is normally covered in a college-level operating systems class, but I know that for most people, we probably need to review some of this stuff. 148 00:14:01,460 --> 00:14:08,660 First thing we're going to talk about is virtual address space layout, and then we're going to talk about some of the concepts of virtual memory. 149 00:14:08,660 --> 00:14:26,620 We're going to go over the ideas of paging and segmentation, page tables and PTEs, how virtual-to-physical address translation actually occurs, what the page fault handler does, and then some performance issues associated with paging, and how those are actually mitigated using a hardware solution, 150 00:14:26,620 --> 00:14:28,420 or the translation look-aside buffer. 151 00:14:28,880 --> 00:14:36,120 Finally, we'll talk about different memory access types, and some little eccentricities with the x86 architecture. 152 00:14:38,240 --> 00:14:47,060 So basically what you have here is a layout of the two most common layouts for the Windows virtual address space. 153 00:14:47,060 --> 00:14:52,060 The first one breaks the virtual address space up into two main components. 154 00:14:52,060 --> 00:14:56,820 Like Jamie said, we have two modes of operation under Windows. 155 00:14:56,820 --> 00:15:03,380 We have a privileged mode, which is basically kernel-in, and we have a non-privileged mode, which is basically user-in. 156 00:15:03,380 --> 00:15:06,880 The memory address space is arranged similarly to that. 157 00:15:07,240 --> 00:15:11,600 The lower two gigabytes correspond to user-in address space. 158 00:15:11,600 --> 00:15:16,520 That's going to contain your application code, DLL code. 159 00:15:16,520 --> 00:15:20,940 The upper two gigabytes is going to be where the operating system kernel is. 160 00:15:21,220 --> 00:15:28,280 In the upper two gigabytes, you're going to have N2S kernel, PAL, hardware abstraction layer, boot drivers. 161 00:15:28,280 --> 00:15:42,640 You're going to have key operating system structures, the process objects that Jamie was talking about, process page tables, and then non-page pool and cache memory. 162 00:15:43,080 --> 00:15:55,340 Alternatively, we have a second address space layout, which has been used on some of the Server 2003 systems, which actually allows you to expand the user address space up to three gigabytes. 163 00:15:56,460 --> 00:16:01,560 By far the most common layout is going to be the one where it's split in half. 164 00:16:03,680 --> 00:16:07,640 So, basically, what is the main idea behind virtual memory? 165 00:16:07,800 --> 00:16:16,320 Basically, the idea is we want to separate the virtual from the physical address spaces, or separate the virtual and physical address spaces. 166 00:16:17,420 --> 00:16:22,580 By that, what I mean is the virtual address space is the size of it. 167 00:16:22,580 --> 00:16:26,880 It's going to be defined by the width of your memory bus. 168 00:16:26,880 --> 00:16:38,260 So, if you have a 32-bit system, that means that you have the ability to address 2 to the 32, or approximately four gigabytes of continuous memory locations. 169 00:16:38,680 --> 00:16:43,520 So, therefore, your virtual address space spans from zero up to four gigabytes. 170 00:16:44,000 --> 00:16:49,520 In contrast, most of us don't really have four gigabytes of RAM installed on our system unless we're really lucky. 171 00:16:49,760 --> 00:16:56,100 So, therefore, we can say that our physical address space is constrained by the amount of RAM on our system. 172 00:16:56,100 --> 00:17:03,720 And the amount of RAM is most likely going to be less than the four gigabyte limit for the virtual address space. 173 00:17:04,240 --> 00:17:12,460 So, basically, virtual memory concerns managing these virtual and physical address spaces by dividing them into fixed-size blocks. 174 00:17:12,740 --> 00:17:16,420 If those blocks are all the same size, we have a paging architecture. 175 00:17:16,420 --> 00:17:20,340 If those blocks may be variable sizes, it's a segmentation architecture. 176 00:17:20,920 --> 00:17:25,460 The x86 architecture is actually a combination of segmentation and paging. 177 00:17:25,460 --> 00:17:33,200 However, for the purposes of our talk, we're focusing upon the paging architecture, because that's the level that we're going to subvert. 178 00:17:33,840 --> 00:17:42,260 As far as how this maps, there's actually mapping information that actually maps a virtual block to a physical block. 179 00:17:42,260 --> 00:17:49,220 And the OS is what actually maintains this mapping information and determines which virtual blocks map to which physical blocks. 180 00:17:49,860 --> 00:17:54,240 Like I said before, the virtual address space may be larger than the physical address space. 181 00:17:54,240 --> 00:18:01,500 And also because the OS is managing this mapping, virtually contiguous memory blocks do not need to be physically contiguous. 182 00:18:03,400 --> 00:18:07,920 This actually shows an illustration of those two points on the previous slide. 183 00:18:07,920 --> 00:18:11,860 As you can see, we have our virtual address space divided up into blocks. 184 00:18:11,880 --> 00:18:15,340 Under a paging architecture, we call these blocks pages. 185 00:18:15,700 --> 00:18:19,120 The physical address space is smaller than the virtual address space. 186 00:18:19,120 --> 00:18:22,180 It's also divided into fixed-size blocks. 187 00:18:22,180 --> 00:18:24,120 This is termed frames. 188 00:18:26,060 --> 00:18:33,380 Basically, as you can see, the virtually contiguous blocks do not necessarily have to be physically contiguous. 189 00:18:35,560 --> 00:18:41,300 So, what actually maintains this mapping information that the OS uses? 190 00:18:41,340 --> 00:18:44,490 This mapping information is contained in page tables. 191 00:18:44,880 --> 00:18:51,000 And page tables consist of entries called PTEs, or page table entries. 192 00:18:51,000 --> 00:18:55,260 Page table entries contain at least two pieces of useful information. 193 00:18:55,680 --> 00:19:00,160 They contain status information, and they contain mapping information. 194 00:19:00,320 --> 00:19:08,500 The page frame number bits are the bits that actually describe where that page actually maps to in physical memory. 195 00:19:08,820 --> 00:19:11,860 There are also a bunch of status bits, as you can see. 196 00:19:11,860 --> 00:19:16,120 A few of the most important ones would be the V there for valid. 197 00:19:16,120 --> 00:19:22,060 That's going to determine if the page is actually resident in memory, or if it's been paged out to the disk. 198 00:19:22,340 --> 00:19:30,940 You have the writable bit, which is going to be concerned with protection, whether or not that page is read-only, or whether it's going to be writable as well. 199 00:19:31,220 --> 00:19:35,160 You also have some bits, the cache-disabled, the dirty bit. 200 00:19:35,460 --> 00:19:42,320 These are bits that the operating system uses in its virtual memory management for page swapping and such. 201 00:19:42,320 --> 00:19:45,780 And then the last bit of interest is going to be the global bit. 202 00:19:45,780 --> 00:19:55,260 The global bit is interesting because it determines whether or not this particular mapping information will be flushed from the hardware cache on a context switch. 203 00:19:55,460 --> 00:19:58,060 This actually becomes important later on. 204 00:20:00,980 --> 00:20:07,040 So, basically, here's an illustration of the big picture, just to kind of summarize all these ideas. 205 00:20:07,040 --> 00:20:14,180 We have our virtual address space layout divided into user space and kernel space. 206 00:20:14,180 --> 00:20:19,390 We have our pages, which are the divisions of the virtual address space. 207 00:20:19,940 --> 00:20:25,580 We have process page tables located in the kernel memory section. 208 00:20:25,580 --> 00:20:36,680 Process page tables contain PTEs, which contain status information and mapping information, which will basically say where those pages are located out in physical memory. 209 00:20:39,820 --> 00:20:47,580 So, how do you go from an address to actually figuring out where that particular page is in the page table? 210 00:20:47,580 --> 00:20:54,760 Basically, virtual addresses can encode the information necessary to index into page tables. 211 00:20:55,060 --> 00:20:58,740 Page tables may actually be single-level or multi-level. 212 00:20:58,800 --> 00:21:02,500 The x86 maintains a two-level paging architecture. 213 00:21:03,700 --> 00:21:07,460 Basically, we divide the virtual address into two pieces. 214 00:21:07,460 --> 00:21:21,300 We're going to divide it into the virtual page number, which is going to contain the page table indexing information, and we divide it into the byte index, which is going to contain an offset from the physical frame in physical memory. 215 00:21:21,640 --> 00:21:33,480 So, basically, the virtual page number, in this case, since we have a two-level architecture, basically what that means is not only do we have a page table, we also have a page directory. 216 00:21:33,480 --> 00:21:38,640 And, basically, the page directory is a table of pointers to page tables. 217 00:21:38,940 --> 00:21:42,500 And the page tables, of course, mean pointers to physical frames. 218 00:21:43,060 --> 00:21:52,360 This actually is used to... a two-level scheme is used to actually save memory, since the page directory is going to have to be resident. 219 00:21:52,580 --> 00:21:58,060 So, basically, in this case, our virtual page number is actually divided into two pieces. 220 00:21:58,060 --> 00:22:09,360 We have to be able to figure out the index into the page directory so we can locate the correct page table, and then we have to figure out the index into the page table so we can locate the correct page frame. 221 00:22:09,380 --> 00:22:20,760 So, basically, under x86, the upper ten bits in the virtual address are going to provide your index into the page directory, while the middle ten bits are going to provide your index into the page table. 222 00:22:23,620 --> 00:22:28,840 This slide gets a little bit complicated, but I'm going to give it a go here to try to explain it. 223 00:22:28,980 --> 00:22:33,720 Basically, there's a number of steps involved in virtual-to-physical address translation. 224 00:22:33,760 --> 00:22:35,740 This is performed by the hardware. 225 00:22:36,440 --> 00:22:42,740 The first step is going to be to locate the page directory, the base address, the page directory and memory. 226 00:22:42,940 --> 00:22:47,020 The page directory base is actually located in the k-process block. 227 00:22:47,020 --> 00:22:52,120 The physical address is also located in CR3, which is the control register on the processor. 228 00:22:53,380 --> 00:22:57,420 So, basically, the first step is to locate the base of the page directory. 229 00:22:58,080 --> 00:23:05,480 The second step is going to be to locate the actual entry in the page directory that contains the pointer to the page table that we need. 230 00:23:06,080 --> 00:23:16,960 So, basically, we're going to extract the upper ten bits from the virtual address and use that to add to the page directory base to obtain the entry in the page directory table. 231 00:23:18,220 --> 00:23:28,160 Now, the contents of this page directory entry is basically the pointer to a page table, or the base physical address of the page table and memory. 232 00:23:29,040 --> 00:23:37,480 So, now at this point, we have the base address of the page table, and now we're trying to get to the PTE, or the entry in the page table. 233 00:23:37,480 --> 00:23:40,860 So, to get to the page table entry, now we do the same type of thing. 234 00:23:40,860 --> 00:23:55,060 We take the middle ten bits of the virtual address and use that to add to the base address of the page table, and we now get the actual page table entry, which is going to contain the pointer to the page frame in physical memory. 235 00:23:55,960 --> 00:24:03,180 So, now at this point, we have a pointer to the page frame, and we're actually looking for some byte within that frame. 236 00:24:03,180 --> 00:24:12,440 So, it's finally at this point that the actual byte index comes in, and we add that to the base address of the physical frame to actually resolve the full physical address. 237 00:24:19,450 --> 00:24:34,030 Like we said earlier, the physical memory may be smaller than the amount of virtually addressable memory you have, so therefore the OS may need to move some pages from main memory out to the disk to satisfy current memory demands. 238 00:24:34,790 --> 00:24:48,410 Basically, when the OS does this, it marks the PTE for the page in question as invalid, writes it out to the disk, and the next time that page is accessed, it's going to generate a page fault. 239 00:24:48,770 --> 00:24:57,270 A page fault is basically just an interrupt that is going to cause a vector to an interrupt service routine, which in this case is going to be the page fault handler. 240 00:24:58,110 --> 00:25:01,510 There are actually several conditions where a page fault can occur. 241 00:25:01,510 --> 00:25:12,250 The most common condition is going to be the case where the page table entry is marked invalid, and that page has been swapped out to the disk and needs to be brought in. 242 00:25:12,470 --> 00:25:24,050 There's a little caveat to that, because not only does the PTE need to be marked invalid, that entry cannot be present inside the TLB, or the hardware cache of virtual to physical mappings. 243 00:25:24,050 --> 00:25:26,630 This becomes very important later on. 244 00:25:27,250 --> 00:25:41,190 The second situation where you can have a memory protection violation is going to be, basically, user mode code attempting to access kernel memory, or perhaps an attempt to write to memory that's marked as read-only. 245 00:25:43,530 --> 00:25:51,590 For all of you guys that I lost on that previous slide of virtual to physical translation, I have a little bit clearer one that shows the whole page fault path. 246 00:26:06,160 --> 00:26:07,040 Okay. 247 00:26:07,340 --> 00:26:09,520 First thing, you have a memory access. 248 00:26:10,160 --> 00:26:16,000 So the first thing we do is we need to look it up in the page directory to resolve the actual page table. 249 00:26:16,320 --> 00:26:18,140 In this case, it's present. 250 00:26:18,140 --> 00:26:21,200 So we can move on and go out to the page table. 251 00:26:21,220 --> 00:26:24,980 Now, in the page table, we're trying to look up actually where the physical frame is. 252 00:26:25,180 --> 00:26:30,140 In this case, that particular PTE has been marked invalid, and a page fault is generated. 253 00:26:31,300 --> 00:26:33,900 This causes the page fault handler to be invoked. 254 00:26:33,900 --> 00:26:43,560 The page fault handler now is responsible for issuing the disk.io to go out to the page file and load that frame back into main memory. 255 00:26:43,740 --> 00:26:48,500 Once it's loaded back into memory, it now marks the PTE as valid. 256 00:26:52,180 --> 00:26:57,260 As you can see, some people would say that paging is not such a good idea. 257 00:26:57,440 --> 00:27:01,100 This is because there's this de-performance hint associated with paging. 258 00:27:01,100 --> 00:27:08,080 Basically, we take one memory reference and we basically turn it into three memory accesses. 259 00:27:08,080 --> 00:27:11,000 So we're cutting our performance by a factor of three. 260 00:27:11,360 --> 00:27:13,940 Basically, the first thing we have to access is the page directory. 261 00:27:13,940 --> 00:27:15,340 That's one memory reference. 262 00:27:15,340 --> 00:27:16,940 We have to access the page table. 263 00:27:16,940 --> 00:27:18,520 There's another memory reference. 264 00:27:18,620 --> 00:27:22,780 Now we have to actually go out and get the byte in the correct physical address. 265 00:27:22,780 --> 00:27:24,540 There's our third memory reference. 266 00:27:24,540 --> 00:27:32,720 So actually, in the best case, every single memory access requires three memory accesses to resolve the virtual to physical address. 267 00:27:32,940 --> 00:27:39,640 In the worst case, not only do we need three memory accesses, now we might potentially need two disk.ios. 268 00:27:39,680 --> 00:27:49,960 Because since we have a two-level paging scheme, our page tables may be non-present and our actual page frames may be non-present as well. 269 00:27:49,960 --> 00:27:53,220 So therefore, you may have up to two additional disk.ios. 270 00:27:53,220 --> 00:27:58,180 So as you can see, paging introduces a speed performance hit. 271 00:27:58,180 --> 00:28:04,500 Hardware designers found this to be unacceptable and they developed a solution for this to help mitigate this problem. 272 00:28:04,500 --> 00:28:07,620 Their solution was the translation look-aside buffer. 273 00:28:07,840 --> 00:28:20,000 The TLV is basically a high-speed hardware cache, generally implemented in associative memory, that holds the most frequently used virtual to physical mappings. 274 00:28:20,000 --> 00:28:27,260 So basically, the TLV caches the information held in the PTEs for certain pages that are used frequently. 275 00:28:28,820 --> 00:28:46,840 The TLV is actually a lot smaller than main memory, which means that it's possible that an entry in the TLV could actually be evicted if another page that maps to that TLV entry needs to be loaded into the TLV. 276 00:28:46,840 --> 00:28:51,140 So basically, the TLV is actually first on the memory access path. 277 00:28:51,140 --> 00:28:53,500 It sits in front of the page table path. 278 00:28:54,220 --> 00:29:00,700 So therefore, on the memory access, the TLV is actually searched first, before we ever even go out to the page tables. 279 00:29:01,260 --> 00:29:14,960 Since it is high-speed associative memory, that means that a reference found in the TLV can be resolved much, much faster than it can be resolved by even going out and doing a single memory reference in main memory. 280 00:29:14,960 --> 00:29:19,400 If that reference is actually found in the TLV, we say it was picked. 281 00:29:19,420 --> 00:29:22,380 If it was not found, we say that we had a miss. 282 00:29:22,940 --> 00:29:27,040 The x86 actually uses a split TLV architecture. 283 00:29:27,200 --> 00:29:30,100 And what I mean by that is there's actually two TLVs. 284 00:29:30,100 --> 00:29:38,480 There's one that holds translations for code, that's the iTLV, and there's another one that holds translations for data, that's the dTLV. 285 00:29:39,000 --> 00:29:53,160 I should note that modern TLVs have extremely high hit rates, which means that most translations are actually resolved via the TLV cache, as opposed to actually having to go out to the page tables, which significantly increases performance. 286 00:29:54,820 --> 00:30:00,380 This is just a simple example of a TLV search. 287 00:30:00,860 --> 00:30:03,140 Basically, the TLV is a cache. 288 00:30:03,140 --> 00:30:04,580 It consists of two parts. 289 00:30:04,580 --> 00:30:16,620 You have a tag, which in this case is going to be the virtual page number, and then you have the corresponding frame that maps to that virtual page number in the data section of the cache line. 290 00:30:16,700 --> 00:30:26,960 Basically, when a memory reference occurs, the TLV tags are going to be searched in parallel to see if they can find a match on that virtual page number. 291 00:30:27,020 --> 00:30:33,300 If they can, the data, or the corresponding physical frame mapping, is going to be returned. 292 00:30:33,300 --> 00:30:40,840 In this case, we had a TLV hit on virtual page 17, and its corresponding frame is 84. 293 00:30:43,040 --> 00:30:50,920 So, basically, here I have an illustration of the memory access path that includes a TLV. 294 00:30:51,820 --> 00:30:57,500 We're also going to stick with the x86 architecture, which actually has a split TLV. 295 00:30:57,500 --> 00:31:04,100 So that means that on a memory access, code accesses are going to be checked in the iTLV. 296 00:31:04,900 --> 00:31:08,960 So, in this case, we have a hit on the iTLV. 297 00:31:08,960 --> 00:31:15,320 We now have the corresponding frame, which is 132, and we can go directly out to physical memory. 298 00:31:15,600 --> 00:31:21,880 In this case, as you can see, since we had a TLV hit, we were able to completely bypass the page tables. 299 00:31:24,180 --> 00:31:27,220 It may be possible that we don't have a TLV hit. 300 00:31:27,220 --> 00:31:28,540 We could get a miss. 301 00:31:28,660 --> 00:31:37,140 And what's even worse than a miss, now that we have to go out to the page tables, now we might actually get a page fault, which will kill performance even further. 302 00:31:37,420 --> 00:31:40,640 So, in this case, our memory access is a code access. 303 00:31:41,000 --> 00:31:45,940 It goes out to the iTLV, searches it, it's not there, so now we have a miss. 304 00:31:46,520 --> 00:31:48,960 So, now we have to go out to the page tables. 305 00:31:49,240 --> 00:31:54,380 In this case, we go out to the page tables, and we find something we need is not present. 306 00:31:54,720 --> 00:31:56,540 So, we have a page fault. 307 00:31:56,540 --> 00:31:59,980 Now the OS page fault handler has to be invoked. 308 00:32:00,000 --> 00:32:11,900 It has to go out to the disk, bring that frame into memory, and finally mark that PTE as being present before the access can be completely resolved. 309 00:32:14,420 --> 00:32:18,580 So, basically, we have three memory access types. 310 00:32:18,580 --> 00:32:20,040 Everyone's familiar with these. 311 00:32:20,040 --> 00:32:22,520 We have read, write, and execute. 312 00:32:22,520 --> 00:32:30,080 Unfortunately, a little caveat to the x86 architecture is we actually only have two memory access types. 313 00:32:30,080 --> 00:32:33,980 We actually have read-execute and read-write-execute. 314 00:32:33,980 --> 00:32:38,380 By that, I mean the execute access is implied to all memory. 315 00:32:39,160 --> 00:32:41,500 This poses a slight problem. 316 00:32:41,500 --> 00:32:47,380 In some cases, we might like to actually differentiate between execute access and read-write access. 317 00:32:48,220 --> 00:32:55,580 One common place where this might be the case would be the implementation of non-executable memory. 318 00:32:55,580 --> 00:33:00,000 This is used as a buffer overflow protection scheme. 319 00:33:00,280 --> 00:33:05,060 Basically, you have your stack space, which is typically readable and writable. 320 00:33:05,060 --> 00:33:06,920 I mean, you store data on the stack. 321 00:33:06,920 --> 00:33:12,980 But normally, the stack does not contain executing code unless a buffer overflow attack is in progress. 322 00:33:12,980 --> 00:33:27,320 So, therefore, heuristically, if we were able to detect an execute access over a read-write access on the stack, we could say if we detect an execute, that heuristically, there's a good probability that we're in progress with buffer overflow attack. 323 00:33:27,960 --> 00:33:36,300 Now, the little caveat here is, like we said, x86 doesn't let us distinguish between read-write and execute. 324 00:33:36,560 --> 00:33:41,400 So, basically, we have not had hardware support up to this point. 325 00:33:41,400 --> 00:33:43,040 We do now have some hardware support. 326 00:33:43,040 --> 00:33:46,600 But previously, there was a project called PAX. 327 00:33:46,600 --> 00:33:54,600 Basically, PAX implemented read-write-no-execute or non-executable memory semantics using strictly software support. 328 00:33:54,600 --> 00:33:58,760 This was before there was any hardware support for non-executable memory. 329 00:33:59,120 --> 00:34:11,000 Windows XP Service Pack 2 and Server 2003 Service Pack 1 now also implement software support in the form of what Microsoft calls Data Execution Prevention or DEP. 330 00:34:11,460 --> 00:34:20,220 As a side note, hardware support has been added for non-executable memory onto 64-bit processors as well as the Pentium 4. 331 00:34:23,300 --> 00:34:35,520 Well, in the case of a rootkit, it's also kind of advantageous to be able to distinguish between executable and read-write access to a range of memory. 332 00:34:35,520 --> 00:34:44,120 Basically, we're going to take an offensive stance on the PAX technology, which, it's been around for a while. 333 00:34:44,120 --> 00:34:45,980 This technique's been around for a while. 334 00:34:45,980 --> 00:34:52,180 But so far, it hasn't really been shown how you could invert that technique for malicious purposes. 335 00:34:52,560 --> 00:34:55,480 So, basically, we want to hide code. 336 00:34:55,500 --> 00:34:58,620 So, we want to differentiate between read-write-and-execute. 337 00:34:58,620 --> 00:35:10,880 So, the idea is, if we have a read access of the code section of our rootkit driver, for example, that's going to be a very strong heuristic that a scanner's trying to locate us. 338 00:35:11,480 --> 00:35:16,680 So, basically, what we would want to do in that case is we want our rootkit code to run. 339 00:35:16,680 --> 00:35:31,320 But if a scanner tries to read it, we want to basically return an image of that memory that does not contain the rootkit code, or contains something clean, in the case of a modification to the operating system. 340 00:35:31,480 --> 00:35:37,840 So, basically, what we're proposing here is we're going to do an implementation that is the inverse of PAX. 341 00:35:37,840 --> 00:35:44,480 That is, it's an implementation of execute, allowed, but diverted read-write memory semantics. 342 00:35:47,220 --> 00:35:54,620 We're going to use the PAX technique that PAX discovered to implement non-executable memory. 343 00:35:54,620 --> 00:35:57,100 But, in this case, we're going to use it to hide a rootkit. 344 00:35:57,280 --> 00:36:03,880 So, like I said, the technique is not totally new, but the application is somewhat new. 345 00:36:04,280 --> 00:36:07,380 So, basically, there's three implementation issues here. 346 00:36:07,700 --> 00:36:11,280 First, we need a way to filter, execute, and read-write accesses. 347 00:36:11,280 --> 00:36:12,360 Step one. 348 00:36:12,360 --> 00:36:16,980 Second, we need a way to fake the read-write accesses once we detect them. 349 00:36:17,240 --> 00:36:22,020 Obviously, we don't want the scanner to receive the correct data when it reads our rootkit memory. 350 00:36:22,280 --> 00:36:26,280 Lastly, we need to ensure that performance is not adversely affected. 351 00:36:26,780 --> 00:36:36,300 Obviously, this would not be a viable solution if, when we install our rootkit, the system administrator notices that his machine is suddenly running really, really slow. 352 00:36:38,420 --> 00:36:53,020 So, to solve the first problem, basically, we said earlier that if we mark the PTE as invalid, the next memory access that occurs to that page is going to generate a page fault. 353 00:36:53,260 --> 00:37:05,820 So, basically, that means that we can trap memory accesses by marking their page table entries as invalid, and basically hooking the page fault handler and replacing the operating system's page fault handler. 354 00:37:06,460 --> 00:37:11,880 In the actual page fault handler, we have access to some interesting information. 355 00:37:12,140 --> 00:37:14,460 We have the saved instruction pointer. 356 00:37:14,460 --> 00:37:18,700 We also have the virtual address where the fault occurred. 357 00:37:18,860 --> 00:37:25,820 So, therefore, if our instruction pointer equals the address of where the fault occurred, we can say that this was an execute access. 358 00:37:25,860 --> 00:37:28,820 Otherwise, it's going to be a read-write access. 359 00:37:29,440 --> 00:37:43,260 There's one important thing to note here, and that is the fact that our memory hook for the rootkit has to work safely with the operating system's memory management, because the operating system's resolving page faults all the time. 360 00:37:43,260 --> 00:38:01,220 So, that means, in practical terms, that if we're going to mark pages not present to capture memory accesses, then we also must be able to distinguish whether those memory accesses are a result of someone trying to access the hidden page, or a result of normal operating system paging activity. 361 00:38:01,380 --> 00:38:05,660 In order to satisfy that, we actually impose two constraints. 362 00:38:05,920 --> 00:38:10,880 The first constraint could be that pages that are hidden are required to be a non-paged memory. 363 00:38:10,880 --> 00:38:15,060 That means there's no possibility that the OS could page that file out to the disk. 364 00:38:15,640 --> 00:38:22,160 The second constraint could be, if you're using pageable memory, that you use an operating system API to lock it down. 365 00:38:24,620 --> 00:38:34,520 So, once you're able to detect execute from read-write, well, now comes the issue of, well, how are we actually going to fake the read-write accesses? 366 00:38:34,660 --> 00:38:48,660 So, basically, this is the technique that was shown in PAX, that it used to implement the inverse of what we're going to talk about, and that is, we're going to desynchronize the hardware caches, the TLBs. 367 00:38:48,660 --> 00:38:51,300 Like I said, the x86 has two of them. 368 00:38:51,300 --> 00:38:54,540 It has one for code, which is searched on execute. 369 00:38:54,540 --> 00:38:57,280 It has one for data, which is searched on read-write. 370 00:38:57,500 --> 00:39:11,500 So, therefore, a memory access, under normal situation, the ITLB is going to contain virtual page number and corresponding physical frame, and the DTLB is going to contain the same thing. 371 00:39:11,500 --> 00:39:16,220 So, in this case, for virtual page number 12, we see it maps to frame 2. 372 00:39:16,220 --> 00:39:19,440 For the DTLB, it also maps to frame 2. 373 00:39:19,440 --> 00:39:25,440 Under normal situations, read-write and execute, they all map to the same physical frame. 374 00:39:27,400 --> 00:39:30,300 However, what if we could desynchronize them? 375 00:39:30,300 --> 00:39:37,820 What if we could have the ITLB to actually map off to a different physical frame than the DTLB? 376 00:39:37,820 --> 00:39:55,200 So, basically, the idea here is that the ITLB contains a pointer to a frame which contains the rootkit code, and the DTLB contains a pointer to the frame which either has a clean copy of something or some random garbage, which is not the rootkit code. 377 00:39:56,160 --> 00:40:08,960 So, basically, since all these translations are performed at the hardware level, this is going to be transparent to the application program, to the kernel driver, or even to the operating system. 378 00:40:09,460 --> 00:40:16,860 So, basically, once we've decided that we're going to desynchronize the TLBs, the question is, well, how do you actually do this? 379 00:40:18,240 --> 00:40:21,060 TLBs are almost entirely hardware controlled. 380 00:40:21,400 --> 00:40:28,420 However, there is a little bit of support for software control, which, in this case, happens to be enough support to implement this. 381 00:40:28,420 --> 00:40:32,400 First of all, we have the capability to flush the TLB. 382 00:40:32,860 --> 00:40:37,780 Basically, we can flush the entire TLB by reloading register CR3. 383 00:40:37,780 --> 00:40:40,680 This normally occurs on a context switch. 384 00:40:40,700 --> 00:40:51,140 On a context switch, typically, CR3 will be reloaded, which ends up flushing the entire TLB, except for global entries, which will remain in the TLB. 385 00:40:51,140 --> 00:40:58,940 This is mainly a performance enhancement for operating system components, which will remain resident at all times. 386 00:40:59,780 --> 00:41:06,920 Also, we also have a method of not only flushing the entire TLB, but of flushing an individual entry in the TLB. 387 00:41:06,940 --> 00:41:09,840 That would be the invalidate page instruction. 388 00:41:10,800 --> 00:41:15,400 So, now that we've established that we can flush the TLB, how about loading it? 389 00:41:16,300 --> 00:41:26,640 It is possible, actually, to load the TLBs separately, which means that actually executing a data access loads the DTLB, but not the ITLB. 390 00:41:27,040 --> 00:41:32,520 Conversely, executing a call loads the ITLB, but not the DTLB. 391 00:41:32,520 --> 00:41:38,740 So, therefore, it is possible to discretely load the two TLBs separately and desynchronize them. 392 00:41:40,760 --> 00:41:46,420 So, basically, our proof-of-concept implementation consists of two main components. 393 00:41:46,420 --> 00:42:04,100 We have the memory hook engine, which has a hook installation component, which sets up the hook, and then a custom page fault handler, which basically is responsible for filtering execute read-write accesses and correctly manually loading up the right TLB. 394 00:42:04,100 --> 00:42:09,320 Also, our second component is going to be a modified FU rootkit. 395 00:42:09,340 --> 00:42:16,700 So, basically, we've modified Jamie's rootkit to work in collusion with our ShadowWalker. 396 00:42:19,220 --> 00:42:24,880 So, basically, on memory hook installation, there are several steps that have to be performed. 397 00:42:24,960 --> 00:42:30,560 First of all, since we're going to mark pages not present, we need to install a new page fault handler. 398 00:42:31,180 --> 00:42:49,120 Once we've done that, on a page fault, we're going to need to be able to determine, relatively quickly, if that fault is due to one of our hidden pages being accessed, or if it's due to the actual operating system performing normal paging activity. 399 00:42:49,120 --> 00:42:55,320 So, in order to do that, we just insert that page into a simple hash table, so we can perform a quick lookup. 400 00:42:55,900 --> 00:42:58,460 Next, we mark the page not present. 401 00:42:59,580 --> 00:43:15,140 And then the last step is really the most important here, because we said that the TLB is first on the memory access path, which means that a page fault is generated, one, when the PTD is marked present, but like I said earlier, two, when that entry is not present in the TLB. 402 00:43:15,140 --> 00:43:19,480 So, therefore, we perform an explicit flush of that page that we're hiding. 403 00:43:19,500 --> 00:43:24,820 This means that on the next memory access, we will be able to generate a page fault. 404 00:43:25,420 --> 00:43:28,840 If you could please hold your questions to end, we'll have time for questions. 405 00:43:32,910 --> 00:43:41,150 So, basically, the custom page fault handler's main responsibility is to filter, read, write, and execute accesses for hooked pages. 406 00:43:41,630 --> 00:43:49,390 If it determines that the page is not a hooked page, meaning it's just a normal operating system page fault, it passes it down to the OS handler. 407 00:43:50,070 --> 00:43:59,850 If it is a page fault due to a hidden page, it's either going to manually load the ITLB or manually load the DTLB, depending upon the type of access. 408 00:44:00,630 --> 00:44:04,390 Most memory references will be resolved via the TLB path. 409 00:44:04,390 --> 00:44:09,750 So, intuitively, you would think that you would be generating a lot of page faults, it would take a big hit in performance. 410 00:44:09,750 --> 00:44:13,890 Not the case, because we're using the hardware caching mechanism. 411 00:44:13,890 --> 00:44:16,170 We're just simply synchronizing it. 412 00:44:16,650 --> 00:44:26,230 So, basically, we will still have page faults, but page faults are not as many as you would think, since the TLB path will handle most of the memory references. 413 00:44:26,770 --> 00:44:32,250 Page faults on the hooked pages will, of course, occur on the first execute or data access to the page. 414 00:44:32,250 --> 00:44:36,590 Remember, we had to flush it, flush the TLB, when we instantiated the hook. 415 00:44:36,830 --> 00:44:39,970 It will occur on the TLB cache line eviction. 416 00:44:39,970 --> 00:44:46,990 So, it's possible that the mapping for that hooked page got evicted from the TLB. 417 00:44:46,990 --> 00:44:57,090 Well, the next time that a memory access occurs, it will now generate a page fault, which the page fault handler will now reload the correct mapping into the TLB, and that memory access will go through. 418 00:44:58,370 --> 00:45:06,750 Likewise, the last case would be on an explicit TLB flush context switch, if the global bit has not been enabled. 419 00:45:09,090 --> 00:45:15,430 So, basically, here we have some really rudimentary page fault handler pseudocode for what's going on. 420 00:45:15,990 --> 00:45:20,570 So, basically, we are only hiding kernel pages right now. 421 00:45:20,570 --> 00:45:23,930 So, we're going to disregard user land accesses. 422 00:45:23,930 --> 00:45:30,310 So, in the page fault handler, we want this to be as efficient as possible, so we don't want to tie things up if we're not interested in handling this fault. 423 00:45:30,330 --> 00:45:33,990 So, the first thing we do is we just check if the processor mode is in user mode. 424 00:45:33,990 --> 00:45:35,410 If it is, we pass it down. 425 00:45:35,410 --> 00:45:37,110 We're not dealing with user mode. 426 00:45:37,270 --> 00:45:43,490 If the faulting address is a user page, we're also going to pass it down, because we don't hook user pages. 427 00:45:43,990 --> 00:45:50,470 In contrast, if the fault is from a hidden page, now we're going to check if it's a read-write or an execute. 428 00:45:51,010 --> 00:45:55,570 If it's an execute, we're going to jump over to load the ITLB. 429 00:45:55,690 --> 00:45:59,950 If it's a data access, we're going to jump over to the load DTLB code. 430 00:46:00,610 --> 00:46:15,630 Basically, in the load ITLB, what we're going to do is we're going to basically replace that physical frame information in the PTE with the mapping for the actual physical frame that contains the rootkit code, because we want it to execute. 431 00:46:15,890 --> 00:46:21,950 We're going to temporarily mark that PTE as present to allow the memory access to go through. 432 00:46:22,030 --> 00:46:26,530 We're going to perform a call into the page, which is going to load up the ITLB. 433 00:46:26,530 --> 00:46:32,670 I should note here, I forgot to put this on the previous page, that we do have to perform a call. 434 00:46:32,790 --> 00:46:40,850 Basically, that means that we need to find an empty byte in our given page that we can insert a return-off code to. 435 00:46:41,310 --> 00:46:52,810 In practice, if you're going to type sections, say in a driver, those are PE sections, so therefore there's going to be some alignment, so therefore there's normally some empty space in there. 436 00:46:52,810 --> 00:46:56,850 So you shouldn't really have trouble finding one byte that you can just patch a return into. 437 00:46:57,390 --> 00:47:10,590 So therefore, basically we just make a call to this point in the page where the return has been patched, and it returns control back to the pagefault handler, and in process, loads up the ITLB. 438 00:47:10,890 --> 00:47:17,310 Finally, the PTE is marked as non-present again, so we can catch subsequent accesses. 439 00:47:17,310 --> 00:47:30,910 We also replace the frame back with the original innocent-looking frame, and we go ahead and we return back to the faulting application, or driver as the case may be, without passing down to the OS. 440 00:47:30,910 --> 00:47:33,550 The OS doesn't need to have any knowledge of this default. 441 00:47:34,170 --> 00:47:43,930 If it's a DTLB load, basically the steps are a little bit simpler, because by default, we're going to maintain the clean mapping in the PTE. 442 00:47:43,930 --> 00:47:50,690 So therefore, we just mark it present, perform a read access, and then mark it non-present again. 443 00:47:50,950 --> 00:47:54,210 And then, of course, return without passing down to the OS. 444 00:47:56,830 --> 00:48:02,970 So, basically all this information so far concerns how you would hide executable code. 445 00:48:08,800 --> 00:48:13,460 Okay, we're running out of time, so I'm going to go ahead and skip this section on data hiding. 446 00:48:13,460 --> 00:48:18,260 This slide should be available probably on rootkit.com within the next week or so. 447 00:48:20,320 --> 00:48:24,120 So basically, I'm going to move ahead and we're just going to talk a little bit about the rootkit here. 448 00:48:24,560 --> 00:48:30,640 The rootkit is, of course, the object that we are implementing this process on to actually hide it. 449 00:48:30,780 --> 00:48:32,100 It's proof of concept. 450 00:48:32,100 --> 00:48:33,520 It doesn't really do anything malicious. 451 00:48:33,520 --> 00:48:35,080 It just hides processes. 452 00:48:35,700 --> 00:48:37,600 It runs as a system thread. 453 00:48:37,880 --> 00:48:45,500 It has no dependence upon user land initialization, which means it has no symbolic link or no functional device in the driver. 454 00:48:45,920 --> 00:48:54,720 And also, basically, we envision this rootkit as being an in-memory rootkit, meaning that ideally it would be installed via a kernel-level exploit. 455 00:48:54,940 --> 00:49:04,060 Obviously, if this rootkit driver is sitting on the disk, we open up a whole new can of worms for detection and basically invalidate all these faulty things that we're trying to do. 456 00:49:04,140 --> 00:49:06,880 So basically, we never want it to reside on the disk. 457 00:49:06,880 --> 00:49:13,580 This is a perfectly reasonable thing if we consider attacking, say, a server system, which is infrequently rebooted. 458 00:49:13,580 --> 00:49:19,820 Chances are high that this rootkit will sit in memory, and by the time we come back, it'll still be there. 459 00:49:22,280 --> 00:49:25,660 So, lastly, the impact on system performance. 460 00:49:25,780 --> 00:49:28,660 Like we said, modern TLBs have high hit rates. 461 00:49:28,800 --> 00:49:39,020 That means most translations will be resolved via the TLB and not the page fault path, which means that we're going to have very little performance hit. 462 00:49:39,040 --> 00:49:59,620 I can't speak from a performance metric point of view here, because we haven't really done any rigorous testing on performance, but I can say that, subjectively speaking, hiding Jamie's rootkit with this ShadowWalker memory hook engine, there is no subjective, 463 00:49:59,620 --> 00:50:01,960 noticeable performance impact. 464 00:50:02,560 --> 00:50:07,940 Clearly, the performance impact will probably vary depending upon the number of pages you're having to hide. 465 00:50:08,060 --> 00:50:12,760 In this case, hiding a kernel driver, how many pages do you really have? 466 00:50:12,760 --> 00:50:19,600 You have four or five sections in a PE, portable executable, so chances are you only have four or five pages. 467 00:50:19,600 --> 00:50:23,600 If they're page-aligned, four or five pages is really negligible. 468 00:50:24,220 --> 00:50:29,180 So, for small numbers of pages, we haven't observed any performance impact at all. 469 00:50:31,200 --> 00:50:37,120 As this is not really a weaponized attack tool, there are a number of known limitations. 470 00:50:37,660 --> 00:50:41,240 Jamie and I have done this basically on our spare time. 471 00:50:41,320 --> 00:50:47,320 So , basically, we've not implemented a fully productized or weaponized version. 472 00:50:47,480 --> 00:50:50,260 So, first of all, there's no PAE support. 473 00:50:50,260 --> 00:50:55,620 PAE basically extends addressability from 32 to 36 bits. 474 00:50:55,620 --> 00:50:57,340 We don't support that yet. 475 00:50:57,340 --> 00:51:00,600 There's no hyper-threading or multi-processor support. 476 00:51:00,600 --> 00:51:07,680 There's clear issues with regards to synchronization problems involved with porting this up to a multi-processor system. 477 00:51:07,680 --> 00:51:09,500 We've not addressed those yet. 478 00:51:09,600 --> 00:51:13,840 Currently, we only hide 4K-sized kernel pages. 479 00:51:14,000 --> 00:51:17,680 We are theoretically capable of hiding 4MB pages. 480 00:51:17,680 --> 00:51:22,320 The x86 architecture actually has two page sizes, 4K and 4MB. 481 00:51:22,320 --> 00:51:29,660 The main point of interest with the 4MB pages are going to be hiding of NTOS kernel, which is sitting on a 4MB page. 482 00:51:29,660 --> 00:51:36,020 Obviously, an interest to a rootkit developer who wants to install hooks into the operating system kernel. 483 00:51:36,080 --> 00:51:38,340 Currently, we can't hide NTOS kernel. 484 00:51:38,340 --> 00:51:42,400 There's some technical difficulties with that, which we're working to resolve. 485 00:51:42,580 --> 00:51:50,160 But it's quite effective for hiding drivers and other drivers, such as maybe Indus, which sit in the 4K page range. 486 00:51:50,160 --> 00:51:53,900 So we could hide the hooks in the network drivers, for example. 487 00:51:55,800 --> 00:51:58,980 As far as detection, I'm going to go over this real, real brief. 488 00:51:58,980 --> 00:52:01,140 I think we're really running out of time here. 489 00:52:01,480 --> 00:52:15,460 First of all, like we said earlier, we have to make the constraint that we have non-paged memory, that we're marking non-present, so we can differentiate our page faults from OS page faults. 490 00:52:15,460 --> 00:52:20,320 Obviously, non-present pages in non-paged memory are heuristically abnormal. 491 00:52:20,340 --> 00:52:24,800 If that's sufficient to indicate that a rootkit's installed, highly debatable. 492 00:52:24,800 --> 00:52:26,400 But it's still abnormal. 493 00:52:27,000 --> 00:52:36,080 Obviously, the page fault handler code itself cannot be marked not present, because that would imply that we're marking the page containing the page fault handler as not present. 494 00:52:36,080 --> 00:52:40,040 We would end up with lots of recursive problems of recursive re-entry. 495 00:52:40,040 --> 00:52:45,520 Therefore, the solution for hiding the page fault handler, we have to fall back to more tried-and-true approaches. 496 00:52:45,760 --> 00:52:53,800 It's small, it's written in assembly, there's no APIs, so polymorphism seems like a fairly reasonable solution to that problem. 497 00:52:54,380 --> 00:52:57,560 Likewise, it's also difficult to conceal hooks on the IDT. 498 00:52:57,560 --> 00:53:01,580 Currently, this proof of concept makes no effort to conceal the IDT hooks. 499 00:53:01,660 --> 00:53:06,980 Yes, this rootkit is very easily detected right now because of this, but that was not really our goal. 500 00:53:06,980 --> 00:53:09,600 We're just showing a proof of concept. 501 00:53:11,000 --> 00:53:17,420 Right now, we're really running short, so I'm going to proceed to the demo and skip the last couple slides. 502 00:53:27,090 --> 00:53:36,870 I know we're late, so if anyone has any other engagements, feel free to head on out, but I am going to go ahead and perform the demo for anyone that's interested. 503 00:53:39,330 --> 00:53:41,650 I'm going to do a GUI version of this demo. 504 00:53:41,650 --> 00:53:47,470 I have a hardcore Softice version if anyone wants to see after the talk. 505 00:53:51,660 --> 00:53:54,380 Basically, the first thing I'm going to do is... 506 00:53:54,380 --> 00:53:56,360 Right now, I'm using Driver Monitor. 507 00:53:56,360 --> 00:53:58,760 It's a tool with the Softice driver suite. 508 00:53:58,760 --> 00:54:01,000 It allows you to load a kernel driver. 509 00:54:01,640 --> 00:54:06,160 The first thing I'm going to do is I'm going to go ahead and load up Jamie's rootkit. 510 00:54:14,150 --> 00:54:16,590 Our driver has started successfully. 511 00:54:16,590 --> 00:54:19,830 Now, if we enter Softice... 512 00:54:23,490 --> 00:54:27,950 we can get the base address of where this driver is loaded in memory. 513 00:54:43,470 --> 00:54:47,430 This is the virtual address of where this driver is loaded in memory. 514 00:54:47,430 --> 00:54:49,110 We're going to go ahead and dump it. 515 00:54:49,110 --> 00:54:50,810 Dump the memory at that address. 516 00:54:51,350 --> 00:54:56,370 For all of you guys that use hex dumps all the time, this should look pretty familiar. 517 00:54:56,370 --> 00:55:02,030 This 4D5A, that's simply the MZ signature inside the PE header. 518 00:55:02,390 --> 00:55:07,450 Right now, what we're seeing is the PE header of this rootkit driver loaded in memory. 519 00:55:08,430 --> 00:55:15,350 At this point, I'm going to go ahead and load up the memory hook engine, ShadowWalker. 520 00:55:28,400 --> 00:55:30,860 It started up successfully. 521 00:55:32,300 --> 00:55:35,600 Now, we're going to go ahead and perform another memory dump. 522 00:55:37,780 --> 00:55:40,180 Now, it's gone. 523 00:55:47,740 --> 00:55:50,400 So, we'll take questions offline. 524 00:55:50,400 --> 00:55:51,540 We're leaving the room. 525 00:55:51,540 --> 00:55:52,800 We're being evicted. 526 00:55:52,960 --> 00:55:54,800 We have swag books. 527 00:55:54,860 --> 00:55:55,880 We'd love to hear from you. 528 00:55:55,880 --> 00:55:56,840 Thanks a lot.