Debugging Linux Kernels with KASLR

This tutorial shows how to use VisualKernel to debug Linux kernels with KASLR enabled.

KASLR (Kernel Address Space Layout Randomization) is a technique that provides an extra layer of protection against certain types of attacks by changing the kernel load address each time the system is started. Although it increases the system security, it complicates debugging a KASLR-enabled kernel, as the debugger would require extra steps in order to map the functions and data in memory to the corresponding debug symbols, that won’t have matching addresses anymore, as the addresses of kernel symbols change after each restart:

In this tutorial we will create a basic kernel module under a KASLR-enabled kernel and will show how to debug it in both regular and post-mortem modes. Before you begin, install VisualKernel 3.0 or later.

  1. Start Visual Studio and open the VisualKernel Module Project Wizard:
  2. Select “Create a new kernel module” -> “Hello, World”:
  3. Select the target machine running a KASLR-enabled kernel:
  4. Proceed with the default file access settings (store sources on Windows, upload modified sources on build):
  5. Finally select the debugging settings that match your system. In this tutorial we will demonstrate debugging a crashed kernel, so we will use the direct VMWare debug connection that runs on top of the kernel itself and won’t be affected by the kernel crash:
  6. Press “Finish” to create the project. Build it by pressing Ctrl-Shift-B and, set a breakpoint in the init() function and start your module. VisualKernel will load the module and the breakpoint will trigger:
  7. Despite the use of KASLR, VisualKernel was able to map the code addresses in the memory to the meaningful symbol names and source file locations. This is achieved by querying the /proc/kallsyms file before starting the debug session, comparing its contents with the actual kernel symbols and adjusting the section offsets automatically:
  8. Now we will show how to debug a crashed kernel with KASLR enabled. Replace the main source file contents with the following code:
    #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/timer.h>
    
    MODULE_LICENSE("Proprietary");
    
    struct timer_list s_Timer;
    
    void timer_callback(unsigned long unused) 
    {
     panic("Attach debugger now!");
    }
    
    static int __init KASLRDemo_init(void)
    {
     printk("KASLRDemo: Hello, world!\n");
     init_timer(&s_Timer);
     setup_timer(&s_Timer, timer_callback, 0);
     mod_timer(&s_Timer, jiffies + msecs_to_jiffies(200));
     return 0;
    }
    
    static void __exit KASLRDemo_exit(void)
    {
     printk("KASLRDemo: Goodbye, world!\n");
    }
    
    module_init(KASLRDemo_init);
    module_exit(KASLRDemo_exit);
  9. Then open VisualKernel Project properties and enable debugging of crashed kernels and the use of offline kallsyms dumps. Click “create/update” to automatically create a dump of the kallsyms file:
  10. The dump simply contains the raw contents of the /proc/kallsyms file. If you restart your target system often, you can configure it to simply read /proc/kallsyms to a text file on each start and upload its contents to a location where VisualKernel can access it:
  11. Build the modified kernel module and load it without starting a debug session:
  12. The Linux system will crash. Go back to the Visual Studio window and start debugging by pressing F5. VisualKernel will use the dump file you created to adjust the symbol offsets for the kernel and will recover the call stack. Right-click on the “KASLRDemo + <address>” line and select “Load Symbols”:
  13. VisualKernel will load the symbols for your module and will show the location in the source file that caused the crash: