内核错误:无法处理 ffffffff81e00520 处的内核分页请求

内核错误:无法处理 ffffffff81e00520 处的内核分页请求

我正在尝试在 Ubuntu 16.04 上的 linux 内核 4.15.0-74-generic 上挂钩系统调用。加载内核模块时,它会给出以下消息dmesg

[96963.055549] BUG: unable to handle kernel paging request at ffffffff81e00520

[96963.055556] IP: entry_point+0x14/0x50 [syshook]

[96963.055557] PGD 1bbe0e067 P4D 1bbe0e067 PUD 1bbe0f063 PMD 0 

[96963.055560] Oops: 0000 [#1] SMP PTI

[96963.055562] Modules linked in: syshook(OE+) xt_recent pci_stub vboxpci(OE) vboxnetadp(OE) vboxnetflt(OE) vboxdrv(OE) ip6t_REJECT nf_reject_ipv6 nf_log_ipv6 xt_hl ip6t_rt nf_conntrack_ipv6 nf_defrag_ipv6 ipt_REJECT nf_reject_ipv4 xt_comment nf_log_ipv4 nf_log_common xt_LOG xt_multiport intel_rapl x86_pkg_temp_thermal xt_limit intel_powerclamp coretemp kvm_intel kvm irqbypass xt_tcpudp snd_hda_codec_realtek snd_hda_codec_generic xt_addrtype input_leds snd_hda_intel snd_hda_codec snd_hda_core snd_hwdep snd_pcm crct10dif_pclmul crc32_pclmul snd_seq_midi snd_seq_midi_event ghash_clmulni_intel snd_rawmidi nf_conntrack_ipv4 nf_defrag_ipv4 pcbc xt_conntrack snd_seq aesni_intel snd_seq_device ip6table_filter aes_x86_64 ip6_tables snd_timer nf_conntrack_netbios_ns nf_conntrack_broadcast crypto_simd nf_nat_ftp
[96963.055583]  nf_nat glue_helper i2c_i801 snd shpchp nf_conntrack_ftp cryptd intel_cstate intel_rapl_perf wmi_bmof mei_me mei soundcore ie31200_edac nf_conntrack libcrc32c iptable_filter lpc_ich binfmt_misc ip_tables ppdev mac_hid parport_pc x_tables lp parport autofs4 hid_generic usbhid hid i915 i2c_algo_bit drm_kms_helper syscopyarea sysfillrect ahci sysimgblt libahci fb_sys_fops drm r8169 mii wmi video

[96963.055611] CPU: 3 PID: 1621 Comm: insmod Tainted: G        W  OE    4.15.0-74-generic #83~16.04.1-Ubuntu

[96963.055612] Hardware name: LENOVO 3492H2Q/, BIOS F1KT52AUS 05/24/2013

[96963.055615] RIP: 0010:entry_point+0x14/0x50 [syshook]

[96963.055616] RSP: 0018:ffffa2cdc89c7c68 EFLAGS: 00010246

[96963.055617] RAX: 0000000000000000 RBX: ffffffffc07a8000 RCX: 0000000000000000

[96963.055618] RDX: 0000000000833a67 RSI: 0000000000000000 RDI: ffffffff81e00280

[96963.055619] RBP: ffffa2cdc89c7ce8 R08: ffff90251f3a6080 R09: ffff902516803980
[96963.055620] R10: ffffc668c14f8a00 R11: ffffffff97c5fc20 R12: ffffffffc07a60c0
[96963.055621] R13: 0000000000000000 R14: 0000000000000001 R15: 0000000000000001

[96963.055622] FS:  00007fd9d1159700(0000) GS:ffff90251f380000(0000) knlGS:0000000000000000

[96963.055623] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033

[96963.055624] CR2: ffffffff81e00520 CR3: 0000000204ff4001 CR4: 00000000001626e0

[96963.055625] Call Trace:

[96963.055631]  ? do_one_initcall+0x55/0x1ac

[96963.055633]  ? _cond_resched+0x1a/0x50

[96963.055636]  ? kmem_cache_alloc_trace+0xa6/0x1d0

[96963.055640]  do_init_module+0x5f/0x223

[96963.055642]  load_module+0x188c/0x1e90

[96963.055646]  ? ima_post_read_file+0x83/0xa0

[96963.055648]  SYSC_finit_module+0xe5/0x120

[96963.055650]  ? SYSC_finit_module+0xe5/0x120

[96963.055652]  SyS_finit_module+0xe/0x10

[96963.055654]  do_syscall_64+0x73/0x130

[96963.055656]  entry_SYSCALL_64_after_hwframe+0x3d/0xa2

[96963.055658] RIP: 0033:0x7fd9d0c8c4d9

[96963.055658] RSP: 002b:00007ffdddf2e158 EFLAGS: 00000202 ORIG_RAX: 0000000000000139

[96963.055660] RAX: ffffffffffffffda RBX: 00005557c908b250 RCX: 00007fd9d0c8c4d9

[96963.055661] RDX: 0000000000000000 RSI: 00005557c87e526b RDI: 0000000000000003

[96963.055661] RBP: 00005557c87e526b R08: 0000000000000000 R09: 00007fd9d0f51ea0

[96963.055662] R10: 0000000000000003 R11: 0000000000000202 R12: 0000000000000000

[96963.055663] R13: 00005557c908b210 R14: 0000000000000000 R15: 0000000000000000

[96963.055664] Code: c0 c9 c3 e8 df b0 ee d5 0f 1f 44 00 00 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 48 c7 c7 80 02 e0 81 55 48 89 3d 74 22 00 00 <48> 8b 04 25 20 05 e0 81 48 89 e5 48 89 05 5a 22 00 00 e8 35 ff

[96963.055684] RIP: entry_point+0x14/0x50 [syshook] RSP: ffffa2cdc89c7c68

[96963.055685] CR2: ffffffff81e00520

[96963.055687] ---[ end trace fa6b1eb8d1543662 ]---

我究竟做错了什么?如何调试内核模块?

源代码是:

#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/moduleparam.h>
#include <linux/unistd.h>
#include <linux/semaphore.h>
#include <linux/dirent.h>
#include <asm/cacheflush.h>
void **sys_call_table;
asmlinkage int (*original_rmdir)(const char __user * pathname);

asmlinkage int sys_rmdir(const char __user * pathname) 
{ 
    printk("Do whatever you want\n");
    return 0; 
}

int set_page_rw(unsigned long addr)
{
  unsigned int level;
  pte_t *pte = lookup_address(addr, &level);
  if (pte->pte &~ _PAGE_RW) pte->pte |= _PAGE_RW;
  return 0;
}

int set_page_ro(unsigned long addr)
{
  unsigned int level;
  pte_t *pte = lookup_address(addr, &level);
  pte->pte = pte->pte &~_PAGE_RW;
  return 0;
}

static int entry_point(void) 
{
    sys_call_table = (void*)0xffffffff81e00280;
    original_rmdir = sys_call_table[__NR_rmdir];
    set_page_rw(sys_call_table);
    sys_call_table[__NR_rmdir] = sys_rmdir;
    return 0;
}

static void exit_point(void)
{
    sys_call_table[__NR_rmdir] = original_rmdir;
    set_page_ro(sys_call_table);
        return 0;

} 
module_init(entry_point);
module_exit(exit_point);

答案1

鉴于您收到的消息:

[96963.055549] BUG: unable to handle kernel paging request at ffffffff81e00520

我会挖掘你的set_page_ro()set_page_rw()功能。

特别是因为该消息为您提供了为系统调用表输入的地址 (ffffffff81e00520)。

为什么你用这样的值来硬编码它?我敢打赌内核会导出一个宏或extern void *类似的东西以获得正确的值......

编辑:因此,如果使用正在运行的内核的 System.map 检索该地址,可能会很好。但是,如果你看这段代码切换页面 RW/RO 的过程看起来与您所做的不同......

当你这样做时:

if (pte->pte &~ _PAGE_RW) pte->pte |= _PAGE_RW;

github上的示例代码是这样的:

set_pte_atomic(pte, pte_clear_flags(*pte, _PAGE_RW));

相关内容