史萊姆論壇

返回   史萊姆論壇 > 教學文件資料庫 > 作業系統操作技術文件
忘記密碼?
論壇說明

歡迎您來到『史萊姆論壇』 ^___^

您目前正以訪客的身份瀏覽本論壇,訪客所擁有的權限將受到限制,您可以瀏覽本論壇大部份的版區與文章,但您將無法參與任何討論或是使用私人訊息與其他會員交流。若您希望擁有完整的使用權限,請註冊成為我們的一份子,註冊的程序十分簡單、快速,而且最重要的是--註冊是完全免費的!

請點擊這裡:『註冊成為我們的一份子!』

Google 提供的廣告


 
 
主題工具 顯示模式
舊 2006-04-03, 01:08 AM   #1
psac
榮譽會員
 
psac 的頭像
榮譽勳章
UID - 3662
在線等級: 級別:30 | 在線時長:1048小時 | 升級還需:37小時級別:30 | 在線時長:1048小時 | 升級還需:37小時級別:30 | 在線時長:1048小時 | 升級還需:37小時級別:30 | 在線時長:1048小時 | 升級還需:37小時級別:30 | 在線時長:1048小時 | 升級還需:37小時
註冊日期: 2002-12-07
住址: 木柵市立動物園
文章: 17381
現金: 5253 金幣
資產: 33853 金幣
預設 Windows平台內核級文件訪問

Windows平台內核級文件訪問

1.背景
在windows平台下,應用程式通常使用API函數來進行文件訪問,新增,開啟,讀寫文件。從kernel32的CreateFile/ReadFile/WriteFile函數,到本機系統服務,再到FileSystem及其FilterDriver,經歷了很多層次。在每個層次上,都存在著安全防護軟體,病毒或者後門作監視或者過濾的機會。作為安全產品開發者,我們需要比別人走得更遠,因此我們需要一個底層的「windows平台內核級文件訪問」的方法來確保我們能夠看到正確的乾淨的文件系統。

2.用途
直接的內核等級文件訪問,在訊息安全領域內有廣泛的用途。用於入侵者的方面,可以讓他繞過殺毒軟體,IDS等安全保護系統的監視。用於檢測者的方面,可以看到一個乾淨的系統,以此來查殺隱藏的後門或者rootkit。用於監控者的方面,則可以瞭解最新的繞過監控的技術,可以根據來設計更新的監控方案。

3.直接訪問FSD的內核等級文件訪問
FSD(FileSystemDriver)層是文件API函數經過本機系統服務層(native API)最後到達的驅動層次。如果我們可以模仿作業系統,在我們自己的驅動程式裡直接向FSD傳送IRP,就可以繞過那些native API 和win32 API了,也就可以繞過設定在這些層次上面的API引上鉤等監控措施。

3.1文件的Create和Open
文件的Create和Open可以通過傳送IRP_MJ_CREATE給FSD,或者使用IoCreateFile函數來完成。Create和Open的區別實際上在於IoCreateFile/IRP_MJ_CREATE的一個參數Disposition的取值。使用IoCreateFile函數的樣例程式碼:

HANDLE openfile(WCHAR* name,ACCESS_MASK access,ULONG share)

//return 0 for error
HANDLE hfile;
IO_STATUS_BLOCK iosb;
int stat;~u;
OBJECT_ATTRIBUTES oba;
UNICODE_STRING nameus;
///
if(KeGetCurrentIrql()>PASSIVE_LEVEL){return 0;}
RtlInitUnicodeString(&nameus,name);
InitializeObjectAttributes(&oba,&nameus,OBJ_KERNEL_HANDLE|OBJ_CASE_INSENSITIVE,0,0);
stat=IoCreateFile(&hfile,access,&oba,&iosb,0,FILE_ATTRIBUTE_NORMAL,share,FILE_OPEN,0,0,0,0,0,0);`iLD91
if(!NT_SUCCESS(stat)){return 0;}
return hfile;
}
HANDLE createnewfile(WCHAR* name,ACCESS_MASK access,ULONG share)
{
//return 0 for error
HANDLE hfile;
IO_STATUS_BLOCK iosb;
int stat;
OBJECT_ATTRIBUTES oba;
UNICODE_STRING nameus;
///
if(KeGetCurrentIrql()>PASSIVE_LEVEL){return 0;}
RtlInitUnicodeString(&nameus,name);
InitializeObjectAttributes(&oba,&nameus,OBJ_KERNEL_HANDLE|OBJ_CASE_INSENSITIVE,0,0);
stat=IoCreateFile(&hfile,access,&oba,&iosb,0,//AllocationSize this set to 0 that when file opened it was zeroed.
FILE_ATTRIBUTE_NORMAL,share,FILE_OVERWRITE_IF,0,0,0,0,0,0);
if(!NT_SUCCESS(stat)){return 0;}
return hfile;
}
通過傳送IRP_MJ_CREATE給FSD的方法與此類似,可以參考IFSDDK document的IRP_MJ_CREATE說明。不同於上面方法的是需要自己新增一個FILE_OBJECT,好於上面方法的是這種方法不需要一個HANDLE,HANDLE是執行緒依賴的,FileObject則是執行緒無關。

3.2文件的Read和Write
我們通過給FSD傳送IRP_MJ_READ來讀取文件,給FSD傳送IRP_MJ_WRITE來改寫文件。
如果我們是通過一個HANDLE來執行(如使用IoCreateFile開啟的文件),就要先用ObReferenceObjectByHandle函數來獲得這個Handle對應的FileObject。我們只能給FileObject傳送IRP。

stat=ObReferenceObjectByHandle(handle,GENERIC_READ,*IoFileObjectType,KernelMode,(PVOID*)&fileob,0);

之後我們使用IoAllocateIrp分配一個IRP。根據FileObject->DeviceObject->Flags的值,我們判斷目標文件系統使用什麼樣的IO方式。

if(fileob->DeviceObject->Flags & DO_BUFFERED_IO)
{6A?lOU
irp->AssociatedIrp.SystemBuffer=buffer;//buffered
}
else if(fileob->DeviceObject->Flags & DO_DIRECT_IO)
{m
mdl=IoAllocateMdl(buffer,count,0,0,0);5]
MmBuildMdlForNonPagedPool(mdl);
irp->MdlAddress=mdl;//direct
}
elseK
{
irp->UserBuffer=buffer;//neither i/o, use kernel buffer
}

對每種不同的IO方式使用不同的位址傳送方式。隨後我們填充IRP內的各個參數域,就可以傳送IRP了。以Read為例:

irpsp->FileObject=fileob;
irpsp->MajorFunction=IRP_MJ_READ;
irpsp->MinorFunction=IRP_MN_NORMAL;
irpsp->Parameters.Read.ByteOffset=offsetused;
irpsp->Parameters.Read.Key=0;
irpsp->Parameters.Read.Length=count;

接著要考慮如果IRP不能及時完成,會異步的返回的情況,我們安裝一個CompletionRoutine,在CompletionRoutine裡面設定一個事件為已啟動,通知我們的主執行緒讀取或者寫入操作已經完成。

IoSetCompletionRoutine(irp,IoCompletion,&event,1,1,1);

NTSTATUS
IoCompletion(#gL
IN PDEVICE_OBJECT DeviceObject
IN PIRP Irp,vc
IN PVOID Contextn
)&

KeSetEvent((PRKEVENT)Context, IO_DISK_INCREMENT, 0);
return STATUS_MORE_PROCESSING_REQUIRED;
}
現在可以傳送IRP了。如果不採取特殊的措施的話,IRP傳送目標是FileObject對應的DeviceObject。傳送後,等待IRP的完成並且解壓縮資源,返回。

stat=IoCallDriver(fileob->DeviceObject,irp);
if(stat==STATUS_PENDING){~5:
KeWaitForSingleObject(&event, Executive,KernelMode,0,0);
stat=irp->IoStatus.Status;
}
if(!NT_SUCCESS(stat))
{
IoFreeIrp(irp);
if(mdl){IoFreeMdl(mdl);}//if DO_DIRECT_IO
return -1;
}
stat=irp->IoStatus.Information;//bytes read
IoFreeIrp(irp);
if(mdl){IoFreeMdl(mdl);}//if DO_DIRECT_IO
return stat;

3.3文件的Delete
Delete實際上是通過向FSD傳送IRP_MJ_SET_INformATION的IRP,並把IrpSp->Parameters.SetFile.FileInformationClass設定為FileDispositionInformation,用一個FILE_DISPOSITION_INformATION結構填充buffer來執行的。

fdi.DeleteFile=TRUE;

irpsp->MajorFunction=IRP_MJ_SET_INformATION;
irpsp->Parameters.SetFile.Length = sizeof(FILE_DISPOSITION_INformATION);
irpsp->Parameters.SetFile.FileInformationClass = FileDispositionInformation;
irpsp->Parameters.SetFile.DeleteHandle = (HANDLE)handle;

3.4文件的Rename
類似於Delete,Rename是向FSD傳送IRP_MJ_SET_INformATION的IRP,把IrpSp->Parameters.SetFile.FileInformationClass設定為FileRenameInformation,填充buffer為FILE_RENAME_INformATION結構。

fri.ReplaceIfExists=TRUE;
fri.RootDirectory=0;//Set fri.FileName to full path name.
fri.FileNameLength=wcslen(filename)*2;
wcscpy(fri.FileName,filename);//If the RootDirectory member is NULL, and the file is being moved to a different directory, this member specifies the full pathname to be assigned to the file. K
irpsp->MajorFunction=IRP_MJ_SET_INformATION;
irpsp->Parameters.SetFile.Length = sizeof(FILE_FILE_RENAME_INformATION);
irpsp->Parameters.SetFile.FileInformationClass = FileRenameInformation;

綜上,於是我們可以在驅動裡面通過傳送IRP來直接存取檔案系統了,繞過了native API 和win32 API層次。

4.繞過文件系統過濾驅動和引上鉤
有了第三部分的內容,我們目前可以直接給FSD傳送請求操作文件。但是這還不夠,因為有很多的殺毒軟體或者監視工具使用FSD Filter Driver或者FSD Hook的辦法來監控文件操作。在今天這篇文章裡我講一些原理性的東西,提供繞過FSD Filter Driver / FSD Hook的思法。

4.1對付文件系統過濾驅動

文件系統過濾驅動Attach在正常的文件系統之上,監視和過濾我們的文件訪問。文件系統驅動棧就是由這一連串的Attach起來的過濾驅動組成。我們可以用IoGetRelatedDeviceObject這個函數來獲得一個FileObject對應的最底層的那個功能驅動對像(FDO)。但是這樣雖然繞過了那些過濾驅動,卻同時也繞過了正常的FSD如Ntfs/Fastfat,因為正常的FSD也是作為一個過濾驅動存在的。磁牒文件對象的對應的最底層的FDO是Ftdisk.sys,它已經因為過於底層而不能處理我們投遞的IRP請求。
其實正常的FSD訊息儲存於在一個Vpb結構中,我們可以使用IoGetBaseFileSystemDeviceObject這個未公開的內核函數來得到它。它就是我們傳送IRP的目標了。

4.2對付取代DispatchRoutine的FSD Hook

這是一種常用的FSD Hook方式。我們需要得到原本的DispatchRoutine,向原本的DispatchRoutine傳送我們的IRP。這裡提供一個思法:我們可以讀取原本FSD驅動的.INIT段或者.TEXT段,搜尋其DriverEntry函數,在它的DriverEntry函數中肯定設定了自己的DriverObject的各個DispatchRoutine。在這個函數中我們就能找到我們想要的DispatchRoutine的位址。只需要使用特徵碼搜尋的方法就可以搜尋到這個值。

4.3對付Inline Hook DispatchRoutine函數本身的FSD Hook

這種Hook方法比較狠毒,但不是非常一般於安全產品中,一般套用在木馬和rootkit上,比如我自己寫的rootkit。它沒有更改DriverObject裡面的DispatchRoutine的函數游標,而是向函數開頭寫入彙編指令的JMP來跳轉函數。對付它的基本思法就是讀取存在磁牒上的FSD的文件,載入到記憶體一份乾淨的制作備份,察看我們要使用的DispatchRoutine開頭的幾個字元和這個乾淨制作備份是否一致。如果不一致,尤其是存在JMP,RET,INT3一類的彙編指令的時候,很可能就是存在了Inline Hook。(但要充分考慮重定位的情況。)如果存在Inline Hook,我們就把乾淨的函數開頭拷貝過來覆蓋掉被感染的函數頭。然後在傳送IRP,就不會被Inline Hook監視或篡改了。
__________________
http://bbsimg.qianlong.com/upload/01/08/29/68/1082968_1136014649812.gif
psac 目前離線  
送花文章: 3, 收花文章: 1631 篇, 收花: 3205 次
向 psac 送花的會員:
Yau2328 (2009-11-06)
感謝您發表一篇好文章
 



發表規則
不可以發文
不可以回覆主題
不可以上傳附加檔案
不可以編輯您的文章

論壇啟用 BB 語法
論壇啟用 表情符號
論壇啟用 [IMG] 語法
論壇禁用 HTML 語法
Trackbacks are 禁用
Pingbacks are 禁用
Refbacks are 禁用


所有時間均為台北時間。現在的時間是 05:46 AM


Powered by vBulletin® 版本 3.6.8
版權所有 ©2000 - 2024, Jelsoft Enterprises Ltd.


SEO by vBSEO 3.6.1