http://hi.baidu.com/charme000/item/23945ecdb49926d2964452a1
原型:
NTSTATUS ZwCreateSection(
OUT PHANDLE SectionHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
IN PLARGE_INTEGER MaximumSize OPTIONAL,
IN ULONG SectionPageProtection,
IN ULONG AllocationAttributes,
IN HANDLE FileHandle OPTIONAL
);
參數理解:
-out:
SectionHandle:這個接受返回的section的句柄。這和創建文件對象是一個套路
-in:
DesiredAccess:訪問的權限,有以下幾個,跟文件對象真的很相似,呼呼
SECTION_EXTEND_SIZE 可擴展大小
SECTION_MAP_EXECUTE
SECTION_MAP_READ
SECTION_MAP_WRITE
SECTION_QUERY 一般的驅動的話 ,就設置這個
SECTION_ALL_ACCESS
ObjectAttributes:這個跟其他地方的一樣。用InitializeObjectAttributes初始化就好了,注意一點就可以了,如果不是運行在系統線程上下文中的話,那必須指定OBJ_KERNEL_HANDLE...而且千萬不能用OBJ_OPENLINK這個屬性值。
MaximumSize:指定section的大小,字節大小,這個大小是對齊到內存頁的大小的。所以我們一般把他初始化成一個內存頁的大小,也就是4KB。
LARGE_INTEGRE sectionSize;
and sectionSize.HighPart,0
mov sectionSize.LowPart,SECTION_SIZE
最后一個參數是一個FileHandle,如果這個參數是NULL 的話,那我們這個參數是必須指定的。這是個什么邏輯呢?
我們很自然的這樣想:如果沒有指定文件,那么按MaximumSize造一個section,那么如果指定了filehandle的話,表示我們要按照文件的大小來確定section的大小。就是這么一個邏輯。
SectionPageProtection:
PAGE_READONLY
PAGE_READWRITE
PAGE_WRITECOPY
PAGE_EXECUTE
PAGE_EXECUTE_READ
PAGE_EXECUTE_READWRITE
PAGE_EXECUTE_WRITECOPY
我們可以這樣想,實際上內核對象沒有什么神秘的,只是在內存中申請了一個內存塊,這個內存塊有一些自己的屬性,要訪問的話,我們需要用微軟提供的一些函數安全高效的使用他們。
那么既然是內存塊的話,必然要涉及到內存的屬性,上面的這個參數就是指定我們要把section映射到什么樣的內存頁上的。
再說的明確點就是:如果filehandle是null,那么就按內存頁文件大小來建section,如果是指定的一個文件句柄,那么久按這個文件的大小來建section。
AllocationAttributes:這個指定了這個section本身的屬性
SEC_BASED 0x00200000 // Map section at same address in each process
SEC_NO_CHANGE 0x00400000 // Disable changes to protection of pages
SEC_IMAGE 0x01000000 // Map section as an image
SEC_VLM 0x02000000 // Map section in VLM region
SEC_RESERVE 0x04000000 // Reserve without allocating pagefile storage
SEC_COMMIT 0x08000000 // Commit pages; the default behavior 默認使用這個
SEC_NOCACHE 0x10000000 // Mark pages as non-cacheable
FileHandle貌似一般都是null的,呼呼。
返回值理解:
成功-STATUS_SUCCESS
失敗-
STATUS_FILE_LOCK_CONFLICT
STATUS_INVALID_FILE_FOR_SECTION
STATUS_INVALID_PAGE_PROTECTION
STATUS_MAPPED_FILE_SIZE_ZERO
STATUS_SECTION_TOO_BIG
字面意思理解就可以了。
運行級別:
PASSIVE_LEVEL
相關的API:
CreateFileMapping
說明:
函數返回的SectionHandle,如果不用了,要用ZwClose來關閉。
擴展:
(1)看到他聯系的這個api,實際上file mapping,我們會以為ZwCreateSection就是跟文件相關的東西。實際上這樣理解不合理,因為系統上面好多的東西都是被看做文件的,最常見的比如硬盤設備。
szFileName1 db '\\.\PHYSICALDRIVE0',0 ; 打開第一個物理硬盤(PhysicalDrive0)
可以當做文件打開,但是不是所有的都能看做文件!!
那么看看相關的一系列函數ZwOpenSection,我們用ZwOpenSection可以打開\Device\PhysicalMemory。這就不是文件,所以我覺得吧section的相關的東西理解為操作內存塊更為妥當。
這個地方謝謝師傅的提醒,,我差點就給理解偏了!!呼呼!
(2)這個函數返回的是一個句柄,既然是句柄,我們要考慮是私有的句柄還是共享的句柄 。這個共享和私有是在哪里區分的?是靠什么區分的?
我找不到相關的解答。我來猜測下吧:
對於user mode的話,就無所謂私有和共享了,因為加載到一個進程地址空間的那些個exe啊或者是dll啊,標示他們的實例句柄都是整個低2GB空間都共享的。只有涉及到了driver的時候才有了私有個共享句柄的這樣的概念。
而且我覺得如果只能在kernel mode使用的句柄叫做私有句柄。能夠在kernel mode和user mode下傳遞使用的是共享句柄。
那么我覺得這樣分是為安全考慮的。
1>要指定為私有句柄的話,在初始化對象屬性的時候指定OBJ_KERNEL_HANDLE。這樣這個句柄就不能被用戶態程序使用了。
2>如果非要共享的話,一般考慮三種策略:
----在kernel mode創建句柄傳遞給user mode。而不是user mode創建句柄傳遞給kernel mode。因為不安全。其實這里我不是很清楚為什么是不安全的。
----初始化對象屬性的時候指定OBJ_FORCE_ACCESS_CHECK,也就是說user mode要使用這個句柄的話,必須要通過一個權限驗證。
----通過調用ObReferenceObjectByPointer 來對這個要共享的句柄做一個引用。每個內核對象內部都維護一個引用計數,所以這樣調用之后實際上就是維持了一個kernel mode對句柄的計數,這樣就不會因為user mode隨意遞減計數而在不適當的時候關閉內核對象而使得handle無效了。
當然最后的話,還要自己遞減這個計數,調用ObDeferenceObject就可以了。
實際上hook的切入點很多時候都在這里。
比如我們這樣寫一個:
NTSTATUS Fake_ZwCreateSection( OUT PHANDLE SectionHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, IN PLARGE_INTEGER MaximumSize OPTIONAL, IN ULONG SectionPageProtection, IN ULONG AllocationAttributes, IN HANDLE FileHandle OPTIONAL) { PFILE_OBJECT FileObject; POBJECT_NAME_INFORMATION wcFilePath; HANDLE ProcessID; ProcessID=PsGetCurrentProcessId(); if (SectionPageProtection & (PAGE_EXECUTE|PAGE_EXECUTE_READ |PAGE_EXECUTE_READWRITE |PAGE_EXECUTE_WRITECOPY) ) { if (NT_SUCCESS(ObReferenceObjectByHandle( FileHandle, 0,NULL,KernelMode, &FileObject,NULL))) { if (IoQueryFileDosDeviceName(FileObject,&wcFilePath)==STATUS_SUCCESS) { DbgPrint("ProcessID:0x%08X %ws\r\n", ProcessID,wcFilePath->Name.Buffer); } else { DbgPrint("ProcessID:0x%08X %ws\r\n",ProcessID,FileObject->FileName.Buffer); }
ObDereferenceObject(FileObject); ExFreePool(wcFilePath); } else { KdPrint(("ProcessID:0x%08X--FileHandle:0x%08X\r\n", ProcessID,FileHandle)); } } return ZwCreateSection(SectionHandle, DesiredAccess,ObjectAttributes, MaximumSize,SectionPageProtection, AllocationAttributes,FileHandle); }
實際上上面這一小段就是圍繞一個filehandle展開的一些查詢,我們當然可以對SectionHandle做一些手腳,但是前提是要考慮上面提的那個問題!!
(3)api CreateFileMapping和native api ZwCreateSection除了上面我說的針對范圍的區別外,還有好多的區別:
Protect里面,api不能指定PAGE_EXECUTE_XXXX相關的保護標志。
Attributes里面,api不能指定SEC_NO_CHANGE SEC_BASED
而且SEC_VLM這個標志只有在win2000下面才能使用,而且對intel平台沒有實現。
(4)在vista以后的系統上,ZwCreateSection里面的參數部分發生了一些變化。SEC_IMAGE變成了0100000h后面變成了5個0.這是MJ發現的。具體的闡述可以看
http://www.debugman.com/read.php?tid=3217
(5)看到MJ介紹這個(4)里面提到的問題的時候,我看到一個函數MmLoadSystemImage,很顯然是內存相關的,而且貌似在系統啟動后,加載驅動的時候就開始使用上ZwCreateSection了。所以我覺得有必要再這里擴展下。
首先,驅動加載的話,我能想到的就是調用ZwLoadDriver。但是對於win2000(不知道xp怎么樣)上w2k.sys這個驅動是在系統啟動的時候就加載的,而且加載的方式不是ZwLoadDriver,那他是怎么加載的。查了下資料:原來是ZwSetSystemInformation這個函數。
他內部調用了MmLoadSystemImage。
我看了下wrk對這個函數的實現
NTSTATUS
NTAPI
MmLoadSystemImage(IN PUNICODE_STRING FileName,
IN PUNICODE_STRING NamePrefix OPTIONAL,
IN PUNICODE_STRING LoadedName OPTIONAL,
IN ULONG Flags,
OUT PVOID *ModuleObject,
OUT PVOID *ImageBaseAddress)
實際上這個ModuleObject就是指向section的。那么也就是說在這個函數的內部必然的調用了ZwCreateSection,事實也正是這樣的。由此也就出現了Mj說的那個問題!
由此看來,這個ZwCreateSection貌似還很重要嘛,這么早據被派上用場了。那么我們也看到了,實際上w2k.sys的加載就是這樣完成的。那么引申出一個加載去東的方法:
_loadDriver:
mov dword ptr [stack],esp
push ImageBaseAddress
push ModuleObject
push 0
push 0
push 0
push driverName
mov edi,805af342h;;MmLoadSystemImage在xp sp3上面的地址
call edi
or eax,eax
jne _fail
mov edi,dword ptr [ImageBaseAddress]
mov ebx,[edi+3ch]
mov ebx,[edi+18h+10h];;;熟悉pe結構的都知道,現在指向了EntryPoint,
add edi,ebx
push 0
push 0
call edi ;;也就是call entry
_fail:
mov esp,[stack]
ret
driverName:
db 'charme.sys',0
ImageBaseAddress dd 0
ModuleObject dd 0
end _loadDriver
非常完美,呼呼!!
(6)我現在特別想知道下ZwCreateSection這個內部是怎么實現的,哪怕是淺嘗呢!那我們看看吧!
我用windbg看了半天貌似就進不去么,直接看wrk怎么實現的吧!
Status = MmCreateSection(&SectionObject,
DesiredAccess,
ObjectAttributes,
MaximumSize,
SectionPageProtection,
AllocationAttributes,
FileHandle,
NULL);
if (NT_SUCCESS(Status))
{
Status = ObInsertObject ((PVOID)SectionObject,
NULL,
DesiredAccess,
0,
NULL,
SectionHandle);
}
return Status;
}
哦,原來是MmCreateSection啊!呼呼,很奇妙啊!看到這個實現,然后看看MmCreateSection的參數,我發現一個很奇怪的地方,就是MmCreateSection的第一個參數不是句柄了。但是filehandle還在,而且回憶MmLoadSystemImage的內部實現,他總是先ZwCreateFile,然后ZwCreateSection,而且后者的FileHandle參數傳遞的就是前者的返回者。看來一般情況下,我們總是走這樣的一個流程,雖然前面說了FileHandle參數可以為NULL,NULL的時候表示是根據內存頁來創建一個Section。
那么我們可以這樣想,比如有一個文件,我們不能讀他,也不能寫他,反正就是百毒不侵吧!那么我們要怎么才能讀取它,改寫他?這是一個問題!看看360的比賽題目我發現,貌似就有這樣的挑戰。
那么我想,我們可以這樣寫一段代碼!我們要讀取這個百毒不侵的文件,可以考慮把他映射以下,但是我說了,即使是直接使用MmCreateSection,它的參數里面也要設計FileHandle這個參數,問題是這個百毒不侵的文件根本就不允許我們獲得有效的文件句柄,那么下面的工作就全白費了。那么我們就想,不適用FileHandle參數,而實現完整的映射。這樣來看看:
///////////////////////////////////////////////////////////////
NTSTATUS NTAPI
MmCreateSection (OUT PVOID * Section,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
IN PLARGE_INTEGER MaximumSize,
IN ULONG SectionPageProtection,
IN ULONG AllocationAttributes,
IN HANDLE FileHandle OPTIONAL,
IN PFILE_OBJECT File OPTIONAL)
///////////////////////以上是注釋/////////////////////////////
NTSTATUS status;
LARGE_INTEGER li;
LARGE_INTEGER off;
SIZE_T st;
PFILE_OBJECT fo;
PVOID pSection;
li.QuadPart=1024*1024*2;
PVOID pCharme=0;
HANDLE hSection;
status=MmCreateSection(&pSection,SECTION_ALL_ACCESS,NULL,&li,PAGE_READWRITE,SEC_RESERVE,NULL,fo);
if (NT_SUCCESS(status))
{
status=ObInsertObject(pSection,NULL,SECTION_ALL_ACCESS,0,NULL,&hSection);
if(NT_SUCCESS(status))
{
status=MmMapViewOfSection(pSection,IoGetCurrentProcess(),&pCharme,0,100,&off,&st,1,MEM_TOP_DOWN,PAGE_READWRITE);
b=*((PBYTE)pCharme);
ZwClose(hSection);
}
else
{
DbgPrint(...)
}
}
else
{
DbgPrint(...)
}
pCharme就是最后映射好的可以讀取的內存塊的首地址。這個時候我們就可以操作剛才的文件了。
(7)翻到一個師傅幫別人解決了的問題。我也順便看了下,當時我還回答了下呢,其實沒有涉及要害!http://www.debugman.com/read.php?tid=3545
現在我來回答下。
首先ZwCreateFile和ZwCreateSection調用之前都得用 InitializeObjectAttributes宏來初始化一個Object_Attributes結構。按常規的邏輯想:
create file 的時候初始化屬性結構肯定要填充fielname為一個有效值的。
而調用ZwCreateSection的時候我們需要的只是filehandle,所以filename就為NULL就可以了。
那個兄弟只初始化了一次對象屬性。雖然把filename改成NULL之后可以了,但是實際上只是沒有編譯錯誤了,我敢保證他絕對不會實現他的那個映射的目的。因為他ZwCreateFile的時候filename是NULL,那么建什么文件呢?
總結:
這個函數貌似我擴展的最多了,累死了!就到這里吧,貌似這個函數只有結合相關的比如ZwOpenSection等函數才能寫個完整的程序出來。
我就不寫了。其實寫的話也很簡單了,弄透徹了原理和內部的一些機制。寫就很簡單了!greate!