windows内核情景分析---进程线程1
本篇主要講述進程的啟動過程、線程的調(diào)度與切換、進程掛靠
一、進程的啟動過程:
BOOL?CreateProcess
(
??LPCTSTR?lpApplicationName,?????????????????//
??LPTSTR?lpCommandLine,??????????????????????//?command?line?string
??LPSECURITY_ATTRIBUTES?lpProcessAttributes,?//?SD
??LPSECURITY_ATTRIBUTES?lpThreadAttributes,??//?SD
??BOOL?bInheritHandles,?????????????????????//?
??DWORD?dwCreationFlags,????????????????????//?creation?flags
??LPVOID?lpEnvironment,?????????????????????//?new?environment?block
??LPCTSTR?lpCurrentDirectory,???????????????//?current?directory?name
??LPSTARTUPINFO?lpStartupInfo,???????????????//?startup?information
??LPPROCESS_INFORMATION?lpProcessInformation?//?process?information
);
這個Win32API在內(nèi)部最終調(diào)用如下:
CreateProcess(…)
{
???…
???NtCreateProcess(…);//間接調(diào)用這個系統(tǒng)服務,先創(chuàng)建進程
???NtCreateThread(…);//間接調(diào)用這個系統(tǒng)服務,再創(chuàng)建該進程的第一個線程(也即主線程)
???…
}
進程的4GB地址空間分兩部分,內(nèi)核空間+用戶空間
看下面幾個定義:
#define?MmSystemRangeStart??0x80000000?//系統(tǒng)空間的起點
#define?MM_USER_PROB_ADDRESS??MmSystemRangeStart-64kb??//除去高端的64kb隔離區(qū)
#define?MM_HIGHEST_USER_ADDRESS???MmUserProbAddress-1?//實際的用戶空間中最高可訪問地址
#define?MM_LOWEST_USER_ADDRESS??64kb??//實際的用戶空間中最低可訪問地址
#define?KI_USER_SHARED_DATA??0xffdf0000???//內(nèi)核空間與用戶空間共享的一塊區(qū)域
由此可見,用戶地址空間的范圍實際上是從??64kb---->0x80000000-64kb?這塊區(qū)域。
(訪問NULL指針報異常的原因就是NULL(0)落在了最前面的64kb保留區(qū)中)
內(nèi)核中提供了一個全局結構變量,該結構的類型是KUSER_SHARED_DATA。內(nèi)核中的那個結構體變量所在的虛擬頁面起始地址為:0xffdf0000,大小為一個頁面大小。這個內(nèi)核頁面對應的物理內(nèi)存頁面也映射到了每個進程的用戶地址空間中,而且是固定映在同一處:0x7ffe0000。這樣,用戶空間的程序直接訪問用戶空間中的這個虛擬地址,就相當于直接訪問了內(nèi)核空間中的那個公共頁面。所以,那個內(nèi)核頁面稱之為內(nèi)核空間提供給各個進程的一塊共享之地。(事實上,這個公共頁面非常有用,可以在這個頁面中放置代碼,應用程序直接在r3層運行這些代碼,如在內(nèi)核中進行IAT?hook)
如上:【用戶空間的范圍就是低2GB的空間除去前后64kb后的那塊區(qū)域】
圈定了用戶空間的地皮后,現(xiàn)在就到了劃分用戶空間的時候了。
用戶空間的布局:(以區(qū)段(Area)為單位進行劃分)
NTSTATUS
MmInitializeProcessAddressSpace(IN?PEPROCESS?Process,IN?PVOID?Section,IN?OUT?PULONG?Flags)
{
????NTSTATUS?Status?=?STATUS_SUCCESS;
????SIZE_T?ViewSize?=?0;
????PVOID?ImageBase?=?0;
????PROS_SECTION_OBJECT?SectionObject?=?Section;
????USHORT?Length?=?0;
????…
????KeAttachProcess(&Process->Pcb);//必須將當前線程掛靠到子進程的地址空間
Process->AddressSpaceInitialized?=?2;
????if?(SectionObject)
????{
????????FileName?=?SectionObject->FileObject->FileName;
????????Source?=?(PWCHAR)((PCHAR)FileName.Buffer?+?FileName.Length);
????????if?(FileName.Buffer)
????????{
????????????while?(Source?>?FileName.Buffer)
????????????{
????????????????if?(*--Source?==L”\\”)
????????????????{
????????????????????Source++;
????????????????????break;
????????????????}
????????????????else
????????????????????Length++;
????????????}
????????}
????????Destination?=?Process->ImageFileName;//任務管理器顯示的進程名就是這個(大小寫相同)
????????Length?=?min(Length,?sizeof(Process->ImageFileName)?-?1);
????????while?(Length--)?*Destination++?=?(UCHAR)*Source++;
????????*Destination?=?’\0’;
????????
????????//將進程的exe文件映射到地址空間中
????????Status?=?MmMapViewOfSection(Section,Process,&ImageBase,0,0,NULL,&ViewSize,0,
????????????????????????????????????MEM_COMMIT,…);
????????Process->SectionBaseAddress?=?ImageBase;//記錄實際映射到的地址(一般為0x00400000)
????}????
????KeDetachProcess();//撤銷掛靠
????return?Status;
}
//上面的函數(shù)將進程的exe文件映射到用戶地址空間中(注意exe文件內(nèi)部是分開按節(jié)映射)
NTSTATUS??PspMapSystemDll(PEPROCESS?Process,PVOID?*DllBase,BOOLEAN?UseLargePages)
{
????LARGE_INTEGER?Offset?=?{{0,?0}};
SIZE_T?ViewSize?=?0;?PVOID?ImageBase?=?0;
//將NTDLL.dll文件映射到地址空間(每個NTDLL.dll事實上都映射到所有進程地址空間的同一處)
????Status?=?MmMapViewOfSection(PspSystemDllSection,Process,&ImageBase,0,0,&Offset,&ViewSize,
????????????????????????????????ViewShare,0,…);
????if?(DllBase)?*DllBase?=?ImageBase;
????return?Status;
}
上面這個函數(shù)將ntdll.dll映射到地址空間
下面這個函數(shù)創(chuàng)建該進程的PEB(Process Environment Block)
NTSTATUS??MmCreatePeb(PEPROCESS?Process,PINITIAL_PEB?InitialPeb,OUT?PPEB?*BasePeb)
{
????PPEB?Peb?=?NULL;
????SIZE_T?ViewSize?=?0;
????PVOID?TableBase?=?NULL;
????KAFFINITY?ProcessAffinityMask?=?0;
????SectionOffset.QuadPart?=?(ULONGLONG)0;
????*BasePeb?=?NULL;
????KeAttachProcess(&Process->Pcb);//因為PEB指針是子進程中的地址,所以要掛靠
????//創(chuàng)建一個PEB
????Status?=?MiCreatePebOrTeb(Process,?sizeof(PEB),?(PULONG_PTR)&Peb);
????RtlZeroMemory(Peb,?sizeof(PEB));
//根據(jù)傳入的InitialPeb參數(shù)初始化新建的peb
????Peb->InheritedAddressSpace?=?InitialPeb->InheritedAddressSpace;
????Peb->Mutant?=?InitialPeb->Mutant;
Peb->ImageUsesLargePages?=?InitialPeb->ImageUsesLargePages;
Peb->ImageBaseAddress?=?Process->SectionBaseAddress;//
????Peb->OSMajorVersion?=?NtMajorVersion;?Peb->OSMinorVersion?=?NtMinorVersion;
????Peb->OSBuildNumber?=?(USHORT)(NtBuildNumber?&?0x3FFF);
????Peb->OSPlatformId?=?2;?/*?VER_PLATFORM_WIN32_NT?*/
????Peb->OSCSDVersion?=?(USHORT)CmNtCSDVersion;
????????
????Peb->NumberOfProcessors?=?KeNumberProcessors;
?//經(jīng)典的兩個調(diào)試檢測標志
Peb->BeingDebugged?=?(BOOLEAN)(Process->DebugPort?!=?NULL???TRUE?:?FALSE);
????Peb->NtGlobalFlag?=?NtGlobalFlag;
?
????Peb->MaximumNumberOfHeaps?=?(PAGE_SIZE?-?sizeof(PEB))?/?sizeof(PVOID);
????Peb->ProcessHeaps?=?(PVOID*)(Peb?+?1);//PEB結構體后面是一個堆數(shù)組
????
????NtHeaders?=?RtlImageNtHeader(Peb->ImageBaseAddress);//獲取文件頭中的NT頭
????Characteristics?=?NtHeaders->FileHeader.Characteristics;
????if?(NtHeaders)
????{
????????_SEH2_TRY
????????{
????????????ImageConfigData?=?RtlImageDirectoryEntryToData(Peb->ImageBaseAddress,TRUE,
??????????????????????????????????????IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG,&ViewSize);
????????????Peb->ImageSubsystem?=?NtHeaders->OptionalHeader.Subsystem;
????????????Peb->ImageSubsystemMajorVersion?=?NtHeaders->OptionalHeader.MajorSubsystemVersion;
????????????Peb->ImageSubsystemMinorVersion?=?NtHeaders->OptionalHeader.MinorSubsystemVersion;
????????????if?(NtHeaders->OptionalHeader.Win32VersionValue)
????????????{
????????????????Peb->OSMajorVersion?=?NtHeaders->OptionalHeader.Win32VersionValue?&?0xFF;
????????????????Peb->OSMinorVersion?=?(NtHeaders->OptionalHeader.Win32VersionValue?>>?8)?&?0xFF;
????????????????Peb->OSBuildNumber?=?(NtHeaders->OptionalHeader.Win32VersionValue?>>?16)?&?0x3FFF;
????????????????Peb->OSPlatformId?=?(NtHeaders->OptionalHeader.Win32VersionValue?>>?30)?^?2;
????????????}
????????????
????????????if?(ImageConfigData?!=?NULL)
????????????{
????????????????ProbeForRead(ImageConfigData,sizeof(IMAGE_LOAD_CONFIG_DIRECTORY),
?????????????????????????????sizeof(ULONG));//讀取pe文件中的加載配置信息
????????????????if?(ImageConfigData->CSDVersion)
????????????????????Peb->OSCSDVersion?=?ImageConfigData->CSDVersion;
????????????????if?(ImageConfigData->ProcessAffinityMask)
????????????????????ProcessAffinityMask?=?ImageConfigData->ProcessAffinityMask;
????????????}
????????????if?(Characteristics?&?IMAGE_FILE_UP_SYSTEM_ONLY)
????????????????Peb->ImageProcessAffinityMask?=?0;
????????????else
????????????????Peb->ImageProcessAffinityMask?=?ProcessAffinityMask;
????????}
????????_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
????????{
????????????KeDetachProcess();
????????????_SEH2_YIELD(return?STATUS_INVALID_IMAGE_PROTECT);
????????}
????????_SEH2_END;
????}
????KeDetachProcess();
????*BasePeb?=?Peb;
????return?STATUS_SUCCESS;
}
如上,上面這個函數(shù)為進程創(chuàng)建一個PEB并根據(jù)exe文件頭中的某些信息初始化里面的某些字段
事實上,這個PEB結構體的地址固定安排在0x7FFDF000處,占據(jù)一個頁面大小。該頁中這個PEB結構體后面就是一個堆數(shù)組,存放該進程中創(chuàng)建的所有堆。
下面的函數(shù)為子進程分配一個參數(shù)塊(即創(chuàng)建參數(shù))和環(huán)境變量塊(即環(huán)境變量字符串)
NTSTATUS
BasepInitializeEnvironment(HANDLE?ProcessHandle,PPEB?Peb,//子進程的Peb
???????????????????????????LPWSTR?ApplicationPathName,LPWSTR?lpCurrentDirectory,
???????????????????????????LPWSTR?lpCommandLine,
???????????????????????????LPVOID?lpEnvironment,//傳給子進程的環(huán)境變量塊
???????????????????????????SIZE_T?EnvSize,//環(huán)境變量塊的大小
???????????????????????????LPSTARTUPINFOW?StartupInfo,DWORD?CreationFlags,
???????????????????????????BOOL?InheritHandles)
{
????PRTL_USER_PROCESS_PARAMETERS?RemoteParameters?=?NULL;
????PPEB?OurPeb?=?NtCurrentPeb();//當前進程(即父進程)的Peb
????LPVOID?Environment?=?lpEnvironment;
????RetVal?=?GetFullPathNameW(ApplicationPathName,?MAX_PATH,FullPath,&Remaining);
????RtlInitUnicodeString(&ImageName,?FullPath);
????RtlInitUnicodeString(&CommandLine,?lpCommandLine);
????RtlInitUnicodeString(&CurrentDirectory,?lpCurrentDirectory);
????if?(StartupInfo->lpTitle)
????????RtlInitUnicodeString(&Title,?StartupInfo->lpTitle);
????else
????????RtlInitUnicodeString(&Title,?L"");
????Status?=?RtlCreateProcessParameters(&ProcessParameters,&ImageName,
????????????????????????????????????????lpCurrentDirectory??&CurrentDirectory?:?NULL,
????????????????????????????????????????&CommandLine,Environment,&Title,..);
????if?(Environment)
????????Environment?=?ScanChar?=?ProcessParameters->Environment;
????else
????????Environment?=?ScanChar?=?OurPeb->ProcessParameters->Environment;
????if?(ScanChar)
????{
???EnviroSize?=CalcEnvSize(ScanChar);//計算環(huán)境變量塊的長度
????????Size?=?EnviroSize;
????????//為子進程分配一個環(huán)境變量塊(跨進程遠程分配內(nèi)存)
????????Status?=?ZwAllocateVirtualMemory(ProcessHandle,
?????????????????????????????????????????(PVOID*)&ProcessParameters->Environment,
?????????????????????????????????????????0,&Size,MEM_COMMIT,PAGE_READWRITE);
????????//將環(huán)境變量塊復制到子進程的空間中
????????ZwWriteVirtualMemory(ProcessHandle,ProcessParameters->Environment,
?????????????????????????????Environment,
?????????????????????????????EnviroSize,
?????????????????????????????NULL);
????}
????ProcessParameters->StartingX?=?StartupInfo->dwX;
????ProcessParameters->StartingY?=?StartupInfo->dwY;
????ProcessParameters->CountX?=?StartupInfo->dwXSize;
????ProcessParameters->CountY?=?StartupInfo->dwYSize;
????ProcessParameters->CountCharsX?=?StartupInfo->dwXCountChars;
????ProcessParameters->CountCharsY?=?StartupInfo->dwYCountChars;
????ProcessParameters->FillAttribute?=?StartupInfo->dwFillAttribute;
????ProcessParameters->WindowFlags?=?StartupInfo->dwFlags;
????ProcessParameters->ShowWindowFlags?=?StartupInfo->wShowWindow;
????if?(StartupInfo->dwFlags?&?STARTF_USESTDHANDLES)//讓子進程使用自定義的三個標準IO句柄
????{???//經(jīng)常用于匿名管道重定向
????????ProcessParameters->StandardInput?=?StartupInfo->hStdInput;
????????ProcessParameters->StandardOutput?=?StartupInfo->hStdOutput;
????????ProcessParameters->StandardError?=?StartupInfo->hStdError;
????}
????if?(CreationFlags?&?DETACHED_PROCESS)
????????ProcessParameters->ConsoleHandle?=?HANDLE_DETACHED_PROCESS;
????else?if?(CreationFlags?&?CREATE_NO_WINDOW)
????????ProcessParameters->ConsoleHandle?=?HANDLE_CREATE_NO_WINDOW;
????else?if?(CreationFlags?&?CREATE_NEW_CONSOLE)
????????ProcessParameters->ConsoleHandle?=?HANDLE_CREATE_NEW_CONSOLE;
????else
{
????//讓子進程繼承父進程的控制臺句柄
????????ProcessParameters->ConsoleHandle?=?OurPeb->ProcessParameters->ConsoleHandle;
????????//讓子進程繼承父進程的三個標準句柄
????????if?(!(StartupInfo->dwFlags?&
???????????????????(STARTF_USESTDHANDLES?|?STARTF_USEHOTKEY?|?STARTF_SHELLPRIVATE)))
????????{
????????????BasepCopyHandles(ProcessParameters,OurPeb->ProcessParameters,InheritHandles);
????????}
????}
Size?=?ProcessParameters->Length;//參數(shù)塊本身的長度
//在子進程中分配一個參數(shù)塊
????Status?=?NtAllocateVirtualMemory(ProcessHandle,&RemoteParameters,0,&Size,
?????????????????????????????????????MEM_COMMIT,PAGE_READWRITE);
ProcessParameters->MaximumLength?=?Size;
//在子進程中分配一個參數(shù)塊
????Status?=?NtWriteVirtualMemory(ProcessHandle,RemoteParameters,ProcessParameters,
??????????????????????????????????ProcessParameters->Length,NULL);
//將參數(shù)塊復制到子進程的地址空間中
????Status?=?NtWriteVirtualMemory(ProcessHandle,
??????????????????????????????????&Peb->ProcessParameters,
??????????????????????????????????&RemoteParameters,
??????????????????????????????????sizeof(PVOID),
??????????????????????????????????NULL);
????RtlDestroyProcessParameters(ProcessParameters);
????return?STATUS_SUCCESS;
}
下面的函數(shù)創(chuàng)建第一個線程的(用戶棧、內(nèi)核棧、初始內(nèi)核棧幀)
HANDLE
BasepCreateFirstThread(HANDLE?ProcessHandle,LPSECURITY_ATTRIBUTES?lpThreadAttributes,
???????????????????????PSECTION_IMAGE_INFORMATION?SectionImageInfo,PCLIENT_ID?ClientId)
{
????BasepCreateStack(ProcessHandle,
?????????????????????SectionImageInfo->MaximumStackSize,//默認為1MB
?????????????????????SectionImageInfo->CommittedStackSize,//默認為4kb
?????????????????????&InitialTeb);//創(chuàng)建(即分配)該線程的用戶棧
BasepInitializeContext(&Context,
NtCurrentPeb(),//賦給context.ebx
SectionImageInfo->TransferAddress,//賦給context.eax(也即oep)
???????????????????????????InitialTeb.StackBase,//?賦給context.esp
0);//0表示是主線程的用戶空間總入口
????ObjectAttributes?=?BasepConvertObjectAttributes(&LocalObjectAttributes,
????????????????????????????????????????????????????lpThreadAttributes,NULL);
????Status?=?NtCreateThread(&hThread,THREAD_ALL_ACCESS,ObjectAttributes,ProcessHandle,
????????????????????????????ClientId,&Context,&InitialTeb,TRUE);????
????Status?=?BasepNotifyCsrOfThread(hThread,?ClientId);//通知csrss進程線程創(chuàng)建通知
????return?hThread;
}
下面的函數(shù)用來分配一個用戶棧(每個線程都要分配一個)【棧底、棧頂、提交界】
NTSTATUS
BasepCreateStack(HANDLE?hProcess,
?????????????????SIZE_T?StackReserve,//棧的保留大小。默認為1MB
?????????????????SIZE_T?StackCommit,//初始提交大小。默認為4KB,一個頁面
?????????????????OUT?PINITIAL_TEB?InitialTeb)//用來構造初始teb
{
????ULONG_PTR?Stack?=?NULL;
????BOOLEAN?UseGuard?=?FALSE;
????Status?=?NtQuerySystemInformation(SystemBasicInformation,&SystemBasicInfo,
??????????????????????????????????????sizeof(SYSTEM_BASIC_INFORMATION),NULL);
????if?(hProcess?==?NtCurrentProcess())
????{
????????Headers?=?RtlImageNtHeader(NtCurrentPeb()->ImageBaseAddress);
????????StackReserve?=?(StackReserve)???
????????????????????????StackReserve?:?Headers->OptionalHeader.SizeOfStackReserve;
????????StackCommit?=?(StackCommit)???
????????????????????????StackCommit?:?Headers->OptionalHeader.SizeOfStackCommit;
????}
????else
????{
????????StackReserve?=?(StackReserve)???StackReserve?:SystemBasicInfo.AllocationGranularity;
????????StackCommit?=?(StackCommit)???StackCommit?:?SystemBasicInfo.PageSize;
}
//棧的區(qū)段長度對齊64kb
????StackReserve?=?ROUND_UP(StackReserve,?SystemBasicInfo.AllocationGranularity);
StackCommit?=?ROUND_UP(StackCommit,?SystemBasicInfo.PageSize);
????//預定這么大小的棧(默認1MB)
Status?=?ZwAllocateVirtualMemory(hProcess,?(PVOID*)&Stack,0,&StackReserve,MEM_RESERVE,
?????????????????????????????????????PAGE_READWRITE);
????InitialTeb->AllocatedStackBase?=?(PVOID)Stack;//棧區(qū)段的分配基址
InitialTeb->StackBase?=?(PVOID)(Stack?+?StackReserve);//棧底
????Stack?+=?StackReserve?-?StackCommit;
????if?(StackReserve?>?StackCommit)
{
????????UseGuard?=?TRUE;
????????Stack?-=?SystemBasicInfo.PageSize;?
????????StackCommit?+=?SystemBasicInfo.PageSize;?//多提交一個保護頁
}
//初始提交這么大小的頁面(也就是最常見的一個頁外加一個保護頁的大小)
????Status?=?ZwAllocateVirtualMemory(hProcess,?(PVOID*)&Stack,0,&StackCommit,MEM_COMMIT,
?????????????????????????????????????PAGE_READWRITE);
????
????InitialTeb->StackLimit?=?(PVOID)Stack;//?StackLimit表示第一個尚未提交頁的邊界
????if?(UseGuard)
????{
????????SIZE_T?GuardPageSize?=?SystemBasicInfo.PageSize;????
????????Status?=?ZwProtectVirtualMemory(hProcess,?(PVOID*)&Stack,&GuardPageSize,
????????????????????????????????????????PAGE_GUARD?|?PAGE_READWRITE);//改為PAGE_GUARD屬性
????????InitialTeb->StackLimit?=?(PVOID)((ULONG_PTR)InitialTeb->StackLimit?-?GuardPageSize);
????}
????return?STATUS_SUCCESS;
}
下面這個函數(shù)構造該線程的初始寄存器上下文
VOID
BasepInitializeContext(IN?PCONTEXT?Context,IN?PVOID?Parameter,IN?PVOID?StartAddress,
???????????????????????IN?PVOID?StackAddress,IN?ULONG?ContextType)
{
????Context->Eax?=?(ULONG)StartAddress;//oep或用戶指定的線程入口函數(shù)
????Context->Ebx?=?(ULONG)Parameter;//peb
????Context->Esp?=?(ULONG)StackAddress;//棧底就是初始棧頂
????Context->SegFs?=?KGDT_R3_TEB?|?RPL_MASK;//fs指向TEB
????Context->SegEs?=?KGDT_R3_DATA?|?RPL_MASK;
????Context->SegDs?=?KGDT_R3_DATA?|?RPL_MASK;
????Context->SegCs?=?KGDT_R3_CODE?|?RPL_MASK;
????Context->SegSs?=?KGDT_R3_DATA?|?RPL_MASK;
????Context->SegGs?=?0;
????Context->EFlags?=?0x3000;?//?IOPL?3?
????if?(ContextType?==?1)???
????????Context->Eip?=?(ULONG)BaseThreadStartupThunk;?//普通線程的用戶空間總入口
????else?if?(ContextType?==?2)??//纖程
????????Context->Eip?=?(ULONG)BaseFiberStartup;
????else?????
????????Context->Eip?=?(ULONG)BaseProcessStartThunk;?//主線程的用戶空間總入口
????Context->ContextFlags?=?CONTEXT_FULL;//所有字段全部有效
????Context->Esp?-=?sizeof(PVOID);//騰出參數(shù)空間
}
當線程創(chuàng)建起來后,會緊跟著創(chuàng)建它的teb。現(xiàn)在暫時不看NtCreateThread是怎樣實現(xiàn)的,看一下teb的創(chuàng)建過程。
NTSTATUS
MmCreateTeb(IN?PEPROCESS?Process,
????????????IN?PCLIENT_ID?ClientId,//線程的客戶id即【進程id.線程id】
????????????IN?PINITIAL_TEB?InitialTeb,
????????????OUT?PTEB?*BaseTeb)//返回teb的地址
{
????NTSTATUS?Status?=?STATUS_SUCCESS;
????*BaseTeb?=?NULL;
????KeAttachProcess(&Process->Pcb);//掛靠到子進程地址空間
????Status?=?MiCreatePebOrTeb(Process,?sizeof(TEB),?(PULONG_PTR)&Teb);//從peb處往低地址端搜索
????_SEH2_TRY
????{
????????RtlZeroMemory(Teb,?sizeof(TEB));
????????Teb->NtTib.ExceptionList?=?-1;//初始是沒有seh
????????Teb->NtTib.Self?=?(PNT_TIB)Teb;//將self指向指針結構的地址,方便尋址
????????Teb->NtTib.Version?=?30?<<?8;
????????Teb->ClientId?=?*ClientId;
????????Teb->RealClientId?=?*ClientId;
????????Teb->ProcessEnvironmentBlock?=?Process->Peb;//關鍵,teb中有個指針指向peb
????????Teb->CurrentLocale?=?PsDefaultThreadLocaleId;
????????if?((InitialTeb->PreviousStackBase?==?NULL)?&&
?????????????????????????????????????(InitialTeb->PreviousStackLimit?==?NULL))
????????{
????????????Teb->NtTib.StackBase?=?InitialTeb->StackBase;//棧底
????????????Teb->NtTib.StackLimit?=?InitialTeb->StackLimit;//提交邊界(最近未提交頁的地址)
????????????Teb->DeallocationStack?=?InitialTeb->AllocatedStackBase;
????????}
????????else
????????{
????????????Teb->NtTib.StackBase?=?InitialTeb->PreviousStackBase;
????????????Teb->NtTib.StackLimit?=?InitialTeb->PreviousStackLimit;
????????}
????????Teb->StaticUnicodeString.MaximumLength?=?sizeof(Teb->StaticUnicodeBuffer);
????????Teb->StaticUnicodeString.Buffer?=?Teb->StaticUnicodeBuffer;
????}
????_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
????{
????????Status?=?_SEH2_GetExceptionCode();
????}
????_SEH2_END;
????KeDetachProcess();
????*BaseTeb?=?Teb;
????return?Status;
}
這樣,經(jīng)過以上的操作后,進程用戶空間的典型布局就定出來了。
----------------------------------------------------------------------------------------->
64kb?????64kb????64kb???一般1MB???????????一般在0x00400000處?????n*4kb?4kb??????4kb??
禁區(qū)|環(huán)境變量塊|參數(shù)塊|主線程的棧|其它空間|exe文件各個節(jié)|其他空間|各teb|peb|內(nèi)核用戶共享區(qū)|
--------------------------->
60kb???64kb?0x80000000開始
無效區(qū)|隔離區(qū)|系統(tǒng)空間…
用戶空間的布局:一句口訣【環(huán)、參、棧、文、堆、t、p】
進程的創(chuàng)建:
NTSTATUS
NtCreateProcess(OUT?PHANDLE?ProcessHandle,
????????????????IN?ACCESS_MASK?DesiredAccess,
????????????????IN?POBJECT_ATTRIBUTES?ObjectAttributes?OPTIONAL,
????????????????IN?HANDLE?ParentProcess,
????????????????IN?BOOLEAN?InheritObjectTable,
????????????????IN?HANDLE?SectionHandle?OPTIONAL,
????????????????IN?HANDLE?DebugPort?OPTIONAL,
????????????????IN?HANDLE?ExceptionPort?OPTIONAL)
{
????ULONG?Flags?=?0;
????if?((ULONG)SectionHandle?&?1)?Flags?=?PS_REQUEST_BREAKAWAY;
????if?((ULONG)DebugPort?&?1)?Flags?|=?PS_NO_DEBUG_INHERIT;
????if?(InheritObjectTable)?Flags?|=?PS_INHERIT_HANDLES;
????return?NtCreateProcessEx(ProcessHandle,
?????????????????????????????DesiredAccess,
?????????????????????????????ObjectAttributes,
?????????????????????????????ParentProcess,
?????????????????????????????Flags,
?????????????????????????????SectionHandle,
?????????????????????????????DebugPort,
?????????????????????????????ExceptionPort,
?????????????????????????????FALSE);
}
NTSTATUS
NtCreateProcessEx(OUT?PHANDLE?ProcessHandle,
??????????????????IN?ACCESS_MASK?DesiredAccess,
??????????????????IN?POBJECT_ATTRIBUTES?ObjectAttributes?OPTIONAL,
??????????????????IN?HANDLE?ParentProcess,
??????????????????IN?ULONG?Flags,
??????????????????IN?HANDLE?SectionHandle?OPTIONAL,
??????????????????IN?HANDLE?DebugPort?OPTIONAL,
??????????????????IN?HANDLE?ExceptionPort?OPTIONAL,
??????????????????IN?BOOLEAN?InJob)
{
????if?(!ParentProcess)
????????Status?=?STATUS_INVALID_PARAMETER;
????else
????{
????????Status?=?PspCreateProcess(ProcessHandle,
??????????????????????????????????DesiredAccess,
??????????????????????????????????ObjectAttributes,
??????????????????????????????????ParentProcess,
??????????????????????????????????Flags,
??????????????????????????????????SectionHandle,
??????????????????????????????????DebugPort,
??????????????????????????????????ExceptionPort,
??????????????????????????????????InJob);
????}
????return?Status;
}
如上,CreateProcess?API調(diào)用NtCreateProcess系統(tǒng)服務,最終會調(diào)用下面的函數(shù)完成進程的創(chuàng)建工作
NTSTATUS
PspCreateProcess(OUT?PHANDLE?ProcessHandle,//返回子進程的句柄
?????????????????IN?ACCESS_MASK?DesiredAccess,
?????????????????IN?POBJECT_ATTRIBUTES?ObjectAttributes?OPTIONAL,
?????????????????IN?HANDLE?ParentProcess?OPTIONAL,//父進程可以是任意第三方進程
?????????????????IN?ULONG?Flags,
?????????????????IN?HANDLE?SectionHandle?OPTIONAL,//exe文件的section對象
?????????????????IN?HANDLE?DebugPort?OPTIONAL,//調(diào)試器進程中某個線程的調(diào)試端口
?????????????????IN?HANDLE?ExceptionPort?OPTIONAL)
{
????ULONG?DirectoryTableBase[2]?=?{0,0};//為子進程分配的頁目錄所在的物理頁面地址
????PETHREAD?CurrentThread?=?PsGetCurrentThread();
????KPROCESSOR_MODE?PreviousMode?=?ExGetPreviousMode();
????PEPROCESS?CurrentProcess?=?PsGetCurrentProcess();
????PACCESS_STATE?AccessState?=?&LocalAccessState;//記錄著當前線程的令牌和申請的訪問權限
????BOOLEAN?NeedsPeb?=?FALSE;//表示是否需要為其創(chuàng)建一個peb,絕大多數(shù)都要
????if?(ParentProcess)//事實上只有”system”進程沒有父進程
????{
????????Status?=?ObReferenceObjectByHandle(ParentProcess,
???????????????????????????????????????????PROCESS_CREATE_PROCESS,//表示要為其創(chuàng)建子進程
???????????????????????????????????????????PsProcessType,PreviousMode,?(PVOID*)&Parent);
????????Affinity?=?Parent->Pcb.Affinity;//繼承父進程的cpu親緣性
????}
????else
????{
????????Parent?=?NULL;
????????Affinity?=?KeActiveProcessors;
????}
????MinWs?=?PsMinimumWorkingSet;
MaxWs?=?PsMaximumWorkingSet;
//關鍵。創(chuàng)建該進程的內(nèi)核對象結構
????Status?=?ObCreateObject(PreviousMode,
????????????????????????????PsProcessType,//進程對象類型
????????????????????????????ObjectAttributes,PreviousMode,NULL,
????????????????????????????sizeof(EPROCESS),//內(nèi)核進程對象
????????????????????????????0,0,?(PVOID*)&Process);
????RtlZeroMemory(Process,?sizeof(EPROCESS));
????InitializeListHead(&Process->ThreadListHead);//初始時該進程尚無任何線程
????PspInheritQuota(Process,?Parent);//繼承父進程的資源配額塊
????ObInheritDeviceMap(Parent,?Process);//繼承父進程的磁盤卷設備位圖
????if?(Parent)
????????Process->InheritedFromUniqueProcessId?=?Parent->UniqueProcessId;//記錄父進程的pid
????if?(SectionHandle)//exe文件的section,一般都有
{
????//獲得對應的section對象
????????Status?=?ObReferenceObjectByHandle(SectionHandle,SECTION_MAP_EXECUTE,
???????????????????????????????????????????MmSectionObjectType,PreviousMode,
???????????????????????????????????????????(PVOID*)&SectionObject);
????}
????Else?…
????Process->SectionObject?=?SectionObject;//記錄該進程的exe文件section
????if?(DebugPort)//由調(diào)試器啟動的子進程,都會傳遞一個調(diào)試端口給子進程
????{
????????Status?=?ObReferenceObjectByHandle(DebugPort,
???????????????????????????????????????????DEBUG_OBJECT_ADD_REMOVE_PROCESS,
???????????????????????????????????????????DbgkDebugObjectType,PreviousMode,
???????????????????????????????????????????(PVOID*)&DebugObject);
//每個被調(diào)進程與調(diào)試器中的一個調(diào)試器線程通過一個調(diào)試端口連接,形成一個調(diào)試會話
????????Process->DebugPort?=?DebugObject;?//可用于檢測調(diào)試
????????if?(Flags?&?PS_NO_DEBUG_INHERIT)//指示不可將調(diào)試端口再繼承給它的子進程
????????????InterlockedOr((PLONG)&Process->Flags,?PSF_NO_DEBUG_INHERIT_BIT);
????????
????}
????else
????{
????????if?(Parent)
?DbgkCopyProcessDebugPort(Process,?Parent);//繼承父進程的調(diào)試端口
????}
????if?(ExceptionPort)
????{
????????Status?=?ObReferenceObjectByHandle(ExceptionPort,PORT_ALL_ACCESS,LpcPortObjectType,
???????????????????????????????????????????PreviousMode,?(PVOID*)&ExceptionPortObject);
????????Process->ExceptionPort?=?ExceptionPortObject;
????}
????Process->ExitStatus?=?STATUS_PENDING;//默認的退出碼
if?(Parent)
{
????/*創(chuàng)建頁目錄和內(nèi)核部分的頁表,然后從系統(tǒng)公共的內(nèi)核頁表中復制內(nèi)核空間中的那些頁表項(這樣,每個進程的內(nèi)核地//址空間的映射就相同了)*/
????????MmCreateProcessAddressSpace(MinWs,Process,DirectoryTableBase)
}
????Else?…
????InterlockedOr((PLONG)&Process->Flags,?PSF_HAS_ADDRESS_SPACE_BIT);
Process->Vm.MaximumWorkingSetSize?=?MaxWs;
//初始化進程對象的內(nèi)部結構成員
????KeInitializeProcess(&Process->Pcb,PROCESS_PRIORITY_NORMAL,Affinity,DirectoryTableBase);
????Status?=?PspInitializeProcessSecurity(Process,?Parent);//繼承父進程的令牌
????Process->PriorityClass?=?PROCESS_PRIORITY_CLASS_NORMAL;//初始創(chuàng)建時都是普通優(yōu)先級類
????Status?=?STATUS_SUCCESS;
????if?(SectionHandle)?//一般都有
????{
????????//初始化地址空間并將exe文件映射到用戶空間中
????????Status?=?MmInitializeProcessAddressSpace(Process,SectionObject,&Flags,ImageFileName);
????????NeedsPeb?=?TRUE;
????}
????Else?…
if?(SectionObject)//映射(即加載)exe文件后,再映射ntdll.dll到用戶空間(事實上固定映到某處)
?PspMapSystemDll(Process,?NULL,?FALSE);
????CidEntry.Object?=?Process;
CidEntry.GrantedAccess?=?0;
//進程id、線程id實際上都是全局PspCidTable句柄表中的句柄,他們也指向對應的對象
????Process->UniqueProcessId?=?ExCreateHandle(PspCidTable,?&CidEntry);//分配pid進程號
????Process->ObjectTable->UniqueProcessId?=?Process->UniqueProcessId;
????if?((Parent)?&&?(NeedsPeb))//用戶空間中的進程都會分配一個peb,且固定在某處
????{
????????RtlZeroMemory(&InitialPeb,?sizeof(INITIAL_PEB));
????????InitialPeb.Mutant?=?(HANDLE)-1;
????????if?(SectionHandle)
????????????Status?=?MmCreatePeb(Process,?&InitialPeb,?&Process->Peb);//創(chuàng)建peb(固定在某處)
????????Else?…
}
/*將進程加入全局的“活動進程鏈表”中,這個鏈表僅供系統(tǒng)統(tǒng)計用,因此可以恣意篡改,如隱藏進程。任務管理器等其他絕大多數(shù)進程枚舉工具內(nèi)部就是遍歷的這個進程鏈表*/
InsertTailList(&PsActiveProcessHead,?&Process->ActiveProcessLinks);
//這個函數(shù)用來將進程對象插入句柄表,返回一個進程句柄
????Status?=?ObInsertObject(Process,AccessState,DesiredAccess,1,NULL,&hProcess);
????//根據(jù)進程的優(yōu)先級類計算該進程的基本優(yōu)先級和時間片(初始創(chuàng)建時作為后臺進程)
?Process->Pcb.BasePriority?=PspComputeQuantumAndPriority(Process,
????????????????????????????????????????????PsProcessPriorityBackground,&Quantum);
????Process->Pcb.QuantumReset?=?Quantum;
KeQuerySystemTime(&Process->CreateTime);//記錄進程的創(chuàng)建時間
????PspRunCreateProcessNotifyRoutines(Process,?TRUE);//發(fā)出一個進程創(chuàng)建通知消息
????*ProcessHandle?=?hProcess;//返回對應的句柄
????return?Status;
}
上面的NtCreateProcess、PspCreateProces只是創(chuàng)建了一個進程(它的內(nèi)核對象、地址空間等),進程本身是不能運行的,所以CreateProcess?API最終還會調(diào)用NtCreateThread創(chuàng)建并啟動主線程。
線程從運行空間角度看,分為兩種線程:
1、?用戶線程(主線程和CreateThread創(chuàng)建的普通線程都是用戶線程):線程部分代碼運行在用戶空間
2、?內(nèi)核線程(由驅動程序調(diào)用PsCreateSystemThread創(chuàng)建的線程):線程的所有代碼運行在內(nèi)核空間
兩種線程的運行路徑分別為:
1、?KiThreadStartup->PspUserThreadStartup->用戶空間中的公共入口->映像文件中的入口
2、?KiThreadStartup->PspSystemThreadStartup->內(nèi)核空間中用戶指定的入口
下面是每個用戶線程的啟動流程
NTSTATUS
NtCreateThread(OUT?PHANDLE?ThreadHandle,//返回線程句柄
???????????????IN?ACCESS_MASK?DesiredAccess,
???????????????IN?POBJECT_ATTRIBUTES?ObjectAttributes?OPTIONAL,
???????????????IN?HANDLE?ProcessHandle,//目標進程
???????????????OUT?PCLIENT_ID?ClientId,//返回pid.tid
???????????????IN?PCONTEXT?ThreadContext,//線程初始的用戶空間寄存器上下文
???????????????IN?PINITIAL_TEB?InitialTeb,//線程的初始teb
???????????????IN?BOOLEAN?CreateSuspended)//是否初始創(chuàng)建為掛起態(tài)
{
????INITIAL_TEB?SafeInitialTeb?=?*InitialTeb;
????if?(KeGetPreviousMode()?!=?KernelMode)
{???
//用戶空間線程必須指定初始的寄存器上下文(因為要模擬回到用戶空間)
????????if?(!ThreadContext)?return?STATUS_INVALID_PARAMETER;?
}
????return?PspCreateThread(ThreadHandle,DesiredAccess,ObjectAttributes,ProcessHandle,
???????????????????????????NULL,ClientId,ThreadContext,&SafeInitialTeb,CreateSuspended,
???????????????????????????NULL,//用戶線程無需StartRoutine
NULL);//用戶線程無需StartContext
}
NTSTATUS
PspCreateThread(OUT?PHANDLE?ThreadHandle,?//返回線程句柄
????????????????IN?ACCESS_MASK?DesiredAccess,
????????????????IN?POBJECT_ATTRIBUTES?ObjectAttributes?OPTIONAL,
????????????????IN?HANDLE?ProcessHandle,?//目標進程(用于創(chuàng)建用戶線程)
????????????????IN?PEPROCESS?TargetProcess,?//目標進程(用于創(chuàng)建內(nèi)核線程)
????????????????OUT?PCLIENT_ID?ClientId,?//返回pid.tid
????????????????IN?PCONTEXT?ThreadContext,//用戶空間的初始寄存器上下文
????????????????IN?PINITIAL_TEB?InitialTeb,?//線程的初始teb(內(nèi)核線程不需要)
????????????????IN?BOOLEAN?CreateSuspended,?//是否初始創(chuàng)建為掛起態(tài)
????????????????IN?PKSTART_ROUTINE?StartRoutine?OPTIONAL,//內(nèi)核線程的用戶指定入口?
????????????????IN?PVOID?StartContext?OPTIONAL)//入口參數(shù)
{
????PTEB?TebBase?=?NULL;
????KPROCESSOR_MODE?PreviousMode?=?ExGetPreviousMode();
????PACCESS_STATE?AccessState?=?&LocalAccessState;
????if?(StartRoutine)?PreviousMode?=?KernelMode;//只有內(nèi)核線程才會顯式指定StartRoutine
????if?(ProcessHandle)//用戶線程的目標進程
????{
????????Status?=?ObReferenceObjectByHandle(ProcessHandle,PROCESS_CREATE_THREAD,PsProcessType,
???????????????????????????????????????????PreviousMode,?(PVOID*)&Process,NULL);
????}
????else
????????Process?=?TargetProcess;
????//關鍵。創(chuàng)建該線程對象的內(nèi)核結構
????Status?=?ObCreateObject(PreviousMode,PsThreadType,ObjectAttributes,PreviousMode,NULL,
????????????????????????????sizeof(ETHREAD),0,0,?(PVOID*)&Thread);
????RtlZeroMemory(Thread,?sizeof(ETHREAD));
????Thread->ExitStatus?=?STATUS_PENDING;
????Thread->ThreadsProcess?=?Process;//指定該線程的所屬進程
Thread->Cid.UniqueProcess?=?Process->UniqueProcessId;?//指定該線程的所屬進程的pid
????CidEntry.Object?=?Thread;
????CidEntry.GrantedAccess?=?0;
????Thread->Cid.UniqueThread?=?ExCreateHandle(PspCidTable,?&CidEntry);//分配一個tid
????InitializeListHead(&Thread->IrpList);//該線程發(fā)起的所有未完成的irp請求鏈表
????InitializeListHead(&Thread->PostBlockList);
????InitializeListHead(&Thread->ActiveTimerListHead);
????if?(ThreadContext)//if?用戶線程
????{
????????//用戶線程都會分配一個teb
Status?=?MmCreateTeb(Process,?&Thread->Cid,?InitialTeb,?&TebBase);?
????????Thread->StartAddress?=?ThreadContext->Eip;//用戶空間中的公共入口
????????Thread->Win32StartAddress?=?ThreadContext->Eax;//真正線程入口(oep或用戶指定的入口)
???//初始化線程對象結構、構造用戶線程的初始運行環(huán)境
????????Status?=?KeInitThread(&Thread->Tcb,
??????????????????????????????PspUserThreadStartup,//內(nèi)核中的用戶線程派遣函數(shù)入口
??????????????????????????????NULL,//StartRoutine=NULL,使用用戶空間中那個公共的入口
??????????????????????????????Thread->StartAddress,//StartContext
??????????????????????????????ThreadContext,//用戶空間中的初始寄存器上下文
??????????????????????????????TebBase,&Process->Pcb);
????}
????Else?//內(nèi)核線程
????{
????????Thread->StartAddress?=?StartRoutine;//用戶指定的入口
????????//初始化線程對象結構、構造內(nèi)核線程的初始運行環(huán)境
????????Status?=?KeInitThread(&Thread->Tcb,
??????????????????????????????PspSystemThreadStartup,?//內(nèi)核中的內(nèi)核線程派遣函數(shù)入口
??????????????????????????????StartRoutine,?//用戶指定的入口
??????????????????????????????StartContext,//入口參數(shù)
??????????????????????????????NULL,//無需context
??????????????????????????????NULL,//無需teb
??????????????????????????????&Process->Pcb);
????}
????InsertTailList(&Process->ThreadListHead,?&Thread->ThreadListEntry);//插入進程總線程鏈表
Process->ActiveThreads++;
????KeStartThread(&Thread->Tcb);//設置該線程的初始優(yōu)先級、時間片信息(從進程繼承)
????PspRunCreateThreadNotifyRoutines(Thread,?TRUE);//通知系統(tǒng)線程創(chuàng)建消息
????if?(CreateSuspended)?KeSuspendThread(&Thread->Tcb);//掛起線程
????//將令牌與申請的權限傳遞到訪問狀態(tài)中
????Status?=?SeCreateAccessStateEx(NULL,ThreadContext??PsGetCurrentProcess()?:?Process,
???????????????????????????????????&LocalAccessState,&AuxData,DesiredAccess,…);
????Status?=?ObInsertObject(Thread,AccessState,DesiredAccess,0,NULL,&hThread);//插入句柄表
????if?(NT_SUCCESS(Status))
????{
????????????if?(ClientId)?*ClientId?=?Thread->Cid;
????????????*ThreadHandle?=?hThread;
????}
????KeQuerySystemTime(&Thread->CreateTime);//記錄線程的創(chuàng)建時間
????KeReadyThread(&Thread->Tcb);?//構造好初始運行環(huán)境后,加入就緒隊列,現(xiàn)在線程就將跑起來了
????return?Status;
}
如上,上面函數(shù)創(chuàng)建內(nèi)核線程對象,然后調(diào)用下面的函數(shù)初始化對象結構,創(chuàng)建它的內(nèi)核棧,然后構造好它的初始運行環(huán)境(指內(nèi)核棧中的初始狀態(tài)),設置好初始的優(yōu)先級和時間片后,就啟動線程運行(指加入就緒隊列)。這樣,當該線程不久被調(diào)度運行時,就能跟著內(nèi)核棧中初始的狀態(tài),一直運行下去(指調(diào)度時:恢復線程切換線程,從KiThreadStartup函數(shù)開始運行,然后恢復用戶空間寄存器現(xiàn)場,回到用戶空間的公共總入口處(kernel32模塊中的BaseProcessStrartThunk或BaseThreadStrartThunk)繼續(xù)執(zhí)行)
NTSTATUS
KeInitThread(IN?OUT?PKTHREAD?Thread,
?????????????IN?PKSYSTEM_ROUTINE?SystemRoutine,//用戶線程是PspUserThreadStartup
?????????????IN?PKSTART_ROUTINE?StartRoutine,//用戶線程是NULL,使用公共的總入口
?????????????IN?PVOID?StartContext,//入口參數(shù)
?????????????IN?PCONTEXT?Context,//用戶空間的初始寄存器上下文
?????????????IN?PVOID?Teb,//用戶線程的初始teb
?????????????IN?PKPROCESS?Process)
{
????BOOLEAN?AllocatedStack?=?FALSE;//表示是否分配了內(nèi)核棧
????KeInitializeDispatcherHeader(&Thread->DispatcherHeader,ThreadObject,
?????????????????????????????????sizeof(KTHREAD)?/?sizeof(LONG),FALSE);//線程也是可等待對象
????InitializeListHead(&Thread->MutantListHead);
????for?(i?=?0;?i<?(THREAD_WAIT_OBJECTS?+?1);?i++)
????????Thread->WaitBlock[i].Thread?=?Thread;//線程內(nèi)部內(nèi)置的四個預定等待塊
????Thread->EnableStackSwap?=?TRUE;?//指示內(nèi)核棧可以被置換到外存
????Thread->IdealProcessor?=?1;
????Thread->SwapBusy?=?FALSE;//一個標記當前線程是否正在進行切換的標記
????Thread->KernelStackResident?=?TRUE;?//線程初始創(chuàng)建時,內(nèi)核棧當然位于物理內(nèi)存中
????Thread->AdjustReason?=?AdjustNone;//優(yōu)先級的調(diào)整原因
Thread->ServiceTable?=?KeServiceDescriptorTable;//該線程使用的系統(tǒng)服務表描述符表(非SSDT)
//初始時,線程的兩個APC隊列都為空
????InitializeListHead(&Thread->ApcState.ApcListHead[0]);
????InitializeListHead(&Thread->ApcState.ApcListHead[1]);
????Thread->ApcState.Process?=?Process;//當前進程
????Thread->ApcStatePointer[OriginalApcEnvironment]?=?&Thread->ApcState;
????Thread->ApcStatePointer[AttachedApcEnvironment]?=?&Thread->SavedApcState;
????Thread->ApcStateIndex?=?OriginalApcEnvironment;
????Thread->ApcQueueable?=?TRUE;//標記初始時,APC隊列可插入
????//一個專用于掛起線程的APC,后文會有介紹
????KeInitializeApc(&Thread->SuspendApc,Thread,
????????????????????OriginalApcEnvironment,
????????????????????KiSuspendNop,
????????????????????KiSuspendRundown,
????????????????????KiSuspendThread,//該apc真正的函數(shù)
????????????????????KernelMode,NULL);
????KeInitializeSemaphore(&Thread->SuspendSemaphore,?0,?2);
????Timer?=?&Thread->Timer;//可復用
????KeInitializeTimer(Timer);
????TimerWaitBlock?=?&Thread->WaitBlock[TIMER_WAIT_BLOCK];//定時器固定占用一個等待快
????TimerWaitBlock->Object?=?Timer;
????TimerWaitBlock->WaitKey?=?STATUS_TIMEOUT;
????TimerWaitBlock->WaitType?=?WaitAny;
????TimerWaitBlock->NextWaitBlock?=?NULL;
????TimerWaitBlock->WaitListEntry.Flink?=?&Timer->Header.WaitListHead;
TimerWaitBlock->WaitListEntry.Blink?=?&Timer->Header.WaitListHead;
????Thread->Teb?=?Teb;//記錄teb
????KernelStack?=?MmCreateKernelStack(FALSE,?0);//關鍵。分配該線程的內(nèi)核棧
????AllocatedStack?=?TRUE;//標記為已分配
????Thread->InitialStack?=?KernelStack;//初始的內(nèi)核棧頂(即棧底)
????Thread->StackBase?=?KernelStack;//內(nèi)核棧底
????Thread->StackLimit?=?KernelStack?-12kb;//普通線程的內(nèi)核棧的大小為12kb
????Thread->KernelStackResident?=?TRUE;//初始時,內(nèi)核棧當然位于物理內(nèi)存中
Status?=?STATUS_SUCCESS;
//關鍵。下面這個函數(shù)構造初始的內(nèi)核棧幀(模擬切換時的狀態(tài))
????KiInitializeContextThread(Thread,
??????????????????????????????SystemRoutine,//用戶線程為PspUserThreadStartup
??????????????????????????????StartRoutine,//用戶線程為NULL(表示使用公共總入口)
??????????????????????????????StartContext,//入口參數(shù)
??????????????????????????????Context);//用戶空間的初始寄存器上下文
????Thread->State?=?Initialized;//標記為已初始化好,可以運行了
????return?Status;
}
下面這個函數(shù)就是用來實際執(zhí)行構造線程的初始運行環(huán)境(即初始的內(nèi)核棧狀態(tài))工作
初始的內(nèi)核棧會模擬該線程仿佛以前曾經(jīng)運行過,曾經(jīng)被切換后的狀態(tài),這樣,該線程一旦得到初始調(diào)度機會,就向得到重新調(diào)度機會一樣,繼續(xù)運行。
每個處于非運行狀態(tài)的線程的內(nèi)核棧的布局是:(從棧底到棧頂)【浮點、trap、函數(shù)、切】
浮點寄存器幀|trap現(xiàn)場幀|內(nèi)核各層函數(shù)參數(shù)、局部變量幀|線程切換幀
每次發(fā)生系統(tǒng)調(diào)用、中斷、異常時線程都會進入內(nèi)核,在內(nèi)核棧先保存浮點寄存器,然后保存寄存器現(xiàn)場,
進入內(nèi)核函數(shù)嵌套調(diào)用,最后由于時間片等原因發(fā)生線程切換,保存切換時的現(xiàn)場,等待下次調(diào)度運行時,從上次切換出時的斷點處繼續(xù)執(zhí)行。
注意每當重回到用戶空間后,線程的內(nèi)核棧就是空白的。一個線程的絕大多數(shù)時間都是運行在用戶空間,因此,絕大多數(shù)時刻,線程的內(nèi)核棧都呈現(xiàn)空白狀態(tài)(里面沒存放任何數(shù)據(jù))。
下面這個函數(shù)就是用來初始構造模擬線程被切換出時的現(xiàn)場(實際線程還沒運行過,即還沒切換過)。
非常關鍵。
VOID
KiInitializeContextThread(IN?PKTHREAD?Thread,
??????????????????????????IN?PKSYSTEM_ROUTINE?SystemRoutine,//用戶線程為PspUserThreadStartup
??????????????????????????IN?PKSTART_ROUTINE?StartRoutine,?//用戶線程為NULL(表示公共總入口)
??????????????????????????IN?PVOID?StartContext,?//入口參數(shù)
??????????????????????????IN?PCONTEXT?ContextPointer)?//用戶線程的初始上下文(內(nèi)核線程沒有)
{
????PFX_SAVE_AREA?FxSaveArea;//內(nèi)核棧中的浮點寄存器保存區(qū)
????PFXSAVE_FORMAT?FxSaveFormat;
????PKSTART_FRAME?StartFrame;//線程公共起始函數(shù)KiThreadStartup的棧幀
????PKSWITCHFRAME?CtxSwitchFrame;//切換幀
????PKTRAP_FRAME?TrapFrame;//trap現(xiàn)場幀
????CONTEXT?LocalContext;//臨時變量
????PCONTEXT?Context?=?NULL;
ULONG?ContextFlags;
????PKUINIT_FRAME?InitFrame;//線程的初始內(nèi)核棧幀(由浮點幀、trap幀、起始函數(shù)幀、切換幀組成)
InitFrame?=?(PKUINIT_FRAME)(?Thread->InitialStack?-?sizeof(KUINIT_FRAME));
FxSaveArea?=?&InitFrame->FxSaveArea;//初始幀中的浮點保存區(qū)
RtlZeroMemory(FxSaveArea,KTRAP_FRAME_LENGTH?+?sizeof(FX_SAVE_AREA));
TrapFrame?=?&InitFrame->TrapFrame;//初始幀中的trap現(xiàn)場幀(最重要)
????StartFrame?=?&InitFrame->StartFrame;//起始函數(shù)(指KiThreadStartup)的參數(shù)幀(重要)
????CtxSwitchFrame?=?&InitFrame->CtxSwitchFrame;//切換幀(非常重要)
????if?(ContextPointer)//如果是要構造用戶線程的初始幀
????{
????????RtlCopyMemory(&LocalContext,?ContextPointer,?sizeof(CONTEXT));
????????Context?=?&LocalContext;
????????ContextFlags?=?CONTEXT_CONTROL;
????????{初始化浮點寄存器部分略}
????????Context->ContextFlags?&=?~CONTEXT_DEBUG_REGISTERS;//初始時不需要調(diào)試寄存器
????????//關鍵。模擬保存進入內(nèi)核空間中時的現(xiàn)場
????????KeContextToTrapFrame(Context,NULL,TrapFrame,Context->ContextFlags?|?ContextFlags,
?????????????????????????????UserMode);//將Context中各個寄存器填寫到Trap幀中(模擬自陷現(xiàn)場)
????????TrapFrame->HardwareSegSs?|=?RPL_MASK;
????????TrapFrame->SegDs?|=?RPL_MASK;TrapFrame->SegEs?|=?RPL_MASK;
????????TrapFrame->Dr7?=?0;//不需要調(diào)試寄存器
????????TrapFrame->DbgArgMark?=?0xBADB0D00;
????????TrapFrame->PreviousPreviousMode?=?UserMode;
????????TrapFrame->ExceptionList?=?-1;
????????Thread->PreviousMode?=?UserMode;//模擬從用戶空間自陷進來時構造的幀
????????StartFrame->UserThread?=?TRUE;//相當于push傳參給KiThreadStartup
????}
????else
{??
????{內(nèi)核線程則會初始化成不同的浮點寄存器,略}
????????Thread->PreviousMode?=?KernelMode;?//模擬從內(nèi)核空間發(fā)起系統(tǒng)調(diào)用時構造的幀
????????StartFrame->UserThread?=?FALSE;?//相當于push傳參給KiThreadStartup
}
????StartFrame->StartContext?=?StartContext;//相當于push傳參給KiThreadStartup
????StartFrame->StartRoutine?=?StartRoutine;?//相當于push傳參給KiThreadStartup
????StartFrame->SystemRoutine?=?SystemRoutine;?//相當于push傳參給KiThreadStartup
//關鍵。模擬線程仿佛上次在執(zhí)行call?KiThreadStartup時,被切換了出去
????CtxSwitchFrame->RetAddr?=?KiThreadStartup;//以后線程一調(diào)度就從這兒開始執(zhí)行下去?
????CtxSwitchFrame->ApcBypassDisable?=?TRUE;
????CtxSwitchFrame->ExceptionList?=?-1;//線程的初始內(nèi)核seh鏈表當然為空(-1表示空)
????Thread->KernelStack?=?CtxSwitchFrame;//記錄上次切換時的內(nèi)核棧頂(模擬的)
}
為了弄懂線程初始時的內(nèi)核棧布局,必須理解下面幾個結構體定義和函數(shù)。
typedef?struct?_KUINIT_FRAME??//每個線程的初始內(nèi)核棧幀
{
????KSWITCHFRAME?CtxSwitchFrame;//切換幀
????KSTART_FRAME?StartFrame;//KiThreadStartup函數(shù)的參數(shù)幀
????KTRAP_FRAME?TrapFrame;//trap現(xiàn)場幀
????FX_SAVE_AREA?FxSaveArea;//浮點保存區(qū)
}?KUINIT_FRAME,?*PKUINIT_FRAME;
其中浮點保存區(qū)就位于棧底,向上依次是trap現(xiàn)場幀、KiThreadStartup的參數(shù)幀、切換幀
typedef?struct?_KSTART_FRAME?//KiThreadStartup的參數(shù)幀
{
????PKSYSTEM_ROUTINE?SystemRoutine;?//用戶線程為PspUserThreadStartup
????PKSTART_ROUTINE?StartRoutine;//用戶線程為NULL(表示使用公共總入口)
????PVOID?StartContext;//入口參數(shù)
????BOOLEAN?UserThread;//標志
}?KSTART_FRAME,?*PKSTART_FRAME;
typedef?struct?_KSWITCHFRAME??//切換幀
{
PVOID?ExceptionList;//保存線程切換時的內(nèi)核she鏈表(不是用戶空間中的seh)
Union
{
BOOLEAN?ApcBypassDisable;//用于首次調(diào)度
UCHAR?WaitIrql;//用于保存切換時的WaitIrql
};
????PVOID?RetAddr;//保存發(fā)生切換時的斷點地址(以后切換回來時從這兒繼續(xù)執(zhí)行)
}?KSWITCHFRAME,?*PKSWITCHFRAME;
不管是用戶線程還是內(nèi)核線程,都是最開始從下面這個函數(shù)開始執(zhí)行起來的。
Void?KiThreadStartup(PKSYSTEM_ROUTINE??SystemRoutine?//用戶線程為PspUserThreadStartup
?????????????????????PKSTART_ROUTINE??StartRoutine?//轉用作PspUserThreadStartup的參數(shù)
?????????????????????Void*?StartContext//轉用作PspUserThreadStartup的參數(shù)
?????????????????????BOOL??UserThread
)
{
???Xor?ebx,ebx
???Xor?esi,esi
???Xor?edi,edi
???Xor?ebp,ebp
???Mov?ecx,APC_LEVEL??
???Call?KfLowerIrql???//降到APC級別
???Pop?eax?//彈出的第一個值剛好是SystemRoutine
???Call?eax?//調(diào)用SystemRoutine(注意StartRoutine,StartContext又是它的參數(shù))
???----------------------------------華麗的分割線------------------------------------------
???//注意若創(chuàng)建的是內(nèi)核線程,那么上面的eax是PspSystemThreadStartup,這個函數(shù)是“不返回的”,
???它執(zhí)行完畢后不會ret回來,而是直接跳去用戶指定的內(nèi)核入口了。反之,若能回來,那么可以肯定是
???用戶線程(而且,StartRoutine和StartContext這兩個參數(shù)已被PspSystemThreadStartup在內(nèi)部彈出)。那么,現(xiàn)在就可以順利彈出trap幀,恢復用戶空間中的寄存器上下文,繼續(xù)執(zhí)行,于是Jmp?KiServiceExit2,?jmp到那兒去,退回用戶空間。
???Pop?ecx?//此時ecx=棧幀中UserThread字段的值
???Or?ecx,ecx
???Jz?BadThread??//UserThread不為1就顯示藍屏界面
???Mov?ebp,esp?//此時的內(nèi)核棧頂就是trap幀的地址。
???Jmp?KiServiceExit2?//此時內(nèi)核棧中只剩余浮點保存區(qū)和trap幀,將恢復用戶空間現(xiàn)場退回用戶空間
}
上面的線程公共入口函數(shù)內(nèi)部會call?SystemRoutine?進入對應的函數(shù)。如果創(chuàng)建的是用戶線程,調(diào)用的就是PspUserThreadStartup。
VOID
PspUserThreadStartup(IN?PKSTART_ROUTINE?StartRoutine,//對于用戶線程無意義
?????????????????????IN?PVOID?StartContext)//對于用戶線程無意義
{
????BOOLEAN?DeadThread?=?FALSE;
????KeLowerIrql(PASSIVE_LEVEL);
????Thread?=?PsGetCurrentThread();
????if?(Thread->DeadThread)
????????DeadThread?=?TRUE;
????Else?…
????if?(!(Thread->DeadThread)?&&?!(Thread->HideFromDebugger))
????????DbgkCreateThread(Thread,?StartContext);//通知內(nèi)核調(diào)試器一個新線程啟動了
????if?(!DeadThread)??
????{
????????KeRaiseIrql(APC_LEVEL,?&OldIrql);
//返回用戶空間的總入口前先執(zhí)行一下apc,完成其他初始工作(如加載其他依賴庫)
????????KiInitializeUserApc(KeGetExceptionFrame(&Thread->Tcb),
????????????????????????????KeGetTrapFrame(&Thread->Tcb),
????????????????????????????PspSystemDllEntryPoint,//ntdll.LdrInitializeThunk
????????????????????????????NULL,PspSystemDllBase,NULL);
????????KeLowerIrql(PASSIVE_LEVEL);
????}
Else?…
//這個函數(shù)是典型的‘返回型’函數(shù),會返回到上面函數(shù)的call?eax指令后面,進而退回用戶空間的總入口函數(shù)去去執(zhí)行。不過,在退回總入口前,這兒插入了一個APC,執(zhí)行完這個附加的APC后才會正式退回用戶空間的總入口(因為這個LdrInitializeThunk?APC函數(shù)還有一些重要的附加工作要做)
Return;
}
相信大家一直有一個疑問,就是用戶空間的總入口到底做了什么工作,我們后文再看。
-------------------------------------------------------------------------------------
現(xiàn)在我們回到CreateProcess?API,總結一下這個函數(shù)到底在內(nèi)部干了什么,看以下總結
1、?打開目標可執(zhí)行文件
若是exe文件,先檢查‘映像劫持’鍵,然后打開文件,創(chuàng)建一個section,等候映射
若是?bat、cmd腳本文件,則啟動的是cmd.exe進程,腳本文件作為命令行參數(shù)
若是DOS的exe、com文件,啟動ntvdm.exe??v86進程,原文件作為命令行參數(shù)
若是posix、os2文件,啟動對應的子系統(tǒng)服務進程
2、?創(chuàng)建、初始化進程對象;創(chuàng)建初始化地址空間;加載映射exe和ntdll文件;分配一個PEB
3、?創(chuàng)建、初始化主線程對象;創(chuàng)建TEB;構造初始的運行環(huán)境(內(nèi)核初始棧幀)
4、?通知windows子系統(tǒng)(csrss.exe進程)新進程創(chuàng)建事件(csrss.exe進程含有絕大多數(shù)進程的句柄)
這樣,進程、主線程都創(chuàng)建起來了,只需等待得到cpu調(diào)度便可投入運行。
-------------------------------------------------------------------------------------
現(xiàn)在具體看一下CreateProcess的創(chuàng)建過程,它會在內(nèi)部直接轉調(diào)CreateProcessInternalW函數(shù)
BOOL
CreateProcessInternalW(HANDLE?hToken,//暫時無用
???????????????????????LPCWSTR?lpApplicationName,//程序文件名
???????????????????????LPWSTR?lpCommandLine,//命令行
???????????????????????LPSECURITY_ATTRIBUTES?lpProcessAttributes,//SD
???????????????????????LPSECURITY_ATTRIBUTES?lpThreadAttributes,//SD
???????????????????????BOOL?bInheritHandles,//是否繼承父進程句柄表中的那些可繼承句柄
???????????????????????DWORD?dwCreationFlags,
???????????????????????LPVOID?lpEnvironment,//環(huán)境變量塊
???????????????????????LPCWSTR?lpCurrentDirectory,//指定給子進程的當前目錄
???????????????????????LPSTARTUPINFOW?lpStartupInfo,//附加啟動信息
???????????????????????LPPROCESS_INFORMATION?lpProcessInformation,//返回創(chuàng)建結果
???????????????????????PHANDLE?hNewToken)//暫時無用
{
????BOOLEAN?CmdLineIsAppName?=?FALSE;//表示文件名是否就是命令行
????UNICODE_STRING?ApplicationName?=?{?0,?0,?NULL?};
????HANDLE?hSection?=?NULL,?hProcess?=?NULL,?hThread?=?NULL,?hDebug?=?NULL;
????LPWSTR?CurrentDirectory?=?NULL;
????PPEB?OurPeb?=?NtCurrentPeb();//當前進程即父進程的peb
????SIZE_T?EnvSize?=?0;//環(huán)境變量塊的大小
????//檢查下面的‘映像劫持’鍵,略
????{HKLM\SOFTWARE\Microsoft\Windows?NT\CurrentVersion\Image?File?Execution?Options}
????if?((dwCreationFlags?&?(DETACHED_PROCESS?|?CREATE_NEW_CONSOLE))?==
????????(DETACHED_PROCESS?|?CREATE_NEW_CONSOLE))
????{
????????SetLastError(ERROR_INVALID_PARAMETER);//這倆標志不可同時使用
????????return?FALSE;
????}
????StartupInfo?=?*lpStartupInfo;
????RtlZeroMemory(lpProcessInformation,?sizeof(PROCESS_INFORMATION));
PriorityClass.Foreground?=?FALSE;//初始創(chuàng)建的進程作為后臺進程看待
//根據(jù)創(chuàng)建標志計算對應的優(yōu)先級類別(一種優(yōu)先級類對應一種基本優(yōu)先級)
????PriorityClass.PriorityClass?=?(UCHAR)BasepConvertPriorityClass(dwCreationFlags);
GetAppName:
????if?(!lpApplicationName)//很常見
????{
????????NameBuffer?=?RtlAllocateHeap(RtlGetProcessHeap(),0,MAX_PATH?*?sizeof(WCHAR));
????????lpApplicationName?=?lpCommandLine;
????????處理NameBuffer,略
????????lpApplicationName?=?NameBuffer;//?最終獲得命令行中包含的應用程序文件名
????}
????else?if?(!lpCommandLine?||?*lpCommandLine?==?UNICODE_NULL)
????{
????????CmdLineIsAppName?=?TRUE;
????????lpCommandLine?=?(LPWSTR)lpApplicationName;
????}
????//事實上只是為程序文件創(chuàng)建一個section,等待映射(函數(shù)名有誤導)
????Status?=?BasepMapFile(lpApplicationName,?&hSection,?&ApplicationName);
????if?(!NT_SUCCESS(Status))
????{
????????If(是一個bat批腳本文件)
???????????命令行改為“cmd?/c?bat文件名”,goto?GetAppName,重新解析
????????Else?…
????}
????if?(!StartupInfo.lpDesktop)//繼承父進程的桌面
????????StartupInfo.lpDesktop?=?OurPeb->ProcessParameters->DesktopInfo.Buffer;
???//查詢section對象的映像文件信息
????Status?=?ZwQuerySection(hSection,SectionImageInformation,
????????????????????????????&SectionImageInfo,sizeof(SectionImageInfo),NULL);
????if?(SectionImageInfo.ImageCharacteristics?&?IMAGE_FILE_DLL)??失敗返回;
????if?(IMAGE_SUBSYSTEM_WINDOWS_GUI?==?SectionImageInfo.SubSystemType)
????{
????????dwCreationFlags?&=?~CREATE_NEW_CONSOLE;//GUI程序無需控制臺
????????dwCreationFlags?|=?DETACHED_PROCESS;
????}
????ObjectAttributes?=?BasepConvertObjectAttributes(&LocalObjectAttributes,
????????????????????????????????????????????????????lpProcessAttributes,NULL);
//if創(chuàng)建的是一個要被當前線程調(diào)試的子進程
????if?(dwCreationFlags?&?(DEBUG_PROCESS?|?DEBUG_ONLY_THIS_PROCESS))
????{
????????Status?=?DbgUiConnectToDbg();//連接到
????????hDebug?=?DbgUiGetThreadDebugObject();//為當前線程創(chuàng)建一個調(diào)試端口(用來父子進程通信)
????}
????//關鍵。調(diào)用系統(tǒng)服務,創(chuàng)建內(nèi)核中的進程對象,并初始化其地址空間等N多內(nèi)容
????Status?=?NtCreateProcess(&hProcess,PROCESS_ALL_ACCESS,ObjectAttributes,
?????????????????????????????NtCurrentProcess(),bInheritHandles,hSection,
?????????????????????????????hDebug,);?//當前線程的調(diào)試端口(將傳給子進程)
????//設置進程的優(yōu)先級類別
????if?(PriorityClass.PriorityClass?!=?PROCESS_PRIORITY_CLASS_INVALID)
????{
????????Status?=?NtSetInformationProcess(hProcess,ProcessPriorityClass,
?????????????????????????????????????????&PriorityClass,sizeof(PROCESS_PRIORITY_CLASS));
????}
????Status?=?NtQueryInformationProcess(hProcess,ProcessBasicInformation,&ProcessBasicInfo,
???????????????????????????????????????sizeof(ProcessBasicInfo),NULL);
????if(lpEnvironment?&&?!(dwCreationFlags?&?CREATE_UNICODE_ENVIRONMENT))
????????lpEnvironment?=?BasepConvertUnicodeEnvironment(&EnvSize,?lpEnvironment);
RemotePeb?=?ProcessBasicInfo.PebBaseAddress;//子進程的peb地址(實際上是固定的)
//關鍵。創(chuàng)建子進程的參數(shù)塊和環(huán)境變量塊
????Status?=?BasepInitializeEnvironment(hProcess,RemotePeb,lpApplicationName,
????????????????????????????????????????CurrentDirectory,lpCommandLine,
????????????????????????????????????????lpEnvironment,EnvSize,//環(huán)境變量塊的地址、長度
????????????????????????????????????????&StartupInfo,dwCreationFlags,bInheritHandles);
????//如果沒有顯式指定這三個標準句柄給子進程,就繼承父進程中的那3個標準句柄(最常見)
????if?(!bInheritHandles?&&?!(StartupInfo.dwFlags?&?STARTF_USESTDHANDLES)?&&
????????SectionImageInfo.SubSystemType?==?IMAGE_SUBSYSTEM_WINDOWS_CUI)
????{
????????PRTL_USER_PROCESS_PARAMETERS?RemoteParameters;
????????Status?=?NtReadVirtualMemory(hProcess,&RemotePeb->ProcessParameters,
?????????????????????????????????????&RemoteParameters,sizeof(PVOID),NULL);
????????BasepDuplicateAndWriteHandle(hProcess,OurPeb->ProcessParameters->StandardInput,
?????????????????????????????????????&RemoteParameters->StandardInput);
????????BasepDuplicateAndWriteHandle(hProcess,OurPeb->ProcessParameters->StandardOutput,
?????????????????????????????????????&RemoteParameters->StandardOutput);
????????BasepDuplicateAndWriteHandle(hProcess,OurPeb->ProcessParameters->StandardError,
?????????????????????????????????????&RemoteParameters->StandardError);
????}
????//通知csrss.exe進程,一個新的進程已創(chuàng)建
????Status?=?BasepNotifyCsrOfCreation(dwCreationFlags,ProcessBasicInfo.UniqueProcessId,
??????????????????????????????????????bInheritHandles);
--------------------------------------華麗的分割線---------------------------------
//至此,已創(chuàng)建好了進程,接下來創(chuàng)建該進程中的第一個線程(主線程)
????//這個函數(shù)創(chuàng)建主線程的用戶棧、內(nèi)核棧,并建立起初始的運行環(huán)境(內(nèi)核棧幀)
????hThread?=?BasepCreateFirstThread(hProcess,lpThreadAttributes,&SectionImageInfo,
?????????????????????????????????????&ClientId);//返回線程的pid.tid
????if?(!(dwCreationFlags?&?CREATE_SUSPENDED))
????????NtResumeThread(hThread,?&Dummy);//恢復線程,掛入就緒隊列(即可以開始運行這個線程了)
????lpProcessInformation->dwProcessId?=?(DWORD)ClientId.UniqueProcess;
????lpProcessInformation->dwThreadId?=?(DWORD)ClientId.UniqueThread;
????lpProcessInformation->hProcess?=?hProcess;
????lpProcessInformation->hThread?=?hThread;
????return?TRUE;
}
NTSTATUS
BasepMapFile(IN?LPCWSTR?lpApplicationName,OUT?PHANDLE?hSection,
IN?PUNICODE_STRING?ApplicationName)
{
RelativeName.Handle?=?NULL;
//轉為NT路徑格式
????RtlDosPathNameToNtPathName_U(lpApplicationName,ApplicationName,NULL,&RelativeName)
????if?(RelativeName.DosPath.Length)
????????ApplicationName?=?&RelativeName.DosPath;
????InitializeObjectAttributes(&ObjectAttributes,ApplicationName,OBJ_CASE_INSENSITIVE,
???????????????????????????????RelativeName.Handle,NULL);
????//打開程序文件
????Status?=?NtOpenFile(&hFile,SYNCHRONIZE?|?FILE_EXECUTE?|?FILE_READ_DATA,&ObjectAttributes,
????????????????????????&IoStatusBlock,FILE_SHARE_DELETE?|?FILE_SHARE_READ,
????????????????????????FILE_SYNCHRONOUS_IO_NONALERT?|?FILE_NON_DIRECTORY_FILE);
???//為文件創(chuàng)建一個公共section,等候映射(多個進程可以同用一個exe文件)
Status?=?NtCreateSection(hSection,SECTION_ALL_ACCESS,NULL,NULL,PAGE_EXECUTE,
SEC_IMAGE,hFile);
????NtClose(hFile);
????return?Status;
}
如上,這個函數(shù)的名字有誤導,其實只是創(chuàng)建一個section,并沒有立即映射到地址空間(多個進程可以共享同一程序文件的)
當用戶線程從內(nèi)核的KiThreadStartup運行起來后,進入PspUserThreadStartup,最后回到用戶空間的總入口處(主線程的用戶空間根是BaseProcessStartThunk,其他線程的用戶空間根是BaseThreadStartThunk)繼續(xù)運行。不過前文講了,在正式從內(nèi)核回到用戶空間的的總入口前,會掃描執(zhí)行中途插入的APC函數(shù),做完附加的APC工作后才從總入口處繼續(xù)運行。插入的這個APC函數(shù)是LdrInitializeThunk,它的主要工作是負責加載exe文件依賴的所有動態(tài)庫以及其他工作。
LdrInitializeThunk()??//APC
{
???Lea?eax,[esp+16]
???Mov?[esp+4],eax??//第一個參數(shù)=Context*
???Xor?ebp,ebp
Jmp?LdrpInit???//實際的工作
}
VOID??LdrpInit(PCONTEXT?Context,PVOID?SystemArgument1,PVOID?SystemArgument2)??//APC
{
????if?(!LdrpInitialized)//if?主線程
????{
????????LdrpInit2(Context,?SystemArgument1,?SystemArgument2);
????????LdrpInitialized?=?TRUE;
????}
????LdrpAttachThread();//各線程創(chuàng)建后都會通知進程中的所有模塊一個ThreadAttach消息
}
看看主線程的初始化工作,也即進程的初始化工作,如下:
VOID??LdrpInit2(PCONTEXT?Context,PVOID?SystemArgument1,PVOID?SystemArgument2)
{
????PPEB?Peb?=?NtCurrentPeb();//現(xiàn)在就是子進程的peb啦(在子進程的地址空間中)
????PVOID?BaseAddress?=?SystemArgument1;//ntdll模塊的地址
????ImageBase?=?Peb->ImageBaseAddress;
????PEDosHeader?=?(PIMAGE_DOS_HEADER)?ImageBase;
????if?(PEDosHeader->e_magic?!=?IMAGE_DOS_SIGNATURE?||?PEDosHeader->e_lfanew?==?0L?||
????????*(PULONG)((PUCHAR)ImageBase?+?PEDosHeader->e_lfanew)?!=?IMAGE_NT_SIGNATURE)
{
????//驗證PE文件簽名
????????ZwTerminateProcess(NtCurrentProcess(),?STATUS_INVALID_IMAGE_FORMAT);
????}
????RtlNormalizeProcessParams(Peb->ProcessParameters);
????NTHeaders?=?(PIMAGE_NT_HEADERS)((ULONG_PTR)ImageBase?+?PEDosHeader->e_lfanew);
????Status?=?ZwQuerySystemInformation(SystemBasicInformation,&SystemInformation,
??????????????????????????????????????sizeof(SYSTEM_BASIC_INFORMATION),NULL);
????Peb->NumberOfProcessors?=?SystemInformation.NumberOfProcessors;
RtlInitializeHeapManager();
//創(chuàng)建進程的默認堆
????Peb->ProcessHeap?=?RtlCreateHeap(HEAP_GROWABLE,NULL,
?????????????????????????????????????NTHeaders->OptionalHeader.SizeOfHeapReserve,//一般為0
?????????????????????????????????????NTHeaders->OptionalHeader.SizeOfHeapCommit,//一般為4kb
?????????????????????????????????????NULL,NULL);
????RtlpInitializeVectoredExceptionHandling();//初始化向量化異常
????RtlInitializeCriticalSection(&PebLock);
Peb->FastPebLock?=?&PebLock;
//初始化peb中內(nèi)置的動態(tài)tls位圖
????Peb->TlsBitmap?=?&TlsBitMap;
????Peb->TlsExpansionBitmap?=?&TlsExpansionBitMap;
????Peb->TlsExpansionCounter?=?64;
????RtlInitializeBitMap(&TlsBitMap,?Peb->TlsBitmapBits,64);//固定指向內(nèi)置的那個64位tls位圖
RtlInitializeBitMap(&TlsExpansionBitMap,?Peb->TlsExpansionBitmapBits,1024);
//初始化回調(diào)表
????Peb->KernelCallbackTable?=?RtlAllocateHeap(RtlGetProcessHeap(),0,sizeof(PVOID)?*
????????????????????????????????????????????????(USER32_CALLBACK_MAXIMUM?+?1));
????RtlInitializeCriticalSection(&LoaderLock);
????Peb->LoaderLock?=?&LoaderLock;
????//從默認堆中分配一個加載信息塊
????Peb->Ldr?=?(PPEB_LDR_DATA)?RtlAllocateHeap(Peb->ProcessHeap,0,sizeof(PEB_LDR_DATA));
????Peb->Ldr->Length?=?sizeof(PEB_LDR_DATA);
????Peb->Ldr->Initialized?=?FALSE;//表示尚未完成初始化(也即尚未完成加載dll等工作)
????Peb->Ldr->SsHandle?=?NULL;
????InitializeListHead(&Peb->Ldr->InLoadOrderModuleList);//加載順序的模塊表
InitializeListHead(&Peb->Ldr->InMemoryOrderModuleList);//內(nèi)存地址順序的模塊表
//初始化順序模塊表,初始化順序與加載順序相反(最底層的dll最先得到初始化)
????InitializeListHead(&Peb->Ldr->InInitializationOrderModuleList);?
????LoadImageFileExecutionOptions(Peb);
ExeModule?=?RtlAllocateHeap(Peb->ProcessHeap,0,sizeof(LDR_DATA_TABLE_ENTRY));
????ExeModule->DllBase?=?Peb->ImageBaseAddress;
????RtlCreateUnicodeString(&ExeModule->FullDllName,
Peb->ProcessParameters->ImagePathName.Buffer);
????RtlCreateUnicodeString(&ExeModule->BaseDllName,
????????????????????????????????wcsrchr(ExeModule->FullDllName.Buffer,?L'\\')?+?1);
????ExeModule->Flags?=?LDRP_ENTRY_PROCESSED;//exe模塊沒有dll標志
????ExeModule->LoadCount?=?-1;//標記為無法動態(tài)卸載
????ExeModule->TlsIndex?=?-1;ExeModule->SectionPointer?=?NULL;ExeModule->CheckSum?=?0;
????NTHeaders?=?RtlImageNtHeader(ExeModule->DllBase);
????ExeModule->SizeOfImage?=?LdrpGetResidentSize(NTHeaders);
ExeModule->TimeDateStamp?=?NTHeaders->FileHeader.TimeDateStamp;
//先插入exe文件的模塊描述符
????InsertTailList(&Peb->Ldr->InLoadOrderModuleList,&ExeModule->InLoadOrderLinks);
????wcscpy(FullNtDllPath,?SharedUserData->NtSystemRoot);//一般為C:\Windows
????wcscat(FullNtDllPath,?L"\\system32\\ntdll.dll");
????NtModule?=?(PLDR_DATA_TABLE_ENTRY)
????????????????RtlAllocateHeap(Peb->ProcessHeap,0,sizeof(LDR_DATA_TABLE_ENTRY));
????memset(NtModule,?0,?sizeof(LDR_DATA_TABLE_ENTRY));
????NtModule->DllBase?=?BaseAddress;
????NtModule->EntryPoint?=?0;???
????RtlCreateUnicodeString(&NtModule->FullDllName,?FullNtDllPath);
????RtlCreateUnicodeString(&NtModule->BaseDllName,?L"ntdll.dll");
????NtModule->Flags?=?LDRP_IMAGE_DLL?|?LDRP_ENTRY_PROCESSED;
NtModule->LoadCount?=?-1;//標記無法動態(tài)卸載ntdll.dll
NtModule->TlsIndex?=?-1;NtModule->SectionPointer?=?NULL;NtModule->CheckSum?=?0;
????NTHeaders?=?RtlImageNtHeader(NtModule->DllBase);
????NtModule->SizeOfImage?=?LdrpGetResidentSize(NTHeaders);
????NtModule->TimeDateStamp?=?NTHeaders->FileHeader.TimeDateStamp;
????//再插入ntdll文件的模塊描述符
????InsertTailList(&Peb->Ldr->InLoadOrderModuleList,&NtModule->InLoadOrderLinks);
????InsertTailList(&Peb->Ldr->InInitializationOrderModuleList,
&NtModule->InInitializationOrderModuleList);//NTDLL不依賴其他庫
????LdrpInitLoader();//獲取“\KnownDlls\KnownDllPath”路徑
????//PE加載器的核心函數(shù),用來執(zhí)行模塊的重定位、加載導入庫、處理tls
????ExeModule->EntryPoint?=?LdrPEStartup(ImageBase,?NULL,?NULL,?NULL);
????Peb->Ldr->Initialized?=?TRUE;//標志該進程的所有dll都已加載完成
????if?(Peb->BeingDebugged)
????????DbgBreakPoint();//int?3通知調(diào)試器,?首次觸發(fā)調(diào)試中斷
}
進程初始時的重點工作就是加載exe文件依賴的所有子孫dll,由下面的函數(shù)完成這項工作
(注意這個函數(shù)專用來啟動初始化進程的主exe文件,是啟動階段的核心函數(shù))
PEPFUNC?LdrPEStartup?(PVOID??ImageBase,//exe文件的內(nèi)存地址(進程的主exe文件)
??????????????????????HANDLE?SectionHandle,
??????????????????????PLDR_DATA_TABLE_ENTRY*?Module,
??????????????????????PWSTR?FullDosName)
{
???PEPFUNC???EntryPoint?=?NULL;
???DosHeader?=?(PIMAGE_DOS_HEADER)?ImageBase;
???NTHeaders?=?(PIMAGE_NT_HEADERS)?((ULONG_PTR)ImageBase?+?DosHeader->e_lfanew);
???//if?實際加載地址與pe頭中的預期加載地址不同,執(zhí)行重定位工作(常見于dll文件,exe文件也可能)
???if?(ImageBase?!=?(PVOID)?NTHeaders->OptionalHeader.ImageBase)
???????Status?=?LdrPerformRelocations(NTHeaders,?ImageBase);//遍歷.reloc節(jié)中項目,執(zhí)行重定位
???if?(Module?!=?NULL)//也即if?是dll文件(事實上這個條件永不滿足)
???{
???????*Module?=?LdrAddModuleEntry(ImageBase,?NTHeaders,?FullDosName);//加入加模塊載順序鏈表
???????(*Module)->SectionPointer?=?SectionHandle;
???}
???Else?//也即進程的主exe文件,這才是正題
???{
???????Module?=?&tmpModule;
???????Status?=?LdrFindEntryForAddress(ImageBase,?Module);//直接在模塊表中查找
???}
???if?(ImageBase?!=?(PVOID)?NTHeaders->OptionalHeader.ImageBase)
???????(*Module)->Flags?|=?LDRP_IMAGE_NOT_AT_BASE;
???Status?=?RtlAllocateActivationContextStack(&ActivationContextStack);
???if?(NT_SUCCESS(Status))
???{
??????NtCurrentTeb()->ActivationContextStackPointer?=?ActivationContextStack;
??????NtCurrentTeb()->ActivationContextStackPointer->ActiveFrame?=?NULL;
???}
???Status?=?LdrFixupImports(NULL,?*Module);//加載子孫dll,修正IAT導入表
???Status?=?LdrpInitializeTlsForProccess();//初始化進程的靜態(tài)tls,詳見后文
???if?(NT_SUCCESS(Status))
???{
??????LdrpAttachProcess();//發(fā)送一個ProcessAttach消息,調(diào)用該模塊的DllMain函數(shù)
??????LdrpTlsCallback(*Module,?DLL_PROCESS_ATTACH);
???}
???if?(NTHeaders->OptionalHeader.AddressOfEntryPoint?!=?0)
??????EntryPoint?=?(ULONG_PTR)ImageBase+?NTHeaders->OptionalHeader.AddressOfEntryPoint;
???return?EntryPoint;//返回oep
}
下面的函數(shù)加載指定模塊依賴的所有子孫dll
NTSTATUS
LdrFixupImports(IN?PWSTR?SearchPath?OPTIONAL,//自定義的dll搜索路徑(不提供的話就使用標準路徑)
????????????????IN?PLDR_DATA_TABLE_ENTRY?Module)//指定模塊
{
???ULONG?TlsSize?=?0;
???NTSTATUS?Status?=?STATUS_SUCCESS;
???//獲取tls目錄
???TlsDirectory?=?(PIMAGE_TLS_DIRECTORY)RtlImageDirectoryEntryToData(Module->DllBase,
???????????????????TRUE,IMAGE_DIRECTORY_ENTRY_TLS,&Size);
???if?(TlsDirectory)
???{
???????TlsSize?=?TlsDirectory->EndAddressOfRawData-?TlsDirectory->StartAddressOfRawData
???????????????????+?TlsDirectory->SizeOfZeroFill;
???????if?(TlsSize?>?0?&&?NtCurrentPeb()->Ldr->Initialized)//if?動態(tài)加載該模塊
???????????TlsDirectory?=?NULL;//?動態(tài)加載的模塊不支持靜態(tài)tls
???}
???ImportModuleDirectory?=?(PIMAGE_IMPORT_DESCRIPTOR)
???????????????????????????RtlImageDirectoryEntryToData(Module->DllBase,
??????????????????????????????????????????????TRUE,IMAGE_DIRECTORY_ENTRY_IMPORT,&Size);
???BoundImportDescriptor?=?(PIMAGE_BOUND_IMPORT_DESCRIPTOR)
???????????????????????????RtlImageDirectoryEntryToData(Module->DllBase,TRUE,
??????????????????????????????????????????????MAGE_DIRECTORY_ENTRY_BOUND_IMPORT,&Size);
???if?(BoundImportDescriptor?!=?NULL?&&?ImportModuleDirectory?==?NULL)
???????return?STATUS_UNSUCCESSFUL;
???if?(BoundImportDescriptor)??處理綁定導入表,略
???else?if?(ImportModuleDirectory)
???{
???????ImportModuleDirectoryCurrent?=?ImportModuleDirectory;//當前依賴的模塊
???????while?(ImportModuleDirectoryCurrent->Name)//遍歷IMT導入模塊表中的各個依賴模塊
???????{
???????????ImportedName?=?Module->DllBase?+?ImportModuleDirectoryCurrent->Name;//模塊名
???????????if?(SearchPath?==?NULL)?//如果沒提供自定義搜索路徑,就構造一個標準搜索路徑
???????????{
????????????????//標準搜索路徑是:exe文件目錄;當前目錄;Sytem32目錄;Windows目錄;Path環(huán)境變量
????????????????ModulePath?=?LdrpQueryAppPaths(Module->BaseDllName.Buffer);
????????????????Status?=?LdrpGetOrLoadModule(ModulePath,?ImportedName,?&ImportedModule,?TRUE);
????????????????if?(NT_SUCCESS(Status))?goto?Success;
???????????}
???????????//在模塊加載表中查找該模塊或者加載該模塊(找不到就加載)
???????????Status?=?LdrpGetOrLoadModule(SearchPath,?ImportedName,?&ImportedModule,?TRUE);
Success:
???????????//處理該依賴模塊的IAT導入地址表(獲取各個導入函數(shù)的實際地址,填到IAT對應的表項中)
???????????Status?=?LdrpProcessImportDirectoryEntry(Module,?ImportedModule,?ImportModuleDirectoryCurrent);
???????????ImportModuleDirectoryCurrent++;//下一個依賴的模塊
???????}
???}
???if?(TlsDirectory?&&?TlsSize?>?0)
???????LdrpAcquireTlsSlot(Module,?TlsSize,?FALSE);
???return?STATUS_SUCCESS;
}
NTSTATUS??LdrpGetOrLoadModule(PWCHAR?SearchPath,//搜索路徑
??????????????????????????????PCHAR?Name,//模塊名,是ASC形式
??????????????????????????????PLDR_DATA_TABLE_ENTRY*?Module,
??????????????????????????????BOOLEAN?Load)//指找不到的話,是否加載
{
???RtlInitAnsiString(&AnsiDllName,?Name);
???Status?=?RtlAnsiStringToUnicodeString(&DllName,?&AnsiDllName,?TRUE);
???Status?=?LdrFindEntryForName?(&DllName,?Module,?Load);
???if?(Load?&&?!NT_SUCCESS(Status))
???{
???????Status?=?LdrpLoadModule(SearchPath,0,&DllName,Module,NULL);
???????if?(NT_SUCCESS(Status))
???????????Status?=?LdrFindEntryForName?(&DllName,?Module,?FALSE);
???}
???return?Status;
}
之所以要在加載模塊表中查找,找不到才加載,是因為避免同一個模塊加載兩次。下面的函數(shù)用來加載一個模塊。(注意這個函數(shù)也供LoadLibrary?API內(nèi)部間接調(diào)用)
NTSTATUS
LdrpLoadModule(IN?PWSTR?SearchPath?OPTIONAL,
???IN?ULONG?LoadFlags,
???IN?PUNICODE_STRING?Name,//模塊名
???PLDR_DATA_TABLE_ENTRY?*Module,
???PVOID?*BaseAddress?OPTIONAL)//返回實際加載的地址
{
if?(Module?==?NULL)
Module?=?&tmpModule;
LdrAdjustDllName(&AdjustedName,?Name,?FALSE);
MappedAsDataFile?=?FALSE;
Status?=?LdrFindEntryForName(&AdjustedName,?Module,?TRUE);//仍要先查找
if?(NT_SUCCESS(Status))
{
if?(NULL?!=?BaseAddress)
*BaseAddress?=?(*Module)->DllBase;
}
else
{
?????????//先嘗試在\KnownDlls對象目錄中查找該dll文件的section對象
Status?=?LdrpMapKnownDll(&AdjustedName,?&FullDosName,?&SectionHandle);
if?(!NT_SUCCESS(Status))//若找不到,則為該dll文件創(chuàng)建一個映像文件section
{
MappedAsDataFile?=?(0?!=?(LoadFlags?&?LOAD_LIBRARY_AS_DATAFILE));
??????????????//內(nèi)部會調(diào)用NtCreateSection系統(tǒng)服務,創(chuàng)建一個section
Status?=?LdrpMapDllImageFile(SearchPath,?&AdjustedName,?&FullDosName,
????????????????????????MappedAsDataFile,?&SectionHandle);
}
ViewSize?=?0;//表示映射整個dll文件
ImageBase?=?0;//表示不指定映射的地址
ArbitraryUserPointer?=?NtCurrentTeb()->NtTib.ArbitraryUserPointer;
NtCurrentTeb()->NtTib.ArbitraryUserPointer?=?FullDosName.Buffer;
Status?=?NtMapViewOfSection(SectionHandle,NtCurrentProcess(),
&ImageBase,0,0,NULL,&ViewSize,ViewShare,0,…);
NtCurrentTeb()->NtTib.ArbitraryUserPointer?=?ArbitraryUserPointer;
if?(NULL?!=?BaseAddress)
*BaseAddress?=?ImageBase;
if?(MappedAsDataFile)//dll可以當做純數(shù)據(jù)文件加載
{
if?(NULL?!=?BaseAddress)
*BaseAddress?=?(PVOID)?((char?*)?*BaseAddress?+?1);//復用標志
*Module?=?NULL;
return?STATUS_SUCCESS;
}
?????????//if?實際加載映射的地址與該dll期望的加載地址不同,執(zhí)行重定位
if?(ImageBase?!=?(PVOID)?NtHeaders->OptionalHeader.ImageBase)
Status?=?LdrPerformRelocations(NtHeaders,?ImageBase);
*Module?=?LdrAddModuleEntry(ImageBase,?NtHeaders,?FullDosName.Buffer);
(*Module)->SectionPointer?=?SectionHandle;
if?(ImageBase?!=?(PVOID)?NtHeaders->OptionalHeader.ImageBase)
(*Module)->Flags?|=?LDRP_IMAGE_NOT_AT_BASE;
if?(NtHeaders->FileHeader.Characteristics?&?IMAGE_FILE_DLL)
(*Module)->Flags?|=?LDRP_IMAGE_DLL;
?????????//又加載該dll本身依賴的所有其他dll,修正它的導入表
Status?=?LdrFixupImports(SearchPath,?*Module);
?????????//當所有子孫dll初始化完后,自己才初始化完畢,此時才加入初始化順序鏈表中
InsertTailList(&NtCurrentPeb()->Ldr->InInitializationOrderModuleList,
??????????&(*Module)->InInitializationOrderModuleList);
}
return?STATUS_SUCCESS;
}
至此,進程啟動時的初始化工作已經(jīng)初始完畢。Exe文件及其依賴的所有dll以及tls工作最終都完成處理了,這時候該APC函數(shù)將返回。這個LdrInitializeThunk要返回哪里呢?答案是返回到內(nèi)核,然后才恢復用戶寄存器現(xiàn)場,正式退回用戶空間,執(zhí)行主線程的用戶空間總入口函數(shù)BaseProcessStartThunk,換句話說,當程序流執(zhí)行到BaseProcessStartThunk這個函數(shù)時,進程已初始化,各dll已完成加載。此時,萬事俱備,只欠東風了,線程可以放馬在用戶空間執(zhí)行了。
_BaseProcessStartThunk@0://主線程的用戶空間總入口(內(nèi)核總入口是KiThreadStartup)
{
????xor?ebp,?ebp???
????push?eax??????????????????//oep
????push?0????????????????????//表示不會返回
jmp?_BaseProcessStartup@4
}
__declspec(noreturn)?)//主線程的入口
VOID??BaseProcessStartup(PPROCESS_START_ROUTINE?lpStartAddress?
{
????UINT?uExitCode?=?0;
????_SEH2_TRY?//放在try塊中保護
????{
????????NtSetInformationThread(NtCurrentThread(),ThreadQuerySetWin32StartAddress,
???????????????????????????????&lpStartAddress,sizeof(PPROCESS_START_ROUTINE));
????????//lpStartAddress即oep,一般就是WinMainCRTStartup/MainCRTStartup
????????uExitCode?=?(lpStartAddress)();//轉去oep
????}
????_SEH2_EXCEPT(BaseExceptionFilter(_SEH2_GetExceptionInformation()))
????{
????????uExitCode?=?_SEH2_GetExceptionCode();
????}
????_SEH2_END;
????ExitProcess(uExitCode);//當WinMain函數(shù)正常退出后,進程才退出
}
-------------------------------------------------------------------------------------
_BaseThreadStartupThunk@0:?//一般普通線程的用戶空間總入口(內(nèi)核總入口是KiThreadStartup)
{
????xor?ebp,?ebp??
????push?ebx??????????????????//用戶自己的context*參數(shù)
????push?eax??????????????????//用戶自己的線程入口函數(shù)
????push?0????????????????????//表示不會返回
jmp?_BaseThreadStartup@8
}
__declspec(noreturn)??//一般普通線程的入口
VOID??BaseThreadStartup(LPTHREAD_START_ROUTINE?lpStartAddress,//用戶自己的線程入口函數(shù)
????????????????????????LPVOID?lpParameter)//用戶自己函數(shù)的context*
{
????volatile?UINT?uExitCode?=?0;
????_SEH2_TRY?//也置于try塊中保護
????{
????????uExitCode?=?(lpStartAddress)((PVOID)lpParameter);
????}
????_SEH2_EXCEPT(BaseThreadExceptionFilter(_SEH2_GetExceptionInformation()))
????{
????????uExitCode?=?_SEH2_GetExceptionCode();
????}?_SEH2_END;
????ExitThread(uExitCode);//用戶自己的線程入口函數(shù)返回后,線程自然退出
}
注:
Dll可以在進程啟動初期,被PE加載器靜態(tài)加載外,程序員也可以調(diào)用LoadLibrary?API顯式的動態(tài)加載。看一下這個函數(shù)的原理,實際上這個函數(shù)不是API,是個宏,指向LoadLibraryW/LoadLibraryA。
HINSTANCE?LoadLibraryW?(LPCWSTR lpLibFileName)
{
return?LoadLibraryExW?(lpLibFileName,?0,?0);
}
HINSTANCE
LoadLibraryExW?(
LPCWSTR lpLibFileName,
HANDLE hFile,
DWORD dwFlags
)
{
????if?(dwFlags?&?DONT_RESOLVE_DLL_REFERENCES)
????????DllCharacteristics?=?IMAGE_FILE_EXECUTABLE_IMAGE;
dwFlags?&=?LOAD_WITH_ALTERED_SEARCH_PATH;
SearchPath?=?GetDllLoadPath(lpLibFileName);//構造該dll的標準搜索路徑
RtlInitUnicodeString(&DllName,?(LPWSTR)lpLibFileName);
????if?(dwFlags?&?LOAD_LIBRARY_AS_DATAFILE)
{
????//在加載模塊表中查找該dll
????????Status?=?LdrGetDllHandle(SearchPath,?NULL,?&DllName,?(PVOID*)&hInst);
????????if?(!NT_SUCCESS(Status))//若找不到
????????{
????????????Status?=?LoadLibraryAsDatafile(SearchPath,?DllName.Buffer,?&hInst);
????????????Return?Status;
????????}
????}
????if?(InWindows)?//Windows中的實現(xiàn)
????????Status?=?LdrLoadDll(SearchPath,&DllCharacteristics,&DllName,?(PVOID*)&hInst);
????Else?//ROS中的實現(xiàn)
????????Status?=?LdrLoadDll(SearchPath,?&dwFlags,?&DllName,?(PVOID*)&hInst);
if?(?!NT_SUCCESS(Status))
{
SetLastErrorByStatus?(Status);
return?NULL;
}
return?hInst;
}
//下面的函數(shù)用來從指定dll文件路徑構造一個dll搜索路徑(完整原代碼)
LPWSTR?GetDllLoadPath(LPCWSTR?lpModule)
{
ULONG?Pos?=?0,?Length?=?0;
PWCHAR?EnvironmentBufferW?=?NULL;
LPCWSTR?lpModuleEnd?=?NULL;
UNICODE_STRING?ModuleName;
DWORD?LastError?=?GetLastError();?
if?((lpModule?!=?NULL)?&&?(wcslen(lpModule)?>?2)?&&?(lpModule[1]?==?':'))
lpModuleEnd?=?lpModule?+?wcslen(lpModule);
else
{
ModuleName?=?NtCurrentPeb()->ProcessParameters->ImagePathName;
lpModule?=?ModuleName.Buffer;
lpModuleEnd?=?lpModule?+?(ModuleName.Length?/?sizeof(WCHAR));
}
if?(lpModule?!=?NULL)
{
while?(lpModuleEnd?>?lpModule?&&?*lpModuleEnd?!=?L'/'?&&
???????*lpModuleEnd?!=?L'\\'?&&?*lpModuleEnd?!=?L':')
{
--lpModuleEnd;
}
Length?=?(lpModuleEnd?-?lpModule)?+?1;
}
?????//看到?jīng)],LoadLibrary的dll搜索路徑順序是這樣(注意與靜態(tài)加載時的搜索路徑不同)
Length?+=?GetCurrentDirectoryW(0,?NULL);
Length?+=?GetDllDirectoryW(0,?NULL);
Length?+=?GetSystemDirectoryW(NULL,?0);
Length?+=?GetWindowsDirectoryW(NULL,?0);
Length?+=?GetEnvironmentVariableW(L"PATH",?NULL,?0);
EnvironmentBufferW?=?RtlAllocateHeap(RtlGetProcessHeap(),?0,Length?*?sizeof(WCHAR));
if?(lpModule)
{
RtlCopyMemory(EnvironmentBufferW,?lpModule,?(lpModuleEnd?-?lpModule)?*sizeof(WCHAR));
Pos?+=?lpModuleEnd?-?lpModule;
EnvironmentBufferW[Pos++]?=?L';';
}
Pos?+=?GetCurrentDirectoryW(Length,?EnvironmentBufferW?+?Pos);
EnvironmentBufferW[Pos++]?=?L';';
Pos?+=?GetDllDirectoryW(Length?-?Pos,?EnvironmentBufferW?+?Pos);
EnvironmentBufferW[Pos++]?=?L';';
Pos?+=?GetSystemDirectoryW(EnvironmentBufferW?+?Pos,?Length?-?Pos);
EnvironmentBufferW[Pos++]?=?L';';
Pos?+=?GetWindowsDirectoryW(EnvironmentBufferW?+?Pos,?Length?-?Pos);
EnvironmentBufferW[Pos++]?=?L';';
Pos?+=?GetEnvironmentVariableW(L"PATH",?EnvironmentBufferW?+?Pos,?Length?-?Pos);
SetLastError(LastError);
return?EnvironmentBufferW;
}
NTSTATUS?NTAPI
LdrLoadDll?(IN?PWSTR?SearchPath?OPTIONAL,
IN?PULONG?LoadFlags?OPTIONAL,
IN?PUNICODE_STRING?Name,
OUT?PVOID?*BaseAddress)//也即返回的hModule
{
PPEB?Peb?=?NtCurrentPeb();
Status?=?LdrpLoadModule(SearchPath,?LoadFlags???*LoadFlags?:?0,?Name,?&Module,?BaseAddress);
if?(NT_SUCCESS(Status)?&&?(!LoadFlags?||?0?==?(*LoadFlags?&?LOAD_LIBRARY_AS_DATAFILE)))
{
if?(!(Module->Flags?&?LDRP_PROCESS_ATTACH_CALLED))
Status?=?LdrpAttachProcess();//通知一個ProcessAttach消息
}
*BaseAddress?=?NT_SUCCESS(Status)???Module->DllBase?:?NULL;
return?Status;
}
下面的函數(shù)在每次一個新線程創(chuàng)建時調(diào)用,用以調(diào)用各個模塊的DllMain和tls回調(diào)函數(shù)
NTSTATUS??LdrpAttachThread?(VOID)
{
Status?=?LdrpInitializeTlsForThread();
if?(NT_SUCCESS(Status))
{
ModuleListHead?=?&NtCurrentPeb()->Ldr->InInitializationOrderModuleList;
Entry?=?ModuleListHead->Flink;
while?(Entry?!=?ModuleListHead)//遍歷初始化順序模塊表
{
Module?=?CONTAINING_RECORD(Entry,?LDR_DATA_TABLE_ENTRY,?InInitializationOrderModuleList);
if?(Module->Flags?&?LDRP_PROCESS_ATTACH_CALLED?&&
!(Module->Flags?&?LDRP_DONT_CALL_FOR_THREADS)?&&
!(Module->Flags?&?LDRP_UNLOAD_IN_PROGRESS))
{
//調(diào)用DllMain,注意是DLL_THREAD_ATTACH通知碼
LdrpCallDllEntry(Module,?DLL_THREAD_ATTACH,?NULL);
}
Entry?=?Entry->Flink;
}
Entry?=?NtCurrentPeb()->Ldr->InLoadOrderModuleList.Flink;//exe模塊
Module?=?CONTAINING_RECORD(Entry,?LDR_DATA_TABLE_ENTRY,?InLoadOrderLinks);
LdrpTlsCallback(Module,?DLL_THREAD_ATTACH);
}
return?Status;
}
下面的函數(shù)在主線程創(chuàng)建時調(diào)用,用以調(diào)用各個模塊的DllMain和tls回調(diào)函數(shù)
NTSTATUS?LdrpAttachProcess(VOID)
{
NTSTATUS?Status?=?STATUS_SUCCESS;
ModuleListHead?=?&NtCurrentPeb()->Ldr->InInitializationOrderModuleList;
Entry?=?ModuleListHead->Flink;
while?(Entry?!=?ModuleListHead)
{
Module?=?CONTAINING_RECORD(Entry,?LDR_DATA_TABLE_ENTRY,
?InInitializationOrderModuleList);
if?(!(Module->Flags?&?(LDRP_LOAD_IN_PROGRESS|LDRP_UNLOAD_IN_PROGRESS|
LDRP_ENTRY_PROCESSED)))
{
Module->Flags?|=?LDRP_LOAD_IN_PROGRESS;
??????????????//調(diào)用DllMain,注意是DLL_PROCESS_ATTACH通知碼
Result?=?LdrpCallDllEntry(Module,?DLL_PROCESS_ATTACH,?(Module->LoadCount?==
?LDRP_PROCESS_CREATION_TIME???1?:?0));
if?(Module->Flags?&?LDRP_IMAGE_DLL?&&?Module->EntryPoint?!=?0)
Module->Flags?|=?LDRP_PROCESS_ATTACH_CALLED|LDRP_ENTRY_PROCESSED;
else
Module->Flags?|=?LDRP_ENTRY_PROCESSED;
Module->Flags?&=?~LDRP_LOAD_IN_PROGRESS;
}
Entry?=?Entry->Flink;
}
return?Status;
}
?
總結
以上是生活随笔為你收集整理的windows内核情景分析---进程线程1的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。