毛德艹内核分析.开坑

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

}

你可能感兴趣的:(内核分析)