为什么 CreateProcess API 调用会导致内存访问冲突?

为什么 CreateProcess API 调用会导致内存访问冲突?

问题描述:

我有一个函数应该启动另一个进程:

I have a function that is supposed to launch another process:

DWORD WINAPI StartCalc(LPVOID lpParam) {
    STARTUPINFOW  info;
    PROCESS_INFORMATION   processInfo;
    std::wstring cmd = L"C:\\Windows\\System32\\calc.exe";
    BOOL hR = CreateProcessW(NULL, (LPWSTR)cmd.c_str(), NULL, NULL, TRUE, 0, NULL, NULL,
                             &info, &processInfo);
    if (hR == 0) {
       DWORD errorMessageID = ::GetLastError();
        printf("Error creating process\n");
        return 1;
    }
    return 0;
}

我在 ntdll.dll 中遇到异常访问冲突读取位置 0xFFFFFFFFFFFFFFFF".我知道有一些常见的错误可能会导致这种情况:

I get an exception in ntdll.dll "Access violation reading location 0xFFFFFFFFFFFFFFFF". I know there are a few common mistakes that might cause this:

  • 不一致的调用约定.我正在使用 __stdcall
  • 编码问题.我将字符串存储为宽字符
  • 这个问题在 x64 和 x86 版本中都会发生
  • 当我尝试创建其他 Windows 进程时出现问题

我做错了什么?

这实际上不是将 cmd.c_str() 转换为 (LPWSTR) 的问题,这部分似乎没问题.我需要初始化 STARTUPINFO 结构:STARTUPINFO info = { 0 };

This actually isn't a problem with casting cmd.c_str() as a (LPWSTR), that part appears to be fine. I needed to initialize the STARTUPINFO struct: STARTUPINFO info = { 0 };

BOOL hR = CreateProcessW(NULL, (LPWSTR)cmd.c_str(), NULL, NULL, TRUE, 0, NULL, NULL, &info, &processInfo);

                                  ^^^^^

这是演员表.一个很好的经验法则是发现演员,你发现错误".

That's a cast. A great rule of thumb is "spot the cast, you spot the error".

这里是真的.CreateProcessW 必须传递一个可写的字符串.这意味着,没有文字,也没有 c_str() 结果.

It's true here. CreateProcessW must be passed a writable string. That means, no literal, and also no c_str() result.

来自文档:

这个函数的 Unicode 版本,CreateProcessW,可以修改这个字符串的内容.因此,此参数不能是指向只读内存的指针(例如 const 变量或文字字符串).如果此参数是一个常量字符串,该函数可能会导致访问冲突.

The Unicode version of this function, CreateProcessW, can modify the contents of this string. Therefore, this parameter cannot be a pointer to read-only memory (such as a const variable or a literal string). If this parameter is a constant string, the function may cause an access violation.

传递一个真正的非const指针,不要玩hide-the-const.&cmd[0] 应该可以工作,这保证是一个可写的字符串.为了超级安全,将 wstring 容量增加到超出您需要的容量,因为 CreateProcessW 会将其用作工作缓冲区.

Pass a real non-const pointer, don't play hide-the-const. &cmd[0] should work, that's guaranteed to be a writable string. To be super-safe, increase your wstring capacity beyond just what you needed, because CreateProcessW is going to use it as a working buffer.