繁体   English   中英

手动映射 PE + 获取输出时出现 AllocConsole 问题

[英]AllocConsole issue when manually mapping PE + fetching output

我正在从事一个涉及在项目过程中手动映射和执行 PE 的项目。 我使用其代码作为基础的主要项目可以在这里找到: https ://github.com/aaaddress1/RunPE-In-Memory/blob/master/RunPE-In-Memory/RunPEinMemory/RunPEinMemory.cpp

上面的项目编译并运行良好。 在该项目用于运行的 PE 的映射过程中,它将挂钩与处理命令行参数相关的 API,允许用户手动指定参数给映射的 PE,而不是映射的 PE 尝试使用提供给 RunPEinMemory 的参数。 exe(因为这一切都发生在同一个进程中)。

我正在努力的项目与这个基础项目的不同之处在于:

  1. 我(已经成功地)连接了 ExitProcess 和 exit() 之类的 API,并将它们重定向到 ExitThread 以防止映射的 PE 结束 RunPEinMemory.exe 进程

  2. 我需要最终从别处的映射 PE 发送输出。 为此,我将 stdout/stderr 重定向到我稍后在运行映射的 PE 之前从 RunPEinMemory 中读取的匿名管道。

我在第二个帐户上苦苦挣扎。

我的项目需要编译为 GUI 应用程序 (subsystem=windows) 而不是控制台应用程序 (subsystem=console)。

出现问题的原因是为了操纵标准输入/标准输出/标准错误句柄,需要将控制台附加到进程。 有许多关于此主题的 StackOverflow 帖子,其中介绍了使用 AllocConsole 然后重新打开 std 句柄以将输出重定向到新控制台(或其他地方)。 可以在这里看到关于此事的重要帖子: Redirecting stdout in win32 does not redirect stdout

我已经实现了这段代码,并且可以成功地手动映射/运行 powershell.exe(例如,将“gci”作为参数传递)。 stdout/stderr 被重定向到匿名管道,然后被读取,之后被写入文件(目前用于测试目的)。 这个成功的测试是用我的 project.exe 按预期为 subsystem=windows 编译的。

当我尝试用 cmd.exe 做同样的事情(传递 '/c dir' 作为参数)时,这就分崩离析了。 在这种情况下,输出无法到达 stdout 或 stderr,并且没有可从匿名管道读取的字节。

现在,当我改为将此项目编译为控制台程序 (subsystem=console) 并删除 AllocConsole 调用(因此使用 Windows 为我分配的控制台)时,程序成功。 我注意到 cmd.exe 输出到 stderr,而不是 stdout。 但无论如何,我能够成功将该输出重定向到其中一个匿名管道,读取它,然后将它写到文件中。

这让我相信我在调用 AllocConsole 时遗漏了一些东西,一些后续步骤来完全设置环境,以便某些程序可以成功地将输出发送到 stdout/stderr。 This post mentions about cmd.exe using win32Api's to write to stdout/stderr(与前面链接中提到的其他方法相反),所以我想知道我是否没有成功设置什么时候手动分配控制台: https ://stackoverflow.com/a/66689266/18776214

相关代码如下:

    //Allocate console. This line is the one that gets commented out between console/windows test
    BOOL suc = AllocConsole();

    //Reopen streams after allocating console + disable buffering
    FILE* fout;
    FILE* ferr;
    freopen_s(&fout, "CONOUT$", "r+", stdout);
    freopen_s(&ferr, "CONOUT$", "r+", stderr);
    setvbuf(stdout, NULL, _IONBF, 0);
    setvbuf(stderr, NULL, _IONBF, 0);

    //Open anonymous pipes for stdout and stderr
    HANDLE hreadout;
    HANDLE hwriteout;
    HANDLE hreaderr;
    HANDLE hwriteerr;

    SECURITY_ATTRIBUTES sao = { sizeof(sao),NULL,TRUE };
    SECURITY_ATTRIBUTES sae = { sizeof(sae),NULL,TRUE };
    CreatePipe(&hreadout, &hwriteout, &sao, 0);
    CreatePipe(&hreaderr, &hwriteerr, &sae, 0);

    printf("CreatePipe last error: %d\n", GetLastError());
    printf("hreadout is: %p\n", hreadout);
    printf("hwriteout is: %p\n", hwriteout);
    printf("hreaderr is: %p\n", hreaderr);
    printf("hwriterr is: %p\n", hwriteerr);

    //Set std_output_handle and std_error_handle to the write-ends of anonymous pipes
    SetStdHandle(STD_OUTPUT_HANDLE, hwriteout);
    SetStdHandle(STD_ERROR_HANDLE, hwriteerr);

    //Convert write-ends of anonymous pipes to file descriptors and use _dup2 to set stdout/stderr to anonymous pipes
    int fo = _open_osfhandle((intptr_t)(hwriteout), _O_TEXT);
    int fe = _open_osfhandle((intptr_t)(hwriteerr), _O_TEXT);
    int res = _dup2(fo, _fileno(fout)); //_fileno(fout)
    int res2 = _dup2(fe, _fileno(ferr)); //_fileno(ferr)

    printf("fo is: %d\n", fo);
    printf("fe is: %d\n", fe);
    printf("res is: %d\n", res);
    printf("res1 is: %d\n", res2);

    //Execute manually mapped PE now that stdout/stderr have been redirected
    Sleep(2000);
    HANDLE hThread = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)retAddr, 0, 0, 0);
    WaitForSingleObject(hThread, 5000);
    Sleep(2000);
    
    //Reopen streams again to set stdout/stderr back to console
    freopen_s(&fout, "CONOUT$", "r+", stdout);
    freopen_s(&ferr, "CONOUT$", "r+", stderr);
    
    //check how much data there is to be read from pipe + allocate buffer
    DWORD cbBytesAvailOut;
    PeekNamedPipe(hreadout, NULL, NULL, NULL, &cbBytesAvailOut, NULL);
    printf("PeekNamedPipe last error: %d\n", GetLastError());
    printf("stdout bytes avail is: %d\n", cbBytesAvailOut);

    DWORD cbBytesAvailErr;
    PeekNamedPipe(hreaderr, NULL, NULL, NULL, &cbBytesAvailErr, NULL);
    printf("PeekNamedPipe last error: %d\n", GetLastError());
    printf("stderr bytes avail is: %d\n", cbBytesAvailErr);

    //Allocate buffer based on number of bytes available to read
    wchar_t* pipeBuf = calloc(cbBytesAvailErr + 2, sizeof(wchar_t));
    char* convertBuf;
    Sleep(2000);

    //Currently only reading from a single pipe, edit this block as needed to get data when it exists
    //Read from pipe
    DWORD bytesRead;
    printf("right before readfile!\n");
    BOOL fSuccess = ReadFile(hreaderr, pipeBuf, (cbBytesAvailErr + 2) * sizeof(wchar_t), &bytesRead, NULL);
    printf("fSuccess is: %d\n", fSuccess);
    printf("ReadFile last error: %d\n", GetLastError());
    printf("wide string: %ls\n", pipeBuf);
    printf("normal string: %s\n", pipeBuf);
    printf("bytesread is: %d\n", bytesRead);

    //Write buffer out to disk
    Sleep(2000);
    char* filename = "C:\\Users\\User\\Inline-Execute-PE-main\\Inline-Execute-PE\\x64\\Release\\outfile.txt";
    DWORD byteswritten;
    HANDLE hFileOut = CreateFileA(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    WriteFile(hFileOut, pipeBuf, bytesRead, &byteswritten, NULL);

所以这两种情况是这样的:

  1. 使用上面的代码编译为控制台应用程序,第一行 (AllocConsole()) 被注释掉,其他一切都一样,使用命令行参数“cmd.exe /c dir”手动映射 cmd.exe -> 工作,cmd.exe 输出是发送到 stderr 管道并可以读取

在此处输入图像描述

  1. 使用上述代码编译为 Windows 应用程序,使用 AllocConsole(),使用命令行参数“cmd.exe /c dir”手动映射 cmd.exe -> 失败,未捕获 cmd.exe 输出

在此处输入图像描述

再次使用 powershell.exe 效果很好。 我在想,因为它可能使用与 cmd.exe 不同的方式写入 stdout/stderr。

有人有什么想法吗?

编辑:作为更新,我尝试在 RunPEinMemory 项目将 PE 映射到内存之前调用 AllocConsole()。 这导致 cmd.exe 的输出显示到输出控制台,但它仍未被重定向到管道。 这至少是一个进步,但由于某种原因,cmd.exe 中的 stdout/stderr 似乎仍未链接或连接到整个过程

我终于能够解决这个问题。

问题似乎是当我调用 AllocConsole 并重定向 stdout/stderr 时。

以前的顺序是:

手动映射PE
修复 PE 的 IAT
分配控制台
使用 freopen_s 和 SetStdHandle 重定向标准输出/标准错误
使用 open_osfhandle 和 _dup2 将 Win32 句柄转换为 C std 句柄
CreateThread运行PE

工作顺序是:

分配控制台
使用 freopen_s 和 SetStdHandle 重定向标准输出/标准错误
手动映射PE
修复 PE 的 IAT
使用 open_osfhandle 和 _dup2 将 Win32 句柄转换为 C std 句柄
CreateThread运行PE

所以看起来当映射的 PE 的 IAT 得到修复/设置时,控制台需要已经存在并首先正确设置。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM