WEBVTT

00:00.000 --> 00:04.560
It will just be a minute more as we get self-paced running and things like that in our presentation.

00:06.300 --> 00:10.700
The presentation was originally developed for an hour and 30 minutes.

00:10.800 --> 00:16.180
However, this time slot is an hour and 50, I mean 50 minutes and we've already filled like 7.

00:16.700 --> 00:22.040
Therefore, we may run a little bit late if we need to leave early.

00:23.900 --> 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.

00:31.520 --> 00:36.860
And Sherry does the interesting slides, so I will introduce her soon.

00:37.000 --> 00:38.740
My name is Jamie Butler.

00:38.740 --> 00:40.400
I'm from Rootkid.com.

00:40.400 --> 00:42.080
This is Sherry Sparks.

00:42.140 --> 00:47.780
And we're developing a project called Shadow Walker and it's to hide rootkits.

00:48.300 --> 00:53.580
You can find a description of this talk probably later on Rootkid.com.

00:53.580 --> 00:54.340
It's in the source code.

00:54.340 --> 00:55.840
I believe Sherry has proposed it.

00:55.840 --> 00:58.580
Also, it will be in FRAG 63.

00:59.200 --> 01:04.160
If it hasn't appeared online already, probably in the next week or two it will be there.

01:04.180 --> 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.

01:12.020 --> 01:16.300
We post what's there for free and there's a lot of source code for free.

01:16.640 --> 01:19.120
Nothing there is for free.

01:19.120 --> 01:26.580
Or you can apply for a rootkit supporting the Windows kernel by Greg Kogel and myself.

01:27.100 --> 01:28.980
So we'll get started with Shadow Walker.

01:32.820 --> 01:34.540
So a quick flow through.

01:34.620 --> 01:37.300
Rootkits, they don't propagate themselves.

01:37.300 --> 01:38.100
They're not like a virus.

01:38.100 --> 01:39.760
They're just here to provide stuff.

01:39.860 --> 01:43.960
So the attacker gets on the box and they try to maintain presence.

01:44.040 --> 01:48.160
They don't want to keep using their zero day to come back all the time to the machine.

01:48.160 --> 01:49.920
So they install a rootkit.

01:50.060 --> 01:56.440
A rootkit is there to hide their presence and also to give them future access at a later date.

01:56.760 --> 02:03.840
Rootkits can also be used for law enforcement for things like a sneak and peek or a properly executed search warrant.

02:04.640 --> 02:11.320
There's two levels of protection within the Windows and Linux operating system.

02:11.320 --> 02:14.660
There's basically two rings, ring 0 and ring 3.

02:14.660 --> 02:18.460
Ring 3 is user land processes.

02:18.600 --> 02:20.260
They are unprivileged.

02:20.260 --> 02:21.960
Ring 0 is kernel land.

02:22.420 --> 02:27.760
Once you're in ring 0, your kernel rootkit can do whatever you want.

02:28.820 --> 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.

02:39.980 --> 02:42.420
So I want to open a port.

02:42.420 --> 02:44.340
The operating system does that for you.

02:44.340 --> 02:47.360
And then hands it back to the user land process.

02:47.360 --> 02:53.860
They also provide an interface between user land processes and the lower hardware.

02:54.180 --> 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.

03:03.700 --> 03:09.800
Some of the different OS components that a rootkit may want to attack would be the IOManager.

03:09.800 --> 03:11.680
You can install keyloggers.

03:11.820 --> 03:16.920
Sharing has a project on rootkit.com that's a keyboard sniffer.

03:17.400 --> 03:20.040
And you can also attack the file system.

03:20.060 --> 03:26.700
You can return file filter driver and interface between every single access of the hard drive.

03:26.700 --> 03:34.560
The object manager has things like processes and threads that it uses to keep bookkeeping.

03:34.620 --> 03:40.400
And you can attack the object manager in order to hide your processes and threads.

03:42.420 --> 03:44.900
That's enough of that configuration manager.

03:44.900 --> 03:46.640
You can use that to hide registries.

03:46.760 --> 03:49.500
So first generation of rootkits, what were they?

03:49.500 --> 03:55.320
These were simple infringement replacement programs on the hard drive.

03:57.060 --> 04:04.360
They may include the Unix login program, LS, ES, so on and so forth.

04:05.040 --> 04:07.160
Second generation rootkits.

04:07.160 --> 04:09.720
These started to be more sophisticated.

04:10.220 --> 04:16.740
These modified static tables within the kernel are within the upper levels of the operating system themselves.

04:16.740 --> 04:25.600
So you can replace the import address table of a PE file and alter the execution path of any command.

04:25.600 --> 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.

04:38.840 --> 04:41.560
Third generation rootkits.

04:41.560 --> 04:48.260
We term these... they use DCOM, direct kernel object manipulation.

04:48.540 --> 04:53.940
These actually don't hook anything because hooks are a bit easier to detect.

04:53.940 --> 04:58.380
Third generation actually manipulate the data structures in memory.

04:58.380 --> 05:04.900
So there's no code... you don't alter the code at all, you just alter the data structures themselves.

05:05.000 --> 05:07.260
This is almost undetectable.

05:07.260 --> 05:10.880
Data structures come and go very frequently and they're constantly changing.

05:10.880 --> 05:12.880
That's why they're in the data structure.

05:14.900 --> 05:17.460
This is very hard to detect.

05:17.460 --> 05:20.960
And we'll talk about how you can use this to hide processes and threads if you wanted.

05:21.080 --> 05:27.980
Or you can use it to hide drivers, escalate process privileges using tokens, etc.

05:29.060 --> 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.

05:37.200 --> 05:40.080
I also mentioned a little bit last year when I talked about Python.

05:40.960 --> 05:47.460
What the kernel uses to keep track of all the processes is this list of active processes.

05:47.880 --> 05:53.220
Every e-process block you see there represents an individual process in the system.

05:53.500 --> 05:56.760
This individual process could be your little hacker program.

05:56.960 --> 06:00.860
It's doing things and you don't want the system administrator to know you're there.

06:01.580 --> 06:06.880
If you simply unlink... at the bottom of the slide you see that there's a series of links.

06:06.880 --> 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.

06:14.100 --> 06:17.900
So when you run task manager, etc., it won't be there.

06:20.420 --> 06:24.740
Now, current rootkit detection methods, there's basically four types.

06:24.740 --> 06:26.680
You don't have time to go into all these.

06:26.820 --> 06:31.640
But just quickly, behavioral, integrity, signature-based, and difference-based.

06:32.560 --> 06:36.720
Some of the AB products are basically an example of signature-based.

06:36.740 --> 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.

06:44.960 --> 06:47.440
Difference-based approaches is kind of a new thing.

06:48.080 --> 06:49.060
Microsoft's doing it.

06:49.060 --> 06:50.620
F-Secure's doing it.

06:51.100 --> 06:51.980
Sysinternals.

06:52.320 --> 06:58.200
What this is, is it does some kind of low-level scan within the kernel by following structures directly.

06:58.200 --> 07:02.540
It tries to get a list of things like files and processes, registry keys, etc.

07:02.840 --> 07:05.060
Then it does an upper-level API call.

07:05.060 --> 07:07.500
If there's a difference, it knows something's up.

07:10.580 --> 07:11.880
Behavioral detection.

07:12.420 --> 07:15.800
In this, you try to detect the presence, the effects of rootkit.

07:15.800 --> 07:22.920
So you might want to look for alteration in the execution path, or what is actually executed within the program.

07:22.920 --> 07:25.600
Some of the system calls, or the ordering, etc.

07:26.080 --> 07:28.780
We don't have time to go into more of that.

07:28.800 --> 07:30.080
Integrity checking, exactly.

07:30.080 --> 07:31.300
It's a CRC.

07:31.380 --> 07:33.340
This could be on-disk or in-memory.

07:33.380 --> 07:35.960
However, hardly anyone is doing it in-memory.

07:37.260 --> 07:45.380
Signature-based scan memory or on-disk for a particular set of bytes that represents the mal code.

07:47.440 --> 07:52.180
So, we have a really powerful way to hide rootkit, or hide processes, etc.

07:52.180 --> 07:55.100
within the kernel by modifying all these data structures.

07:55.840 --> 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.

08:08.860 --> 08:11.020
And that's not very good.

08:11.360 --> 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.

08:22.520 --> 08:26.720
You want to make it so you can't detect our rootkit within the kernel.

08:27.140 --> 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.

08:34.640 --> 08:42.100
However, in the kernel, it gets more problematic to do really good polymorphism code.

08:42.100 --> 08:50.280
Also, no one's doing it other than a holy father and his hacker defender.

08:50.480 --> 08:52.680
And you have to get him to handle that.

08:53.020 --> 08:55.840
And I think it's like 40 heroes in the county does it.

08:56.380 --> 09:03.460
So, really quickly, we're going to walk through how we deploy this in memory and share it with people.

09:09.910 --> 09:11.270
Thank you, Jamie.

09:11.270 --> 09:19.370
I'm going to be discussing the technical implementation of ShadowWalker, which is our rootkit hiding technology.

09:19.690 --> 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.

09:29.130 --> 09:31.330
Viruses need to hide their code.

09:31.610 --> 09:37.890
Rootkits not only need to hide their code, but they also need to hide changes to operating system components.

09:38.870 --> 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.

09:49.870 --> 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.

10:01.730 --> 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.

10:11.570 --> 10:16.930
Basically, the alternative to polymorphism is virtual memory subversion.

10:18.550 --> 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.

10:34.730 --> 10:39.950
Now, the cool thing about this technology is the fact that there's a minimal performance impact.

10:40.270 --> 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.

10:50.570 --> 10:53.910
It has no slowdown or system performance hit.

10:53.910 --> 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.

11:06.250 --> 11:09.930
Just a few implications of virtual memory subversion.

11:10.030 --> 11:16.690
For a long time, rootkit scanners relied upon the integrity of the operating system API.

11:16.910 --> 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.

11:34.030 --> 11:38.250
So basically, rootkit scanners have gotten a little bit smarter.

11:38.250 --> 11:44.650
They're starting to realize that they can't rely upon operating system APIs anymore.

11:44.650 --> 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.

11:53.810 --> 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.

12:02.250 --> 12:09.250
What we're proposing is actually to return some data that is, in fact, not actually stored at that address.

12:09.530 --> 12:12.950
So basically, there are two implications here.

12:13.010 --> 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.

12:23.730 --> 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.

12:36.570 --> 12:39.310
We can also fool integrity checkers.

12:39.310 --> 12:48.330
Those kind of checkers, like Jamie Butler, presented a talk on a tool called Vice, which basically attempts to detect hooks.

12:48.330 --> 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.

13:00.010 --> 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.

13:10.510 --> 13:16.090
The normal code would be such stuff as stack setup code, allocation of local variables.

13:16.390 --> 13:22.430
So basically, Jamie uses heuristics like this to determine if functions are hooked.

13:22.430 --> 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.

13:44.440 --> 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.

13:51.100 --> 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.

14:01.460 --> 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.

14:08.660 --> 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,

14:26.620 --> 14:28.420
or the translation look-aside buffer.

14:28.880 --> 14:36.120
Finally, we'll talk about different memory access types, and some little eccentricities with the x86 architecture.

14:38.240 --> 14:47.060
So basically what you have here is a layout of the two most common layouts for the Windows virtual address space.

14:47.060 --> 14:52.060
The first one breaks the virtual address space up into two main components.

14:52.060 --> 14:56.820
Like Jamie said, we have two modes of operation under Windows.

14:56.820 --> 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.

15:03.380 --> 15:06.880
The memory address space is arranged similarly to that.

15:07.240 --> 15:11.600
The lower two gigabytes correspond to user-in address space.

15:11.600 --> 15:16.520
That's going to contain your application code, DLL code.

15:16.520 --> 15:20.940
The upper two gigabytes is going to be where the operating system kernel is.

15:21.220 --> 15:28.280
In the upper two gigabytes, you're going to have N2S kernel, PAL, hardware abstraction layer, boot drivers.

15:28.280 --> 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.

15:43.080 --> 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.

15:56.460 --> 16:01.560
By far the most common layout is going to be the one where it's split in half.

16:03.680 --> 16:07.640
So, basically, what is the main idea behind virtual memory?

16:07.800 --> 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.

16:17.420 --> 16:22.580
By that, what I mean is the virtual address space is the size of it.

16:22.580 --> 16:26.880
It's going to be defined by the width of your memory bus.

16:26.880 --> 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.

16:38.680 --> 16:43.520
So, therefore, your virtual address space spans from zero up to four gigabytes.

16:44.000 --> 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.

16:49.760 --> 16:56.100
So, therefore, we can say that our physical address space is constrained by the amount of RAM on our system.

16:56.100 --> 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.

17:04.240 --> 17:12.460
So, basically, virtual memory concerns managing these virtual and physical address spaces by dividing them into fixed-size blocks.

17:12.740 --> 17:16.420
If those blocks are all the same size, we have a paging architecture.

17:16.420 --> 17:20.340
If those blocks may be variable sizes, it's a segmentation architecture.

17:20.920 --> 17:25.460
The x86 architecture is actually a combination of segmentation and paging.

17:25.460 --> 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.

17:33.840 --> 17:42.260
As far as how this maps, there's actually mapping information that actually maps a virtual block to a physical block.

17:42.260 --> 17:49.220
And the OS is what actually maintains this mapping information and determines which virtual blocks map to which physical blocks.

17:49.860 --> 17:54.240
Like I said before, the virtual address space may be larger than the physical address space.

17:54.240 --> 18:01.500
And also because the OS is managing this mapping, virtually contiguous memory blocks do not need to be physically contiguous.

18:03.400 --> 18:07.920
This actually shows an illustration of those two points on the previous slide.

18:07.920 --> 18:11.860
As you can see, we have our virtual address space divided up into blocks.

18:11.880 --> 18:15.340
Under a paging architecture, we call these blocks pages.

18:15.700 --> 18:19.120
The physical address space is smaller than the virtual address space.

18:19.120 --> 18:22.180
It's also divided into fixed-size blocks.

18:22.180 --> 18:24.120
This is termed frames.

18:26.060 --> 18:33.380
Basically, as you can see, the virtually contiguous blocks do not necessarily have to be physically contiguous.

18:35.560 --> 18:41.300
So, what actually maintains this mapping information that the OS uses?

18:41.340 --> 18:44.490
This mapping information is contained in page tables.

18:44.880 --> 18:51.000
And page tables consist of entries called PTEs, or page table entries.

18:51.000 --> 18:55.260
Page table entries contain at least two pieces of useful information.

18:55.680 --> 19:00.160
They contain status information, and they contain mapping information.

19:00.320 --> 19:08.500
The page frame number bits are the bits that actually describe where that page actually maps to in physical memory.

19:08.820 --> 19:11.860
There are also a bunch of status bits, as you can see.

19:11.860 --> 19:16.120
A few of the most important ones would be the V there for valid.

19:16.120 --> 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.

19:22.340 --> 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.

19:31.220 --> 19:35.160
You also have some bits, the cache-disabled, the dirty bit.

19:35.460 --> 19:42.320
These are bits that the operating system uses in its virtual memory management for page swapping and such.

19:42.320 --> 19:45.780
And then the last bit of interest is going to be the global bit.

19:45.780 --> 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.

19:55.460 --> 19:58.060
This actually becomes important later on.

20:00.980 --> 20:07.040
So, basically, here's an illustration of the big picture, just to kind of summarize all these ideas.

20:07.040 --> 20:14.180
We have our virtual address space layout divided into user space and kernel space.

20:14.180 --> 20:19.390
We have our pages, which are the divisions of the virtual address space.

20:19.940 --> 20:25.580
We have process page tables located in the kernel memory section.

20:25.580 --> 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.

20:39.820 --> 20:47.580
So, how do you go from an address to actually figuring out where that particular page is in the page table?

20:47.580 --> 20:54.760
Basically, virtual addresses can encode the information necessary to index into page tables.

20:55.060 --> 20:58.740
Page tables may actually be single-level or multi-level.

20:58.800 --> 21:02.500
The x86 maintains a two-level paging architecture.

21:03.700 --> 21:07.460
Basically, we divide the virtual address into two pieces.

21:07.460 --> 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.

21:21.640 --> 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.

21:33.480 --> 21:38.640
And, basically, the page directory is a table of pointers to page tables.

21:38.940 --> 21:42.500
And the page tables, of course, mean pointers to physical frames.

21:43.060 --> 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.

21:52.580 --> 21:58.060
So, basically, in this case, our virtual page number is actually divided into two pieces.

21:58.060 --> 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.

22:09.380 --> 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.

22:23.620 --> 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.

22:28.980 --> 22:33.720
Basically, there's a number of steps involved in virtual-to-physical address translation.

22:33.760 --> 22:35.740
This is performed by the hardware.

22:36.440 --> 22:42.740
The first step is going to be to locate the page directory, the base address, the page directory and memory.

22:42.940 --> 22:47.020
The page directory base is actually located in the k-process block.

22:47.020 --> 22:52.120
The physical address is also located in CR3, which is the control register on the processor.

22:53.380 --> 22:57.420
So, basically, the first step is to locate the base of the page directory.

22:58.080 --> 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.

23:06.080 --> 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.

23:18.220 --> 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.

23:29.040 --> 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.

23:37.480 --> 23:40.860
So, to get to the page table entry, now we do the same type of thing.

23:40.860 --> 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.

23:55.960 --> 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.

24:03.180 --> 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.

24:19.450 --> 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.

24:34.790 --> 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.

24:48.770 --> 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.

24:58.110 --> 25:01.510
There are actually several conditions where a page fault can occur.

25:01.510 --> 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.

25:12.470 --> 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.

25:24.050 --> 25:26.630
This becomes very important later on.

25:27.250 --> 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.

25:43.530 --> 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.

26:06.160 --> 26:07.040
Okay.

26:07.340 --> 26:09.520
First thing, you have a memory access.

26:10.160 --> 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.

26:16.320 --> 26:18.140
In this case, it's present.

26:18.140 --> 26:21.200
So we can move on and go out to the page table.

26:21.220 --> 26:24.980
Now, in the page table, we're trying to look up actually where the physical frame is.

26:25.180 --> 26:30.140
In this case, that particular PTE has been marked invalid, and a page fault is generated.

26:31.300 --> 26:33.900
This causes the page fault handler to be invoked.

26:33.900 --> 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.

26:43.740 --> 26:48.500
Once it's loaded back into memory, it now marks the PTE as valid.

26:52.180 --> 26:57.260
As you can see, some people would say that paging is not such a good idea.

26:57.440 --> 27:01.100
This is because there's this de-performance hint associated with paging.

27:01.100 --> 27:08.080
Basically, we take one memory reference and we basically turn it into three memory accesses.

27:08.080 --> 27:11.000
So we're cutting our performance by a factor of three.

27:11.360 --> 27:13.940
Basically, the first thing we have to access is the page directory.

27:13.940 --> 27:15.340
That's one memory reference.

27:15.340 --> 27:16.940
We have to access the page table.

27:16.940 --> 27:18.520
There's another memory reference.

27:18.620 --> 27:22.780
Now we have to actually go out and get the byte in the correct physical address.

27:22.780 --> 27:24.540
There's our third memory reference.

27:24.540 --> 27:32.720
So actually, in the best case, every single memory access requires three memory accesses to resolve the virtual to physical address.

27:32.940 --> 27:39.640
In the worst case, not only do we need three memory accesses, now we might potentially need two disk.ios.

27:39.680 --> 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.

27:49.960 --> 27:53.220
So therefore, you may have up to two additional disk.ios.

27:53.220 --> 27:58.180
So as you can see, paging introduces a speed performance hit.

27:58.180 --> 28:04.500
Hardware designers found this to be unacceptable and they developed a solution for this to help mitigate this problem.

28:04.500 --> 28:07.620
Their solution was the translation look-aside buffer.

28:07.840 --> 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.

28:20.000 --> 28:27.260
So basically, the TLV caches the information held in the PTEs for certain pages that are used frequently.

28:28.820 --> 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.

28:46.840 --> 28:51.140
So basically, the TLV is actually first on the memory access path.

28:51.140 --> 28:53.500
It sits in front of the page table path.

28:54.220 --> 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.

29:01.260 --> 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.

29:14.960 --> 29:19.400
If that reference is actually found in the TLV, we say it was picked.

29:19.420 --> 29:22.380
If it was not found, we say that we had a miss.

29:22.940 --> 29:27.040
The x86 actually uses a split TLV architecture.

29:27.200 --> 29:30.100
And what I mean by that is there's actually two TLVs.

29:30.100 --> 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.

29:39.000 --> 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.

29:54.820 --> 30:00.380
This is just a simple example of a TLV search.

30:00.860 --> 30:03.140
Basically, the TLV is a cache.

30:03.140 --> 30:04.580
It consists of two parts.

30:04.580 --> 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.

30:16.700 --> 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.

30:27.020 --> 30:33.300
If they can, the data, or the corresponding physical frame mapping, is going to be returned.

30:33.300 --> 30:40.840
In this case, we had a TLV hit on virtual page 17, and its corresponding frame is 84.

30:43.040 --> 30:50.920
So, basically, here I have an illustration of the memory access path that includes a TLV.

30:51.820 --> 30:57.500
We're also going to stick with the x86 architecture, which actually has a split TLV.

30:57.500 --> 31:04.100
So that means that on a memory access, code accesses are going to be checked in the iTLV.

31:04.900 --> 31:08.960
So, in this case, we have a hit on the iTLV.

31:08.960 --> 31:15.320
We now have the corresponding frame, which is 132, and we can go directly out to physical memory.

31:15.600 --> 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.

31:24.180 --> 31:27.220
It may be possible that we don't have a TLV hit.

31:27.220 --> 31:28.540
We could get a miss.

31:28.660 --> 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.

31:37.420 --> 31:40.640
So, in this case, our memory access is a code access.

31:41.000 --> 31:45.940
It goes out to the iTLV, searches it, it's not there, so now we have a miss.

31:46.520 --> 31:48.960
So, now we have to go out to the page tables.

31:49.240 --> 31:54.380
In this case, we go out to the page tables, and we find something we need is not present.

31:54.720 --> 31:56.540
So, we have a page fault.

31:56.540 --> 31:59.980
Now the OS page fault handler has to be invoked.

32:00.000 --> 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.

32:14.420 --> 32:18.580
So, basically, we have three memory access types.

32:18.580 --> 32:20.040
Everyone's familiar with these.

32:20.040 --> 32:22.520
We have read, write, and execute.

32:22.520 --> 32:30.080
Unfortunately, a little caveat to the x86 architecture is we actually only have two memory access types.

32:30.080 --> 32:33.980
We actually have read-execute and read-write-execute.

32:33.980 --> 32:38.380
By that, I mean the execute access is implied to all memory.

32:39.160 --> 32:41.500
This poses a slight problem.

32:41.500 --> 32:47.380
In some cases, we might like to actually differentiate between execute access and read-write access.

32:48.220 --> 32:55.580
One common place where this might be the case would be the implementation of non-executable memory.

32:55.580 --> 33:00.000
This is used as a buffer overflow protection scheme.

33:00.280 --> 33:05.060
Basically, you have your stack space, which is typically readable and writable.

33:05.060 --> 33:06.920
I mean, you store data on the stack.

33:06.920 --> 33:12.980
But normally, the stack does not contain executing code unless a buffer overflow attack is in progress.

33:12.980 --> 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.

33:27.960 --> 33:36.300
Now, the little caveat here is, like we said, x86 doesn't let us distinguish between read-write and execute.

33:36.560 --> 33:41.400
So, basically, we have not had hardware support up to this point.

33:41.400 --> 33:43.040
We do now have some hardware support.

33:43.040 --> 33:46.600
But previously, there was a project called PAX.

33:46.600 --> 33:54.600
Basically, PAX implemented read-write-no-execute or non-executable memory semantics using strictly software support.

33:54.600 --> 33:58.760
This was before there was any hardware support for non-executable memory.

33:59.120 --> 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.

34:11.460 --> 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.

34:23.300 --> 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.

34:35.520 --> 34:44.120
Basically, we're going to take an offensive stance on the PAX technology, which, it's been around for a while.

34:44.120 --> 34:45.980
This technique's been around for a while.

34:45.980 --> 34:52.180
But so far, it hasn't really been shown how you could invert that technique for malicious purposes.

34:52.560 --> 34:55.480
So, basically, we want to hide code.

34:55.500 --> 34:58.620
So, we want to differentiate between read-write-and-execute.

34:58.620 --> 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.

35:11.480 --> 35:16.680
So, basically, what we would want to do in that case is we want our rootkit code to run.

35:16.680 --> 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.

35:31.480 --> 35:37.840
So, basically, what we're proposing here is we're going to do an implementation that is the inverse of PAX.

35:37.840 --> 35:44.480
That is, it's an implementation of execute, allowed, but diverted read-write memory semantics.

35:47.220 --> 35:54.620
We're going to use the PAX technique that PAX discovered to implement non-executable memory.

35:54.620 --> 35:57.100
But, in this case, we're going to use it to hide a rootkit.

35:57.280 --> 36:03.880
So, like I said, the technique is not totally new, but the application is somewhat new.

36:04.280 --> 36:07.380
So, basically, there's three implementation issues here.

36:07.700 --> 36:11.280
First, we need a way to filter, execute, and read-write accesses.

36:11.280 --> 36:12.360
Step one.

36:12.360 --> 36:16.980
Second, we need a way to fake the read-write accesses once we detect them.

36:17.240 --> 36:22.020
Obviously, we don't want the scanner to receive the correct data when it reads our rootkit memory.

36:22.280 --> 36:26.280
Lastly, we need to ensure that performance is not adversely affected.

36:26.780 --> 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.

36:38.420 --> 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.

36:53.260 --> 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.

37:06.460 --> 37:11.880
In the actual page fault handler, we have access to some interesting information.

37:12.140 --> 37:14.460
We have the saved instruction pointer.

37:14.460 --> 37:18.700
We also have the virtual address where the fault occurred.

37:18.860 --> 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.

37:25.860 --> 37:28.820
Otherwise, it's going to be a read-write access.

37:29.440 --> 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.

37:43.260 --> 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.

38:01.380 --> 38:05.660
In order to satisfy that, we actually impose two constraints.

38:05.920 --> 38:10.880
The first constraint could be that pages that are hidden are required to be a non-paged memory.

38:10.880 --> 38:15.060
That means there's no possibility that the OS could page that file out to the disk.

38:15.640 --> 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.

38:24.620 --> 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?

38:34.660 --> 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.

38:48.660 --> 38:51.300
Like I said, the x86 has two of them.

38:51.300 --> 38:54.540
It has one for code, which is searched on execute.

38:54.540 --> 38:57.280
It has one for data, which is searched on read-write.

38:57.500 --> 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.

39:11.500 --> 39:16.220
So, in this case, for virtual page number 12, we see it maps to frame 2.

39:16.220 --> 39:19.440
For the DTLB, it also maps to frame 2.

39:19.440 --> 39:25.440
Under normal situations, read-write and execute, they all map to the same physical frame.

39:27.400 --> 39:30.300
However, what if we could desynchronize them?

39:30.300 --> 39:37.820
What if we could have the ITLB to actually map off to a different physical frame than the DTLB?

39:37.820 --> 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.

39:56.160 --> 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.

40:09.460 --> 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?

40:18.240 --> 40:21.060
TLBs are almost entirely hardware controlled.

40:21.400 --> 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.

40:28.420 --> 40:32.400
First of all, we have the capability to flush the TLB.

40:32.860 --> 40:37.780
Basically, we can flush the entire TLB by reloading register CR3.

40:37.780 --> 40:40.680
This normally occurs on a context switch.

40:40.700 --> 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.

40:51.140 --> 40:58.940
This is mainly a performance enhancement for operating system components, which will remain resident at all times.

40:59.780 --> 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.

41:06.940 --> 41:09.840
That would be the invalidate page instruction.

41:10.800 --> 41:15.400
So, now that we've established that we can flush the TLB, how about loading it?

41:16.300 --> 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.

41:27.040 --> 41:32.520
Conversely, executing a call loads the ITLB, but not the DTLB.

41:32.520 --> 41:38.740
So, therefore, it is possible to discretely load the two TLBs separately and desynchronize them.

41:40.760 --> 41:46.420
So, basically, our proof-of-concept implementation consists of two main components.

41:46.420 --> 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.

42:04.100 --> 42:09.320
Also, our second component is going to be a modified FU rootkit.

42:09.340 --> 42:16.700
So, basically, we've modified Jamie's rootkit to work in collusion with our ShadowWalker.

42:19.220 --> 42:24.880
So, basically, on memory hook installation, there are several steps that have to be performed.

42:24.960 --> 42:30.560
First of all, since we're going to mark pages not present, we need to install a new page fault handler.

42:31.180 --> 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.

42:49.120 --> 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.

42:55.900 --> 42:58.460
Next, we mark the page not present.

42:59.580 --> 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.

43:15.140 --> 43:19.480
So, therefore, we perform an explicit flush of that page that we're hiding.

43:19.500 --> 43:24.820
This means that on the next memory access, we will be able to generate a page fault.

43:25.420 --> 43:28.840
If you could please hold your questions to end, we'll have time for questions.

43:32.910 --> 43:41.150
So, basically, the custom page fault handler's main responsibility is to filter, read, write, and execute accesses for hooked pages.

43:41.630 --> 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.

43:50.070 --> 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.

44:00.630 --> 44:04.390
Most memory references will be resolved via the TLB path.

44:04.390 --> 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.

44:09.750 --> 44:13.890
Not the case, because we're using the hardware caching mechanism.

44:13.890 --> 44:16.170
We're just simply synchronizing it.

44:16.650 --> 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.

44:26.770 --> 44:32.250
Page faults on the hooked pages will, of course, occur on the first execute or data access to the page.

44:32.250 --> 44:36.590
Remember, we had to flush it, flush the TLB, when we instantiated the hook.

44:36.830 --> 44:39.970
It will occur on the TLB cache line eviction.

44:39.970 --> 44:46.990
So, it's possible that the mapping for that hooked page got evicted from the TLB.

44:46.990 --> 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.

44:58.370 --> 45:06.750
Likewise, the last case would be on an explicit TLB flush context switch, if the global bit has not been enabled.

45:09.090 --> 45:15.430
So, basically, here we have some really rudimentary page fault handler pseudocode for what's going on.

45:15.990 --> 45:20.570
So, basically, we are only hiding kernel pages right now.

45:20.570 --> 45:23.930
So, we're going to disregard user land accesses.

45:23.930 --> 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.

45:30.330 --> 45:33.990
So, the first thing we do is we just check if the processor mode is in user mode.

45:33.990 --> 45:35.410
If it is, we pass it down.

45:35.410 --> 45:37.110
We're not dealing with user mode.

45:37.270 --> 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.

45:43.990 --> 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.

45:51.010 --> 45:55.570
If it's an execute, we're going to jump over to load the ITLB.

45:55.690 --> 45:59.950
If it's a data access, we're going to jump over to the load DTLB code.

46:00.610 --> 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.

46:15.890 --> 46:21.950
We're going to temporarily mark that PTE as present to allow the memory access to go through.

46:22.030 --> 46:26.530
We're going to perform a call into the page, which is going to load up the ITLB.

46:26.530 --> 46:32.670
I should note here, I forgot to put this on the previous page, that we do have to perform a call.

46:32.790 --> 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.

46:41.310 --> 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.

46:52.810 --> 46:56.850
So you shouldn't really have trouble finding one byte that you can just patch a return into.

46:57.390 --> 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.

47:10.890 --> 47:17.310
Finally, the PTE is marked as non-present again, so we can catch subsequent accesses.

47:17.310 --> 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.

47:30.910 --> 47:33.550
The OS doesn't need to have any knowledge of this default.

47:34.170 --> 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.

47:43.930 --> 47:50.690
So therefore, we just mark it present, perform a read access, and then mark it non-present again.

47:50.950 --> 47:54.210
And then, of course, return without passing down to the OS.

47:56.830 --> 48:02.970
So, basically all this information so far concerns how you would hide executable code.

48:08.800 --> 48:13.460
Okay, we're running out of time, so I'm going to go ahead and skip this section on data hiding.

48:13.460 --> 48:18.260
This slide should be available probably on rootkit.com within the next week or so.

48:20.320 --> 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.

48:24.560 --> 48:30.640
The rootkit is, of course, the object that we are implementing this process on to actually hide it.

48:30.780 --> 48:32.100
It's proof of concept.

48:32.100 --> 48:33.520
It doesn't really do anything malicious.

48:33.520 --> 48:35.080
It just hides processes.

48:35.700 --> 48:37.600
It runs as a system thread.

48:37.880 --> 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.

48:45.920 --> 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.

48:54.940 --> 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.

49:04.140 --> 49:06.880
So basically, we never want it to reside on the disk.

49:06.880 --> 49:13.580
This is a perfectly reasonable thing if we consider attacking, say, a server system, which is infrequently rebooted.

49:13.580 --> 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.

49:22.280 --> 49:25.660
So, lastly, the impact on system performance.

49:25.780 --> 49:28.660
Like we said, modern TLBs have high hit rates.

49:28.800 --> 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.

49:39.040 --> 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,

49:59.620 --> 50:01.960
noticeable performance impact.

50:02.560 --> 50:07.940
Clearly, the performance impact will probably vary depending upon the number of pages you're having to hide.

50:08.060 --> 50:12.760
In this case, hiding a kernel driver, how many pages do you really have?

50:12.760 --> 50:19.600
You have four or five sections in a PE, portable executable, so chances are you only have four or five pages.

50:19.600 --> 50:23.600
If they're page-aligned, four or five pages is really negligible.

50:24.220 --> 50:29.180
So, for small numbers of pages, we haven't observed any performance impact at all.

50:31.200 --> 50:37.120
As this is not really a weaponized attack tool, there are a number of known limitations.

50:37.660 --> 50:41.240
Jamie and I have done this basically on our spare time.

50:41.320 --> 50:47.320
So , basically, we've not implemented a fully productized or weaponized version.

50:47.480 --> 50:50.260
So, first of all, there's no PAE support.

50:50.260 --> 50:55.620
PAE basically extends addressability from 32 to 36 bits.

50:55.620 --> 50:57.340
We don't support that yet.

50:57.340 --> 51:00.600
There's no hyper-threading or multi-processor support.

51:00.600 --> 51:07.680
There's clear issues with regards to synchronization problems involved with porting this up to a multi-processor system.

51:07.680 --> 51:09.500
We've not addressed those yet.

51:09.600 --> 51:13.840
Currently, we only hide 4K-sized kernel pages.

51:14.000 --> 51:17.680
We are theoretically capable of hiding 4MB pages.

51:17.680 --> 51:22.320
The x86 architecture actually has two page sizes, 4K and 4MB.

51:22.320 --> 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.

51:29.660 --> 51:36.020
Obviously, an interest to a rootkit developer who wants to install hooks into the operating system kernel.

51:36.080 --> 51:38.340
Currently, we can't hide NTOS kernel.

51:38.340 --> 51:42.400
There's some technical difficulties with that, which we're working to resolve.

51:42.580 --> 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.

51:50.160 --> 51:53.900
So we could hide the hooks in the network drivers, for example.

51:55.800 --> 51:58.980
As far as detection, I'm going to go over this real, real brief.

51:58.980 --> 52:01.140
I think we're really running out of time here.

52:01.480 --> 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.

52:15.460 --> 52:20.320
Obviously, non-present pages in non-paged memory are heuristically abnormal.

52:20.340 --> 52:24.800
If that's sufficient to indicate that a rootkit's installed, highly debatable.

52:24.800 --> 52:26.400
But it's still abnormal.

52:27.000 --> 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.

52:36.080 --> 52:40.040
We would end up with lots of recursive problems of recursive re-entry.

52:40.040 --> 52:45.520
Therefore, the solution for hiding the page fault handler, we have to fall back to more tried-and-true approaches.

52:45.760 --> 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.

52:54.380 --> 52:57.560
Likewise, it's also difficult to conceal hooks on the IDT.

52:57.560 --> 53:01.580
Currently, this proof of concept makes no effort to conceal the IDT hooks.

53:01.660 --> 53:06.980
Yes, this rootkit is very easily detected right now because of this, but that was not really our goal.

53:06.980 --> 53:09.600
We're just showing a proof of concept.

53:11.000 --> 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.

53:27.090 --> 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.

53:39.330 --> 53:41.650
I'm going to do a GUI version of this demo.

53:41.650 --> 53:47.470
I have a hardcore Softice version if anyone wants to see after the talk.

53:51.660 --> 53:54.380
Basically, the first thing I'm going to do is...

53:54.380 --> 53:56.360
Right now, I'm using Driver Monitor.

53:56.360 --> 53:58.760
It's a tool with the Softice driver suite.

53:58.760 --> 54:01.000
It allows you to load a kernel driver.

54:01.640 --> 54:06.160
The first thing I'm going to do is I'm going to go ahead and load up Jamie's rootkit.

54:14.150 --> 54:16.590
Our driver has started successfully.

54:16.590 --> 54:19.830
Now, if we enter Softice...

54:23.490 --> 54:27.950
we can get the base address of where this driver is loaded in memory.

54:43.470 --> 54:47.430
This is the virtual address of where this driver is loaded in memory.

54:47.430 --> 54:49.110
We're going to go ahead and dump it.

54:49.110 --> 54:50.810
Dump the memory at that address.

54:51.350 --> 54:56.370
For all of you guys that use hex dumps all the time, this should look pretty familiar.

54:56.370 --> 55:02.030
This 4D5A, that's simply the MZ signature inside the PE header.

55:02.390 --> 55:07.450
Right now, what we're seeing is the PE header of this rootkit driver loaded in memory.

55:08.430 --> 55:15.350
At this point, I'm going to go ahead and load up the memory hook engine, ShadowWalker.

55:28.400 --> 55:30.860
It started up successfully.

55:32.300 --> 55:35.600
Now, we're going to go ahead and perform another memory dump.

55:37.780 --> 55:40.180
Now, it's gone.

55:47.740 --> 55:50.400
So, we'll take questions offline.

55:50.400 --> 55:51.540
We're leaving the room.

55:51.540 --> 55:52.800
We're being evicted.

55:52.960 --> 55:54.800
We have swag books.

55:54.860 --> 55:55.880
We'd love to hear from you.

55:55.880 --> 55:56.840
Thanks a lot.

