找回密码
 立即注册→加入我们

QQ登录

只需一步,快速开始

搜索
热搜: 下载 VB C 实现 编写
查看: 116|回复: 4

【Win32API】关于CreateProcess的第二个参数的研究

[复制链接]
发表于 2024-12-27 17:55:17 | 显示全部楼层 |阅读模式

欢迎访问技术宅的结界,请注册或者登录吧。

您需要 登录 才可以下载或查看,没有账号?立即注册→加入我们

×
本帖最后由 YY菌 于 2024-12-27 17:52 编辑

原帖来源:【Win32API】关于CreateProcess的第二个参数的研究 https://www.0xaa55.com/thread-27526-1-1.html (出处: 技术宅的结界)

  在学Win32API编程的过程中肯定少不了创建子进程的需求,以前在学VB6的时候就会用到Shell函数来创建子进程,到了学VC的时候就需要手动调用WinAPI来实现了,这个时候我们最开始发现和VB6的Shell类似的自然是WinExec函数(连参数都跟VB6的Shell“一样”)。随着需求的进一步增加又会发现WinExec的功能不够用了(比如VB6的Shell至少会返回进程id,而WinExec返回的却是错误代码,还有就是WinExec只有ANSI版没有Unicode版),这时就会接触到一个更加高级的API函数CreateProcess,可以发现WinExec的第一个参数对应CreateProcess的第二个参数,WinExec的第二参数则是对应STARTUPINFO结构体中的wShowWindow字段。最开始大家都是先从ANSI编程开始的按照WinExec的习惯去使用CreateProcessA没有任何问题,后来开始学习Unicode编程了用到CreateProcessW就发现不对劲了(程序产生了0xC0000005异常),新手时期大多数人都会一脸懵逼。

  在后面有了一定技术水平之后,就会知道0xC0000005异常是内存权限错误,而CreateProcessW的第二个参数的类型是LPWSTR(并且标记了__inout_opt),也就是说这个参数是会被CreateProcessW修改的,于是试了一下先把字符串常量赋值给字符数组变量再传给CreateProcessW果然就OJBK了(提示:在旧版C艹标准中,const wchar_t *和const wchar_t []的都是不能赋值给wchar_t *变量的,但是 L"字符串常量" 却可以赋值给wchar_t *这就是导致CreateProcessW坑到一大堆新手的罪魁祸首)。当第一次了解到这里的时候直接就被震惊了,甚至感觉很不科学,明明在CreateProcessW返回后的这个参数的内容和传入之前一模一样,为啥就被修改了呢?既然修改了返回后的内容还是一样,那修改这个参数的意义是啥呢?同样是 __inout_opt LPSTR 参数的CreateProcessA为啥却没有这个问题?随着一系列的疑问下来,便决定对CreateProcess的这第二个参数好好研究。

  根据MSDN的描述来看,当第一个参数lpApplicationName为nullptr时,会根据第二个参数lpCommandLine中的第一个命令行来生成lpApplicationName,看到这里还暂时看不出来为啥跟lpCommandLine参数被修改有啥关系,接下来再看如果lpCommandLine参数的第一个命令行不是绝对路径的话,它会按搜素路径顺序去查找对应可执行文件(1.当前进程的主exe安装路径,2.当前进程的工作目录,3.GetSystemDirectory返回的目录,4. GetWindowsDirectory返回的目录,5.Path环境变量对应的目录),到这里绝对大部对C/C艹语言或WinAPI不熟悉的开发者多半会觉得不就是查找文件嘛,跟lpCommandLine被修改有啥关系?这时我就需要提示一下,在C语言标准中字符串都是以\0为结束标记的,而lpCommandLine是包含多个命令行参数组合的字符串,每个命令行参数之间都是使用空格来分割的(如果参数中本身包含空格则需要带上双引号,如果参数本身也包含双引号那就需要连续两个双引号转义一个双引号),这时是不是就会想到lpCommandLine中的第一个命令行参数大概率不会是\0结尾(除非只有一个参数且没带双引号的情况)。所以,CreateProcessW是不是需要先把可执行文件名结尾处的空格或双引号先临时改成\0才能实现对文件的搜索?至于最后调用完后返回的内容没有变,这是因为在搜索完可执行文件的全路径后,再最终创建进程的时候要把原始的命令行参数传给子进程,所以必须得改回去。经过测试发现,当我们正确传入非空lpApplicationName参数时,再给lpCommandLine直接传字符串常量,这时调用CreateProcessW就没有任何问题,这也更加进一步验证了lpCommandLine被修改问题的关键就在这个路径搜索上。

  关于CreateProcessW的原理是研究明白了,但大家是不是还有很多疑惑?为啥CreateProcessA的lpApplicationName为nullptr给lpCommandLine传常量却没有这个问题呢?难道CreateProcessA不走路径搜索吗?答案显然不是,因为在Win32中kernel32.dll这层API函数的ANSI版绝大多数都是内部做临时编码转换,然后再调用的Unicode版实现。也就是说CreateProcessA会把ANSI字符集的lpCommandLine在内存中临时转换成Unicode字符集的副本,然后再调用CreateProcessW,这样CreateProcessW在处理路径搜索时修改的就是在CreateProcessA分配的临时内存上(这个内存是一定具有写入权限的,不然转换出来的Unicode字符串也没法写进去),而不是CreateProcessA本来接收的lpCommandLine参数。因此CreateProcessA永远不会修改用户传入的参数,自然也就不会有这个问题。在最新版的MSDN文档中巨硬已经补充了一句“此函数的 Unicode 版本(CreateProcessW)可以修改此字符串的内容。 因此,此参数不能是指向只读内存的指针(例如 常量 变量或文本字符串)。 如果此参数是常量字符串,则函数可能会导致访问冲突。”

  自此,我们现在已经完全搞明白了CreateProcess的第二个参数lpCommandLine神奇现象的原理,下面就来模仿一下其内部实现(这里会用到一个冷门API函数SearchPathW):

  1. #define WIN32_LEAN_AND_MEAN
  2. #include <Windows.h>
  3. #include <malloc.h>

  4. #ifdef UNICODE
  5. #define MyCreateProcess  MyCreateProcessW
  6. #else
  7. #define MyCreateProcess  MyCreateProcessA
  8. #endif // !UNICODE

  9. // Unicode 版实现
  10. EXTERN_C DECLSPEC_NOINLINE BOOL WINAPI MyCreateProcessW(__in_opt LPCWSTR lpApplicationName, __inout_opt LPWSTR lpCommandLine, __in_opt LPSECURITY_ATTRIBUTES lpProcessAttributes, __in_opt LPSECURITY_ATTRIBUTES lpThreadAttributes, __in BOOL bInheritHandles, __in DWORD dwCreationFlags, __in_opt LPVOID lpEnvironment, __in_opt LPCWSTR lpCurrentDirectory, __in LPSTARTUPINFOW lpStartupInfo, __out LPPROCESS_INFORMATION lpProcessInformation)
  11. {
  12.         // 先判断第一个参数为空且第二个参数不为空的情况(如果成立则从lpCommandLine中取第一个命令行参数来搜索可执行文件)
  13.         if (!lpApplicationName && lpCommandLine) {
  14.                 // 定义两个变量分别标记lpCommandLine中可执行文件的起始位置和结束位置
  15.                 LPWSTR app4cl = lpCommandLine, appec;
  16.                 // 如果lpCommandLine的第一个字符不是双引号,那么就以空格结尾,否则就以双引号结尾。
  17.                 if (L'"' != *app4cl) appec = wcschr(app4cl, L' ');
  18.                 else appec = wcschr(++app4cl, L'"');
  19.                 // 如果找到正确位置需要临时修改成\0结束标记,否则SearchPathW会出错,没有找到则说明lpCommandLine中没有多个参数(刚好本身就有\0结尾)。
  20.                 if (appec) *appec = L'\0';
  21.                 // 开始用从lpCommandLine中取到的文件名搜索对应的.exe文件(SearchPathW的第一个参数传nullptr表示使用系统默认的搜索路径规则)
  22.                 DWORD len = SearchPathW(nullptr, app4cl, L".exe", 0, nullptr, nullptr);        // 在不接收搜索结果的情况下函数会返回包括\0的长度
  23.                 if (len) {        // 搜索成功则分配内存并获取结果
  24.                         // 一般来说可执行文件路径都会被限制在MAX_PATH长度,不会太长这里就偷懒从堆栈上来分配临时内存了。
  25.                         (void*&)lpApplicationName = _alloca(len << 1);        // 注意:内存的分配都是按字节大小来算长度的,所以Unicode文本长度需要 * 2。
  26.                         // 分配好缓冲区内存之后就可以开始接收搜索结果了(提示:现在返回的长度是不包括\0的了)
  27.                         len = SearchPathW(nullptr, app4cl, L".exe", len, LPWSTR(lpApplicationName), nullptr);
  28.                 }
  29.                 // 搜索完成后就把被临时修改的lpCommandLine给改回去(由于确定只会有双引号和空格两种情况,所以我这里偷懒了没有保存原始的字符)
  30.                 if (appec) *appec = app4cl != lpCommandLine ? L'"' : L' ';
  31.                 // 如果搜索失败则返回失败(SearchPathW内部会设置LastError,不用自己再设置)
  32.                 if (!len) return FALSE;
  33.         }
  34.         // 假设存在的一个内部CreateProcess函数(由于这里仅研究第二个参数lpCommandLine,不考虑其它细节,因此放弃了使用RtlCreateUserProcess来实现的想法)
  35.         return CreateProcessW(lpApplicationName, lpCommandLine, lpProcessAttributes, lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment, lpCurrentDirectory, lpStartupInfo, lpProcessInformation);
  36. }

  37. // ANSI 版实现
  38. EXTERN_C DECLSPEC_NOINLINE BOOL WINAPI MyCreateProcessA(__in_opt LPCSTR lpApplicationName, __inout_opt LPSTR lpCommandLine, __in_opt LPSECURITY_ATTRIBUTES lpProcessAttributes, __in_opt LPSECURITY_ATTRIBUTES lpThreadAttributes, __in BOOL bInheritHandles, __in DWORD dwCreationFlags, __in_opt LPVOID lpEnvironment, __in_opt LPCSTR lpCurrentDirectory, __in LPSTARTUPINFOA lpStartupInfo, __out LPPROCESS_INFORMATION lpProcessInformation)
  39. {
  40.         // 字符串参数的索引常量
  41.         enum { AppLen, CmdLen, CurLen, DskLen, TitLen, LensCount, };
  42.         // Unicode 缓冲区
  43.         LPSTARTUPINFOEXW psiex = nullptr;
  44.         __try {
  45.                 int srcls[LensCount], dstls[LensCount + 1];
  46.                 // 计算所有ANSI参数的完整长度(含\0,因此nullptr长度为0 ""则为1)
  47.                 srcls[AppLen] = lpApplicationName ? lstrlenA(lpApplicationName) + 1 : 0;
  48.                 srcls[CmdLen] = lpCommandLine ? lstrlenA(lpCommandLine) + 1 : 0;
  49.                 srcls[CurLen] = lpCurrentDirectory ? lstrlenA(lpCurrentDirectory) + 1 : 0;
  50.                 srcls[DskLen] = lpStartupInfo->lpDesktop ? lstrlenA(lpStartupInfo->lpDesktop) + 1 : 0;
  51.                 srcls[TitLen] = lpStartupInfo->lpTitle ? lstrlenA(lpStartupInfo->lpTitle) + 1 : 0;
  52.                 *dstls = ((max(sizeof(*lpStartupInfo), lpStartupInfo->cb) + 3) >> 2) << 1;
  53.                 // 计算所有ANSI参数转换为Unicode之后的完全长度(含\0,算的字符个数不是字节数)
  54.                 dstls[AppLen + 1] = lpApplicationName ? MultiByteToWideChar(CP_ACP, 0, lpApplicationName, srcls[AppLen], nullptr, 0) : 0;
  55.                 dstls[CmdLen + 1] = lpCommandLine ? MultiByteToWideChar(CP_ACP, 0, lpCommandLine, srcls[CmdLen], nullptr, 0) : 0;
  56.                 dstls[CurLen + 1] = lpCurrentDirectory ? MultiByteToWideChar(CP_ACP, 0, lpCurrentDirectory, srcls[CurLen], nullptr, 0) : 0;
  57.                 dstls[DskLen + 1] = lpStartupInfo->lpDesktop ? MultiByteToWideChar(CP_ACP, 0, lpStartupInfo->lpDesktop, srcls[DskLen], nullptr, 0) : 0;
  58.                 dstls[TitLen + 1] = lpStartupInfo->lpTitle ? MultiByteToWideChar(CP_ACP, 0, lpStartupInfo->lpTitle, srcls[TitLen], nullptr, 0) : 0;
  59.                 // 累加所有长度,以便后期使用的复杂度从On降至O1
  60.                 for (int i = 0; i < LensCount;) { register auto cur = dstls[i]; dstls[++i] += cur; }
  61.                 // 使用总长度分配缓冲区内存(这次的参数可能会特别长,特别是lpCommandLine最大长度为32767,因此最好从堆上分配)
  62.                 (LPVOID&)psiex = LocalAlloc(LMEM_FIXED, dstls[LensCount] << 1);
  63.                 if (!psiex) __leave;
  64.                 // 拷贝StartupInfo结构体(STARTUPINFOA和STARTUPINFOW的结构体完全一致,只是需要将其中的LPSTR字段替换成LPWSTR字段)
  65.                 CopyMemory(psiex, lpStartupInfo, max(sizeof(*lpStartupInfo), lpStartupInfo->cb));
  66.                 // 转换所有的ANSI参数为Unicode,并按条件替换
  67.                 if (lpApplicationName) MultiByteToWideChar(CP_ACP, 0, lpApplicationName, srcls[AppLen], LPWSTR(psiex) + dstls[AppLen], dstls[AppLen + 1] - dstls[AppLen]);
  68.                 if (lpCommandLine) MultiByteToWideChar(CP_ACP, 0, lpCommandLine, srcls[CmdLen], LPWSTR(psiex) + dstls[CmdLen], dstls[CmdLen + 1] - dstls[CmdLen]);
  69.                 if (lpCurrentDirectory) MultiByteToWideChar(CP_ACP, 0, lpCurrentDirectory, srcls[CurLen], LPWSTR(psiex) + dstls[CurLen], dstls[CurLen + 1] - dstls[CurLen]);
  70.                 if (lpStartupInfo->lpDesktop) MultiByteToWideChar(CP_ACP, 0, lpStartupInfo->lpDesktop, srcls[DskLen], psiex->StartupInfo.lpDesktop = LPWSTR(psiex) + dstls[DskLen], dstls[DskLen + 1] - dstls[DskLen]);
  71.                 if (lpStartupInfo->lpTitle) MultiByteToWideChar(CP_ACP, 0, lpStartupInfo->lpTitle, srcls[TitLen], psiex->StartupInfo.lpTitle = LPWSTR(psiex) + dstls[TitLen], dstls[TitLen + 1] - dstls[TitLen]);
  72.                 //转换完成后调用对应的 Unicode 版实现就行了
  73.                 return MyCreateProcessW((lpApplicationName ? LPCWSTR(psiex) + dstls[AppLen] : 0), (lpCommandLine ? LPWSTR(psiex) + dstls[CmdLen] : 0), lpProcessAttributes, lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment, (lpCurrentDirectory ? LPCWSTR(psiex) + dstls[CurLen] : 0), &psiex->StartupInfo, lpProcessInformation);
  74.         }
  75.         __finally {
  76.                 // 再离开作用域后需要释放从堆中分配的内存
  77.                 if (psiex) LocalFree(psiex);
  78.         }
  79.         return FALSE;
  80. }

  81. // WinExec 实现
  82. EXTERN_C DECLSPEC_NOINLINE UINT WINAPI MyWinExec(__in LPCSTR lpCmdLine, __in UINT uCmdShow)
  83. {
  84.         // 等待超时时长(WinExec最长等待30秒,ShellExecuteEx则是60秒,CreateProcess则不会做任何等待)
  85.         static const DWORD TIMEOUT = 30000;
  86.         PROCESS_INFORMATION processInfo;
  87.         // 启动参数设置(WinExec只有用到wShowWindow字段)
  88.         STARTUPINFOA startupInfo = { sizeof(startupInfo), nullptr, nullptr, nullptr, 0, 0, 0, 0, 0, 0, 0, STARTF_USESHOWWINDOW, WORD(uCmdShow), };
  89.         // 调用CreateProcessA(由于CreateProcessA内部永远不会修改到lpCommandLine,所以直接强转LPSTR传递是安全的)
  90.         if (MyCreateProcessA(nullptr, LPSTR(lpCmdLine), nullptr, nullptr, FALSE, 0, nullptr, nullptr, &startupInfo, &processInfo)) {
  91.                 // 关闭线程句柄(用不上)
  92.                 CloseHandle(processInfo.hThread);
  93.                 // 等待子进程GUI初始化完成(以第一次把消息队列清空为条件)
  94.                 WaitForInputIdle(processInfo.hProcess, TIMEOUT);
  95.                 // 关闭进程句柄(用完了)
  96.                 CloseHandle(processInfo.hProcess);
  97.                 // 返回成功
  98.                 return HINSTANCE_ERROR + TRUE;
  99.         }
  100.         // 失败则获取错误码
  101.         DWORD dwErr = GetLastError();
  102.         // 转换错误码
  103.         switch (dwErr)
  104.         {
  105.         case ERROR_FILE_NOT_FOUND:        // 找不到可执行文件
  106.         case ERROR_PATH_NOT_FOUND:        // 无效的文件路径
  107.                 break;
  108.         case ERROR_BAD_EXE_FORMAT:        // 文件不是EXE格式
  109.                 dwErr = ERROR_BAD_FORMAT;
  110.                 break;
  111.         default:        // 其它错误状态都是返回0
  112.                 dwErr = FALSE;
  113.         }
  114.         return UINT(dwErr);
  115. }
复制代码
压缩包密码没有密码 ,如果有那就在隐藏内容中(回帖可见) )
游客,如果您要查看本帖隐藏内容请回复

cpimpl.7z

5.98 KB, 下载次数: 0

CreateProcess

回复

使用道具 举报

发表于 2024-12-28 07:35:57 | 显示全部楼层
如果有任何关于WIN32API的疑问(包括内核API),都可以直接参考泄露的WINXP源码。
回复 赞! 1 靠! 0

使用道具 举报

发表于 2024-12-27 19:11:55 | 显示全部楼层
膜拜大佬技术贴
兴奋的进来,一脸懵逼的出去。。。
回复 赞! 靠!

使用道具 举报

发表于 2024-12-30 15:08:07 | 显示全部楼层
啥也不说了,帖子就是带劲!
回复 赞! 靠!

使用道具 举报

 楼主| 发表于 5 天前 | 显示全部楼层
美俪女神 发表于 2024-12-28 07:35
如果有任何关于WIN32API的疑问(包括内核API),都可以直接参考泄露的WINXP源码。 ...

找到XP源码了,它内部确实是使用的临时修改lpCommandLine参数然后SearchPathW搜索的exe文件,搜完后再修改回来,最后再真正创建进程:


  1. BOOL
  2. WINAPI
  3. CreateProcessInternalW(
  4.     HANDLE hUserToken,
  5.     LPCWSTR lpApplicationName,
  6.     LPWSTR lpCommandLine,
  7.     LPSECURITY_ATTRIBUTES lpProcessAttributes,
  8.     LPSECURITY_ATTRIBUTES lpThreadAttributes,
  9.     BOOL bInheritHandles,
  10.     DWORD dwCreationFlags,
  11.     LPVOID lpEnvironment,
  12.     LPCWSTR lpCurrentDirectory,
  13.     LPSTARTUPINFOW lpStartupInfo,
  14.     LPPROCESS_INFORMATION lpProcessInformation,
  15.     PHANDLE hRestrictedUserToken
  16.     )

  17. /*++

  18. Routine Description:

  19.     This is the worker routine for CreateProcess and CreateProcessAsUser.
  20.     CreateProcessAsUser supplies a User token to be stamped on the new process.
  21.     CreateProcess supplies NULL and the current process token is used.

  22.     A process and thread object are created and a handle opened to each
  23.     object using CreateProcessInternal.  Note that WinExec and LoadModule are
  24.     still supported, but are implemented as a call to CreateProcess.

  25. Arguments:

  26.     hUserToken - Supplies an optional token handle to be set on the new process.
  27.         Process token is used is this parameter is NULL.

  28.     lpApplicationName - Supplies an optional pointer to a null terminated
  29.         character string that contains the name of the image file to
  30.         execute.  This is a fully qualified DOS path name.  If not
  31.         specified, then the image file name is the first whitespace
  32.         delimited token on the command line.

  33.     lpCommandLine - Supplies a null terminated character string that
  34.         contains the command line for the application to be executed.
  35.         The entire command line is made available to the new process
  36.         using GetCommandLine.  If the lpApplicationName parameter was
  37.         not specified, then the first token of the command line
  38.         specifies file name of the application (note that this token
  39.         begins at the beginning of the command line and ends at the
  40.         first "white space" character).  If the file name does not
  41.         contain an extension (the presence of a "."), then .EXE is
  42.         assumed.  If the file name does not contain a directory path,
  43.         Windows will search for the executable file in:

  44.           - The current directory

  45.           - The windows directory

  46.           - The windows system directory

  47.           - The directories listed in the path environment variable

  48.         This parameter is optional onlu if the lpApplicationName
  49.         parameter is specified.  In this case the command line the
  50.         application receives will be the application name.

  51.     lpProcessAttributes - An optional parameter that may be used to
  52.         specify the attributes of the new process.  If the parameter is
  53.         not specified, then the process is created without a security
  54.         descriptor, and the resulting handle is not inherited on process
  55.         creation:

  56.         SECURITY_ATTRIBUTES Structure:

  57.         DWORD nLength - Specifies the length of this structure.  Must be
  58.             set to sizeof( SECURITY_ATTRUBUTES ).

  59.         LPVOID lpSecurityDescriptor - Points to a security descriptor for
  60.             the object (must be NULL for Win32, used on NT/Win32). The
  61.             security descriptor controls the sharing of an object.

  62.         BOOL bInheritHandle - Supplies a flag that indicates whether
  63.             or not the returned handle is to be inherited by a new
  64.             process during process creation.  A value of TRUE
  65.             indicates that the new process will inherit the handle.

  66.     lpThreadAttributes - An optional parameter that may be used to specify
  67.         the attributes of the new thread.  If the parameter is not
  68.         specified, then the thread is created without a security
  69.         descriptor, and the resulting handle is not inherited on
  70.         process creation.

  71.     dwCreationFlags - Supplies additional flags that control the creation
  72.         of the process.

  73.         dwCreationFlags Flags:

  74.         DEBUG_PROCESS - If this flag bit is set, then the creating
  75.             process is treated as a debugger, and the process being
  76.             created is created as a debugee.  All debug events occuring
  77.             in the debugee are reported to the debugger.  If this bit is
  78.             clear, but the calling process is a debugee, then the
  79.             process becomes a debugee of the calling processes debugger.
  80.             If this bit is clear and the calling processes is not a
  81.             debugee then no debug related actions occur.

  82.         DEBUG_ONLY_THIS_PROCESS - If this flag is set, then the
  83.             DEBUG_PROCESS flag bit must also be set.  The calling
  84.             process is is treated as a debugger, and the new process is
  85.             created as its debuggee.  If the new process creates
  86.             additional processes, no debug related activities (with
  87.             respect to the debugger) occur.

  88.         CREATE_SUSPENDED - The process is created, but the initial thread
  89.             of the process remains suspended. The creator can resume this
  90.             thread using ResumeThread. Until this is done, code in the
  91.             process will not execute.

  92.         CREATE_UNICODE_ENVIRONMENT - If set, the environment pointer
  93.             points to a Unicode environment block.  Otherwise, the
  94.             block is ANSI (actually OEM.)

  95.     bInheritHandles - Supplies a flag that specifies whether or not the
  96.         new process is to inherit handles to objects visible to the
  97.         calling process.  A value of TRUE causes handles to be inherited
  98.         by the new process.  If TRUE was specified, then for each handle
  99.         visible to the calling process, if the handle was created with
  100.         the inherit handle option, the handle is inherited to the new
  101.         process.  The handle has the same granted access in the new
  102.         process as it has in the calling process, and the value of the
  103.         handle is the same.

  104.     lpEnvironment - An optional parameter, that if specified, supplies a
  105.         pointer to an environment block.  If the parameter is not
  106.         specified, the environment block of the current process is used.
  107.         This environment block is made available to the new process
  108.         using GetEnvironmentStrings.

  109.     lpCurrentDirectory - An optional parameter, that if specified,
  110.         supplies a string representing the current drive and directory
  111.         for the new process.  The string must be a fully qualified
  112.         pathname that includes a drive letter.  If the parameter is not
  113.         specified, then the new process is created with the same current
  114.         drive and directory as the calling process.  This option is
  115.         provided primarily for shells that want to start an application
  116.         and specify its initial drive and working directory.

  117.     lpStartupInfo - Supplies information that specified how the
  118.         applications window is to be shown. This structure is described
  119.         in the Win32 User Interface API Book.

  120.     lpProcessInformation - Returns identification information about the
  121.         new process.

  122.     PROCESS_INFORMATION Structure:

  123.         HANDLE hProcess - Returns a handle to the newly created process.
  124.             Through the handle, all operations on process objects are
  125.             allowed.

  126.         HANDLE hThread - Returns a handle to the newly created thread.
  127.             Through the handle, all operations on thread objects are
  128.             allowed.

  129.         DWORD dwProcessId - Returns a global process id that may be used
  130.             to identify a process.  The value is valid from the time the
  131.             process is created until the time the process is terminated.

  132.         DWORD dwThreadId - Returns a global thread id that may be used
  133.             to identify a thread.  The value is valid from the time the
  134.             thread is created until the time the thread is terminated.

  135.     hRestrictedUserToken - Returns a restricted token if a UsetToken was
  136.         supplied. This is applicable for the CreateProcessAsUser case.
  137.         The token is released by CreateProcessAsUser.

  138. Return Value:

  139.     TRUE - The operation was successful

  140.     FALSE/NULL - The operation failed. Extended error status is available
  141.         using GetLastError.

  142. --*/

  143. {
  144.     NTSTATUS Status;
  145.     OBJECT_ATTRIBUTES Obja;
  146.     POBJECT_ATTRIBUTES pObja;
  147.     HANDLE ProcessHandle, ThreadHandle, VdmWaitHandle = NULL;
  148.     HANDLE FileHandle, SectionHandle;
  149.     CLIENT_ID ClientId;
  150.     UNICODE_STRING PathName;
  151.     IO_STATUS_BLOCK IoStatusBlock;
  152.     BOOLEAN TranslationStatus;
  153.     RTL_RELATIVE_NAME RelativeName;
  154.     PVOID FreeBuffer;
  155.     LPWSTR NameBuffer;
  156.     LPWSTR WhiteScan;
  157.     ULONG Length,i;
  158.     PROCESS_BASIC_INFORMATION ProcessInfo;
  159.     SECTION_IMAGE_INFORMATION ImageInformation;
  160.     NTSTATUS StackStatus;
  161.     BOOLEAN bStatus;
  162.     INITIAL_TEB InitialTeb;
  163.     CONTEXT ThreadContext;
  164.     PPEB Peb;
  165.     BASE_API_MSG m;
  166.     PBASE_CREATEPROCESS_MSG a = &m.u.CreateProcess;
  167.     PBASE_CHECKVDM_MSG b = &m.u.CheckVDM;
  168.     PWCH TempNull = NULL;
  169.     WCHAR TempChar;
  170.     UNICODE_STRING VdmNameString;
  171.     PVOID BaseAddress;
  172.     ULONG VdmReserve;
  173.     SIZE_T BigVdmReserve;
  174.     ULONG iTask=0;
  175.     LPWSTR CurdirBuffer, CurdirFilePart;
  176.     DWORD CurdirLength,CurdirLength2;
  177.     ULONG VDMCreationState=0;
  178.     ULONG VdmBinaryType = 0;
  179.     BOOL  bMeowBinary = FALSE;
  180.     UNICODE_STRING  SubSysCommandLine;
  181.     PIMAGE_NT_HEADERS NtHeaders;
  182.     DWORD dwNoWindow = (dwCreationFlags & CREATE_NO_WINDOW);
  183.     ANSI_STRING AnsiStringVDMEnv;
  184.     UNICODE_STRING UnicodeStringVDMEnv;
  185.     WCHAR ImageFileDebuggerCommand[ MAX_PATH ];
  186.     LPWSTR QuotedBuffer;
  187.     BOOLEAN QuoteInsert;
  188.     BOOLEAN QuoteCmdLine = FALSE;
  189.     BOOLEAN QuoteFound;
  190.     BOOL bSaferChecksNeeded = FALSE;
  191.     BOOLEAN SearchRetry;
  192.     BOOLEAN IsWowBinary = FALSE;
  193.     STARTUPINFOW StartupInfo;
  194.     DWORD LastError;
  195.     DWORD fileattr;
  196.     PROCESS_PRIORITY_CLASS PriClass;
  197.     PVOID State;
  198.     HANDLE DebugPortHandle = NULL;
  199.     PVOID pAppCompatDataTemp;
  200.     PVOID pAppCompatData  = NULL;
  201.     DWORD cbAppCompatData = 0;    // for the future
  202.     BOOLEAN bVdmRetry = FALSE;
  203.     DWORD Flags;
  204.     PVOID pAppCompatSxsData = NULL;
  205.     DWORD cbAppCompatSxsData = 0;
  206.     SXS_OVERRIDE_STREAM AppCompatSxsManifest;
  207.     PCSR_CAPTURE_HEADER CaptureBuffer = NULL;
  208.     SIZE_T SxsConglomeratedBufferSizeBytes;
  209.     PBYTE SxsConglomeratedByteBuffer = NULL; // this contains all the of the below in one large right-sized heap allocation
  210.                             // if we compute its size wrong, other code (if it gets it right..) should
  211.                             // do more heap allocation
  212.     ULONG sxsi; // for loop counter
  213.     RTL_UNICODE_STRING_BUFFER SxsWin32ManifestPathBuffer;
  214.     RTL_UNICODE_STRING_BUFFER SxsWin32PolicyPathBuffer;
  215.     RTL_UNICODE_STRING_BUFFER SxsWin32AssemblyDirectoryBuffer;
  216.     RTL_UNICODE_STRING_BUFFER SxsNtManifestPathBuffer;
  217.     RTL_UNICODE_STRING_BUFFER SxsNtPolicyPathBuffer;
  218.     const PRTL_UNICODE_STRING_BUFFER SxsStringBuffers[] = {
  219.         // The order here does not matter.
  220.         &SxsWin32ManifestPathBuffer,
  221.         &SxsWin32PolicyPathBuffer,
  222.         &SxsWin32AssemblyDirectoryBuffer,
  223.         &SxsNtManifestPathBuffer,
  224.         &SxsNtPolicyPathBuffer
  225.     };
  226.     UNICODE_STRING SxsWin32ExePath;
  227.     UNICODE_STRING SxsNtExePath;
  228.     BASE_MSG_SXS_HANDLES SxsExeHandles = {0};
  229.     BASE_MSG_SXS_HANDLES SxsManifestFileHandles = {0};
  230.     CONST SXS_CONSTANT_WIN32_NT_PATH_PAIR SxsExePathPair = { &SxsWin32ExePath, &SxsNtExePath };
  231.     CONST SXS_WIN32_NT_PATH_PAIR SxsManifestPathPair = { &SxsWin32ManifestPathBuffer, &SxsNtManifestPathBuffer };
  232.     CONST SXS_WIN32_NT_PATH_PAIR SxsPolicyPathPair = { &SxsWin32PolicyPathBuffer, &SxsNtPolicyPathBuffer };
  233.     BASE_MSG_SXS_HANDLES SxsPolicyHandles = {0};
  234.     PWSTR ExePathFullBuffer = NULL;

  235.     DWORD dwJobMemberLevel = 0;
  236.     HANDLE hSaferAssignmentJob = NULL;
  237.     HANDLE hSaferRestrictedToken = NULL;

  238.     DWORD dwBasePushProcessParametersFlags = 0;

  239. #if defined(BUILD_WOW6432) || defined(_WIN64)
  240.     BOOLEAN ComPlusILImage;
  241.     LPCWSTR lpOriginalApplicationName = lpApplicationName;
  242.     LPWSTR lpOriginalCommandLine = lpCommandLine;
  243. #endif

  244. #if defined(WX86)
  245.     HANDLE Wx86Info = NULL;
  246. #endif

  247. #if defined WX86
  248.     BOOLEAN UseKnownWx86Dll;
  249.     UseKnownWx86Dll = NtCurrentTeb()->Wx86Thread.UseKnownWx86Dll;
  250.     NtCurrentTeb()->Wx86Thread.UseKnownWx86Dll = FALSE;
  251. #endif

  252.     RtlZeroMemory(&a->Sxs, sizeof(a->Sxs));
  253.     RtlZeroMemory(lpProcessInformation,sizeof(*lpProcessInformation));

  254.     if (ARGUMENT_PRESENT( hRestrictedUserToken )) {
  255.         *hRestrictedUserToken = NULL;
  256.         }

  257.     // Private VDM flag should be ignored; Its meant for internal use only.
  258.     dwCreationFlags &= (ULONG)~CREATE_NO_WINDOW;

  259.     if ((dwCreationFlags & (DETACHED_PROCESS | CREATE_NEW_CONSOLE)) ==
  260.         (DETACHED_PROCESS | CREATE_NEW_CONSOLE)) {

  261.         SetLastError(ERROR_INVALID_PARAMETER);
  262.         return FALSE;
  263.         }

  264.     AnsiStringVDMEnv.Buffer = NULL;
  265.     UnicodeStringVDMEnv.Buffer = NULL;

  266.     //
  267.     // the lowest specified priority class is used.
  268.     //

  269.     if (dwCreationFlags & IDLE_PRIORITY_CLASS ) {
  270.         PriClass.PriorityClass = PROCESS_PRIORITY_CLASS_IDLE;
  271.         }
  272.     else if (dwCreationFlags & BELOW_NORMAL_PRIORITY_CLASS ) {
  273.         PriClass.PriorityClass = PROCESS_PRIORITY_CLASS_BELOW_NORMAL;
  274.         }
  275.     else if (dwCreationFlags & NORMAL_PRIORITY_CLASS ) {
  276.         PriClass.PriorityClass = PROCESS_PRIORITY_CLASS_NORMAL;
  277.         }
  278.     else if (dwCreationFlags & ABOVE_NORMAL_PRIORITY_CLASS ) {
  279.         PriClass.PriorityClass = PROCESS_PRIORITY_CLASS_ABOVE_NORMAL;
  280.         }
  281.     else if (dwCreationFlags & HIGH_PRIORITY_CLASS ) {
  282.         PriClass.PriorityClass =  PROCESS_PRIORITY_CLASS_HIGH;
  283.         }
  284.     else if (dwCreationFlags & REALTIME_PRIORITY_CLASS ) {
  285.         if ( BasepIsRealtimeAllowed(FALSE) ) {
  286.             PriClass.PriorityClass =  PROCESS_PRIORITY_CLASS_REALTIME;
  287.             }
  288.         else {
  289.             PriClass.PriorityClass =  PROCESS_PRIORITY_CLASS_HIGH;
  290.             }
  291.         }
  292.     else {
  293.         PriClass.PriorityClass = PROCESS_PRIORITY_CLASS_UNKNOWN;
  294.         }
  295.     PriClass.Foreground = FALSE;

  296.     dwCreationFlags = (dwCreationFlags & ~PRIORITY_CLASS_MASK );

  297.     //
  298.     // Default separate/shared VDM option if not explicitly specified.
  299.     //

  300.     if (dwCreationFlags & CREATE_SEPARATE_WOW_VDM) {
  301.         if (dwCreationFlags & CREATE_SHARED_WOW_VDM) {
  302.             SetLastError(ERROR_INVALID_PARAMETER);

  303.             return FALSE;
  304.             }
  305.         }
  306.     else
  307.     if ((dwCreationFlags & CREATE_SHARED_WOW_VDM) == 0) {
  308.         if (BaseStaticServerData->DefaultSeparateVDM) {
  309.             dwCreationFlags |= CREATE_SEPARATE_WOW_VDM;
  310.             }
  311.         }

  312.     if ((dwCreationFlags & CREATE_SEPARATE_WOW_VDM) == 0) {
  313.         //
  314.         // If the creator is running inside a job object, always
  315.         // set SEPERATE_WOW_VDM so the VDM is part of the job.
  316.         //
  317.         JOBOBJECT_BASIC_UI_RESTRICTIONS UiRestrictions;

  318.         Status = NtQueryInformationJobObject(NULL,
  319.                                              JobObjectBasicUIRestrictions,
  320.                                              &UiRestrictions,
  321.                                              sizeof(UiRestrictions),
  322.                                              NULL);
  323.         if (Status != STATUS_ACCESS_DENIED) {
  324.             //
  325.             // Anything other than STATUS_ACCESS_DENIED indicates the
  326.             // current process is inside a job.
  327.             //
  328.             dwCreationFlags = (dwCreationFlags & (~CREATE_SHARED_WOW_VDM)) |
  329.                                   CREATE_SEPARATE_WOW_VDM;
  330.             }
  331.         }


  332.     //
  333.     //  If ANSI environment, convert to Unicode
  334.     //

  335.     if (lpEnvironment && !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT) ) {
  336.         PUCHAR s;
  337.         STRING Ansi;
  338.         UNICODE_STRING Unicode;
  339.         MEMORY_BASIC_INFORMATION MemoryInformation;

  340.         Ansi.Buffer = s = lpEnvironment;
  341.         while (*s || *(s+1))            // find end of block
  342.             s++;

  343.         Ansi.Length = (USHORT)(s - Ansi.Buffer) + 1;
  344.         Ansi.MaximumLength = Ansi.Length + 1;
  345.         MemoryInformation.RegionSize = Ansi.MaximumLength * sizeof(WCHAR);
  346.         Unicode.Buffer = NULL;
  347.         Status = NtAllocateVirtualMemory( NtCurrentProcess(),
  348.                                           &Unicode.Buffer,
  349.                                           0,
  350.                                           &MemoryInformation.RegionSize,
  351.                                           MEM_COMMIT,
  352.                                           PAGE_READWRITE
  353.                                         );
  354.         if (!NT_SUCCESS(Status) ) {
  355.             BaseSetLastNTError(Status);

  356.             return FALSE;
  357.             }

  358.         Unicode.MaximumLength = (USHORT)MemoryInformation.RegionSize;
  359.         Status = RtlAnsiStringToUnicodeString(&Unicode, &Ansi, FALSE);
  360.         if (!NT_SUCCESS(Status) ) {
  361.             NtFreeVirtualMemory( NtCurrentProcess(),
  362.                                  &Unicode.Buffer,
  363.                                  &MemoryInformation.RegionSize,
  364.                                  MEM_RELEASE
  365.                                );
  366.             BaseSetLastNTError(Status);

  367.             return FALSE;
  368.             }
  369.         lpEnvironment = Unicode.Buffer;
  370.         }

  371.     FileHandle = NULL;
  372.     SectionHandle = NULL;
  373.     ProcessHandle = NULL;
  374.     ThreadHandle = NULL;
  375.     FreeBuffer = NULL;
  376.     NameBuffer = NULL;
  377.     VdmNameString.Buffer = NULL;
  378.     BaseAddress = (PVOID)1;
  379.     VdmReserve = 0;
  380.     CurdirBuffer = NULL;
  381.     CurdirFilePart = NULL;
  382.     SubSysCommandLine.Buffer = NULL;
  383.     QuoteFound = FALSE;
  384.     QuoteInsert = FALSE;
  385.     QuotedBuffer = NULL;

  386.     try {

  387.         //
  388.         // Make a copy of the startup info so we can change it.
  389.         //

  390.         StartupInfo = *lpStartupInfo;

  391.         //
  392.         // STARTF_USEHOTKEY means hStdInput is really the hotkey value.
  393.         // STARTF_HASSHELLDATA means std handles are used for shell-private
  394.         // data.  This flag is used if an icon is passed to ShellExecuteEx.
  395.         // As a result they cannot be specified with STARTF_USESTDHANDLES.
  396.         // Consistent with Win95, USESTDHANDLES is ignored.
  397.         //

  398.         if (StartupInfo.dwFlags & STARTF_USESTDHANDLES &&
  399.             StartupInfo.dwFlags & (STARTF_USEHOTKEY | STARTF_HASSHELLDATA)) {

  400.             StartupInfo.dwFlags &= ~STARTF_USESTDHANDLES;
  401.             }

  402. VdmRetry:
  403.         //
  404.         // None of this cleanup/reinit occurs for launching a Win32 or Win64 .exe,
  405.         // but they generally do occur for launching 16bit, .bat, etc.
  406.         //
  407.         if (NameBuffer) {
  408.             RtlFreeHeap(RtlProcessHeap(), 0, NameBuffer);
  409.             NameBuffer = NULL;
  410.         }
  411.         if (FreeBuffer) {
  412.             RtlFreeHeap(RtlProcessHeap(), 0, FreeBuffer);
  413.             FreeBuffer = NULL;
  414.         }
  415.         if (FileHandle) {
  416.             NtClose(FileHandle);
  417.             FileHandle = NULL;
  418.         }

  419.         LastError = 0;
  420.         SearchRetry = TRUE;
  421.         QuoteInsert = FALSE;
  422.         QuoteCmdLine = FALSE;
  423.         if (!ARGUMENT_PRESENT( lpApplicationName )) {

  424.             //
  425.             // Locate the image
  426.             //

  427.             // forgot to free NameBuffer before goto VdmRetry???
  428.             ASSERT(NameBuffer == NULL);

  429.             NameBuffer = RtlAllocateHeap( RtlProcessHeap(),
  430.                                           MAKE_TAG( TMP_TAG ),
  431.                                           MAX_PATH * sizeof( WCHAR ));
  432.             if ( !NameBuffer ) {
  433.                 BaseSetLastNTError(STATUS_NO_MEMORY);
  434.                 return FALSE;
  435.                 }
  436.             lpApplicationName = lpCommandLine;
  437.             TempNull = (PWCH)lpApplicationName;
  438.             WhiteScan = (LPWSTR)lpApplicationName;

  439.             //
  440.             // check for lead quote
  441.             //
  442.             if ( *WhiteScan == L'"' ) {
  443.                 SearchRetry = FALSE;
  444.                 WhiteScan++;
  445.                 lpApplicationName = WhiteScan;
  446.                 while(*WhiteScan) {
  447.                     if ( *WhiteScan == (WCHAR)'"' ) {
  448.                         TempNull = (PWCH)WhiteScan;
  449.                         QuoteFound = TRUE;
  450.                         break;
  451.                         }
  452.                     WhiteScan++;
  453.                     TempNull = (PWCH)WhiteScan;
  454.                     }
  455.                 }
  456.             else {
  457. retrywsscan:
  458.                 lpApplicationName = lpCommandLine;
  459.                 while(*WhiteScan) {
  460.                     if ( *WhiteScan == (WCHAR)' ' ||
  461.                          *WhiteScan == (WCHAR)'\t' ) {
  462.                         TempNull = (PWCH)WhiteScan;
  463.                         break;
  464.                         }
  465.                     WhiteScan++;
  466.                     TempNull = (PWCH)WhiteScan;
  467.                     }
  468.                 }
  469.             TempChar = *TempNull;
  470.             *TempNull = UNICODE_NULL;

  471. #ifdef WX86

  472.             //
  473.             // Wx86 applications must use x86 version of known exes
  474.             // for compatibility.
  475.             //

  476.             if (UseKnownWx86Dll) {
  477.                LPWSTR KnownName;

  478.                NtCurrentTeb()->Wx86Thread.UseKnownWx86Dll = FALSE;

  479.                KnownName = BasepWx86KnownExe(lpApplicationName);
  480.                if (KnownName) {
  481.                   lpApplicationName = KnownName;
  482.                   }
  483.                }
  484. #endif


  485.             Length = SearchPathW(
  486.                         NULL,
  487.                         lpApplicationName,
  488.                         L".exe",
  489.                         MAX_PATH,
  490.                         NameBuffer,
  491.                         NULL
  492.                         )*2;

  493.             if (Length != 0 && Length < MAX_PATH * sizeof( WCHAR )) {
  494.                 //
  495.                 // SearchPathW worked, but file might be a directory
  496.                 // if this happens, we need to keep trying
  497.                 //
  498.                 fileattr = GetFileAttributesW(NameBuffer);
  499.                 if ( fileattr != 0xffffffff &&
  500.                      (fileattr & FILE_ATTRIBUTE_DIRECTORY) ) {
  501.                     Length = 0;
  502.                 } else {
  503.                     Length++;
  504.                     Length++;
  505.                 }
  506.             }

  507.             if ( !Length || Length >= MAX_PATH<<1 ) {

  508.                 //
  509.                 // If we search pathed, then return file not found.
  510.                 // otherwise, try to be more specific.
  511.                 //
  512.                 RTL_PATH_TYPE PathType;
  513.                 HANDLE hFile;

  514.                 PathType = RtlDetermineDosPathNameType_U(lpApplicationName);
  515.                 if ( PathType != RtlPathTypeRelative ) {

  516.                     //
  517.                     // The failed open should set get last error properly.
  518.                     //

  519.                     hFile = CreateFileW(
  520.                                 lpApplicationName,
  521.                                 GENERIC_READ,
  522.                                 FILE_SHARE_READ | FILE_SHARE_WRITE,
  523.                                 NULL,
  524.                                 OPEN_EXISTING,
  525.                                 FILE_ATTRIBUTE_NORMAL,
  526.                                 NULL
  527.                                 );
  528.                     if ( hFile != INVALID_HANDLE_VALUE ) {
  529.                         CloseHandle(hFile);
  530.                         BaseSetLastNTError(STATUS_OBJECT_NAME_NOT_FOUND);
  531.                         }
  532.                     }
  533.                 else {
  534.                     BaseSetLastNTError(STATUS_OBJECT_NAME_NOT_FOUND);
  535.                     }

  536.                 //
  537.                 // remember initial last error value for the retry scan path
  538.                 //

  539.                 if ( LastError ) {
  540.                     SetLastError(LastError);
  541.                     }
  542.                 else {
  543.                     LastError = GetLastError();
  544.                     }

  545.                 //
  546.                 // restore the command line
  547.                 //

  548.                 *TempNull = TempChar;
  549.                 lpApplicationName = NameBuffer;

  550.                 //
  551.                 // If we still have command line left, then keep going
  552.                 // the point is to march through the command line looking
  553.                 // for whitespace so we can try to find an image name
  554.                 // launches of things like:
  555.                 // c:\word 95\winword.exe /embedding -automation
  556.                 // require this. Our first iteration will stop at c:\word, our next
  557.                 // will stop at c:\word 95\winword.exe
  558.                 //
  559.                 if (*WhiteScan && SearchRetry) {
  560.                     WhiteScan++;
  561.                     TempNull = WhiteScan;
  562.                     QuoteInsert = TRUE;
  563.                     QuoteFound = TRUE;
  564.                     goto retrywsscan;
  565.                 }

  566.                 return FALSE;
  567.                 }
  568.             //
  569.             // restore the command line
  570.             //

  571.             *TempNull = TempChar;
  572.             lpApplicationName = NameBuffer;

  573.             //
  574.             // check whether it is setup.exe started by winlogon.exe
  575.             //
  576.             if (BasepIsSetupInvokedByWinLogon(lpApplicationName))
  577.             {
  578.                 // validate the flag
  579.                 if (!(dwCreationFlags & CREATE_IGNORE_SYSTEM_DEFAULT))
  580.                 {
  581.                     //
  582.                     // BUGBUBGUBGUBUGBUGBUGUBGBUGUBGUB
  583.                     //    Winlogon does not set the flag correctly
  584.                     //    in phase1, ignore it(now)
  585.                     //    in phase2, ASSERT it
  586.                     // BUGBUBGUBGUBUGBUGBUGUBGBUGUBGUB
  587.                     //
  588.                     dwCreationFlags |= CREATE_IGNORE_SYSTEM_DEFAULT;
  589.                 }
  590.             }
  591.             
  592.         }
  593.         else
  594.         if (!ARGUMENT_PRESENT( lpCommandLine ) || *lpCommandLine == UNICODE_NULL ) {
  595.             QuoteCmdLine = TRUE;
  596.             lpCommandLine = (LPWSTR)lpApplicationName;
  597.             }


  598. #ifdef WX86

  599.        //
  600.        // Wx86 applications must use x86 version of known exes
  601.        // for compatibility.
  602.        //

  603.        if (UseKnownWx86Dll) {
  604.            LPWSTR KnownName;

  605.            NtCurrentTeb()->Wx86Thread.UseKnownWx86Dll = FALSE;

  606.            KnownName = BasepWx86KnownExe(lpApplicationName);
  607.            if (KnownName) {

  608.                RtlFreeHeap(RtlProcessHeap(), 0, NameBuffer);
  609.                NameBuffer = KnownName;
  610.                lpApplicationName = KnownName;
  611.                }
  612.            }

  613. #endif

  614.         //
  615.         // Translate to an NT name.
  616.         //

  617.         TranslationStatus = RtlDosPathNameToNtPathName_U(
  618.                                 lpApplicationName,
  619.                                 &PathName,
  620.                                 NULL,
  621.                                 &RelativeName
  622.                                 );

  623.         if ( !TranslationStatus ) {
  624.             SetLastError(ERROR_PATH_NOT_FOUND);

  625.             return FALSE;
  626.             }

  627.         // forgot to free FreeBuffer before goto VdmRetry????
  628.         ASSERT(FreeBuffer == NULL);
  629.         FreeBuffer = PathName.Buffer;

  630.         RtlInitUnicodeString(&SxsWin32ExePath, lpApplicationName);

  631.         {
  632.             RTL_PATH_TYPE SxsWin32ExePathType = RtlDetermineDosPathNameType_U(lpApplicationName);

  633.             if ((SxsWin32ExePathType != RtlPathTypeDriveAbsolute) &&
  634.                 (SxsWin32ExePathType != RtlPathTypeUncAbsolute)) {
  635.                 if (ExePathFullBuffer == NULL) {
  636.                     // It seems that with VDM things, we can rerun this code with a new lpApplication, so
  637.                     // we protect against double-allocating the buffer and just allocate a big
  638.                     // MAX_PATH one the first time through, assuming it's good enough for the 2ndary times
  639.                     // too.
  640.                     ExePathFullBuffer = RtlAllocateHeap(RtlProcessHeap(), 0, (MAX_PATH + 1) * sizeof(WCHAR));
  641.                     if (ExePathFullBuffer == NULL) {
  642.                         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  643.                         return FALSE;
  644.                     }
  645.                 }

  646.                 RtlGetFullPathName_U(lpApplicationName, (MAX_PATH + 1) * sizeof(WCHAR), ExePathFullBuffer, NULL);

  647.                 RtlInitUnicodeString(&SxsWin32ExePath, ExePathFullBuffer);
  648.             }
  649.         }


  650.         SxsNtExePath = PathName;

  651.         if ( RelativeName.RelativeName.Length ) {
  652.             PathName = *(PUNICODE_STRING)&RelativeName.RelativeName;
  653.             }
  654.         else {
  655.             RelativeName.ContainingDirectory = NULL;
  656.             }

  657.         InitializeObjectAttributes(
  658.             &Obja,
  659.             &PathName,
  660.             OBJ_CASE_INSENSITIVE,
  661.             RelativeName.ContainingDirectory,
  662.             NULL
  663.             );

  664.         //
  665.         // Open the file for red and execute access
  666.         //

  667.         Status = NtOpenFile(
  668.                     &FileHandle,
  669.                     SYNCHRONIZE | FILE_EXECUTE | FILE_READ_ATTRIBUTES | FILE_READ_DATA,
  670.                     &Obja,
  671.                     &IoStatusBlock,
  672.                     FILE_SHARE_READ | FILE_SHARE_DELETE,
  673.                     FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE
  674.                     );

  675.         if (!NT_SUCCESS(Status) ) {

  676.             //
  677.             // We failed. Open the file for lesser access.
  678.             //

  679.             Status = NtOpenFile(
  680.                         &FileHandle,
  681.                         SYNCHRONIZE | FILE_EXECUTE,
  682.                         &Obja,
  683.                         &IoStatusBlock,
  684.                         FILE_SHARE_READ | FILE_SHARE_DELETE,
  685.                         FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE
  686.                         );

  687.             if (!NT_SUCCESS(Status) ) {
  688.                 //
  689.                 // if we failed, see if this is a device. If it is a device,
  690.                 // then just return invalid image format
  691.                 //

  692.                 if ( RtlIsDosDeviceName_U(RTL_CONST_CAST(PWSTR)(lpApplicationName)) ) {
  693.                     SetLastError(ERROR_BAD_DEVICE);
  694.                     }
  695.                 else {
  696.                     BaseSetLastNTError(Status);
  697.                     }

  698.                 return FALSE;
  699.             }

  700.         }

  701.         //
  702.         // If no desktop has been specified, use the caller's
  703.         // desktop.
  704.         //

  705.         if (StartupInfo.lpDesktop == NULL) {
  706.             StartupInfo.lpDesktop =
  707.                     (LPWSTR)((PRTL_USER_PROCESS_PARAMETERS)NtCurrentPeb()->
  708.                         ProcessParameters)->DesktopInfo.Buffer;
  709.             }

  710.         //
  711.         // Create a section object backed by the file
  712.         //

  713.         Status = NtCreateSection(
  714.                     &SectionHandle,
  715.                     SECTION_ALL_ACCESS,
  716.                     NULL,
  717.                     NULL,
  718.                     PAGE_EXECUTE,
  719.                     SEC_IMAGE,
  720.                     FileHandle
  721.                     );

  722.         //
  723.         // App Certification DLL
  724.         //

  725.         if (NT_SUCCESS(Status)) {
  726.             Status = BasepIsProcessAllowed(lpApplicationName);

  727.             if (!NT_SUCCESS(Status)) {
  728.                 BaseSetLastNTError(Status);
  729.                 NtClose(SectionHandle);
  730.                 return FALSE;
  731.             }


  732.       
  733.           //
  734.           // If Meow subsystem is enabled and caller specified CREATE_FORECEDOS for a win32 image
  735.           // push it into the meow subsystem
  736.           //

  737.           if ((dwCreationFlags & CREATE_FORCEDOS) && BaseStaticServerData->ForceDos) {
  738.                dwCreationFlags &= ~(CREATE_SHARED_WOW_VDM | CREATE_FORCEDOS);
  739.                dwCreationFlags |= CREATE_SEPARATE_WOW_VDM;
  740.                Status = STATUS_INVALID_IMAGE_WIN_16;
  741.                bMeowBinary = TRUE;

  742.                NtClose(SectionHandle);
  743.                SectionHandle = NULL;
  744.           }


  745.        }

  746.         //
  747.         // check appcompat (aka apphelp)
  748.         //
  749.         // if we are running under debugger, bVdmRetry will be FALSE
  750.         // yet pAppCompatData may have some data (from the app itself)
  751.         // debugger will do a separate CreateProcess on debugee
  752.         //
  753.         // apphelp gets called if it is win32 app or if it is a .bat or .cmd

  754.        if(!bVdmRetry &&
  755.           (NT_SUCCESS(Status) ||
  756.            (Status == STATUS_INVALID_IMAGE_NOT_MZ && !BaseIsDosApplication(&PathName,Status)))
  757.          ) {
  758.             NTSTATUS BadAppStatus;

  759.             if (NULL != pAppCompatData) {
  760.                 RtlFreeHeap(RtlProcessHeap(), 0, pAppCompatData);
  761.                 pAppCompatData = NULL;
  762.                 }
  763.               
  764.             if (NULL != pAppCompatSxsData) {
  765.                 RtlFreeHeap(RtlProcessHeap(), 0, pAppCompatSxsData);
  766.                 pAppCompatSxsData = NULL;
  767.                 }
  768.             
  769.             //
  770.             // we only check ONCE --
  771.             // the second time around is rather meaningless - to check for posix/ntvdm/os2 emulation
  772.             //
  773.             BadAppStatus = BasepCheckBadapp(FileHandle,
  774.                                             PathName.Buffer,
  775.                                             (WCHAR*)lpEnvironment,
  776.                                             &pAppCompatData,
  777.                                             &cbAppCompatData,
  778.                                             &pAppCompatSxsData,
  779.                                             &cbAppCompatSxsData);
  780.                     
  781.             if (!NT_SUCCESS(BadAppStatus)) {
  782.                 if (BadAppStatus == STATUS_ACCESS_DENIED) {
  783.                     SetLastError(ERROR_CANCELLED);
  784.                     }
  785.                 else {
  786.                     BaseSetLastNTError(BadAppStatus);
  787.                     }

  788.                 if (SectionHandle) {
  789.                     NtClose(SectionHandle);
  790.                     SectionHandle = NULL;
  791.                     }
  792.                 return FALSE;
  793.                 }
  794.             }

  795.        //
  796.        // Winsafer code
  797.        //
  798.        // If this is the first time then we will have to do Safer checks.
  799.        // Note that we do not impose any restrictions on the interpreter
  800.        // itself since it is part of OS.
  801.        //


  802.        if ((!bVdmRetry) &&
  803.            ( (dwCreationFlags & CREATE_PRESERVE_CODE_AUTHZ_LEVEL) == 0 )) {

  804.            NTSTATUS SaferStatus;


  805.            {

  806.                //
  807.                // WinSafer process sandbox restrictions handling.
  808.                // Should be done for non .NET images only.
  809.                //

  810.                SaferStatus = BasepCheckWinSaferRestrictions(
  811.                                                       hUserToken,
  812.                                                       lpApplicationName,   // same as PathName.Buffer
  813.                                                       FileHandle,
  814.                                                       &dwJobMemberLevel,
  815.                                                       &hSaferRestrictedToken,
  816.                                                       &hSaferAssignmentJob);
  817.                if (SaferStatus == -1) {
  818.                    SetLastError(ERROR_ACCESS_DISABLED_BY_POLICY);
  819.                    bStatus =  FALSE;
  820.                    leave;
  821.                } else if (!NT_SUCCESS(SaferStatus)) {
  822.                    BaseSetLastNTError(SaferStatus);
  823.                    bStatus = FALSE;
  824.                    leave;
  825.                }
  826.            }
  827.        }


  828.         if (!NT_SUCCESS(Status)) {
  829.             switch (Status) {
  830.                 // 16 bit OS/2 exe
  831.                 case STATUS_INVALID_IMAGE_NE_FORMAT:
  832. #if defined(i386) && defined(OS2_SUPPORT_ENABLED)
  833.                 //
  834.                 // Use OS/2 if x86 (OS/2 not supported on risc),
  835.                 //    and CreationFlags don't have forcedos bit
  836.                 //    and Registry didn't specify ForceDos
  837.                 //
  838.                 // else execute as a DOS bound app.
  839.                 //
  840.                 //

  841.                 if (!(dwCreationFlags & CREATE_FORCEDOS) &&
  842.                     !BaseStaticServerData->ForceDos)
  843.                   {

  844.                     if ( !BuildSubSysCommandLine( L"OS2 /P ",
  845.                                                   lpApplicationName,
  846.                                                   lpCommandLine,
  847.                                                   &SubSysCommandLine
  848.                                                 ) ) {
  849.                         return FALSE;
  850.                         }

  851.                     lpCommandLine = SubSysCommandLine.Buffer;

  852.                     lpApplicationName = NULL;

  853.                     bVdmRetry = TRUE;
  854.                     goto VdmRetry;
  855.                     }
  856. #endif
  857.                     // Falls into Dos case, so that stub message will be
  858.                     // printed, and bound apps will run w/o OS/2 subsytem

  859.                 // Dos .exe or .com

  860.                 case STATUS_INVALID_IMAGE_PROTECT:
  861.                 case STATUS_INVALID_IMAGE_NOT_MZ:
  862. ForceDos:
  863.                     {
  864.                     ULONG BinarySubType;

  865.                     BinarySubType = BINARY_TYPE_DOS_EXE;
  866.                     if (Status == STATUS_INVALID_IMAGE_PROTECT   ||
  867.                         Status == STATUS_INVALID_IMAGE_NE_FORMAT ||
  868.                        (BinarySubType = BaseIsDosApplication(&PathName,Status)) )
  869.                        {
  870. #if defined(_WIN64) || defined(BUILD_WOW6432)
  871.                         //
  872.                         // If this a DOS application, then we need to pop up a dialog
  873.                         // saying that this an invalid win32 application.
  874.                         //
  875.                         goto RaiseInvalidWin32Error;
  876. #endif
  877.                         VdmBinaryType = BINARY_TYPE_DOS;

  878.                         // create the environment before going to the
  879.                         // server. This was done becuase we want NTVDM
  880.                         // to have the new environment when it gets
  881.                         // created.
  882.                         if (!BaseCreateVDMEnvironment(
  883.                                     lpEnvironment,
  884.                                     &AnsiStringVDMEnv,
  885.                                     &UnicodeStringVDMEnv
  886.                                     )) {
  887.                             return FALSE;
  888.                         }

  889.                         if(!BaseCheckVDM(VdmBinaryType | BinarySubType,
  890.                                          lpApplicationName,
  891.                                          lpCommandLine,
  892.                                          lpCurrentDirectory,
  893.                                          &AnsiStringVDMEnv,
  894.                                          &m,
  895.                                          &iTask,
  896.                                          dwCreationFlags,
  897.                                          &StartupInfo
  898.                                          )) {

  899.                             return FALSE;
  900.                         }

  901.                         // Check the return value from the server
  902.                         switch (b->VDMState & VDM_STATE_MASK) {
  903.                             case VDM_NOT_PRESENT:
  904.                                 // mark this so the server can undo
  905.                                 // creation if something goes wrong.
  906.                                 // We marked it "partially created" because
  907.                                 // the NTVDM has yet not been fully created.
  908.                                 // a call to UpdateVdmEntry to update
  909.                                 // process handle will signal the NTVDM
  910.                                 // process completed creation
  911.                                 VDMCreationState = VDM_PARTIALLY_CREATED;
  912.                                 // fail the call if NTVDM process is being
  913.                                 // created DETACHED.
  914.                                 // note that, we let it go if NTVDM process
  915.                                 // is already running.
  916.                                 if (dwCreationFlags & DETACHED_PROCESS) {
  917.                                     SetLastError(ERROR_ACCESS_DENIED);
  918.                                     return FALSE;
  919.                                     }
  920.                                 if (!BaseGetVdmConfigInfo(lpCommandLine,
  921.                                                           iTask,
  922.                                                           VdmBinaryType,
  923.                                                           &VdmNameString,
  924.                                                           &VdmReserve)) {
  925.                                     BaseSetLastNTError(Status);
  926.                                     return FALSE;
  927.                                     }

  928.                                 lpCommandLine = VdmNameString.Buffer;
  929.                                 lpApplicationName = NULL;

  930.                                 break;

  931.                             case VDM_PRESENT_NOT_READY:
  932.                                 SetLastError (ERROR_NOT_READY);
  933.                                 return FALSE;

  934.                             case VDM_PRESENT_AND_READY:
  935.                                 VDMCreationState = VDM_BEING_REUSED;
  936.                                 VdmWaitHandle = b->WaitObjectForParent;
  937.                                 break;
  938.                             }
  939.                          VdmReserve--;               // we reserve from addr 1
  940.                          if(VdmWaitHandle)
  941.                             goto VdmExists;
  942.                          else{
  943.                             bInheritHandles = FALSE;
  944.                             if (lpEnvironment &&
  945.                                 !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT)){
  946.                                 RtlDestroyEnvironment(lpEnvironment);
  947.                                 }
  948.                             lpEnvironment = UnicodeStringVDMEnv.Buffer;
  949.                             bVdmRetry = TRUE;
  950.                             goto VdmRetry;
  951.                             }
  952.                         }
  953.                     else {

  954.                         //
  955.                         //  must be a .bat or .cmd file
  956.                         //

  957.                         static PWCHAR CmdPrefix = L"cmd /c ";
  958.                         PWCHAR NewCommandLine;
  959.                         ULONG Length;
  960.                         PWCHAR Last4 = &PathName.Buffer[PathName.Length / sizeof( WCHAR )-4];

  961.                         if ( PathName.Length < 8 ) {
  962.                             SetLastError(ERROR_BAD_EXE_FORMAT);
  963.                             return FALSE;
  964.                             }

  965.                         if (_wcsnicmp( Last4, L".bat", 4 ) && _wcsnicmp( Last4, L".cmd", 4 )) {
  966.                             SetLastError(ERROR_BAD_EXE_FORMAT);
  967.                             return FALSE;
  968.                         }

  969.                         Length = wcslen( CmdPrefix )
  970.                                  + (QuoteCmdLine || QuoteFound )
  971.                                  + wcslen( lpCommandLine )
  972.                                  + (QuoteCmdLine || QuoteFound)
  973.                                  + 1;

  974.                         NewCommandLine = RtlAllocateHeap( RtlProcessHeap( ),
  975.                                                           MAKE_TAG( TMP_TAG ),
  976.                                                           Length * sizeof( WCHAR ) );

  977.                         if (NewCommandLine == NULL) {
  978.                             BaseSetLastNTError(STATUS_NO_MEMORY);
  979.                             return FALSE;
  980.                         }

  981.                         wcscpy( NewCommandLine, CmdPrefix );
  982.                         if (QuoteCmdLine || QuoteFound) {
  983.                             wcscat( NewCommandLine, L""" );
  984.                         }
  985.                         wcscat( NewCommandLine, lpCommandLine );
  986.                         if (QuoteCmdLine || QuoteFound) {
  987.                             wcscat( NewCommandLine, L""" );
  988.                         }

  989.                         RtlInitUnicodeString( &SubSysCommandLine, NewCommandLine );

  990.                         lpCommandLine = SubSysCommandLine.Buffer;

  991.                         lpApplicationName = NULL;

  992.                         bVdmRetry = TRUE;
  993.                         goto VdmRetry;

  994.                         }

  995.                     }

  996.                 // 16 bit windows exe
  997.                 case STATUS_INVALID_IMAGE_WIN_16:
  998. #if defined(BUILD_WOW6432) || defined(_WIN64)
  999.                    if (lpOriginalApplicationName == NULL) {
  1000.                        // pass in the part of the command line after the exe name
  1001.                        // including whitespace
  1002.                        lpCommandLine = ((*TempNull == '"') ? TempNull + 1 : TempNull);
  1003.                    } else {
  1004.                        lpCommandLine = lpOriginalCommandLine;
  1005.                    }

  1006.                    return NtVdm64CreateProcess(lpOriginalApplicationName == NULL,
  1007.                                                lpApplicationName,             // this is now the real file name we've loaded
  1008.                                                lpCommandLine,
  1009.                                                lpProcessAttributes,
  1010.                                                lpThreadAttributes,
  1011.                                                bInheritHandles,
  1012.                                                (dwCreationFlags & ~CREATE_UNICODE_ENVIRONMENT),  // the environment has already been converted to unicode
  1013.                                                lpEnvironment,
  1014.                                                lpCurrentDirectory,
  1015.                                                lpStartupInfo,
  1016.                                                lpProcessInformation
  1017.                                                );
  1018. #endif
  1019.                    if (dwCreationFlags & CREATE_FORCEDOS) {
  1020.                        goto ForceDos;
  1021.                        }

  1022.                     IsWowBinary = TRUE;
  1023.                     if (!BaseCreateVDMEnvironment(lpEnvironment,
  1024.                                                   &AnsiStringVDMEnv,
  1025.                                                   &UnicodeStringVDMEnv)) {
  1026.                         return FALSE;
  1027.                         }

  1028. RetrySepWow:
  1029.                     VdmBinaryType = dwCreationFlags & CREATE_SEPARATE_WOW_VDM
  1030.                                      ? BINARY_TYPE_SEPWOW : BINARY_TYPE_WIN16;

  1031.                     if (!BaseCheckVDM(VdmBinaryType,
  1032.                                       lpApplicationName,
  1033.                                       lpCommandLine,
  1034.                                       lpCurrentDirectory,
  1035.                                       &AnsiStringVDMEnv,
  1036.                                       &m,
  1037.                                       &iTask,
  1038.                                       dwCreationFlags,
  1039.                                       &StartupInfo
  1040.                                       ))
  1041.                        {
  1042.                         //
  1043.                         // If we failed with access denied, caller may not
  1044.                         // be allowed allowed to access the shared wow's
  1045.                         // desktop, so retry as a separate wow
  1046.                         //
  1047.                         if (VdmBinaryType == BINARY_TYPE_WIN16 &&
  1048.                             GetLastError() == ERROR_ACCESS_DENIED)
  1049.                           {
  1050.                            dwCreationFlags |= CREATE_SEPARATE_WOW_VDM;
  1051.                            }
  1052.                         else {
  1053.                             return FALSE;
  1054.                             }
  1055.                         goto RetrySepWow;
  1056.                         }

  1057.                     // Check the return value from the server
  1058.                     switch (b->VDMState & VDM_STATE_MASK){
  1059.                         case VDM_NOT_PRESENT:
  1060.                             // mark this so the server can undo
  1061.                             // creation if something goes wrong.
  1062.                             // We marked it "partitially created" because
  1063.                             // the NTVDM has yet not been fully created.
  1064.                             // a call to UpdateVdmEntry to update
  1065.                             // process handle will signal the NTVDM
  1066.                             // process completed creation

  1067.                             VDMCreationState = VDM_PARTIALLY_CREATED;

  1068.                             // jarbats: 1/8/2001
  1069.                             // Tell BaseGetVdmConfigInfo to create
  1070.                             // vdm commandline for meow
  1071.                             //

  1072.                             if (bMeowBinary)
  1073.                                {
  1074.                                VdmReserve = 1;
  1075.                                }

  1076.                             if (!BaseGetVdmConfigInfo(
  1077.                                     lpCommandLine,
  1078.                                     iTask,
  1079.                                     VdmBinaryType,
  1080.                                     &VdmNameString,
  1081.                                     &VdmReserve
  1082.                                     )) {
  1083.                                 BaseSetLastNTError(Status);
  1084.                                 return FALSE;
  1085.                                 }

  1086.                             lpCommandLine = VdmNameString.Buffer;
  1087.                             lpApplicationName = NULL;


  1088.                             //
  1089.                             // Wow must have a hidden console
  1090.                             // Throw away DETACHED_PROCESS flag which isn't
  1091.                             // meaningful for Win16 apps.
  1092.                             //

  1093.                             dwCreationFlags |= CREATE_NO_WINDOW;
  1094.                             dwCreationFlags &= ~(CREATE_NEW_CONSOLE | DETACHED_PROCESS);


  1095.                             //
  1096.                             // We're starting a WOW VDM, turn on feedback unless
  1097.                             // the creator passed STARTF_FORCEOFFFEEDBACK.
  1098.                             //

  1099.                             StartupInfo.dwFlags |= STARTF_FORCEONFEEDBACK;

  1100.                             break;

  1101.                         case VDM_PRESENT_NOT_READY:
  1102.                             SetLastError (ERROR_NOT_READY);
  1103.                             return FALSE;

  1104.                         case VDM_PRESENT_AND_READY:
  1105.                             VDMCreationState = VDM_BEING_REUSED;
  1106.                             VdmWaitHandle = b->WaitObjectForParent;
  1107.                             break;
  1108.                         }

  1109.                     VdmReserve--;               // we reserve from addr 1
  1110.                     if(VdmWaitHandle)
  1111.                         goto VdmExists;
  1112.                     else {
  1113.                         bInheritHandles = FALSE;
  1114.                         // replace the environment with ours
  1115.                         if (lpEnvironment &&
  1116.                             !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT)) {
  1117.                             RtlDestroyEnvironment(lpEnvironment);
  1118.                             }
  1119.                         lpEnvironment = UnicodeStringVDMEnv.Buffer;
  1120.                         bVdmRetry = TRUE;
  1121.                         goto VdmRetry;
  1122.                         }

  1123.                 case STATUS_FILE_IS_OFFLINE:
  1124.                     SetLastError(ERROR_FILE_OFFLINE);
  1125.                     break;

  1126.                 default :
  1127.                     SetLastError(ERROR_BAD_EXE_FORMAT);
  1128.                     return FALSE;
  1129.             }
  1130.         }

  1131.         //
  1132.         // Make sure only WOW apps can have the CREATE_SEPARATE_WOW_VDM flag.
  1133.         //

  1134.         if (!IsWowBinary && (dwCreationFlags & CREATE_SEPARATE_WOW_VDM)) {
  1135.             dwCreationFlags &= ~CREATE_SEPARATE_WOW_VDM;
  1136.         }

  1137.         //
  1138.         // Query the section to determine the stack parameters and
  1139.         // image entrypoint.
  1140.         //

  1141.         Status = NtQuerySection(
  1142.                     SectionHandle,
  1143.                     SectionImageInformation,
  1144.                     &ImageInformation,
  1145.                     sizeof( ImageInformation ),
  1146.                     NULL
  1147.                     );

  1148.         if (!NT_SUCCESS( Status )) {
  1149.             BaseSetLastNTError(Status);
  1150.             return FALSE;
  1151.             }

  1152.         if (ImageInformation.ImageCharacteristics & IMAGE_FILE_DLL) {
  1153.             SetLastError(ERROR_BAD_EXE_FORMAT);
  1154.             return FALSE;
  1155.             }

  1156.         ImageFileDebuggerCommand[ 0 ] = UNICODE_NULL;
  1157.         if (!(dwCreationFlags & (DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS)) ||
  1158.             NtCurrentPeb()->ReadImageFileExecOptions
  1159.            ) {
  1160.             LdrQueryImageFileExecutionOptions( &PathName,
  1161.                                                L"Debugger",
  1162.                                                REG_SZ,
  1163.                                                ImageFileDebuggerCommand,
  1164.                                                sizeof( ImageFileDebuggerCommand ),
  1165.                                                NULL
  1166.                                              );
  1167.             }


  1168.         if ((ImageInformation.Machine < USER_SHARED_DATA->ImageNumberLow) ||
  1169.             (ImageInformation.Machine > USER_SHARED_DATA->ImageNumberHigh)) {
  1170. #if defined(_WIN64) || defined(BUILD_WOW6432)
  1171.             if (ImageInformation.Machine == IMAGE_FILE_MACHINE_I386) {
  1172.                // Fall through since this is a valid machine type.
  1173.                 }
  1174.              else
  1175. #endif
  1176.                 {
  1177.                 ULONG_PTR ErrorParameters[2];
  1178.                 ULONG ErrorResponse;

  1179. #if defined(_WIN64) || defined(BUILD_WOW6432)
  1180. RaiseInvalidWin32Error:
  1181. #endif
  1182.                 ErrorResponse = ResponseOk;
  1183.                 ErrorParameters[0] = (ULONG_PTR)&PathName;

  1184.                 NtRaiseHardError( STATUS_IMAGE_MACHINE_TYPE_MISMATCH_EXE,
  1185.                                   1,
  1186.                                   1,
  1187.                                   ErrorParameters,
  1188.                                   OptionOk,
  1189.                                   &ErrorResponse
  1190.                                 );
  1191.                 if ( NtCurrentPeb()->ImageSubsystemMajorVersion <= 3 ) {
  1192.                     SetLastError(ERROR_BAD_EXE_FORMAT);
  1193.                     }
  1194.                 else {
  1195.                     SetLastError(ERROR_EXE_MACHINE_TYPE_MISMATCH);
  1196.                     }
  1197.                 return FALSE;
  1198.                 }
  1199.             }

  1200.         if ( ImageInformation.SubSystemType != IMAGE_SUBSYSTEM_WINDOWS_GUI &&
  1201.              ImageInformation.SubSystemType != IMAGE_SUBSYSTEM_WINDOWS_CUI ) {

  1202.             // POSIX exe

  1203.             NtClose(SectionHandle);
  1204.             SectionHandle = NULL;

  1205.             if ( ImageInformation.SubSystemType == IMAGE_SUBSYSTEM_POSIX_CUI ) {

  1206.                 if ( !BuildSubSysCommandLine( L"POSIX /P ",
  1207.                                               lpApplicationName,
  1208.                                               lpCommandLine,
  1209.                                               &SubSysCommandLine
  1210.                                             ) ) {
  1211.                     return FALSE;
  1212.                 }

  1213.                 lpCommandLine = SubSysCommandLine.Buffer;

  1214.                 lpApplicationName = NULL;
  1215.                 bVdmRetry = TRUE;
  1216.                 goto VdmRetry;
  1217.                 }
  1218.             else {
  1219.                 SetLastError(ERROR_CHILD_NOT_COMPLETE);
  1220.                 return FALSE;
  1221.                 }
  1222.             }
  1223.         else {
  1224.             if (!BasepIsImageVersionOk( ImageInformation.SubSystemMajorVersion,
  1225.                                         ImageInformation.SubSystemMinorVersion) ) {
  1226.                 SetLastError(ERROR_BAD_EXE_FORMAT);
  1227.                 return FALSE;
  1228.                 }
  1229.             }

  1230.         if (ImageFileDebuggerCommand[ 0 ] != UNICODE_NULL) {
  1231.             SIZE_T n;

  1232.             n = wcslen( lpCommandLine );
  1233.             if (n == 0) {
  1234.                 lpCommandLine = (LPWSTR)lpApplicationName;
  1235.                 n = wcslen( lpCommandLine );
  1236.                 }

  1237.             n += wcslen( ImageFileDebuggerCommand ) + 1 + 2;
  1238.             n *= sizeof( WCHAR );

  1239.             SubSysCommandLine.Buffer = RtlAllocateHeap( RtlProcessHeap(), MAKE_TAG( TMP_TAG ), n );
  1240.             SubSysCommandLine.Length = 0;
  1241.             SubSysCommandLine.MaximumLength = (USHORT)n;
  1242.             RtlAppendUnicodeToString( &SubSysCommandLine, ImageFileDebuggerCommand );
  1243.             RtlAppendUnicodeToString( &SubSysCommandLine, L" " );
  1244.             RtlAppendUnicodeToString( &SubSysCommandLine, lpCommandLine );
  1245. #if DBG
  1246.             DbgPrint( "BASE: Calling debugger with '%wZ'\n", &SubSysCommandLine );
  1247. #endif
  1248.             lpCommandLine = SubSysCommandLine.Buffer;
  1249.             lpApplicationName = NULL;
  1250.             NtClose(SectionHandle);
  1251.             SectionHandle = NULL;
  1252.             RtlFreeHeap(RtlProcessHeap(), 0, NameBuffer);
  1253.             NameBuffer = NULL;
  1254.             RtlFreeHeap(RtlProcessHeap(), 0, FreeBuffer);
  1255.             FreeBuffer = NULL;
  1256.             goto VdmRetry;
  1257.             }

  1258.         //
  1259.         // Create the process object
  1260.         //

  1261.         pObja = BaseFormatObjectAttributes(&Obja,lpProcessAttributes,NULL);

  1262.         Flags = 0;
  1263.         if (dwCreationFlags & CREATE_BREAKAWAY_FROM_JOB ) {
  1264.             Flags |= PROCESS_CREATE_FLAGS_BREAKAWAY;
  1265.         }

  1266.         if ( dwCreationFlags & (DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS) ) {
  1267.             Status = DbgUiConnectToDbg();
  1268.             if ( !NT_SUCCESS(Status) ) {
  1269.                 BaseSetLastNTError(Status);
  1270.                 return FALSE;
  1271.             }
  1272.             DebugPortHandle = DbgUiGetThreadDebugObject ();
  1273.             if (dwCreationFlags & DEBUG_ONLY_THIS_PROCESS) {
  1274.                 Flags |= PROCESS_CREATE_FLAGS_NO_DEBUG_INHERIT;
  1275.             }
  1276.         }

  1277.         if (bInheritHandles) {
  1278.             Flags |= PROCESS_CREATE_FLAGS_INHERIT_HANDLES;
  1279.         }

  1280.         if (((ImageInformation.LoaderFlags & IMAGE_LOADER_FLAGS_COMPLUS) != 0)) {

  1281. #if defined(_WIN64) || defined(BUILD_WOW6432)

  1282.             //
  1283.             // Check if this is a 32-bit IL_ONLY COM+ image that needs to run natively
  1284.             // on Win64.
  1285.             //

  1286.             if ( ImageInformation.Machine == IMAGE_FILE_MACHINE_I386 ) {

  1287.                 Status = BasepIsComplusILImage(
  1288.                                               SectionHandle,
  1289.                                               &ImageInformation,
  1290.                                               &ComPlusILImage
  1291.                                               );

  1292.                 if ((NT_SUCCESS (Status)) && (ComPlusILImage != FALSE)) {
  1293.                     Flags |= PROCESS_CREATE_FLAGS_OVERRIDE_ADDRESS_SPACE;
  1294.                 }
  1295.             }

  1296. #endif
  1297.         }
  1298.             
  1299.         //
  1300.         // This is temporary till we get Shim dlls support for native Win64 applications.
  1301.         //

  1302.         if (ImageInformation.Machine != IMAGE_FILE_MACHINE_I386) {
  1303.             pAppCompatDataTemp = NULL;
  1304.         } else {
  1305.             pAppCompatDataTemp = pAppCompatData;
  1306.         }

  1307.         Status = NtCreateProcessEx(
  1308.                     &ProcessHandle,
  1309.                     PROCESS_ALL_ACCESS,
  1310.                     pObja,
  1311.                     NtCurrentProcess(),
  1312.                     Flags,
  1313.                     SectionHandle,
  1314.                     DebugPortHandle,
  1315.                     NULL,
  1316.                     dwJobMemberLevel         // Job member level
  1317.                     );
  1318.         if ( !NT_SUCCESS(Status) ) {
  1319.             BaseSetLastNTError(Status);
  1320.             return FALSE;
  1321.         }

  1322.         //
  1323.         // NtCreateProcess will set to normal OR inherit if parent is IDLE or Below
  1324.         // only override if a mask is given during the create.
  1325.         //

  1326.         if ( PriClass.PriorityClass != PROCESS_PRIORITY_CLASS_UNKNOWN ) {
  1327.             State = NULL;
  1328.             if ( PriClass.PriorityClass ==  PROCESS_PRIORITY_CLASS_REALTIME ) {
  1329.                 State = BasepIsRealtimeAllowed(TRUE);
  1330.                 }
  1331.             Status = NtSetInformationProcess(
  1332.                         ProcessHandle,
  1333.                         ProcessPriorityClass,
  1334.                         (PVOID)&PriClass,
  1335.                         sizeof(PriClass)
  1336.                         );
  1337.             if ( State ) {
  1338.                 BasepReleasePrivilege( State );
  1339.                 }

  1340.             if ( !NT_SUCCESS(Status) ) {
  1341.                 BaseSetLastNTError(Status);
  1342.                 return FALSE;
  1343.                 }
  1344.             }

  1345.         if (dwCreationFlags & CREATE_DEFAULT_ERROR_MODE) {
  1346.             UINT NewMode;
  1347.             NewMode = SEM_FAILCRITICALERRORS;
  1348.             NtSetInformationProcess(
  1349.                 ProcessHandle,
  1350.                 ProcessDefaultHardErrorMode,
  1351.                 &NewMode,
  1352.                 sizeof(NewMode)
  1353.                 );
  1354.             }

  1355.         //
  1356.         // If the process is being created for a VDM call the server with
  1357.         // process handle.
  1358.         //

  1359.         if (VdmBinaryType) {
  1360.             VdmWaitHandle = ProcessHandle;
  1361.             if (!BaseUpdateVDMEntry(UPDATE_VDM_PROCESS_HANDLE,
  1362.                                     &VdmWaitHandle,
  1363.                                     iTask,
  1364.                                     VdmBinaryType
  1365.                                     ))
  1366.                 {
  1367.                 //make sure we don't close the handle twice --
  1368.                 //(VdmWaitHandle == ProcessHandle) if we don't do this.
  1369.                 VdmWaitHandle = NULL;
  1370.                 return FALSE;
  1371.                 }

  1372.             //
  1373.             // For Sep wow the VdmWaitHandle = NULL (there is none!)
  1374.             //

  1375.             VDMCreationState |= VDM_FULLY_CREATED;
  1376.             }


  1377. #if defined(i386)
  1378.         //
  1379.         // Reserve memory in the new process' address space if necessary
  1380.         // (for vdms). This is required only for x86 system.
  1381.         //

  1382.     if ( VdmReserve ) {
  1383.             BigVdmReserve = VdmReserve;
  1384.             Status = NtAllocateVirtualMemory(
  1385.                         ProcessHandle,
  1386.                         &BaseAddress,
  1387.                         0L,
  1388.                         &BigVdmReserve,
  1389.                         MEM_RESERVE,
  1390.                         PAGE_EXECUTE_READWRITE
  1391.                         );
  1392.             if ( !NT_SUCCESS(Status) ){
  1393.                 BaseSetLastNTError(Status);
  1394.                 return FALSE;
  1395.             }
  1396.     }
  1397. #endif

  1398.         //
  1399.         // Abuse the StaticSize fields temporarily.
  1400.         // They are somewhat private, but we are using them for temporary space, sort of.
  1401.         // These are the right values for them, but we need to call the proper initialization function,
  1402.         // which will store the values a second time (possibly optimized away).
  1403.         //
  1404.         SxsWin32ManifestPathBuffer.ByteBuffer.StaticSize = SxsWin32ExePath.Length + sizeof(SXS_MANIFEST_SUFFIX);
  1405.         SxsWin32PolicyPathBuffer.ByteBuffer.StaticSize = SxsWin32ExePath.Length + sizeof(SXS_POLICY_SUFFIX);
  1406.         SxsWin32AssemblyDirectoryBuffer.ByteBuffer.StaticSize = SxsWin32ExePath.Length + sizeof(WCHAR); // Win32AssemblyDirectory overestimate
  1407.         SxsNtManifestPathBuffer.ByteBuffer.StaticSize = SxsNtExePath.Length + sizeof(SXS_MANIFEST_SUFFIX);
  1408.         SxsNtPolicyPathBuffer.ByteBuffer.StaticSize = SxsNtExePath.Length + sizeof(SXS_POLICY_SUFFIX);
  1409.         //
  1410.         // now add them up as BYTE sizes
  1411.         //
  1412.         SxsConglomeratedBufferSizeBytes = 0;
  1413.         for (sxsi = 0 ; sxsi != RTL_NUMBER_OF(SxsStringBuffers) ; ++sxsi) {
  1414.             SxsConglomeratedBufferSizeBytes += SxsStringBuffers[sxsi]->ByteBuffer.StaticSize;
  1415.         }
  1416. #if DBG
  1417.         DbgPrintEx(
  1418.             DPFLTR_SXS_ID,
  1419.             DPFLTR_INFO_LEVEL,
  1420.             "SXS: SxsConglomeratedBufferSizeBytes:%Id\n",
  1421.             SxsConglomeratedBufferSizeBytes
  1422.             );
  1423. #endif
  1424.         //
  1425.         // one honking heap allocation
  1426.         //
  1427.         SxsConglomeratedByteBuffer = (PBYTE)RtlAllocateHeap(RtlProcessHeap(), 0, SxsConglomeratedBufferSizeBytes);
  1428.         if (SxsConglomeratedByteBuffer == NULL) {
  1429.             BaseSetLastNTError(STATUS_NO_MEMORY);
  1430.             return FALSE;
  1431.         }
  1432.         //
  1433.         // now dole out pieces, calling the proper initialization function
  1434.         //
  1435.         for (sxsi= 0 ; sxsi != RTL_NUMBER_OF(SxsStringBuffers) ; ++sxsi) {
  1436.             RtlInitUnicodeStringBuffer(
  1437.                 SxsStringBuffers[sxsi],
  1438.                 (sxsi != 0) ? SxsStringBuffers[sxsi - 1]->ByteBuffer.Buffer + SxsStringBuffers[sxsi- 1]->ByteBuffer.StaticSize
  1439.                          : SxsConglomeratedByteBuffer,
  1440.                 SxsStringBuffers[sxsi]->ByteBuffer.StaticSize
  1441.                 );
  1442.         }

  1443.         SxsExeHandles.Process = ProcessHandle;
  1444.         SxsExeHandles.File = FileHandle;
  1445.          // The 1 bit here means something different than in the loader.
  1446.         ASSERT((((ULONG_PTR)SectionHandle) & (ULONG_PTR)1) == 0);
  1447.         SxsExeHandles.Section = SectionHandle;

  1448.         // if we have an override stream, use it
  1449.         if (NULL != pAppCompatSxsData) {
  1450.             AppCompatSxsManifest.Name    = SxsWin32ExePath;     // unicode string
  1451.             AppCompatSxsManifest.Address = pAppCompatSxsData;   // pointer to unicode manifest
  1452.             AppCompatSxsManifest.Size    = cbAppCompatSxsData;  // byte count
  1453.         }

  1454.         Status = BasepSxsCreateProcessCsrMessage(
  1455.             (NULL != pAppCompatSxsData) ? &AppCompatSxsManifest : NULL, // override manifest (appcompat hook)
  1456.             NULL, // override policy (appcompat hook)
  1457.             &SxsManifestPathPair,
  1458.             &SxsManifestFileHandles,
  1459.             &SxsExePathPair,
  1460.             &SxsExeHandles,
  1461.             &SxsPolicyPathPair,
  1462.             &SxsPolicyHandles,
  1463.             &SxsWin32AssemblyDirectoryBuffer,
  1464.             &a->Sxs
  1465.             );
  1466. #if DBG
  1467.         // verify the buffer size calculation
  1468.         for (sxsi = 0 ; sxsi != RTL_NUMBER_OF(SxsStringBuffers) ; ++sxsi)
  1469.         {
  1470.             if (SxsStringBuffers[sxsi]->ByteBuffer.Buffer != SxsStringBuffers[sxsi]->ByteBuffer.StaticBuffer)
  1471.             {
  1472.                 DbgPrintEx(
  1473.                     DPFLTR_SXS_ID,
  1474.                     DPFLTR_WARNING_LEVEL,
  1475.                     "SXS: SxsStringBuffers[%lu]'s StaticSize was computed too small (%Id, %Id)\n",
  1476.                     sxsi,
  1477.                     SxsStringBuffers[sxsi]->ByteBuffer.StaticSize,
  1478.                     SxsStringBuffers[sxsi]->ByteBuffer.Size
  1479.                     );
  1480.             }
  1481.         }
  1482. #endif
  1483.         if ( !NT_SUCCESS( Status ) ) {
  1484.             BaseSetLastNTError(Status);
  1485.             return FALSE;
  1486.             }

  1487.         //
  1488.         // Determine the location of the
  1489.         // processes PEB.
  1490.         //

  1491.         Status = NtQueryInformationProcess(
  1492.                     ProcessHandle,
  1493.                     ProcessBasicInformation,
  1494.                     &ProcessInfo,
  1495.                     sizeof( ProcessInfo ),
  1496.                     NULL
  1497.                     );
  1498.         if ( !NT_SUCCESS( Status ) ) {
  1499.             BaseSetLastNTError(Status);
  1500.             return FALSE;
  1501.             }

  1502.         Peb = ProcessInfo.PebBaseAddress;

  1503.         //
  1504.         // Push the parameters into the address space of the new process
  1505.         //

  1506.         if ( ARGUMENT_PRESENT(lpCurrentDirectory) ) {
  1507.             CurdirBuffer = RtlAllocateHeap( RtlProcessHeap(),
  1508.                                             MAKE_TAG( TMP_TAG ),
  1509.                                             (MAX_PATH + 1) * sizeof( WCHAR ) );
  1510.             if ( !CurdirBuffer ) {
  1511.                 BaseSetLastNTError(STATUS_NO_MEMORY);
  1512.                 return FALSE;
  1513.                 }
  1514.             CurdirLength2 = GetFullPathNameW(
  1515.                                 lpCurrentDirectory,
  1516.                                 MAX_PATH,
  1517.                                 CurdirBuffer,
  1518.                                 &CurdirFilePart
  1519.                                 );
  1520.             if ( CurdirLength2 > MAX_PATH ) {
  1521.                 SetLastError(ERROR_DIRECTORY);
  1522.                 return FALSE;
  1523.                 }

  1524.             //
  1525.             // now make sure the directory exists
  1526.             //

  1527.             CurdirLength = GetFileAttributesW(CurdirBuffer);
  1528.             if ( (CurdirLength == 0xffffffff) ||
  1529.                  !(CurdirLength & FILE_ATTRIBUTE_DIRECTORY) ) {
  1530.                 SetLastError(ERROR_DIRECTORY);
  1531.                 return FALSE;
  1532.                 }
  1533.             }


  1534.         if ( QuoteInsert || QuoteCmdLine) {
  1535.             QuotedBuffer = RtlAllocateHeap(RtlProcessHeap(),0,wcslen(lpCommandLine)*2+6);

  1536.             if ( QuotedBuffer ) {
  1537.                 wcscpy(QuotedBuffer,L""");

  1538.                 if ( QuoteInsert ) {
  1539.                     TempChar = *TempNull;
  1540.                     *TempNull = UNICODE_NULL;
  1541.                     }

  1542.                 wcscat(QuotedBuffer,lpCommandLine);
  1543.                 wcscat(QuotedBuffer,L""");

  1544.                 if ( QuoteInsert ) {
  1545.                     *TempNull = TempChar;
  1546.                     wcscat(QuotedBuffer,TempNull);
  1547.                     }

  1548.                 }
  1549.             else {
  1550.                 if ( QuoteInsert ) {
  1551.                     QuoteInsert = FALSE;
  1552.                     }
  1553.                 if ( QuoteCmdLine ) {
  1554.                     QuoteCmdLine = FALSE;
  1555.                     }
  1556.                 }
  1557.             }


  1558.         // If we found a manifest, we want to push that fact to the new process.
  1559.         if (a->Sxs.Flags & BASE_MSG_SXS_MANIFEST_PRESENT)
  1560.             dwBasePushProcessParametersFlags |= BASE_PUSH_PROCESS_PARAMETERS_FLAG_APP_MANIFEST_PRESENT;

  1561.         if (!BasePushProcessParameters(
  1562.                 dwBasePushProcessParametersFlags,
  1563.                 ProcessHandle,
  1564.                 Peb,
  1565.                 lpApplicationName,
  1566.                 CurdirBuffer,
  1567.                 QuoteInsert || QuoteCmdLine ? QuotedBuffer : lpCommandLine,
  1568.                 lpEnvironment,
  1569.                 &StartupInfo,
  1570.                 dwCreationFlags | dwNoWindow,
  1571.                 bInheritHandles,
  1572.                 IsWowBinary ? IMAGE_SUBSYSTEM_WINDOWS_GUI : 0,
  1573.                 pAppCompatDataTemp,
  1574.                 cbAppCompatData
  1575.                 ) ) {
  1576.             return FALSE;
  1577.             }


  1578.         RtlFreeUnicodeString(&VdmNameString);
  1579.         VdmNameString.Buffer = NULL;

  1580.         //
  1581.         // Stuff in the standard handles if needed
  1582.         //
  1583.         if (!VdmBinaryType &&
  1584.             !bInheritHandles &&
  1585.             !(StartupInfo.dwFlags & STARTF_USESTDHANDLES) &&
  1586.             !(dwCreationFlags & (DETACHED_PROCESS | CREATE_NEW_CONSOLE | CREATE_NO_WINDOW)) &&
  1587.             ImageInformation.SubSystemType == IMAGE_SUBSYSTEM_WINDOWS_CUI
  1588.            ) {
  1589.             PRTL_USER_PROCESS_PARAMETERS ParametersInNewProcess;

  1590.             Status = NtReadVirtualMemory( ProcessHandle,
  1591.                                           &Peb->ProcessParameters,
  1592.                                           &ParametersInNewProcess,
  1593.                                           sizeof( ParametersInNewProcess ),
  1594.                                           NULL
  1595.                                         );
  1596.             if (NT_SUCCESS( Status )) {
  1597.                 if (!CONSOLE_HANDLE( NtCurrentPeb()->ProcessParameters->StandardInput )) {
  1598.                     StuffStdHandle( ProcessHandle,
  1599.                                     NtCurrentPeb()->ProcessParameters->StandardInput,
  1600.                                     &ParametersInNewProcess->StandardInput
  1601.                                   );
  1602.                     }
  1603.                 if (!CONSOLE_HANDLE( NtCurrentPeb()->ProcessParameters->StandardOutput )) {
  1604.                     StuffStdHandle( ProcessHandle,
  1605.                                     NtCurrentPeb()->ProcessParameters->StandardOutput,
  1606.                                     &ParametersInNewProcess->StandardOutput
  1607.                                   );
  1608.                     }
  1609.                 if (!CONSOLE_HANDLE( NtCurrentPeb()->ProcessParameters->StandardError )) {
  1610.                     StuffStdHandle( ProcessHandle,
  1611.                                     NtCurrentPeb()->ProcessParameters->StandardError,
  1612.                                     &ParametersInNewProcess->StandardError
  1613.                                   );
  1614.                     }
  1615.                 }
  1616.             }

  1617.         //
  1618.         // Create the thread...
  1619.         //

  1620.         //
  1621.         // Allocate a stack for this thread in the address space of the target
  1622.         // process.
  1623.         //

  1624.         StackStatus = BaseCreateStack(
  1625.                         ProcessHandle,
  1626.                         ImageInformation.CommittedStackSize,
  1627.                         (ImageInformation.MaximumStackSize < 256*1024) ? 256*1024 : ImageInformation.MaximumStackSize,
  1628.                         &InitialTeb
  1629.                         );

  1630.         if ( !NT_SUCCESS(StackStatus) ) {
  1631.             BaseSetLastNTError(StackStatus);
  1632.             return FALSE;
  1633.             }


  1634.         //
  1635.         // Create an initial context for the new thread.
  1636.         //

  1637.         BaseInitializeContext(
  1638.             &ThreadContext,
  1639.             Peb,
  1640.             ImageInformation.TransferAddress,
  1641.             InitialTeb.StackBase,
  1642.             BaseContextTypeProcess
  1643.             );


  1644.         //
  1645.         // Create the actual thread object
  1646.         //

  1647.         pObja = BaseFormatObjectAttributes(&Obja,lpThreadAttributes,NULL);

  1648.         Status = NtCreateThread(
  1649.                     &ThreadHandle,
  1650.                     THREAD_ALL_ACCESS,
  1651.                     pObja,
  1652.                     ProcessHandle,
  1653.                     &ClientId,
  1654.                     &ThreadContext,
  1655.                     &InitialTeb,
  1656.                     TRUE
  1657.                     );

  1658.         if (!NT_SUCCESS(Status) ) {
  1659.             BaseSetLastNTError(Status);
  1660.             return FALSE;
  1661.             }

  1662.         a->Peb = (ULONGLONG) Peb;

  1663.         //
  1664.         // From here on out, do not modify the address space of the
  1665.         // new process.  WOW64's implementation of NtCreateThread()
  1666.         // reshuffles the new process' address space if the current
  1667.         // process is 32-bit and the new process is 64-bit.
  1668.         //
  1669. #if DBG
  1670.         Peb = NULL;
  1671. #endif

  1672. #if defined(WX86)

  1673.         //
  1674.         // if this is a Wx86 Process, setup for a Wx86 emulated Thread
  1675.         //

  1676.         if (Wx86Info) {

  1677.             //
  1678.             // create a WX86Tib and initialize it's Teb->Vdm.
  1679.             //
  1680.             Status = BaseCreateWx86Tib(ProcessHandle,
  1681.                                        ThreadHandle,
  1682.                                        (ULONG)((ULONG_PTR)ImageInformation.TransferAddress),
  1683.                                        (ULONG)ImageInformation.CommittedStackSize,
  1684.                                        (ULONG)ImageInformation.MaximumStackSize,
  1685.                                        TRUE
  1686.                                        );

  1687.             if (!NT_SUCCESS(Status)) {
  1688.                 BaseSetLastNTError(Status);
  1689.                 return( FALSE );
  1690.                 }


  1691.             //
  1692.             // Mark Process as WX86
  1693.             //
  1694.             Status = NtSetInformationProcess (ProcessHandle,
  1695.                                               ProcessWx86Information,
  1696.                                               &Wx86Info,
  1697.                                               sizeof(Wx86Info)
  1698.                                               );

  1699.             if (!NT_SUCCESS(Status)) {
  1700.                 BaseSetLastNTError(Status);
  1701.                 return( FALSE );
  1702.                 }
  1703.             }
  1704. #endif


  1705.         //
  1706.         // Call the Windows server to let it know about the
  1707.         // process.
  1708.         //

  1709.         a->ProcessHandle = ProcessHandle;
  1710.         a->ThreadHandle = ThreadHandle;
  1711.         a->ClientId = ClientId;

  1712.         switch (ImageInformation.Machine) {
  1713.         case IMAGE_FILE_MACHINE_I386:
  1714. #if defined(_WIN64) || defined(BUILD_WOW6432)
  1715.             a->ProcessorArchitecture = PROCESSOR_ARCHITECTURE_IA32_ON_WIN64;
  1716. #else
  1717.             a->ProcessorArchitecture = PROCESSOR_ARCHITECTURE_INTEL;
  1718. #endif
  1719.             break;

  1720.         case IMAGE_FILE_MACHINE_IA64:
  1721.             a->ProcessorArchitecture = PROCESSOR_ARCHITECTURE_IA64;
  1722.             break;

  1723.         case IMAGE_FILE_MACHINE_AMD64:
  1724.             a->ProcessorArchitecture = PROCESSOR_ARCHITECTURE_AMD64;
  1725.             break;

  1726.         default:
  1727.             DbgPrint("kernel32: No mapping for ImageInformation.Machine == %04x\n", ImageInformation.Machine);
  1728.             a->ProcessorArchitecture = PROCESSOR_ARCHITECTURE_UNKNOWN;
  1729.             break;
  1730.         }

  1731.         //
  1732.         // remove debug flags now its not being done by CSR
  1733.         //
  1734.         a->CreationFlags = dwCreationFlags & ~ (DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS);
  1735.         a->DebuggerClientId.UniqueProcess = NULL;
  1736.         a->DebuggerClientId.UniqueThread = NULL;


  1737.         //
  1738.         // Set the 2 bit if a gui app is starting. The window manager needs to
  1739.         // know this so it can synchronize the startup of this app
  1740.         // (WaitForInputIdle api). This info is passed using the process
  1741.         // handle tag bits.  The 1 bit asks the window manager to turn on
  1742.         // or turn off the application start cursor (hourglass/pointer).
  1743.         //
  1744.         // When starting a WOW process, lie and tell UserSrv NTVDM.EXE is a GUI
  1745.         // process.  We also turn on bit 0x8 so that UserSrv can ignore the
  1746.         // UserNotifyConsoleApplication call made by the console during startup.
  1747.         //

  1748.         if ( ImageInformation.SubSystemType == IMAGE_SUBSYSTEM_WINDOWS_GUI ||
  1749.              IsWowBinary ) {

  1750.             a->ProcessHandle = (HANDLE)((ULONG_PTR)a->ProcessHandle | 2);

  1751.             //
  1752.             // If the creating process is a GUI app, turn on the app. start cursor
  1753.             // by default.  This can be overridden by STARTF_FORCEOFFFEEDBACK.
  1754.             //

  1755.             NtHeaders = RtlImageNtHeader((PVOID)GetModuleHandle(NULL));
  1756.             if ( NtHeaders
  1757.                  && (NtHeaders->OptionalHeader.Subsystem
  1758.                      == IMAGE_SUBSYSTEM_WINDOWS_GUI ) ) {
  1759.                 a->ProcessHandle = (HANDLE)((ULONG_PTR)a->ProcessHandle | 1);
  1760.                 }
  1761.             }


  1762.         //
  1763.         // If feedback is forced on, turn it on. If forced off, turn it off.
  1764.         // Off overrides on.
  1765.         //

  1766.         if (StartupInfo.dwFlags & STARTF_FORCEONFEEDBACK)
  1767.             a->ProcessHandle = (HANDLE)((ULONG_PTR)a->ProcessHandle | 1);
  1768.         if (StartupInfo.dwFlags & STARTF_FORCEOFFFEEDBACK)
  1769.             a->ProcessHandle = (HANDLE)((ULONG_PTR)a->ProcessHandle & ~1);

  1770.         a->VdmBinaryType = VdmBinaryType; // just tell server the truth

  1771.         if (VdmBinaryType){
  1772.            a->hVDM    = iTask ? 0 : NtCurrentPeb()->ProcessParameters->ConsoleHandle;
  1773.            a->VdmTask = iTask;
  1774.         }

  1775. #if defined(BUILD_WOW6432)
  1776.         m.ReturnValue = CsrBasepCreateProcess(a);
  1777. #else
  1778.         m.u.CreateProcess = *a;
  1779.         if (m.u.CreateProcess.Sxs.Flags != 0)
  1780.         {
  1781.             const PUNICODE_STRING StringsToCapture[] =
  1782.             {
  1783.                 &m.u.CreateProcess.Sxs.Manifest.Path,
  1784.                     &m.u.CreateProcess.Sxs.Policy.Path,
  1785.                     &m.u.CreateProcess.Sxs.AssemblyDirectory
  1786.             };

  1787.             Status =
  1788.                 CsrCaptureMessageMultiUnicodeStringsInPlace(
  1789.                 &CaptureBuffer,
  1790.                 RTL_NUMBER_OF(StringsToCapture),
  1791.                 StringsToCapture
  1792.                 );
  1793.             if (!NT_SUCCESS(Status)) {
  1794.                 BaseSetLastNTError(Status);
  1795.                 return FALSE;
  1796.             }
  1797.         }

  1798.         CsrClientCallServer( (PCSR_API_MSG)&m,
  1799.                              CaptureBuffer,
  1800.                              CSR_MAKE_API_NUMBER( BASESRV_SERVERDLL_INDEX,
  1801.                                                   BasepCreateProcess
  1802.                                                 ),
  1803.                              sizeof( *a )
  1804.                            );

  1805.         if ( CaptureBuffer ) {
  1806.             CsrFreeCaptureBuffer( CaptureBuffer );
  1807.             CaptureBuffer = NULL;
  1808.         }

  1809. #endif

  1810.         if (!NT_SUCCESS((NTSTATUS)m.ReturnValue)) {
  1811.             BaseSetLastNTError((NTSTATUS)m.ReturnValue);
  1812.             NtTerminateProcess(ProcessHandle, (NTSTATUS)m.ReturnValue);
  1813.             return FALSE;
  1814.             }



  1815.         //
  1816.         // If the WinSafer sandboxing policy indicates that the
  1817.         // process needs to be run with a restricted token or placed
  1818.         // into a restricted job object, then do those actions now.
  1819.         // Do not replace the token if the restricted token was created
  1820.         // from a caller supplied token i.e. the CreateProcessAsUser case.
  1821.         //
  1822.         if ((hSaferRestrictedToken != NULL) && (hUserToken == NULL)) {
  1823.             Status = BasepReplaceProcessThreadTokens(
  1824.                     hSaferRestrictedToken,
  1825.                     ProcessHandle,
  1826.                     ThreadHandle);
  1827.             if (!NT_SUCCESS(Status)) {
  1828.                 // kill and cleanup.
  1829.                 NtTerminateProcess(ProcessHandle, Status);
  1830.                 BaseSetLastNTError(Status);
  1831.                 return FALSE;
  1832.             }
  1833.         }
  1834.         if (hSaferAssignmentJob != NULL) {
  1835.             Status = NtAssignProcessToJobObject(
  1836.                     hSaferAssignmentJob, ProcessHandle);
  1837.             if (!NT_SUCCESS(Status)) {
  1838.                 // kill and cleanup.
  1839.                 NtTerminateProcess(ProcessHandle, STATUS_ACCESS_DENIED);
  1840.                 BaseSetLastNTError(Status);
  1841.                 return FALSE;
  1842.             }
  1843.         }


  1844.         //
  1845.         // Make the thread start execution if we are allowed to.
  1846.         //
  1847.         if (!( dwCreationFlags & CREATE_SUSPENDED) ) {
  1848.             NtResumeThread(ThreadHandle,&i);
  1849.             }

  1850. VdmExists:
  1851.         bStatus = TRUE;
  1852.         if (VDMCreationState)
  1853.             VDMCreationState |= VDM_CREATION_SUCCESSFUL;

  1854.         try {
  1855.             if (VdmWaitHandle) {

  1856.                 //
  1857.                 // tag Shared WOW VDM handles so that wait for input idle has a
  1858.                 // chance to work.  Shared WOW VDM "process" handles are actually
  1859.                 // event handles,  Separate WOW VDM handles are real process
  1860.                 // handles. Also mark DOS handles with 0x1 so WaitForInputIdle
  1861.                 // has a way to distinguish DOS apps and not block forever.
  1862.                 //

  1863.                 if (VdmBinaryType == BINARY_TYPE_WIN16)  {
  1864.                     lpProcessInformation->hProcess =
  1865.                             (HANDLE)((ULONG_PTR)VdmWaitHandle | 0x2);

  1866.                     //
  1867.                     // Shared WOW doesn't always start a process, so
  1868.                     // we don't have a process ID or thread ID to
  1869.                     // return if the VDM already existed.
  1870.                     //
  1871.                     // Separate WOW doesn't hit this codepath
  1872.                     // (no VdmWaitHandle).
  1873.                     //

  1874.                     if (VDMCreationState & VDM_BEING_REUSED) {
  1875.                         ClientId.UniqueProcess = 0;
  1876.                         ClientId.UniqueThread = 0;
  1877.                         }

  1878.                     }
  1879.                 else  {
  1880.                     lpProcessInformation->hProcess =
  1881.                             (HANDLE)((ULONG_PTR)VdmWaitHandle | 0x1);
  1882.                     }


  1883.                 //
  1884.                 // Close the ProcessHandle, since we are returning the
  1885.                 // VdmProcessHandle instead.
  1886.                 //

  1887.                 if (ProcessHandle != NULL)
  1888.                     NtClose(ProcessHandle);
  1889.                 }
  1890.             else{
  1891.                 lpProcessInformation->hProcess = ProcessHandle;
  1892.                 }

  1893.             lpProcessInformation->hThread = ThreadHandle;
  1894.             lpProcessInformation->dwProcessId = HandleToUlong(ClientId.UniqueProcess);
  1895.             lpProcessInformation->dwThreadId = HandleToUlong(ClientId.UniqueThread);
  1896.             ProcessHandle = NULL;
  1897.             ThreadHandle = NULL;
  1898.             }
  1899.         __except ( EXCEPTION_EXECUTE_HANDLER ) {
  1900.             NtClose( ProcessHandle );
  1901.             NtClose( ThreadHandle );
  1902.             ProcessHandle = NULL;
  1903.             ThreadHandle = NULL;
  1904.             if (VDMCreationState)
  1905.                 VDMCreationState &= ~VDM_CREATION_SUCCESSFUL;
  1906.             }
  1907.         }
  1908.     __finally {

  1909.         if (ExePathFullBuffer != NULL) {
  1910.             SxsWin32ExePath.Buffer = NULL;
  1911.             SxsWin32ExePath.Length = 0;
  1912.             SxsWin32ExePath.MaximumLength = 0;
  1913.             RtlFreeHeap(RtlProcessHeap(), 0, ExePathFullBuffer);
  1914.             ExePathFullBuffer = NULL;
  1915.         }

  1916.         if (!VdmBinaryType) {
  1917.             NTSTATUS Status1;

  1918.             BasepSxsCloseHandles(&SxsManifestFileHandles);
  1919.             BasepSxsCloseHandles(&SxsPolicyHandles);
  1920.             //
  1921.             // don't close SxsExeHandles, they are
  1922.             // aliases of other variables that are either closed
  1923.             // or returned to the caller
  1924.             //

  1925.             //
  1926.             // This loop only really frees any memory if our computation
  1927.             // of the overall buffer size was too low, which it is not supposed to be.
  1928.             //
  1929.             if (SxsConglomeratedByteBuffer != NULL) {
  1930.                 for (sxsi= 0 ; sxsi != RTL_NUMBER_OF(SxsStringBuffers) ; ++sxsi) {
  1931.                     RtlFreeUnicodeStringBuffer(SxsStringBuffers[sxsi]);
  1932.                     }
  1933.                 RtlFreeHeap(RtlProcessHeap(), 0,SxsConglomeratedByteBuffer);
  1934.                 }
  1935.             }
  1936.         if (lpEnvironment && !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT) ) {
  1937.             RtlDestroyEnvironment(lpEnvironment);
  1938.             lpEnvironment = NULL;
  1939.             }
  1940.         RtlFreeHeap(RtlProcessHeap(), 0,QuotedBuffer);
  1941.         RtlFreeHeap(RtlProcessHeap(), 0,NameBuffer);
  1942.         RtlFreeHeap(RtlProcessHeap(), 0,CurdirBuffer);
  1943.         RtlFreeHeap(RtlProcessHeap(), 0,FreeBuffer);
  1944.         if ( FileHandle ) {
  1945.             NtClose(FileHandle);
  1946.             }
  1947.         if ( SectionHandle ) {
  1948.             NtClose(SectionHandle);
  1949.             }
  1950.         if ( ThreadHandle ) {
  1951.             NtTerminateProcess(ProcessHandle,STATUS_SUCCESS);
  1952.             NtClose(ThreadHandle);
  1953.             }
  1954.         if ( ProcessHandle ) {
  1955.             NtClose(ProcessHandle);
  1956.             }
  1957.         if ( hSaferAssignmentJob ) {
  1958.             NtClose(hSaferAssignmentJob);
  1959.             }
  1960.         if ( hSaferRestrictedToken ) {
  1961.             if (hUserToken == NULL) {
  1962.                 // CreateProcess case
  1963.                 NtClose(hSaferRestrictedToken);
  1964.                 }
  1965.             else{
  1966.                 // CreateProcessAsUser case
  1967.                 *hRestrictedUserToken = hSaferRestrictedToken;
  1968.                 }
  1969.             }

  1970.         if (NULL != pAppCompatData) {
  1971.             RtlFreeHeap(RtlProcessHeap(), 0, pAppCompatData);
  1972.             }

  1973.         if (NULL != pAppCompatSxsData) {
  1974.             RtlFreeHeap(RtlProcessHeap(), 0, pAppCompatSxsData);
  1975.             }

  1976.         RtlFreeUnicodeString(&VdmNameString);
  1977.         RtlFreeUnicodeString(&SubSysCommandLine);
  1978.         if (AnsiStringVDMEnv.Buffer || UnicodeStringVDMEnv.Buffer)
  1979.             BaseDestroyVDMEnvironment(&AnsiStringVDMEnv, &UnicodeStringVDMEnv);

  1980.         if (VDMCreationState && !(VDMCreationState & VDM_CREATION_SUCCESSFUL)){
  1981.             BaseUpdateVDMEntry (
  1982.                 UPDATE_VDM_UNDO_CREATION,
  1983.                 (HANDLE *)&iTask,
  1984.                 VDMCreationState,
  1985.                 VdmBinaryType
  1986.                 );
  1987.             if(VdmWaitHandle) {
  1988.                 NtClose(VdmWaitHandle);
  1989.                 }
  1990.             }
  1991.         }

  1992.     if (lpEnvironment && !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT) ) {
  1993.         RtlDestroyEnvironment(lpEnvironment);
  1994.         }
  1995.     return bStatus;
  1996. }

  1997. BOOL
  1998. WINAPI
  1999. CreateProcessW(
  2000.     LPCWSTR lpApplicationName,
  2001.     LPWSTR lpCommandLine,
  2002.     LPSECURITY_ATTRIBUTES lpProcessAttributes,
  2003.     LPSECURITY_ATTRIBUTES lpThreadAttributes,
  2004.     BOOL bInheritHandles,
  2005.     DWORD dwCreationFlags,
  2006.     LPVOID lpEnvironment,
  2007.     LPCWSTR lpCurrentDirectory,
  2008.     LPSTARTUPINFOW lpStartupInfo,
  2009.     LPPROCESS_INFORMATION lpProcessInformation
  2010.     )

  2011. /*++

  2012. Routine Description:

  2013.     A process and thread object are created and a handle opened to each
  2014.     object using CreateProcess.  Note that WinExec and LoadModule are
  2015.     still supported, but are implemented as a call to CreateProcess.

  2016. Arguments:

  2017.     lpApplicationName - Supplies an optional pointer to a null terminated
  2018.         character string that contains the name of the image file to
  2019.         execute.  This is a fully qualified DOS path name.  If not
  2020.         specified, then the image file name is the first whitespace
  2021.         delimited token on the command line.

  2022.     lpCommandLine - Supplies a null terminated character string that
  2023.         contains the command line for the application to be executed.
  2024.         The entire command line is made available to the new process
  2025.         using GetCommandLine.  If the lpApplicationName parameter was
  2026.         not specified, then the first token of the command line
  2027.         specifies file name of the application (note that this token
  2028.         begins at the beginning of the command line and ends at the
  2029.         first "white space" character).  If the file name does not
  2030.         contain an extension (the presence of a "."), then .EXE is
  2031.         assumed.  If the file name does not contain a directory path,
  2032.         Windows will search for the executable file in:

  2033.           - The current directory

  2034.           - The windows directory

  2035.           - The windows system directory

  2036.           - The directories listed in the path environment variable

  2037.         This parameter is optional onlu if the lpApplicationName
  2038.         parameter is specified.  In this case the command line the
  2039.         application receives will be the application name.

  2040.     lpProcessAttributes - An optional parameter that may be used to
  2041.         specify the attributes of the new process.  If the parameter is
  2042.         not specified, then the process is created without a security
  2043.         descriptor, and the resulting handle is not inherited on process
  2044.         creation:

  2045.         SECURITY_ATTRIBUTES Structure:

  2046.         DWORD nLength - Specifies the length of this structure.  Must be
  2047.             set to sizeof( SECURITY_ATTRUBUTES ).

  2048.         LPVOID lpSecurityDescriptor - Points to a security descriptor for
  2049.             the object (must be NULL for Win32, used on NT/Win32). The
  2050.             security descriptor controls the sharing of an object.

  2051.         BOOL bInheritHandle - Supplies a flag that indicates whether
  2052.             or not the returned handle is to be inherited by a new
  2053.             process during process creation.  A value of TRUE
  2054.             indicates that the new process will inherit the handle.

  2055.     lpThreadAttributes - An optional parameter that may be used to specify
  2056.         the attributes of the new thread.  If the parameter is not
  2057.         specified, then the thread is created without a security
  2058.         descriptor, and the resulting handle is not inherited on
  2059.         process creation.

  2060.     dwCreationFlags - Supplies additional flags that control the creation
  2061.         of the process.

  2062.         dwCreationFlags Flags:

  2063.         DEBUG_PROCESS - If this flag bit is set, then the creating
  2064.             process is treated as a debugger, and the process being
  2065.             created is created as a debugee.  All debug events occuring
  2066.             in the debugee are reported to the debugger.  If this bit is
  2067.             clear, but the calling process is a debugee, then the
  2068.             process becomes a debugee of the calling processes debugger.
  2069.             If this bit is clear and the calling processes is not a
  2070.             debugee then no debug related actions occur.

  2071.         DEBUG_ONLY_THIS_PROCESS - If this flag is set, then the
  2072.             DEBUG_PROCESS flag bit must also be set.  The calling
  2073.             process is is treated as a debugger, and the new process is
  2074.             created as its debuggee.  If the new process creates
  2075.             additional processes, no debug related activities (with
  2076.             respect to the debugger) occur.

  2077.         CREATE_SUSPENDED - The process is created, but the initial thread
  2078.             of the process remains suspended. The creator can resume this
  2079.             thread using ResumeThread. Until this is done, code in the
  2080.             process will not execute.

  2081.         CREATE_UNICODE_ENVIRONMENT - If set, the environment pointer
  2082.             points to a Unicode environment block.  Otherwise, the
  2083.             block is ANSI (actually OEM.)

  2084.     bInheritHandles - Supplies a flag that specifies whether or not the
  2085.         new process is to inherit handles to objects visible to the
  2086.         calling process.  A value of TRUE causes handles to be inherited
  2087.         by the new process.  If TRUE was specified, then for each handle
  2088.         visible to the calling process, if the handle was created with
  2089.         the inherit handle option, the handle is inherited to the new
  2090.         process.  The handle has the same granted access in the new
  2091.         process as it has in the calling process, and the value of the
  2092.         handle is the same.

  2093.     lpEnvironment - An optional parameter, that if specified, supplies a
  2094.         pointer to an environment block.  If the parameter is not
  2095.         specified, the environment block of the current process is used.
  2096.         This environment block is made available to the new process
  2097.         using GetEnvironmentStrings.

  2098.     lpCurrentDirectory - An optional parameter, that if specified,
  2099.         supplies a string representing the current drive and directory
  2100.         for the new process.  The string must be a fully qualified
  2101.         pathname that includes a drive letter.  If the parameter is not
  2102.         specified, then the new process is created with the same current
  2103.         drive and directory as the calling process.  This option is
  2104.         provided primarily for shells that want to start an application
  2105.         and specify its initial drive and working directory.

  2106.     lpStartupInfo - Supplies information that specified how the
  2107.         applications window is to be shown. This structure is described
  2108.         in the Win32 User Interface API Book.

  2109.     lpProcessInformation - Returns identification information about the
  2110.         new process.

  2111.     PROCESS_INFORMATION Structure:

  2112.         HANDLE hProcess - Returns a handle to the newly created process.
  2113.             Through the handle, all operations on process objects are
  2114.             allowed.

  2115.         HANDLE hThread - Returns a handle to the newly created thread.
  2116.             Through the handle, all operations on thread objects are
  2117.             allowed.

  2118.         DWORD dwProcessId - Returns a global process id that may be used
  2119.             to identify a process.  The value is valid from the time the
  2120.             process is created until the time the process is terminated.

  2121.         DWORD dwThreadId - Returns a global thread id that may be used
  2122.             to identify a thread.  The value is valid from the time the
  2123.             thread is created until the time the thread is terminated.

  2124. Return Value:

  2125.     TRUE - The operation was successful

  2126.     FALSE/NULL - The operation failed. Extended error status is available
  2127.         using GetLastError.

  2128. --*/

  2129. {
  2130.     return CreateProcessInternalW(
  2131.                NULL, // Create new process with the token on the creator process
  2132.                lpApplicationName,
  2133.                lpCommandLine,
  2134.                lpProcessAttributes,
  2135.                lpThreadAttributes,
  2136.                bInheritHandles,
  2137.                dwCreationFlags,
  2138.                lpEnvironment,
  2139.                lpCurrentDirectory,
  2140.                lpStartupInfo,
  2141.                lpProcessInformation,
  2142.                NULL  // Do not return the restricted token
  2143.                );
  2144. }
复制代码
回复 赞! 靠!

使用道具 举报

本版积分规则

QQ|Archiver|小黑屋|技术宅的结界 ( 滇ICP备16008837号 )|网站地图

GMT+8, 2025-1-7 08:02 , Processed in 0.048414 second(s), 30 queries , Gzip On.

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表