線上訂房服務-台灣趴趴狗聯合訂房中心
發文 回覆 瀏覽次數:2128
推到 Plurk!
推到 Facebook!

NT/2000下不用驅動的Ring0代碼實現

 
axsoft
版主


發表:681
回覆:1056
積分:969
註冊:2002-03-13

發送簡訊給我
#1 引用回覆 回覆 發表時間:2003-02-25 16:58:18 IP:61.218.xxx.xxx 未訂閱

NT/2000下不用驅動的Ring0代碼實現

資料來源: WebCrazy(http://webcrazy.yeah.net/) 大家知道,Windows NT/2000為實現其可靠性,嚴格將系統劃分為內核模式與用戶模式, 在i386系統中分別對應CPU的Ring0與Ring3級別。Ring0下,可以執行特權級指令,對 任何I/O設備都有訪問權等等。要實現從用戶態進入核心態,即從Ring 3進入Ring 0必 須借助CPU的某種門機制,如中斷門、調用門等。而Windows NT/2000提供用戶態執行系 統服務(Ring 0例程)的此類機制即System Service的int 2eh中斷服務等,嚴格的參數 檢查,只能嚴格的執行Windows NT/2000提供的服務,而如果想執行用戶提供的Ring 0 代碼(指運行在Ring 0權限的代碼),常規方法似乎只有編寫設備驅動程序。本文將介紹 一種在用戶態不借助任何驅動程序執行Ring0代碼的方法。 Windows NT/2000將設備驅動程序調入內核區域(常見的位於地址0x80000000上),由DPL 為0的GDT項8,即cs為8時實現Ring 0權限。本文通過在系統中構造一個指向我們的代碼 的調用門(CallGate),實現Ring0代碼。基於這個思路,為實現這個目的主要是構造自己 的CallGate。CallGate由系統中叫Global Descriptor Table(GDT)的全局表指定。GDT地 址可由i386指令sgdt獲得(sgdt不是特權級指令,普通Ring 3程序均可執行)。GDT地址在 Windows NT/2000保存於KPCR(Processor Control Region)結構中(見《再談Windows NT/ 2000環境切換》)。GDT中的CallGate是如下的格式:

typedef struct
{
unsigned short offset_0_15;
unsigned short selector;    unsigned char param_count : 4;
unsigned char some_bits : 4;    unsigned char type : 4;
unsigned char app_system : 1;
unsigned char dpl : 2;
unsigned char present : 1;    unsigned short offset_16_31;
} CALLGATE_DESCRIPTOR;
GDT位於內核區域,一般用戶態的程序是不可能對這段內存區域有直接的訪問權。幸運的 是Windows NT/2000提供了一個叫PhysicalMemory的Section內核對像位於\Device的路徑 下。顧名思義,通過這個Section對象可以對物理內存進行操作。用objdir.exe對這個對 象分析如下:
    C:\NTDDK\bin>objdir /D \Device    PhysicalMemory 
Section
DACL - 
Ace[ 0] - Grant - 0xf001f - NT AUTHORITY\SYSTEM
Inherit: 
Access: 0x001F and ( D RCtl WOwn WDacl )    Ace[ 1] - Grant - 0x2000d - BUILTIN\Administrators
Inherit: 
Access: 0x000D and ( RCtl )
從dump出的這個對象DACL的Ace可以看出默認情況下只有SYSTEM用戶才有對這個對象的讀寫 權限,即對物理內存有讀寫能力,而Administrator只有讀權限,普通用戶根本就沒有權限。 不過如果我們有Administrator權限就可以通過GetSecurityInfo、SetEntriesInAcl與Set SecurityInfo這些API來修改這個對象的ACE。這也是我提供的代碼需要Administrator的原 因。實現的代碼如下:

VOID SetPhyscialMemorySectionCanBeWrited(HANDLE hSection)
{    PACL pDacl=NULL;
PACL pNewDacl=NULL;
PSECURITY_DESCRIPTOR pSD=NULL;
DWORD dwRes;
EXPLICIT_ACCESS ea;    if(dwRes=GetSecurityInfo(hSection,SE_KERNEL_OBJECT,DACL_SECURITY_INFORMATION,
NULL,NULL,&pDacl,NULL,&pSD)!=ERROR_SUCCESS)
{
printf( "GetSecurityInfo Error %u\n", dwRes );
goto CleanUp;
}    ZeroMemory(&ea, sizeof(EXPLICIT_ACCESS));
ea.grfAccessPermissions = SECTION_MAP_WRITE;
ea.grfAccessMode = GRANT_ACCESS;
ea.grfInheritance= NO_INHERITANCE;
ea.Trustee.TrusteeForm = TRUSTEE_IS_NAME;
ea.Trustee.TrusteeType = TRUSTEE_IS_USER;
ea.Trustee.ptstrName = "CURRENT_USER";    if(dwRes=SetEntriesInAcl(1,&ea,pDacl,&pNewDacl)!=ERROR_SUCCESS)
{
printf( "SetEntriesInAcl %u\n", dwRes );
goto CleanUp;
}    if(dwRes=SetSecurityInfo(hSection,SE_KERNEL_OBJECT,DACL_SECURITY_INFORMATION,
NULL,NULL,pNewDacl,NULL)!=ERROR_SUCCESS)
{
printf("SetSecurityInfo %u\n",dwRes);
goto CleanUp;
}    CleanUp:    if(pSD)
LocalFree(pSD);
if(pNewDacl)
LocalFree(pSD);
}
這段代碼對給定HANDLE的對象增加了如下的ACE:

PhysicalMemory 
Section
DACL - 
Ace[ 0] - Grant - 0x2 - WEBCRAZY\Administrator
Inherit: 
Access: 0x0002 //SECTION_MAP_WRITE
這樣我們在有Administrator權限的條件下就有了對物理內存的讀寫能力。但若要修改GDT 表實現Ring 0代碼。我們將面臨著另一個難題,因為sgdt指令獲得的GDT地址是虛擬地址 (線性地址),我們只有知道GDT表的物理地址後才能通過\Device\PhysicalMemory對像修 改GDT表,這就牽涉到了線性地址轉化成物理地址的問題。我們先來看一看Windows NT/ 2000是如何實現這個的: kd> u nt!MmGetPhysicalAddress l 30 ntoskrnl!MmGetPhysicalAddress: 801374e0 56 push esi 801374e1 8b742408 mov esi,[esp 0x8] 801374e5 33d2 xor edx,edx 801374e7 81fe00000080 cmp esi,0x80000000 801374ed 722c jb ntoskrnl!MmGetPhysicalAddress 0x2b (8013751b) 801374ef 81fe000000a0 cmp esi,0xa0000000 801374f5 7324 jnb ntoskrnl!MmGetPhysicalAddress 0x2b (8013751b) 801374f7 39153ce71780 cmp [ntoskrnl!MmKseg2Frame (8017e73c)],edx 801374fd 741c jz ntoskrnl!MmGetPhysicalAddress 0x2b (8013751b) 801374ff 8bc6 mov eax,esi 80137501 c1e80c shr eax,0xc 80137504 25ffff0100 and eax,0x1ffff 80137509 6a0c push 0xc 8013750b 59 pop ecx 8013750c e8d3a7fcff call ntoskrnl!_allshl (80101ce4) 80137511 81e6ff0f0000 and esi,0xfff 80137517 03c6 add eax,esi 80137519 eb17 jmp ntoskrnl!MmGetPhysicalAddress 0x57 (80137532) 8013751b 8bc6 mov eax,esi 8013751d c1e80a shr eax,0xa 80137520 25fcff3f00 and eax,0x3ffffc 80137525 2d00000040 sub eax,0x40000000 8013752a 8b00 mov eax,[eax] 8013752c a801 test al,0x1 8013752e 7506 jnz ntoskrnl!MmGetPhysicalAddress 0x44 (80137536) 80137530 33c0 xor eax,eax 80137532 5e pop esi 80137533 c20400 ret 0x4 從這段彙編代碼可看出如果線性地址在0x80000000與0xa0000000範圍內,只是簡單的進行 移位操作(位於801374ff-80137519指令間),並未查頁表。我想Microsoft這樣安排肯定是 出於執行效率的考慮。這也為我們指明了一線曙光,因為GDT表在Windows NT/2000中一般 情況下均位於這個區域(我不知道/3GB開關的Windows NT/2000是不是這種情況)。 經過這樣的分析,我們就可以只通過用戶態程序修改GDT表了。而增加一個CallGate就不是 我可以介紹的了,找本Intel手冊自己看一看了。具體實現代碼如下:

typedef struct gdtr {
short Limit;
short BaseLow;
short BaseHigh;
} Gdtr_t, *PGdtr_t;    ULONG MiniMmGetPhysicalAddress(ULONG virtualaddress)
{
if(virtualaddress<0x80000000||virtualaddress>=0xA0000000)
return 0;
return virtualaddress&0x1FFFF000;
}    BOOL ExecRing0Proc(ULONG Entry,ULONG seglen)
{
Gdtr_t gdt;
__asm sgdt gdt;    ULONG mapAddr=MiniMmGetPhysicalAddress(gdt.BaseHigh<<16U|gdt.BaseLow);
if(!mapAddr) return 0;    HANDLE hSection=NULL;
NTSTATUS status;
OBJECT_ATTRIBUTES objectAttributes;
UNICODE_STRING objName;
CALLGATE_DESCRIPTOR *cg;    status = STATUS_SUCCESS;    RtlInitUnicodeString(&objName,L"\\Device\\PhysicalMemory");    InitializeObjectAttributes(&objectAttributes,
&objName,
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
NULL,
(PSECURITY_DESCRIPTOR) NULL);    status = ZwOpenSection(&hSection,SECTION_MAP_READ|SECTION_MAP_WRITE,
&objectAttributes);    if(status == STATUS_ACCESS_DENIED){
status = ZwOpenSection(&hSection,READ_CONTROL|WRITE_DAC
           ,&objectAttributes);
SetPhyscialMemorySectionCanBeWrited(hSection);
ZwClose(hSection);
status =ZwOpenSection(&hSection,SECTION_MAP_WRITE|SECTION_MAP_WRITE,
        &objectAttributes);
}    if(status != STATUS_SUCCESS)
{
printf("Error Open PhysicalMemory Section Object,Status:X\n",status);
return 0;
}    PVOID BaseAddress;    BaseAddress=MapViewOfFile(hSection,
FILE_MAP_READ|FILE_MAP_WRITE,
0,
mapAddr, //low part
(gdt.Limit 1));    if(!BaseAddress)
{
printf("Error MapViewOfFile:");
PrintWin32Error(GetLastError());
return 0;
}    BOOL setcg=FALSE;    for(cg=(CALLGATE_DESCRIPTOR *)((ULONG)BaseAddress (gdt.Limit&0xFFF8));
              (ULONG)cg>(ULONG)BaseAddress;cg--)
if(cg->type == 0){
cg->offset_0_15 = LOWORD(Entry);
cg->selector = 8;
cg->param_count = 0;
cg->some_bits = 0;
cg->type = 0xC; // 386 call gate
cg->app_system = 0; // A system descriptor
cg->dpl = 3; // Ring 3 code can call
cg->present = 1;
cg->offset_16_31 = HIWORD(Entry);
setcg=TRUE;
break;
}    if(!setcg){
ZwClose(hSection);
return 0;
}    short farcall[3];    farcall[2]=((short)((ULONG)cg-(ULONG)BaseAddress))|3; //Ring 3 callgate;    if(!VirtualLock((PVOID)Entry,seglen))
{
printf("Error VirtualLock:");
PrintWin32Error(GetLastError());
return 0;
}    SetThreadPriority(GetCurrentThread(),THREAD_PRIORITY_TIME_CRITICAL);    Sleep(0);    _asm call fword ptr [farcall]    SetThreadPriority(GetCurrentThread(),THREAD_PRIORITY_NORMAL);    VirtualUnlock((PVOID)Entry,seglen);    //Clear callgate
*(ULONG *)cg=0;
*((ULONG *)cg 1)=0;    ZwClose(hSection);
return TRUE;    }
我在提供的代碼中演示了對Control Register與I/O端口的操作。CIH病毒在Windows 9X中就是因為獲得Ring 0權限才有了一定的危害,但Windows NT/2000畢竟不是Windo ws 9X,她已經有了比較多的安全審核機制,本文提供的代碼也要求具有Administra tor權限,但如果系統存在某種漏洞,如緩衝區溢出等等,還是有可能獲得這種權限的, 所以我不對本文提供的方法負有任何的責任,所有討論只是一個技術熱愛者在討論 技術而已。謝謝! 參考資料: 1.Intel Corp << Intel Architecture Software Developer's Manual,Volume 3 >> 聯盟----Visita網站http://www.vista.org.tw ---[ 發問前請先找找舊文章 ]--- 發表人 - axsoft 於 2003/02/25 17:04:31
系統時間:2024-07-01 21:08:22
聯絡我們 | Delphi K.Top討論版
本站聲明
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。
2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。
3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇!