API Function Prefix:
Ex: Executive
Ke/Ki: Kernel
ke, kernel manager layer
ki, kernel low lever about (trap , interrupt, break)
Hal: Hardware Abstraction Layer
Ob: Object
Mm: Memory Manager
Ps: Porcess
Se: Security
Io: I/O
Fs: File System
Cc: Cache
Cm: Configuration Manager
Pp: PnP
Rtl: Runtime Library
with 'f' in function name = fast call, example: FASTCALL ObfDereferenceObject = fastcall object
fastcall use ECX as call flag
asm func name:
@_RtlZeroMemory@8, prefix@=fastcall, @8=2byte=2param
Interrupt: anyone request > CPU services, passive
Exception: execute code failed > go into kernel
Trap: active
tradition way use int 0x2e enter kernel model, it process follow steps:
. CPU go into kernel model
. from "Task Status Seciton"TSS load SS , ESP(kernel space)
. SS, ESP, EFLAGS, CS, EIP push into (user space stack)
. Load IDT 0x2e -> program entery address
* iret 逆处理上面过程
add some ways: 'sysenter' 'sysexit' = fast kernel call = trap
sysenter , for this func, CPU add 3 MSR register: SYSENTER_CS_MSR| SYSENTER_EIP_MSR| SYSENTER_ESP_MSR (MSR=Mode Specific Register)
if user want to set the 3 register, just can use 'rdmsr' 'wrmsr' two spefic command
. user set 3 new MSR register
. run sysenter
. CS -> point kernel space, EIP -> point kernel fastcall entry, SS/ESP -> kernel space stack top
ReactOS NTReadFile() process example:
. load ReadFile() API from kernel32.dll - 可以查看导出表/ DDK头文件之中有接口定义
. ReadFIle() load NtReadFile() from ntoskrnl.exe - 可以查看到导出表, 但这个NtReadFile是用户态的 :(
. this NtReadFile asm
before run this func, paramters should be push into stack first(user space)
so, in stack : |ESP|param1|param2|param2|……|EBP|
{
push ebp - ebp = base pointr 不变(存取固定参数), esp = stack pointer 变(stack top) - 存下调用前的ebp
mov ebp, esp - 重新定义本次ebp
mov eax, 152 - eax持有调用号152 = NtReadFile(kernel model)
lea edx, 8[ebp] - lea: 内存地址赋予寄存器 > edx = 8 + [ebp] (压入两个参数) = edx 指向user space参数块的起点
int 0x2E - 进入内核
pop ebp // 平衡堆栈
ret 9 // ..
}
like such function, calls 中介函数,just connect user space and kernel space, we call it 'stub' : user space <- func -> kernel space
中介函数一般都放在 ntdll.dll 之中
ntdll.dll启动之后一直驻留在系统之中,
but, kernel sapce 0xffdf0000 = user space 0x7ffe0300, 两块64KB的虚拟内存都映射到同一块物理内存
newer version:
{
move eax, 152
move ecx, KUSER_SHARED_SYSCALL // KUSER_SHARED_SYSCALL=user space 0x7FFE0300=one func pointer, point to KiIntSystemCall() or KiFastSystemCall() depend on system
call [ecx]
ret 9
}
explain KiIntSystemCall()
= old source code
explain KiFastSystemCall()
= invoke sysenter; sysexit; do not use edx pass through params. but make edx point to return point, System will caculate params automatic.
: 切换到kernel model 本质在CPU, 其和内存管理部件MMU (Memory Manager Unit)共同作用,使得控制那些虚拟内存是可以读的
in enterence of kernel, trap, interrupt, exception is almost same.
but!
windows idt != CPU idt
windows concept idt like:
idt _KiTrap0, INT_32_DPL0, KGDT_R0_CODE; 一个windows内核idt项, 再次强调!= CPU IDT
idt =
{
.marco idt Handler, Bits
.long \Handler // Hander = entry address like '_KiTrap0'
.short \Bits // example : INT_32_DPL0 = 0x8E00 = 10001110 = CPU IDT: P flag = 1, enable this idt; DPL = 00 , in ring 0; D = 1, 32bit CPU, 结合下面CPU idt来看
.short KGDT_R0_CODE // const number > KGDT_R0_CODE = 0x8 = 1000 参考上册P36, 这里不是太理解
}
CPU interrupt: in intel document it calls (Gate Descriptor = IDT Descriptor)
format: |Offset 31- 16| P|DPL|0D110|000|5 bit empty| 这个才是cpu的IDT
P: Present (1 bit) enable/disable
DPL: Descriptor Privilege Level, run level (2 bit)
D: if D=1 IDT is 32bit, else D=0 IDT is 16bit
if CPU is 64X 最低16位和最高16位合并在一起成为32位address = 程序入口, 上面例子是32cpu情形
in OS, register IDTR pointto -> KiIdt[]
windows Trap:
int 3 - debug - entery _KiTrap3
int 2c - debug - _KiRaiseAssertion
int 2d - debug - _KiDebugService
int 2e - trap - entry _KiSystemService
int 2a - timer - entry _KiGetTickCount - high acculturate clock
int 2b - call user space func from kernel model - _KiCallBackReturn
这些是常用IDT, 如果要找合适的地方hook, 要自己分析idt, 具体参考 _KiIdt 描述表
----------------------------------------------------------------------------------------------------------------------------------------------------------
detail analyse int 2e - _KiSystemService:
when enter kernel through 2e trap
cpu do this things follow:
. get SS/ESP from TSS lead by TR
. push register into stack
user model SS // stack segment
user model ESP
EFLAGS
user model CS // cs code segment
user model EIP
------------ <-------- System ESP point at here
. get CS/EIP from IDTR : is a pointto -> KiIdt[]
attantion: every thread have its own kernel space,
its SS & ESP store in TSS (Task Status Segment) Data Struct: software
compare with that
CPU have a register call 'TR'
each time switch user model to kernel model,
CPU get SS/ESP from TSS which lead by TR info
different of common Segment Register,
there are 2 command 'ltr' 'str' to save TR or get data from TR, user model can not invoke them.
continue analyse _KiSystemService:
each time enter _KiSystemService, it will call
{
SYSCALL_PROLOG kss_a, kss_t // 为了创建一个Trap invoke的框架
jump SharedCode
}
detail about SYSCALL_PROLOG , 细细读 _KiSystemService trap call 里面的准备部分
{
.macro SYSCALL_PROLOG Label EndLabel // EndLabel is this macro param, it will use later
/*Create a trap frame*/
push 0 //
push ebp
push ebx
push esi
push edi
push fs // save user space FS
/* Load PCR Selector into fs*/
mov ebx, KGDT_R0_PCR //KGDT_R0_PCR = 0x30 = 0011,0000
.byte 0x66
mov fs, bx // 使得FS段的起点与KPCR数据结构对齐,怎么理解?
/* Get a pointer to the current thread*/
mov esi, PCR[KPCR_CURRENT_THREAD] // make ESI 指向当前进程的KTHREAD结构
/* Save the previous exception list*/
push PCR[KPCR_EXCEPTION_LIST] // 保存老的ExceptionList
/* Set the exception handler chain terminator */
mov dword ptr PCR[KPCR_EXCEPTION_LIST], -1 //新的 Exception为空白
/* Save the old previous mode */
push [esi+KTHREAD_PREVIOUS_MODE]
/* Skip the other registers*/
sub esp, 0x48
/*Set the new previous mode based on the saved CS selector*/
mov ebx, [esp+0x6C] // 系统调用前夕的CS映像
and ebx, 1 // 0 ring 的最低位为0, 3环的最低位为1
mov byte ptr [esi+KTHREAD_PREVIOUS_MODE], bl //新的“先前模式”
/* Go on the Kernel stack frame*/
mov ebp, esp
/* Save the old trap frame pointer where EDX would be saved*/
mov ebx, [esi+KTHREAD_TRAP_FRAME] //KTHREAD结构中的指针 TrapFrame
mov [ebp+KTRAP_FRAME_EDX], ebx //暂时保存在这里
/* Flush DR7 */
and dword ptr [ebp+KTRAP_FRAME_DR7], 0
/* Check if the thread was being debugged */
test byte ptr[esi+KTHREAD_DEBUG_ACTIVE], 0xFF
/* Set the thread's trap frame and clear direction flag*/
mov [esi+KTHREAD_TRAP_FRAME],ebp //新的TrapFrame, 指向堆栈上的框架
cld
/* Save DR registers if needed*/
jnz DR_&Label
/* Save the trap frame debug header*/
Dr_&EndLabel: // use macro param: this line means strcat, = Dr_param = Dr_kss_t (label)
SET_TF_DEBUG_HEADER
/* Enable interrupts */
sti
.endm
}