转载请您注明出处:http://www.cnblogs.com/lsh123/p/7405796.html

0x01 CreateProcessW

  CreateProcess的使用有ANSI版本的CreateProcessA和UNICODE版本的CreateProcessW:

  不过查看源码就可以发现其实CreateProcessA内部调用的还是CreateProcessW:

BOOL
WINAPI
CreateProcessA(
LPCSTR lpApplicationName,
LPSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCSTR lpCurrentDirectory,
LPSTARTUPINFOA lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation
) /*++ ANSI thunk to CreateProcessW --*/ {
NTSTATUS Status;
PUNICODE_STRING CommandLine;
UNICODE_STRING ApplicationName;
UNICODE_STRING CurrentDirectory;
STARTUPINFOW StartupInfo;
ANSI_STRING AnsiString;
UNICODE_STRING Unicode;
UNICODE_STRING DynamicCommandLine;
UNICODE_STRING NullUnicodeString;
BOOL ReturnStatus; if (ARGUMENT_PRESENT (lpCommandLine)) {
if ( (strlen( lpCommandLine ) + 1) * sizeof( WCHAR ) <
NtCurrentTeb()->StaticUnicodeString.MaximumLength ) { DynamicCommandLine.Buffer = NULL; CommandLine = Basep8BitStringToStaticUnicodeString( lpCommandLine );
if (CommandLine == NULL) {
return FALSE;
}
} else {
if (!Basep8BitStringToDynamicUnicodeString( &DynamicCommandLine,
lpCommandLine )) {
return FALSE;
}
}
} else {
DynamicCommandLine.Buffer = NULL;
CommandLine = &NullUnicodeString;
CommandLine->Buffer = NULL;
} ApplicationName.Buffer = NULL;
ApplicationName.Buffer = NULL;
CurrentDirectory.Buffer = NULL;
RtlMoveMemory(&StartupInfo,lpStartupInfo,sizeof(*lpStartupInfo));
ASSERT(sizeof(StartupInfo) == sizeof(*lpStartupInfo));
StartupInfo.lpReserved = NULL;
StartupInfo.lpDesktop = NULL;
StartupInfo.lpTitle = NULL; try {
try {
if (ARGUMENT_PRESENT(lpApplicationName)) { if (!Basep8BitStringToDynamicUnicodeString( &ApplicationName,
lpApplicationName )) {
ReturnStatus = FALSE;
goto tryexit;
}
} if (ARGUMENT_PRESENT(lpCurrentDirectory)) {
if (!Basep8BitStringToDynamicUnicodeString( &CurrentDirectory,
lpCurrentDirectory )) {
ReturnStatus = FALSE;
goto tryexit;
}
} if (ARGUMENT_PRESENT(lpStartupInfo->lpReserved)) { //
// Win95 does not touch reserved, and Intergraph Voxtel passes
// garbage for this. Handle this by probing lpReserved, and if
// the pointer is bad, ignore it
// try { RtlInitAnsiString(&AnsiString,lpStartupInfo->lpReserved); }
except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION
? EXCEPTION_EXECUTE_HANDLER
: EXCEPTION_CONTINUE_SEARCH) {
goto bail_on_reserved;
} Unicode.MaximumLength = (USHORT)RtlAnsiStringToUnicodeSize(&AnsiString) ;
StartupInfo.lpReserved = RtlAllocateHeap( RtlProcessHeap(),
MAKE_TAG( TMP_TAG ),
Unicode.MaximumLength);
if ( !StartupInfo.lpReserved ) {
BaseSetLastNTError(STATUS_NO_MEMORY);
ReturnStatus = FALSE;
goto tryexit;
}
Unicode.Buffer = StartupInfo.lpReserved;
Status = RtlAnsiStringToUnicodeString(&Unicode,&AnsiString,FALSE);
if ( !NT_SUCCESS(Status) ) {
BaseSetLastNTError(Status);
ReturnStatus = FALSE;
goto tryexit;
}
} bail_on_reserved:
if (ARGUMENT_PRESENT(lpStartupInfo->lpDesktop)) {
RtlInitAnsiString(&AnsiString,lpStartupInfo->lpDesktop);
Unicode.MaximumLength = (USHORT)RtlAnsiStringToUnicodeSize(&AnsiString) ;
StartupInfo.lpDesktop = RtlAllocateHeap( RtlProcessHeap(),
MAKE_TAG( TMP_TAG ),
Unicode.MaximumLength);
if ( !StartupInfo.lpDesktop ) {
BaseSetLastNTError(STATUS_NO_MEMORY);
ReturnStatus = FALSE;
goto tryexit;
}
Unicode.Buffer = StartupInfo.lpDesktop;
Status = RtlAnsiStringToUnicodeString(&Unicode,&AnsiString,FALSE);
if ( !NT_SUCCESS(Status) ) {
BaseSetLastNTError(Status);
ReturnStatus = FALSE;
goto tryexit;
}
} if (ARGUMENT_PRESENT(lpStartupInfo->lpTitle)) {
RtlInitAnsiString(&AnsiString,lpStartupInfo->lpTitle);
Unicode.MaximumLength = (USHORT)RtlAnsiStringToUnicodeSize(&AnsiString) ;
StartupInfo.lpTitle = RtlAllocateHeap( RtlProcessHeap(),
MAKE_TAG( TMP_TAG ),
Unicode.MaximumLength);
if ( !StartupInfo.lpTitle ) {
BaseSetLastNTError(STATUS_NO_MEMORY);
ReturnStatus = FALSE;
goto tryexit;
}
Unicode.Buffer = StartupInfo.lpTitle;
Status = RtlAnsiStringToUnicodeString(&Unicode,&AnsiString,FALSE);
if ( !NT_SUCCESS(Status) ) {
BaseSetLastNTError(Status);
ReturnStatus = FALSE;
goto tryexit;
}
}
}
except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION
? EXCEPTION_EXECUTE_HANDLER
: EXCEPTION_CONTINUE_SEARCH) {
BaseSetLastNTError(GetExceptionCode());
ReturnStatus = FALSE;
goto tryexit;
}
ReturnStatus = CreateProcessW(
ApplicationName.Buffer,
DynamicCommandLine.Buffer ? DynamicCommandLine.Buffer
: CommandLine->Buffer,
lpProcessAttributes,
lpThreadAttributes,
bInheritHandles,
dwCreationFlags,
lpEnvironment,
CurrentDirectory.Buffer,
&StartupInfo,
lpProcessInformation
);
tryexit:;
}
finally {
RtlFreeUnicodeString(&DynamicCommandLine);
RtlFreeUnicodeString(&ApplicationName);
RtlFreeUnicodeString(&CurrentDirectory);
RtlFreeHeap(RtlProcessHeap(), 0,StartupInfo.lpReserved);
RtlFreeHeap(RtlProcessHeap(), 0,StartupInfo.lpDesktop);
RtlFreeHeap(RtlProcessHeap(), 0,StartupInfo.lpTitle);
} return ReturnStatus; }

  CreateProcessW的十个参数:

WINBASEAPI
BOOL
WINAPI
CreateProcessW(
LPCWSTR lpApplicationName, //指向一个NULL结尾的,新进程的可执行文件的名称
LPWSTR lpCommandLine, //指向一个NULL结尾的,传给新进程的命令行字符串
LPSECURITY_ATTRIBUTES lpProcessAttributes,
//指向一个SECURITY_ATTRIBUTES结构体,分配给新的进程对象
//SECURITY_ATTRIBUTES结构可以决定是否返回的句柄可以被子进程继承(bInheritHandle )。如果lpProcessAttributes参数为空(NULL),那么句柄不能被继承。
//SECURITY_ATTRIBUTES结构的lpSecurityDescriptor成员可以指定新进程的安全描述符,如果参数为空,新进程使用默认的安全描述符。
LPSECURITY_ATTRIBUTES lpThreadAttributes,
//指向一个SECURITY_ATTRIBUTES结构体,分配给新的线程对象
BOOL bInheritHandles,
//标识新进程是否可以从调用进程处继承所有可继承的句柄。被继承的句柄与原进程拥有完全相同的值和访问权限。
DWORD dwCreationFlags,
//标识了影响新进程创建方式的标志,多个标志按位或进行组合
LPVOID lpEnvironment,
//指向一块内存,其中包含新进程要使用的环境字符串。如果此参数为空,新进程继承父进程的一组环境字符串。
LPCWSTR lpCurrentDirectory,
//指向一个以NULL结尾的字符串,用来设置新进程的当前驱动器和目录,这个字符串必须是一个包含驱动器名的绝对路径。如果这个参数为空,新进程将使用与父进程相同的驱动器和目录。
LPSTARTUPINFOW lpStartupInfo,
//指向一个用于决定新进程的主窗体如何显示的STARTUPINFO结构体。
LPPROCESS_INFORMATION lpProcessInformation
//指向一个用来接收新进程的识别信息的PROCESS_INFORMATION结构体。
);

  (1)STARTUPINFO 和 PROCESS_INFORMATION的使用前初始化为空:

  STARTUPINFO StartupInfo = { 0 };
  StartupInfo.cb = sizeof(STARTUPINFO);
  PROCESS_INFORMATION ProcessInfo = { 0 };

  (2)第六参数:dwCreationFlags 的部分标志:

  ⑴值:CREATE_DEFAULT_ERROR_MODE
  含义:新的进程不继承调用进程的错误模式。CreateProcess函数赋予新进程当前的默认错误模式作为替代。应用程序可以调用SetErrorMode函数设置当前的默认错误模式。
  这个标志对于那些运行在没有硬件错误环境下的多线程外壳程序是十分有用的。
  对于CreateProcess函数,默认的行为是为新进程继承调用者的错误模式。设置这个标志以改变默认的处理方式。
 
  ⑵值:CREATE_NEW_CONSOLE
  含义:新的进程将使用一个新的控制台,而不是继承父进程的控制台。这个标志不能与DETACHED_PROCESS标志一起使用。
 
  ⑶值:CREATE_NEW_PROCESS_GROUP
  含义:新进程将是一个进程树的根进程。进程树中的全部进程都是根进程的子进程。新进程树的用户标识符与这个进程的标识符是相同的,由lpProcessInformation参数返回。进程树经常使用GenerateConsoleCtrlEvent函数允许发送CTRL+C或   CTRL+BREAK信号到一组控制台进程。
 
  ⑷值:CREATE_SEPARATE_WOW_VDM
  如果被设置,新进程将会在一个私有的虚拟DOS机(VDM)中运行。另外,默认情况下所有的16位Windows应用程序都会在同一个共享的VDM中以线程的方式运行。单独运行一个16位程序的优点是一个应用程序的崩溃只会结束这一个VDM的运行;其他那些在不同VDM中运行的程序会继续正常的运行。同样的,在不同VDM中运行的16位Windows应用程序拥有不同的输入队列,这意味着如果一个程序暂时失去响应,在独立的VDM中的应用程序能够继续获得输入。
 
  ⑸值:CREATE_SHARED_WOW_VDM
如果WIN.INI中的Windows段的DefaultSeparateVDM选项被设置为真,这个标识使得CreateProcess函数越过这个选项并在共享的虚拟DOS机中运行新进程。
 
  ⑹值:CREATE_SUSPENDED
  含义:新进程的主线程会以挂起的状态被创建,直到调用ResumeThread函数被调用时才运行。
 
  ⑺值:CREATE_UNICODE_ENVIRONMENT
  含义:如果被设置,由lpEnvironment参数指定环境块使用Unicode字符,如果为空,环境块默认使用ANSI字符。
 
  ⑻值:DEBUG_PROCESS
  含义:如果这个标志被设置,调用进程将被当做一个调试程序,并且新进程会被当做被调试的进程。系统把被调试程序发生的所有调试事件通知给调试器。
  如果你使用这个标志创建进程,只有调用进程(调用CreateProcess函数的进程)可以调用WaitForDebugEvent函数。
 
  ⑼值:DEBUG_ONLY_THIS_PROCESS
  含义:如果此标志没有被设置且调用进程正在被调试,新进程将成为调试调用进程的调试器的另一个调试对象。如果调用进程没有被调试,有关调试的行为就不会产生。
 
  ⑽值:DETACHED_PROCESS
  含义:对于控制台进程,新进程没有访问父进程控制台的权限。新进程可以通过AllocConsole函数自己创建一个新的控制台。这个标志不可以与CREATE_NEW_CONSOLE标志一起使用。
 
  〔11〕值:CREATE_NO_WINDOW
  含义:系统不为新进程创建CUI窗口,使用该标志可以创建不含窗口的CUI程序。
 
 
 
 
 
 
 
 
 
 
0x02  创建进程详细步骤1:打开目标映像文件,创建进程对象,线程对象
    
   CreateProcessW源代码(windows2000)
  

 BOOL
WINAPI
CreateProcessW(
LPCWSTR lpApplicationName,
LPWSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCWSTR lpCurrentDirectory,
LPSTARTUPINFOW lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation
) {
NTSTATUS Status;
OBJECT_ATTRIBUTES Obja;
POBJECT_ATTRIBUTES pObja;
HANDLE ProcessHandle, ThreadHandle, VdmWaitHandle = NULL;
HANDLE FileHandle, SectionHandle;
CLIENT_ID ClientId;
UNICODE_STRING PathName;
IO_STATUS_BLOCK IoStatusBlock;
BOOLEAN TranslationStatus;
RTL_RELATIVE_NAME RelativeName;
PVOID FreeBuffer;
LPWSTR NameBuffer;
LPWSTR WhiteScan;
ULONG Length,i;
PROCESS_BASIC_INFORMATION ProcessInfo;
SECTION_IMAGE_INFORMATION ImageInformation;
NTSTATUS StackStatus;
BOOLEAN bStatus;
INITIAL_TEB InitialTeb;
CONTEXT ThreadContext;
PPEB Peb;
BASE_API_MSG m;
PBASE_CREATEPROCESS_MSG a= (PBASE_CREATEPROCESS_MSG)&m.u.CreateProcess;
PBASE_CHECKVDM_MSG b= (PBASE_CHECKVDM_MSG)&m.u.CheckVDM;
PWCH TempNull = NULL;
WCHAR TempChar;
UNICODE_STRING VdmNameString;
PVOID BaseAddress;
ULONG VdmReserve;
SIZE_T BigVdmReserve;
ULONG iTask=;
LPWSTR CurdirBuffer, CurdirFilePart;
DWORD CurdirLength,CurdirLength2;
ULONG VDMCreationState=;
ULONG VdmBinaryType = ;
UNICODE_STRING SubSysCommandLine;
PIMAGE_NT_HEADERS NtHeaders;
DWORD dwNoWindow = (dwCreationFlags & CREATE_NO_WINDOW);
ANSI_STRING AnsiStringVDMEnv;
UNICODE_STRING UnicodeStringVDMEnv;
WCHAR ImageFileDebuggerCommand[ ];
LPWSTR QuotedBuffer;
BOOLEAN QuoteInsert;
BOOLEAN QuoteCmdLine = FALSE;
BOOLEAN QuoteFound;
BOOLEAN SearchRetry;
BOOLEAN IsWowBinary = FALSE;
STARTUPINFOW StartupInfo;
DWORD LastError;
DWORD fileattr;
PROCESS_PRIORITY_CLASS PriClass;
PVOID State;
#if defined(BUILD_WOW6432) || defined(_WIN64)
LPCWSTR lpOriginalApplicationName = lpApplicationName;
LPWSTR lpOriginalCommandLine = lpCommandLine;
#endif #if defined(WX86) || defined(_AXP64_)
HANDLE Wx86Info = NULL;
#endif #if defined WX86
BOOLEAN UseKnownWx86Dll;
UseKnownWx86Dll = NtCurrentTeb()->Wx86Thread.UseKnownWx86Dll;
NtCurrentTeb()->Wx86Thread.UseKnownWx86Dll = FALSE;
#endif RtlZeroMemory(lpProcessInformation,sizeof(*lpProcessInformation)); // Private VDM flag should be ignored; Its meant for internal use only.
dwCreationFlags &= (ULONG)~CREATE_NO_WINDOW; //
// CREATE_WITH_USERPROFILE is the new Create Flag that is used
// only by CreateProcessWithLogonW. If this flags ends up getting
// passed to CreateProcess, we must reject it.
//
if (dwCreationFlags & CREATE_WITH_USERPROFILE ) {
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
} if ((dwCreationFlags & (DETACHED_PROCESS | CREATE_NEW_CONSOLE)) ==
(DETACHED_PROCESS | CREATE_NEW_CONSOLE)) { SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
} AnsiStringVDMEnv.Buffer = NULL;
UnicodeStringVDMEnv.Buffer = NULL; //
// the lowest specified priority class is used.
// if (dwCreationFlags & IDLE_PRIORITY_CLASS ) {
PriClass.PriorityClass = PROCESS_PRIORITY_CLASS_IDLE;
}
else if (dwCreationFlags & BELOW_NORMAL_PRIORITY_CLASS ) {
PriClass.PriorityClass = PROCESS_PRIORITY_CLASS_BELOW_NORMAL;
}
else if (dwCreationFlags & NORMAL_PRIORITY_CLASS ) {
PriClass.PriorityClass = PROCESS_PRIORITY_CLASS_NORMAL;
}
else if (dwCreationFlags & ABOVE_NORMAL_PRIORITY_CLASS ) {
PriClass.PriorityClass = PROCESS_PRIORITY_CLASS_ABOVE_NORMAL;
}
else if (dwCreationFlags & HIGH_PRIORITY_CLASS ) {
PriClass.PriorityClass = PROCESS_PRIORITY_CLASS_HIGH;
}
else if (dwCreationFlags & REALTIME_PRIORITY_CLASS ) {
if ( BasepIsRealtimeAllowed(FALSE) ) {
PriClass.PriorityClass = PROCESS_PRIORITY_CLASS_REALTIME;
}
else {
PriClass.PriorityClass = PROCESS_PRIORITY_CLASS_HIGH;
}
}
else {
PriClass.PriorityClass = PROCESS_PRIORITY_CLASS_UNKNOWN;
}
PriClass.Foreground = FALSE; dwCreationFlags = (dwCreationFlags & ~PRIORITY_CLASS_MASK ); //
// Default separate/shared VDM option if not explicitly specified.
// if (dwCreationFlags & CREATE_SEPARATE_WOW_VDM) {
if (dwCreationFlags & CREATE_SHARED_WOW_VDM) {
SetLastError(ERROR_INVALID_PARAMETER); return FALSE;
}
}
else
if ((dwCreationFlags & CREATE_SHARED_WOW_VDM) == ) {
if (BaseStaticServerData->DefaultSeparateVDM) {
dwCreationFlags |= CREATE_SEPARATE_WOW_VDM;
}
} if ((dwCreationFlags & CREATE_SEPARATE_WOW_VDM) == ) {
//
// If the creator is running inside a job object, always
// set SEPERATE_WOW_VDM so the VDM is part of the job.
//
JOBOBJECT_BASIC_UI_RESTRICTIONS UiRestrictions; Status = NtQueryInformationJobObject(NULL,
JobObjectBasicUIRestrictions,
&UiRestrictions,
sizeof(UiRestrictions),
NULL);
if (Status != STATUS_ACCESS_DENIED) {
//
// Anything other than STATUS_ACCESS_DENIED indicates the
// current process is inside a job.
//
dwCreationFlags = (dwCreationFlags & (~CREATE_SHARED_WOW_VDM)) |
CREATE_SEPARATE_WOW_VDM;
}
} //
// If ANSI environment, convert to Unicode
// if (lpEnvironment && !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT) ) {
PUCHAR s;
STRING Ansi;
UNICODE_STRING Unicode;
MEMORY_BASIC_INFORMATION MemoryInformation; Ansi.Buffer = s = lpEnvironment;
while (*s || *(s+)) // find end of block
s++; Ansi.Length = (USHORT)(s - Ansi.Buffer) + ;
Ansi.MaximumLength = Ansi.Length + ;
MemoryInformation.RegionSize = Ansi.MaximumLength * sizeof(WCHAR);
Unicode.Buffer = NULL;
Status = NtAllocateVirtualMemory( NtCurrentProcess(),
&Unicode.Buffer,
,
&MemoryInformation.RegionSize,
MEM_COMMIT,
PAGE_READWRITE
);
if (!NT_SUCCESS(Status) ) {
BaseSetLastNTError(Status); return FALSE;
} Unicode.MaximumLength = (USHORT)MemoryInformation.RegionSize;
Status = RtlAnsiStringToUnicodeString(&Unicode, &Ansi, FALSE);
if (!NT_SUCCESS(Status) ) {
NtFreeVirtualMemory( NtCurrentProcess(),
&Unicode.Buffer,
&MemoryInformation.RegionSize,
MEM_RELEASE
);
BaseSetLastNTError(Status); return FALSE;
}
lpEnvironment = Unicode.Buffer;
} FileHandle = NULL;
SectionHandle = NULL;
ProcessHandle = NULL;
ThreadHandle = NULL;
FreeBuffer = NULL;
NameBuffer = NULL;
VdmNameString.Buffer = NULL;
BaseAddress = (PVOID);
VdmReserve = ;
CurdirBuffer = NULL;
CurdirFilePart = NULL;
SubSysCommandLine.Buffer = NULL;
QuoteFound = FALSE;
QuoteInsert = FALSE;
QuotedBuffer = NULL; try { //
// Make a copy of the startup info so we can change it.
// StartupInfo = *lpStartupInfo; //
// STARTF_USEHOTKEY means hStdInput is really the hotkey value.
// STARTF_HASSHELLDATA means std handles are used for shell-private
// data. This flag is used if an icon is passed to ShellExecuteEx.
// As a result they cannot be specified with STARTF_USESTDHANDLES.
// Consistent with Win95, USESTDHANDLES is ignored.
// if (StartupInfo.dwFlags & STARTF_USESTDHANDLES &&
StartupInfo.dwFlags & (STARTF_USEHOTKEY | STARTF_HASSHELLDATA)) { StartupInfo.dwFlags &= ~STARTF_USESTDHANDLES;
} VdmRetry:
LastError = ;
SearchRetry = TRUE;
QuoteInsert = FALSE;
QuoteCmdLine = FALSE;
if (!ARGUMENT_PRESENT( lpApplicationName )) { //
// Locate the image
// // forgot to free NameBuffer before goto VdmRetry???
ASSERT(NameBuffer == NULL); NameBuffer = RtlAllocateHeap( RtlProcessHeap(),
MAKE_TAG( TMP_TAG ),
MAX_PATH * sizeof( WCHAR ));
if ( !NameBuffer ) {
BaseSetLastNTError(STATUS_NO_MEMORY);
return FALSE;
}
lpApplicationName = lpCommandLine;
TempNull = (PWCH)lpApplicationName;
WhiteScan = (LPWSTR)lpApplicationName; //
// check for lead quote
//
if ( *WhiteScan == L'\"' ) {
SearchRetry = FALSE;
WhiteScan++;
lpApplicationName = WhiteScan;
while(*WhiteScan) {
if ( *WhiteScan == (WCHAR)'\"' ) {
TempNull = (PWCH)WhiteScan;
QuoteFound = TRUE;
break;
}
WhiteScan++;
TempNull = (PWCH)WhiteScan;
}
}
else {
retrywsscan:
lpApplicationName = lpCommandLine;
while(*WhiteScan) {
if ( *WhiteScan == (WCHAR)' ' ||
*WhiteScan == (WCHAR)'\t' ) {
TempNull = (PWCH)WhiteScan;
break;
}
WhiteScan++;
TempNull = (PWCH)WhiteScan;
}
}
TempChar = *TempNull;
*TempNull = UNICODE_NULL; #ifdef WX86 //
// Wx86 applications must use x86 version of known exes
// for compatibility.
// if (UseKnownWx86Dll) {
LPWSTR KnownName; NtCurrentTeb()->Wx86Thread.UseKnownWx86Dll = FALSE; KnownName = BasepWx86KnownExe(lpApplicationName);
if (KnownName) {
lpApplicationName = KnownName;
}
}
#endif Length = SearchPathW(
NULL,
lpApplicationName,
(PWSTR)L".exe",
MAX_PATH,
NameBuffer,
NULL
)*; if (Length != && Length < MAX_PATH * sizeof( WCHAR )) {
//
// SearchPathW worked, but file might be a directory
// if this happens, we need to keep trying
//
fileattr = GetFileAttributesW(NameBuffer);
if ( fileattr != 0xffffffff &&
(fileattr & FILE_ATTRIBUTE_DIRECTORY) ) {
Length = ;
} else {
Length++;
Length++;
}
} if ( !Length || Length >= MAX_PATH<< ) { //
// If we search pathed, then return file not found.
// otherwise, try to be more specific.
//
RTL_PATH_TYPE PathType;
HANDLE hFile; PathType = RtlDetermineDosPathNameType_U(lpApplicationName);
if ( PathType != RtlPathTypeRelative ) { //
// The failed open should set get last error properly.
// hFile = CreateFileW(
lpApplicationName,
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
if ( hFile != INVALID_HANDLE_VALUE ) {
CloseHandle(hFile);
BaseSetLastNTError(STATUS_OBJECT_NAME_NOT_FOUND);
}
}
else {
BaseSetLastNTError(STATUS_OBJECT_NAME_NOT_FOUND);
} //
// remember initial last error value for the retry scan path
// if ( LastError ) {
SetLastError(LastError);
}
else {
LastError = GetLastError();
} //
// restore the command line
// *TempNull = TempChar;
lpApplicationName = NameBuffer; //
// If we still have command line left, then keep going
// the point is to march through the command line looking
// for whitespace so we can try to find an image name
// launches of things like:
// c:\word 95\winword.exe /embedding -automation
// require this. Our first iteration will stop at c:\word, our next
// will stop at c:\word 95\winword.exe
//
if (*WhiteScan && SearchRetry) {
WhiteScan++;
TempNull = WhiteScan;
QuoteInsert = TRUE;
QuoteFound = TRUE;
goto retrywsscan;
} return FALSE;
}
//
// restore the command line
// *TempNull = TempChar;
lpApplicationName = NameBuffer;
}
else
if (!ARGUMENT_PRESENT( lpCommandLine ) || *lpCommandLine == UNICODE_NULL ) {
QuoteCmdLine = TRUE;
lpCommandLine = (LPWSTR)lpApplicationName;
} #ifdef WX86 //
// Wx86 applications must use x86 version of known exes
// for compatibility.
// if (UseKnownWx86Dll) {
LPWSTR KnownName; NtCurrentTeb()->Wx86Thread.UseKnownWx86Dll = FALSE; KnownName = BasepWx86KnownExe(lpApplicationName);
if (KnownName) { RtlFreeHeap(RtlProcessHeap(), , NameBuffer);
NameBuffer = KnownName;
lpApplicationName = KnownName;
}
} #endif //
// Translate to an NT name.
// TranslationStatus = RtlDosPathNameToNtPathName_U(
lpApplicationName,
&PathName,
NULL,
&RelativeName
); if ( !TranslationStatus ) {
SetLastError(ERROR_PATH_NOT_FOUND); return FALSE;
} // forgot to free FreeBuffer before goto VdmRetry????
ASSERT(FreeBuffer == NULL);
FreeBuffer = PathName.Buffer; if ( RelativeName.RelativeName.Length ) {
PathName = *(PUNICODE_STRING)&RelativeName.RelativeName;
}
else {
RelativeName.ContainingDirectory = NULL;
} InitializeObjectAttributes(
&Obja,
&PathName,
OBJ_CASE_INSENSITIVE,
RelativeName.ContainingDirectory,
NULL
); //
// Open the file for execute access
// Status = NtOpenFile(
&FileHandle,
SYNCHRONIZE | FILE_EXECUTE,
&Obja,
&IoStatusBlock,
FILE_SHARE_READ | FILE_SHARE_DELETE,
FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE
);
if (!NT_SUCCESS(Status) ) { //
// if we failed, see if this is a device. If it is a device,
// then just return invalid image format
// if ( RtlIsDosDeviceName_U((PWSTR)lpApplicationName) ) {
SetLastError(ERROR_BAD_DEVICE);
}
else {
BaseSetLastNTError(Status);
} return FALSE;
} //
// If no desktop has been specified, use the caller's
// desktop.
// if (StartupInfo.lpDesktop == NULL) {
StartupInfo.lpDesktop =
(LPWSTR)((PRTL_USER_PROCESS_PARAMETERS)NtCurrentPeb()->
ProcessParameters)->DesktopInfo.Buffer;
} //
// Create a section object backed by the file
// Status = NtCreateSection(
&SectionHandle,
SECTION_ALL_ACCESS,
NULL,
NULL,
PAGE_EXECUTE,
SEC_IMAGE,
FileHandle
); NtClose(FileHandle);
FileHandle = NULL; //
// App Certification DLL
// if (NT_SUCCESS(Status)) { Status = BasepIsProcessAllowed(lpApplicationName); if (!NT_SUCCESS(Status)) {
BaseSetLastNTError(Status);
return FALSE;
} } if (!NT_SUCCESS(Status)) { switch (Status) {
// 16 bit OS/2 exe
case STATUS_INVALID_IMAGE_NE_FORMAT:
#ifdef i386
//
// Use OS/2 if x86 (OS/2 not supported on risc),
// and CreationFlags don't have forcedos bit
// and Registry didn't specify ForceDos
//
// else execute as a DOS bound app.
//
// if (!(dwCreationFlags & CREATE_FORCEDOS) &&
!BaseStaticServerData->ForceDos)
{ if ( !BuildSubSysCommandLine( L"OS2 /P ",
lpApplicationName,
lpCommandLine,
&SubSysCommandLine
) ) {
return FALSE;
} lpCommandLine = SubSysCommandLine.Buffer; lpApplicationName = NULL; RtlFreeHeap(RtlProcessHeap(), ,FreeBuffer);
FreeBuffer = NULL;
RtlFreeHeap(RtlProcessHeap(), , NameBuffer);
NameBuffer = NULL;
goto VdmRetry;
}
#endif
// Falls into Dos case, so that stub message will be
// printed, and bound apps will run w/o OS/2 subsytem // Dos .exe or .com case STATUS_INVALID_IMAGE_PROTECT:
case STATUS_INVALID_IMAGE_NOT_MZ:
ForceDos:
{
ULONG BinarySubType; BinarySubType = BINARY_TYPE_DOS_EXE;
if (Status == STATUS_INVALID_IMAGE_PROTECT ||
Status == STATUS_INVALID_IMAGE_NE_FORMAT ||
(BinarySubType = BaseIsDosApplication(&PathName,Status)) )
{
VdmBinaryType = BINARY_TYPE_DOS; // create the environment before going to the
// server. This was done becuase we want NTVDM
// to have the new environment when it gets
// created.
if (!BaseCreateVDMEnvironment(
lpEnvironment,
&AnsiStringVDMEnv,
&UnicodeStringVDMEnv
))
return FALSE; if(!BaseCheckVDM(VdmBinaryType | BinarySubType,
lpApplicationName,
lpCommandLine,
lpCurrentDirectory,
&AnsiStringVDMEnv,
&m,
&iTask,
dwCreationFlags,
&StartupInfo
))
return FALSE; // Check the return value from the server
switch (b->VDMState & VDM_STATE_MASK){
case VDM_NOT_PRESENT:
// mark this so the server can undo
// creation if something goes wrong.
// We marked it "partitially created" because
// the NTVDM has yet not been fully created.
// a call to UpdateVdmEntry to update
// process handle will signal the NTVDM
// process completed creation
VDMCreationState = VDM_PARTIALLY_CREATED;
// fail the call if NTVDM process is being
// created DETACHED.
// note that, we let it go if NTVDM process
// is already running.
if (dwCreationFlags & DETACHED_PROCESS) {
SetLastError(ERROR_ACCESS_DENIED);
return FALSE;
}
if (!BaseGetVdmConfigInfo(lpCommandLine,
iTask,
VdmBinaryType,
&VdmNameString,
&VdmReserve
))
{
BaseSetLastNTError(Status);
return FALSE;
} lpCommandLine = VdmNameString.Buffer;
lpApplicationName = NULL; break; case VDM_PRESENT_NOT_READY:
SetLastError (ERROR_NOT_READY);
return FALSE; case VDM_PRESENT_AND_READY:
VDMCreationState = VDM_BEING_REUSED;
VdmWaitHandle = b->WaitObjectForParent;
break;
}
RtlFreeHeap(RtlProcessHeap(), ,FreeBuffer);
FreeBuffer = NULL;
RtlFreeHeap(RtlProcessHeap(), , NameBuffer);
NameBuffer = NULL;
VdmReserve--; // we reserve from addr 1
if(VdmWaitHandle)
goto VdmExists;
else{
bInheritHandles = FALSE;
if (lpEnvironment &&
!(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT)){
RtlDestroyEnvironment(lpEnvironment);
}
lpEnvironment = UnicodeStringVDMEnv.Buffer;
goto VdmRetry;
}
}
else { //
// must be a .bat or .cmd file
// static PWCHAR CmdPrefix = L"cmd /c ";
PWCHAR NewCommandLine;
ULONG Length;
PWCHAR Last4 = &PathName.Buffer[PathName.Length / sizeof( WCHAR )-]; if ( PathName.Length < ) {
SetLastError(ERROR_BAD_EXE_FORMAT);
return FALSE;
} if (_wcsnicmp( Last4, L".bat", ) && _wcsnicmp( Last4, L".cmd", )) {
SetLastError(ERROR_BAD_EXE_FORMAT);
return FALSE;
} Length = wcslen( CmdPrefix )
+ (QuoteCmdLine || QuoteFound )
+ wcslen( lpCommandLine )
+ (QuoteCmdLine || QuoteFound)
+ ; NewCommandLine = RtlAllocateHeap( RtlProcessHeap( ),
MAKE_TAG( TMP_TAG ),
Length * sizeof( WCHAR ) ); if (NewCommandLine == NULL) {
BaseSetLastNTError(STATUS_NO_MEMORY);
return FALSE;
} wcscpy( NewCommandLine, CmdPrefix );
if (QuoteCmdLine || QuoteFound) {
wcscat( NewCommandLine, L"\"" );
}
wcscat( NewCommandLine, lpCommandLine );
if (QuoteCmdLine || QuoteFound) {
wcscat( NewCommandLine, L"\"" );
} RtlInitUnicodeString( &SubSysCommandLine, NewCommandLine ); lpCommandLine = SubSysCommandLine.Buffer; lpApplicationName = NULL; RtlFreeHeap(RtlProcessHeap(), ,FreeBuffer);
FreeBuffer = NULL;
RtlFreeHeap(RtlProcessHeap(), , NameBuffer);
NameBuffer = NULL;
goto VdmRetry; } } // 16 bit windows exe
case STATUS_INVALID_IMAGE_WIN_16:
#if defined(BUILD_WOW6432) || defined(_WIN64)
if (lpOriginalApplicationName == NULL) {
// pass in the part of the command line after the exe name
// including whitespace
lpCommandLine = ((*TempNull == '\"') ? TempNull + : TempNull);
} else {
lpCommandLine = lpOriginalCommandLine;
}
return NtVdm64CreateProcess(lpOriginalApplicationName == NULL,
lpApplicationName, // this is now the real file name we've loaded
lpCommandLine,
lpProcessAttributes,
lpThreadAttributes,
bInheritHandles,
(dwCreationFlags & ~CREATE_UNICODE_ENVIRONMENT), // the environment has already been converted to unicode
lpEnvironment,
lpCurrentDirectory,
lpStartupInfo,
lpProcessInformation
);
#endif
if (dwCreationFlags & CREATE_FORCEDOS) {
goto ForceDos;
} IsWowBinary = TRUE;
if (!BaseCreateVDMEnvironment(lpEnvironment,
&AnsiStringVDMEnv,
&UnicodeStringVDMEnv
))
{
return FALSE;
} RetrySepWow:
VdmBinaryType = dwCreationFlags & CREATE_SEPARATE_WOW_VDM
? BINARY_TYPE_SEPWOW : BINARY_TYPE_WIN16; if (!BaseCheckVDM(VdmBinaryType,
lpApplicationName,
lpCommandLine,
lpCurrentDirectory,
&AnsiStringVDMEnv,
&m,
&iTask,
dwCreationFlags,
&StartupInfo
))
{
//
// If we failed with access denied, caller may not
// be allowed allowed to access the shared wow's
// desktop, so retry as a separate wow
//
if (VdmBinaryType == BINARY_TYPE_WIN16 &&
GetLastError() == ERROR_ACCESS_DENIED)
{
dwCreationFlags |= CREATE_SEPARATE_WOW_VDM;
}
else {
return FALSE;
}
goto RetrySepWow;
} // Check the return value from the server
switch (b->VDMState & VDM_STATE_MASK){
case VDM_NOT_PRESENT:
// mark this so the server can undo
// creation if something goes wrong.
// We marked it "partitially created" because
// the NTVDM has yet not been fully created.
// a call to UpdateVdmEntry to update
// process handle will signal the NTVDM
// process completed creation VDMCreationState = VDM_PARTIALLY_CREATED; if (!BaseGetVdmConfigInfo(
lpCommandLine,
iTask,
VdmBinaryType,
&VdmNameString,
&VdmReserve
))
{
BaseSetLastNTError(Status);
return FALSE;
} lpCommandLine = VdmNameString.Buffer;
lpApplicationName = NULL; //
// Wow must have a hidden console
// Throw away DETACHED_PROCESS flag which isn't
// meaningful for Win16 apps.
// dwCreationFlags |= CREATE_NO_WINDOW;
dwCreationFlags &= ~(CREATE_NEW_CONSOLE | DETACHED_PROCESS); //
// We're starting a WOW VDM, turn on feedback unless
// the creator passed STARTF_FORCEOFFFEEDBACK.
// StartupInfo.dwFlags |= STARTF_FORCEONFEEDBACK; break; case VDM_PRESENT_NOT_READY:
SetLastError (ERROR_NOT_READY);
return FALSE; case VDM_PRESENT_AND_READY:
VDMCreationState = VDM_BEING_REUSED;
VdmWaitHandle = b->WaitObjectForParent;
break;
} RtlFreeHeap(RtlProcessHeap(), ,FreeBuffer);
FreeBuffer = NULL;
RtlFreeHeap(RtlProcessHeap(), , NameBuffer);
NameBuffer = NULL;
VdmReserve--; // we reserve from addr 1
if(VdmWaitHandle)
goto VdmExists;
else {
bInheritHandles = FALSE;
// replace the environment with ours
if (lpEnvironment &&
!(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT)) {
RtlDestroyEnvironment(lpEnvironment);
}
lpEnvironment = UnicodeStringVDMEnv.Buffer;
goto VdmRetry;
} default :
SetLastError(ERROR_BAD_EXE_FORMAT);
return FALSE;
}
} //
// Make sure only WOW apps can have the CREATE_SEPARATE_WOW_VDM flag.
// if (!IsWowBinary && (dwCreationFlags & CREATE_SEPARATE_WOW_VDM)) {
dwCreationFlags &= ~CREATE_SEPARATE_WOW_VDM;
} //
// Query the section to determine the stack parameters and
// image entrypoint.
// Status = NtQuerySection(
SectionHandle,
SectionImageInformation,
&ImageInformation,
sizeof( ImageInformation ),
NULL
); if (!NT_SUCCESS( Status )) {
BaseSetLastNTError(Status);
RtlFreeHeap(RtlProcessHeap(), ,FreeBuffer);
FreeBuffer = NULL;
return FALSE;
} if (ImageInformation.ImageCharacteristics & IMAGE_FILE_DLL) {
SetLastError(ERROR_BAD_EXE_FORMAT);
RtlFreeHeap(RtlProcessHeap(), ,FreeBuffer);
FreeBuffer = NULL;
return FALSE;
} ImageFileDebuggerCommand[ ] = UNICODE_NULL;
if (!(dwCreationFlags & (DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS)) ||
NtCurrentPeb()->ReadImageFileExecOptions
) {
LdrQueryImageFileExecutionOptions( &PathName,
L"Debugger",
REG_SZ,
ImageFileDebuggerCommand,
sizeof( ImageFileDebuggerCommand ),
NULL
);
} if ((ImageInformation.Machine < USER_SHARED_DATA->ImageNumberLow) ||
(ImageInformation.Machine > USER_SHARED_DATA->ImageNumberHigh)) {
#if defined(WX86) || defined(_AXP64_)
if (ImageInformation.Machine == IMAGE_FILE_MACHINE_I386)
{
Wx86Info = (HANDLE)UIntToPtr(sizeof(WX86TIB));
}
else
#endif // WX86
#if defined(_AXP64_)
if (ImageInformation.Machine == IMAGE_FILE_MACHINE_ALPHA) {
// Fall through since this is a valid machine type.
}
else
#elif defined(_IA64_)
if (ImageInformation.Machine == IMAGE_FILE_MACHINE_I386) {
// Fall through since this is a valid machine type.
}
else
#endif // _AXP64_
#if defined(BUILD_WOW6432)
// 32-bit kernel32.dll on NT64 can run 64-bit binaries
#if defined(_ALPHA_)
if (ImageInformation.Machine == IMAGE_FILE_MACHINE_ALPHA) {
// Fall through since this is a valid machine type.
}
else
#elif defined(_X86_)
if (ImageInformation.Machine == IMAGE_FILE_MACHINE_I386) {
// Fall through since this is a valid machine type.
}
else
#endif // ALPHA or IA64
#endif // BUILD_WOW6432
{
ULONG_PTR ErrorParameters[];
ULONG ErrorResponse; ErrorResponse = ResponseOk;
ErrorParameters[] = (ULONG_PTR)&PathName; NtRaiseHardError( STATUS_IMAGE_MACHINE_TYPE_MISMATCH_EXE,
,
,
ErrorParameters,
OptionOk,
&ErrorResponse
);
if ( NtCurrentPeb()->ImageSubsystemMajorVersion <= ) {
SetLastError(ERROR_BAD_EXE_FORMAT);
}
else {
SetLastError(ERROR_EXE_MACHINE_TYPE_MISMATCH);
}
RtlFreeHeap(RtlProcessHeap(), ,FreeBuffer);
FreeBuffer = NULL;
return FALSE;
}
} RtlFreeHeap(RtlProcessHeap(), ,FreeBuffer);
FreeBuffer = NULL;
if ( ImageInformation.SubSystemType != IMAGE_SUBSYSTEM_WINDOWS_GUI &&
ImageInformation.SubSystemType != IMAGE_SUBSYSTEM_WINDOWS_CUI ) { // POSIX exe NtClose(SectionHandle);
SectionHandle = NULL; if ( ImageInformation.SubSystemType == IMAGE_SUBSYSTEM_POSIX_CUI ) { if ( !BuildSubSysCommandLine( L"POSIX /P ",
lpApplicationName,
lpCommandLine,
&SubSysCommandLine
) ) {
return FALSE;
} lpCommandLine = SubSysCommandLine.Buffer; lpApplicationName = NULL;
RtlFreeHeap(RtlProcessHeap(), , NameBuffer);
NameBuffer = NULL;
goto VdmRetry;
}
else {
SetLastError(ERROR_CHILD_NOT_COMPLETE);
return FALSE;
}
}
else {
if (!BasepIsImageVersionOk( ImageInformation.SubSystemMajorVersion,
ImageInformation.SubSystemMinorVersion) ) {
SetLastError(ERROR_BAD_EXE_FORMAT);
return FALSE;
}
} if (ImageFileDebuggerCommand[ ] != UNICODE_NULL) {
USHORT n; n = (USHORT)wcslen( lpCommandLine );
if (n == ) {
lpCommandLine = (LPWSTR)lpApplicationName;
n = (USHORT)wcslen( lpCommandLine );
} n += wcslen( ImageFileDebuggerCommand ) + + ;
n *= sizeof( WCHAR ); SubSysCommandLine.Buffer = RtlAllocateHeap( RtlProcessHeap(), MAKE_TAG( TMP_TAG ), n );
SubSysCommandLine.Length = ;
SubSysCommandLine.MaximumLength = n;
RtlAppendUnicodeToString( &SubSysCommandLine, ImageFileDebuggerCommand );
RtlAppendUnicodeToString( &SubSysCommandLine, L" " );
RtlAppendUnicodeToString( &SubSysCommandLine, (PWSTR)lpCommandLine );
#if DBG
DbgPrint( "BASE: Calling debugger with '%wZ'\n", &SubSysCommandLine );
#endif
lpCommandLine = SubSysCommandLine.Buffer;
lpApplicationName = NULL; NtClose(SectionHandle);
SectionHandle = NULL;
RtlFreeHeap(RtlProcessHeap(), , NameBuffer);
NameBuffer = NULL;
goto VdmRetry;
} //
// Create the process object
// pObja = BaseFormatObjectAttributes(&Obja,lpProcessAttributes,NULL); if (dwCreationFlags & CREATE_BREAKAWAY_FROM_JOB ) {
SectionHandle = (HANDLE)( (UINT_PTR)SectionHandle | );
} Status = NtCreateProcess(
&ProcessHandle,
PROCESS_ALL_ACCESS,
pObja,
NtCurrentProcess(),
(BOOLEAN)bInheritHandles,
SectionHandle,
NULL,
NULL
);
if ( !NT_SUCCESS(Status) ) {
BaseSetLastNTError(Status);
return FALSE;
} //
// NtCreateProcess will set to normal OR inherit if parent is IDLE or Below
// only override if a mask is given during the create.
// if ( PriClass.PriorityClass != PROCESS_PRIORITY_CLASS_UNKNOWN ) {
State = NULL;
if ( PriClass.PriorityClass == PROCESS_PRIORITY_CLASS_REALTIME ) {
State = BasepIsRealtimeAllowed(TRUE);
}
Status = NtSetInformationProcess(
ProcessHandle,
ProcessPriorityClass,
(PVOID)&PriClass,
sizeof(PriClass)
);
if ( State ) {
BasepReleasePrivilege( State );
} if ( !NT_SUCCESS(Status) ) {
BaseSetLastNTError(Status);
return FALSE;
}
} NtClose(SectionHandle);
SectionHandle = NULL; if (dwCreationFlags & CREATE_DEFAULT_ERROR_MODE) {
UINT NewMode;
NewMode = SEM_FAILCRITICALERRORS;
NtSetInformationProcess(
ProcessHandle,
ProcessDefaultHardErrorMode,
(PVOID) &NewMode,
sizeof(NewMode)
);
} //
// If the process is being created for a VDM call the server with
// process handle.
// if (VdmBinaryType) {
VdmWaitHandle = ProcessHandle;
if (!BaseUpdateVDMEntry(UPDATE_VDM_PROCESS_HANDLE,
&VdmWaitHandle,
iTask,
VdmBinaryType
))
{
//make sure we don't close the handle twice --
//(VdmWaitHandle == ProcessHandle) if we don't do this.
VdmWaitHandle = NULL;
return FALSE;
} //
// For Sep wow the VdmWaitHandle = NULL (there is none!)
// VDMCreationState |= VDM_FULLY_CREATED;
} //
// if we're a detached priority, we don't have the focus, so
// don't create with boosted priority.
// if (dwCreationFlags & DETACHED_PROCESS) {
KPRIORITY SetBasePriority; SetBasePriority = (KPRIORITY)NORMAL_BASE_PRIORITY;
Status = NtSetInformationProcess(ProcessHandle,
ProcessBasePriority,
(PVOID) &SetBasePriority,
sizeof(SetBasePriority)
);
ASSERT(NT_SUCCESS(Status));
} #if defined(i386) || defined(_IA64_)
//
// Reserve memory in the new process' address space if necessary
// (for vdms). This is required only for x86 system.
// if ( VdmReserve ) {
BigVdmReserve = VdmReserve;
Status = NtAllocateVirtualMemory(
ProcessHandle,
&BaseAddress,
0L,
&BigVdmReserve,
MEM_RESERVE,
PAGE_EXECUTE_READWRITE
);
if ( !NT_SUCCESS(Status) ){
BaseSetLastNTError(Status);
return FALSE;
}
}
#endif //
// Determine the location of the
// processes PEB.
// Status = NtQueryInformationProcess(
ProcessHandle,
ProcessBasicInformation,
&ProcessInfo,
sizeof( ProcessInfo ),
NULL
);
if ( !NT_SUCCESS( Status ) ) {
BaseSetLastNTError(Status);
return FALSE;
} Peb = ProcessInfo.PebBaseAddress; //
// Push the parameters into the address space of the new process
// if ( ARGUMENT_PRESENT(lpCurrentDirectory) ) {
CurdirBuffer = RtlAllocateHeap( RtlProcessHeap(),
MAKE_TAG( TMP_TAG ),
(MAX_PATH + ) * sizeof( WCHAR ) );
if ( !CurdirBuffer ) {
BaseSetLastNTError(STATUS_NO_MEMORY);
return FALSE;
}
CurdirLength2 = GetFullPathNameW(
lpCurrentDirectory,
MAX_PATH,
CurdirBuffer,
&CurdirFilePart
);
if ( CurdirLength2 > MAX_PATH ) {
SetLastError(ERROR_DIRECTORY);
return FALSE;
} //
// now make sure the directory exists
// CurdirLength = GetFileAttributesW(CurdirBuffer);
if ( (CurdirLength == 0xffffffff) ||
!(CurdirLength & FILE_ATTRIBUTE_DIRECTORY) ) {
SetLastError(ERROR_DIRECTORY);
return FALSE;
}
} if ( QuoteInsert || QuoteCmdLine) {
QuotedBuffer = RtlAllocateHeap(RtlProcessHeap(),,wcslen(lpCommandLine)*+); if ( QuotedBuffer ) {
wcscpy(QuotedBuffer,L"\""); if ( QuoteInsert ) {
TempChar = *TempNull;
*TempNull = UNICODE_NULL;
} wcscat(QuotedBuffer,lpCommandLine);
wcscat(QuotedBuffer,L"\""); if ( QuoteInsert ) {
*TempNull = TempChar;
wcscat(QuotedBuffer,TempNull);
} }
else {
if ( QuoteInsert ) {
QuoteInsert = FALSE;
}
if ( QuoteCmdLine ) {
QuoteCmdLine = FALSE;
}
}
} if (!BasePushProcessParameters(
ProcessHandle,
Peb,
lpApplicationName,
CurdirBuffer,
QuoteInsert || QuoteCmdLine ? QuotedBuffer : lpCommandLine,
lpEnvironment,
&StartupInfo,
dwCreationFlags | dwNoWindow,
bInheritHandles,
IsWowBinary ? IMAGE_SUBSYSTEM_WINDOWS_GUI :
) ) {
return FALSE;
} RtlFreeUnicodeString(&VdmNameString);
VdmNameString.Buffer = NULL; //
// Stuff in the standard handles if needed
//
if (!VdmBinaryType &&
!bInheritHandles &&
!(StartupInfo.dwFlags & STARTF_USESTDHANDLES) &&
!(dwCreationFlags & (DETACHED_PROCESS | CREATE_NEW_CONSOLE | CREATE_NO_WINDOW)) &&
ImageInformation.SubSystemType == IMAGE_SUBSYSTEM_WINDOWS_CUI
) {
PRTL_USER_PROCESS_PARAMETERS ParametersInNewProcess; Status = NtReadVirtualMemory( ProcessHandle,
&Peb->ProcessParameters,
&ParametersInNewProcess,
sizeof( ParametersInNewProcess ),
NULL
);
if (NT_SUCCESS( Status )) {
if (!CONSOLE_HANDLE( NtCurrentPeb()->ProcessParameters->StandardInput )) {
StuffStdHandle( ProcessHandle,
NtCurrentPeb()->ProcessParameters->StandardInput,
&ParametersInNewProcess->StandardInput
);
}
if (!CONSOLE_HANDLE( NtCurrentPeb()->ProcessParameters->StandardOutput )) {
StuffStdHandle( ProcessHandle,
NtCurrentPeb()->ProcessParameters->StandardOutput,
&ParametersInNewProcess->StandardOutput
);
}
if (!CONSOLE_HANDLE( NtCurrentPeb()->ProcessParameters->StandardError )) {
StuffStdHandle( ProcessHandle,
NtCurrentPeb()->ProcessParameters->StandardError,
&ParametersInNewProcess->StandardError
);
}
}
} //
// Create the thread...
// //
// Allocate a stack for this thread in the address space of the target
// process.
// StackStatus = BaseCreateStack(
ProcessHandle,
ImageInformation.CommittedStackSize,
(ImageInformation.MaximumStackSize < *) ? * : ImageInformation.MaximumStackSize,
&InitialTeb
); if ( !NT_SUCCESS(StackStatus) ) {
BaseSetLastNTError(StackStatus);
return FALSE;
} //
// Create an initial context for the new thread.
// BaseInitializeContext(
&ThreadContext,
Peb,
ImageInformation.TransferAddress,
InitialTeb.StackBase,
BaseContextTypeProcess
); //
// Create the actual thread object
// pObja = BaseFormatObjectAttributes(&Obja,lpThreadAttributes,NULL); Status = NtCreateThread(
&ThreadHandle,
THREAD_ALL_ACCESS,
pObja,
ProcessHandle,
&ClientId,
&ThreadContext,
&InitialTeb,
TRUE
); if (!NT_SUCCESS(Status) ) {
BaseSetLastNTError(Status);
return FALSE;
} //
// From here on out, do not modify the address space of the
// new process. WOW64's implementation of NtCreateThread()
// reshuffles the new process' address space if the current
// process is 32-bit and the new process is 64-bit.
//
#if DBG
Peb = NULL;
#endif #if defined(WX86) || defined(_AXP64_) //
// if this is a Wx86 Process, setup for a Wx86 emulated Thread
// if (Wx86Info) { //
// create a WX86Tib and initialize it's Teb->Vdm.
//
Status = BaseCreateWx86Tib(ProcessHandle,
ThreadHandle,
(ULONG)((ULONG_PTR)ImageInformation.TransferAddress),
(ULONG)ImageInformation.CommittedStackSize,
(ULONG)ImageInformation.MaximumStackSize,
TRUE
); if (!NT_SUCCESS(Status)) {
BaseSetLastNTError(Status);
return( FALSE );
} //
// Mark Process as WX86
//
Status = NtSetInformationProcess (ProcessHandle,
ProcessWx86Information,
&Wx86Info,
sizeof(Wx86Info)
); if (!NT_SUCCESS(Status)) {
BaseSetLastNTError(Status);
return( FALSE );
}
}
#endif //
// Call the Windows server to let it know about the
// process.
// a->ProcessHandle = ProcessHandle;
a->ThreadHandle = ThreadHandle;
a->ClientId = ClientId;
a->CreationFlags = dwCreationFlags; if ( dwCreationFlags & (DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS) ) {
Status = DbgUiConnectToDbg();
if ( !NT_SUCCESS(Status) ) {
NtTerminateProcess(ProcessHandle, Status);
BaseSetLastNTError(Status);
return FALSE;
}
a->DebuggerClientId = NtCurrentTeb()->ClientId;
}
else {
a->DebuggerClientId.UniqueProcess = NULL;
a->DebuggerClientId.UniqueThread = NULL;
} //
// Set the 2 bit if a gui app is starting. The window manager needs to
// know this so it can synchronize the startup of this app
// (WaitForInputIdle api). This info is passed using the process
// handle tag bits. The 1 bit asks the window manager to turn on
// or turn off the application start cursor (hourglass/pointer).
//
// When starting a WOW process, lie and tell UserSrv NTVDM.EXE is a GUI
// process. We also turn on bit 0x8 so that UserSrv can ignore the
// UserNotifyConsoleApplication call made by the console during startup.
// if ( ImageInformation.SubSystemType == IMAGE_SUBSYSTEM_WINDOWS_GUI ||
IsWowBinary ) { a->ProcessHandle = (HANDLE)((ULONG_PTR)a->ProcessHandle | ); //
// If the creating process is a GUI app, turn on the app. start cursor
// by default. This can be overridden by STARTF_FORCEOFFFEEDBACK.
// NtHeaders = RtlImageNtHeader((PVOID)GetModuleHandle(NULL));
if ( NtHeaders->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI )
a->ProcessHandle = (HANDLE)((ULONG_PTR)a->ProcessHandle | ); } //
// If feedback is forced on, turn it on. If forced off, turn it off.
// Off overrides on.
// if (StartupInfo.dwFlags & STARTF_FORCEONFEEDBACK)
a->ProcessHandle = (HANDLE)((ULONG_PTR)a->ProcessHandle | );
if (StartupInfo.dwFlags & STARTF_FORCEOFFFEEDBACK)
a->ProcessHandle = (HANDLE)((ULONG_PTR)a->ProcessHandle & ~); a->VdmBinaryType = VdmBinaryType; // just tell server the truth if (VdmBinaryType){
a->hVDM = iTask ? : NtCurrentPeb()->ProcessParameters->ConsoleHandle;
a->VdmTask = iTask;
} #if defined(BUILD_WOW6432)
m.ReturnValue = CsrBasepCreateProcess(a);
#else
CsrClientCallServer( (PCSR_API_MSG)&m,
NULL,
CSR_MAKE_API_NUMBER( BASESRV_SERVERDLL_INDEX,
BasepCreateProcess
),
sizeof( *a )
);
#endif if (!NT_SUCCESS((NTSTATUS)m.ReturnValue)) {
BaseSetLastNTError((NTSTATUS)m.ReturnValue);
NtTerminateProcess(ProcessHandle, (NTSTATUS)m.ReturnValue);
return FALSE;
} if (!( dwCreationFlags & CREATE_SUSPENDED) ) {
NtResumeThread(ThreadHandle,&i);
} VdmExists:
bStatus = TRUE;
if (VDMCreationState)
VDMCreationState |= VDM_CREATION_SUCCESSFUL; try {
if (VdmWaitHandle) { //
// tag Shared WOW VDM handles so that wait for input idle has a
// chance to work. Shared WOW VDM "process" handles are actually
// event handles, Separate WOW VDM handles are real process
// handles. Also mark DOS handles with 0x1 so WaitForInputIdle
// has a way to distinguish DOS apps and not block forever.
// if (VdmBinaryType == BINARY_TYPE_WIN16) {
lpProcessInformation->hProcess =
(HANDLE)((ULONG_PTR)VdmWaitHandle | 0x2); //
// Shared WOW doesn't always start a process, so
// we don't have a process ID or thread ID to
// return if the VDM already existed.
//
// Separate WOW doesn't hit this codepath
// (no VdmWaitHandle).
// if (VDMCreationState & VDM_BEING_REUSED) {
ClientId.UniqueProcess = ;
ClientId.UniqueThread = ;
} }
else {
lpProcessInformation->hProcess =
(HANDLE)((ULONG_PTR)VdmWaitHandle | 0x1);
} //
// Close the ProcessHandle, since we are returning the
// VdmProcessHandle instead.
// if (ProcessHandle != NULL)
NtClose(ProcessHandle);
}
else{
lpProcessInformation->hProcess = ProcessHandle;
} lpProcessInformation->hThread = ThreadHandle;
lpProcessInformation->dwProcessId = HandleToUlong(ClientId.UniqueProcess);
lpProcessInformation->dwThreadId = HandleToUlong(ClientId.UniqueThread);
ProcessHandle = NULL;
ThreadHandle = NULL;
}
except ( EXCEPTION_EXECUTE_HANDLER ) {
NtClose( ProcessHandle );
NtClose( ThreadHandle );
ProcessHandle = NULL;
ThreadHandle = NULL;
if (VDMCreationState)
VDMCreationState &= ~VDM_CREATION_SUCCESSFUL;
}
}
finally {
if (lpEnvironment && !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT) ) {
RtlDestroyEnvironment(lpEnvironment);
lpEnvironment = NULL;
}
RtlFreeHeap(RtlProcessHeap(), ,QuotedBuffer);
RtlFreeHeap(RtlProcessHeap(), ,NameBuffer);
RtlFreeHeap(RtlProcessHeap(), ,CurdirBuffer);
RtlFreeHeap(RtlProcessHeap(), ,FreeBuffer);
if ( FileHandle ) {
NtClose(FileHandle);
}
if ( SectionHandle ) {
NtClose(SectionHandle);
}
if ( ThreadHandle ) {
NtTerminateProcess(ProcessHandle,STATUS_SUCCESS);
NtClose(ThreadHandle);
}
if ( ProcessHandle ) {
NtClose(ProcessHandle);
}
RtlFreeUnicodeString(&VdmNameString);
RtlFreeUnicodeString(&SubSysCommandLine);
if (AnsiStringVDMEnv.Buffer || UnicodeStringVDMEnv.Buffer)
BaseDestroyVDMEnvironment(&AnsiStringVDMEnv, &UnicodeStringVDMEnv); if (VDMCreationState && !(VDMCreationState & VDM_CREATION_SUCCESSFUL)){
BaseUpdateVDMEntry (
UPDATE_VDM_UNDO_CREATION,
(HANDLE *)&iTask,
VDMCreationState,
VdmBinaryType
);
if(VdmWaitHandle) {
NtClose(VdmWaitHandle);
}
}
} if (lpEnvironment && !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT) ) {
RtlDestroyEnvironment(lpEnvironment);
}
return bStatus;
}

CreateProcessW

  暂且忽略掉之前的一些环境字符串等等相关操作,注意到第一个值得关注的函数:NtOpenFile()打开其目标文件获取到了文件句柄FIileHandle
 
  

        //
// Open the file for execute access
// Status = NtOpenFile(
&FileHandle,
SYNCHRONIZE | FILE_EXECUTE,
&Obja,
&IoStatusBlock,
FILE_SHARE_READ | FILE_SHARE_DELETE,
FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE
);

  

  紧接其后的是NtCreateSectiont通过文件句柄创建一个Section文件映射区,将文件内容映射进来
        //
// Create a section object backed by the file
// Status = NtCreateSection(
&SectionHandle,
SECTION_ALL_ACCESS,
NULL,
NULL,
PAGE_EXECUTE,
SEC_IMAGE,
FileHandle
);

  

  继续向下看关键函数:NtCreateProcess出现啦!

        Status = NtCreateProcess(
&ProcessHandle,
PROCESS_ALL_ACCESS,
pObja,
NtCurrentProcess(),
(BOOLEAN)bInheritHandles,
SectionHandle,
NULL,
NULL
);

  在源码中继续延伸,发现调用过程是NtCreateProcess——>NtCreateProcessEx——>PspCreateProcess(Windows2000源码中是没有NtCreateProcessEx函数这个中间过程的,我是在另外一套源码中找到的NtCreateProcessEx函数,但无奈另一套较新的源码中又没有CreateProcessW的定义,故而开始参考的是Windows2000 源码)

  NtCreateProcess函数和NtCreateProcessEx函数:

NTSTATUS
NtCreateProcess(
__out PHANDLE ProcessHandle,
__in ACCESS_MASK DesiredAccess,
__in_opt POBJECT_ATTRIBUTES ObjectAttributes,
__in HANDLE ParentProcess,
__in BOOLEAN InheritObjectTable,
__in_opt HANDLE SectionHandle,
__in_opt HANDLE DebugPort,
__in_opt HANDLE ExceptionPort
)
{
ULONG Flags = 0; if ((ULONG_PTR)SectionHandle & 1) {
Flags |= PROCESS_CREATE_FLAGS_BREAKAWAY;
} if ((ULONG_PTR) DebugPort & 1) {
Flags |= PROCESS_CREATE_FLAGS_NO_DEBUG_INHERIT;
} if (InheritObjectTable) {
Flags |= PROCESS_CREATE_FLAGS_INHERIT_HANDLES;
} return NtCreateProcessEx (ProcessHandle,
DesiredAccess,
ObjectAttributes OPTIONAL,
ParentProcess,
Flags,
SectionHandle,
DebugPort,
ExceptionPort,
0);
} NTSTATUS
NtCreateProcessEx(
__out PHANDLE ProcessHandle,
__in ACCESS_MASK DesiredAccess,
__in_opt POBJECT_ATTRIBUTES ObjectAttributes,
__in HANDLE ParentProcess,
__in ULONG Flags,
__in_opt HANDLE SectionHandle,
__in_opt HANDLE DebugPort,
__in_opt HANDLE ExceptionPort,
__in ULONG JobMemberLevel
) {
NTSTATUS Status; PAGED_CODE(); if (KeGetPreviousMode() != KernelMode) { //
// Probe all arguments
// try {
ProbeForWriteHandle (ProcessHandle);
} except (EXCEPTION_EXECUTE_HANDLER) {
return GetExceptionCode ();
}
} if (ARGUMENT_PRESENT (ParentProcess)) {
Status = PspCreateProcess (ProcessHandle,
DesiredAccess,
ObjectAttributes,
ParentProcess,
Flags,
SectionHandle,
DebugPort,
ExceptionPort,
JobMemberLevel);
} else {
Status = STATUS_INVALID_PARAMETER;
} return Status;
}

  

  看以看到NtCreateProcess函数简单地对处理一下参数,然后把创建进程的任务交给NtCreateProcessEx函数。

  NtCreateProcessEx函数的流程。它也只是简单地检查ProcessHandle参数代表的句柄是否可写,ParentProcess是否不为空。真正的创建工作交给PspCreateProcess函数。

  

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,
IN HANDLE DebugPort OPTIONAL,
IN HANDLE ExceptionPort OPTIONAL,
IN ULONG JobMemberLevel
)
/*++ Routine Description: This routine creates and initializes a process object. It implements the
foundation for NtCreateProcess and for system initialization process
creation. --*/ { NTSTATUS Status;
PEPROCESS Process;
PEPROCESS CurrentProcess;
PEPROCESS Parent;
PETHREAD CurrentThread;
KAFFINITY Affinity;
KPRIORITY BasePriority;
PVOID SectionObject;
PVOID ExceptionPortObject;
PVOID DebugPortObject;
ULONG WorkingSetMinimum, WorkingSetMaximum;
HANDLE LocalProcessHandle;
KPROCESSOR_MODE PreviousMode;
INITIAL_PEB InitialPeb;
BOOLEAN CreatePeb;
ULONG_PTR DirectoryTableBase[2];
BOOLEAN AccessCheck;
BOOLEAN MemoryAllocated;
PSECURITY_DESCRIPTOR SecurityDescriptor;
SECURITY_SUBJECT_CONTEXT SubjectContext;
NTSTATUS accesst;
NTSTATUS SavedStatus;
ULONG ImageFileNameSize;
HANDLE_TABLE_ENTRY CidEntry;
PEJOB Job;
PPEB Peb;
AUX_ACCESS_DATA AuxData;
PACCESS_STATE AccessState;
ACCESS_STATE LocalAccessState;
BOOLEAN UseLargePages;
SCHAR QuantumReset;
#if defined(_WIN64)
INITIAL_PEB32 InitialPeb32;
#endif PAGED_CODE(); CurrentThread = PsGetCurrentThread ();
PreviousMode = KeGetPreviousModeByThread(&CurrentThread->Tcb);
CurrentProcess = PsGetCurrentProcessByThread (CurrentThread); CreatePeb = FALSE;
UseLargePages = FALSE;
DirectoryTableBase[0] = 0;
DirectoryTableBase[1] = 0;
Peb = NULL; //
// Reject bogus create parameters for future expansion
//
if (Flags&~PROCESS_CREATE_FLAGS_LEGAL_MASK) {
return STATUS_INVALID_PARAMETER;
} //
// Parent
// if (ARGUMENT_PRESENT (ParentProcess)) {
Status = ObReferenceObjectByHandle (ParentProcess,
PROCESS_CREATE_PROCESS,
PsProcessType,
PreviousMode,
&Parent,
NULL);
if (!NT_SUCCESS (Status)) {
return Status;
} if (JobMemberLevel != 0 && Parent->Job == NULL) {
ObDereferenceObject (Parent);
return STATUS_INVALID_PARAMETER;
} Affinity = Parent->Pcb.Affinity;
WorkingSetMinimum = PsMinimumWorkingSet;
WorkingSetMaximum = PsMaximumWorkingSet; } else { Parent = NULL;
Affinity = KeActiveProcessors;
WorkingSetMinimum = PsMinimumWorkingSet;
WorkingSetMaximum = PsMaximumWorkingSet;
} //
// Create the process object
//
Status = ObCreateObject (PreviousMode,
PsProcessType,
ObjectAttributes,
PreviousMode,
NULL,
sizeof (EPROCESS),
0,
0,
&Process); if (!NT_SUCCESS (Status)) {
goto exit_and_deref_parent;
} //
// The process object is created set to NULL. Errors
// That occur after this step cause the process delete
// routine to be entered.
//
// Teardown actions that occur in the process delete routine
// do not need to be performed inline.
// RtlZeroMemory (Process, sizeof(EPROCESS));
ExInitializeRundownProtection (&Process->RundownProtect);
PspInitializeProcessLock (Process);
InitializeListHead (&Process->ThreadListHead); #if defined(_WIN64) if (Flags & PROCESS_CREATE_FLAGS_OVERRIDE_ADDRESS_SPACE) {
PS_SET_BITS (&Process->Flags, PS_PROCESS_FLAGS_OVERRIDE_ADDRESS_SPACE);
} #endif PspInheritQuota (Process, Parent);
ObInheritDeviceMap (Process, Parent);
if (Parent != NULL) {
Process->DefaultHardErrorProcessing = Parent->DefaultHardErrorProcessing;
Process->InheritedFromUniqueProcessId = Parent->UniqueProcessId; } else {
Process->DefaultHardErrorProcessing = PROCESS_HARDERROR_DEFAULT;
Process->InheritedFromUniqueProcessId = NULL;
} //
// Section
// if (ARGUMENT_PRESENT (SectionHandle)) {
Status = ObReferenceObjectByHandle (SectionHandle,
SECTION_MAP_EXECUTE,
MmSectionObjectType,
PreviousMode,
&SectionObject,
NULL);
if (!NT_SUCCESS (Status)) {
goto exit_and_deref;
} } else {
SectionObject = NULL;
if (Parent != PsInitialSystemProcess) { //
// Fetch the section pointer from the parent process
// as we will be cloning. Since the section pointer
// is removed at last thread exit we need to protect against
// process exit here to be safe.
// if (ExAcquireRundownProtection (&Parent->RundownProtect)) {
SectionObject = Parent->SectionObject;
if (SectionObject != NULL) {
ObReferenceObject (SectionObject);
} ExReleaseRundownProtection (&Parent->RundownProtect);
} if (SectionObject == NULL) {
Status = STATUS_PROCESS_IS_TERMINATING;
goto exit_and_deref;
}
}
} Process->SectionObject = SectionObject; //
// DebugPort
// if (ARGUMENT_PRESENT (DebugPort)) {
Status = ObReferenceObjectByHandle (DebugPort,
DEBUG_PROCESS_ASSIGN,
DbgkDebugObjectType,
PreviousMode,
&DebugPortObject,
NULL); if (!NT_SUCCESS (Status)) {
goto exit_and_deref;
} Process->DebugPort = DebugPortObject;
if (Flags&PROCESS_CREATE_FLAGS_NO_DEBUG_INHERIT) {
PS_SET_BITS (&Process->Flags, PS_PROCESS_FLAGS_NO_DEBUG_INHERIT);
} } else {
if (Parent != NULL) {
DbgkCopyProcessDebugPort (Process, Parent);
}
} //
// ExceptionPort
// if (ARGUMENT_PRESENT (ExceptionPort)) {
Status = ObReferenceObjectByHandle (ExceptionPort,
0,
LpcPortObjectType,
PreviousMode,
&ExceptionPortObject,
NULL); if (!NT_SUCCESS (Status)) {
goto exit_and_deref;
} Process->ExceptionPort = ExceptionPortObject;
} Process->ExitStatus = STATUS_PENDING; //
// Clone parent's object table.
// If no parent (booting) then use the current object table created in
// ObInitSystem.
// if (Parent != NULL) { //
// Calculate address space
//
// If Parent == PspInitialSystem
// if (!MmCreateProcessAddressSpace (WorkingSetMinimum,
Process,
&DirectoryTableBase[0])) { Status = STATUS_INSUFFICIENT_RESOURCES;
goto exit_and_deref;
} } else {
Process->ObjectTable = CurrentProcess->ObjectTable; //
// Initialize the Working Set Mutex and address creation mutex
// for this "hand built" process.
// Normally, the call to MmInitializeAddressSpace initializes the
// working set mutex, however, in this case, we have already initialized
// the address space and we are now creating a second process using
// the address space of the idle thread.
// Status = MmInitializeHandBuiltProcess (Process, &DirectoryTableBase[0]);
if (!NT_SUCCESS (Status)) {
goto exit_and_deref;
}
} PS_SET_BITS (&Process->Flags, PS_PROCESS_FLAGS_HAS_ADDRESS_SPACE);
Process->Vm.MaximumWorkingSetSize = WorkingSetMaximum;
KeInitializeProcess (&Process->Pcb,
NORMAL_BASE_PRIORITY,
Affinity,
&DirectoryTableBase[0],
(BOOLEAN)(Process->DefaultHardErrorProcessing & PROCESS_HARDERROR_ALIGNMENT_BIT)); //
// Initialize the security fields of the process
// The parent may be null exactly once (during system init).
// Thereafter, a parent is always required so that we have a
// security context to duplicate for the new process.
// Status = PspInitializeProcessSecurity (Parent, Process);
if (!NT_SUCCESS (Status)) {
goto exit_and_deref;
} Process->PriorityClass = PROCESS_PRIORITY_CLASS_NORMAL;
if (Parent != NULL) {
if (Parent->PriorityClass == PROCESS_PRIORITY_CLASS_IDLE ||
Parent->PriorityClass == PROCESS_PRIORITY_CLASS_BELOW_NORMAL) {
Process->PriorityClass = Parent->PriorityClass;
} //
// if address space creation worked, then when going through
// delete, we will attach. Of course, attaching means that the kprocess
// must be initialized, so we delay the object stuff till here.
// Status = ObInitProcess ((Flags&PROCESS_CREATE_FLAGS_INHERIT_HANDLES) ? Parent : NULL,
Process); if (!NT_SUCCESS (Status)) {
goto exit_and_deref;
} } else {
Status = MmInitializeHandBuiltProcess2 (Process);
if (!NT_SUCCESS (Status)) {
goto exit_and_deref;
}
} Status = STATUS_SUCCESS;
SavedStatus = STATUS_SUCCESS; //
// Initialize the process address space
// The address space has four possibilities
//
// 1 - Boot Process. Address space is initialized during
// MmInit. Parent is not specified.
//
// 2 - System Process. Address space is a virgin address
// space that only maps system space. Process is same
// as PspInitialSystemProcess.
//
// 3 - User Process (Cloned Address Space). Address space
// is cloned from the specified process.
//
// 4 - User Process (New Image Address Space). Address space
// is initialized so that it maps the specified section.
// if (SectionHandle != NULL) { //
// User Process (New Image Address Space). Don't specify Process to
// clone, just SectionObject.
//
// Passing in the 4th parameter as below lets the EPROCESS struct contain its image file name, provided that
// appropriate audit settings are enabled. Memory is allocated inside of MmInitializeProcessAddressSpace
// and pointed to by ImageFileName, so that must be freed in the process deletion routine (PspDeleteProcess())
// Status = MmInitializeProcessAddressSpace (Process,
NULL,
SectionObject,
&Flags,
&(Process->SeAuditProcessCreationInfo.ImageFileName)); if (!NT_SUCCESS (Status)) {
goto exit_and_deref;
} //
// In order to support relocating executables, the proper status
// (STATUS_IMAGE_NOT_AT_BASE) must be returned, so save it here.
// SavedStatus = Status;
CreatePeb = TRUE;
UseLargePages = ((Flags & PROCESS_CREATE_FLAGS_LARGE_PAGES) != 0 ? TRUE : FALSE); } else if (Parent != NULL) {
if (Parent != PsInitialSystemProcess) {
Process->SectionBaseAddress = Parent->SectionBaseAddress; //
// User Process ( Cloned Address Space ). Don't specify section to
// map, just Process to clone.
// Status = MmInitializeProcessAddressSpace (Process,
Parent,
NULL,
&Flags,
NULL); if (!NT_SUCCESS (Status)) {
goto exit_and_deref;
} CreatePeb = TRUE;
UseLargePages = ((Flags & PROCESS_CREATE_FLAGS_LARGE_PAGES) != 0 ? TRUE : FALSE); //
// A cloned process isn't started from an image file, so we give it the name
// of the process of which it is a clone, provided the original has a name.
// if (Parent->SeAuditProcessCreationInfo.ImageFileName != NULL) {
ImageFileNameSize = sizeof(OBJECT_NAME_INFORMATION) +
Parent->SeAuditProcessCreationInfo.ImageFileName->Name.MaximumLength; Process->SeAuditProcessCreationInfo.ImageFileName =
ExAllocatePoolWithTag (PagedPool,
ImageFileNameSize,
'aPeS'); if (Process->SeAuditProcessCreationInfo.ImageFileName != NULL) {
RtlCopyMemory (Process->SeAuditProcessCreationInfo.ImageFileName,
Parent->SeAuditProcessCreationInfo.ImageFileName,
ImageFileNameSize); //
// The UNICODE_STRING in the process is self contained, so calculate the
// offset for the buffer.
// Process->SeAuditProcessCreationInfo.ImageFileName->Name.Buffer =
(PUSHORT)(((PUCHAR) Process->SeAuditProcessCreationInfo.ImageFileName) +
sizeof(UNICODE_STRING)); } else {
Status = STATUS_INSUFFICIENT_RESOURCES;
goto exit_and_deref;
}
} } else { //
// System Process. Don't specify Process to clone or section to map
// Flags &= ~PROCESS_CREATE_FLAGS_ALL_LARGE_PAGE_FLAGS;
Status = MmInitializeProcessAddressSpace (Process,
NULL,
NULL,
&Flags,
NULL); if (!NT_SUCCESS (Status)) {
goto exit_and_deref;
} //
// In case the image file name of this system process is ever queried, we give
// a zero length UNICODE_STRING.
// Process->SeAuditProcessCreationInfo.ImageFileName =
ExAllocatePoolWithTag (PagedPool,
sizeof(OBJECT_NAME_INFORMATION),
'aPeS'); if (Process->SeAuditProcessCreationInfo.ImageFileName != NULL) {
RtlZeroMemory (Process->SeAuditProcessCreationInfo.ImageFileName,
sizeof(OBJECT_NAME_INFORMATION));
} else {
Status = STATUS_INSUFFICIENT_RESOURCES;
goto exit_and_deref;
}
}
} //
// Create the process ID
// CidEntry.Object = Process;
CidEntry.GrantedAccess = 0;
Process->UniqueProcessId = ExCreateHandle (PspCidTable, &CidEntry);
if (Process->UniqueProcessId == NULL) {
Status = STATUS_INSUFFICIENT_RESOURCES;
goto exit_and_deref;
} ExSetHandleTableOwner (Process->ObjectTable, Process->UniqueProcessId); //
// Audit the process creation.
// if (SeDetailedAuditingWithToken (NULL)) {
SeAuditProcessCreation (Process);
} //
// See if the parent has a job. If so reference the job
// and add the process in.
// if (Parent) {
Job = Parent->Job;
if (Job != NULL && !(Job->LimitFlags & JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK)) {
if (Flags&PROCESS_CREATE_FLAGS_BREAKAWAY) {
if (!(Job->LimitFlags & JOB_OBJECT_LIMIT_BREAKAWAY_OK)) {
Status = STATUS_ACCESS_DENIED; } else {
Status = STATUS_SUCCESS;
} } else {
Status = PspGetJobFromSet (Job, JobMemberLevel, &Process->Job);
if (NT_SUCCESS (Status)) {
PACCESS_TOKEN Token, NewToken;
Job = Process->Job;
Status = PspAddProcessToJob (Job, Process); //
// Duplicate a new process token if one is specified for the job
// Token = Job->Token;
if (Token != NULL) {
Status = SeSubProcessToken (Token,
&NewToken,
FALSE,
Job->SessionId); if (!NT_SUCCESS (Status)) {
goto exit_and_deref;
} SeAssignPrimaryToken (Process, NewToken);
ObDereferenceObject (NewToken);
}
}
} if (!NT_SUCCESS (Status)) {
goto exit_and_deref;
}
}
} if (Parent && CreatePeb) { //
// For processes created w/ a section,
// a new "virgin" PEB is created. Otherwise,
// for forked processes, uses inherited PEB
// with an updated mutant.
// RtlZeroMemory (&InitialPeb, FIELD_OFFSET(INITIAL_PEB, Mutant)); InitialPeb.Mutant = (HANDLE)(-1);
InitialPeb.ImageUsesLargePages = (BOOLEAN) UseLargePages; if (SectionHandle != NULL) {
Status = MmCreatePeb (Process, &InitialPeb, &Process->Peb);
if (!NT_SUCCESS (Status)) {
Process->Peb = NULL;
goto exit_and_deref;
} Peb = Process->Peb; } else {
SIZE_T BytesCopied; InitialPeb.InheritedAddressSpace = TRUE;
Process->Peb = Parent->Peb;
MmCopyVirtualMemory (CurrentProcess,
&InitialPeb,
Process,
Process->Peb,
sizeof (INITIAL_PEB),
KernelMode,
&BytesCopied); #if defined(_WIN64)
if (Process->Wow64Process != NULL) { RtlZeroMemory (&InitialPeb32, FIELD_OFFSET(INITIAL_PEB32, Mutant));
InitialPeb32.Mutant = -1;
InitialPeb32.InheritedAddressSpace = TRUE;
InitialPeb32.ImageUsesLargePages = (BOOLEAN) UseLargePages; MmCopyVirtualMemory (CurrentProcess,
&InitialPeb32,
Process,
Process->Wow64Process->Wow64,
sizeof (INITIAL_PEB32),
KernelMode,
&BytesCopied);
}
#endif }
} Peb = Process->Peb; //
// Add the process to the global list of processes.
// PspLockProcessList (CurrentThread);
InsertTailList (&PsActiveProcessHead, &Process->ActiveProcessLinks);
PspUnlockProcessList (CurrentThread);
AccessState = NULL;
if (!PsUseImpersonationToken) {
AccessState = &LocalAccessState;
Status = SeCreateAccessStateEx (NULL,
(Parent == NULL || Parent != PsInitialSystemProcess)?
PsGetCurrentProcessByThread (CurrentThread) :
PsInitialSystemProcess,
AccessState,
&AuxData,
DesiredAccess,
&PsProcessType->TypeInfo.GenericMapping);
if (!NT_SUCCESS (Status)) {
goto exit_and_deref;
}
} //
// Insert the object. Once we do this is reachable from the outside world via
// open by name. Open by ID is still disabled. Since its reachable
// somebody might create a thread in the process and cause
// rundown.
// Status = ObInsertObject (Process,
AccessState,
DesiredAccess,
1, // bias the refcnt by one for future process manipulations
NULL,
&LocalProcessHandle); if (AccessState != NULL) {
SeDeleteAccessState (AccessState);
} if (!NT_SUCCESS (Status)) {
goto exit_and_deref_parent;
} //
// Compute the base priority and quantum reset values for the process and
// set the memory priority.
// ASSERT(IsListEmpty(&Process->ThreadListHead) == TRUE); BasePriority = PspComputeQuantumAndPriority(Process,
PsProcessPriorityBackground,
&QuantumReset); Process->Pcb.BasePriority = (SCHAR)BasePriority;
Process->Pcb.QuantumReset = QuantumReset; //
// As soon as a handle to the process is accessible, allow the process to
// be deleted.
// Process->GrantedAccess = PROCESS_TERMINATE;
if (Parent && Parent != PsInitialSystemProcess) {
Status = ObGetObjectSecurity (Process,
&SecurityDescriptor,
&MemoryAllocated); if (!NT_SUCCESS (Status)) {
ObCloseHandle (LocalProcessHandle, PreviousMode);
goto exit_and_deref;
} //
// Compute the subject security context
// SubjectContext.ProcessAuditId = Process;
SubjectContext.PrimaryToken = PsReferencePrimaryToken(Process);
SubjectContext.ClientToken = NULL;
AccessCheck = SeAccessCheck (SecurityDescriptor,
&SubjectContext,
FALSE,
MAXIMUM_ALLOWED,
0,
NULL,
&PsProcessType->TypeInfo.GenericMapping,
PreviousMode,
&Process->GrantedAccess,
&accesst); PsDereferencePrimaryTokenEx (Process, SubjectContext.PrimaryToken);
ObReleaseObjectSecurity (SecurityDescriptor,
MemoryAllocated); if (!AccessCheck) {
Process->GrantedAccess = 0;
} //
// It does not make any sense to create a process that can not
// do anything to itself.
// Note: Changes to this set of bits should be reflected in psquery.c
// code, in PspSetPrimaryToken.
// Process->GrantedAccess |= (PROCESS_VM_OPERATION |
PROCESS_VM_READ |
PROCESS_VM_WRITE |
PROCESS_QUERY_INFORMATION |
PROCESS_TERMINATE |
PROCESS_CREATE_THREAD |
PROCESS_DUP_HANDLE |
PROCESS_CREATE_PROCESS |
PROCESS_SET_INFORMATION |
STANDARD_RIGHTS_ALL |
PROCESS_SET_QUOTA); } else {
Process->GrantedAccess = PROCESS_ALL_ACCESS;
} KeQuerySystemTime (&Process->CreateTime);
try {
if (Peb != NULL && CurrentThread->Tcb.Teb != NULL) {
((PTEB)(CurrentThread->Tcb.Teb))->NtTib.ArbitraryUserPointer = Peb;
} *ProcessHandle = LocalProcessHandle; } except (EXCEPTION_EXECUTE_HANDLER) {
NOTHING;
} if (SavedStatus != STATUS_SUCCESS) {
Status = SavedStatus;
} exit_and_deref:
ObDereferenceObject (Process); exit_and_deref_parent:
if (Parent != NULL) {
ObDereferenceObject (Parent);
} return Status;
}

  

 

  来具体分析一下PspCreateProcess所做的关键工作:

  (1)调用ObCreateObject 创建一个类型为PsProcessType的内核对象,置于局部变量Process中,对象体为EPROCESS,即创建EPROCESS

    Status = ObCreateObject (PreviousMode,
PsProcessType,
ObjectAttributes,
PreviousMode,
NULL,
sizeof (EPROCESS),
0,
0,
&Process);

 

  (2)MmCreateProcessAddressSpace创建新的地址空间

    if (Parent != NULL) {
//创建新的地址空间
if (!MmCreateProcessAddressSpace (WorkingSetMinimum,
Process,
&DirectoryTableBase[0])) { Status = STATUS_INSUFFICIENT_RESOURCES;
goto exit_and_deref;
}

  

  (3)MmInitializeProcessAddressSpace 初始化新进程的地址空间

if (Parent != PsInitialSystemProcess) {
       //根据父进程来初始化进程地址空间,并把父进程的映像名称拷贝到新进程对象的数据结构中。
Process->SectionBaseAddress = Parent->SectionBaseAddress; //
// User Process ( Cloned Address Space ). Don't specify section to
// map, just Process to clone.
// Status = MmInitializeProcessAddressSpace (Process,
Parent,
NULL,
&Flags,
NULL); if (!NT_SUCCESS (Status)) {
goto exit_and_deref;
} CreatePeb = TRUE;
UseLargePages = ((Flags & PROCESS_CREATE_FLAGS_LARGE_PAGES) != 0 ? TRUE : FALSE); //
// A cloned process isn't started from an image file, so we give it the name
// of the process of which it is a clone, provided the original has a name.
// if (Parent->SeAuditProcessCreationInfo.ImageFileName != NULL) {
ImageFileNameSize = sizeof(OBJECT_NAME_INFORMATION) +
Parent->SeAuditProcessCreationInfo.ImageFileName->Name.MaximumLength; Process->SeAuditProcessCreationInfo.ImageFileName =
ExAllocatePoolWithTag (PagedPool,
ImageFileNameSize,
'aPeS'); if (Process->SeAuditProcessCreationInfo.ImageFileName != NULL) {
RtlCopyMemory (Process->SeAuditProcessCreationInfo.ImageFileName,
Parent->SeAuditProcessCreationInfo.ImageFileName,
ImageFileNameSize); //
// The UNICODE_STRING in the process is self contained, so calculate the
// offset for the buffer.
// Process->SeAuditProcessCreationInfo.ImageFileName->Name.Buffer =
(PUSHORT)(((PUCHAR) Process->SeAuditProcessCreationInfo.ImageFileName) +
sizeof(UNICODE_STRING)); } else {
Status = STATUS_INSUFFICIENT_RESOURCES;
goto exit_and_deref;
}
} } else { //
// System Process. Don't specify Process to clone or section to map
// Flags &= ~PROCESS_CREATE_FLAGS_ALL_LARGE_PAGE_FLAGS;
Status = MmInitializeProcessAddressSpace (Process,
NULL,
NULL,
&Flags,
NULL); if (!NT_SUCCESS (Status)) {
goto exit_and_deref;
} //
// In case the image file name of this system process is ever queried, we give
// a zero length UNICODE_STRING.
// Process->SeAuditProcessCreationInfo.ImageFileName =
ExAllocatePoolWithTag (PagedPool,
sizeof(OBJECT_NAME_INFORMATION),
'aPeS'); if (Process->SeAuditProcessCreationInfo.ImageFileName != NULL) {
RtlZeroMemory (Process->SeAuditProcessCreationInfo.ImageFileName,
sizeof(OBJECT_NAME_INFORMATION));
} else {
Status = STATUS_INSUFFICIENT_RESOURCES;
goto exit_and_deref;
}
}
}

  (4)ExCreateHandle函数在CID句柄表中创建一个进程ID项。

  

    CidEntry.Object = Process;
CidEntry.GrantedAccess = 0;
Process->UniqueProcessId = ExCreateHandle (PspCidTable, &CidEntry);
if (Process->UniqueProcessId == NULL) {
Status = STATUS_INSUFFICIENT_RESOURCES;
goto exit_and_deref;
} ExSetHandleTableOwner (Process->ObjectTable, Process->UniqueProcessId);

  (5)MmCreatePeb 创建一个PEB,如果进程通过映像内存区来创建,创建一个PEB,如果是进程拷贝,则使用继承的PEB。

  

 RtlZeroMemory (&InitialPeb, FIELD_OFFSET(INITIAL_PEB, Mutant));

        InitialPeb.Mutant = (HANDLE)(-1);
InitialPeb.ImageUsesLargePages = (BOOLEAN) UseLargePages; if (SectionHandle != NULL) {
Status = MmCreatePeb (Process, &InitialPeb, &Process->Peb);
if (!NT_SUCCESS (Status)) {
Process->Peb = NULL;
goto exit_and_deref;
} Peb = Process->Peb; } else {
SIZE_T BytesCopied; InitialPeb.InheritedAddressSpace = TRUE;
Process->Peb = Parent->Peb;
MmCopyVirtualMemory (CurrentProcess,
&InitialPeb,
Process,
Process->Peb,
sizeof (INITIAL_PEB),
KernelMode,
&BytesCopied);

  (6)InsertTailList 函数把新进程加入全局的LIST_ENTRY进程链表中(位置:PsActiveProcessHead)

    PspLockProcessList (CurrentThread);
InsertTailList (&PsActiveProcessHead, &Process->ActiveProcessLinks);
PspUnlockProcessList (CurrentThread);
AccessState = NULL;
if (!PsUseImpersonationToken) {
AccessState = &LocalAccessState;
Status = SeCreateAccessStateEx (NULL,
(Parent == NULL || Parent != PsInitialSystemProcess)?
PsGetCurrentProcessByThread (CurrentThread) :
PsInitialSystemProcess,
AccessState,
&AuxData,
DesiredAccess,
&PsProcessType->TypeInfo.GenericMapping);
if (!NT_SUCCESS (Status)) {
goto exit_and_deref;
}
}

  (7)ObInsertObject 将新进程对象记录到当前进程的句柄表中。

Status = ObInsertObject (Process,
AccessState,
DesiredAccess,
1, // bias the refcnt by one for future process manipulations
NULL,
&LocalProcessHandle);

  

  

  NtCreateProcess结束后,就创建好了进程,然而进程只不过是一个容器,接下来的代码就可以看到NtCreateThread函数的线程创建了:

NTSTATUS
NtCreateThread(
__out PHANDLE ThreadHandle,
__in ACCESS_MASK DesiredAccess,
__in_opt POBJECT_ATTRIBUTES ObjectAttributes,
__in HANDLE ProcessHandle,
__out PCLIENT_ID ClientId,
__in PCONTEXT ThreadContext,
__in PINITIAL_TEB InitialTeb,
__in BOOLEAN CreateSuspended
)
/*++ Routine Description: This system service API creates and initializes a thread object. --*/ {
NTSTATUS Status;
INITIAL_TEB CapturedInitialTeb; PAGED_CODE(); //
// Probe all arguments
// try {
if (KeGetPreviousMode () != KernelMode) {
ProbeForWriteHandle (ThreadHandle); if (ARGUMENT_PRESENT (ClientId)) {
ProbeForWriteSmallStructure (ClientId, sizeof (CLIENT_ID), sizeof (ULONG));
} if (ARGUMENT_PRESENT (ThreadContext) ) {
ProbeForReadSmallStructure (ThreadContext, sizeof (CONTEXT), CONTEXT_ALIGN);
} else {
return STATUS_INVALID_PARAMETER;
}
ProbeForReadSmallStructure (InitialTeb, sizeof (InitialTeb->OldInitialTeb), sizeof (ULONG));
} CapturedInitialTeb.OldInitialTeb = InitialTeb->OldInitialTeb;
if (CapturedInitialTeb.OldInitialTeb.OldStackBase == NULL &&
CapturedInitialTeb.OldInitialTeb.OldStackLimit == NULL) {
//
// Since the structure size here is less than 64k we don't need to reprobe
//
CapturedInitialTeb = *InitialTeb;
}
} except (ExSystemExceptionFilter ()) {
return GetExceptionCode ();
} Status = PspCreateThread (ThreadHandle,
DesiredAccess,
ObjectAttributes,
ProcessHandle,
NULL,
ClientId,
ThreadContext,
&CapturedInitialTeb,
CreateSuspended,
NULL,
NULL); return Status;
}

  

  看到这里NtCreateThread函数还是调用了PspCreateThread函数来完成实际的工作。NtCreateThread——>PspCreateThread

NTSTATUS
PspCreateThread(
OUT PHANDLE ThreadHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
IN HANDLE ProcessHandle,
IN PEPROCESS ProcessPointer,
OUT PCLIENT_ID ClientId OPTIONAL,
IN PCONTEXT ThreadContext OPTIONAL,
IN PINITIAL_TEB InitialTeb OPTIONAL,
IN BOOLEAN CreateSuspended,
IN PKSTART_ROUTINE StartRoutine OPTIONAL,
IN PVOID StartContext
)
/*++
Routine Description:
This routine creates and initializes a thread object. It implements the
foundation for NtCreateThread and for PsCreateSystemThread.
--*/ { HANDLE_TABLE_ENTRY CidEntry;
NTSTATUS Status;
PETHREAD Thread;
PETHREAD CurrentThread;
PEPROCESS Process;
PTEB Teb;
KPROCESSOR_MODE PreviousMode;
HANDLE LocalThreadHandle;
BOOLEAN AccessCheck;
BOOLEAN MemoryAllocated;
PSECURITY_DESCRIPTOR SecurityDescriptor;
SECURITY_SUBJECT_CONTEXT SubjectContext;
NTSTATUS accesst;
LARGE_INTEGER CreateTime;
ULONG OldActiveThreads;
PEJOB Job;
AUX_ACCESS_DATA AuxData;
PACCESS_STATE AccessState;
ACCESS_STATE LocalAccessState; PAGED_CODE(); CurrentThread = PsGetCurrentThread (); if (StartRoutine != NULL) {
PreviousMode = KernelMode;
} else {
PreviousMode = KeGetPreviousModeByThread (&CurrentThread->Tcb);
} Teb = NULL; Thread = NULL;
Process = NULL; if (ProcessHandle != NULL) {
//
// Process object reference count is biased by one for each thread.
// This accounts for the pointer given to the kernel that remains
// in effect until the thread terminates (and becomes signaled)
// Status = ObReferenceObjectByHandle (ProcessHandle,
PROCESS_CREATE_THREAD,
PsProcessType,
PreviousMode,
&Process,
NULL);
} else {
if (StartRoutine != NULL) {
ObReferenceObject (ProcessPointer);
Process = ProcessPointer;
Status = STATUS_SUCCESS;
} else {
Status = STATUS_INVALID_HANDLE;
}
} if (!NT_SUCCESS (Status)) {
return Status;
} //
// If the previous mode is user and the target process is the system
// process, then the operation cannot be performed.
// if ((PreviousMode != KernelMode) && (Process == PsInitialSystemProcess)) {
ObDereferenceObject (Process);
return STATUS_INVALID_HANDLE;
} Status = ObCreateObject (PreviousMode,
PsThreadType,
ObjectAttributes,
PreviousMode,
NULL,
sizeof(ETHREAD),
0,
0,
&Thread); if (!NT_SUCCESS (Status)) {
ObDereferenceObject (Process);
return Status;
} RtlZeroMemory (Thread, sizeof (ETHREAD)); //
// Initialize rundown protection for cross thread TEB refs etc.
//
ExInitializeRundownProtection (&Thread->RundownProtect); //
// Assign this thread to the process so that from now on
// we don't have to dereference in error paths.
//
Thread->ThreadsProcess = Process; Thread->Cid.UniqueProcess = Process->UniqueProcessId; CidEntry.Object = Thread;
CidEntry.GrantedAccess = 0;
Thread->Cid.UniqueThread = ExCreateHandle (PspCidTable, &CidEntry); if (Thread->Cid.UniqueThread == NULL) {
ObDereferenceObject (Thread);
return (STATUS_INSUFFICIENT_RESOURCES);
} //
// Initialize Mm
// Thread->ReadClusterSize = MmReadClusterSize; //
// Initialize LPC
// KeInitializeSemaphore (&Thread->LpcReplySemaphore, 0L, 1L);
InitializeListHead (&Thread->LpcReplyChain); //
// Initialize Io
// InitializeListHead (&Thread->IrpList); //
// Initialize Registry
// InitializeListHead (&Thread->PostBlockList); //
// Initialize the thread lock
// PspInitializeThreadLock (Thread); KeInitializeSpinLock (&Thread->ActiveTimerListLock);
InitializeListHead (&Thread->ActiveTimerListHead); if (!ExAcquireRundownProtection (&Process->RundownProtect)) {
ObDereferenceObject (Thread);
return STATUS_PROCESS_IS_TERMINATING;
} if (ARGUMENT_PRESENT (ThreadContext)) { //
// User-mode thread. Create TEB etc
// Status = MmCreateTeb (Process, InitialTeb, &Thread->Cid, &Teb);
if (!NT_SUCCESS (Status)) {
ExReleaseRundownProtection (&Process->RundownProtect);
ObDereferenceObject (Thread);
return Status;
} try {
//
// Initialize kernel thread object for user mode thread.
// Thread->StartAddress = (PVOID)CONTEXT_TO_PROGRAM_COUNTER(ThreadContext); #if defined(_AMD64_) Thread->Win32StartAddress = (PVOID)ThreadContext->Rdx; #elif defined(_X86_) Thread->Win32StartAddress = (PVOID)ThreadContext->Eax; #else #error "no target architecture" #endif } except (EXCEPTION_EXECUTE_HANDLER) { Status = GetExceptionCode();
} if (NT_SUCCESS (Status)) {
Status = KeInitThread (&Thread->Tcb,
NULL,
PspUserThreadStartup,
(PKSTART_ROUTINE)NULL,
Thread->StartAddress,
ThreadContext,
Teb,
&Process->Pcb);
} } else { Teb = NULL;
//
// Set the system thread bit thats kept for all time
//
PS_SET_BITS (&Thread->CrossThreadFlags, PS_CROSS_THREAD_FLAGS_SYSTEM); //
// Initialize kernel thread object for kernel mode thread.
// Thread->StartAddress = (PKSTART_ROUTINE) StartRoutine;
Status = KeInitThread (&Thread->Tcb,
NULL,
PspSystemThreadStartup,
StartRoutine,
StartContext,
NULL,
NULL,
&Process->Pcb);
} if (!NT_SUCCESS (Status)) {
if (Teb != NULL) {
MmDeleteTeb(Process, Teb);
}
ExReleaseRundownProtection (&Process->RundownProtect);
ObDereferenceObject (Thread);
return Status;
} PspLockProcessExclusive (Process, CurrentThread);
//
// Process is exiting or has had delete process called
// We check the calling threads termination status so we
// abort any thread creates while ExitProcess is being called --
// but the call is blocked only if the new thread would be created
// in the terminating thread's process.
//
if ((Process->Flags&PS_PROCESS_FLAGS_PROCESS_DELETE) != 0 ||
(((CurrentThread->CrossThreadFlags&PS_CROSS_THREAD_FLAGS_TERMINATED) != 0) &&
(ThreadContext != NULL) &&
(THREAD_TO_PROCESS(CurrentThread) == Process))) { PspUnlockProcessExclusive (Process, CurrentThread); KeUninitThread (&Thread->Tcb); if (Teb != NULL) {
MmDeleteTeb(Process, Teb);
}
ExReleaseRundownProtection (&Process->RundownProtect);
ObDereferenceObject(Thread); return STATUS_PROCESS_IS_TERMINATING;
} OldActiveThreads = Process->ActiveThreads++;
InsertTailList (&Process->ThreadListHead, &Thread->ThreadListEntry); KeStartThread (&Thread->Tcb); PspUnlockProcessExclusive (Process, CurrentThread); ExReleaseRundownProtection (&Process->RundownProtect); //
// Failures that occur after this point cause the thread to
// go through PspExitThread
// if (OldActiveThreads == 0) {
PERFINFO_PROCESS_CREATE (Process); if (PspCreateProcessNotifyRoutineCount != 0) {
ULONG i;
PEX_CALLBACK_ROUTINE_BLOCK CallBack;
PCREATE_PROCESS_NOTIFY_ROUTINE Rtn; for (i=0; i<PSP_MAX_CREATE_PROCESS_NOTIFY; i++) {
CallBack = ExReferenceCallBackBlock (&PspCreateProcessNotifyRoutine[i]);
if (CallBack != NULL) {
Rtn = (PCREATE_PROCESS_NOTIFY_ROUTINE) ExGetCallBackBlockRoutine (CallBack);
Rtn (Process->InheritedFromUniqueProcessId,
Process->UniqueProcessId,
TRUE);
ExDereferenceCallBackBlock (&PspCreateProcessNotifyRoutine[i],
CallBack);
}
}
}
} //
// If the process has a job with a completion port,
// AND if the process is really considered to be in the Job, AND
// the process has not reported, report in
//
// This should really be done in add process to job, but can't
// in this path because the process's ID isn't assigned until this point
// in time
//
Job = Process->Job;
if (Job != NULL && Job->CompletionPort &&
!(Process->JobStatus & (PS_JOB_STATUS_NOT_REALLY_ACTIVE|PS_JOB_STATUS_NEW_PROCESS_REPORTED))) { PS_SET_BITS (&Process->JobStatus, PS_JOB_STATUS_NEW_PROCESS_REPORTED); KeEnterCriticalRegionThread (&CurrentThread->Tcb);
ExAcquireResourceSharedLite (&Job->JobLock, TRUE);
if (Job->CompletionPort != NULL) {
IoSetIoCompletion (Job->CompletionPort,
Job->CompletionKey,
(PVOID)Process->UniqueProcessId,
STATUS_SUCCESS,
JOB_OBJECT_MSG_NEW_PROCESS,
FALSE);
}
ExReleaseResourceLite (&Job->JobLock);
KeLeaveCriticalRegionThread (&CurrentThread->Tcb);
} PERFINFO_THREAD_CREATE(Thread, InitialTeb); //
// Notify registered callout routines of thread creation.
// if (PspCreateThreadNotifyRoutineCount != 0) {
ULONG i;
PEX_CALLBACK_ROUTINE_BLOCK CallBack;
PCREATE_THREAD_NOTIFY_ROUTINE Rtn; for (i = 0; i < PSP_MAX_CREATE_THREAD_NOTIFY; i++) {
CallBack = ExReferenceCallBackBlock (&PspCreateThreadNotifyRoutine[i]);
if (CallBack != NULL) {
Rtn = (PCREATE_THREAD_NOTIFY_ROUTINE) ExGetCallBackBlockRoutine (CallBack);
Rtn (Thread->Cid.UniqueProcess,
Thread->Cid.UniqueThread,
TRUE);
ExDereferenceCallBackBlock (&PspCreateThreadNotifyRoutine[i],
CallBack);
}
}
} //
// Reference count of thread is biased once for itself and once for the handle if we create it.
// ObReferenceObjectEx (Thread, 2); if (CreateSuspended) {
try {
KeSuspendThread (&Thread->Tcb);
} except ((GetExceptionCode () == STATUS_SUSPEND_COUNT_EXCEEDED)?
EXCEPTION_EXECUTE_HANDLER :
EXCEPTION_CONTINUE_SEARCH) {
}
//
// If deletion was started after we suspended then wake up the thread
//
if (Thread->CrossThreadFlags&PS_CROSS_THREAD_FLAGS_TERMINATED) {
KeForceResumeThread (&Thread->Tcb);
}
} AccessState = NULL;
if (!PsUseImpersonationToken) {
AccessState = &LocalAccessState;
Status = SeCreateAccessStateEx (NULL,
ARGUMENT_PRESENT (ThreadContext)?PsGetCurrentProcessByThread (CurrentThread) : Process,
AccessState,
&AuxData,
DesiredAccess,
&PsThreadType->TypeInfo.GenericMapping); if (!NT_SUCCESS (Status)) {
PS_SET_BITS (&Thread->CrossThreadFlags,
PS_CROSS_THREAD_FLAGS_DEADTHREAD); if (CreateSuspended) {
(VOID) KeResumeThread (&Thread->Tcb);
}
KeReadyThread (&Thread->Tcb);
ObDereferenceObjectEx (Thread, 2); return Status;
}
} Status = ObInsertObject (Thread,
AccessState,
DesiredAccess,
0,
NULL,
&LocalThreadHandle); if (AccessState != NULL) {
SeDeleteAccessState (AccessState);
} if (!NT_SUCCESS (Status)) { //
// The insert failed. Terminate the thread.
// //
// This trick is used so that Dbgk doesn't report
// events for dead threads
// PS_SET_BITS (&Thread->CrossThreadFlags,
PS_CROSS_THREAD_FLAGS_DEADTHREAD); if (CreateSuspended) {
KeResumeThread (&Thread->Tcb);
} } else { try { *ThreadHandle = LocalThreadHandle;
if (ARGUMENT_PRESENT (ClientId)) {
*ClientId = Thread->Cid;
}
} except(EXCEPTION_EXECUTE_HANDLER) { PS_SET_BITS (&Thread->CrossThreadFlags,
PS_CROSS_THREAD_FLAGS_DEADTHREAD); if (CreateSuspended) {
(VOID) KeResumeThread (&Thread->Tcb);
}
KeReadyThread (&Thread->Tcb);
ObDereferenceObject (Thread);
ObCloseHandle (LocalThreadHandle, PreviousMode);
return GetExceptionCode();
}
} KeQuerySystemTime(&CreateTime);
ASSERT ((CreateTime.HighPart & 0xf0000000) == 0);
PS_SET_THREAD_CREATE_TIME(Thread, CreateTime); if ((Thread->CrossThreadFlags&PS_CROSS_THREAD_FLAGS_DEADTHREAD) == 0) {
Status = ObGetObjectSecurity (Thread,
&SecurityDescriptor,
&MemoryAllocated);
if (!NT_SUCCESS (Status)) {
//
// This trick us used so that Dbgk doesn't report
// events for dead threads
//
PS_SET_BITS (&Thread->CrossThreadFlags,
PS_CROSS_THREAD_FLAGS_DEADTHREAD); if (CreateSuspended) {
KeResumeThread(&Thread->Tcb);
}
KeReadyThread (&Thread->Tcb);
ObDereferenceObject (Thread);
ObCloseHandle (LocalThreadHandle, PreviousMode);
return Status;
} //
// Compute the subject security context
// SubjectContext.ProcessAuditId = Process;
SubjectContext.PrimaryToken = PsReferencePrimaryToken(Process);
SubjectContext.ClientToken = NULL; AccessCheck = SeAccessCheck (SecurityDescriptor,
&SubjectContext,
FALSE,
MAXIMUM_ALLOWED,
0,
NULL,
&PsThreadType->TypeInfo.GenericMapping,
PreviousMode,
&Thread->GrantedAccess,
&accesst); PsDereferencePrimaryTokenEx (Process, SubjectContext.PrimaryToken); ObReleaseObjectSecurity (SecurityDescriptor,
MemoryAllocated); if (!AccessCheck) {
Thread->GrantedAccess = 0;
} Thread->GrantedAccess |= (THREAD_TERMINATE | THREAD_SET_INFORMATION | THREAD_QUERY_INFORMATION); } else {
Thread->GrantedAccess = THREAD_ALL_ACCESS;
} KeReadyThread (&Thread->Tcb);
ObDereferenceObject (Thread); return Status;
}

  继续分析PspCreateThread函数实现的关键步骤:

  (1)ObCreateObject创建一个线程对象ETHREAD,并初始化为零。

    Status = ObCreateObject (PreviousMode,
PsThreadType,
ObjectAttributes,
PreviousMode,
NULL,
sizeof(ETHREAD),
0,
0,
&Thread); if (!NT_SUCCESS (Status)) {
ObDereferenceObject (Process);
return Status;
} RtlZeroMemory (Thread, sizeof (ETHREAD));

  (2)ExCreateHandle函数创建线程ID

Thread->ThreadsProcess = Process;

    Thread->Cid.UniqueProcess = Process->UniqueProcessId;

    CidEntry.Object = Thread;
CidEntry.GrantedAccess = 0;
Thread->Cid.UniqueThread = ExCreateHandle (PspCidTable, &CidEntry); if (Thread->Cid.UniqueThread == NULL) {
ObDereferenceObject (Thread);
return (STATUS_INSUFFICIENT_RESOURCES);
}

  (3)MmCreateTeb函数创建TEB

Status = MmCreateTeb (Process, InitialTeb, &Thread->Cid, &Teb);
if (!NT_SUCCESS (Status)) {
ExReleaseRundownProtection (&Process->RundownProtect);
ObDereferenceObject (Thread);
return Status;
}

  (4)利用ThreadContext中的程序指针(Eip寄存器)来设置线程的启动地址

       Thread->StartAddress = (PKSTART_ROUTINE) StartRoutine;

  (5)InsertTailList 函数将线程插入线程LIST_ENTRY链表

OldActiveThreads = Process->ActiveThreads++;
InsertTailList (&Process->ThreadListHead, &Thread->ThreadListEntry);

  (6)判断如果这个线程是进程中的第一个进程,回调函数则触发进程的创建通知

if (OldActiveThreads == 0) {
PERFINFO_PROCESS_CREATE (Process); if (PspCreateProcessNotifyRoutineCount != 0) {
ULONG i;
PEX_CALLBACK_ROUTINE_BLOCK CallBack;
PCREATE_PROCESS_NOTIFY_ROUTINE Rtn; for (i=0; i<PSP_MAX_CREATE_PROCESS_NOTIFY; i++) {
CallBack = ExReferenceCallBackBlock (&PspCreateProcessNotifyRoutine[i]);
if (CallBack != NULL) {
Rtn = (PCREATE_PROCESS_NOTIFY_ROUTINE) ExGetCallBackBlockRoutine (CallBack);
Rtn (Process->InheritedFromUniqueProcessId,
Process->UniqueProcessId,
TRUE);
ExDereferenceCallBackBlock (&PspCreateProcessNotifyRoutine[i],
CallBack);
}
}
}
}

  (7)ObInsertObject函数将线程对象插入到当前进程的句柄表中

  

    Status = ObInsertObject (Thread,
AccessState,
DesiredAccess,
0,
NULL,
&LocalThreadHandle);

  (8)KeReadyThread函数使线程进入“就绪”状态,准备马上执行

  

    KeReadyThread (&Thread->Tcb);
ObDereferenceObject (Thread);

  到目前为止进程对象和线程对象的创建工作就完成了。

0x03  创建进程详细步骤2: 通知windows子系统有新进程创建,启动初始线程,用户空间的初始化和Dll连接

  四:通知windows子系统

  每个进程在创建/退出的时候都要向windows子系统进程csrss.exe进程发出通知,因为它担负着对windows所有进程的管理的责任,
注意,这里发出通知的是CreateProcess的调用者,不是新建出来的进程,因为它还没有开始运行。
 
  至此,CreateProcess的操作已经完成,但子进程中的线程却尚未开始运行,它的运行还要经历下面的第五和第六阶段。
 
  五:启动初始线程

  在内核中,新线程的启动例程是KiThreadStartup函数,这是当PspCreateThread 调用KeInitThread 函数时,KeInitThread 函数调用KiInitializeContextThread(参见base\ntos\ke\i386\thredini.c 文件)来设置的。

KiThreadStartup 函数首先将IRQL 降低到APC_LEVEL,然后调用系统初始的线程函数PspUserThreadStartup。这里的PspUserThreadStartup 函数是PspCreateThread 函数在调用KeInitThread 时指定的,。注意,PspCreateThread函数在创建系统线程时指定的初始线程函数为PspSystemThreadStartup  。线程启动函数被作为一个参数传递给PspUserThreadStartup,在这里,它应该是kernel32.dll 中的BaseProcessStart。

PspUserThreadStartup 函数被调用。逻辑并不复杂,但是涉及异步函数调用(APC)机制。

  新创建的线程未必是可以被立即调度运行的,因为用户可能在创建时把标志位CREATE_ SUSPENDED设成了1;
如果那样的话,就需要等待别的进程通过系统调用恢复其运行资格以后才可以被调度运行。否则现在已经可以被调度运行了。至于什么时候才会被调度运行,则就要看优先级等等条件了。
 
 
  六:用户空间的初始化和Dll连接

  PspUserThreadStartup 函数返回以后,KiThreadStartup 函数返回到用户模式,此时,PspUserThreadStartup 插入的APC 被交付,于是LdrInitializeThunk 函数被调用,这是映像加载器(image loader)的初始化函数。LdrInitializeThunk 函数完成加载器、堆管理器等初始化工作,然后加载任何必要的DLL,并且调用这些DLL 的入口函数。最后,当LdrInitializeThunk 返回到用户模式APC 分发器时,该线程开始在用户模式下执行,调用应用程序指定的线程启动函数,此启动函数的地址已经在APC 交付时被压到用户栈中。

  DLL连接由ntdll.dll中的LdrInitializeThunk()在用户空间完成。在此之前ntdll.dll与应用软件尚未连接,但是已经被映射到了用户空间
函数LdrInitializeThunk()在映像中的位置是系统初始化时就预先确定并记录在案的,所以在进入这个函数之前也不需要连接。

 

0x04  最后总结一下整个流程

    1.打开目标映像文件(NtOpenFile()获取句柄, NtCreateSectiont通过文件句柄创建一个Section文件映射区,将文件内容映射进来)

    2.创建进程对象

       NtCreateProcess——>NtCreateProcessEx——>PspCreateProcess——>

                           ObCreateObject函数创建EPROCESS

                           MmCreateProcessAddressSpace创建新的地址空间,MmInitializeProcessAddressSpace 初始化新进程的地址空间

                           ExCreateHandle函数在CID句柄表中创建一个进程ID项。

                              MmCreatePeb 创建一个PEB,如果进程通过映像内存区来创建,创建一个PEB,如果是进程拷贝,则使用继承的PEB。

                           InsertTailList 函数把新进程加入全局的LIST_ENTRY进程链表中(位置:PsActiveProcessHead)

                           ObInsertObject 将新进程对象记录到当前进程的句柄表中。

        3.创建线程对象

      NtCreateThread——>PspCreateThread——>

                           ObCreateObject创建一个线程对象ETHREAD,并初始化为零。

                           ExCreateHandle函数创建线程ID

                           MmCreateTeb函数创建TEB

                           利用ThreadContext中的程序指针(Eip寄存器)来设置线程的启动地址Thread->StartAddress = (PKSTART_ROUTINE) StartRoutine;

                              InsertTailList 函数将线程插入线程LIST_ENTRY链表

                           判断如果这个线程是进程中的第一个进程,回调函数则触发进程的创建通知PCREATE_PROCESS_NOTIFY_ROUTINE Rtn;

                              ObInsertObject函数将线程对象插入到当前进程的句柄表中

                              KeReadyThread函数使线程进入“就绪”状态,准备马上执行

    4.通知windows子系统

     CreateProcess的调用者向windows子系统进程csrss.exe进程发出进程创建通知

    5.启动初始线程

       在内核中,新线程的启动例程是KiThreadStartup函数,这是当PspCreateThread 调用KeInitThread 函数时,KeInitThread 函数调用KiInitializeContextThread(参见base\ntos\ke\i386\thredini.c 文件)来设置的。

    6.用户空间的初始化和Dll连接

     DLL连接由ntdll.dll中的LdrInitializeThunk()在用户空间完成

进程创建过程详解 CreateProcess的更多相关文章

  1. Spring源码分析之Bean的创建过程详解

    前文传送门: Spring源码分析之预启动流程 Spring源码分析之BeanFactory体系结构 Spring源码分析之BeanFactoryPostProcessor调用过程详解 本文内容: 在 ...

  2. twitter storm源码走读之8 -- TridentTopology创建过程详解

    欢迎转载,转载请注明出处,徽沪一郎. 从用户层面来看TridentTopology,有两个重要的概念一是Stream,另一个是作用于Stream上的各种Operation.在实现层面来看,无论是str ...

  3. 【转载】详解CreateProcess调用内核创建进程的过程

    原文:详解CreateProcess调用内核创建进程的过程 昨天同学接到了腾讯的电面,有一题问到了CreateProcess创建进程的具体实现过程,他答得不怎么好吧应该是, 为了以防万一,也为了深入学 ...

  4. Linux LVM逻辑卷配置过程详解(创建,增加,减少,删除,卸载)

    Linux LVM逻辑卷配置过程详解 许多Linux使用者安装操作系统时都会遇到这样的困境:如何精确评估和分配各个硬盘分区的容量,如果当初评估不准确,一旦系统分区不够用时可能不得不备份.删除相关数据, ...

  5. MySQL关闭过程详解和安全关闭MySQL的方法

    MySQL关闭过程详解和安全关闭MySQL的方法 www.hongkevip.com 时间: -- : 阅读: 整理: 红客VIP 分享到: 红客VIP(http://www.hongkevip.co ...

  6. 转载:C/C++源代码到可执行程序的过程详解

    C/C++源代码到可执行程序的过程详解 编译,编译程序读取源程序(字符流),对之进行词法和语法的分析,将高级语言指令转换为功能等效的汇编代码,再由汇编程序转换为机器语言,并且按照操作系统对可执行文件格 ...

  7. Android的init过程详解(一)

    Android的init过程详解(一) Android的init过程(二):初始化语言(init.rc)解析 本文使用的软件版本 Android:4.2.2 Linux内核:3.1.10 本文及后续几 ...

  8. linux内核剖析(零)linux系统启动过程详解-开机加电后发生了什么

    本文参考了如下文章 深入理解linux启动过程 mbr (主引导记录(Master Boot Record)) 电脑从开机加电到操作系统main函数之前执行的过程 详解linux系统的启动过程及系统初 ...

  9. hadoop1.2.1+zk-3.4.5+hbase-0.94.1集群安装过程详解

    hadoop1.2.1+zk-3.4.5+hbase-0.94.1集群安装过程详解 一,环境: 1,主机规划: 集群中包括3个节点:hadoop01为Master,其余为Salve,节点之间局域网连接 ...

随机推荐

  1. 用 EasyUEFI 在 Win8/10 中硬盘安装 Ubuntu16.04图文教程

    用 EasyUEFI 在 Win8/10 中硬盘安装 Ubuntu 作者:TeliuTe 来源:基础教程网 1.准备Ubuntu安装文件 1)下载带amd的64位 Ubuntu 桌面版光盘镜像文件,如 ...

  2. samtools faidx输出的fai文件格式解析 | fasta转bed | fasta to bed

    fai示例: Sc0000003 2774837 10024730 60 61 Sc0000004 2768176 12845826 60 61 Sc0000005 2756750 15660150 ...

  3. canvas学习之饼状图

    接着上一节说,这次我使用canvas绘制了饼状图,主要是SectorGraph.js, 引入 import {canvasPoint} from '../../assets/js/canvas';im ...

  4. android -------- 打开本地浏览器或指定浏览器加载,打电话,打开第三方app

    开发中常常有打开本地浏览器加载url或者指定浏览器加载, 还有打开第三方app, 如 打开高德地图 百度地图等 在Android程序中我们可以通过发送隐式Intent来启动系统默认的浏览器. 如果手机 ...

  5. SSH登录服务器修改VNC的问题

    远程登陆服务器直接用 ssh hostname@ip_address 即可登陆.hostname就是自己的用户名,ip_address就是IP地址. 关于VNC使用的命令: 杀死VNC: vncser ...

  6. memcached的部署

    window下memcached注册服务 cmd:在学习Memcached时,为了模拟分布存储,常常需要建多个Memcached服务,如何建呢,只能使用命令行了 以管理员身份运行cmd,输入如下命令 ...

  7. 在word中粘贴的图片为什么显示不完整

    一.背景 整理系统测试说明文档,截得图片粘贴到word中显示不完整. 二.错误问题 问题:在word中粘贴的图片为什么显示不完整,如图所示: 三.分析问题: 原因是原来设置的行间距是固定值,图片也作一 ...

  8. Oracle外部表详解

    外部表概述 外部表只能在Oracle 9i之后来使用.简单地说,外部表,是指不存在于数据库中的表.通过向Oracle提供描述外部表的元数据,我们可以把一个操作系统文件当成一个只读的数据库表,就像这些数 ...

  9. ActiveMQ 中的链表

    ActiveMQ 中的消息在内存中时,以链表形式保存,以 PendingList 表示,每一个消息是 PendingNode. PendingList 主要有2种实现:OrderedPendingLi ...

  10. Linux登录超时自动退出处理办法

    出于安全方面的考虑,机器常要求配置一个登录时间期限,当闲置超过这一期限就自动退出:但在某些场合我们需要时不时地就使用机器,如果每次都要重新ssh登录那是非常麻烦的 方法一:让当前会话一直处于工作状态 ...