簡體   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