System Call Hooking
Posted on Thu 10 July 2014 in Linux Kernel Hacking
Welcome to the third post on Linux kernel hacking. In the first we looked at how to create a basic LKM and in the second we created a character device and communicated with it.
Now we are going to do something which is obviously very useful for malware, system call hooking.
Hooking a system call means that you are able to manipulate data sent from userland applications to the operating system (OS) and vice versa.
This means that you can hide things from applications running on the OS and influence their behaviour.
Here we will develop an LKM that will hide files from the unix ls
command.
Determining Relevant System Calls
The first step is to determine the system calls used by ls
to list the filenames in a directory.
strace
is a tool that can be used to trace every system call used by an application:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 |
|
This gives us lots of information, most of it is useless to us right now so we can use some shell-fu to get rid of it and only display the actual system calls that ls
is using:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
|
Now we have a decent list of system calls to look at we can use man
to look at what these system calls do.
After you have done that you will notice that getdents64
get directory entries is the one we want to look at, here is the prototype shown on the man
page:
1 2 |
|
The man page also shows the declaration of the linux_dirent
structure:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
This will help us when figuring out how to iterate through the list returned by this syscall.
Taking A Closer Look
If you want to have a look at how the system call is implemented, you can see where in the kernel it is implemented in /usr/include/asm-generic/unistd.h
:
1 2 3 4 |
|
So getdents64 is implemented in fs/readdir.c
in the kernel source.
Its worth noting that it might not tell you the relevant source file on the line above, it depends on if there were multiple syscalls implemented in the same file, have a proper look through /usr/include/asm-generic/unistd.h to see what I mean.
On my test machine this file is in /usr/src/linux-source-3.14/fs/readdir.c
because I have the source package installed:
1 2 3 |
|
We don't really need to know this for what we want to do but its handy to know if you are going to be kernel hacking.
One thing this has shown us is that getdents64
takes the linux_dirent64
struct and not the linux_dirent
struct. After some more grepping we can see that this struct is defined in include/linux/dirent.h
as:
1 2 3 4 5 6 7 |
|
This is slightly different to linux_dirent
and this means we will have to include linux/dirent.h
in our LKM.
If we look at the number of entries that was returned to ls
, we can see that it is the exact number of files in the current directory:
1 2 3 4 5 |
|
There is 1 more in the ls -la
because of the total line at the top.
Using all of the information we have gathered we can create our hook function:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
Here we just run the actual system call, loop through the struct that is returned, searching each filename (linux_dirent64->d_name
) with the static constant FILE_NAME
, and if it matches recalculating what is being returned.
The sys_call_table
The sys_call_table is the table kept by the kernel containing all of the system calls and pointers to where they are in memory.
We need to do 2 things regarding this, firstly find the address of the sys_call_table and secondly figure out how to make this table writable (because by default this table is read only).
The first part is pretty easy providing you don't want a portable version. The current kernels System.map
file will tell us this:
1 2 |
|
Easy enough, now to figure out how to make this writable.
To do this we need to change the page table entry relating to the address where sys_call_table
is stored.
We can get this entry using the lookup_address
function defined in arch/x86/mm/pageattr.c
:
1 2 3 4 |
|
As you can see it returns a pointer to some type of pte_t
structure. After a grep through the source again the definition of this structure is in arch/x86/include/asm/pgtable_64_types.h
:
1 |
|
This just contains 1 member (pteval_t pte
), luckily the definition of pteval_t
is in the same file:
1 |
|
So basically this is a structure of 1 member of type unsigned long
. The question now becomes how do we manipulate this to make the section of memory writable.
After more grepping through the kernel source it appears the answer to our questions is in arch/x86/include/asm/pgtable_types.h
, here is an excerpt:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
|
As you can see, the writable bit is 1 and can be referenced with _PAGE_RW
.
Using this information its easy to write our functions to make memory writable and readonly again:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
Putting It All Together
Now we have enough information to build our LKM:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
|
I've set the static constant FILE_NAME
to thisisatestfile.txt
. Now to edit the Makefile
:
1 2 3 4 5 6 7 8 9 |
|
Now to compile and test:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
|
Woohoo! There is 1 problem with this:
1 2 3 4 5 6 7 8 9 10 |
|
So if you put the whole filename there it still shows that the file exists but we can improve upon that later, we will need to hook different system calls.
Conclusion
There is a lot involved with manipulating the kernel like this, it requires a lot of patients and determination.
You will need to look through a lot of source code and use tools like grep
to find exactly what you need to get the job done.
Also strace
is very useful when looking for the system calls being used by an application but its also handy to be able to clean up the output for readability.
Happy Hacking :-)