Jump to content
Tuts 4 You

How to read from CLI till its finished


LCF-AT

Recommended Posts

LCF-AT

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

Link to post
evlncrn8

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

  • Like 2
Link to post

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.

  • Like 1
Link to post
LCF-AT

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

Link to post
LCF-AT

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

Link to post
  • 2 years later...
LCF-AT

Hi again,

so I have another addon question about starting console windows,So how to manage to start more than one console instances?Lets say I wanna run a CLI app few times via CreateProcess function and I wanna also check whether they are still running and I wanna also force to exit them if I want by sending CTRL_C_EVENT (same code as above).Is that doable?In example above I have to use that functions...

CreateProcess
----------------
AttachConsole
SetConsoleCtrlHandler
GenerateConsoleCtrlEvent
GetExitCodeProcess
CloseHandle
FreeConsole
SetConsoleCtrlHandler

...which are working for one instance without problems but now I wanna start more than one instances at same time or one by one you know.So above in code example I saw the info...

    // It's impossible to be attached to 2 consoles at the same time,
    // so release the current one.

...which tells me that there is any limit and I only can attach only one  CLI.Is that right?

CreateProcess
----------------
invoke AttachConsole,PI.dwProcessId                      <-- PID
invoke SetConsoleCtrlHandler,NULL,TRUE                   <-- No PID
invoke GenerateConsoleCtrlEvent,CTRL_C_EVENT,NULL        <-- No PID
invoke GetExitCodeProcess,[PI.hProcess],addr ExitCode    <-- PID
invoke CloseHandle, [PI.hThread]                         <-- PID
invoke CloseHandle, [PI.hProcess]                        <-- PID
invoke FreeConsole                                       <-- No PID
invoke SetConsoleCtrlHandler,NULL,FALSE                  <-- No PID

Above you can see the functions I can use a PID with and some of them I can not use a specific PID.When I start more than one CLI apps at same time then I can not control all at the samt time?How to manage that problem to get access to all running CLI instances I did started to send Events into the right CLI window?

Or have I just to set a lock on that attach / FreeConsole code to handle each PID seperated one by one?Some kind of "right of way" like in traffic etc?Maybe you can tell me or show some code lines how to manage that better to run more than one instances at same time.Thank you.

greetz

Link to post
LCF-AT

Hi fearless,

thanks for this info but I'am not sure about that whether I could use it (as I think) and its also some C stuff (no clue about that) you know.So if I read it right then it wants to create some child processes for another CLI window/s but in my case I want to run diffrent instances seperated from each other.Otherwise lets say I do start 4 CLI windows (anyhow with that C code) then it will build child processes from first started CLI right?But what is when I have to exit the first CLI process?Then child processes get closed too or not?

greetz

Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...