Windows API調用詳解
綜述:
Windows內核中的執行體層暴露了大量執行體中的對象給Windows用戶層的API來操作,那么用戶層的API是怎么調用這些功能的呢,比如說創建一個文件,文件是一個內核對象必須得有內核層來處理,所以肯定有一個從用戶層到內核層然后內核層解決再返回到用戶層的一個流程。
API調用流程:
這里以CreateFile舉例:(大致流程如下)

采用OllyDbg跟蹤觀察:
#include#includeusing namespace std;int main(){ cout << "Begin" << endl; auto hFile =CreateFileW( L"temp.txt", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ); return NULL;}

可以看到在調用CreateFile時,是直接采用的Kernel32中的CreateFileW函數:
然后跟進Kernel32.CreateFileW函數中:

會跳轉到KernelBase中的CreateFileW函數中,繼續步入查看:
一直往下翻才能看到NtCreateFile函數,前面的我猜是給創建新文件所需進行初始化的一些代碼。

正所謂前人栽樹后人乘涼,書上的大牛寫的是調用ntdll中的NtCreateFile我們就不要懷疑了,然后步入Ntdll.NtCreateFile查看:

這里的call esi就是調用NtCreateFile的地方,然后繼續步入查看:

就成了這樣子,這個我也不知道什么意思,就繼續call dword ptr ds:[edx]查看:

最后的最后是由三個匯編指令為結尾:
mov edx,espsysenterretn
retn是返回指令,表示已經執行完成。
sysenter在od中無法看到,不管是步入還是步過只會直接執行完跳過。
所以目前而言的API調用過程是這樣的:

現在不理解的就是NtCreateFile函數內部的代碼邏輯:
mov eax,xxmov edx,xxxcall dword ptr ds:[edx]retn xxx... mov edx,espsysenter
這幾行匯編代碼是什么意思。
詳解NtCreateFile:
首先是NtCreateFile中的第一層匯編代碼:
mov eax,xxmov edx,xxcall dword ptr ds:[edx]retn xxx
這附近的匯編代碼都是這樣的樣式:

mov eax,xx
這里的eax叫做系統服務號,它代表進入0環后,調用那一個API,就類似于數組和下標的關系,根據下標一對一對應內核中的某一個API,然后內核調用內核的對應的API。
也就是說由eax的值來決定內核API的調用。
mov edx,xxx

所有通過ntdll調用的函數,給edx所傳遞的值都是一樣的,這里都是0x7FFE0300。
該值是_KUSER_SHARED_DATA結構體的一個成員,該結構體所在的內存空間是一個提供給User層和Kernel層共享的一個內存空間,該空間主要用來在User和Kernel之間快速傳遞信息。
該結構體所在的內存空間是固定的:User層下:0x7FFE0000,Kernel層下:0xFFDF0000。
//0x5f0 bytes (sizeof)struct _KUSER_SHARED_DATA{ ULONG TickCountLowDeprecated; //0x0 ULONG TickCountMultiplier; //0x4 volatile struct _KSYSTEM_TIME InterruptTime; //0x8 volatile struct _KSYSTEM_TIME SystemTime; //0x14 volatile struct _KSYSTEM_TIME TimeZoneBias; //0x20 USHORT ImageNumberLow; //0x2c USHORT ImageNumberHigh; //0x2e WCHAR NtSystemRoot[260]; //0x30 ULONG MaxStackTraceDepth; //0x238 ULONG CryptoExponent; //0x23c ULONG TimeZoneId; //0x240 ULONG LargePageMinimum; //0x244 ULONG Reserved2[7]; //0x248 enum _NT_PRODUCT_TYPE NtProductType; //0x264 UCHAR ProductTypeIsValid; //0x268 ULONG NtMajorVersion; //0x26c ULONG NtMinorVersion; //0x270 UCHAR ProcessorFeatures[64]; //0x274 ULONG Reserved1; //0x2b4 ULONG Reserved3; //0x2b8 volatile ULONG TimeSlip; //0x2bc enum _ALTERNATIVE_ARCHITECTURE_TYPE AlternativeArchitecture; //0x2c0 ULONG AltArchitecturePad[1]; //0x2c4 union _LARGE_INTEGER SystemExpirationDate; //0x2c8 ULONG SuiteMask; //0x2d0 UCHAR KdDebuggerEnabled; //0x2d4 UCHAR NXSupportPolicy; //0x2d5 volatile ULONG ActiveConsoleId; //0x2d8 volatile ULONG DismountCount; //0x2dc ULONG ComPlusPackage; //0x2e0 ULONG LastSystemRITEventTickCount; //0x2e4 ULONG NumberOfPhysicalPages; //0x2e8 UCHAR SafeBootMode; //0x2ec union { UCHAR TscQpcData; //0x2ed struct { UCHAR TscQpcEnabled:1; //0x2ed UCHAR TscQpcSpareFlag:1; //0x2ed UCHAR TscQpcShift:6; //0x2ed }; }; UCHAR TscQpcPad[2]; //0x2ee union { ULONG SharedDataFlags; //0x2f0 struct { ULONG DbgErrorPortPresent:1; //0x2f0 ULONG DbgElevationEnabled:1; //0x2f0 ULONG DbgVirtEnabled:1; //0x2f0 ULONG DbgInstallerDetectEnabled:1; //0x2f0 ULONG DbgSystemDllRelocated:1; //0x2f0 ULONG DbgDynProcessorEnabled:1; //0x2f0 ULONG DbgSEHValidationEnabled:1; //0x2f0 ULONG SpareBits:25; //0x2f0 }; }; ULONG DataFlagsPad[1]; //0x2f4 ULONGLONG TestRetInstruction; //0x2f8 ULONG SystemCall; //0x300 ULONG SystemCallReturn; //0x304 ULONGLONG SystemCallPad[3]; //0x308 union { volatile struct _KSYSTEM_TIME TickCount; //0x320 volatile ULONGLONG TickCountQuad; //0x320 ULONG ReservedTickCountOverlay[3]; //0x320 }; ULONG TickCountPad[1]; //0x32c ULONG Cookie; //0x330 ULONG CookiePad[1]; //0x334 LONGLONG ConsoleSessionForegroundProcessId; //0x338 ULONG Wow64SharedInformation[16]; //0x340 USHORT UserModeGlobalLogger[16]; //0x380 ULONG ImageFileExecutionOptions; //0x3a0 ULONG LangGenerationCount; //0x3a4 ULONGLONG Reserved5; //0x3a8 volatile ULONGLONG InterruptTimeBias; //0x3b0 volatile ULONGLONG TscQpcBias; //0x3b8 volatile ULONG ActiveProcessorCount; //0x3c0 volatile USHORT ActiveGroupCount; //0x3c4 USHORT Reserved4; //0x3c6 volatile ULONG AitSamplingValue; //0x3c8 volatile ULONG AppCompatFlag; //0x3cc ULONGLONG SystemDllNativeRelocation; //0x3d0 ULONG SystemDllWowRelocation; //0x3d8 ULONG XStatePad[1]; //0x3dc struct _XSTATE_CONFIGURATION XState; //0x3e0};
edx是該結構體中的SystemCall成員,該結構體成員指定了從User層到Kernel層的調用方式,這里我們可以通過WinDbg來查看一下:
kd> dt nt!_KUSER_SHARED_DATA 0xFFDF0000 +0x000 TickCountLowDeprecated : 0 +0x004 TickCountMultiplier : 0xf99a027 +0x008 InterruptTime : _KSYSTEM_TIME +0x014 SystemTime : _KSYSTEM_TIME +0x020 TimeZoneBias : _KSYSTEM_TIME +0x02c ImageNumberLow : 0x14c +0x02e ImageNumberHigh : 0x14c +0x030 NtSystemRoot : [260] "C:\Windows" +0x238 MaxStackTraceDepth : 0 +0x23c CryptoExponent : 0 +0x240 TimeZoneId : 0 +0x244 LargePageMinimum : 0x200000 +0x248 Reserved2 : [7] 0 +0x264 NtProductType : 1 ( NtProductWinNt ) +0x268 ProductTypeIsValid : 0x1 '' +0x26c NtMajorVersion : 6 +0x270 NtMinorVersion : 1 +0x274 ProcessorFeatures : [64] "" +0x2b4 Reserved1 : 0x7ffeffff +0x2b8 Reserved3 : 0x80000000 +0x2bc TimeSlip : 0 +0x2c0 AlternativeArchitecture : 0 ( StandardDesign ) +0x2c4 AltArchitecturePad : [1] 0 +0x2c8 SystemExpirationDate : _LARGE_INTEGER 0x0 +0x2d0 SuiteMask : 0x110 +0x2d4 KdDebuggerEnabled : 0x3 '' +0x2d5 NXSupportPolicy : 0x2 '' +0x2d8 ActiveConsoleId : 1 +0x2dc DismountCount : 0 +0x2e0 ComPlusPackage : 0xffffffff +0x2e4 LastSystemRITEventTickCount : 0 +0x2e8 NumberOfPhysicalPages : 0x3ff7e +0x2ec SafeBootMode : 0 '' +0x2ed TscQpcData : 0 '' +0x2ed TscQpcEnabled : 0y0 +0x2ed TscQpcSpareFlag : 0y0 +0x2ed TscQpcShift : 0y000000 (0) +0x2ee TscQpcPad : [2] "" +0x2f0 SharedDataFlags : 0xe +0x2f0 DbgErrorPortPresent : 0y0 +0x2f0 DbgElevationEnabled : 0y1 +0x2f0 DbgVirtEnabled : 0y1 +0x2f0 DbgInstallerDetectEnabled : 0y1 +0x2f0 DbgSystemDllRelocated : 0y0 +0x2f0 DbgDynProcessorEnabled : 0y0 +0x2f0 DbgSEHValidationEnabled : 0y0 +0x2f0 SpareBits : 0y0000000000000000000000000 (0) +0x2f4 DataFlagsPad : [1] 0 +0x2f8 TestRetInstruction : 0xc3 +0x300 SystemCall : 0x76f46c00 +0x304 SystemCallReturn : 0x76f46c04 +0x308 SystemCallPad : [3] 0 +0x320 TickCount : _KSYSTEM_TIME +0x320 TickCountQuad : 0x9806 +0x320 ReservedTickCountOverlay : [3] 0x9806 +0x32c TickCountPad : [1] 0 +0x330 Cookie : 0xccc8c182 +0x334 CookiePad : [1] 0 +0x338 ConsoleSessionForegroundProcessId : 0n1504 +0x340 DEPRECATED_Wow64SharedInformation : [16] 0 +0x380 UserModeGlobalLogger : [16] 0 +0x3a0 ImageFileExecutionOptions : 0 +0x3a4 LangGenerationCount : 1 +0x3a8 Reserved5 : 0 +0x3b0 InterruptTimeBias : 0 +0x3b8 TscQpcBias : 0 +0x3c0 ActiveProcessorCount : 1 +0x3c4 ActiveGroupCount : 1 +0x3c6 Reserved4 : 0 +0x3c8 AitSamplingValue : 0 +0x3cc AppCompatFlag : 1 +0x3d0 DEPRECATED_SystemDllNativeRelocation : 0 +0x3d8 DEPRECATED_SystemDllWowRelocation : 0 +0x3dc XStatePad : [1] 0 +0x3e0 XState : _XSTATE_CONFIGURATION +0x300 SystemCall : 0x76f46c00,該字段存放著函數的調用地址,結合前面的內容可以看到確實是該地址:

//然后反匯編查看一下該函數的內容:kd> u 0x76f46c00ntdll!KiFastSystemCall:76f46c00 ?? ??? ^ Memory access error in 'u 0x76f46c00' 這個函數的名字叫做KiFastSystemCall 那么這一段匯編代碼:mov eax,xxmov edx,xxcall dword ptr ds:[edx]retn xxx 就可以抽象成一個函數:KiFastSystemCall()//當然里面肯定有參數
那么圖就可以變成這樣子:

然后重點就來到了剩下的兩個。
mov edx,esp:
這個其實就是函數傳參,參數在棧里面,然后把棧的首地址交給edx。
sysenter
sysenter才是整個調用的關鍵。
整個調用方式最關鍵的就是通過sysenter從User層到達Kernel層,可以說前面的都是在給這一步做鋪墊。
sysenter叫做快速系統調用,叫快速是因為之前的系統調用不快,在Pentium II(奔騰2代CPU)之后才有的sysenter,在其之前是采用的 KiIntSystemCall函數來處理的。
在IDA下查看:該函數:
; Exported entry 109. KiIntSystemCall ; _DWORD __stdcall KiIntSystemCall()public _KiIntSystemCall@0_KiIntSystemCall@0 proc near arg_4= byte ptr 8 lea edx, [esp+arg_4]int 2Eh ; DOS 2+ internal - EXECUTE COMMAND ; DS:SI -> counted CR-terminated command stringretn_KiIntSystemCall@0 endp
這里的int 2Eh,采用的就是一種CPU機制,叫做中斷門:
Windows內核中的CPU架構-6-中斷門(32-Bit Interrupt Gate) - Sna1lGo - 博客園 (cnblogs.com)(https://www.cnblogs.com/Sna1lGo/p/15515746.html)
這樣調用會用到內存,比較麻煩,所以就引入了快速系統調用:
sysenter/sysexit 兩個函數和三個MSR寄存器。
MSR寄存器比較類似內存,直接根據序號來命名,沒有像通用寄存器EAX,EBX一樣,單獨命名。
和sysenter連用的是MSR174和,MSR175,MSR176三個寄存器。

sysenter內部邏輯為:
//1:設置寄存器CS = IA32_SYSENTER_CSSS = IA32_SYSENTER_CS+8eip = IA32_SYSENTER_EIPesp = IA32_SYSENTER_ESP //2:切換特權級切換到0環特權級,(其實設置了寄存器就是切換了) //3:切換CPU模式清楚eflags寄存器中的虛擬8086模式(VM標志) //4:執行執行系統例程調用