Исходник Лоадер с правильной авторизацией с форума Xenforo

Олдфаг
Статус
Оффлайн
Регистрация
18 Фев 2019
Сообщения
2,825
Реакции[?]
1,852
Поинты[?]
24K
На форуме есть очень много тем с авторизацией на форуме Xenforo, но большая часть из них имеет в себе те или иные недочете в коде и в алгоритмах получения данных о пользователе. Сегодня я постараюсь подробно расписать процесс привязки вашего приложения к сайту с использованием Xenforo API 2.1+. Код будет написан на C#, но при желании его можно переделать под C++ и других современных языков программирования.

1. Получаем ключ API для форума.
Для нормальной работы с api форума нам нужен соответствующий ключ, который можно создать в админ-панеле. В параметрах найдите вкладку "Ключи API" и откройте ее. Если пункт не появляется, то, скорее всего, у вас отключен api в конфиге форума. Это исправляется изменением строки $config['enableApi'] = false; на $config['enableApi'] = true;.
Создайте новый ключ API с настройками, как на следующем скриншоте:
При желании можно добавить еще несколько разрешенных областей, если вы уверены, что это не навредит безопасности проекта.

2. Пишем wrapper для API.
Для начала стоит проверить работоспособность api форума. По умолчанию эндроинты располагаются по пути http(s)://yourforum.com/api/endpoint, но если у вас не настроены friendly URLs, то скорее всего это http(s)://yourforum.com/index.php?api/endpoint. Для первых тестов советую использовать бесплатную программу
Пожалуйста, авторизуйтесь для просмотра ссылки.
, либо онлайн-сервис
Пожалуйста, авторизуйтесь для просмотра ссылки.
. Нам нужно послать POST запрос на api/auth, указав в параметрах запроса наш логин (почту или юзернейм) и пароль. Также в заголовки необходимо добавить XF-Api-Key: Ваш ключ. Так сказано на официальном сайте Xenforo, и без этих данный сервер будет возвращать ошибку.
Если вы все сделали правильно, то в окне response должен массив данных в формате JSON. Вот пример ответа от моего сервера:
JSON:
{
    "success": true,
    "user": {
        "about": "",
        "activity_visible": true,
        "alert_optout": [],
        "allow_post_profile": "members",
        "allow_receive_news_feed": "everyone",
        "allow_send_personal_conversation": "members",
        "allow_view_identities": "everyone",
        "allow_view_profile": "everyone",
        "avatar_urls": {
            "o": null,
            "h": null,
            "l": null,
            "m": null,
            "s": null
        },
        "can_ban": false,
        "can_converse": false,
        "can_edit": false,
        "can_follow": false,
        "can_ignore": false,
        "can_post_profile": false,
        "can_view_profile": true,
        "can_view_profile_posts": true,
        "can_warn": false,
        "content_show_signature": true,
        "creation_watch_state": "watch_no_email",
        "custom_fields": {
            "skype": "",
            "facebook": "",
            "twitter": ""
        },
        "custom_title": "",
        "email": "mysuperemail@mail.ru",
        "email_on_conversation": false,
        "gravatar": "",
        "interaction_watch_state": "watch_no_email",
        "is_admin": false,
        "is_discouraged": false,
        "is_moderator": false,
        "is_staff": false,
        "is_super_admin": false,
        "last_activity": 1624800266,
        "location": "",
        "message_count": 0,
        "push_on_conversation": false,
        "push_optout": [],
        "reaction_score": 0,
        "receive_admin_email": false,
        "register_date": 1624788945,
        "secondary_group_ids": [],
        "show_dob_date": false,
        "show_dob_year": false,
        "signature": "",
        "timezone": "Europe/Moscow",
        "trophy_points": 0,
        "usa_tfa": false,
        "user_group_id": 5,
        "user_id": 5,
        "user_state": "valid",
        "user_title": "Customer",
        "username": "TestUser",
        "visible": true,
        "website": ""
    }
}
Многие авторы статей на нашем форуме на этом этапе обычно приступают к написанию клиента, который будет отправлять запросы по шаблону от Xenforo. Но я не советую так делать. Как-никак, в заголовках запроса участвует суперключ api форума, который может позволить злоумышленникам брутить пароли пользователей, либо использовать другие его важные методы, которые включены в админ-панеле. Хорошим решением будет использование дополнительной PHP страницы, которая послужит нам посредником в "отношениях" с сервером.

Вот простой код с использованием cURL. Можно было воспользоваться стандартными средствави PHP, но на моем хостинге, к сожалению, это не очень хотело работать.
PHP:
<?php
$url = "http://yourforum.com/api/auth";

$post_data = array (
    'login' => $_POST["login"],
    'password' => $_POST["password"],
);
if (!isset($_POST["login"]) && !isset($_POST["password"]))
    $post_data = array();
if (isset($_POST["login"]) && !isset($_POST["password"]))
    $post_data = array('login' => $_POST["login"]);
if (!isset($_POST["login"]) && isset($_POST["password"]))
    $post_data = array('password' => $_POST["password"]);

$ch = curl_init();

curl_setopt($ch, CURLOPT_HTTPHEADER, array(
    'Accept: application/json',
    'XF-Api-Key: Ключ API'
));

curl_setopt($ch, CURLOPT_URL, $url);

curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);

$output = curl_exec($ch);

curl_close($ch);

echo $output;
?>
Теперь нужно загрузить файл на наш хостинг и проверить его работоспособность с помощью вышеупомянутых сервисов. На этот раз достаточно указать только login и password.

3.1. Написание классов для нашего лоадера.
Для начала стоит позаботиться о правильной десериализации ответа от нашего сервера. Поскольку мы работаем с форматом данных JSON, нам необходимо написать класс, с помощью которого можно спарсить всю неоюходимую информацию. Очень легко это можно сделать с помощью программы, которую я выкладывал около полугода назад - https://yougame.biz/threads/173834/. Выходной файл нужно лишь немного подрежактировать, добавив поддержку отображения ошибок.
Конкретно мой класс можно скачать с пастбина:
Пожалуйста, авторизуйтесь для просмотра ссылки.
.

В принципе, на этом можно было перейти к написанию основного кода, но я считаю правильным добавить сохранение авторизационных данных в реестр компьютера. Конечно, можно было это сделать через дополнительный файл или настройки Settings.settings, но работа с реестром будет гораздо более надежной из-за отсутствия привязки к расположению файла.

C#:
using Microsoft.Win32;
using System;

namespace XenforoLoader
{
    class Session
    {
        public string login { get; private set; }
        public string password { get; private set; }

        public void LoadData()
        {
            RegistryKey directory = Registry.CurrentUser.CreateSubKey("SuperiorLoader");
            login = (string)directory.GetValue("login");
            password = (string)directory.GetValue("password");
            directory.Close();
        }

        public void SaveData(string newLogin, string newPassword)
        {
            login = newLogin;
            password = newPassword;

            RegistryKey directory = Registry.CurrentUser.CreateSubKey("SuperiorLoader");
            directory.SetValue("login", login);
            directory.SetValue("password", password);
            directory.Close();
        }

        public void ClearData()
        {
            login = null;
            password = null;

            RegistryKey directory = Registry.CurrentUser.CreateSubKey("SuperiorLoader");
            directory.SetValue("login", "");
            directory.SetValue("password", "");
            directory.Close();
        }

        public bool isAuthed()
        {
            return !String.IsNullOrEmpty(login) && !String.IsNullOrEmpty(password);
        }

        public Session()
        {
            LoadData();
        }
    }
}
Создавать ключ я советую в разделе HKEY_CURRENT_USER, но можете воспользоваться и HKEY_LOCAL_MACHINE, если хотите избавиться от привязки к пользователю компьютера.

3.2. Написание основной части программы.
Мне не очень хочется заострять внимание на каждой мелочи в коде, поэтому я просто добавлю комментарии к основным моментам в программе:
C#:
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using Newtonsoft.Json;
using System.Threading;

namespace XenforoLoader
{
    class Program
    {
        static void Main(string[] args)
        {
            // Устанавливаем заголовок консоли и выводим логотип нашего лоадера.
            Console.Title = "Loader";
            Console.WriteLine(@" _                   _ _____        ");
            Console.WriteLine(@"| |                   | |___  |    ");
            Console.WriteLine(@"| |     ___   __ _  __| |  / /  _ __");
            Console.WriteLine(@"| |    / _ \ / _` |/ _` |  \  \| '__/");
            Console.WriteLine(@"| |___| (_) | (_| | (_| | __)  | |");
            Console.WriteLine(@"\_____|\___/ \__,_|\__,_|\____/|_|");
            Console.WriteLine("\nyougame.biz | vk.com/ugame | t.me/govthing\n\n");

            // Объявляем переменные с данными о пользователе и сохраненной "сессии". Мы прописали в конструкторе выгрузку данных из реестра, поэтому нет необхомости писать session.LoadData();
            User user = null;
            Session session = new Session();

            // Фактически, мы проверяем строки с логином и паролем на пустоту.
            if (session.isAuthed())
            {
                Console.WriteLine("Saved data found. Trying to log in...");
                string resp = POST("http://irval.yourforum.com/auth.php", $"login={session.login}&password={session.password}"); // Отправляем POST запрос на наш wrapper.
                Response response = JsonConvert.DeserializeObject<Response>(resp); // Десериализация ответа.
                if (response.success == true) // Если success == null, то запрос не удался и мы выведем текст ошибки.
                    user = response.User; // Заносим данные об авторизированном пользователе в user.
                else
                {
                    Console.WriteLine("Cannot auth with saved credentials: " + response.errors.First().message);
                    session.ClearData();
                   
                    Process.Start(Assembly.GetEntryAssembly().Location); // Перезапуск приложения.
                    Process.GetCurrentProcess().Kill();
                }
            }
            else
            {
                string login, password;
                Console.Write("[>] Enter username or email: ");
                login = Console.ReadLine();
                Console.Write("[>] Enter password: ");
                password = Console.ReadLine();

                string resp = POST("http://irval.yourforum.com/auth.php", $"login={login}&password={password}"); // POST запрос на наш wrapper. См. строки 34-45.
                Response response = JsonConvert.DeserializeObject<Response>(resp);
                if (response.success == true)
                {
                    user = response.User;
                    session.SaveData(login, password); // Записываем данные пользователя в реестр.
                }
                else
                    Console.WriteLine("Cannot auth with entered credentials: " + response.errors.First().message);
            }

            if (user == null) // Если мы не передали значение user, т.е. авторизация не удалась.
            {
                Console.ReadKey(); // Читаем один символ, после чего закрываем процесс.
                Process.GetCurrentProcess().Kill();
            }

            Console.WriteLine("You have successfully logged in.");
            Console.Title = $"Loader [{user.username}]"; // Добавлем имя пользователя в заголовок.
            Thread.Sleep(1500);
            Console.Clear();
            Console.WriteLine("Current user information:\n"); // Выводим некоторую информацию о пользователе Xenforo.
            Console.WriteLine($"Username: {user.username}\nEmail: {user.email}\nisStaff: {user.is_staff}\nisBanned: {user.is_banned}");
            string secondaryGroups = user.secondary_group_ids != null && user.secondary_group_ids.Count > 0 ? String.Join(", ", user.secondary_group_ids) : "No groups";
            Console.WriteLine($"MainGroup: {user.user_group_id}\nSecondaryGroups: {secondaryGroups}\nAvatar: {user.avatar_urls.m ?? "null"}");
            Console.Write("\n\nPress Delete to unlogin or any key to exit...");
           
            if (Console.ReadKey().Key == ConsoleKey.Delete) // Прроверка на нажатие Delete.
            {
                session.ClearData(); // Очищаем строки в реестре.
                Console.WriteLine("\nSuccessfully logged out.");
                Thread.Sleep(2500);
            }
            Process.GetCurrentProcess().Kill();
        }

        private static string POST(string Url, string Data) // Функция POST запроса.
        {
            try
            {
                System.Net.WebRequest req = System.Net.WebRequest.Create(Url);
                req.Method = "POST";
                req.Timeout = 100000;
                req.ContentType = "application/x-www-form-urlencoded";
                byte[] sentData = Encoding.GetEncoding(1251).GetBytes(Data);
                req.ContentLength = sentData.Length;
                System.IO.Stream sendStream = req.GetRequestStream();
                sendStream.Write(sentData, 0, sentData.Length);
                sendStream.Close();
                System.Net.WebResponse res = req.GetResponse();
                System.IO.Stream ReceiveStream = res.GetResponseStream();
                System.IO.StreamReader sr = new System.IO.StreamReader(ReceiveStream, Encoding.UTF8);
                //Кодировка указывается в зависимости от кодировки ответа сервера
                Char[] read = new Char[256];
                int count = sr.Read(read, 0, 256);
                string Out = String.Empty;
                while (count > 0)
                {
                    String str = new String(read, 0, count);
                    Out += str;
                    count = sr.Read(read, 0, 256);
                }
                return Out;
            }
            catch (Exception ex)
            {
                File.WriteAllText("crash.log", ex.Message); // Пишем лог-файл с ошибкой запроса.
                return null;
            }
        }
    }
}
Скриншоты работы программы:
1624817732625.png
1624817752184.png
Скачать готовые исходники клиента можно здесь:
Пожалуйста, авторизуйтесь для просмотра ссылки.
.
Отдельное спасибо HORMAN за предоставленный форум, на котором мы еще будем делать много интересного :)
 
Последнее редактирование:
Олдфаг
Статус
Оффлайн
Регистрация
18 Фев 2019
Сообщения
2,825
Реакции[?]
1,852
Поинты[?]
24K
Я не хотел создать лоадер с хорошей защитой, поэтому прошу воздержаться от подобных комментариев. Если хотите соответствующий гайд - пишите мне в лс, посмотрим, что можно сделать.
 
Пользователь
Статус
Оффлайн
Регистрация
28 Фев 2021
Сообщения
591
Реакции[?]
118
Поинты[?]
0
Я не хотел создать лоадер с хорошей защитой, поэтому прошу воздержаться от подобных комментариев. Если хотите соответствующий гайд - пишите мне в лс, посмотрим, что можно сделать.
Давай p защиту, если есть свободное время напишите лоадер с привязкой к сайту. Так чисто для паст)
 
всем прив верите ли вы в призраков ???
Забаненный
Статус
Оффлайн
Регистрация
17 Авг 2018
Сообщения
861
Реакции[?]
338
Поинты[?]
0
Обратите внимание, пользователь заблокирован на форуме. Не рекомендуется проводить сделки.
Полезная темка, жаль что не на плюсах (ненавижу HTTP/S запросы на С++ :rage: :rage: )
 
Олдфаг
Статус
Оффлайн
Регистрация
18 Фев 2019
Сообщения
2,825
Реакции[?]
1,852
Поинты[?]
24K
Полезная темка, жаль что не на плюсах (ненавижу HTTP/S запросы на С++ :rage: :rage: )
Да, к сожалению, на C++ достаточно тяжело написать HTTP клиент, не используя сторонние библиотеки. Варианты с сокетами, конечно, не рассматриваем). Если будет время - обязательно сделаю C++ лоадер-крякми с исходниками.
 
Пользователь
Статус
Оффлайн
Регистрация
28 Фев 2021
Сообщения
591
Реакции[?]
118
Поинты[?]
0
Кстати вопрос, это лоадер с инжектом? Просто я ни одной строки инжекта не увидел ( и скачки дллки с форума )
Да, к сожалению, на C++ достаточно тяжело написать HTTP клиент, не используя сторонние библиотеки. Варианты с сокетами, конечно, не рассматриваем). Если будет время - обязательно сделаю C++ лоадер-крякми с исходниками.
Желательно под хайдом xD, я смотрел прошлые сливы от iDarling были хорошие исходники, но на данный момент не работают, можешь их поченить? NiversoLoader желательно, потому что лоадер красивый )
 
всем прив верите ли вы в призраков ???
Забаненный
Статус
Оффлайн
Регистрация
17 Авг 2018
Сообщения
861
Реакции[?]
338
Поинты[?]
0
Обратите внимание, пользователь заблокирован на форуме. Не рекомендуется проводить сделки.
Да, к сожалению, на C++ достаточно тяжело написать HTTP клиент, не используя сторонние библиотеки. Варианты с сокетами, конечно, не рассматриваем). Если будет время - обязательно сделаю C++ лоадер-крякми с исходниками.
Да, сокеты конечно хорошо, но к ним же надо SSL прикрутить еще
 
Олдфаг
Статус
Оффлайн
Регистрация
18 Фев 2019
Сообщения
2,825
Реакции[?]
1,852
Поинты[?]
24K
Кстати вопрос, это лоадер с инжектом? Просто я ни одной строки инжекта не увидел ( и скачки дллки с форума )
Нет, инжекта в лоадере нет. Функционал обрывается на получении информации о пользователе.
Желательно под хайдом xD, я смотрел прошлые сливы от iDarling были хорошие исходники, но на данный момент не работают, можешь их поченить? NiversoLoader желательно, потому что лоадер красивый )
Посмотрю, что там можно сделать. Но в планах выкатить что-то свое.
 
Олдфаг
Статус
Оффлайн
Регистрация
18 Фев 2019
Сообщения
2,825
Реакции[?]
1,852
Поинты[?]
24K
Да, сокеты конечно хорошо, но к ним же надо SSL прикрутить еще
С одной стороны, это не так уж и сложно. Вообще, использование сокетов - самый простой вариант реализации запроса на C++, но далеко не самый правильный
 
Пользователь
Статус
Оффлайн
Регистрация
28 Фев 2021
Сообщения
591
Реакции[?]
118
Поинты[?]
0
Посмотрю, что там можно сделать. Но в планах выкатить что-то свое.
Хорошо, дизайн просто красивый очень и если посмотреть Web panel , то она тоже красивая и подходит под лоадер ( глаз радуется )
 
[Яifk⁷] > all
Участник
Статус
Оффлайн
Регистрация
4 Июн 2019
Сообщения
472
Реакции[?]
165
Поинты[?]
0
Вот это довольно интересно, правда хотелось бы видеть реализацию на C++
:p
 
Олдфаг
Статус
Оффлайн
Регистрация
18 Фев 2019
Сообщения
2,825
Реакции[?]
1,852
Поинты[?]
24K
Пользователь
Статус
Оффлайн
Регистрация
28 Фев 2021
Сообщения
591
Реакции[?]
118
Поинты[?]
0
О нет, спасибо. Мне кряка РазДва хватило для осознания защиты питона
Напиши мне shit loader с привязкой к панельке ) xD
а че не на бейсике или на паскале?
Паскаль даже не защитит наверное, там сразу напишет ( Я Паскаль крякни меня пожалуйста, вот ссылка на dll )
 
[Яifk⁷] > all
Участник
Статус
Оффлайн
Регистрация
4 Июн 2019
Сообщения
472
Реакции[?]
165
Поинты[?]
0
Паскаль даже не защитит наверное, там сразу напишет ( Я Паскаль крякни меня пожалуйста, вот ссылка на dll )
Ахах, ну а хули, че зря учили его в шкиле? (я, конечно понимаю, что это для того, чтобы понять основы итд)
 
Пользователь
Статус
Оффлайн
Регистрация
28 Фев 2021
Сообщения
591
Реакции[?]
118
Поинты[?]
0
Ахах, ну а хули, че зря учили его в шкиле? (я, конечно понимаю, что это для того, чтобы понять основы итд)
Мы в шкиле питон учим, а не паскаль) Я бы лучше этому информатку этот питон в жо*** запихнул чем учить этот питон ( я питон знаю , щит язык ). С++ лучше чем ваши щит языки). И информация для Ирвала ( что бы не посчитали за 1.1 :D ) сделай пожалуйста какой то щит лоадер для квеста :D, или лоадер с дллкой какого то чита, что бы мы попытались крякнуть
 
[Яifk⁷] > all
Участник
Статус
Оффлайн
Регистрация
4 Июн 2019
Сообщения
472
Реакции[?]
165
Поинты[?]
0
Мы в шкиле питон учим, а не паскаль) Я бы лучше этому информатку этот питон в жо*** запихнул чем учить этот питон ( я питон знаю , щит язык ). С++ лучше чем ваши щит языки)
Ну, нам сначала дали "строителя", кирпичи ебашили (для понятия, как создаются алгоритмы), потом нам дали ебаный паскаль.

Упд: я понимаю, если бы дали C#, как мне показалось, он средней сложности.
 
Сверху Снизу