解读cih进入ring0的秘密
看了大家对前面几篇的评论,觉得有点怪异,看来大家对技术的追求还不是那么强烈,声明以下,我写的这些东西或许永远也不会对你的实际应用有帮助,这只是黑客们对技术的研究,如果你的愿望只是一般的编程,这篇文章不适合你。
这几天开始编一个游戏,以前没有接触过游戏编程,觉得很有意思,还有directX的编程,真得很爽,我估计也就几天的热情吧,呵呵。
还有,我知道我的老婆每天都来看我的文章,这里问个好:亲爱的tina,你好啊!
正题。
我们今天研究的代码主要是进入ring0的实现。首先我们要有几个问题,不知道你们有没有这样的问题,反正我在研究cih的时候是有。
1、什么是ring0?
2、为什么要进入ring0?
3、怎么样才算进入ring0
现在解答一下。
什么是ring0呢?windows分4层保护结构,最核心的ring0,最外面的是ring3,ring0层的应用程序可以直接和硬件打交道,其他层的就必须通过hal.dll来调用相应的api来和硬件打交道,所以如果想突破限制,搞些破坏就要进入ring0.
怎么才算进入ring0? 当cs=28,ds=30时就可以认为进入了ring0.
进入的方法还有很多种,比如什么中断门,陷阱门,调用门,具体请察看微机原理相应的知识。
怎么通过seh进入呢?记得上一章我们说的lpcontext么?就是靠它了!
现在说一下流程:
1、设置seh链表
2、触发断点异常int3
3、清除自己触发的异常
4、通过lpcontext修改cs=28,ds=30
这就是主要的了,是不是很简单亚。其实到昨天我还是想不通为什么要通过seh才能修改,今天终于灵光一现,想通了,其实就是看我自己写的第3篇的那段关于lpcontext的话才想到的,呵呵。
下面是我学习cih的代码,我也加入了一些注释,你们看吧:
;;;;;;;;;;;;;;;;;;;;;;;;;;我估计这段代码使tasm编译的,和masm有些不一样
include "../inc/win32n.inc"
extern _ExitProcess@4
extern _GetTickCount@0
extern _GetStdHandle@4
extern _MessageBeep@4
extern _WriteConsoleA@20
global _mainCRTStartup
SEGMENT .text USE32 class=code
_mainCRTStartup
push dword STD_OUTPUT_HANDLE ;
call _GetStdHandle@4 ;获取输出控制屏的句柄
mov [stdout],eax ;保存句柄
;******************************************
;* 以下这一段是按照SEH必要的方式形成链表
;******************************************
xor ebx,ebx ;先清0
push dword eh ;压入自己ExceptHandle指针,即 eh 处
push dword [fs:ebx] ;压入当前[FS:0] ,想想为什么不直接fs:0?
mov [fs:ebx],esp ;形成链表形式
pushfd ;入栈保存当前EFlags标志寄存器的内容
mov eax,esp ;保存当前堆栈指针到 eax
;******************************************
;* 这里将产生断点中断,从而进入异常处理程序
;******************************************
int3
;******************************************
;* 这里是从异常处理程序中返回后来到的地址
;******************************************
mov ebx,cr3 ;以后将会显示ebx的内容,即显示 cr3 的内容
;**************************************************************************************
;* 以下是一种特殊方式的跳转。由于使用 push/iretd 来进行跳转,注意不是 push/retn。
;* 所以,在堆栈中要把各个寄存器的内容入栈,这是因为当中断发生时,系统会自动把
;* 这些寄存器的内容入栈,而在 iretd 返回时自动出栈。这就是 iretd 和 retn 的区别。
;* 另外,由于在异常处理程序中进行了以下修改。所以刚返回时:ECX=CS,CS=28,EDX=SS,SS=30,
;* 所以,此时我们看到各寄存器的内容与此相同。这主要是一种演示,或者说是一种简单的应用。
;这一段我也不懂,不懂为什么可以达到目的,我和figozhu讨论过,也没有答案,这个我先欠大家的 **************************************************************************************
push dword edx ; GS
push dword edx ; FS
push dword edx ; ES
push dword edx ; DS
push dword edx ; SS
push eax ; ESP
push dword [eax] ; EFLAGS
push dword ecx ; CS
push dword .wow ; EIP
iretd
.wow
;******************************************
;* 显示 ebx 的内容
;******************************************
popfd
call printaddress
;******************************************
;* 这一段将在SEH链表中清除自己的异常处理
;******************************************
pop dword [fs:0] ;通过出栈的方式修改[FS:0]的内容
add esp,4
push dword MB_ICONASTERISK
call _MessageBeep@4 ;鸣声提示
push dword 0
call _ExitProcess@4 ;退出程序
; retn
;******************************************
;* 这里是自己的异常处理程序开始
;******************************************
eh
push ebp ;保存ebp,当前指针
mov ebp,esp
push ebx ;入栈保存
push ecx ;入栈保存
;******************************************
;* 这一段为判断异常标志(ExceptionFlags)是否
;* 为UNWIND_STACK。
;******************************************
mov eax,[ebp+8] ;[EBP+8]=EXCEPTION_RECORD_ptr,即EAX为EXCEPTION_RECORD的指针 。
test dword [eax+4],6 ;[EAX+4]=EXCEPTION_RECORD_ptr->ExceptionFlags,
;即判断ExceptionFlags是否为UNWIND_STACK,此在SEH.inc中有定义
jz .cont ;是,则跳转
;******************************************
;* 显示 oi
;******************************************
push dword 0 ;保存现场后调用函数显示
push dword written
push dword 2
push dword oi
push dword [stdout]
call _WriteConsoleA@20
mov eax,1
jmp .e
.cont
;*********************************************************
;* 判断异常代码(ExceptionCode)是否为EXCEPTION_BREAKPOINT ,也就是int3
;*********************************************************
mov ebx,[ebp+8] ;[EBP+8]=EXCEPTION_RECORD_ptr,即EAX为EXCEPTION_RECORD的指针
mov eax,1
cmp dword [ebx],0x80000003 ;[EBX]=EXCEPTION_RECORD_ptr->ExceptionCode, int3的异常特征码是0x80000003
;即判断ExceptionCode是否为EXCEPTION_BREAKPOINT(断点标志)
jne .e ;不是,则跳转,返回值EAX为1
;**********************************************************
;* 修改xFrame_RECORD_ptr->cx_Ecx=xFrame_RECORD_ptr->cxSegCs
;主要利用context来修改**********************************************************
mov eax,[ebp+0x10] ;[EBP+10]=xFrame_RECORD_ptr,即EAX为xFrame_RECORD的指针
movzx ecx,word [eax+CONTEXT.cx_SegCs]
mov [eax+CONTEXT.cx_Ecx],ecx ;xFrame_RECORD_ptr->cx_Ecx=xFrame_RECORD_ptr->cxSegCs
;**********************************************************
;* 修改xFrame_RECORD_ptr->cx_SegCs=0x28
;**********************************************************
mov dword [eax+CONTEXT.cx_SegCs],0x28 ;修改cs=28
;**********************************************************
;* 修改xFrame_RECORD_ptr->cx_Edx=xFrame_RECORD_ptr->cxSegSs
;**********************************************************
movzx ecx, word [eax+CONTEXT.cx_SegSs]
mov [eax+CONTEXT.cx_Edx],ecx
;**********************************************************
;* 修改xFrame_RECORD_ptr->cx_SegSs=0x30
;**********************************************************
mov dword [eax+CONTEXT.cx_SegSs],0x30
;ss=30
;**********************************************************
;* 修改xFrame_RECORD_ptr->cx_EFlags=0x0200
;**********************************************************
or dword [eax+CONTEXT.cx_EFlags],0x0200 ;设置EFlags中的IF标志(中断允许标志)为1,即 CLI
;*********************************************************
;* 在控制屏上显示 ebx 的内容
;*********************************************************
mov ebx,0x12345678
call printaddress
;*********************************************************
;* EAX=0,即通知Kernel将继续执行
;*********************************************************
mov eax,0
.e
pop ecx ;恢复现场 ;出栈恢复
pop ebx ;出栈恢复
mov esp,ebp
pop ebp
retn ;返回
;******************************************
;* 这里是自己的异常处理程序结束处 到这里进入ring0也结束了
;******************************************
;******************************************
;* 转换ebx的内容为字符形式,并在屏幕上显示
;* ebx为入口参数,代表一个地址
;******************************************
printaddress
push ecx ; just a lame routine to print out
push edx ; a hex address, no explanations ;-)
xor ecx,ecx
.n
rol ebx,4
mov dl,bl
and dl,0x0F
cmp dl,0x9
ja .abcdef
add dl,'0'
jmp .a
.abcdef
add dl,'A'-0xA
.a
mov [ecx+address],dl
inc cl
cmp cl,8
jne .n
push dword 0
push dword written
push dword 9
push dword address
push dword [stdout]
call _WriteConsoleA@20
pop edx
pop ecx
retn
SEGMENT .data USE32 class=data
stdout dd 0
onedot db '.'
oi db '!',0x0A
written dd 0
address db 0,1,2,3,4,5,6,7,0x0A
;**********************************end********
好了,该说得都说了,代码也贴出来了,是不是觉得有点拨云见日的感觉?我是有这种感觉的,以前听他们说怎么怎么难,其实也不过如此,所以没有亲身体会过的人是没有资格评论什么难易的。
要随时准备学习新的技术,这是我的心得。接下来准备写点apihook的文章,自己学习也希望和朋友们分享,如果有时间我想每天写一篇文章,比玩游戏好。