关于Windows系统调用

以Kernel32.dll 中的 ReadFile 方法为例:

首先找到 kernel32!ReadFile 函数,从如下截取的汇编代码中可以看到,改函数会调用kernel32!_imp__NtReadFile指向的函数

kernel32!ReadFile+0xd2:
7c801962 bb03010000      mov     ebx,103h
7c801967 891e            mov     dword ptr [esi],ebx
7c801969 8b4608          mov     eax,dword ptr [esi+8]
7c80196c 8945d0          mov     dword ptr [ebp-30h],eax
7c80196f 8b460c          mov     eax,dword ptr [esi+0Ch]
7c801972 8945d4          mov     dword ptr [ebp-2Ch],eax
7c801975 8b4610          mov     eax,dword ptr [esi+10h]
7c801978 6a00            push    0
7c80197a 8d4dd0          lea     ecx,[ebp-30h]
7c80197d 51              push    ecx
7c80197e ff7510          push    dword ptr [ebp+10h]
7c801981 ff750c          push    dword ptr [ebp+0Ch]
7c801984 56              push    esi
7c801985 8bc8            mov     ecx,eax
7c801987 80e101          and     cl,1
7c80198a f6d9            neg     cl
7c80198c 1bc9            sbb     ecx,ecx
7c80198e f7d1            not     ecx
7c801990 23ce            and     ecx,esi
7c801992 51              push    ecx
7c801993 6a00            push    0
7c801995 50              push    eax
7c801996 57              push    edi
7c801997 ff159c11807c    call    dword ptr [kernel32!_imp__NtReadFile (7c80119c)]
7c80199d 85c0            test    eax,eax
7c80199f 7c04            jl      kernel32!ReadFile+0x140 (7c8019a5)

调用的系统调用为  kernel32!_imp__NtReadFile 所指向的函数,其实就是 ntdll!NtReadFile 方法。
ntdll!NtReadFile:
7c92d9ce b8b7000000      mov     eax,0B7h
7c92d9d3 ba0003fe7f      mov     edx,offset SharedUserData!SystemCallStub (7ffe0300)
7c92d9d8 ff12            call    dword ptr [edx]
7c92d9da c22400          ret     24h
7c92d9dd 90              nop
这里是调入内核的地方,eax为内核服务号,0xB7,进入内核的方法由 SharedUserData!SystemCallStub 指向。
跟进去,发现这个指针指向的地址为 0x7c92e510,看该地址的函数,反汇编一下。
0: kd> dd 7ffe0300
7ffe0300  7c92e510 7c92e514 00000000 00000000
7ffe0310  00000000 00000000 00000000 00000000
可以看一下该指针指向的地址为什么方法,如下所示:
ntdll!KiFastSystemCall:
7c92e510 8bd4            mov     edx,esp
7c92e512 0f34            sysenter
ntdll!KiFastSystemCallRet:
7c92e514 c3              ret
7c92e515 8da42400000000  lea     esp,[esp]
7c92e51c 8d642400        lea     esp,[esp]
ntdll!KiFastSystemCall 函数,将堆栈的栈顶指针放到了 edx中,并且调用了 sysenter 指令。

该指令使用了三个MSR寄存器,分别如下,其中EIP指明了 该指令要跳转到的内核的地址。
MSR 地址
IA32_SYSENTER_CS 174H
IA32_SYSENTER_ESP 175H
IA32_SYSENTER_EIP 176H

获得要跳转的地址
1: kd> rdmsr 176
msr[176] = 00000000`80542590
看这个地址所指向的函数:
1: kd> u 80542590
nt!KiFastCallEntry:
80542590 b923000000      mov     ecx,23h
80542595 6a30            push    30h
80542597 0fa1            pop     fs
80542599 8ed9            mov     ds,cx
8054259b 8ec1            mov     es,cx
8054259d 648b0d40000000  mov     ecx,dword ptr fs:[40h]
805425a4 8b6104          mov     esp,dword ptr [ecx+4]
805425a7 6a23            push    23h

/////////////////////////////////////////////////////////////////////////////////////////////////////////
另外一种进入内核的方式是通过中断,即在NtReadFile中会调用如下的函数,通过 int 2Eh 中断,进入2E号中断函数中
ntdll!KiIntSystemCall:
7c92e520 8d542408        lea     edx,[esp+8]
7c92e524 cd2e            int     2Eh
7c92e526 c3              ret
7c92e527 90              nop
查找IDT,查看2Eh中断的所对应的处理函数。
一种方法是手动查找:
1: kd> !pcr
KPCR for Processor 1 at ba338000:
    Major 1 Minor 1
	NtTib.ExceptionList: aff426d8
	    NtTib.StackBase: aff42df0
	   NtTib.StackLimit: aff3e000
	 NtTib.SubSystemTib: 00000000
	      NtTib.Version: 00000000
	  NtTib.UserPointer: 00000000
	      NtTib.SelfTib: 7ffde000


	            SelfPcr: ba338000
	               Prcb: ba338120
	               Irql: 00000000
	                IRR: 00000000
	                IDR: ffffffff
	      InterruptMode: 00000000
	                IDT: ba33c590
	                GDT: ba33c190
	                TSS: ba338d70


	      CurrentThread: 898478d8
	         NextThread: 00000000
	         IdleThread: ba33ae20


	          DpcQueue: 
从这里可以看到 IDT表的基地址,根据IDT的格式,
1: kd> dq /c 1 0xba33c590 + 0x2e*8
ba33c700  8054ee00`000824c1
ba33c708  80548e00`00085868
ba33c710  80548e00`00081b80
找到对应的IDT表项,可以得到偏移值为 0x805424c1 ,使用选择子 0x0008 从GDT中找到对应项。
1: kd> dq /c 1 0xba33c190 + 1*8
ba33c198  00cf9b00`0000ffff
ba33c1a0  00cf9300`0000ffff
由此可以知道,基地址为 0x00000000,得到对应的函数如下,为 nt!KiSystemService
1: kd> u 0x805424c1
nt!KiSystemService:
805424c1 6a00            push    0
805424c3 55              push    ebp
805424c4 53              push    ebx
805424c5 56              push    esi
805424c6 57              push    edi
805424c7 0fa0            push    fs
805424c9 bb30000000      mov     ebx,30h
805424ce 668ee3          mov     fs,bx

有一个命令,可以直接获取IDT的表项:
1: kd> !idt 2e

Dumping IDT:
2e:	805424c1 nt!KiSystemService


////////////////////////////
××××××××××××××××××××××××××××
查看调用栈的变化情况

查看传参以及返回的方法,以及返回值如何获取
××××××××××××××××××××××××××××
////////////////////////////

你可能感兴趣的:(关于Windows系统调用)