Status
Not open for further replies.

kram7750

New Member
Joined
Apr 12, 2014
Messages
993
#1
Hello everyone, I am back today... And today, I am going to talk to you about rootkits (how they work for example), however I will also be covering much more in this thread not directly related to rootkits to help you understand rootkits and techniques they use.

Before reading this thread, I advise you to read my previous thread regarding rootkits: http://malwaretips.com/threads/kernel-user-mode-rootkits.47121/

I've written about a lot of things included in this thread before, most likely not as detailed or extensive. (and probably not all of it either).

The reason I am making a second thread is so I can introduce more extensive information, go more advanced into techniques used by rootkits and focus on individual topics. This thread is more aimed at either people getting into development in security, or people who like learning about the Native and Windows API, different malware types and how they work (techniques applied, how the techniques are made etc) and learning about methods/techniques used to stop the particular thread in this topic (in this scenario, rootkits) and how you can actually go about programming the methods used to protect against or clean them. In fact, I've decided I will involve very deep and low level functions involved in Windows in this thread. Whether that's a good idea, we'll have to find out in the future, but it's beneficial to MT members for learning and I care about educating people my knowledge to an extent I'm happy with.

I've set the font size at 3 as it helps me in writing long threads and may be easier to read for you. If it is not easier to read for you, just zoom in with the browser until you are comfortable with reading. (zooming in once from the default size on the webpage can really help and make things easier to be read).

Below is an index of different sections to my thread, I won't start at the very beginning explaining what a rootkit is etc, since that is all covered in the thread I linked at the top of this thread. Before reading this thread, I really advise you read the previous one [http://malwaretips.com/threads/kernel-user-mode-rootkits.47121/] as it covers sufficient information you should read before trying to understand the information in this thread, even then it may be difficult unless you are a developer in security.

Table contents:
1). Quick intoduction on a few things which hopefully will be easy to remember
- What the Von Neumann Model/Architecture is and how it works
- What is memory protection and how it works
- Why memory protection doesn't stop rootkits
- What hooking/detouring is (very brief example, I'll base the example on a memory cells)
- How memory works with the CPU in a program
- What PatchGuard/KPP (Kernel Patch Protection) is and why it was introduced
- Why PatchGuard/KPP doesn't necessarily stop the existence of rootkits
- Why malware writers can learn to make rootkits very easily in today's world
- Additional information (known companies have also purposefully made use of rootkits, Sony as an example...)
2). Some techniques used by rootkits (hooking, injection, kernel-mode drivers - how they protect and stay stealthy)
- How rootkits perform and how they use IAT (Import Address Table) hooking. (along with the byte representation of the JMP opcode instruction for both x86 processes and x64 processes)
- How rootkits perform and how they use SSDT (System Service Dispatch Table) hooking.
- How rootkits perform and how they use IRP (I/O Request Packet) hooking.
- How rootkits perform and how they use IDT (Interrupt Descriptor Table) hooking.
- What the GDT (Global Descriptor Table) is.
- How rootkits protect themselves from detection and removal via hooking.
- What a callback is from kernel-mode, why they are used, how they are used and some examples of some callbacks which a rootkit may make use of.
- How rootkits can inject into other processes for user-mode hooking.
- How rootkits can load drivers (just a few examples).
- How a user-mode rootkit can prevent injection into it's process.
- How a rootkit can protect it's driver from being unloaded from user-mode API call requests.
- Examples of dangerous things a rootkit can do from kernel-mode (unrelated from hooking, more about the Master Boot Record).
- List of functions a rootkit can hook
3). Methods for cleaning rootkits
- Brief info about unhooking (originally I had dedicated sub topics but due to the thread being too large they have been replaced by 2 short and simple paragraphs).

NOTE: To understand things properly, you MAY require some programming experience with at the minimum .NET. However knowing Assembly, C and C++ will be extremely beneficial, and knowing about driver development with a language like C would be even better.

Chapter 1). Quick introducion
1.0 - The Von Neumann Model/Architecture and how it works


The Von Neumann Model/Architecture is a very important concept in my opinion to be understood, because it will help you with how memory works. You can say the Von Neumann Architecture* or Model*. It doesn't matter which end word you use between Model or Architecture, they represent the same thing.

Before the Von Neumann Architecture was introduced, memory worked significantly different. When you ran programs on the system, every program had access to the memory on the system (so they ran in the same memory as all the others). As in, if you had 3 programs, they'd all share the same memory to run the program. However, this was a big issue... A big issue with this was that if one of the programs had an error and crashed (in memory of course since that's where it's executing), it would result in every program sharing the memory to the program which crashed to stop working (crash) as well. Which meant all programs executing in memory would crash. That's not good, is it now? In the last month, think about how much software has crashed? For lucky people maybe none, but for me a lot of programs have crashed (probably due to testing code until it works without crashing the target process). But anyway, moving on... The solution to this was the Von Neumann Architecture.

The Von Neumann Architecture was introduced because it was found to work well and be good, and it worked (works*) by allowing each program running on the system to have it's own memory (which is allocated for it by Windows for you). After the architecture was introduced, when you ran a program, instead of it sharing memory it would be allocated its own section of memory.

If you had a system with 6GB RAM and then you had 3 programs open, before the architecture had been introduced, all 3 programs would have shared the 6GB between themselves. However after the architecture had been introduced if you had 3 programs open, the memory would be split up into 3 equal sections with each section representing 2000MB (2GB) of memory. 6GB represents 6000MB, and we have 3 programs running therefore the math involved would be 6000 / 3 = 2000, therefore each program is allocated 2000MB of memory. You can also say 1GB is equal to 1024MB mathematically, but I prefer to keep it rounded to the nearest thousand when working with memory in explanations. If the program crashed (where it had its own 2000MB of memory allocated for it), only that program would crash instead of all the programs because the memory to other programs wouldn't be affected. This also means a system restart wouldn't be necessary after a program had crashed since the OS would not be running in memory shared between all programs.

A better term instead of "section" would be "segment".

I also want to quickly note that user-mode programs don't directly access the memory (hardware), but they have pointer addresses in memory which point to the real memory addresses.

As a quick summary, think of it like a way programs are allocated memory (well that is essentially what it is).

1.1 - What is memory protection and how it works

After the Von Neumann Architecture was introduced, another flaw with how memory worked was found. This flaw was that even though each program now was allocated its own "section" of memory, programs could access the memory to other programs running and read/write to it... Do what they wanted really. Now you may be wondering "Is that really such a big issue?", the answer to that question depends on the situation for the usage of reading/writing to a process' memory. But to help assist in answering that question if you had the thought in your head:

Let's pretend you are using your computer to process a payment with your bank card, and that you were using desktop software provided by the bank to do so. You enter your bank details, you're about to process the payment to an item which costs £34. Due to the flaw of any program executing being able to read and write memory as it likes, it would be able to read the memory of the bank program and then potentially steal your bank details. The reason it'd be able to steal your bank details you entered for the payment processing is because that information has to be stored somewhere... Memory! And I am sure none of us want our bank information to be vulnerable from a program... Or our passwords from a password manager... So memory protection was introduced! (well maybe they had different examples to bring it in). (of course password managers etc would probably encrypt the data in memory, but still).

Memory protection works by preventing other programs not executing within the memory they want too e.g. write to from writing to it. The only way you can write to another process' memory is if you unprotected the memory if it was protected (due to memory protection). I'll explain unprotecting memory a bit more in the next part.

As a quick summar, think of it like a way of preventing other programs from interfering with memory to another program running (well that is essentially what it is).


1.2 - Why memory protection doesn't stop rootkits


Rootkits won't be stopped due to memory protection... That's right. Re-read this line just so you remember that memory protection will not stop a rootkit. (of course I'll further explain why).

The reason why rootkits will not be stopped by something like memory protection is because the Windows API offers many API functions which can be called to actually unprotect memory at the target address of a process. You might be wondering why memory protection is even there when it can be unprotected with one API call request (and then other API functions are called and all the work is done by Windows for the rest of unprotecting the memory)... And honestly, I don't blame you. I guess it's still there as a safety precaution. It can still be useful, and there are definitely dumb malware writers who haven't even progressed to rootkits due to not having any idea with how to do anything for them.

A good example of an API function (Windows API to be precise) is the function VirtualProtect(). It takes in 4 parameters: the first parameter is for the address you want to start unprotecting at, the second parameter is the size in bytes you want to unprotect from the start address (first parameter), the third parameter is for the protection setting for the memory and then the the last parameter is a way for you to store the old protection to a variable for future reference. For now, I won't go further in VirtualProtect(), because I'll be showing a use of unprotecting memory later in this thread.

Therefore, a rootkit will just unprotect memory of a process if it needs too (user-mode rootkit). so it can write it's own instructions in the memory of the process it is targetting. However, memory protection is still useful.

1.3 - What hooking/detouring is (very brief example)
Hooking is the process of where you can manipulate the way a certain program works. This includes manipulating results from a function call or altering the behaviour of the way something works altogether. Hooking/detouring is used usually to check the parameter inputs to a function call it's hooked to decide whether it needs to take action or allow continuation of the function call. It's essentially changing memory to how you want it, allowing you to have the control over things.

There are different types of hooks, a common list of hooking types are as follows:
- IAT (Import Address Table) hooks
- SSDT (System Service Dispatch Table) hooks
- IRP (I/O request packt) hooks
- GDT (Global Descriptor Table) hooks
- IDT (Interrupt Descriptor Table) hooks

I most likely am more familiar with IAT, SSDT and IRP hooks as opposed to GDT and IDT hooks. The reason for this is because I am still trying to study more about GDT and IDT hooks (well I'm learning more about all of them further all the time, especially when I write code) from the time of posting this.

I'll explain more about them when we encounter them later in the thread.

For now, I will explain you a very simple example on hooking:
Let's say we have a program called sleep.exe and that at a memory address we have an instruction in a memory cell called SYSTEMSLEEP. SYSTEMSLEEP is a fake thing I have just made up for this example, but lets pretend that when the function is called like that, the system will go to sleep. However, what if we hooked the function?

Well before we could start to hook the program to make it do something else instead of put the system to sleep, we need to unprotect the memory before we can do this. After unprotecting the memory, for best code practice, we should probably check if our hook already exists before setting one, and if it doesn't, we can place the hook in memory at the target address (in our case, the target address would be at the address where SYSTEMSLEEP is). Placing the hook can be done with many methods, it can even be done with very simple C++ knowledge on writing to memory and working with type-casting (type-casting is a technique used in programming to convert one data type to another data type).

Therefore, if we unprotected the memory in the process running sleep.exe, and then we got the address of SYSTEMSLEEP and we changed the instruction from SYSTEMSLEEP to SYSTEMSHUTDOWN, then when the CPU gets to the address where SYSTEMSLEEP originally was, it would execute the instruction we wrote at the address (we replaced the other one with SYSTEMSLEEP), so it would shutdown the system instead.

In my example, SYSTEMSLEEP represents putting the system to sleep and SYSTEMSHUTDOWN represents shutting the system down. These functions are not valid in Windows, its just an example.

As a quick summary, think of hooking/detouring as a method of changing the execution flow of a program/altering the behaviour of a program. You'll learn about different hooking types and should understand more about it later on, if you do not understand it, don't worry.

1.4 - How memory works with the CPU in a program

I'm honestly not sure if I have ever explained this to someone on the forums before... Regardless, let's continue!

Before I can continue, I should explain how the CPU works, at least to the point of how it moves to different addresses in memory of a process. I believe it's a necessary part to know especially, since we are learning about things like hooking and that is heavy involved with memory because as I stated previously, hooking is pretty much controlling memory to have your own outcomes and results as opposed to the orginal (altering the program behaviour).

Firstly, I'll just cover the most simplest thing about the CPU in this thread: the CPU stands for the Central Processing Unit, it's responsible for executing instructions for the system, however, there is a lot involved in execution of "instructions", such as "electronic signaling". Okay, great... Once you've remembered that, we can continue!

In the CPU, there is something called the Instruction Pointer (IP) (it can also be known as the Program Counter). The Instruction Pointer is responsible for moving to the addresses in the memory mapped out for a program which is running. The Instruction Pointer will start at the first address in the memory which is 0x0 (represents 0 - in computers we start counting at 0, so instead of 1 being the start, 0 is the very beginning) and then it will keep progressing with the addresses as it moves forward with them. When the Instruction Pointer has reached an address, the CPU will continue to read the memory at that address. When the memory has been read, then there will be executable instructions for the CPU to execute, therefore these instructions will then be executed by the CPU via "Electronic Signaling".

Therefore, if we ran a program, the CPU Instruction Pointer would start at 0x00000000. If the program won't start, you may be returned an error with that address noted. Since it's the first address, if memory fails to be read there and the instructions executed there cannot be executed successfully for whatever reason, the program won't work as it won't be able to execute properly. The Instruction pointer would then continue to progress with the addresses: 0x00000001, 0x00000002, 0x00000003, 0x00000005,.... 0x00000050,.... However, if there is an issue with the process memory allocated to the process, the system won't crash but only that process because as we learnt earlier, each process is allocated it's own memory (virtual memory which points to real memory addresses) due to the Von Neumann Architecture.

Please note that CPUs today are very advanced and the cycle with the Instruction Pointer moving to addresses and then the CPU reading memory and executing the instructions happens billions of billions of times... In a constant cycle at very fast speeds.

That should be sufficient enough. As a quick summary, the CPU is responsible for executing instructions on the system via "electronic signalling", and there is something called the Instruction Pointer inside of the CPU which is responsible for moving to addresses, which in turn will be read (memory address found by the IP) by the CPU and then the instructions there will be executed.

1.5 - What PatchGuard/KPP (Kernel Patch Protection) is and why it was introduced

PatchGuard is a feature introduced in 64-bit versions of Windows which helps protect the user in a number of ways, and helps the OS stay more stable. It includes features which prevent a driver which probably shouldn't be loaded on the system from being loaded and other things related to the kernel, such as preventing patching of the kernel on 64-bit systems. The reason why PatchGuard (Kernel Patch Protection) was not introduced onto x86 (32-bit) systems was down to the fact that Microsoft felt that there was already too much software available on the market which made use of kernel-mode components (drivers) and things which PatchGuard prevents, therefore they decided to leave 32-bit more vulnerable and have it only available for 64-bit systems.

PatchGuard was introduced to prevent the patching of the kernel. Patching the kernel can be very dangerous and can cause big system instability, however it's a technique which was also used by Antivirus products at one point in time (if we move a few years back, products like AVG (I know AVG used to use it for a fact, however I am not sure if they now still use SSDT hooking on x86 versions of Windows) (and many other products) made use of SSDT hooking for things like self-protection, most likely other things such as monitoring new registry key creations, file creation (unless a filesystem mini-filter driver was used) and so on. Even today, patching of the kernel may still be used by security products such as an Antivirus, however it's only available on an x86 system due to PatchGuard unless a zero-day vulnerability was discovered (and then used) to use kernel patching techniques on x64 (which isn't impossible, and PatchGuard has been exploited before, however Microsoft have done a good job in my opinion at securing it against exploits and patching up found vulnerabilities).

PatchGuard is essentially extra layer of protection and it does help protect you against rootkits in a way, just not enough. And that isn't really Microsoft's fault, because there is only so much they can do. Anyway, let's continue and talk about what PatchGuard does.

Kernel Patch Protection tries to protect against the modification/replacement in the Windows kernel. Kernel patching is essentially changing things with the kernel.

PatchGuard includes protection against patching of the kernel as noted above, this means that kernel patching techniques like SSDT hooking will not work on x64 due to PatchGuard. (SSDT hooking can only occur if KeDescriptorTable is exported anyway).

PatchGuard doesn't just prevent patching of the kernel (attempt to), but it tries to prevent loading of unsigned drivers. By 'unsigned drivers' I am referring to drivers without a digital signature (code signing). If a driver wants to be loaded on an 64-bit system, it'd either have to be signed by a digital signature (code signed), a vulnerability exploited to bypass PatchGuard to have it loaded or Test Mode and Enable Driver Signature Enforcement disabled (I've done this before when testing drivers when they haven't been signed after testing them on a Virtual Machine or spare computer).

Good advantages to PatchGuard is that if kernel patching is being prevented, it can help prevent system crashes (BSODs). An Blue Screen of Death occurs when an issue in kernel-mode (ring0) occurs, therefore if kernel patching is prevented, it can help prevent the amount of kernel-mode issues (instability) because then a third-party driver would be prevented from ending up messing something serious up (although if it wanted to purposefully cause a BSOD on either x86 or x64 architecture, it just has to load a driver and do something incorrectly and there you go, you got yourself a BSOD). Another advantage is that software may stay more reliable for usage on newer Operating System versions/updates, since if a product is relying on patching of the kernel and then Microsoft decide to make a change to how kernel-mode works in the newer OS, then the product may stop working on that newer OS and take either a long time to be fixed to work on it (or support removed for such features it had in the product). Therefore PatchGuard isn't the most attractive thing for developers and does hint away and show that Microsoft really advise against kernel patching.

Personally I don't like PatchGuard that much anymore. I don't like it because it means if you made something really good using kernel patch techniques, it won't work on x64. However, kernel patch protection definitely is not full-proof, nothing is, therefore there are ways to escape the defence of PatchGuard and patch the kernel on x64 systems. However, I won't be covering this here and it's not necessary for me too cover it here either. It also causes stress to Antivirus developers (potentially). Malware writers find other ways to patch the kernel/exploit PatchGuard (if they are experienced enough) and malware doesn't necessarily need to patch the kernel to function (there are also user-mode rootkits which are just as effective at their job than some kernel-mode rootkits). On a x64 system, a rootkit designed for x64 Operating Systems for Windows can also just work with callbacks to protect its process and registry keys, etc... Therefore as long as the malware writer is clever enough and dedicated to learning (or already experienced), great and advanced rootkits can be made for x64 systems as well. However it does indeed make things harder in some cases and it does protect things, but you are still not protected against rootkits or other malicious attacks.

PatchGuard can be useful to some, but it isn't as good as it seems to most people or how it seems when the advantages are pointed out. But of course there are some good advantages as noted above already. I find the prevention of unsigned drivers a good feature as it helps prevent the use of bad drivers which shouldn't be loaded in the first place, however code signing can be irritating and a big blockade for some people, and take time to get sorted. Malware writers also have techniques for stealing code signing certificates these days as well.

1.6 - Why PatchGuard/KPP doesn't necessarily stop the existence of rootkits

The reason why PatchGuard doesn't necessarily stop the existence of rootkits is due to the fact that there will be workarounds around PatchGuard which malware writers have/will discover, and due to the fact that kernel patching isn't required to make a rootkit. Think of it like this: you have a rootkit infection, so it sets a bunch of user-mode hooks to prevent the loading of drivers, protects its processes/files/registry keys and does all sorts. You eventually find out you are infected, but to remove it you'd need an advanced product to bypass the defence the rootkit has setup. New rootkit infections are out all the time, new rootkits... Which are more advanced than others or not as good as previous ones. For security developers to keep up to date and keep finding ways to outsmart malware writers' malware (rootkits to be precise), it can be a very difficult task and usually requires many people working on maybe one method for either preventing or cleaning up rootkits.

As well as this, there is something called a "callback", and this can be used from kernel-mode to do things like protect a process. I'll talk more about this in chapter 2.

As well as this, user-mode rootkits exist and PatchGuard doesn't really help against user-mode processes from writing to the memory of each other and altering how the target program works. It's strictly related to kernel-mode.

The point is, PatchGuard isn't perfect, nothing is. Malware writers/hackers don't sleep, they are awake just as much as securtiy researchers (good ones)/developers, and they create new things all the time to be abused by malicious software. All we can do is keep counterfeiting their new methods and working to protect against ones we already found which they haven't managed to find/use yet.

1.7 - Why malware writers can learn to make rootkits very easily in today's world
In the day and age we currently live in, it's never been easier for a malware writer to go from developing unstable, failing malicious software to making high-end malicious software with rootkit capabilities. I'm serious. People might disagree with me, but that is what I think. If someone wanted to become a true malware writer instead of copy pasting code from the web all the time, they could easily do it. All they'd have to do is learn more by reading books and access online content on dark forums where there is source code for rootkits which other experienced people have made and shared to help educate people.

All they really have to do is strengthen their skills with the target programming language (if they want to make something to be high-end and work well, it'll have to be a native language like C/C++ which also helps with forgetting about dependencies which .NET projects have) and learn more about the internals of Windows (functions, libraries, how certain OS features work) and security overall. This can easily be done. Within maybe 4 - 6 months of learning, the malware writer could have already learnt enough and be sufficient enough with skill to create a user-mode rootkit to protect it's process, registry keys and files from termination... And hide it's process, registry keys and files from view from the user. Of course it wouldn't be the most sophisticated thing ever, but it'd be enough to do the job. And of course an AV product would probably pick it up, but that's not the point... Because the malware writer would keep learning, eventually they'd probably move into exploits, and then after a few years, they'd be a master with things and would be exploiting security software, using kernel-mode drivers to patch the kernel, all sorts.

With the amount of forums out there on the internet for people to learn from, it's even easier for them... Some malware writers just copy paste and have no idea about how anything works, then distribute the compiled sample as their own and try to get credit from their "hacker name" or "hacker identity" to try to look like the next big hacker. It's quite ridiculous and funny, but it does happen.

But seriously, there are so many people out there becoming experts in developing malware all the time (especially rootkits, it gets much more sophisticated).

Although, even if you aren't a malware writer with the intention to harm someones system and distribute malicious software or malicious code on a forum, learning malware programming can be beneficial. It can help improve your understanding on how malware works overall, and how to protect against it.

1.9 - Additional information (known companies have also purposefully made use of rootkits

(not 1.8 because I'd need to add another sub-chapter, so I just called it 1.9).

In the past, known companies have made use of rootkits, and the government have also made use of them. Sony back in the day used a rootkit regarding the DRM to try to prevent copyright, however they were caught out by Mark Russinovich from Wininternals. I don't think Sony will do this again, it rid of a lot of trust to the company since it's never nice to know a rootkit has been installed onto your system, even if it was for what you could call an "legitimate" reason or not.

Chapter 2). Some techniques used by rootkits
2.0 - How rootkits perform and how they use IAT (Import Address Table) hooking


Before I can explain how rootkits make use of IAT (Import Address Table) hooking, I first need to make sure you are familiar with what the Import Address Table actually is. Therefore, below I will explain what the Import Address Table is and how it works, and then I will explain how rootkits may use IAT hooking.

The Import Address Table is only there for an Portable Executable (PE) file (e.g. *.exe). The Import Address Table (IAT) is what you can call an section which contains addresses to functions which are dynamically linked from external modules. When you call an API function from a library, it will check the Import Address Table (if it was dynamically linked) as a lookup to get the pointer address to the function from the library. Since an address (pointer) to memory address is in the IAT for each function dynamically linked from a module, the program can use the address found after looking up in the IAT to call the function. Without the IAT, things would be more difficult because then it'd require programs to make use of static linking (which makes the size of the executable much larger because it'd require for the function from the library to be inside of the code) or run-time linking (which is actually used quite often and can evade IAT hooks).

If you have a function dynamically linked from a different module, when you use that API function (call it) from your program, it will use the IAT to lookup the pointer address in memory to the function and will use this to call the function. However, if an IAT hook takes place, then the instruction at the target address will be changed so instead of going to the pointer address in memory to the function which was called, it will redirect the code execution to the code of the new address placed with a JMP instruction to it (call can also be used, etc).

IAT hooking can be useful for user-mode rootkits because it will allow the rootkit to have its own code executed when a function which had been dynamically linked is called. Rootkits can make use of IAT hooking to hide files from view with explorer.exe, hide processes from Task Manager, protect processes from Task Manager... You can also do things like implement a "form grabber" into the rootkit by hooking HttpSendRequest (Windows API function), however Internet Explorer uses this and it wouldn't work for other browsers.

For an x86 process, the byte representation for the JMP instruction is 0xE9.
For an x64 process, the byte representation for the JMP instruction is 0x48.

An IAT hook is relatively easy to do, user-mode rootkits usually use what you can call an "trampoline" with their IAT hook. It means more work such as setting the trampoline bytes and doing a bit more math (calculations) for it to work right, but it's much better. You can just return back the trampoline to the API call which was originally called by the program you detoured the function in with the original parameters to allow the execution of it, or you can alter the parameters. However, usually you'd check the parameters and depending on them take an action. For example, if you hooked a function like ZwTerminateProcess, then you could filter out the parameters to check if the API call request was to terminate e.g. notepad, and then decide if you want to allow it or deny access.

Before you can actually write to the memory of the process to set the hook on the Import Address Table to the function you want to hook, you need to do a few things: unprotect the memory at the target function, write a callback function and check if your hook already exists so you don't waste time/end up messing up as there is no need to re-write the hook if it's already been set, right?

Before you can unprotect the memory, you need to store the address of the target address you want to unprotect memory at and investigate the prologue of the function to count up the bytes and make sure everythng fits right, make sure to unprotect enough bytes to set the hook for the function. The target address will be the address of the function you want to hook. In C++, you can use kernel32!LoadLibraryA with kernel32!GetProcAddress. (NOTE: The first part represents the library DLL module the function is from, after the ! you put the function name. It means that the function name (after the !) is in the module you put before the !. That's how programmers mention functions from a module).

Code:
// You can use a variable (LPVOID type) to store the address, or a DWORD. I'll show you examples for both:

LPVOID lpAddressOfFunction = GetProcAddress(LoadLibraryA("ntdll.dll"), "ZwTerminateProcess");

// example for DWORD:

DWORD dwAddressOfFunction = (DWORD)GetProcAddress(LoadLibraryA("ntdll.dll"), "ZwTerminateProcess");
In the second example above, if you use DWORD, you must use type-casting to the address returned into a DWORD as the variable you are trying to store it in is type of DWORD. If you don't, it won't work right.

The above code gets the address of ZwTerminateProcess in the module ntdll.dll (ntdll!ZwTerminateProcess).

Now you need to unprotect the memory at the address of ZwTerminateProcess in the IAT. This is so you can place your JMP instruction to the address you want to have code executed at (our callback). As stated previously at the beginning of this thread in Chapter 1 when we spoke about memory protection, we can use VirtualProtect(...) WINAPI function. You can find the MSDN documentation for this function here: https://msdn.microsoft.com/en-us/library/windows/desktop/aa366898(v=vs.85).aspx

I'll quote some information here:
_In_ LPVOID lpAddress,
_In_ SIZE_T dwSize,
_In_ DWORD flNewProtect,
_Out_ PDWORD lpflOldProtect
The above quoted information is the parameters which are on the MSDN page. There are 4 parameters used. The first is the address we want to start at, the second is how many bytes we want to make an affect on, the third is the protection setting (since we want to unprotect memory for us to read and write to it, we want the new protection setting to be PAGE_EXECUTE_READWRITE) and then the last parameter we can use to store the old settings into a variable (we can use DWORD).

Just so you know, PAGE_EXECUTE_READWRITE is defined in Windows.h (header file library for using Windows API functions) and it's a constant value which represents 0x40. We can unprotect the memory at the address we got earlier of ZwTerminateProcess by using the VirtualProtect function like this (2 examples):

Code:
DWORD dwOldProtectionSetting = 0; // this will store the old protection settings

// First example (using the LPVOID and PAGE_EXECUTE_READWRITE defined for us):
VirtualProtect(lpAddressOfFunction, 10, PAGE_EXECUTE_READWRITE, &dwOldProtectionSetting);

*/ This will unprotect 10 bytes from the address of ZwTerminateProcess, more than enough room for an x86 trampoline IAT hook. /*

/* However, if we used the DWORD earlier, we have to use typecasting to make it into an LPVOID for using VirtualProtect, since earlier as we saw the parameter for VirtualProtect for the target address is LPVOID*/

/* In the next example, I'll also show 0x40 instead of PAGE_EXECUTE_READWRITE. It'll do the same thing as 0x40 is defined as PAGE_EXECUTE_READWRITE */

VirtualProtect((LPVOID)dwAddressOfFunction, 10, 0x40, &dwOldProtectionSetting);
with 3 more lines, you can successfully set the hook (just make sure you have the callback at least).

I will write 2 more lines (the next line to place the x86 jmp opcode instruction as the byte representation and then the last line to reprotect the memory, you'll have to do one line which is place the address of the offset of the callback and the target address + 1 from the target address. The reason you + 1 from the target address before placing the offset address is because if you don't you'd be overwriting the JMP instruction and you want the new address to JMP to + 1 so it's after the jmp instruction:

Code:
*(BYTE*)(lpAddressOfFunction) = 0xE9;
// here you work out offset math and place the new address + 1 from target address so it's after the 0xE9 jmp opcode instruction
VirtualProtect(lpAddressOfFunction, 10, dwOldProtectionSetting, &dwOldProtectionSetting);

// the last line with VirtualProtect is where we reprotect the memory. The variable DWORD of dwOldProtectionSetting contains the old settings before we unprotected the memory earlier, therefore we use it to reprotect the memory now and restore the protection settings of unprotected memory to the dwOldProtectionSetting.
All of the above is for the x86 process hook (the VirtualProtect and getting the address of the function is used on an x64 hook as well, just 0xE9 is not used on an x64 hook because x64 Assembly opcode instructions are different to x86, you JMP with 0x48 and use 0xB8. So for x64 hook, things will be different.

If you wanted to prevent a program from terminating any process via ZwTerminateProcess (when a function like TerminateProcess is called, the ZwTerminateProcess function is called), you can just return 0xC0000022 which represents STATUS_ACCESS_DENIED. The reason you return 0xC0000022 is because it is an NTSTATUS return value for STATUS_ACCESS_DENIED, and you use NTSTATUS return value for it because ZwTerminateProcess is an Native API function. So to block process termination via ZwTerminateProcess (for all processes, including the process the hook is set for), the callback would look like this:

Code:
NTSTATUS NTAPI ZwTerminateProcessCallback(_In_ HANDLE ProcessHandle, _In_opt_ NTSTATUS ExitStatus)
{
return 0xC0000022;
}
Any request to ZwTerminateProcess will be denied with Access Denied. Filtering can be added to only block if the process notepad.exe is being attacked by ZwTerminateProcess and then allow it via returning the trampoline to the function with the correct parameters if it isn't trying to attack notepad.exe. So a rootkit would filter out the parameters to block it's process form being attacked by certain functions, and allow for anything but it's process.

Little diagram which may help:

However the above is not patching the API with a trampoline IAT hook technique. To use a trampoline, you'll need to create a variable of the function and then use that to setup the trampoline at the target address. I'll start you off a bit with some tips if you wanted to do this:

1). Use VirtualAllocEx to allocate memory in the process for the trampoline bytes


2). Create the trampoline variable like this:

Code:
typedef LONG(NTAPI *oZwTerminateProcess)(_In_ HANDLE ProcessHandle, _In_opt_ NTSTATUS ExitStatus);

oZwTerminateProcess trampolineVar;
So you'd use trampolineVar variable as the trampoline for setting it up.

Then since ZwTerminateProcess is an NTAPI, you have to do this to return back:
Code:
NTSTATUS ZwReturn = trampolineVar(ProcessHandle, ExitStatus);

return ZwReturn;
The above code would create an NTSTATUS return value of the trampoline with the original parameters. But if you wanted to nullify it or alter the parameters, you could modify the parameters... So instead of it terminating the process it was going too, you could make it terminate another process:

Code:
HANDLE ZwHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, PIDHERE);
NTSTATUS ZwReturn = trampolineVar (ZwHandle, 0);
return ZwReturn;
So now instead of it terminating the target process, it'll get a HANDLE to the process (replace PIDHERE with the PID of the process, you can get the PID programmatically by making a function to get the PID from the processname with the <TlHelp32.h> library and using PROCESSENTRY32.

So you could make a check, if it is trying to terminate the rootkit process, just make it find another process and terminate it... Or you could set the rootkit process as a CRITICAL_PROCESS in the hook, then allow the function call to continue and if the rootkit process is terminated, it'd result in an BSOD crash with the error message as CRITICAL_PROCESS_DIED.

Rootkits can hook ZwTerminateProcess as a method of protecting themself, but they can also protect against process suspension by hooking ZwSuspendProcess and ZwSuspendThread (prevent the process' threads being suspended via an remote attack from another process). DLL injection into the rootkit process can be prevented by hooking LdrLoadDLL which is a Native API loader function and it's called when a DLL is loaded, so you can prevent loading of a DLL into the process (injection) by hooking the function. Hooking ZwOpenProcess is also idea because if the attacker cannot get the HANDLE to the process, well then it's out of luck if it wants to do things like inject into it, terminate it, suspend it, ... You can do other things such as hide processes from Task Manager, hide and protect registry keys and files, spoof fake registry values in regedit and programs trying to read the value of a key when in actual fact it's not what it appears to be to the user... So much power can be done via hooking. IAT hooking can be very powerful if applied well and created well, however it has some disadvantages:

1). It can be detected very easily. Inline hooking is harder to detect, although it's not covered here. A better made rootkit would use remote hooking via code injection and it'd be inline.

2). If the hooks are detected, they can be removed easily by restoring the prologue to the function with the correct bytes and opcode instructions to the prologue.

3). IAT hooks can be bypassed via run-time linking. It will work well for dynamic linked functions from modules though.

To perform IAT hooking, you need to have a method of writing to the memory of another process. This can either be done via code injection, or via DLL injection. DLL injection is probably a common method used by beginners, code injection is more advanced in my opinion and better option overall for the task.

2.1 - How rootkits perform and how they use SSDT (System Service Dispatch Table) hooking

On x86 systems (we mentioned about PatchGuard already which is why we do not use it on x64 - SSDT hooking can occur on x64, I remember reading about this over at CodeProject in the past when I first started learning about the SSDT: http://www.codeproject.com/Articles/28318/Bypassing-PatchGuard, also mentions a conversaton between Symantec and Microsoft regarding SSDT hooking).

SSDT stands for the System Service Dispatch Table, however it can also be known as the System Service Descriptor Table. The System Service Dispatch Table holds the pointer addresses to Native Windows API functions in what you can call a "table". By "table", I am referring to a massive structure of values of pointer addresses, one pointer address for each function.

The System Service Descriptor Table is a big dispatch table which contains the pointer addresses to the functions.

SSDT hooking can only occur if KeDescriptorTable is exported. This thread won't cover code on hooking the SSDT, although I will give some tips on SSDT hooking. (not complete though). If you want to learn how to perform SSDT hooking, run a Google Search and you'll find many tutorials on learning. I learnt SSDT hooking by reading the code off opensource rootkits (so I could learn how it all worked and how they worked, what functions they hooked etc).

The structure for the System Service Descriptor Table is as follows (beneath):
Code:
typedef struct _SSDescriptorTable
{
    MEETING ServiceTableBase;
    MEETING ServiceCounterTableBase;
    DWORD NumberOfServices;
    CUP ParamTableBase;
}SSDTSTRUCT,*PSSDTSTRUCT;
At the bottom, you will notice SSDTSTRUCT and PSSDTSTRUCT. You can use PSSDTSTRUCT (pointer) to create a pointer variable of the System Service Descriptor Table which can be used for hooking the SSDT.

Like every hook, you'll need to make a callback. An empty callback in a driver for SSDT hooking (let's say for NtTerminateProcess) can look like this:

Code:
NTSTATUS NtTerminateProcessCallback(HANDLE ProcessHandle, NTSTATUS ExitStatus)
{

}
In a kernel-mode driver, you have the DriverEntry and DriverUnload functions. An example of a kernel-mode driver with just the 2 routines for DriverEntry and the additional DriverUnload is shown below (however you can do other stuff):

Code:
#include <ntddk.h> // driver header

void DriverUnloadRoutine(PDRIVER_OBJECT pDriverObject)
{
UNREFERENCED_PARAMETER(pDriverObject);
DbgPrint("Kram7750 is peace signing out :D");
}

NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegistryPath)
{
DbgPrint("Kram7750 driver has loaded :D");
pDriverObject->DriverUnload = DriverUnloadRoutine;
return STATUS_SUCCESS; // driver successfully loaded and got to end of DriverEntry routine
}
That is a very basic driver, when it is loaded it will print out to the debugger "Kram7750 driver has loaded :D" and it will print the message encapsulated in speech marks with the DbgPrint function in the DriverUnloadRoutine when the driver has unloaded.

The reason I am explaining this is before you can hook the SSDT, you need to know how to make a basic driver. (and kernel-debugging knowledge is advised).

The function DbgPrint(""); will print out the contents in the speech marks out to the debugger. You can use a debugger like DebugView [https://technet.microsoft.com/en-us/library/bb896647.aspx].

The DriverEntry (NTSTATUS type) is what you can call the entry point of the driver, like on an Windows user-mode application, you have main/WinMain. DriverEntry is the entry for the driver code from the driver.

The DriverUnloadRoutine is a type of void because it doesn't return a value. Void functions don't return a value, whereas NTSTATUS returns an NTSTATUS value (like with the DriverEntry, we return STATUS_SUCCESS and that tells the OS that the driver successfully loaded and executed the code in the DriverEntry routine successfully). It doesn't return a value from the function because it's the last code from the driver before it's unloaded. It happens on the unload routine, when the driver is being unloaded from being loaded in kernel-mode.

You may have noticed the 2 parameters in the DriverEntry routine: PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegistryPath. pDriverObject is a pointer variable to type of PDRIVER_OBJECT, pRegistryPath is the pointer to the PUNICODE_STRING variable type. That is why there is a "p" at the beginning, (could be capital too), it's commonly used by programmers to represent a "pointer".

Remember that we are in kernel-mode and we have access to all the virtual memory for all processes running (and access to the hardware). If an error/crash occurs, it will result in an BSOD crash.

You can compile drivers using either DDK (Driver Development Kit), used for older Operating Systems like Windows XP, or with WDK (Windows Driver Kit) which supports newer Operating Systems like Windows 7, Windows 8/8.1.

Anyway, back to the SSDT hooking...
Once you have the callback setup for the function you are hooking, you must store the service number for it in a variable and make sure to backup the pointer address.

You'll need to disable write protection via the cr0 register and then enable it again. If the cr0 register is not equal to 1, then you can write to read-only protected memory via kernel-mode. If it's not equal to 0, then you cannot.

After disabling write protection, you can backup the old service numbers/pointer addresses to functions you are hooking in the System Service Dispatch Table and then you can write your own pointer address to your callback to the function by replacing the old pointer address in the Dispatch Table. This means when the function is called (functon you hooked), your callback code will be executed instead because the address has been set to the pointer address of your function, as opposed to the original one.

After placing the hook, enable (restore) write protection again to keep things more secure and working correctly.

Rootkits take advantage of SSDT hooking to provide new capabilities to the rootkit. It can use SSDT hooking to do things such as protect its process (in the callback, filter out the parameters and if the call for e.g. NtTerminateProcess is targeting the rootkit usermode process, it can return STATUS_ACCESS_DENIED), hide its process from programs like Task Manager by hooking functions like NtQuerySystemInformation, hide files and protect them from removal, hide and protect registry keys/registry values, ...

From kernel-mode, all you have to do from the callback is:
Code:
return STATUS_ACCESS_DENIED;
To deny access for the action in the callback hook. STATUS_ACCESS_DENIED is defined in ntddk.h.

2.2 - How rootkits perform and how they use IRP (I/O request packet) hooking

IRP stands for I/O Request Packet, therefore IRP hooking is hooking of the I/O Request Packets. I/O requests are sent through drivers to be processed, sometimes multiple drivers for different things. To learn about I/O Request Packets, I recommend you just read the MSDN information provided here: https://msdn.microsoft.com/en-us/library/windows/hardware/hh439638(v=vs.85).aspx

I'll quote a part you should definitely read:
Most of the requests that are sent to device drivers are packaged in I/O request packets (IRPs). An operating system component or a driver sends an IRP to a driver by calling IoCallDriver, which has two parameters: a pointer to aDEVICE_OBJECT and a pointer to an IRP. The DEVICE_OBJECT has a pointer to an associated DRIVER_OBJECT. When a component calls IoCallDriver, we say the component sends the IRP to the device object or sends the IRP to the driver associated with the device object. Sometimes we use the phrase passes the IRP or forwards the IRP instead of sends the IRP.

Typically an IRP is processed by several drivers that are arranged in a stack. Each driver in the stack is associated with a device object. For more information, see Device nodes and device stacks. When an IRP is processed by a device stack, the IRP is usually sent first to the top device object in the device stack. For example, if an IRP is processed by the device stack shown in this diagram, the IRP would be sent first to the filter device object (Filter DO) at the top of the device stack.
I don't know how to explain it better than Microsoft themselves yet, so that should help you learn.

If a rootkit set an IRP hook, it could do things such as protect files from being accessed/removed from the system. Of course it could just hook the SSDT with functions like NtDeleteFile, NtWriteFile, NtOpenFile or try IAT hooking for it, but IRP hooking is much better for protecting files because with one hook you can prevent file access which also prevents the file being written too, being removed, moved and so on. You need file access to do all sorts of those things, therefore if a rootkit decided to set an IRP hook, it can prevent its files to target protection from being removed by the user/products trying to remove it, which makes things much more difficult to rid of the infection.

In IRP major functions, they have structures which contain pointer addresses. Like with the SSDT Dispatch Table, all you have to do is modify the pointer address to what you can call a callback function, however the callback function MUST have the same structure as the IRP major function you are hooking. At the end of the "callback" hook, you return with the original pointer address to let the execution flow continue for the request.

Rootkits can make use of IRP hooking not just to protect files by hooking a major function like IRP_MJ_CREATE, but it can also use it for keylogging technology.

IRP_MJ_CREATE is used to get an handle on an object, however it is used to do things such as create or open a file (and then you have the handle). Which is why hooking this major function will allow you to protect a file. It also works against kernel-mode drivers, if a driver uses a function like ZwCreateFile then the IRP_MJ_CREATE function will be invoked from the kernel stack with the request. It can even be used for somewhat "Real-Time protection".

Here is some information from MSDN you may find useful:
The I/O Manager sends the IRP_MJ_CREATE request when a new file or directory is being created, or when an existing file, device, directory, or volume is being opened. Normally this IRP is sent on behalf of a user-mode application that has called a Microsoft Win32 function such as CreateFile or on behalf of a kernel-mode component that has called IoCreateFile, IoCreateFileSpecifyDeviceObjectHint, ZwCreateFile, orZwOpenFile. If the create request is completed successfully, the application or kernel-mode component receives a handle to the file object.

2.3 - How rootkits perform and how they use IDT (Interrupt Descriptor Table) hooking

The IDT stands for the Interrupt Descriptor Table, is pretty much a massive structure in kernel-mode containing pointer addresses (I'm sure we all know that SSDT, IRP, IDT hooking... POINTER ADDRESSES ARE INVOLVED!). The pointer addresses from the Interrupt Descriptor Table get stored in the ISR (Interrupt Service Routine). The Interrupt Service Routine is used when an interrupt occurs on the system, meaning since the pointer addresses are already stored in the ISR, they can be accessed.

An interrupt is used to handle an event or exception in a program which occurs by the processor. The process works with the interrupts to execute the instructions of course.

To hook the IDT, you'd need to have an ISR (Interrupt Service Routine) with the same structure and then you'd need to work with the pointer address to overwrite it with your own pointer address (like with SSDT hooking, we place the pointer address in the System Service Dispatch Table with our own).

Rootkits can use IDT hooking for all sorts, since it has control of intercepting the interrupt calls... If you get an IDT rootkit, then good luck. But IDT hooks can be detected, so don't

2.4 - What the GDT (Global Descriptor Table) is

The GDT stands for the Global Descriptor Table. Once again, another "Table", and it contains pointer addresses just like the Import Address Table, System Service Dispatch Table, IRP hooking (the structures with pointer addresses to the major functions) and the Interrupt Descriptor Table. See a pattern here? Pointer addresses.

The Global Descriptor Table is essentially a structure (data structure) for the IA32 (x86) processor. The GDT stores the global segments, and then there is the Local Descriptor Table (LDT) which holds the memory segments.

I know least about the GDT compared to the others, I am currenty studying it a lot (both for OS development and for learning about GDT rootkits and unhooking it), therefore I advise you read the content on the following website, it helped me: http://wiki.osdev.org/Global_Descriptor_Table

2.5 - How rootkits protect themselves from detection and removal via hooking

Hopefully you have understood a lot in this thread so far, and if you have/have experience before reading this thread, chances are you can already guess how a rootkit can now protect itself from detection and removal. But for those who do not know, I will explain it, and also introduce some more things such as how a rootkit would go about hiding and preventing a registry key from view/removal.

To start off, I'll kick off the focus on protection against process termination. Before a rootkit developer can start protecting his process, he needs to take a few things into consideration: what methods he will use to protect the process, and what functions will be involved to prevent at attacking his process. Process termination comes under process protection. As we know, rootkits are famous for protecting their processes from being attacked, both by users using tools after discovering them, and by security software attempting to remove the rootkit. In our example, I will mention the function NtTerminateProcess and talk about different hooking techniques for preventing process termination.

The function NtTerminateProcess is an Native API function and it belongs in the ntdll.dll module (hence why it has the Nt* suffix at the beginning). From user-mode you use NtTerminateProcess, from kernel-mode you use ZwTerminateProcess. Zw* functions are the same as Nt* functions from user-mode, however from kernel-mode you don't use Nt* functions, you use Zw* functions. Therefore, if you wanted to use NtTerminateProcess from kernel-mode, you'd use ZwTerminateProcess instead. However, from user-mode it won't matter which one you use.

The function NtTerminateProcess takes in 2 parameters:
Code:
_In_ HANDLE ProcessHandle

[SIZE=4]_In_opt_ NTSTATUS ExitStatus

To use the function to terminate a process, you must have the HANDLE to the process. An HANDLE is a pointer to an object. If you get the handle to a process, you can proceed to use the function. In C++, to get the handle of a process, you can use an Windows API function like OpenProcess(...).

The Windows API function OpenProcess takes in 3 parameters:
Code:
_In_ DWORD dwDesiredAccess,

_In_ BOOL  bInheritHandle,
_In_ DWORD dwProcessId

// https://msdn.microsoft.com/en-us/library/windows/desktop/ms684320(v=vs.85).aspx
The first parameter, dwDesiredAccess, is for inputting the access rights you want to have with the handle of the process. Usually, I like to use PROCESS_ALL_ACCESS which gives you STANDARD_RIGHTS_ACCESS and SYNCHRONIZE (0x00100000L).

The second parameter, bInheritHandle is type of boolean, therefore you can put in TRUE or FALSE. HANDLE Inheritance is mentioned over at MSDN, I'll quote what Microsoft say below: https://msdn.microsoft.com/en-us/library/windows/desktop/ms724466(v=vs.85).aspx

A child process can inherit handles from its parent process. An inherited handle is valid only in the context of the child process. To enable a child process to inherit open handles from its parent process, use the following steps.
  1. Create the handle with the bInheritHandle member of the SECURITY_ATTRIBUTES structure set to TRUE.
  2. Create the child process using the CreateProcess function, with the bInheritHandles parameter set to TRUE.
The DuplicateHandle function duplicates a handle to be used in the current process or in another process. If an application duplicates one of its handles for another process, the duplicated handle is valid only in the context of the other process.

The last parameter, dwProcessID (DWORD - Double Word), is meant to be the PID of the process you want to have a HANDLE on. You cannot just get a handle on a process, Windows will need to know which process, therefore Process ID is used so it knows which process you want a handle on. A Process ID is very useful, especially in these cases. Each process running on Windows is assigned it's own Process ID, this Process ID is used to differentiate between the different processes so Windows doesn't get mixed up, and it also allows the programmer to make use of them for things such as getting an handle to an object of the process via the Process ID. Process ID stands for PID for short, if you didn't know this, you do now. If you do not know the PID of the process you want to gain a handle on, you can use the libary TlHelp32.h which contains useful things to make a function to get the PID from the Process Name (string). I remember when I first started C++ with the Windows API functions I needed to get the Process ID from the process name, therefore I did research and learnt how to get the Process ID from the process name as an input with using PROCESSENTRY32 and a do while loop. I've just written out a function I learnt to do a long time ago, hopefully if anyone needs it, it's there:

Code:
DWORD retProcessId(std::string targetProcessName)

{
    HANDLE hProcess;
    PROCESSENTRY32 processEntry32;
    processEntry32.dwSize = sizeof (PROCESSENTRY32);
    hProcess = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    do{
        if (!strcmp(processEntry32.szExeFile, targetProcessName.c_str()))
        {
            CloseHandle(hProcess); // close handle to hProcess handle, better code practice
            return processEntry32.th32ProcessID; // its already DWORD so we just return it, no type-casting or conversion required
        }
    } while (Process32Next(hProcess, &processEntry32));
    CloseHandle (hProcess);
    return 0;
}
The above code works like this: you call the function with a string parameter for the process name, if the process is running and can be found, the PID is returned. If not, 0 is returned.

It can be used with OpenProcess like this (get the handle of notepad by getting the PID and using OpenProcess with it):
Code:
DWORD procId = retProcessId("notepad.exe");


// I did some debugging and made sure that the correct value was there and it worked.

// or you can use openprocess directly:
OpenProcess(PROCESS_ALL_ACCESS, FALSE, retProcessId("notepad.exe");
To setup a variable of HANDLE for using a function like ZwTerminateProcess, you must assign the handle with OpenProcess(). You can get the handle to a process via the PID with the above function for returning the PID from the process name like this:

Code:
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, retProcessId("notepad");
Anyway, back to the process protection against NtTerminateProcess... Once you have the handle, you can call ZwTerminateProcess with the ExitStatus as 0. So what you'd need to do, is hook the function NtTerminateProcess, and then inside the hook do a comparison to check if the PID from the HANDLE is for the process you are trying to protect. If it is, return an NTSTATUS return value to block the action (deny access), if it isn't, allow it to continue with the function.

For this task, you could use an IAT hook, however its relatively easy to bypass an IAT hook (and detect if a hook has been set and unhook it). Another idea could be an SSDT hook on an x86 system which would be more secure than an IAT hook (and probably easier as well in a way, because you wouldn't have to inject into processes to set your hook). An even more secure option would be via hooking the Interrupt Descriptor Table (IDT).

Hooking NtTerminateProcess can be a good idea because if you hook NtTerminateProcess, it also protects against Windows API functions like TerminateProcess. (Task Manager uses TerminateProcess).

Antivirus software usually make sure they are protected from termination attempts to both any user-mode processes or process "services" running under SYSTEM (I will actually write about processes running under SYSTEM later on, because it is possible).

Blocking attacks to the process threads for termination via hooking NtTerminateThread is also a wise decision.

However, there is more to process protection than protecting against process termination. You also need to protect against other attacks such as NtSuspendProcess, NtSuspendThread, NtOpenProcess, NtAllocateVirtualMemory, NtWriteVirtualMemory, NtUnmapViewOfSection...

Hooking NtSuspendProcess and NtSuspendThread is a good idea to prevent the suspension of your code execution. You see, a process is built up of threads, and on the threads is where your code is executed. If the threads are "suspended", they are basically paused and then the execution of your code is paused... And if your code cannot execute, the process cannot function. Because no code would be executing from your process' threads, they'd be suspended. Therefore hooking NtSuspendProcess and NtSuspendThread is important for process protection. There's not much point in protecting against process termination if your rootkit process is vulnerable to process suspension.

Hooking NtOpenProcess is a very good idea because NtOpenProcess is used to get the handle to your process. When you use functions like OpenProcess, it uses NtOpenProcess in the end from kernel-mode. Like ZwTerminateProcess and TerminateProcess, where when you call TerminateProcess, ZwTerminateProcess is called in the end from kernel-mode. If a process cannot get the handle to the process you want to protect, it cannot attempt to terminate it, suspend it, inject into it, etc.

Hooking NtAllocateVirtualMemory and NtWriteVirtualMemory is good to prevent injection into the process. (both code and DLL). However, an even better idea for preventing DLL injection would be via a loader function in the Windows API, which is LdrLoadDLL. Hooking LdrLoadDLL would be like hooking a usual Native API function, and it will protect your process from DLLs being successfully injected into them.

For hiding processes, its a bit different. Hooking NtTerminateProcess function will be good for protecting a process, but not hiding it. So how do we hide a process? You can hook a function like NtQuerySystemInformation.

MSDN have documented this function, you can view the MSDN documentation here: https://msdn.microsoft.com/en-us/library/windows/desktop/ms724509(v=vs.85).aspx

NtQuerySystemInformation is used for enumerating processes in programs like Task Manager, therefore by hooking it you can filter the parameters and results to make it hide your process (skip it).

To protect files, you can use a hook on the IRP to IRP_MJ_CREATE via changing the pointer address to your own function of it with the same structure. You can then deny access to certain files.

To protect registry keys, you can hook a funtion like NtDeleteKey to prevent removal of registry keys.

When I have some spare time, might make some sample code to explain this more (like of an SSDT hook maybe).
[/SIZE]

2.6 - What a callback is from kernel-mode, why they are used, how they are used and some examples of some callbacks which a rootkit may make use of

In kernel-mode, due to PatchGuard on x64 systems preventing the use of SSDT, IAT and GDT hooking (and other techniques for kernel patching), you have to work with something called a "callback" for things for process protection. A callback is basically like a notification when an certain event occurs, and allows you to take action on the event. For example, if we used a callback to protect a process, we could prevent it from being accessed via functions like NtOpenProcess or terminated via functions like NtTerminateProcess (and since we block NtOpenProcess, we also prevent use of other functions).

Think of it like a notification to when an event happens, which also allows you to take an action before the kernel actually executes what will happen for the event. For example, if you go to terminate a process and then you have a callback routine setup to stop this, you will be notified via the callback and from the callback you can execute your code, which can be used to prevent process termination of a specific process.

Now lets take this a step further, talk about some callbacks and how they may be used (what for) and how rootkits might make use of kernel-mode callbacks of a benefit for itself.

Firstly, for process protection, the callback I recommend you look into is ObRegisterCallbacks, its available from Windows Vista and upwards. To remove a callback set, you can use ObUnRegisterCallbacks. The routine for using it is as follows:

Code:
NTSTATUS ObRegisterCallbacks(

  _In_  POB_CALLBACK_REGISTRATION CallBackRegistration,
  _Out_ PVOID                     *RegistrationHandle
ʱ??
You can read more at MSDN (luckily its documented): https://msdn.microsoft.com/en-us/library/windows/hardware/ff558692(v=vs.85).aspx

CallBackRegistration [in]

A pointer to an OB_CALLBACK_REGISTRATION structure that specifies the list of callback routines and other registration information.

RegistrationHandle [out]
A pointer to a variable that receives a value that identifies the set of registered callback routines. The caller passes this value to the ObUnRegisterCallbacks routine to unregister the set of callbacks.
As usual, since we are dealing with kernel-mode, the return status is NTSTATUS... STATUS_SUCCESS represents the operation was successful, although if STATUS_ACCESS_DENIED (0xC0000022) is returned, then it tells you that it failed to do this (you can obtain the last error return).

Just so you are aware, I believe that Avast uses ObRegisterCallbacks for process protection... Many AVs probably use it, because its a good technique, especially for x64. And without being able to patch the kernel without exploiting KPP (which wouldn't work right because Microsoft would patch it up when they find out eventually), its probably a better idea to use it... Its more secure than hooking in a process via IAT hooks and injection.

Another useful callback is CmRegisterCallbackEx. This callback is used for registry modifications. You can process and check registry modifications before they actually occur in the registry. If you did this, you can protect registry keys from being accessed (which also prevents them from being removed). Since Windows Vista, you use CmRegisterCallbackEx instead of CmRegisterCallback.

The driver routine for CmRegisterCallback:
Code:
NTSTATUS CmRegisterCallback(

  _In_     PEX_CALLBACK_FUNCTION Function,
  _In_opt_ PVOID                 Context,
  _Out_    PLARGE_INTEGER        Cookie
);

The driver routine for CmRegisterCallbackEx:
Code:
NTSTATUS CmRegisterCallbackEx(

  _In_       PEX_CALLBACK_FUNCTION Function,
  _In_       PCUNICODE_STRING      Altitude,
  _In_       PVOID                 Driver,
  _In_opt_   PVOID                 Context,
  _Out_      PLARGE_INTEGER        Cookie,
  _Reserved_ PVOID                 Reserved
);
When the routine is used, a callback to RegistyCallback will be made. Its also availabe on MSDN: https://msdn.microsoft.com/en-us/library/windows/hardware/ff560903(v=vs.85).aspx

Code:
EX_CALLBACK_FUNCTION RegistryCallback;

NTSTATUS RegistryCallback(
_In_ PVOID CallbackContext,
_In_opt_ PVOID Argument1,
_In_opt_ PVOID Argument2
)
{ ... }
As we can see from this, MSDN is also very useful for information... As long as you are sticking to documented things. If you are sticking to undocumented things, look into debugging (both user-mode processes and debugging of the kernel) to find out new functions and things in general you can experiment.

Rootkits may use callbacks like ObRegisterCallback and CmRegisterCallbackEx because they are very useful for process protection and protecting registry keys. Using these 2 callbacks alongside an IRP hook for protecting the files will make a quite decent rootkit, especially for a beginner. You can make things even better by better hiding and stealth by the rootkit. For example, you need to make sure when you intercept an API call, you do things quickly that dont slow things down and make the user suspicious, because pretty much everybody thinks they may have an infection when their system starts slowing down a lot and behaving strange, so you need to make it so they think everything is normal.

Note that callbacks from kernel-mode are actually officially supported by Microsoft. As far as I am aware, they introduced it as an alternative of kernel patching, probably due to security software setting hooks in the kernel.

2.7 - How rootkits can inject into other processes for user-mode hooking

If you remember at the start of chapter 2 (2.0 to be precise), I gave you a quick introduction to what the Import Address Table and some information about hooking it (at the end I have left some code examples to help anyone trying to learn IAT hooking from this thread, but I recommend if you want to actually learn IAT hooking, go elsewhere because this thread is not to teach you it exactly in a detailed fasion). However, if a rootkit wants to make use of IAT hooking, it needs to have a method to inject into the process to modify the IAT of the process if its doing it at run-time instead of statically.

For beginners especially, the most common method to inject into a process would be via DLL injection. Since this is the most common method for beginners, I will cover it here. However, a more stealthy rootkit would make use of code injection (its quieter and more secure in my opinion). The reason its quieter is because to perform code injection, you do things like allocate some memory for your code to be stored and execute on a thread and write to the process memory, whereas with DLL injection, when they check the modules in the process, they'll find the DLL there (unless you'd been clever enough to hide your DLL from being shown up via hooking). Although, both are considered "quiet" and if a user has been infected without any knowledge on security, chances are you'll succeed as long as you do it so well that processes don't start crashing and slowing down too much.

First up, what is DLL injection? DLL injection is where you inject an DLL into the address space of another process where the code in the DLL becomes executed in the address space of the targetted DLL. When you use DLL injection techniques, it counts as "dynamically-linking" a DLL because you are linking the DLL into the process (injecting it in remotely) during execution time.

Before you can learn about injection though, there is some theory I should probably get covered beforehand: if you have an x86 DLL, you inject it into an x86 process, and if you have an x64 DLL you inject it into an x64 process. I won't start talking about "cheats" around the Windows OS to inject an x86 DLL into an x64 process. Every DLL has an entry point (every Portable Executable has an entry point), and the entry point for a DLL is the DllMain, shown below:

Code:
BOOL APIENTRY DllMain( HMODULE hModule,

                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        MessageBoxA(0, "The DLL has been attached to the process (injected)", 0, MB_OK);
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}
Inside of this entry point function (DllMain) we have a select case statement. The first one, DLL_PROCESS_ATTACH will have the code executed in the case when the DLL has been attached to the process. In this case, when the DLL is attached to the process, a MessageBox alert will be displayed since I inserted a MessageBoxA function call in the case.

Now I got that out of the way, lets talk about different methods to actually inject your DLL into other processes running on the system. I will list some methods below, then I'll start explaining what I have learnt on them:

1). CreateRemoteThread()
2). App_Init registry key
3). ZwCreateThreadEx/NtCreateThreadEx

CreateRemoteThread isn't a good idea unless you are targetting Windows XP, because an update was released on Windows 7 a long time ago which removed support for the function use. A lot of people complained that CreateRemoteThread function wasn't working on their Windows 7 system at the time, especially on online tutorials on using the function.

I'll be showing you how to use the App_Init registry key to have an DLL injected into a process, please bear in mind that it will only work with programs which make use of User32.dll for their program; some programs don't use User32.dll... An advanced rootkit would use methods like ZwCreateThread, or a kernel-mode component to inject into a process.

First up, there are 2 different registry keys. It depends on which architectue your Operating System is.
If your OS architecture is 32-bit then you will be using this registry key:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows

If your OS architecture is 64-bit then you will be using this registry key:

HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows NT\CurrentVersion\Windows

Once you are at that location, find the registry key LoadAppInit_DLLs. The value should be 0x00000000 by default (which represents 0 and that it is disabled). Double click the registry key and change the value data to 1 (which means it'll change to 0x00000001).

Changing the value from 0 to 1 means that the Windows Operating System will know that it should be injecting DLLs from the AppInit_DLLs registry key (value) into programs which use User32.dll.

After you have enabled it by setting the value to 1 via LoadAppInit_DLLs registry key, you can now double click on AppInit_DLLs registry key and set the data to the path of your DLL you wish to inject into programs.

After you've done this, restart your system. It should all be working now (restart for if changes don't take affect for whatever reason).

Remarks:
If your DLL is the architecture of x86 and you open up an x64 DLL, it won't be injected (work). The DLL will only be injected if the program is the same architecture as the DLL. The reason for this is because x64 programs work differently to x86 processes. x64 processes use x64 Assembly instructions for the CPU to execute, and mixing x86 code could mess up and break the program. The way Windows has been designed, this is the result. (it is actually possible to mix x86 and x64 code together via looking into functions like X86SwitchTo64BitMode).

If you use the AppInit_DLLs for 32-bit systems, if you are running an 64-bit version of Windows, the changes to the LoadAppInit_DLLs and AppInit_DLLs registry keys will be disabled. The same applies to the 64-bit copy if you are running an 32-bit copy of Windows. Make sure to use the correct one or you'll find out it isn't working and you'll be wondering why.

This can be done programmatically, you can change the value of a registy key via the Native API function NtSetValueKey. The parameters for this function are shown below:
Code:
_In_     HANDLE          KeyHandle,

  _In_     PUNICODE_STRING ValueName,
  _In_opt_ ULONG           TitleIndex,
  _In_     ULONG           Type,
  _In_opt_ PVOID           Data,
  _In_     ULONG           DataSize
Read more about it here: https://msdn.microsoft.com/en-us/library/windows/hardware/ff567109(v=vs.85).aspx

2.8 - How rootkits can load drivers (just a few examples)

Since we've got this far, I can't just end the thread and walk off! Oh no, I need to explain much more, and one of these things is how drivers are loaded (and then I'll move onto rootkit related ideas of it).

Now to start off, there are 2 legitimate methods of loading a driver onto the system: via the Service Manager (shortened down to SC Manager), or via using console commands in batch to do it.

I guess I need to talk a bit of theory as usual, the theory will be kept as short as possible and not detailed for this section: when you load a driver on the system, it runs under what we call a "service". A kernel-mode driver is loaded and executes from kernel-mode (also known as ring0). On the Operating System, there are 4 rings: ring0, ring1, ring2 and ring3. Ring0 represents kernel-mode, ring3 represents user-mode. Ring1 and ring2 you do not need to learn about, however I believe products like VirtualBox/VMWare load the kernel of the OS they are virtualizing into ring1, although I could be wrong. Ring0 is where the Windows NT Kernel runs, if you have the ability to execute code from ring0 then you have the control over the system which you need for a very stealthy and advanced rootkit, or security product. If you are working from ring3, you can still do a lot, but you are out of control if you are attacked by a kernel-mode component (driver running in ring0) because it has the control over you. In user-mode, as we learnt from chapter 1, each process is allocated its own "section" of memory and without the use of APIs to unprotect memory of a process, you don't have permission to do things like read/write to it. However, from kernel-mode, you have access to all the virtual memory on the system between all processes; you also have access to the hardware components of the system to work with them.

An example of loading a driver via batch: (first copy it to the windows directory)
Code:
sc create notarootkit type= kernel start= system error= ignore binPath= %windir%\notarootkit.sys>nul

sc start notarootkit>nul
However, this method will actually use the SC manager anyway (service manager) to create the service with the driver.

The other method is by using the CreateService function and StartService function from the Windows API from within an executable program (made in C++ for example).

Now speaking of rootkits... There are other methods to load a driver on the system, more non-recommended methods which legitimate software doesn't really resort to using directly, the first 2 methods are done via Native API functions:

1). ZwLoadDriver
2). ZwSetSystemInformation > more non-legitimate and an old function

To use ZwLoadDriver (C++):
Code:
// make sure to include <winternl.h> for NTSTATUS etc


typedef LONG(NTAPI *oZwLoadDriver)(UNICODE_STRING DriverServiceName);
To be completely honest with you, I don't know why I always use a "o" before the function name when I do the above... I should use a "p" to represent a pointer address, but oh well!

Anyway, to actually use the above, you must do the following: (C++)
Code:
oZwLoadDriver ZwLoadDriverFunc = (oZwLoadDriver)GetProcAddress(LoadLibraryA("ntdll.dll"), "ZwLoadDriver");


// now you can use ZwLoadDriver using ZwLoadDriverFunc:

ZwLoadDriverFunc(...);
The way the above works is first you use typedef to setup a pointer address for use of the function with the parameters, and then you create a new variable using it and assign the new variable to the type of the oZwLoadDriver and assign the address of the ZwLoadDriver function to it (ntdll!ZwLoadDriver).

GetProcAddress will return the address of the given function in the given module name. You can use GetModuleHandle() instead of LoadLibaryA, but only if you already know the module is already linked in.

Please note that you will need the below typedef struct to use the ZwLoadDriver. The reason is because the parameter for it is type of UNICODE_STRING (DriverServiceName), therefore you'll need it.

Code:
typedef struct _LSA_UNICODE_STRING {

  USHORT Length;
  USHORT MaximumLength;
  PWSTR  Buffer;
} LSA_UNICODE_STRING, *PLSA_UNICODE_STRING, UNICODE_STRING, *PUNICODE_STRING;
Although the service manager is used anyway. ZwSetSystemInformation direct use for loading a driver is more of an old and unknown method to some, but it certainly does get used.

However, now to move back onto the topics of rootkits, they have a few tricks to make their way into the kernel mode via a driver without doing the work themselves to start the service, etc. I will mention one, installing the driver to load at boot via the Registry. Since we don't require a driver to be digitally signed on 32-bit systems because it doesn't include PatchGuard. I won't go further in discussing "tricks", even if they are known.

2.9 - How a user-mode rootkit can prevent injection into it's process

As we already know, a kernel-mode rootkit can be used to protect it's user-mode process(es), but what if a user-mode rootkit wanted to protect itself without a driver? Don't worry, there's a very good method which can be applied to do just that... Several. I will mention some of the more obvious ones, then I will mention the best one (from my point of view, even Antivirus software from big vendors might apply the technique).

Before you can choose a method, you need a technique to apply... Now this will involve hooking, since we are not in the kernel we don't have access to SSDT, IDT, GDT by default (unless you found a very big vulnerability to exploit, haha). Therefore, we'll put the focus at hooking the IAT.

The first method to prevent injection would be without hooking. It can work by regularly checking which modules are in your program, and if any which shouldn't be there are in the address space of your program, try to unload the module from the process. But there's a downside to this method which I should point out: this will only work for unloading DLLs because code injection doesn't require a DLL... And since you'd be enumerating modules, you'll find the DLL libraries. The other downside is unloading the DLL can cause instability and even cause your process to crash, it really depends what the DLL did. If the program (executable) had been patched to use functions from the DLL injected into your program, as soon as your program calls a function patched in, if the module no longer exists in your process it'll cause the program to crash because it would be trying to use a function it cannot find (in memory).

The next 2 methods use hooking, this second method involves hooking functions like CreateRemoteThread, NtAllocateVirtualMemory, NtWriteVirtualMemory, NtCreateThread... However, why bother when the third solution is much better? I guess there may be times you'd want to do such thing... Such as if you were trying to make a BB (behaviour blocker) and wanted to monitor injection, therefore of course you'd monitor uses of functions and then decide where to pause execution flow of the program and either block it, alert for user-intervention, etc.

The third method, this is the best method for preventing injection into your process I think. Hooking LdrLoadDLL... LdrLoadDLL is a loader function (Native API). If you hook this, you should be sorted. If an AV hooks this function, it should be protected well enough to what it needs against injection attacks like DLL injection.

2.10 - How a rootkit can protect it's driver from being unloaded from user-mode API call requests

Haha, messed up the numbering order again.. So this section is 2.10.

So we've spoken about how drivers might be loaded... Now about unloading drivers! Again, this topic won't be very big and neither was the one about loading drivers... I'll keep this one nice and short if I can, but let's continue!

So as we now know, ZwLoadDriver can be a used method involved in the process of loading a driver onto the system. Guess what! There is also a function called ZwUnloadDriver.

Code:
NTSTATUS ZwUnloadDriver(
_In_ PUNICODE_STRING DriverServiceName
);
**It also requires PUNICODE_STRING (unicode) structure like ZwLoadDriver.

Service Manager can also be used as usual, haha.

2.11 - Examples of dangerous things a rootkit can do from kernel-mode (unrelated from hooking, more about the Master Boot Record)

So far I haven't mentioned the Master Boot Record on this thread... So I thought I'd include it here. Before I can explain furthermore about a rootkit and the Master Boot Record (which actually moves into the term "Bootkit" instead of "Rootkit"), I must explain some theory as usual.

The Master Boot Record is a disk sector, its 512 bytes in size. Its used to load the OS (in conjunction with the OS boot loader). That should be all you need to know for now. To learn more, I have a thread on the Master Boot Record over on the Malware Analysis area, reading it might be of help as it does explain things: http://malwaretips.com/threads/detecting-mbr-modification-malware-analysis.44334/

From Windows Vista and onwards, you are required to have a kernel-mode driver to modify the Master Boot Record. You will be unable to get a handle on it from user-mode, like you could do from Windows XP (speaking about bootkits, I still remember the bootkit I was infected with on Windows XP) unless you found a vulnerability to modify it from user-mode.

If a kernel-mode driver is loaded from Windows Vista and upwards, the rootkit will be able to modify the Master Boot Record. Modifying the Master Boot Record can be useful in some cases, for example bootkit infection will work by modifying the Master Boot Record to startup before Windows does, exploiting PatchGuard (in the past when PatchGuard had been exploited to load a driver which was unsigned on 64-bit systems, it involved use of MBR access and modification, working by doing the work before PatchGuard has loaded), or just making the system unbootable to the Operating System. Modifications to the Master Boot Record can leave the system unbootable to the Operating System, leaving error messages on a blank black screen such as "Operating System Not Found". (I actually encountered this issue when I was infected by a bootkit when I had Windows XP, which would have been easy to happen... It was about 5 years ago now I think). Bootkit activity can be used in rootkits, it can help the rootkit startup early (bootkit). Just it has to make sure it doesn't affect system startup too much or the user might get worried and think they have an infection, then start investigating.

From kernel-mode, you can also access hardware enough to do things such as infect it. I'm sure you've all heard of a "hardware rootkit", right? While I am not covering hardware rootkits in this thread (maybe in the future when I've studied it enough I will), they are very deadly and usually result in changing your hardware components. It can be useful to the rootkit, though. If a rootkit wrote directly to your hard disk, it could allow it to reinstall the rootkit after reinstalling the Operating System (although formatting the hard disk wouldn't work with that technique). Although other infections occur on the firmware (BIOS). Again, very deadly. You'd have to try all sorts of stuff such as flashing your BIOS.


2.12 - List of functions a rootkit can hook

There's a bunch of functions:

- NtTerminateProcess
- NtOpenProcess
- NtSuspendProcess
- NtTerminateThread
- NtSuspendThread
- NtAllocateVirtualMemory
- NtWriteVirtualMemory
- NtCreateFile
- NtWriteFile
- NtOpenFile
- NtDeleteFile
- NtCreateKey
- NtSetValueKey
- NtEnumerateKey
- NtDeleteKey
- NtQuerySystemInformation
- NtSetSystemInformation
- NtLoadDriver

Chapter 3: methods of detecting rootkit hooks

To detect an IAT hook via a JMP instruction, you must read the process memory at the target address of the function you want to check is hooked and check if it contains 0xE9 as the instruction for an x86 process, and for an x64 process check if it contains 0x48. If it does, you must then restore the prologue of the function to unhook it. You can obtain the prologue of the function.

To detect an SSDT hook, you must load a kernel-mode driver to scan the SSDT and check the pointer addresses in the dispatch table to the System Service Descriptor Table. If the pointer address is not the same pointer address exported in the ntsokrnl, then it means that the function has been hooked.

Just so you are aware, this thread was meant to be much more detailed. Chapter 3 had much more detail and individual topics for both detecting and restoring an IAT and SSDT hook. Originally, I had included source code examples of a rootkit setting an IAT hook and then code to detect it. But this is MalwareTips and I doubt it'll be found useful by anyone who is trying to do good and not bad anyway, so I have removed this... And code examples at the bottom of this thread which were originally there whilst writing the threads.

I hope this thread helped you learn a bit more, hopefully at least one new thing about Windows/CPU/memory/rootkits... It was a pleasure writing the thread. Took hours and hours to write though, a lot of time. But no worries, as long as at least one person finds it helpful, then it was well spent time I think.

Things like hardware rootkits weren't discussed here... Maybe in the future I'll make a thread dedicated to it. The reason I haven't added it here is because I currently focus mainly on software because I'd like to make sure I've mastered a specific computational concept or topic before moving on to another one, so maybe in the future when I've studied more about hardware rootkits, I'll make a thread on it. And since I am still studying things (especially IDT, GDT, IRP hooks) further, it will take time.

If anyone found a mistake in spelling/information here, please let me know... When I started writing this (well when I worked on this thread full stop over the period of 2 - 3 days) I was tired as I could only work on it properly in the evenings after a hard day of work... So please let me know to correct anything.. As it would help people who read it. I'm aware there are many spelling mistakes, I'll go on a hunt to fix them now.

Was looking forward to chapter 3, ran out of time. If people want it and I get the time I'll extend it like the original plan...

People who may find this thread of interest:
@MalwareHunter @Malware1 @Dani Santos @yigido @thepierrezou @Kardo Kristal @Klipsh .. I don't know who else might.

Cheers. ;)
 
Last edited:
L

LabZero

Guest
#2
Well, extremely technical and interesting mega-thread that goes more specifically by providing great understanding tools.

Very interesting the IAT hooking technique.

If the rootkit uses the redirection, the various IAT thunk will not contain the actual address of the imported API, but will aim to put the stub packer who then will jump to imported API code.

If It used the stolen bytes technique, so that the stub also contains API code, becomes extremely difficult to reconstruct the IAT, since there is a direct reference to the entry point of the API.

These days I'm studying unpacking very thoroughly, and thanks @kram7750 for this valuable information you've shared..:)
 
Last edited by a moderator:

kram7750

New Member
Joined
Apr 12, 2014
Messages
993
#3
@Klipsh I'm glad you like it, time well spent.

The IAT technique can be useful, but it can be bypassed of course. That is why I think that patching of the kernel is much more secure; Microsoft just irritate a lot of people with PatchGuard because then the kernel patching techniques don't work on 64-bit versions... And it puts a massive block off for security developers, but for malicious software it's not a problem as much because a lot of malware is user-mode and more advanced user-mode malicious software will just bypass the IAT hooks, and if malware wanted to patch the kernel on an 64-bit system, if the developer of it was experienced and clever enough, they could try to work and find a vulnerability to bypass PatchGuard (which usually involves some method related to the Master Boot Record - good thing you need a kernel-mode driver from Vista and upwards to access it, but still).

Microsoft should allow security vendors to bypass PatchGuard; block it off for everyone else - so security software has the control it really needs to fight advanced malware and preventing anything like a kernel-mode rootkit even if it's very sophisticated, and prevent other malicious software from using the kernel patching techniques on an 64-bit system.

If a rootkit decides to make use of IAT hooking (a rootkit made by a new developer would probably use it, you learn user-mode hooking before kernel-mode usually), then it would mean that instead of the original pointer address in memory for the function in the IAT, there will be an instruction opcode to allow it to move to another address (JMP instruction is most common in my opinion but other opcode instructions like call can be used) and then a pointer address next to that. Meaning when the lookup to go to the address of the function occurs with the IAT, it will execute the instruction of moving to the new pointer address which is actually inside the address space of the process calling it (since you injected into the process and have your callback function).

If a hook has been set via the IAT, and you detect it based on finding the byte instruction representations to a memory address, you can continue to do more research and investigation via the use of debugging; debugging is very useful in this scenarios, especially whilst trying to learn about new undocumented functions on Windows. I recommend WinDbg for a debugger, I use it because I prefer it to OllyDbg and any others. Once you have detected the IAT hook, it can be removed and the function can be restored back to its original function prologue.

Nothing is full-proof, I just believe that kernel-mode is more secure. When I write security tools, I like to use both user-mode and kernel-mode, so it works like this: user-mode protection is there, however if it's bypassed then the kernel-mode protection kicks in.

I will keep editing this thread, adding more to it when I have time. There was meant to be a lot more topics in Chapter 2, and Chapter 3 was meant to be very big, but due to time I stopped yesterday and posted it. Therefore if it is wanted to be more complete (and I have the time), I'll happily improve the thread and make it as it was originally planed. I'll let you know when I've edited it with the finish.

Cheers.
 
Status
Not open for further replies.