在系统里User32.dll,kernel32.dll,shell32.dll,gdi32.dll,rpcrt4.dll,comctl32.dll,advapi32.dll,version.dll等dll代表了Win32 API的基本提供者。Win32 API中的所有调用最终都转向了ntdll.dll,再由它转发至ntoskrnl.exe。ntdll.dll是本机 API用户模式的终端。真正的接口在ntoskrnl.exe里完成。事实上,内核模式的驱动大部分时间调用这个模块,如果它们请求系统服务。Ntdll.dll的主要作用就是让内核函数的特定子集可以被用户模式下运行的程序调用。Ntdll.dll通过软件中断int 2Eh进入ntoskrnl.exe,就是通过中断门切换cpu特权级。比如kernel32.dll导出的函数DeviceIoControl()实际上调用ntdll.dll中导出的NtDeviceIoControlFile(),反汇编一下这个函数可以看到,EAX载入magic数0x38,实际上是系统调用号,然后EDX指向堆栈。目标地址是当前堆栈指针ESP+4,所以EDX指向返回地址后面一个,也就是指向在进入NtDeviceIoControlFile()之前存入堆栈的东西。事实上就是函数的参数。下一个指令是int 2Eh,转到中断描述符表IDT位置0x2E处的中断处理程序。
下面就来分析怎么样加载NTDLL.DLL,具体实现代码始下:
#001 NTSTATUS
#002 NTAPI
#003 PsLocateSystemDll(VOID)
#004 {
#005 OBJECT_ATTRIBUTES ObjectAttributes;
#006 IO_STATUS_BLOCK IoStatusBlock;
#007 HANDLE FileHandle,SectionHandle;
#008 NTSTATUS Status;
#009 ULONG_PTR HardErrorParameters;
#010 ULONG HardErrorResponse;
#011
初始化NTDLL.DLL的对象,其中PsNtDllPathName是它的名称//SystemRoot//system32//ntdll.dll。在前面已经把系统路径设置好了。
#012 /* Locate and open NTDLL to determine ImageBase and LdrStartup */
#013 InitializeObjectAttributes(&ObjectAttributes,
#014 &PsNtDllPathName,
#015 0,
#016 NULL,
#017 NULL);
打开文件NTDLL.DLL。
#018 Status = ZwOpenFile(&FileHandle,
#019 FILE_READ_ACCESS,
#020 &ObjectAttributes,
#021 &IoStatusBlock,
#022 FILE_SHARE_READ,
#023 0);
#024 if (!NT_SUCCESS(Status))
#025 {
#026 /* Failed,bugcheck */
#027 KeBugCheckEx(PROCESS1_INITIALIZATION_Failed,Status,2,0);
#028 }
#029
检查NTDLL.DLL文件是否合法。
#030 /* Check if the image is valid */
#031 Status = MmCheckSystemImage(FileHandle,TRUE);
#032 if (Status == STATUS_IMAGE_CHECKSUM_MISMATCH)
#033 {
#034 /* Raise a hard error */
#035 HardErrorParameters = (ULONG_PTR)&PsNtDllPathName;
#036 NtRaiseHardError(Status,
#037 1,
#038 1,
#039 &HardErrorParameters,
#040 OptionOk,
#041 &HardErrorResponse);
#042 return Status;
#043 }
#044
#045 /* Create a section for NTDLL */
#046 Status = ZwCreateSection(&SectionHandle,
#047 SECTION_ALL_ACCESS,
#048 NULL,
#049 NULL,
#050 PAGE_EXECUTE,
#051 SEC_IMAGE,
#052 FileHandle);
#053 ZwClose(FileHandle);
#054 if (!NT_SUCCESS(Status))
#055 {
#056 /* Failed,bugcheck */
#057 KeBugCheckEx(PROCESS1_INITIALIZATION_Failed,3,0);
#058 }
#059
添加引用段对象。
#060 /* Reference the Section */
#061 Status = ObReferenceObjectByHandle(SectionHandle,
#062 SECTION_ALL_ACCESS,
#063 MmSectionObjectType,
#064 KernelMode,
#065 (PVOID*)&PspSystemDllSection,
#066 NULL);
#067 ZwClose(SectionHandle);
#068 if (!NT_SUCCESS(Status))
#069 {
#070 /* Failed,bugcheck */
#071 KeBugCheckEx(PROCESS1_INITIALIZATION_Failed,4,0);
#072 }
#073
把NTDLL.DLL映射到内核空间。PspSystemDllBase是动态连接库的基地址。
#074 /* Map it */
#075 Status = PspMapSystemDll(PsGetCurrentProcess(),&PspSystemDllBase,FALSE);
#076 if (!NT_SUCCESS(Status))
#077 {
#078 /* Failed,bugcheck */
#079 KeBugCheckEx(PROCESS1_INITIALIZATION_Failed,5,0);
#080 }
#081
#082 /* Return status */
#083 return Status;
#084}