LCF-AT 2,355 Posted October 6, 2018 Hi guys, I have again a small question about piping a CLI output to my buffer.So I just wanna start a CLI app with paramters and reading the output (content I also see in CMD window) so long till its finished.So the only problem I have so far is how to wait till its finished.Lets say the CLI app needs a while (a minute etc).My question is what function/s I need to use for that?I see I cant use WaitForSingleObject with INFINITE paramter so this dosent work because the CLI app dosent exit itself so long I didnt had reading the bytes.If I just read the bytes using PeekNamedPipe / ReadFile function then I dont get the whole content = it quits to early and the CLI app is still working. My goal is it to read the bytes on fly and checking whether the pipe / process is still working / activ or not. Below some example code: _CreatePipe PROC Local RetVal:DWORD invoke RtlZeroMemory,addr SecuAttr,sizeof SecuAttr invoke RtlZeroMemory,addr StartInfo,sizeof StartInfo invoke RtlZeroMemory,addr PI,sizeof PI mov RetVal,0 mov SecuAttr.nLength,sizeof SECURITY_ATTRIBUTES mov SecuAttr.bInheritHandle,TRUE invoke CreatePipe,addr hRead,addr hWrite,addr SecuAttr,NULL .if eax == FALSE invoke MessageBox,0,chr$("CreatePipe failed!"),chr$("Problem!"),MB_ICONWARNING mov eax,FALSE ret .endif invoke SetHandleInformation, hRead, HANDLE_FLAG_INHERIT, 0 .if eax == FALSE invoke CloseHandle,hRead invoke CloseHandle,hWrite invoke MessageBox,0,chr$("SetHandleInformation failed!"),chr$("Problem!"),MB_ICONWARNING mov eax,FALSE ret .endif mov RetVal,eax mov StartInfo.cb,sizeof StartInfo invoke GetStartupInfo,addr StartInfo xor eax,eax mov StartInfo.lpReserved,eax mov eax,hWrite mov StartInfo.hStdOutput,eax mov StartInfo.hStdError,eax mov StartInfo.dwFlags, STARTF_USESTDHANDLES mov StartInfo.wShowWindow,SW_SHOW mov eax,RetVal ret _CreatePipe endp ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; .elseif ax == IDB_TEST invoke VirtualAlloc,0,50000h,MEM_COMMIT,PAGE_EXECUTE_READWRITE mov esi, eax push esi invoke _CreatePipe .if eax != FALSE lea edi, chr$("cmd.exe /c C:\tool.exe -t -r") invoke CreateProcess, NULL, edi, NULL,NULL,TRUE,CREATE_NO_WINDOW,NULL,NULL, addr StartInfo, addr PI .if eax != FALSE @@: invoke Sleep,300 invoke PeekNamedPipe,hRead,offset PIPEBUFFER,sizeof PIPEBUFFER,offset PIPEREADBYTES,addr AVAL,NULL .if eax != FALSE .if AVAL != 0h invoke ReadFile,hRead,esi,PIPEREADBYTES,addr BYTESREAD,0 .if eax != FALSE add esi, BYTESREAD jmp @B .else invoke MessageBox,0,chr$("ReadFile failed!"),chr$("Problem!"),MB_ICONWARNING jmp @F .endif .else pop esi push esi invoke MessageBox,0,esi,chr$("Info!"),MB_ICONINFORMATION .endif .else invoke MessageBox,0,chr$("PeekNamedPipe failed!"),chr$("Problem!"),MB_ICONWARNING .endif @@: invoke CloseHandle, [PI.hThread] invoke CloseHandle, [PI.hProcess] invoke CloseHandle, hRead invoke CloseHandle, hWrite .else invoke CloseHandle, hRead invoke CloseHandle, hWrite .endif .endif pop esi invoke VirtualFree,esi,0,MEM_RELEASE How to handle the wait operation correctly and to read all bytes / content so long the CLI app is activ / working?Maybe anyone could help a little to fix that problem. Thank you Share this post Link to post
evlncrn8 354 Posted October 6, 2018 https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-getexitcodeprocess that at least lets you check 100% if the process has exited, the rest is just polling, to check if there's input and so on also, mov StartInfo.hStdOutput,eax mov StartInfo.hStdError,eax was that intentional ? theres stdout, stdin, and stderr.. also you didnt set hread from what i saw but i didnt go through the code in detail 2 Share this post Link to post
atom0s 433 Posted October 7, 2018 When you use CreateProcess the PROCESS_INFORMATION class gives back the main process handle and the main thread handle. You can use those to pull the exit codes of either. - GetExitCodeProcess and GetExitCodeThread Keep in mind if the application is multi-threaded, it is not guaranteed that the main thread handle will remain alive for the entire duration of the app. 1 Share this post Link to post
LCF-AT 2,355 Posted October 7, 2018 Hi guys, thanks for your answers.Ok I am just checking whether the process is still active or not using GetExitCodeProcess function.Ok,so this seems to work so far and I can read while pipe content.Now I found another small problem.So if I start the CLI app and it does run for a longer while then I wanna also stop the process similar like using CRTL+C key press in CMD window.First I just tried to use TermniateProcess function with the hprocess ID of CLI app but this dosent work and CLI app keeps working in background / taskmanager and I need to wait till its finished by itself.Now I checked again the internet and found something about using GenerateConsoleCtrlEvent function to send CTRL_C_EVENT.I also found some example code like this... https://codetitans.pl/blog/post/sending-ctrl-c-signal-to-another-application-on-windows [DllImport("kernel32.dll", SetLastError = true)] static extern bool AttachConsole(uint dwProcessId); [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)] static extern bool FreeConsole(); [DllImport("kernel32.dll")] static extern bool SetConsoleCtrlHandler(ConsoleCtrlDelegate handler, bool add); // Delegate type to be used as the Handler Routine for SCCH delegate Boolean ConsoleCtrlDelegate(CtrlTypes type); // Enumerated type for the control messages sent to the handler routine enum CtrlTypes : uint { CTRL_C_EVENT = 0, CTRL_BREAK_EVENT, CTRL_CLOSE_EVENT, CTRL_LOGOFF_EVENT = 5, CTRL_SHUTDOWN_EVENT } [DllImport("kernel32.dll")] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool GenerateConsoleCtrlEvent(CtrlTypes dwCtrlEvent, uint dwProcessGroupId); public static void StopProgram(uint pid) { // It's impossible to be attached to 2 consoles at the same time, // so release the current one. FreeConsole(); // This does not require the console window to be visible. if (AttachConsole(pid)) { // Disable Ctrl-C handling for our program SetConsoleCtrlHandler(null, true); GenerateConsoleCtrlEvent(CtrlTypes.CTRL_C_EVENT, 0); // Must wait here. If we don't and re-enable Ctrl-C // handling below too fast, we might terminate ourselves. Thread.Sleep(2000); FreeConsole(); // Re-enable Ctrl-C handling or any subsequently started // programs will inherit the disabled state. SetConsoleCtrlHandler(null, false); } } ...I tried this like this for testing... invoke CreateProcess, NULL,addr commandline,NULL,NULL,TRUE,CREATE_NO_WINDOW,NULL,NULL,addr StartInfo,addr PI .if eax != FALSE invoke Sleep,2000 invoke AttachConsole,PI.dwProcessId .if eax != FALSE invoke SetConsoleCtrlHandler,NULL,TRUE .if eax != FALSE invoke GenerateConsoleCtrlEvent,CTRL_C_EVENT,NULL .if eax != FALSE invoke Sleep,2000 invoke FreeConsole invoke SetConsoleCtrlHandler,NULL,FALSE .endif .endif .endif ...so it seems to work and the CLI app gets closed but I am not so sure whether this code example is good idea to use it on that way etc.In Olly it does crash after I did traced over this code after (not in outside of Olly).So is it needed to use FreeConsole & SetConsoleCtrlHandler at the end if the CLI is no more present?I dont think so or?I am using already cmd.exe /c paramter in CreateProcess function. Other issue I see is that I dont use the flag CREATE_NEW_PROCESS_GROUP (in the link above they told to use it but then CTRL_C_EVENT isnt working).Maybe you have some another little clue how I should do it correctly to send CTRL_C_EVENT & wait without to get any trouble later and I also dont wanna exit the CLI process rough you know. greetz Share this post Link to post
evlncrn8 354 Posted October 7, 2018 https://docs.microsoft.com/en-us/windows/console/generateconsolectrlevent should do it invoke FreeConsole invoke SetConsoleCtrlHandler,NULL,FALSE it might well crash there, considering you still have the handler registered so maybe flip that code around.. setevent then freeconsole.. see what happens .. 1 Share this post Link to post
LCF-AT 2,355 Posted October 8, 2018 Hi again, ok I used it so now with freeconsole & Setconsolehandler at the end.So if I dont do it then it dosent work anymore after first try.My thread code look so now... local WASATTACHED:DWORD mov WASATTACHED, 0 @@: .if STOP == 1 ; <--- stop CLI invoke GetExitCodeProcess,[PI.hProcess],addr ExitCode .if ExitCode == STILL_ACTIVE && WASATTACHED == NULL invoke AttachConsole,PI.dwProcessId mov WASATTACHED, 1 invoke SetConsoleCtrlHandler,NULL,TRUE invoke GenerateConsoleCtrlEvent,CTRL_C_EVENT,NULL .endif .endif invoke RtlZeroMemory,addr PIPEBUFFER,sizeof PIPEBUFFER invoke Sleep,30 invoke PeekNamedPipe,hRead,offset PIPEBUFFER,sizeof PIPEBUFFER-10h,offset PIPEREADBYTES,addr AVAL,NULL .if eax != FALSE .if AVAL != 0h invoke RtlZeroMemory,addr PIPEBUFFER,sizeof PIPEBUFFER invoke ReadFile,hRead,addr PIPEBUFFER,PIPEREADBYTES,addr BYTESREAD,0 .if eax != FALSE invoke SendMessage,RICHEDITHANDLE,EM_REPLACESEL,FALSE,addr PIPEBUFFER invoke RichSetPos, RICHEDITHANDLE, -1h jmp @B .else invoke MessageBox,0,chr$("ReadFile failed!"),chr$("Problem!"),MB_ICONWARNING jmp @F .endif .else invoke GetExitCodeProcess,[PI.hProcess],addr ExitCode .if ExitCode == STILL_ACTIVE jmp @B .endif .endif .else invoke MessageBox,0,chr$("PeekNamedPipe failed!"),chr$("Problem!"),MB_ICONWARNING .endif @@: invoke CloseHandle, hRead invoke CloseHandle, hWrite invoke CloseHandle, [PI.hThread] invoke CloseHandle, [PI.hProcess] .if STOP == 1 && WASATTACHED == 1 invoke FreeConsole invoke SetConsoleCtrlHandler,NULL,FALSE .endif mov STOP, 0 xor eax,eax Ret ...seems to work fine so far. Thanks again for the help. greetz Share this post Link to post