Однажды возникла острая необходимость выяснить логины/пароли от ряда сетевых ресурсов в крупной организации. Выходов было два - MITM и неизвестность или конкретная цель и кейлогер.
Поскольку практически все известные кейлогеры есть в базах KAV и прочей антивирусной шняги, было принято решение писать что-то свое, без всяких няшных гуев на C# (.Net 3.5-4.0).
Only console,only hardcore.
Задач было две - перехватывать все нажатия клавиш с учетом раскладки клавиатуры, отслеживать долгие нажатия клавиш (зажатый shift и пр.,определять где большая буква, где маленькая.) + забрать логи без палева по сети через TCP на случайном порту.
И в этой статье я покажу несложную реализацию, но сразу скажу, что в ней будет несколько ограничений, чтобы киды не шалили. Кто шарит - допилит под себя. (исходник - в конце статьи)
Начнем.
Кодить можно в любой байде,я выбрал Microsoft VisualStudio.
Разработка состоит из следующих этапов:
1. установка хука
2. обработка нажатий
3. шифрование
3. запись в файл
4. создать сервер на произвольном порту, чтобы забирать дамп.
Сперва подключим пространства имен:
выделим из них using System.IO - ввод/вывод, System.Net.Sockets - для создания соединений, using System.Security.Cryptography - шифрование.
Затем инициализируем параметры:
WM_KEYDOWN = 0x0100; //нажали
WM_KEYUP = 0x0101; //отпустили
WM_SYSKEYUP = 0x0105; //отпустили syskey
WM_SYSKEYDOWN = 0x0104 //нажали syskey
public const int KF_REPEAT = 0X40000000; //флаг удержания
Ставим "указатели" для установки Hook . Вообще хук - древнее, испытанное средство, работающее от winxp до win10. Инфы по ним - масса.
Описание хука должно быть вынесено за пределы всяких процедур и находится в общем классе.
Нашему приложении придется выполнять две задачи сразу: писать нажатия и висеть с открытым сокетом. Без тредов тут не обойтись.
До описания void Main пишем [STAThread] - это позволит создать два потока.
Поскольку приложение у нас консольное - нам нужно, чтобы его не было видно.
Для этого в static void Main пишем:
ShowWindow(handle, SW_HIDE); //если закомментировать - окно будет visible
Ставим хук (описание будет ниже)
Далее там же определяем текущую раскладку клавы. Используем метод GetKeyboardLayout();
Обращаем внимание на Writer(Encrypt)( ) - в дальнейшем функция будет записывать и шифровать каждый нажатый символ. Создаем потоки через System.Threading.Thread.
Не забываем снять хук после завершения работы программы:
Теперь пишем функцию для установки хуков. Вызывается из блока Main:
Пишем делегат уже за пределами функции SetHook:
Теперь хук установлен (почти). Куда же идти дальше? Дальше - рыбалка
Пишем функцию для отлова нажатий клавиш:
int vkCode = Marshal.ReadInt32(lParam); - один из самых надежных методов - Marshal, но он дает нам не клавишу в чистом виде, а ее код. Например: F == 70
Далее - переводим из marshal в string.
Если нажали F, то в vkCode будет 70, а mystring=="F"
Далее определяем раскладку клавиатуры:
Writer(encrypted); - пишем факт смены раскладки.
mss = mss_check; -по этой переменной при следующем нажатии определяем, изменилась ли раскладка. Кстати если будете дебажить - учтите, что софт будет писать и клавиши отладки (F9,F10 и реагировать на них).
Пишем все остальные нажатия:
Далее пишем только те что были отпущены (в нашем случае все контрольные)
Похожим образом можно ловить сочетания клавиш, например так:
и обрабатывать их со своими правилами.
Или же делать вообще веселые вещи - блокировать сочетания клавиш:
return (IntPtr)1; - как раз и блокирует
Чтобы наше приложение не зависло после первой нажатой клавиши, нужно предусмотреть выход из процедуры:
На этом с функцией HookCallback покончено. Займемся троянским конем с одной функцией - передать нам данные из файла лога.
Поскольку сокеты вещь капризная, оформляем ее в try - catch конструкцию:
public static void ServerSocket()
{
while (true) // цикл с условием
{
{
try
{
Создаем точку подключения из текущего IP и порта 9050. Я специально убрал поиск и выбор случайного порта, но порт 9050 как правило, не закрыт.
1. Получаем текущий IP и устанавливаем порт.
2. Создаем сокет.
3. Биндим и ждем подключений .
Поскольку наша прога - сервер - мы ждем клиента. Создаем сокет для клиента:
1. Получаем удаленный IP.
2. Создаем коннект.
3. Пишем в лог, что подключились.
Далее необходимо инициировать отправку файла от сервера клиенту. Для этого снова делаем обертку из try-catch.
Для отправки используем Socket.SendFile. Лог сохраняется туда же, где лежит текущее приложение.
После передачи - разрываем коннект.
Желающие могут переписать client.SendFile на FtpWebRequest reqFTP или SmtpClient client = new SmtpClient(), чтобы использовать возможности FTP или email, но последний не советую, т.к. корпоративные почтовики пишут логи+придется писать еще модуль для работы через proxy.
(подобным же образом можно дописать и шелл-оболочку, hello metsvc )
Осталось только дописать две функции: запись и шифрование.
Запись реализуется чрезвычайно просто:
Обращаем внимание - пишем каждый символ с новой строки. Почему так - скажу в конце.
А вот теперь модуль Encrypt. Дабы не изобретать велосипед - возьмем готовый.
Опишу модуль лишь в кратце. Сложностей там нет.
Шифрование будет AES-256. Параметры у меня предустановлены. Главные из них - длина ключа и вектор.
Cоздаем MemoryStream - работать с памятью удобнее и быстрее. Для шифрование - лучший вариант.
Затем создаем крипто-поток. Получается в некотором роде "матрешка" из потоков.
//пишем
cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
// записали - почистили
cryptoStream.FlushFinalBlock();
//закрываем потоки
memStream.Close();
cryptoStream.Close();
Теперь есть одна сложность, почему пишем с новой строки, а не продолжаем дальше. AES не позволяет шифровать данные на лету. Если мы так попробуем сделать - файл будет тупо перезаписываться или в конец будет добавлено шифрованное содержимое, которые мы уже никогда не расшифруем, ибо программа не сможет определить где начало,где конец.
Что же делать? Выход прост и лаконичен:
После symmetricKey.Clear(); дописываем следующую строчку:
Да,да. Старый, добрый Base64.
Работает это так: нажали А - открыли файл для записи, зашифровали А в <&5d92_)_*3>, а <&5d92_)_*3> записали в Base64 H1BoaX7twhPxtE5AuydhHQ==
В итоге шифрованный файл у нас будет выглядеть как-то так:
3/Cy4CkgahXIZxzWIcHB/w==
bEvDy6XwQ/TCSLteiHV8HNfOpYHaM1wcUjbWXcIA2PjN3NuyZ7s5MdfA+l3fkVHp
fHiGcrcccgtDbW4+Tpf7wA==
bEvDy6XwQ/TCSLteiHV8HNfOpYHaM1wcUjbWXcIA2PhUzj0ghbJZVoJgsbRiS6CH
H1BoaX7twhPxtE5AuydhHQ==
GMv5+3F6dLb5GymIvv3Lhg==
9har47I/6mOOnWtllg52nQ==
fHiGcrcccgtDbW4+Tpf7wA==
8E2+NLMvagqELD4ggKoT0g==
IFwu4krJ7dIVqKdbH/EQ0g==
8E2+NLMvagqELD4ggKoT0g==
Присутствует потенциальная уязвимость, ибо одинаковые символы шифруются AES и Base64 одинаково, и статистически возможно определение одного символа или группы символов
(пароли и вектор ведь не меняются).
По TCP все передается plaintext - поскольку все и так зашифровано - для расшифровки нужно знать длину ключа, пароль, вектор. Без этих данных расшифровка - тухлое дело.
И чуть не забыл самое главное. Чтобы хук работал, нужно получить все нужные нам функции через DLLImport. В конец главного класса дописываем:
Все перечислять не буду - полное описание будет во вложении.
Последнее замечание: поскольку всё это написано на C# 4.0 (уж так получилось), то работать это будет на всей ветке Windows, начиная с Win2003S до Win8.1 и скорее всего и Win10 с установленным .NetFramework 4.0 (лично я проверял на WinXP, Win7,Win2008,Win8.1)
На этом создание кейлогера окончено. Что делать дальше?
Писать клиента для подключения к кейлогеру и дешифратор.
Многие наверняка уже поняли, что функцию Encrypt легко можно превратить в Decrypt, просто порядок другой, и сначала нужно будет открыть файл
Всем спасибо!
p.s. материалы и описание функций - все есть в MSDN и Google
Поскольку практически все известные кейлогеры есть в базах KAV и прочей антивирусной шняги, было принято решение писать что-то свое, без всяких няшных гуев на C# (.Net 3.5-4.0).
Only console,only hardcore.
Задач было две - перехватывать все нажатия клавиш с учетом раскладки клавиатуры, отслеживать долгие нажатия клавиш (зажатый shift и пр.,определять где большая буква, где маленькая.) + забрать логи без палева по сети через TCP на случайном порту.
И в этой статье я покажу несложную реализацию, но сразу скажу, что в ней будет несколько ограничений, чтобы киды не шалили. Кто шарит - допилит под себя. (исходник - в конце статьи)
Начнем.
Кодить можно в любой байде,я выбрал Microsoft VisualStudio.
Разработка состоит из следующих этапов:
1. установка хука
2. обработка нажатий
3. шифрование
3. запись в файл
4. создать сервер на произвольном порту, чтобы забирать дамп.
Сперва подключим пространства имен:
выделим из них using System.IO - ввод/вывод, System.Net.Sockets - для создания соединений, using System.Security.Cryptography - шифрование.
Затем инициализируем параметры:
WM_KEYDOWN = 0x0100; //нажали
WM_KEYUP = 0x0101; //отпустили
WM_SYSKEYUP = 0x0105; //отпустили syskey
WM_SYSKEYDOWN = 0x0104 //нажали syskey
public const int KF_REPEAT = 0X40000000; //флаг удержания
Ставим "указатели" для установки Hook . Вообще хук - древнее, испытанное средство, работающее от winxp до win10. Инфы по ним - масса.
Описание хука должно быть вынесено за пределы всяких процедур и находится в общем классе.
Нашему приложении придется выполнять две задачи сразу: писать нажатия и висеть с открытым сокетом. Без тредов тут не обойтись.
До описания void Main пишем [STAThread] - это позволит создать два потока.
Поскольку приложение у нас консольное - нам нужно, чтобы его не было видно.
Для этого в static void Main пишем:
ShowWindow(handle, SW_HIDE); //если закомментировать - окно будет visible
Ставим хук (описание будет ниже)
Далее там же определяем текущую раскладку клавы. Используем метод GetKeyboardLayout();
Обращаем внимание на Writer(Encrypt)( ) - в дальнейшем функция будет записывать и шифровать каждый нажатый символ. Создаем потоки через System.Threading.Thread.
Не забываем снять хук после завершения работы программы:
Теперь пишем функцию для установки хуков. Вызывается из блока Main:
Пишем делегат уже за пределами функции SetHook:
Теперь хук установлен (почти). Куда же идти дальше? Дальше - рыбалка
Пишем функцию для отлова нажатий клавиш:
int vkCode = Marshal.ReadInt32(lParam); - один из самых надежных методов - Marshal, но он дает нам не клавишу в чистом виде, а ее код. Например: F == 70
Далее - переводим из marshal в string.
Если нажали F, то в vkCode будет 70, а mystring=="F"
Далее определяем раскладку клавиатуры:
Writer(encrypted); - пишем факт смены раскладки.
mss = mss_check; -по этой переменной при следующем нажатии определяем, изменилась ли раскладка. Кстати если будете дебажить - учтите, что софт будет писать и клавиши отладки (F9,F10 и реагировать на них).
Пишем все остальные нажатия:
Далее пишем только те что были отпущены (в нашем случае все контрольные)
Похожим образом можно ловить сочетания клавиш, например так:
и обрабатывать их со своими правилами.
Или же делать вообще веселые вещи - блокировать сочетания клавиш:
return (IntPtr)1; - как раз и блокирует
Чтобы наше приложение не зависло после первой нажатой клавиши, нужно предусмотреть выход из процедуры:
На этом с функцией HookCallback покончено. Займемся троянским конем с одной функцией - передать нам данные из файла лога.
Поскольку сокеты вещь капризная, оформляем ее в try - catch конструкцию:
public static void ServerSocket()
{
while (true) // цикл с условием
{
{
try
{
Создаем точку подключения из текущего IP и порта 9050. Я специально убрал поиск и выбор случайного порта, но порт 9050 как правило, не закрыт.
1. Получаем текущий IP и устанавливаем порт.
2. Создаем сокет.
3. Биндим и ждем подключений .
Поскольку наша прога - сервер - мы ждем клиента. Создаем сокет для клиента:
1. Получаем удаленный IP.
2. Создаем коннект.
3. Пишем в лог, что подключились.
Далее необходимо инициировать отправку файла от сервера клиенту. Для этого снова делаем обертку из try-catch.
Для отправки используем Socket.SendFile. Лог сохраняется туда же, где лежит текущее приложение.
После передачи - разрываем коннект.
Желающие могут переписать client.SendFile на FtpWebRequest reqFTP или SmtpClient client = new SmtpClient(), чтобы использовать возможности FTP или email, но последний не советую, т.к. корпоративные почтовики пишут логи+придется писать еще модуль для работы через proxy.
(подобным же образом можно дописать и шелл-оболочку, hello metsvc )
Осталось только дописать две функции: запись и шифрование.
Запись реализуется чрезвычайно просто:
Обращаем внимание - пишем каждый символ с новой строки. Почему так - скажу в конце.
А вот теперь модуль Encrypt. Дабы не изобретать велосипед - возьмем готовый.
Опишу модуль лишь в кратце. Сложностей там нет.
Шифрование будет AES-256. Параметры у меня предустановлены. Главные из них - длина ключа и вектор.
Cоздаем MemoryStream - работать с памятью удобнее и быстрее. Для шифрование - лучший вариант.
Затем создаем крипто-поток. Получается в некотором роде "матрешка" из потоков.
//пишем
cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
// записали - почистили
cryptoStream.FlushFinalBlock();
//закрываем потоки
memStream.Close();
cryptoStream.Close();
Теперь есть одна сложность, почему пишем с новой строки, а не продолжаем дальше. AES не позволяет шифровать данные на лету. Если мы так попробуем сделать - файл будет тупо перезаписываться или в конец будет добавлено шифрованное содержимое, которые мы уже никогда не расшифруем, ибо программа не сможет определить где начало,где конец.
Что же делать? Выход прост и лаконичен:
После symmetricKey.Clear(); дописываем следующую строчку:
Да,да. Старый, добрый Base64.
Работает это так: нажали А - открыли файл для записи, зашифровали А в <&5d92_)_*3>, а <&5d92_)_*3> записали в Base64 H1BoaX7twhPxtE5AuydhHQ==
В итоге шифрованный файл у нас будет выглядеть как-то так:
3/Cy4CkgahXIZxzWIcHB/w==
bEvDy6XwQ/TCSLteiHV8HNfOpYHaM1wcUjbWXcIA2PjN3NuyZ7s5MdfA+l3fkVHp
fHiGcrcccgtDbW4+Tpf7wA==
bEvDy6XwQ/TCSLteiHV8HNfOpYHaM1wcUjbWXcIA2PhUzj0ghbJZVoJgsbRiS6CH
H1BoaX7twhPxtE5AuydhHQ==
GMv5+3F6dLb5GymIvv3Lhg==
9har47I/6mOOnWtllg52nQ==
fHiGcrcccgtDbW4+Tpf7wA==
8E2+NLMvagqELD4ggKoT0g==
IFwu4krJ7dIVqKdbH/EQ0g==
8E2+NLMvagqELD4ggKoT0g==
Присутствует потенциальная уязвимость, ибо одинаковые символы шифруются AES и Base64 одинаково, и статистически возможно определение одного символа или группы символов
(пароли и вектор ведь не меняются).
По TCP все передается plaintext - поскольку все и так зашифровано - для расшифровки нужно знать длину ключа, пароль, вектор. Без этих данных расшифровка - тухлое дело.
И чуть не забыл самое главное. Чтобы хук работал, нужно получить все нужные нам функции через DLLImport. В конец главного класса дописываем:
Все перечислять не буду - полное описание будет во вложении.
Последнее замечание: поскольку всё это написано на C# 4.0 (уж так получилось), то работать это будет на всей ветке Windows, начиная с Win2003S до Win8.1 и скорее всего и Win10 с установленным .NetFramework 4.0 (лично я проверял на WinXP, Win7,Win2008,Win8.1)
На этом создание кейлогера окончено. Что делать дальше?
Писать клиента для подключения к кейлогеру и дешифратор.
Многие наверняка уже поняли, что функцию Encrypt легко можно превратить в Decrypt, просто порядок другой, и сначала нужно будет открыть файл
Всем спасибо!
p.s. материалы и описание функций - все есть в MSDN и Google
Вложения
Последнее редактирование модератором: