커널 오류: 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));

관련 정보