算是对之前实验的补充,依旧是xp系统
几个重要dll
kernel32.dll:管理内存,进程和线程相关函数
User32.dll:用户界面相关应用程序接口,创建窗口和发送消息等
GDI32.dll:画图,显示文本相关函数
Ntdll.dll:三环进零环相关
ReadProcessMemory
kernel32.dll里的ReadProcessMemory为例
调用了ntdll里的导出函数
.text:7C8021E5 FF 15 18 14 80 7C call ds:__imp__NtReadVirtualMemory@20 ; NtReadVirtualMemory(x,x,x,x,x)
|
ntdll里
.text:7C92D9E0 B8 BA 00 00 00 mov eax, 0BAh ;BA编号对应内核函数NtReadVirtualMemory .text:7C92D9E5 BA 00 03 FE 7F mov edx, 7FFE0300h .text:7C92D9EA FF 12 call dword ptr [edx] .text:7C92D9EC C2 14 00 retn 14h
|
7FFE0300h是啥
_KUSER_SHARED_DATA结构体:用户态的地址0x7FFE0000
内核态的地址0xffdf0000
共享一张物理页,用户态是只读的,内核态是可写的
kd> dt _KUSER_SHARED_DATA 0x7FFE0000 ... +0x300 SystemCall : 0x7c92e4f0
|
7FFE0300h处存在两种情况:KiFastSystemCall或者KiIntSystemCall(都是ntdll导出函数)
eax置0,执行cpuid指令会把处理器特征存在ecx edx,edx 第11为SEP为1则支持快速调用sysenter/sysexit指令为KiFastSystemCall
kd> u 0x7c92e4f0 7c92e4f0 8bd4 mov edx,esp 7c92e4f2 0f34 sysenter 7c92e4f4 c3 ret
|
否则为KiIntSystemCall
.text:7C92E500 ; _DWORD __stdcall KiIntSystemCall() .text:7C92E500 public _KiIntSystemCall@0 .text:7C92E500 _KiIntSystemCall@0 proc near ; DATA XREF: .text:off_7C923428↑o .text:7C92E500 .text:7C92E500 arg_4= byte ptr 8 .text:7C92E500 .text:7C92E500 8D 54 24 08 lea edx, [esp+arg_4] ;32位用栈传参嘛,所以这里是系统调用参数地址 .text:7C92E504 CD 2E int 2Eh ; DOS 2+ internal - EXECUTE COMMAND .text:7C92E504 ; DS:SI -> counted CR-terminated command string .text:7C92E504 .text:7C92E506 C3 retn
|
int 0x2e和sysenter区别联系
相同点
从3环到0环都需要改变cs ss esp eip
不同点
int 2e
通过idtr寄存器,找到中断门描述符得到CS,EIP,再通过TR寄存器找到TSS取出ss和esp
到0环执行内核函数KiSystemService
sysenter
通过MSR寄存器找到CS,EIP,SS,ESP(其中SS是用CS+8算出来的,这样只需要把他们的选择子相邻,减少寄存器数量),加载到寄存器是cpu自动完成的,和操作系统没关系,操作系统负责用rdmsr和wrmst来给msr寄存器赋正确值(手册第三卷5.87)
到0环执行内核函数KiFastCallEntry
KiSystemService
内核函数KiSystemService和KiFastCallEntry都在c:windows/system32下,非pae分页用的是ntoskrnl.exe,pae分页是ntkrnlpa.exe
保存现场
涉及到一些结构体
dt _Ktrap_frame 保存寄存器的结构体 dt _KPCR:KPCR中存储了CPU本身要用的一些重要数据:GDT、IDT以及线程相关的一些信息,一个cpu核一个 dd KeNumberProcessors 查看cpu核数 dd KiProcessorBlock 查看每个cpu的kpcr结构体中偏移0x120处的_KPRCB结构体的位置 dt _ETHREAD 线程相关结构体
|
.text:00466489 BB 30 00 00 00 mov ebx, 30h ; '0' .text:0046648E 66 8E E3 mov fs, bx
|
0x30按照段描述符拆分一下 00110 0 00,0环全局描述符表的第6个
kd> dq @gdtr+8*6 l1 8003f030 ffc093df`f0000001
|
再拆分一下段描述符ffc093df f0000001
得到基址ffdf f000
ffdf f000指向的就是kpcr结构体,
fs在三环时指向TEB结构体
.text:00466481 arg_0= dword ptr 4 .text:00466481 .text:00466481 push 0 ;push errcode,在此之前中断时cpu已经自动帮我们把ss esp eflags cs ip压栈 .text:00466483 push ebp .text:00466484 push ebx .text:00466485 push esi .text:00466486 push edi .text:00466487 push fs ; 保存到_ktrap_frame对应位置 .text:00466489 mov ebx, 30h ; '0' .text:0046648E mov fs, bx .text:00466491 assume fs:nothing .text:00466491 push dword ptr ds:0FFDFF000h ; 保存kpcr结构体第一项_EXCEPTION_REGISTRATION_RECORD异常链表到ktrap_frame结构体 .text:00466497 mov dword ptr ds:0FFDFF000h, 0FFFFFFFFh ; 把第一项_EXCEPTION_REGISTRATION_RECORD异常链表置零 .text:004664A1 mov esi, ds:0FFDFF124h ; KPCR结构体里KPCRB+4的位置是当前CPU所执行线程的_ETHREAD结构体 .text:004664A7 push dword ptr [esi+140h] ; 保存_ETHREAD第一个成员_KTHREAD+0x140处为PreviousMode先前模式到fram结构体,1代表先前模式为3环,0代表0环 .text:004664AD sub esp, 48h ; esp指向_kTrap_Frame的起始位置 .text:004664B0 mov ebx, [esp+68h+arg_0] ; _Ktrap_frame-->SegCs .text:004664B4 and ebx, 1 .text:004664B7 mov [esi+140h], bl ; _KTHREAD的PreviousMode赋值为cs寄存器特权级,1代表3环来的,0代表0环来的 .text:004664BD mov ebp, esp ; ebp=_kTrap_Frame首地址 .text:004664BF mov ebx, [esi+134h] ; ebx=KTHREAD-->TrapFrame .text:004664C5 mov [ebp+3Ch], ebx .text:004664C8 mov [esi+134h], ebp ; 老的TrapFrame的值新TrapFram-->Edx,然后将新的TrapFrame放到EHTREAD+134h的trapfram处 .text:004664CE cld .text:004664CF mov ebx, [ebp+60h] .text:004664D2 mov edi, [ebp+68h] ; 原来三环的ebp放到ebx里,三环eip放到edi中 .text:004664D5 mov [ebp+0Ch], edx ; KiIntSystemCall中edx保存参数地址,存放到trapfram的DbgArgPointer .text:004664D8 mov dword ptr [ebp+8], 0BADB0D00h .text:004664DF mov [ebp+0], ebx ; 原来三环ebp放到trapfram的DbgEbp .text:004664E2 mov [ebp+4], edi ; 原来三环eip放到trapfram的DbgEip .text:004664E5 test byte ptr [esi+2Ch], 0FFh ; _ETHREAD._KTHREAD.DebugActive .text:004664E9 jnz Dr_kss_a ; 处于调试状态,值不等于0xff,则跳转,跳转位置主要是保存trapfram中的调试寄存器 .text:004664E9 .text:004664EF .text:004664EF loc_4664EF: ; CODE XREF: Dr_kss_a+10↑j .text:004664EF ; Dr_kss_a+7C↑j .text:004664EF sti .text:004664F0 jmp loc_4665CD ; jmp到_KiFastCallEntry函数里某个的位置,此时和KifastCallentry一样流程
|
调用内核函数
_KPCR.PrcbData(_KPRCB).CurrentThread(_KTHREAD)+0x0e0 处为ServiceTable,有下面两个table
ServiceTable指向的是函数地址数组,每个成员四个字节
Count表示调用次数
ServiceLimit表示这张表有几个函数;
ArgumentTable指向对应函数有几个字节的参数,每个成员一个字节
通过eax索引号找到相关函数
分析一下
.text:004665CD loc_4665CD: ; CODE XREF: _KiBBTUnexpectedRange+18↑j .text:004665CD ; _KiSystemService+6F↑j .text:004665CD mov edi, eax ; edi=传入系统服务号 .text:004665CF shr edi, 8 ; 右移8位 .text:004665D2 and edi, 30h ; 基本系统调用号都是小于0x1000 .text:004665D2 ; 窗口界面操作调用号大于0x1000定义在win32k.sys .text:004665D2 ; 如果12位为1,与完后edi=0x10,否则就为0 .text:004665D5 mov ecx, edi .text:004665D7 add edi, [esi+0E0h] ; KPCR.PrcbData(_KPRCB).CurrentThread(_KTHREAD)+0x0e0=ServiceTable .text:004665D7 ; 如果edi是0x10(ServiceTable大小0x10)正好寻址到win32k.sys的ServiceTabl .text:004665DD mov ebx, eax .text:004665DF and eax, 0FFFh ; 保留调用号低12位,作为函数地址表的下标 .text:004665E4 cmp eax, [edi+8] ; ServiceTable.count比较 .text:004665E7 jnb _KiBBTUnexpectedRange ; 大于等于跳转 .text:004665E7 .text:004665ED cmp ecx, 10h ; 判断是哪张服务表 .text:004665F0 jnz short loc_46660C ; 上图绿色那张表跳转 .text:004665F0 .text:004665F2 mov ecx, ds:0FFDFF018h .text:004665F8 xor ebx, ebx .text:004665F8 .text:004665FA .text:004665FA loc_4665FA: ; DATA XREF: _KiTrap0E+113↓o .text:004665FA or ebx, [ecx+0F70h] .text:00466600 jz short loc_46660C ; _KPRCB -> +0x518 KeSystemCalls++; .text:00466600 .text:00466602 push edx .text:00466603 push eax .text:00466604 call ds:_KeGdiFlushUserBatch .text:00466604 .text:0046660A pop eax .text:0046660B pop edx .text:0046660B .text:0046660C .text:0046660C loc_46660C: ; CODE XREF: _KiFastCallEntry+B0↑j .text:0046660C ; _KiFastCallEntry+C0↑j .text:0046660C inc dword ptr ds:0FFDFF638h ; _KPRCB -> +0x518 KeSystemCalls++; .text:00466612 mov esi, edx ; edx=三环栈上参数地址 .text:00466614 mov ebx, [edi+0Ch] ; ebx=ServiceTabl.ParamTableBase .text:00466617 xor ecx, ecx .text:00466619 mov cl, [eax+ebx] ; cl=参数总字节数 .text:0046661C mov edi, [edi] ; edi=ServiceTable.函数数组 .text:0046661E mov ebx, [edi+eax*4] ; ebx=目标函数 .text:00466621 sub esp, ecx ; 提升堆栈 .text:00466623 shr ecx, 2 ; ecx/4=一次拷贝四个字节的拷贝次数 .text:00466626 mov edi, esp ; 拷贝目标地址 .text:00466628 cmp esi, ds:_MmUserProbeAddress ; 和用户能访问的最大地址范围比较,判断是否越界 .text:0046662E jnb loc_4667DC .text:0046662E .text:00466634 .text:00466634 loc_466634: ; CODE XREF: _KiFastCallEntry+2A0↓j .text:00466634 ; DATA XREF: _KiTrap0E+109↓o .text:00466634 rep movsd ; copy参数 .text:00466636 call ebx ; 调用 .text:00466636 .text:00466638 .text:00466638 loc_466638: ; CODE XREF: _KiFastCallEntry+2AB↓j .text:00466638 ; DATA XREF: _KiTrap0E+129↓o .text:00466638 ; _KiTrap0E+149↓o .text:00466638 mov esp, ebp .text:00466638 .text:0046663A .text:0046663A loc_46663A: ; CODE XREF: _KiBBTUnexpectedRange+38↑j .text:0046663A ; _KiBBTUnexpectedRange+43↑j .text:0046663A mov ecx, ds:0FFDFF124h .text:00466640 mov edx, [ebp+3Ch] .text:00466643 mov [ecx+134h], edx .text:00466643 .text:00466643 _KiFastCallEntry endp
|
系统服务描述符表
System Services Descriptor Table(SSDT)
typedef struct _KSERVICE_TABLE_DESCRIPTOR { KSYSTEM_SERVICE_TABLE ntoskrnl; KSYSTEM_SERVICE_TABLE win32k; KSYSTEM_SERVICE_TABLE notUsed1; KSYSTEM_SERVICE_TABLE notUsed2; } KSERVICE_TABLE_DESCRIPTOR, * PKSERVICE_TABLE_DESCRIPTOR;
|
KeServiceDescriptorTable是ntkrnlpa导出符号
KeServiceDescriptorTable处只有系统服务ntkrnlpa.exe对应的SystemServiceTable
kd> dd KeServiceDescriptorTable 80553fa0 80502b8c 00000000 0000011c 80503000 80553fb0 00000000 00000000 00000000 00000000 80553fc0 00000000 00000000 00000000 00000000 80553fd0 00000000 00000000 00000000 00000000
|
win32k需要通过SSDT Shadow来访问到,这是一个未导出的结构,就可以获取到两表了
kd> dd KeServiceDescriptorTableShadow 80553f60 80502b8c 00000000 0000011c 80503000 80553f70 bf999b80 00000000 0000029b bf99a890 80553f80 00000000 00000000 00000000 00000000 80553f90 00000000 00000000 00000000 0000000
|
Reference
https://www.braveenough.cn/2022/01/30/%E7%B3%BB%E7%BB%9F%E8%B0%83%E7%94%A8/