I am currently working on a small LKM that searches for modules which hide themselves inside the kernel-space like some kernel-level-rootkits used to do. Cause kernel-space for a module is allocated via vmalloc() find_module_list()
iterates over all entries in vmap_area_list
and invokes check_module():
void find_module_list(void) {
count = 0;
spin_lock(lock);
list_for_each_entry(va,l, list) {
if (!(va->flags & VM_VM_AREA))
continue;
vm = va->vm;
vaddr = (unsigned long) vm->addr;
if((vaddr < VMALLOC_START) || (vaddr > VMALLOC_END))
break;
size = vm->size;
printk(KERN_INFO"ADDRESS: %lx\n",vaddr);
printk(KERN_INFO"SIZE: %lu\n",size);
count++;
printk(KERN_INFO"NUMBER: %lu\n",count);
unsigned char *i =(unsigned char*)vm->addr;
unsigned char *max = (unsigned char*)(vaddr+size);
check_module(i,max-PAGE_SIZE);
}
spin_unlock(lock);
}
int init_module(void) {
lock = (struct spinlock*)0xc1aff8a6; // vmap_area_lock
l = (struct list_head*)0xc18fd01c; // vmap_area_list
printk(KERN_INFO"VMALLOC_START: %lx\n",VMALLOC_START);
printk(KERN_INFO"VMALLOC_END: %lx\n",VMALLOC_END);
find_module_list();
return 0;
}
check_module()
gets the two parameters i
and max-PAGE_SIZE
and invokes several other functions which search for byte sequences that identify a module within that memory area declared by i
and max-PAGE_SIZE
:
void check_module(unsigned char *start, unsigned char* end) {
unsigned long err;
unsigned char *i = start;
unsigned char *max = end;
while(i < max){
err = (unsigned long)check_text_section(i,max);
if (err!=0) {
printk(KERN_INFO"FOUND FUNCTION-PROLOG AT: %lx\n",err);
}
i++;
}
}
check_text_section()
checks if the allocated memory area contains a byte sequence which is part of a function prolog `(push %ebp, mov %esp,%ebp):
unsigned char* check_text_section(unsigned char* startAddress, unsigned char* max) {
unsigned char *i = startAddress;
if ((i<max)&&((i+1)<max)&&((i+2)<max)) {
if ((*i == 0x55)&&(*(i+1)==0x89)&&(*(i+2)==0xe5)) {
return i;
}
else return 0;
}
else return 0;
}
Unfortunately I have some problems reading at some virtual addresses:
[ 1853.954993] FOUND FUNCTION-PROLOG AT: f99dc300
[ 1853.954998] FOUND FUNCTION-PROLOG AT: f99dc5b0
[ 1853.954999] FOUND FUNCTION-PROLOG AT: f99dc610
[ 1853.955118] ADDRESS: ffb8e000
[ 1853.955124] SIZE: 458752
[ 1853.955129] NUMBER: 159
[ 1853.955162] BUG: unable to handle kernel paging request at ffb8f000
[ 1853.955202] IP: [<f8abb01a>] check_text_section+0x1a/0x40 [mod]
[ 1853.955233] *pdpt = 0000000001a3d001 *pde = 000000003020d067 *pte = 0000000000000000
[ 1853.955271] Oops: 0000 [#1] SMP
[ 1853.955291] Modules linked in: mod(OF+) pci_stub vboxpci(OF) vboxnetadp(OF) vboxnetflt(OF) vboxdrv(OF) vmnet(OF) vmw_vsock_vmci_transport vsock vmw_vmci vmmon(OF) snd_hda_codec_hdmi snd_hda_codec_idt uvcvideo intel_powerclamp videobuf2_vmalloc videobuf2_memops coretemp videobuf2_core snd_hda_intel videodev snd_hda_codec arc4 snd_hwdep kvm_intel snd_pcm kvm iwldvm mac80211 snd_page_alloc snd_seq_midi snd_seq_midi_event snd_rawmidi i915 crc32_pclmul joydev bnep aesni_intel snd_seq snd_seq_device aes_i586 snd_timer xts lrw gf128mul rfcomm drm_kms_helper ablk_helper cryptd snd drm iwlwifi psmouse hp_accel hp_wmi lis3lv02d tpm_infineon ppdev input_polldev sparse_keymap serio_raw i2c_algo_bit lpc_ich wmi soundcore bluetooth cfg80211 mei_me video mei microcode tpm_tis parport_pc mac_hid lp parport binfmt_misc hid_generic usbhid hid firewire_ohci e1000e ahci firewire_core libahci sdhci_pci ptp crc_itu_t sdhci pps_core
[ 1853.955823] CPU: 1 PID: 3813 Comm: insmod Tainted: GF O 3.10.9-031009-generic #201308201935
[ 1853.955863] Hardware name: Hewlett-Packard HP ProBook 6460b/161D, BIOS 68SCE Ver. F.04 05/10/2011
[ 1853.955903] task: ec095a20 ti: ec386000 task.ti: ec386000
[ 1853.955928] EIP: 0060:[<f8abb01a>] EFLAGS: 00010212 CPU: 1
[ 1853.955952] EIP is at check_text_section+0x1a/0x40 [mod]
[ 1853.955975] EAX: ffb8f000 EBX: ffb8f000 ECX: ffb8f002 EDX: ffbfd000
[ 1853.956001] ESI: ffbfd000 EDI: 00000000 EBP: ec387e90 ESP: ec387e90
[ 1853.956027] DS: 007b ES: 007b FS: 00d8 GS: 00e0 SS: 0068
[ 1853.956050] CR0: 80050033 CR2: ffb8f000 CR3: 2ad08000 CR4: 000407f0
[ 1853.956076] DR0: 00000000 DR1: 00000000 DR2: 00000000 DR3: 00000000
[ 1853.956104] DR6: ffff0ff0 DR7: 00000400
[ 1853.956126] Stack:
[ 1853.956139] ec387ea8 f8abb0e1 00000000 00000000 00000000 000004b3 ec387eb8 f8abb200
[ 1853.956187] f8abc063 0000009f ec387ec8 f8abb316 f8abc09c ffbfe000 ec387ef8 c1002051
[ 1853.956231] 00000000 00000000 00000000 00000000 f843d000 ec387ef8 f8abb290 f8abd000
[ 1853.956274] Call Trace:
[ 1853.956290] [<f8abb0e1>] check_module+0x21/0x50 [mod]
[ 1853.956313] [<f8abb200>] find_module_list+0xf0/0x120 [mod]
[ 1853.956343] [<f8abb316>] init_module+0x86/0x90 [mod]
[ 1853.956369] [<c1002051>] do_one_initcall+0x31/0x150
[ 1853.956394] [<f8abb290>] ? find_module_plain+0x20/0x20 [mod]
[ 1853.956422] [<c1622aeb>] do_init_module+0x80/0x1c6
[ 1853.956445] [<c10aca1d>] load_module+0x33d/0x4e0
[ 1853.956466] [<c10aa990>] ? add_kallsyms+0x1e0/0x1e0
[ 1853.956490] [<c10acc56>] SyS_init_module+0x96/0xb0
[ 1853.956516] [<c163d80d>] sysenter_do_call+0x12/0x28
[ 1853.957898] Code: <80> 38 55 75 11 80 78 01 89 75 0b 80 78 02 e5 75 05 5d c3 8d 76 00
[ 1853.959348] EIP: [<f8abb01a>] check_text_section+0x1a/0x40 [mod] SS:ESP 0068:ec387e90
[ 1853.960810] CR2: 00000000ffb8f000
[ 1853.969435] ---[ end trace 055fd4e5ddc5998f ]---
I thought if the memory region is listed in vmap_area_list
then all the virtual addresses in it should be mapped and accessible/readable. Is there a possibility to check if a virtual address is mapped ? Or am I getting something totally wrong ? Are there any other possibilites to read the kernel memeory (between VMALLOC_START
and VMALLOC_END
) ?
I am using debian 7.2 with kernel 3.10.9.
Thanks in advance.
Under Linux use the file /proc/modules shows what kernel modules (drivers) are currently loaded into memory.
You need to use modinfo command to display or show information about a Linux Kernel loaded modules. Use the lsmod command to obtain list of loaded modules in the Linux kernel.
modinfo command in Linux system is used to display the information about a Linux Kernel module. This command extracts the information from the Linux kernel modules given on the command line.
The kernel virtual memory contains the code and data structures in the kernel. Some regions of the kernel virtual memory are mapped to physical pages that are shared by all processes. For example, each process shares the kernel's code and global data structures.
There is one obvious issue in your implementation. Even if I assume you take the right vmap_area_lock when you scan the vmap_area_list, you still have a race condition at least with vmalloc itself which could run into page fault. That is, you could access a vm area which is allocated and inserted, but not been mapped yet in vmalloc. In this case, a typical data abort - page fault will happen as above.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With