Search This Blog

Showing posts with label kernel module. Show all posts
Showing posts with label kernel module. Show all posts

Monday, March 12, 2018

"Mastering Assembly Programming" - the review

I have recently been contacted by one of my respected readers, who has not only purchased the book but also offered to write a review once he is done reading it. Given the fact that, by now, I have not received much feedback on the book at all, I agreed, although felt a bit uncomfortable. After all, this is my first book and the only reviews I saw this far were those written by the editorial staff and the technical reviewer, getting one from a reader was rather exciting.

For some reasons (still unknown to us) it was not possible to publish the review on publisher's website as the option just disappeared, therefore, I am posting it here having the kind permission to do so from Mr. Tristano Ajmone - the author of the review. 

(The review has been originally posted as a comment on my Google+ page here https://plus.google.com/+AlexeyLyashko/posts/DeBndGe5sgH?cfem=1).

REVIEW OF "MASTERING ASSEMBLY PROGRAMMING"

10/10, because this book lives up to its promises. It's a pragmatic book by a pragmatic author. Lyashko proves himself an expert guide into the land of Assembly language; all along the journey he's always focused on the reader, on how to make his journey of exploration comfortable and profitable.


It's not the usual academic book, and it's not the usal course or manual either: Lyashko never takes the stance of the professor speaking from the high pulpit of academy, and it doesn't take the one-morsel-at-the-time approach either. The experience of this book is not that of speaking about Assembly: it's about entering the land of Assembly and finding a local guide to welcome you.

Like a native who loves his land, Lyashko warmly welcomes you to the esotic land of Assembly and makes you feel comfortably at home right from the onset. He provides you the right equipment for the journey, and dives you head-on into a guided tour of the land. Each trip (or chapter) has a specific goal to it, and as you walk toward it Lyashko shows you the surroundings, explains the history of the places you walk by, the local traditions, the anecdotes; and by time you reach your destination you no longer feel a stranger in a strangeland, you feel at home — and even if you don't yet speak the native language, you start to make sense of its sounds, to see patterns of speech emerge, you begin to reckognize isolated words and sentences. All of a sudden, the journey seems less scarier, it's turning into a thrilling adventure, and the dream of becoming a citizen of Assembly land seems an achievable goal.

The leap from high-level languages to Assembly is a huge one. No single book could ever bridge fully across this gap. At the mother-board level there are no alibis: you can't abstract away from the building blocks of the machinery. It's a very thin edge for a student to walk over, and for the authors the risk is always between extermes: providing to many details or to little, too much theory or to little. It takes a novel approach to solve these ancient dilemmas, and I believe this book does an excellent job at providing the right balance of theory and practice. But most of all, it takes a certain type of author too.

I belong to the old school, and I strongly believe that what really counts in programming is the method — nothing but the method. This book gave me method: where all I could see before where technical data and isolated tools scattered on the workbench, now I've seen a good example of how the master craftsman employs these tools in the art.

As an avid reader, I've come across too many books where competent authors failed to relate directly to their readers. The subject at matter is never as important as the gap that separates the reader from the subject itself. Programming books are always written for competent people (and it would be offensive to suggest otherwise), so it's never an issue of having to teach again the alphabet from scratch to reader; it's about filling the gap between what the reader already knows and how his knowledge has to adpat and relate to the new subject at hand. Lyashko is clearly a good teacher, he knows what Assembly newbies need in order to jump on the running wagon and get going; he knows the right balances and measures required to feed the reader's desire to learn and at the same time keep his appetite alive — avoiding an indegestion of theory, or tech-jargon poisoning.

At the end of the day, every introductiory book to Assembly has to cover the same topics (and this book is no execption). What makes the difference between a good and ejoyable book and a boring and over-complex text is the author's skills as a teacher. And I'm truely impressed by Alexey Lyashko's pedagogy: it's fresh, innovative, pragmatic, and speaks to the reader as peers do: by putting himself in the reader's shoes, he's able to relate at the same level of the reader, and raise him up all the way to the master's level — which is the promise of this book.

We all have to stand on the shoulders of giants to encompass new horizons. "Mastering Assembly Programming" gently lifts us from the barren grounds and places us on the giant's shoulders, allowing us the see the promised land of Assembly with our own eyes. Like any other journey, teachers can only take us so far by hand, then we have to carry on with our own legs — to expect more would be a mistake on the student's side, to expect less would be an unforgivable mistake on the teacher's side. Lyashko is guilty of none, for he has fulfilled his promise to the reader.

Tristano Ajmone (Italy)

Friday, April 3, 2015

Linux Loadable Kernel Module in Assembly

Hello everyone! First of all, sorry for being silent for the last two years. There have been certain reasons for this. Anyway, I am back and I am going to share a portion of what I've learnt over this period.

Before I begin, as usual, a note for nerds: the code in this article is for demonstration purposes only and does not contain certain things, like error checking, that would otherwise be inevitable. 

I have recently seen tones of posts about writing kernel module for a pre-compiled kernel on the Internet. Guys are doing good work, but there is one thing that I personally did not like - they all refer you to the configuration file for such kernel, which may be obtained this way or the other. Well, having configuration of the running kernel makes it almost no different from building a module for a kernel you compiled yourself (just almost). The bottom line - you want something to be done your way, do it yourself.

Tools used

Since building a kernel module written in C for the kernel you have no .config for may become a huge pain in certain parts of your body, I decided to go as low as possible and chose flat assembler (the good old flat assembler that may be found here). This wonderful instrument provides you with everything you may need when it comes to x86/x86_64 development (of course, most of your potential projects may be too complex for being implemented in assembly).


Target system

I was brave enough to perform this experiment on my dev machine running Debian with 3.2.0-4 kernel. Obviously, I do have proper kernel sources installed, but made no use of them in this example.


Loadable Kernel Module

I am not going to dive into the basics of Linux kernel structure and the way LKM support is implemented. It is simply irrelevant at this time. What we are interested in, is the structure of a module. To put it simple, the structure of a LKM may be described as:
  1. .init.text section  - contains all the module initialization code.
  2. .exit.text section - contains all the cleanup code executed right before the module is unloaded.
  3. Module information.
  4. All the rest.
While we may keep "all the rest" out of it for now, we do need to take care of proper representation of the init/exit sections and module information. In fact, init/exit sections are not a problem at all - that's just code after all, whereas module information is a bit problematic. But, first things first.

.modinfo section

This section contains some strings that let the kernel identify our module as a one that may be safely loaded and executed.

The first string tells the kernel about how our module is licensed:

"license=GPL"

You may use other license (e.g. "proprietary"), but that would make some symbols exported by the kernel invisible for your module.

The next one is

"depends="

here you should list modules your module depends on. Since our tiny module has no dependencies, we leave this string empty.

The last and the most important one is:

"vermagic=3.2.0-4-amd64 SMP mod_unload modversions "

this string tells us (and the kernel) which kernel the module was built for and what LKM handling options are enabled. However, the above string contains information that is good for building a module on my system, but it may (and almost certainly will) be wrong for your system. Don't worry, there is a simple way to get this string - run /sbin/modinfo on any *.ko file in your /lib/modules/`uname -r`/ directory.

__versions section

You can try to build a module without this section and it may even load and do its job, but you will get some nasty complaints from the kernel on being tainted.

The purpose of this section is to make sure your module and kernel are speaking the same language, meaning they use identical symbols. The structure of it is rather simple - an array of checksum/name pairs, where checksum is (in my case it is a x86_64 system) 8 bytes followed by a 56 bytes name (since names are shorter they are padded with 0). It is not as simple to find the proper values if you do not have properly configured kernel sources, though. You would have to simply check some modules for presence of specific symbol. I would suggest doing so in IDA Pro, but any hex editor would suffice too. 

.gnu.linkonce.this_module section

This section contains just one structure - module. I would not like to dive into specifics of this structure, after all, you can download kernel source and check include/linux/module.h file for struct module declaration. What is important to know, however, is that this structure contains the name of the module (as it would appear in lsmod's output) and pointers to module_init() and module_cleanup() functions.

Implementation

Well, seems like we've covered all the most important aspects. Let's get to the implementation itself. The following code may be compiled with flat assembler.

format ELF64
extrn printk
section '.init.text' executable

module_init:
push rdi
mov rdi, str1
xor eax, eax
call printk
xor eax, eax
pop rdi
ret


section '.exit.text' executable
module_cleanup:
xor eax, eax
ret
section '.rodata.str1.1'
str1 db '<0> Here I am, gentlemen!', 0x0a, 0
section '.modinfo' align 10h
db 'license=GPL', 0
db 'depends=', 0
db 'vermagic=3.2.0-4-amd64 SMP mod_unload modversions ', 0
  db  'vermagic=3.16.0-4-amd64 SMP mod_unload modversions ', 0

section '.gnu.linkonce.this_module' writable
this_module:
rb 18h
db 'simple_module', 0
rb 148h - ($ - this_module)
rb 150h - ($ - this_module) dq module_init
rb 238h - ($ - this_module)
rb 248h - ($ - this_module) dq module_cleanup
dq 0
section '__versions'
dq 0x568fba06
dq 0x2ab9dba5  @@:
db 'module_layout', 0
rb 56 - ($ - @b)
dq 0x27e1a049
  @@:
db 'printk', 0
rb 56 - ($ - @b)

Hope this article is helpful in some way. Thanks for reading and see you with the next post!

P.S. Updated the source to fit the latest kernel version.


Thursday, October 13, 2011

Hijack Linux System Calls: Part III. System Call Table

This is the last part of the Hijack Linux System Calls series. By now, we have created a simple loadable kernel module which registers a miscellaneous character device. This means, that we have everything we need in order to patch the system call table. Almost everything, to be honest. We still have to fill the our_ioctl function and add a couple of declarations to our source file. By the end of this article we will be able to intercept any system call in our system should there be a need for that.

System Call Table
System Call table is simply an area in the kernel memory space that contains addresses of system call handlers. Actually, a system call number is an offset into that table. This means that when we call sys_write (to be more precise - when libc calls sys_write) on a 32 bit system and passes number 4 in EAX register before int 0x80, it simply tells the kernel to go to the system call table, get the value at offset 4 from the system call table's address and call the function that address points to. It may be number 1 in RAX in case of a 64 bit system (and syscall instead of int 0x80). System call numbers are defined in arch/x86/include/asm/unistd_32.h and arch/x86/include/asm/unistd_64.h for 32 and 64 bit platforms respectively. In this article, we are going to deal with sys_open system call which is number 5 for 32 bit systems and number 2 for 64 bit systems.

Due to the fact, that modern kernels do not export the sys_call_table symbol any more, we will have to find its location in memory ourselves. There are some "hackish" ways of finding the location of the sys_call_table programmatically, but the problem is that they may work, but may not work as well. Especially the way they are written. Therefore, we are going to use the simplest and the safest way - read its location from /boot/System.map file. For simplicity reasons, we will just use grep and hardcode the address. On my computer, the command grep "sys_call_table" /boot/System.map (you should check the file name on your system, as on mine it is /boot/System.map-2.6.38-11-generic) gives this output "ffffffff816002e0 R sys_call_table". Add global variable unsigned long *sys_call_table = (unsigned long*)0xYour_Address_Of_Sys_call_table.

Preparations
We will start, as usual, by adding new includes to our code. This time, those include files are:

   #include <linux/highmem.h>
   #include <asm/unistd.h>

The first one is needed due to the fact that system call table is located in read only memory area in modern kernels and we will have to modify the protection attributes of the memory page containing the address of the system call that we want to intercept. The second one is self explanatory after the previous paragraph. We are not going to use hardcoded values for system calls, instead, we will use the values defined in unistd.h header.

Now we define two values, which would be used as cmd argument to our_ioctl function. One will tell us to patch the table, another one will tell us to fix it by restoring the original value.

   /* IOCTL commands */
   #define IOCTL_PATCH_TABLE 0x00000001
   #define IOCTL_FIX_table   0x00000004

Add one more global variable int is_set=0 which will be used as flag telling whether the real (0) or custom(1) system call is in use.

It is important to save the address of the original sys_open as we are not going to fully implement our own, instead, our function will log information about the call arguments and then perform the actual (original) call. Therefore, we define a function pointer (for original call) and a function (for custom call):

   /* Pointer to the original sys_open */
   asmlinkage int (*real_open)(const char* __user, int, int);
   
   /* Our replacement */
   asmlinkage int custom_open(const char* __user file_name, int flags, int mode)
   {
      printk("interceptor: open(\"%s\", %X, %X)\n", file_name,
                                                    flags, 
                                                    mode);
      return real_open(file_name, flags, mode);
   }

You have noticed the "asmlinkage" attribute. Well, it is, actually, a define for the attribute. We will not go that deep this time, I will just say that this attribute tells the compiler about how it should pass arguments to the function, given that it is being called from an assembly code. The "__user" macro, signifies that the argument is in user space and the function must perform certain operations to copy it to kernel space when needed. We do not need that, meaning that we may ignore it for now.

Another couple of crucial functions is the set that will allow us modify the memory page protection attributes directly. One may say that his is risky, but, in my opinion, this is less risky then actually patching the system call table as it is, first of all, architecture dependent  and we know that architectures do not change drastically, second - we use kernel functions for that.

   /* Make the page writable */
   int make_rw(unsigned long address)
   {
      unsigned int level;
      pte_t *pte = lookup_address(address, &level);
      if(pte->pte &~ _PAGE_RW)
         pte->pte |= _PAGE_RW;
      return 0;
   }

   /* Make the page write protected */
   int make_ro(unsinged long address)
   {
      unsigned int level;
      pte_t *pte = lookup_address(address, &level);
      pte->pte = pte->pte &~ _PAGE_RW;
      return 0;
   }

pte_t stands for typedef struct { unsigned long pte } pte_t and represents the page table entry Although, it is simply an unsigned long, it is declared as struct in order to avoid type misuse.

pte_t *lookup_address(unsigned long address, unsigned int *level) is provided by the kernel and performs all the dirty work for us and returns a pointer to the page table entry that describes the page containing the address. This function accepts the following arguments:

address - an address in virtual memory;
level - pointer to unsigned integer value which accepts the level of the mapping.

Let's Get to Business
We are almost there. The only thing left is the actual implementation of the our_ioctl function. Add the following lines:

   switch(cmd)
   {
      case IOCTL_PATCH_TABLE:
         make_rw((unsigned long)sys_call_table);
         real_open = (void*)*(sys_call_table + __NR_open);
         *(sys_call_table + __NR_open) = (unsigned long)custom_open;
         make_ro((unsigned long)sys_call_table);
         is_set=1;
         break;
      case IOCTL_FIX_TABLE:
         make_rw((unsigned long)sys_call_table);
         *(sys_call_table + __NR_open) = (unsigned long)real_open;
         make_ro((unsigned long)sys_call_table);
         is_set=0;
         break;
      default:
         printk("Ooops....\n");
         break;
   }

And these lines to the cleanup_module function:

   if(is_set)
   {
      make_rw((unsigned long)sys_call_table);
      *(sys_call_table + __NR_open) = (unsigned long)real_open;
      make_ro((unsigned long)sys_call_table);
   }

Our interceptor module is ready. Well, almost ready as we need to compile it. Do that as usual - make.

Test
Finally, we have our module set and ready to use, but we have to create a "client" application, the code that will "talk" to our module and tell it what to do. Fortunately, this is much simpler then the rest of the work, that we have done here. Create a new source file and enter the following lines:


   #include <stdio.h>
   #include <sys/ioctl.h>
   #include <sys/types.h>
   #include <sys/stat.h>
   #include <fcntl.h>


   /* Define ioctl commands */
   #define IOCTL_PATCH_TABLE 0x00000001
   #define IOCTL_FIX_TABLE   0x00000004


   int main(void)
   {
      int device = open("/dev/interceptor", O_RDWR);
      ioctl(device, IOCTL_PATCH_TABLE);
      sleep(5);
      ioctl(device, IOCTL_FIX_TABLE);
      close(device);
      return 0;
   }


save it as manager.c and compile it with gcc -o manager manager.c


Load the module, run ./manager and then unload the module when manager exits. If you issue the dmesg | tail command. If you see lines containing "interceptor: open(blah blah blah)", then you know that those lines were produced by our handler.




Now we are able to intercept system calls in modern kernels despite the fact that sys_call_table is no longer exported. Although, we deal with low level structures, which normally are only used by kernel, this still is a relatively safe method as long as your module is compiled against the running kernel.


Hope this post was helpful. See you at the next one!

Wednesday, October 12, 2011

Hijack Linux System Calls: Part II. Miscellaneous Character Drivers

We all know what device drivers are - the hands of the operating system that make it possible for the kernel to handle hardware.  We also know that there are two types of devices  - character and block, depending on the way they handle data transmissions, but what does "miscellaneous" device mean? To put it simple - it means what it means. On one hand, this may be a driver that handles simple hardware, on the other hand, it is the way Linux allows us to create virtual devices, as one of the ways to communicate with kernel modules, which is exactly what we need in order to hijack Linux System Calls. 

In this section, we will create a simple virtual character device, which will be used by a user space process to instruct our kernel module whether it should hijack or restore certain system call. This virtual device will be controlled with ioctl function. For simplicity, I decided not to add read/write handlers to this device as it is not really required for what we are about to do. Although, it is a "nice to have" feature.

Miscellaneous devices are represented with the struct miscdevice which is declared in /include/linux/miscdevice.h as

   struct miscdevice
   {
      int minor;
      const char *name;
      const struct file_operations *fops;
      struct list_head list;
      struct device *parent;
      struct device *this_device;
      const char *nodename;
      mode_t mode;
   };

Quite a big one, ha? However, we only should take care of the first three members of the structure:

minor stands for the minor number of the device. It is preferred to set it to
              MISC_DYNAMIC_MINOR (at least in for our module) unless you need some specific
              number to be used.
name  this is the name of our device as it should appear in the /dev filesystem
struct file_operations is a set of pointers to corresponding implementations of IO
              functions and a pointer to the owner module. 
              This structure is too big to be presented here, but you may find it in 
              /include/linux/fs.h

So, first of all, add another include file to your code (which we've written in previous article) with

   #include <linux/miscdevice.h>

Then, we should add custom handlers for functions and global variables we are interested in, namely - open, release, ioctl and variable in_use.

   /* We will set this variable to 1 in our open handler and erset it
       back to zero in release handler*/
   int in_use = 0;

   /* This function will be invoked each time a user process attempts
       to open our device. You should keep in mind that the prototype
      of this function may change along different kernel versions. */
   static int our_open(struct inode *inode, struct file *file)
   {
      /* We would not like to allow multiple processes to open this device */
      if(in_use)
         return -EBUSY;
      in_use++;
      printk("device has been opened\n");
      return 0;
   }

   /* This function, in turn, will be called when a process closes our device */
   static int our_release(struct inode *inode, struct file *file)
   {
      in_use--;
      printk("device has been closed\n");
      return 0;
   }

   /* This function will handle ioctl calls performed on our device */
   static int our_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
   {
      int retval = 0;
      
      /* We will fill this function in the Part III of this series */

      return retval;
   }

Now it's time to create the struct file_operations and struct miscdevice and populate the relevant fields:

   static const struct file_operations our_fops =\
   {
      .owner = THIS_MODULE,
      .open = &our_open,
      .release = &our_release,
      .unlocked_ioctl = (void*)&our_ioctl,
      .compat_ioctl = (void*)&our_ioctl
   }

A reasonable question would be "Why do we set unlocked_ioctl and compat_ioctl with the same value and where the heck is the regular ioctl?". There is nothing special about this. unlocked_ioctl is used on 64 bit platforms and compat_ioctl in 32 bit or in compatibility mode and it is totally normal to make them point at the same location as long as you handler function does not mess the types up. As to ioctl, it is simply not there any more...

   static struct miscdevice our_device = \
   {
      MISC_DYNAMIC_MINOR,
      "interceptor",
      &our_fops
   };

After all this, we should make a small adjustment to our init_module function by inserting the following code:

   int retval = misc_register(&our_device);

You should also change "return 0;" to "return retval;". The code above tells the system to register a miscellaneous device described by the miscdevice structure with the kernel. In case the minor field is assigned MISC_DYNAMIC_MINOR (our case exactly), kernel fills it with random (from our point of view) number, otherwise, the requested minor number is used.

Our cleanup_module function should have this line added:

   misc_deregister(&our_device);

in order to unregister our device and remove it from the system.

Important note: functions exported by kernel return 0 upon success or negative error code in case of failure.

By now we have a working kernel module which registers a miscellaneous device when loaded and unregisters it when unloaded. Build it now with the make command. Load it with insmod and check the content of the /dev file system. You will see that there is a new device called "interceptor" with number 10 as major number and what ever has been assigned as minor number. You may unload it now. 

If you wish, you may try to open and close the /dev/interceptor device from a user process and check the log with dmesg | tail. You will see the lines "device has been opened" and "device has been closed" respectively. You may also try to open the device from two user processes simultaneously, then you will see that only one process may successfully open it.

In the next section we are going to add some code to our module, which would make it possible to actually patch the sys_call_table and replace original calls with custom wrappers.

Hope this post was helpful. See you at the next one!