從linux的檔案刪除機制解釋為何linux的病毒那麼少

來源:互聯網
上載者:User

很多人曾經說過,linux下的病毒沒有windows多的原因在於linux使用的人不多,人們不屑於開發linux下的病毒,看到這個言論我笑了,想必很多人都有過explorer被注入的經曆吧,explorer被替換然後怎麼也刪除不掉,或者一個system32下的一個頑固檔案怎麼也刪除不掉,更不幸的是,它就是一個木馬,此時,網上流行的一些工具就顯得有用了,比如icesword,這些工具不得不動用核心驅動來靠核心的超高特權來刪除那些檔案,windows的註冊表也是一個難纏的東西,一旦註冊表被篡改,後果將不堪設想,因此註冊表備份工具就顯得必要,這一切的關鍵在於windows核心的架構,windows的啟動過程非常的複雜,不僅是核心的啟動,更有很多的使用者空間的服務是使用者所不能控制的,比如winlogon進程,在使用者可以接手系統之前,很多的使用者空間服務已經啟動了,即便是安全模式也是這樣,既然使用者空間的東西可以在使用者接手系統之前啟動,那麼如果這些是惡意程式的話,你的系統將乖乖的隨駭客的心而動,恐怖吧,十分恐怖!windows的檔案對應是一個十分令人又愛又恨的機制,它可以使你的重要檔案在被訪問期間不能被刪除,當然也可以使病毒被使用期間不能被刪除...這一切都是因為windows將檔案的刪除和檔案保護以及系統安全統一在了一起,這是它微核心的架構所決定的,windows總是盡自己最大的努力保持系統的完整,而往往結構卻適得其反,windows作為一個商品,它不希望初級使用者由於自己的誤操作刪除了一個系統檔案造成系統不能啟動然後把過錯歸於微軟,它需要考慮的問題太多了。linux呢,恰恰不是這樣,它對於檔案的保護採用了懶惰的機制,並且將最大的自由交給使用者,它不阻止你刪除任何檔案,因為系統的根本歸根結底都是磁碟的檔案,所以它甚至允許你刪除核心本身,結果就是下次不能再啟動了,刪除了核心本身難道還能繼續運行下去,是的,可以,這就是linux的vfs對檔案刪除管理的懶惰機制,如果有人在用該檔案,那麼系統是不會刪除它的,另外一個原因就是linux核心是大核心,並且全部映射到實體記憶體,對於核心而言沒有分頁記憶體的概念,即便磁碟的檔案沒有了,只要記憶體中還有就不會出現問題。對於檔案的刪除操作,不管是什麼檔案一律平等對待,linux系統看到的只是一個引用計數,一旦引用計數為0了,就要刪除該檔案了,如果有人使用著該檔案,那麼它的引用計數最起碼是1,最起碼也要等到該使用者不再使用的時候才可以刪除該檔案,這樣的結果就是當你刪除一個檔案的時候,你放心的進行刪除操作,系統等到沒有用該檔案的時候執行最終的刪除,因此在linux中沒有刪除不了的檔案,linux不對檔案的刪除處理提供任何的保護,不耦合任何別的機制,刪除的語義很簡單,就是遞減一個引用計數。

另外一點就是,linux的核心和使用者空間分得很清晰,使用者甚至可以在啟動時定義自己的init=XXX參數使得使用者空間的第一個進程是自己定義的,這種核心空間和核心空間的不耦合是十分重要的,核心在init核心線程中通過execve一個使用者進程讓使用者接手系統,這個進程是可以自己定義的,不過一般是/sbin/init進程,這樣的結果就是即使使用者空間全部被注入了,那麼你第一,可以刪除這些骯髒的檔案;第二,可以設定一個你自己定義的乾淨的init進程,需要做的就是重新啟動一下系統,一切就搞定了,linux中強大shell命令使得你可以很簡單的備份一份乾淨的無病毒的根檔案系統,因此在linux下殺毒將是一件很簡單的事情。使用者可以自主控制使用者空間的第一個進程是這裡的要點,在windows下這是很難的,你想替換smss程式,試試看,系統會提示你“請確定磁碟未滿或未被防寫保護而且檔案未被使用”,並且system32下的dllcache也是一個讓你又愛又恨的目錄,不信的話,請手動刪除一下IE試試看。

要點就是不要試圖保護檔案不被刪除,作業系統的使用者空間應該提供系統保護機制而不是讓檔案系統承擔這個責任,不管那個檔案有多重要,系統也不要提供保護它不被刪除的機制,特別是核心更不能,因為如果良民可以通過這種方式得到保護,那麼強盜也可以。

linux中通過兩個引用計數管理了檔案的存在與刪除,這就是i_count以及i_nlink,前者的意義是當前的使用者,後者的意義是當前的介質連結數量,這二者的意義也可以理解成前者是檔案在記憶體的引用計數,而後者是檔案在磁碟的引用計數,只有在二者都為0的情況下,介質的刪除操作才真正進行,也可以說,i_nlink是檔案被刪除的充分條件,而i_count是檔案被刪除的必要條件,最後我會說明,若不是為了別的管理需要,用一個引用計數就夠了而沒有必要用兩個。在刪除一個檔案的時候,最終要調用iput,我們看一下這個函數:

void iput(struct inode *inode)

{

if (inode) {

struct super_operations *op = inode->i_sb->s_op;

if (op && op->put_inode)

op->put_inode(inode);

if (atomic_dec_and_lock(&inode->i_count, &inode_lock)) //檔案的記憶體的引用計數為0的話才進行最終的刪除

iput_final(inode);

}

}

iput_final中主要調用一個generic_drop_inode,緊接著看一下這個generic_drop_inode:

static void generic_drop_inode(struct inode *inode)

{

if (!inode->i_nlink)

generic_delete_inode(inode);

else

generic_forget_inode(inode);

}

可以看出,記憶體引用計數為0是一個前提條件,畢竟記憶體引用計數代表著進程對檔案的訪問,只有在沒有進程對該檔案進行訪問的情況下才可以權衡接下來的刪除操作:

void generic_delete_inode(struct inode *inode)

{

struct super_operations *op = inode->i_sb->s_op;

list_del_init(&inode->i_list);

inode->i_state|=I_FREEING;

inodes_stat.nr_inodes--;

spin_unlock(&inode_lock);

if (inode->i_data.nrpages)

truncate_inode_pages(&inode->i_data, 0);

security_inode_delete(inode);

if (op->delete_inode) {

void (*delete)(struct inode *) = op->delete_inode;

if (!is_bad_inode(inode))

DQUOT_INIT(inode);

delete(inode); //這個檔案系統相關的delete回呼函數最終銷毀了磁碟的inode

} else

clear_inode(inode);

spin_lock(&inode_lock);

hlist_del_init(&inode->i_hash);

spin_unlock(&inode_lock);

wake_up_inode(inode);

if (inode->i_state != I_CLEAR)

BUG();

destroy_inode(inode);

}

以上函數可以看出,銷毀介質inode的操作是檔案系統的超級塊操作的一個回呼函數,這是合理的,因為檔案系統的超級塊就是管理全域的,所有常規檔案的塊分配,空閑塊管理都要由它來進行,因此這理應是它的職責,具體例子就是有些檔案系統是偽檔案系統,而且還偽裝成塊檔案,比如記憶體檔案系統,它根本就沒有非易失介質,因此也就沒有必要提供真實的delete回呼函數了。總之,一個檔案系統是什麼樣子,應該怎樣進行管理,它的超級塊最清楚。linux的vfs其實是一幅很美麗的圖畫,不同的檔案系統有著不同的行為,vfs核心負責協調它們使它們更好的合作,已到達最終系統的安全,穩定和高效。

刪除檔案的操作只有一個就是unlink,僅僅從名字上看看不出它是刪除的意思,實際上linux中任何的刪除檔案的操作最終都要歸結到這個unlink,這個系統調用的實質就是遞減檔案磁碟的引用計數,也就是遞減i_nlink計數器,按照道理來說檔案的刪除和檔案解除連結有著不同的意義,但是它們卻統一到了一個一致的系統調用,這二者是從不同角度來講的同一個意思,刪除是從檔案本身來說的,而解除連結是從使用者角度來講的,現在就看一下這個被兩個語義一起使用的系統調用:

asmlinkage long sys_unlink(const char __user * pathname)

{

...

if (!IS_ERR(dentry)) {

if (nd.last.name[nd.last.len])

goto slashes;

inode = dentry->d_inode;

if (inode)

atomic_inc(&inode->i_count);

error = vfs_unlink(nd.dentry->d_inode, dentry); //調用具體檔案系統的unlink函數

exit2:

dput(dentry);

}

up(&nd.dentry->d_inode->i_sem);

if (inode)

iput(inode);

...

}

static int ext2_unlink(struct inode * dir, struct dentry *dentry)

{

struct inode * inode = dentry->d_inode;

...

ext2_dec_count(inode); //遞減了該inode的i_nlink的引用計數

err = 0;

out:

return err;

}

剛才說了其實用一個引用計數就可以搞定一切,此話怎講呢?這裡姑且不討論太複雜的inode問題,僅以檔案討論,我們把檔案的初始引用計數設定為1,一旦開啟一個檔案就遞增引用計數,一旦關閉就遞減之,只有在unlink的情況才遞增一次,遞減兩次,總的遞減一次,這裡也不討論檔案對應,僅以為凡是操作檔案必須是先開啟再訪問,這樣的話,如果一個進程訪問一個檔案後關閉之,它遞減的該檔案的引用計數僅僅是它為了訪問該檔案open時遞增的那一次,和開啟前的引用計數相等,無論哪一種情況,只要該檔案的引用計數為0則刪除之,這種機制簡單的闡明了linux中檔案刪除的懶惰機制,正是這種機制使得linux下的病毒無處藏匿。如果僅僅管理檔案刪除,那麼一個引用計數當然就夠了,但是unix/linux一般不會讓一個實體身兼數職的,如果現實中需要別的意義,那麼就會抽象出一個新的新的資料結構用於此新意義,在檔案的管理中,i_nlink的出現就是為了管理檔案的永久連結的。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.