Newsgroup: it.comp.programmare.win32
Titolo: problema con redirezione pipe console

Ciao a tutti,
nel progetto a cui sto' lavorando avrei bisogno di catturare l'output
(stdoutput)
di una console (win32) che al momento lancio con createprocess().
Ho trovato diversi esempi in cui questa cosa viene affrontata con
la redirezione degli stdhandler (stdin,stdout,stderr) tramite pipe anonime
dal processo figlio
al processo padre.
Questi esempi lavorano perfettamente con console che interagiscono nei
seguenti modi:
1) il proc della console esegue il suo lavoro, getta un output e quindi
termina.
2) il proc della console si pone in attesa di input, quando ne ha li
processa e restituisce un output, quindi
si ripone in stato di attesa.
Purtroppo la console della quale avrei bisogno di monitorare l'output subito
dopo il lancio non si pone
mai in stato di attesa, ma al contrario ad intervalli regolari (circa 1
secondo) invia output che descrive
il suo stato.
Tutti gli esempi che ho trovato non catturano alcun output con questo tipo
di interazione,
nemmeno la redirezione standard di windows ( >out ) lavora (mentre lavora
correttamente
quando viene lanciata con parametri sbagliati e quindi dopo aver descritto
la propria sintassi,
la console termina).
Sono giunto alla conclusione, senza certezza (e comunque non ho idee per la
soluzione), che si
tratti di un problema di lettura sincrona/asincrona, il codice che ho visto
e che ho provato ad
utilizzare fa sempre uso di readfile() (in modalita' sincrona), in alcuni
casi viene utilizzata anche
peeknamedpipe() prima della lettura, il risultato e' sempre lo stesso. Ho
letto da qualche parte
(e quindi non ne ho la certezza) che l'output su console e' bufferizzato per
linee mentre quando
viene rediretto in pipe vengono utilizzati buffer di piu' grandi dimensioni,
ho quindi provato
la funzione flushfilebuffer() in diversi modi, ma il suo utilizzo in questo
contesto non mi e' molto chiaro
e non ho avuto risultati positivi.
Non e' giusto usare le pipe anonymous?
Forse le pipe redirette non sono la via giusta?
Lo sono ma bisogna usare readfile() in modalita' asincrona? (overlapped)
Flushfilebuffer() potrebbe essere cosa buona ma va' utilizzato in qualche
modo particolare?
(devo prima creare handler particolari? usarlo prima/dopo sleep()? dopo
dinner() a piccole dosi...?!? )
Non so' dove sbattere la testa, (<- ...per questo non ho bisogno di
consigli... :)
Grazie
Alex

la propria sintassi, la console termina).
tutto sbagliato, l'output non viene catturato nel caso
si interrompa il processo con ctrl+c/terminateprocess().
Se al processo viene fatto capire che deve terminare
regolarmente (exitprocess() dal processo stesso)
tutto l'output viene riportato correttamente.
Questo mi fa' pensare che si tratti veramente di una
questione di bufferizzazione della pipe e purtroppo
il problema mi rimane visto che dovrei leggere la pipe
mentre il processo e' al lavoro ( chr puo' durare anche
ore ) e non alla fine.
Saluti,
Alex

 e non ho avuto risultati positivi.
FlushFileBuffer() serve quando devi fare il flush di dati in *uscita*,
mentre nel tuo caso il problema e` nella ricezione (e d'altra parte non puoi
chiamare FlushFileBuffer() sul lato della pipe che hai assegnato a stdout,
perche` tale lato non ti "appartiene" piu`). Piuttosto potresti provare a
specificare una dimensione di buffer di un byte quando crei le pipe con
CreatePipe() (probabilmente adesso specifichi zero, e cosi` facendo lasci
che sia Windows a decidere per te); in questo modo non dovrebbe essere
aggiunto nessun tipo di buffering, a parte quello di stdout (per il quale
direi che tu non possa fare nulla).
--
Lorenzo

Avevo gia' provato con valori tipo 80 e cose di questo genere,
ho riprovato con 1 comunque, il risultato purtroppo non cambia.
da createpipe():
"nSize: Specifies the buffer size for the pipe.
The size is only a suggestion; the system uses the value
to calculate an appropriate buffering mechanism.
If this parameter is zero, the system uses the default buffer size."
Visto che al momento non ho la minima idea di come costringere
il child process a eseguire il flush o a ridurre il buffersize, pensavo,
non mi sarebbe possibile in qualche modo magari piu' sporco
grabbare il testo della window (class tty) del child con qualche
sendmessage? Oppure, conoscendo la funzione che scrive l'output
nella console (writefile o altro) hookarla?
Saluti.

"Alex" ha scritto
: >Tutti gli esempi che ho trovato non catturano alcun

:
Se ho capito il problema in un caso analogo io avevo usato
la PeekNamedPipe come nell'esempio seguente (non e` commentato
ed e` stato poco testato):
  do
  {
    PeekNamedPipe( hChildStdoutRd, NULL, 0, NULL, &dwToRead, NULL );
    if ( dwToRead > 0 )
    {
      do
      {
        dwReading = dwToRead > dwBufferSize ? dwBufferSize : dwToRead ;
        ReadFile( hChildStdoutRd, Buffer, dwReading, &dwRead, NULL );
        Buffer[dwRead] = TEXT( '\0' );
        SendMessage( hOutput, WM_USER, dwRead+1, (LPARAM) Buffer );
      } while ( (dwToRead -= dwRead) > 0 );
      dwToRead = 0;
    }
  } while ( WaitForSingleObject( pi.hProcess, 1000 ) == WAIT_TIMEOUT );
HTH
ciao
Massimo

grazie Lorenzo e grazie Massimo.
In effetti il codice che ho usato e' sempre piu' o meno
di quel tipo. Il buffer della pipe ( che non posso
alterare ) sembrerebbe aggirarsi intorno ai 4400 bytes.
Troppo alto per il programma
che sto' cercando di grabbare, passa qualche minuto
prima che ne conosca lo stato.
Ho verificato che il programma usa WriteFile per scivere
l'output in console, sto' cercando di hookkarla con
ImageDirectoryEntryToData() e WriteProcessMemory(),
per rimpiazzare WriteFile e costringerlo a usare anche FlushFileBuffer.
Ma per qualche motivo, che ancora non ho risolto, l'indirizzo
della funzione (WriteFile) nel modulo exe che carico con LoadLibrary
e' diverso dal  GetProcAddress(GetModuleHandle("kernel32"),"WriteFile")
e quindi al momento del replacement la cosa non funziona.
kernel32.dll GetModuleHandle: 77E80000h
Kernel32.WriteFile GetProcAddress: 77E8667Bh
Replacing kernel32.WriteFile( 77E8667Bh ) with
gllCallExt.WriteFileAndFlush( 120100Fh )...
(gllCallExt e' una nuova dll, contiente WriteFileAndFLush
che vorrei usare come rimpiazzo)
Dopo aver verificato che il modulo exe importa effettivamente
funzioni di kernel32.dll looppo i riferimenti a queste funzioni
verificando che si tratti di quella che mi interessa.
PROC* ppfn = (PROC*) &pThunk->u1.Function;
dove, le funzioni importate da kernel32 nel modulo(exe)
per me sono:
*ppfn = 0x0007215e;
*ppfn = 0x0007213e;
*ppfn = 0x0007212e;
...
presumo che *ppfn sia un offset, relativo, ma non capisco a cosa
(con precisione) la cosa che ci va' piu' vicino e':
(kernel mod handle) 77E80000h - (offset) 7212Eh = 77E0DED2h,
e comunque nella funzione che ho trovato per rimpiazzare
i riferimenti non si faceva parola di offset relativi,
veniva svolta come se questi fossero gia' assoluti o risolti,
ho usato LoadLibrary("file.exe") per mappare quello
che dovrebbe essere il child (appunto l'exe) nell'area di memoria
del parent( o padre, o launcher o come cavolo lo si voglia chiamare)
ma non sono sicuro che sia il modo giusto.
In pratica, se uso LoadLibrary(file.exe) e poi CreateProcess(file.exe)
windows usa quello gia' mappato?
C'e' qualche alternativa a LoadLibrary
o qualche modo particolare di usarlo dove gli offset delle funzioni
importate vengono gia' risolti?
Grazie
Saluti,
Alex
---- Link utili per anonymous pipe, redirezione output. ---
How to Pipe ( | ) Input into a QuickBasic Program (Q46376)
http://support.microsoft.com/default.aspx?scid=kb;EN-US;q46376
Questo esempio in qbasic mi sembrava molto interessante
visto che e' l'unico che propone una vera alternativa, ma
credo di aver capito che interrupt, in win32, vuol
dire vxd e sia dell'uno che dell'altro ne so' ancora
poco. Se qualcuno puo' confermarmi che grabberebbe
in realtime l'output potrei spenderci del tempo.
Questi sono semplici tutoriali e ref per
le pipe se a qualcuno puo' interessare ( che
non funzionano per il mio caso specifico ma
funzionano benissimo in tutti gli altri casi )
Win32 asm x86:
http://codedata.box.sk/asm/iczelion/tut21.html
MFC:
http://codeguru.earthweb.com/console/QuickWin.shtml
http://www.codeguru.com/misc/redirect.shtml
http://www.codeguru.com/misc/RedirectOutputToPipe.shtml
ATL x Visual basic
http://www.codeproject.com/atl/redirect.asp
--- Alcuni reference: ---
Capturing console I/O in Win32:
http://homepages.tesco.net/~J.deBoynePollard/FGA/capture-console-win32.html
Asynchronous (overlapped) read and write operations are not supported by
anonymous pipes
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/ipc/pipes_1
o4z.asp
Using anonymous pipes to redirect standard input/output of
a child process. by Borland Developer Support Staff
http://community.borland.com/article/0,1410,10387,00.html
HOWTO: Spawn Console Processes with Redirected Standard Handles
http://support.microsoft.com/default.aspx?scid=kb;en-us;Q190351
INFO: Redirection Issues on Windows 95 MS-DOS Applications
http://support.microsoft.com/default.aspx?scid=kb;en-us;Q150956
HOWTO: Create a Process for Reading and Writing to a Pipe
http://support.microsoft.com/default.aspx?scid=kb;EN-US;q173085
Platform SDK: DLLs, Processes, and Threads
Creating a Child Process with Redirected Input and Output
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dllproc/pro
thred_4uus.asp

"Alex" ha scritto
...
: presumo che *ppfn sia un offset, relativo, ma non capisco a cosa
: (con precisione) la cosa che ci va' piu' vicino e':
: (kernel mod handle) 77E80000h - (offset) 7212Eh = 77E0DED2h,
: e comunque nella funzione che ho trovato per rimpiazzare
: i riferimenti non si faceva parola di offset relativi,
: veniva svolta come se questi fossero gia' assoluti o risolti,
: ho usato LoadLibrary("file.exe") per mappare quello
: che dovrebbe essere il child (appunto l'exe) nell'area di memoria
: del parent( o padre, o launcher o come cavolo lo si voglia chiamare)
: ma non sono sicuro che sia il modo giusto.
: In pratica, se uso LoadLibrary(file.exe) e poi CreateProcess(file.exe)
: windows usa quello gia' mappato?
: C'e' qualche alternativa a LoadLibrary
: o qualche modo particolare di usarlo dove gli offset delle funzioni
: importate vengono gia' risolti?
Premesso che non ho mai fatto nulla di simile, potresti provare con qualcosa
tipo
CreateProcess ( ..., CREATE_SUSPENDED, ... )
GetModuleHandle( )
GetProcAddress( )
...
...
ResumeThread()
e poi aspetti sulla pipe.
ciao
Massimo

La cosa e' giusto un pelino piu' complessa,
per fortuna uso createprocess e questo mi offre un vantaggio,
nel processo parent posso creare un oggetto memmappingfile
( lo chiamero' cosi' per comodita' )
e renderlo inheritable al processo child, a questo punto dopo
aver creato il child come suspended devo recuperare
il primary thread starting memory address, salvare qualche byte
(istruzioni x86) da qualche parte e sostituirli con un salto
al memfilemapping.
Resumo il thread, nel memmappingfile naturalmente avro'
del codice che carica la dll con la funzione che voglio sostituire,
una funzione che scorrera' la tabella degli import del processo
child per rintracciare la vecchia e sostituirla con la nuova funzione.
A questo punto dovrei ripristinare il codice originale e far continuare
il thread da li'.
Concettualmente la cosa e' abbastanza chiara, le questioni che
ancora non mi sono chiare sono:
come ottenere il primary thread starting memory address,
( il pinfo di create process potrebbe aiutarmi )
una volta ottenuto, il contesto sara' ancora nel parent process
quindi come accedervi? (readprocess/writeprocess?)
nel codice che inseriro' nel memmappingfile ci saranno delle
funzioni che dovrebbero essere linkate a kernel32.dll,
questo codice verra' eseguito nel contesto del child process,
i link lavoreranno correttamente ugualmente? ( per lavorare
correttamente la kernel32.dll dovrebbe essere mappata
allo stesso indirizzo da entrambi i processi.. non sono sicuro che
sia cosi' [ A meno che non possa mappare nel memmappingfile
un modulo dll o exe che risolva i problemi di link al momento
del load]).
Un altro punto non mi era chiaro ma mi e' passato di mente.
Saluti,
Alex

 Un altro punto non mi era chiaro ma mi e' passato di mente.
Mi e' tornato in mente, non sono sicuro che il memmappingfile
verra' mappato nei due processi allo stesso indirizzo, mi e'
indispensabile saperlo ( e sapere come ovviare ) per la
deviazione che dovro' fare nel primary thread starting memory
del child.
Ciao
:)

 verra' mappato nei due processi allo stesso indirizzo.
Sembrerebbe di si, ( Sotto win98, devo verificare 2000/NT )
Fra l'altro sotto win98 lo spazio delle dll tipo kernel32 sono
uguali e quindi sembrerebbero tutte mappate allo stesso
indirizzo per tutti i processi, questo mi risolverebbe almeno
i problemi degli import nel codice che va nel memmappingfile.
( In win98, per gli altri verifichero' )
Oltre a queste verifiche (ed eventuali soluzioni) mi macherebbe
ancora di accedere alla memoria di starting del child dal parent.
Saluti.

 ancora di accedere alla memoria di starting del child dal parent.
usando le toolhelp per 98 e le psapi si puo' fare,
nelle msdn c'e' una splendida dll per ottenere l'entrypoint di un pe,
per stasera la versione 98 dovrebbe essere almeno funzionante.
Mi rendo conto che oramai e' un monologo,
la netiquette prevede provvedimenti in proposito? internamento?
Saluti,
Alex

"Alex" ha scritto
: "Alex" ha scritto:
: usando le toolhelp per 98 e le psapi si puo' fare,
: nelle msdn c'e' una splendida dll per ottenere l'entrypoint di un pe,
: per stasera la versione 98 dovrebbe essere almeno funzionante.
:
: Mi rendo conto che oramai e' un monologo,
: la netiquette prevede provvedimenti in proposito? internamento?
Non penso :-) finche` sei IT .
Non ho postato suggerimenti, idee o commenti perche` non essendo
suffragati dall'esperienza sarebbero probabilmente stati solo motivo
di confusione.
C'e` un dubbio che mi rimane. Nella documentazione su FlushFileBuffers
si legge:
...
Windows NT: The function fails if hFile is a handle to console output.
That is because console output is not buffered. The function returns
FALSE, and GetLastError returns ERROR_INVALID_HANDLE.
Windows 95: The function does nothing if hFile is a handle to console
output. That is because console output is not buffered. The function
returns TRUE, but it does nothing.
che sembrerebbe rendere inutile la chiamata a FlushFileBuffers...
molto probabilmente non hai a che fare con una console output. Boh.
ciao
Massimo

 molto probabilmente non hai a che fare con una console output. Boh.
In effetti credo che la bufferizzazione sia una proprietà della pipe e non
dell'handle di output della console, immagino comunque che flushfilebuffer
per questo o altri motivi potrebbe non funzionare, ma una volta che posso
hookare writefile... l'utilizzo di flushfilebuffer e' "relativo" :)
Oggi non sono riuscito a completare tutta la procedura per 98 visto che mi
sono scontrato con qualche mio limite di conoscenza del C.
Devo ricavare il pointer a una funzione e la dimensione in byte della
funzione stessa per poi copiarla nell'area di memoria mappata, ci
riprovero' domani.( Sarebbe stato + facile assemblarla con masm e
linkarla ma vorrei rimanere in c )
Per ora riesco semplicemente a mappare l'area di memoria dal parent al
child, copiare 64k ( dal baseaddress ) del child nel parent, recuperare
l'entrypoint del child e quindi copiare 64k dall'entrypoint del child al
parent, backuppo i primi 32k nei successivi 32k,
nei primi gli metto un bel jmp assoluto ( al blocco mappato ) come
prima istruzione e quindi lo rispedisco nel child.
Poi dovro' mettere giu' il codice che andra' nell'area mappata, piu' o
meno so' gia' cosa fare.
Poi potro' resumare il thread dal parent process e li' si vedra'.
Saluti,
Alex

quindi pure per nt)
Occhio: con Dependency Walker più di una volta numeracci rossi mi 
annunciano un conflitto di indirizzi, per cui una DLL necessita di 
essere rilocata, e kernel32 e compagnia cantando NON sono esentate: 
hanno tutte quante in bella mostra la loro sezione .reloc, quindi 
l'eventualità è stata almeno prevista
Con alcuni programmi non convenzionali potresti avere grane
-- 
Nato nel vizio, morto nel supplizio
Nato nel peccato, ad entrare sia invitato

 l'eventualità è stata almeno prevista
Ho visto in giro qualche sorgente per recuperare almeno
la kernel32 senza far uso di api ( e mi basterebbe quella ),
se dovessero saltarmi fuori problemi ne faro' uso.
Dopo aver sofferto qualche giorno, sono riuscito ad hookkare
WriteFile sotto 98! E per ora non da' problemi,
Come qualcuno aveva previsto pero' (Massimo),
FlushFileBuffer non sortisce alcun effetto, poco importa visto
che ho a disposizione l'output prima che questi sia mandato alla console.
Grazie,
Saluti
Alex

Funziona tutto molto meglio solo
con l'hook su writefile, SENZA PIPE!
Ciao