中断表进入0环
通过中断门进入0环,首先了解一下中断门的构成
 
 构造一个中断号
Base:函数地址
DPL:3	//因为三环使用调用门的条件就是CPL(即cs段选择子的RPL)<=DPL
P:1		//P为1时,中断表才有效
Segment Selector: 0x0008 //中断成功后切换自己的CPL 
在ce中直接写入
 
 或者在windbg中写入
eq 8003f500 0040ee00`00081040
使用Xuetr工具查看
 
 成功构造好的中断号
 通过int指令即可调用
关闭内核写保护
pushad
pushfdmov eax,cr0			//关闭页保护
and eax,not 0x10000
mov cr0,eaxmov eax,cr0			//开启页保护
or eax,0x10000
mov cr0,eaxpopad
popfd 
关于页保护机制
 使用windbg查看0x8054252D的pte
 
正常地址有-KWEV权限
 
因此使用上述指令关闭页写入保护,防止有的地方无法写入jmp指令,导致无法hook
解决进入0环无法任务调度的问题
首先,浅聊一下fs的问题
 逆向内核文件 ntkrnlpa.exe 原因是 单核处理器运行该程序
 操作系统的内核模块根据处理器的个数和是否支持PAE(Physical Address Extension物理地址扩展)分为以下四种
ntoskrnl.exe ---Uniprocessor单处理器,不支持PAE
ntkrnlpa.exe ---Uniprocessor单处理器,支持PAE
ntkrnlmp.exe ---Multiprocessor多处理器,不支持PAE
ntkrpamp.exe ---Mulitiprocessor多处理器,支持PAE
ntkrnlpa.exe 查看windows内核的0环调用
 
push 0x30
pop fs
fs与CPU的任务切换有一定关系
 通过修改fs 切换线程 完成CPU的任务调度
 
 Ring 3 和Ring 0的FS没有切换,所以CPU并没有进行任务调度
 使用以下指令修改FS寄存器,使CPU进行任务调度
push 0x30 //写入fs
pop fspush 0x3b	//恢复FS
pop fs  

尝试调用0环的API
尝试使用Windows的0环API ExAllocatePool(目前已弃用) 但是依然可以直接call该地址获取
 从汇编指令看 传入了2个参数
 使用函数指针的方式调用该函数
 
首先在Xuetr工具中寻找ntkrnlpa.exe的基地址
 使用ida的rebase功能
 
将查找到的基地址放入 重新初始化地址

寻找ExAllocPool函数
 
编写如下代码
#include <windows.h>
typedef (__stdll *Ex_ALLOCPOOL)(DWORD PoolType,DWORD NumberOfBytes)
Ex_ALLOCPOOL ExALLOCPOOL = (Ex_ALLOCPOOL)0x80537FF8

得到了被分配的内存
Jmp/Call硬编码问题
接下来进行hook操作,使用jmp指令跳转到刚才的内存里面
 jmp的硬编码是e9
 那么后面的四位硬编码是什么呢?
 可以看到在3环程序中,jmp之后的地址与真实跳转的地址是不一样的,以call举例子

可以看到真正call的地址不是0x000213CF
 而是0xFFFFFA27(小端序问题)
 那这步地址怎么算?
[Address] =真正要跳转的地址-[call指令的地址]+5(call指令的下一条指令的地址)
也就是说真正要call的地址是0x000213CF - 0x000219A8 = 0xFFFFFA27(溢出保留)

使用__emit来实现一下

程序正常执行了Call_func函数
Hook _kifastCall函数
首先看看_kifastCall函数原来的汇编
 
我们知道jmp [address]的长度是5个字节 一个jmp指令(1字节)+一个地址(4字节)
 刚好此处第一行的mov ecx,23h也是5个字节,因此劫持该函数
 使此处jmp到gdt表中(因为gdtr有相当一部分空间没用用到)[也可以用api来创建内存,不过比较麻烦],然后在gdt表中写入要hook的指令即可
要hook的指令
mov eax,ds:[0x8003f7f0] //写好hook代码
inc eax
mov ds:[0x8003f7f0],eax
就是系统每调用一次api都会经过_kifastCall因此在此处加一,即可得知,程序被调用了多少次
 然后再执行接下来程序本来该执行的指令
mov ecx,23h	//执行程序接下来的动作 因为这几步需要用cx寄存器,因此先把用到cx寄存器的代码执行掉
push 30h
pop fs
mov ds,cx
mov es,cx
接下来再返回mov es,cx的下一步指令
 
也就是0x8054252D
 完整代码如下
#include<stdio.h>
#include<stdlib.h>
void Hooked();
char* p;
int i;
void __declspec(naked) CpyMemory() {p = (char*)0x8003f120;for ( i = 0; i < 64; i++){*p = ((char*)Hooked)[i];p++;}__asm {iretd}
}void __declspec(naked) Hooked() {__asm {pushad		//保存当前堆栈pushfd mov eax,ds:[0x8003f7f0] //写好hook代码inc eaxmov ds:[0x8003f7f0],eaxpopfd		//恢复堆栈popadmov ecx,23h	//执行程序接下来的动作 因为这几步需要用cx寄存器,因此先把用到cx寄存器的代码执行掉push 30hpop fsmov ds,cxmov es,cxmov ecx,0x8054252D	//返回一个固定地址 否则需要计算地址(不固定)jmp ecx}
}void __declspec(naked) Hookinline() {__asm {mov eax,cr0	//关闭页保护and eax,not 10000hmov cr0,eax mov ax, 0xe9		//写入jmp指令mov ds:[0x80542520],almov eax,0xFFAFCBFBmov ds:[0x80542521],eax	mov eax,cr0	//打开页保护or eax,10000hmov cr0,eaxiretd}
}	void intEntry() {__asm {int 0x21 //先写入gdtr表	调用CpyMemory函数int 0x20 //Hook __kifastCall 函数	调用Hookinline}
}int main() {printf("CpyMemory Address:%p\n", CpyMemory);printf("Hookinline Address %p\n", Hookinline);intEntry();system("pause");return 0;
}
成功之后,使用ce观察0x8003f7f0发现该地址不断在加一
 
即为hook成功
 流程如下
 