如何传递(多个)参数给CreateProcess WinAPI

huangapple go评论61阅读模式
英文:

How to pass (multiple) arguments to CreateProcess winapi

问题

在使用 CreateProcess() 创建子进程并向其传递参数时,您可以通过命令行参数传递指针和值,而不是将它们作为字符串传递。下面是您提供的示例的翻译:

Caller(调用者):

struct Arguments {
    void* ptr;
    int value;
} Arg;

Arg = { some_pointer, 1337 };

// 我知道 "(LPWSTR)&Arg" 不起作用,但这实际上是我想要的。
CreateProcessW(L"…\\Injet x86.exe",
        (LPWSTR)&Arg, 0, 0, 0, 0, 0, 0, &si, &pi)

然后,在子进程中获取参数:

Child(子进程):

struct Arguments {
    void* ptr;
    int value;
};

// 是的,我试图避免使用默认库
void WINAPI WinMainCRTStartup()
{
    Sleep(10000);    // 用于调试
    Arguments* ptrArgs = (Arguments*)GetCommandLineW();
  
    // 进行操作
}

您可以通过将 Arguments 结构体指针传递给子进程的 GetCommandLineW() 函数来获取参数。请注意,这种方法有一些限制和风险,因为您需要确保子进程能够正确解释和使用这些参数。这不是一种通用的方法,但如果您有充分的控制权和了解子进程的实现,这样的做法可能是可行的。

英文:

I want to create a child process with CreateProcess() and pass some arguments to it, but I dont want to pass a string as argument, I want to pass a pointer to a memory, a value or more.
Here is my example,
Caller:

struct Arguments {
    void* ptr;
    int value;
} Arg;

Arg = { some_pointer, 1337 };

// I know that "(LPWSTR)&Arg" doesnt work, but thats effectively what I want.
CreateProcessW(L"...\\Injet x86.exe",
        (LPWSTR)&Arg, 0, 0, 0, 0, 0, 0, &si, &pi)

And than I try to get the result in the child process:

Child:

struct Arguments {
    void* ptr;
    int value;
};

// Yes, Im trying to not use default libs
void WINAPI WinMainCRTStartup()
{
    Sleep(10000);    // For debug
    Arguments* ptrArgs = (Arguments*)GetCommandLineW();
  
    // Do stuf
}

I know that this doesnt work, but what I want is to pass arguments to the child process, not a string, but variables/an adress so it can modify. All the examples I found used a string as argument, but I dont want to do that, how can I accomplish it?

答案1

得分: 1

以下是翻译好的部分:

  1. 已记录的方法是将所需的数据作为单个字符串参数传递。这是你所期望的操作方式。

  2. 示例

    struct Arguments {
        void* ptr;
        int value;
    } Arg;
    
    Arg = { some_pointer, 1337 };
    
    WCHAR szCmdLine[32];
    swprintf(szCmdLine, L"%p %d", Arg.ptr, Arg.value);
    
    CreateProcessW(L"<path to>\\Injet x86.exe", szCmdLine, ...);
    
    struct Arguments {
        void* ptr;
        int value;
    };
    
    void WINAPI WinMainCRTStartup()
    {
        Sleep(10000);    // 用于调试
    
        Arguments Arg;
        swscanf(GetCommandLineW(), L"%p %d", &Arg.ptr, &Arg.value);
    
        // 执行操作
    }
    
  3. 然而,还有一种未记录的方法(即,风险自负!)可以让CreateProcess()将任意字节传递给新的进程。这种方法在此文章中描述:1

    未记录的CreateProcess

    将任意数据传递给子进程!

    最后一个未记录的技巧与之前的技巧相当不同,因此我认为应该保存到最后。STARTUPINFO结构包含两个成员,lpReserved2和cbReserved2:

    WORD cbReserved2;
    LPBYTE lpReserved2;
    

    这两个成员提供了一种机制,可以在一个进程和另一个进程之间传递任意数量的数据,而无需调用VirtualAllocEx / WriteProcessMemory。cbReserved2成员是一个16位整数,指定了lpReserved2指向的缓冲区的大小。这意味着lpReserved2可以达到65535字节。

  4. 示例

    struct Arguments {
        void* ptr;
        int value;
    } Arg;
    
    Arg = { some_pointer, 1337 };
    
    BYTE buf[sizeof(DWORD) + sizeof(Arg)];
    *(DWORD*)buf = 0;
    memcpy(buf + sizeof(DWORD), &Arg, sizeof(Arg));
    
    STARTUPINFO si = { sizeof(si) };
    si.lpReserved2 = buf;
    si.cbReserved2 = sizeof(buf);
    
    CreateProcessW(L"<path to>\\Injet x86.exe", ... &si, ...);
    
    struct Arguments {
        void* ptr;
        int value;
    };
    
    void WINAPI WinMainCRTStartup()
    {
        Sleep(10000);    // 用于调试
    
        STARTUPINFO si = { sizeof(si) };
        GetStartupInfo(&si);
    
        Arguments* ptrArgs = (Arguments*) (si.lpReserved2 + sizeof(DWORD));
    
        // 执行操作
    }
    
  5. 1 请注意,在Vista WOW64下存在一个注意事项,会影响通过lpReserved2传递的缓冲区的格式,如下面的讨论所述:64位Vista下WOW64中的GetStartupInfo问题

  6. 更安全的方法是由父进程创建一个命名管道、监听套接字或命名文件映射中的共享内存块,然后使用命令行字符串将该资源的标识符传递给子进程,子进程可以连接并从该资源中读取所需的数据。

  7. 无论采取哪种方法,要知道传递给子进程的任何内存指针在子进程的上下文中将无效,只能在父进程的上下文中使用。在子进程中,这种指针的唯一有效用途将是在调用ReadProcessMemory()以从父进程的内存中读取数据时,例如:

    struct Arguments {
        void* ptr;
        int value;
    };
    
    void WINAPI WinMainCRTStartup()
    {
        Sleep(10000);    // 用于调试
    
        Arguments Arg;
        // 根据需要从父进程中填充...
    
        DWORD dwProcessId = ...; // 从父进程中获取
        HANDLE hParent = OpenProcess(PROCESS_VM_READ, FALSE, dwProcessId);
        if (hParent)
        {
            ReadProcessMemory(hParent, Arg.ptr, ...);
            CloseHandle(hParent);
    
            // 执行操作
        }
    }
    
英文:

The documented approach is to pass your desired data as a single string argument. That is what you are expected to do.

For example:

struct Arguments {
    void* ptr;
    int value;
} Arg;

Arg = { some_pointer, 1337 };

WCHAR szCmdLine[32];
swprintf(szCmdLine, L&quot;%p %d&quot;, Arg.ptr, Arg.value);

CreateProcessW(L&quot;&lt;path to&gt;\\Injet x86.exe&quot;, szCmdLine, ...);
struct Arguments {
    void* ptr;
    int value;
};

void WINAPI WinMainCRTStartup()
{
    Sleep(10000);    // For debug

    Arguments Arg;
    swscanf(GetCommandLineW(), L&quot;%p %d&quot;, &amp;Arg.ptr, &amp;Arg.value);
  
    // Do stuff
}

However, there is also an undocumented approach (ie, use at your own risk!) to have CreateProcess() pass arbitrary bytes to the new process. This approach is described in this article <sup>1</sup>:

Undocumented CreateProcess

> Pass arbitrary data to a child process!
>
> The last undocumented trick is quite different to the previous ones so I thought I’d save it until last. The STARTUPINFO structure contains two members, lpReserved2 and cbReserved2 :
>
>
&gt; WORD cbReserved2;
&gt; LPBYTE lpReserved2;
&gt;

>
> These two members provide a mechanism for passing arbitrary amounts of data from one process to another, without having to call VirtualAllocEx / WriteProcessMemory. The cbReserved2 member is a 16bit integer and specifies the size of the buffer pointed to by lpReserved2. This means that lpReserved2 can be as big as 65535 bytes.
>
> ...
>
> Should you wish to use lpReserved2 in your own programs (using CreateProcess instead of spawn / exec) you will need to be careful because the lpReserved2 buffer must be properly constructed. Failure to do so will result in the child processes crashing, or at the very least becoming unstable - the reason being that the child process is expecting to find lpReserved2 in a particular format.

For example:

struct Arguments {
    void* ptr;
    int value;
} Arg;

Arg = { some_pointer, 1337 };

BYTE buf[sizeof(DWORD) + sizeof(Arg)];
*(DWORD*)buf = 0;
memcpy(buf + sizeof(DWORD), &amp;Arg, sizeof(Arg));

STARTUPINFO si = { sizeof(si) };
si.lpReserved2 = buf;
si.cbReserved2 = sizeof(buf);

CreateProcessW(L&quot;&lt;path to&gt;\\Injet x86.exe&quot;, ... &amp;si, ...);
struct Arguments {
    void* ptr;
    int value;
};

void WINAPI WinMainCRTStartup()
{
    Sleep(10000);    // For debug

    STARTUPINFO si = { sizeof(si) };
    GetStartupInfo(&amp;si);

    Arguments* ptrArgs = (Arguments*) (si.lpReserved2 + sizeof(DWORD));

    // Do stuff
}

<sup>1</sup> Note, there is a caveat under Vista WOW64, which affects the format of the buffer passed via lpReserved2, as described in this discussion: GetStartupInfo problem under WOW64 on 64 bit Vista

A safer approach would be to have the parent process create a named pipe, or a listening socket, or a block of shared memory in a named file mapping, and then use the command-line string to pass that resource's identifier to the child process, which can then connect and read data from that resource as needed.

That being said, whatever approach you decide to take, know that any memory pointer you pass to the child process will not be valid in the context of the child process, only in the context of the parent process. The only valid use for such a pointer in the child process would be in a call to ReadProcessMemory() to read data from the parent process' memory, eg:

struct Arguments {
    void* ptr;
    int value;
};

void WINAPI WinMainCRTStartup()
{
    Sleep(10000);    // For debug

    Arguments Arg;
    // populate from parent as needed...

    DWORD dwProcessId = ...; // obtain from parent as needed
    HANDLE hParent = OpenProcess(PROCESS_VM_READ, FALSE, dwProcessId);
    if (hParent)
    {
        ReadProcessMemory(hParent, Arg.ptr, ...);
        CloseHandle(hParent);

        // Do stuff
    }
}

答案2

得分: 0

根据文档:CreateProcessW 函数(processthreadsapi.h)

[in, out, optional] lpCommandLine

要执行的命令行。

字符串的最大长度为 32,767 个字符,包括 Unicode 终止空字符。

此参数只接受一个字符串,不能传递指向内存、值或更多的指针。

英文:

According to the Doc: CreateProcessW function (processthreadsapi.h)

> [in, out, optional] lpCommandLine
>
> The command line to be executed.
>
> The maximum length of this string is 32,767 characters, including the
> Unicode terminating null character.

This parameter only accepts a string, you couldn't pass a pointer to a memory, a value or more.

huangapple
  • 本文由 发表于 2023年2月24日 12:37:16
  • 转载请务必保留本文链接:https://go.coder-hub.com/75552683.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定