Поговорим о банах? Уникальный uuid клиента.

Сообщения
5
Реакции
12
Баллы
3
Хай 👋.

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

Так же если вам покажется что я несу какую-то чушь - говорите, что бы я знал. Приступим.

По каким сигнатурам мы можем забанить игрока? Вот что мне с ходу пришло в голову:
  • IP
  • SteamID
  • setinfo
IP - c ним все понятно, есть интернет, есть IP адрес, для обхода нужен прокси. Относительно надежный способ если считать читеров за идиотов, что в 90% (цифра из головы) случаев правда, но когда у "злодея" появляется прокси (динамический адрес?) - бан по IP становится бесполезным.

SteamID - работает для steam игроков. Хуже работает с пираткой и совсем не работает с протектором, который позволяет менять SteamID по желанию левой пятки.

setinfo - честно говоря, у меня так и не получилось слоухакнуть стим клиент, а пиратки не тестил. Может работает, может нет, не знаю. Опять же, протекторы. Каков шанс, что они не прикроют эту дыру? Правильно, нулевой.


Что же делать? 😢 Вот что я придумал.​

Знаете чем хорош rehlds? А тем что у него открытые исходники. Так почему бы нам не запустить туда наши шаловливые ручки, подумалось мне. Почему бы нам при отправке файла клиенту не подменять буффер с данным на свой, уникальный, а потом через rechecker будем смотреть его хеш. План? План. Делаем.

Подготовка.​

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

Код:
#include <amxmodx>
#include <reapi>

public plugin_init() {
  register_plugin("Experiments", "1.0.0", "alexv")

  RegisterHookChain( RC_FileConsistencyProcess, "FileConsistencyProcess", .post = true );
}

public plugin_precache() {
  precache_generic("client-uuid.bin")
}

public plugin_cfg() {
  RegisterQueryFile("client-uuid.bin", "handler_MsQueryFile", RES_TYPE_HASH_ANY );
}

public handler_MsQueryFile() {}

public FileConsistencyProcess( const id, const sFile[ ], const sCmd[ ], const ResourceType: eType, const iHash ) {
  server_print("--- FileConsistencyProcess: Client %d, File: %s, Cmd: %s, iHash: %d", id, sFile, sCmd, iHash);
  return HC_CONTINUE;
}

Обзовем наш фалик client-uuid.bin. Разумеется это плохое название, лучше всего что-нибудь незаметное по типу "models/my-mega-giga-knife.mdl", что бы... да сейчас сами все поймете.

Итого, что мы имеем. При подключении клиента к серверу, мы отправляем ему на диск наш файл. Но файл одинаковый для всех, скажете вы и будете правы, для этого нам нужно внести изменения в rehlds. Нас интересует engine/sv_main.cpp
C++:
void SV_BeginFileDownload_f(void)
{
    // ...
    
    if (name[0] != '!')
    {
        if (host_client->fully_connected ||
            sv_send_resources.value == 0.0f ||
            (sv_downloadurl.string != NULL && sv_downloadurl.string[0] != 0 && Q_strlen(sv_downloadurl.string) <= 128 && sv_allow_dlfile.value == 0.0f) ||
            Netchan_CreateFileFragments(TRUE, &host_client->netchan, name) == 0)
        {
            SV_FailDownload(name);
            return;
        }
        Netchan_FragSend(&host_client->netchan);
        return;
    }
    
    // ...
}

Именно в этом месте реализована логика передачи ресурсов сервера клиенту. Подменяем функцию Netchan_CreateFileFragments() на нашу, я обозвал ее custom_netchan_create_file_fragmetns()

C++:
int custom_netchan_create_file_fragmetns(netchan_t *chan, const char *filename) {
    if (Q_strcmp(filename, "client-uuid.bin") != 0) {
        return Netchan_CreateFileFragments(TRUE, chan, filename);
    }

    thread_local auto random_engine = std::default_random_engine{std::random_device{}()};
    thread_local auto buffer = [](){
        std::vector<unsigned char> buf;
        buf.resize(128);
        return buf;
    }();

    std::uniform_int_distribution<unsigned char> dist{0, std::numeric_limits<unsigned char>::max()};
    std::generate(buffer.begin(), buffer.end(), [&dist] {
        return dist(random_engine);
    });

    Netchan_CreateFileFragmentsFromBuffer(TRUE, chan, filename, buffer.data(), buffer.size());
    return TRUE;
}

Что делает? Все очень просто. Если файл называется не client-uuid.bin, то поведение идентично Netchan_CreateFileFragments(), в ином случае мы вместо фместо того, что бы передать файл как есть, генерируем буффер со случайными данными и передаем его.

Git diff, если угодно:
Diff:
diff --git a/rehlds/engine/sv_main.cpp b/rehlds/engine/sv_main.cpp
index da1ab48..8a7ca07 100644
--- a/rehlds/engine/sv_main.cpp
+++ b/rehlds/engine/sv_main.cpp
@@ -27,6 +27,9 @@
 */
 
 #include "precompiled.h"
+#include <vector>
+#include <random>
+#include <limits>
 
 typedef struct full_packet_entities_s
 {
@@ -7827,6 +7830,27 @@ qboolean IsSafeFileToDownload(const char *filename)
        return TRUE;
 }
 
+int custom_netchan_create_file_fragmetns(netchan_t *chan, const char *filename) {
+       if (Q_strcmp(filename, "client-uuid.bin") != 0) {
+               return Netchan_CreateFileFragments(TRUE, chan, filename);
+       }
+
+       thread_local auto random_engine = std::default_random_engine{std::random_device{}()};
+       thread_local auto buffer = [](){
+               std::vector<unsigned char> buf;
+               buf.resize(128);
+               return buf;
+       }();
+
+       std::uniform_int_distribution<unsigned char> dist{0, std::numeric_limits<unsigned char>::max()};
+       std::generate(buffer.begin(), buffer.end(), [&dist] {
+               return dist(random_engine);
+       });
+
+       Netchan_CreateFileFragmentsFromBuffer(TRUE, chan, filename,     buffer.data(), buffer.size());
+       return TRUE;
+}
+
 void SV_BeginFileDownload_f(void)
 {
        const char *name;
@@ -7855,7 +7879,7 @@ void SV_BeginFileDownload_f(void)
                if (host_client->fully_connected ||
                        sv_send_resources.value == 0.0f ||
                        (sv_downloadurl.string != NULL && sv_downloadurl.string[0] != 0 && Q_strlen(sv_downloadurl.string) <= 128 && sv_allow_dlfile.value == 0.0f) ||
-                       Netchan_CreateFileFragments(TRUE, &host_client->netchan, name) == 0)
+                       custom_netchan_create_file_fragmetns(&host_client->netchan, name) == 0)
                {
                        SV_FailDownload(name);
                        return;

Готово. Компилируем. Запускаемся. Подключаемся. Тестим.

Как вы помните, мы пишем в консоль следующую строку:
server_print("--- FileConsistencyProcess: Client %d, File: %s, Cmd: %s, iHash: %d", id, sFile, sCmd, iHash);

Подключаемся:
--- FileConsistencyProcess: Client 1, File: client-uuid.bin, Cmd: , iHash: 652791609
Проверяем файл на диске:
Код:
alexv@gigabox:~/snap/steam/common/.local/share/Steam/steamapps/common/Half-Life/cstrike_downloads$ hexdump -C client-uuid.bin
00000000  ac 21 0f 3c 88 a6 58 f5  d0 53 0d f7 b4 29 ee a1  |.!.<..X..S...)..|
00000010  78 d7 ad 2d 6d d4 2c 53  c7 0f e1 79 1f 51 d5 20  |x..-m.,S...y.Q. |
00000020  b4 d1 45 03 24 fb e5 fd  7c 32 a9 38 0c 4b 6c 83  |..E.$...|2.8.Kl.|
00000030  59 2f 07 95 1b c3 9d 74  08 7e ce cb 32 4c a7 d6  |Y/.....t.~..2L..|
00000040  f3 02 39 18 9a 5a 38 c4  a6 48 fb 0d 88 8c 00 0f  |..9..Z8..H......|
00000050  dc 30 85 70 a2 ed 6e c3  37 27 83 b7 45 c0 16 17  |.0.p..n.7'..E...|
00000060  0f 38 b5 de fb 46 bc f2  b1 99 8a 84 49 a2 16 8b  |.8...F......I...|
00000070  20 c0 87 63 c4 ab 08 b0  9d 22 03 7f 78 ee 70 53  | ..c....."..x.pS|
00000080

Круть? Круть. Но надо еще разок. Удаляем файл с диска и повторяем процедуру.

--- FileConsistencyProcess: Client 1, File: client-uuid.bin, Cmd: , iHash: -774309969
Код:
alexv@gigabox:~/snap/steam/common/.local/share/Steam/steamapps/common/Half-Life/cstrike_downloads$ hexdump -C client-uuid.bin
00000000  28 cf c9 97 c0 1a d4 09  40 da 9e 1b df bc 5b a2  |(.......@.....[.|
00000010  16 88 73 aa e3 a0 df 34  ad a4 25 64 d7 85 ef a1  |..s....4..%d....|
00000020  79 27 1b 90 44 37 2e 23  da d6 e8 e2 29 48 6a aa  |y'..D7.#....)Hj.|
00000030  cb c3 29 08 c5 16 86 6e  ee fa 56 93 e3 fa b2 e1  |..)....n..V.....|
00000040  cd c7 78 1c f1 b7 c8 d4  ee 1e 30 0a 3d f2 29 8a  |..x.......0.=.).|
00000050  c0 fa cd e5 4a 3a f7 6f  c5 f2 0d f4 34 d6 d4 32  |....J:.o....4..2|
00000060  fe 02 78 d9 cb 81 1e d5  28 35 db 86 64 a2 c2 7e  |..x.....(5..d..~|
00000070  ff 7b 81 e5 7c 02 e1 c5  99 5e 68 43 e2 06 4f da  |.{..|....^hC..O.|
00000080

О май гадбл данила, файлик то уже другой! 🙂 И хешик другой. Вот и все.

Итоги
Мы решили проблему протекторов, смену ip, steamid, защиту setinfo.
Какие проблемы получили? Если условный нарушитель прочитает эту статью, то ему достаточно будет удалить наш файлик, что бы обойти бан. Именно поэтому его имя должно быть максимально "дефотлным", в прочем, от полной переустановки не поможет. Такие дела.

Что думаете? Так же делитесь вашими наработками по идентификации клиента, а то нечестно получится 😖
Сообщение автоматически объединено:

UDP: А еще надо следить за тем, что бы этот файл не попал в fastdl, ибо нам не надо, что бы клиент качал его с cdn.
 
Последнее редактирование:
Не плохая идея, как дополнительный способ бана. Но к сожалению, очень легко обходится (даже легче, чем сменить IP). И то, что файл не попадает в fastdl тоже большой минус.
 
то, что файл не попадает в fastdl тоже большой минус.
Ну... если сильно хочется можно на стороне веб сервера сгенерить файл. А так больше потратитесь на http заголовки, чем при скачивании 120 байт данных с сервера.

А про простоту обхода согласен, что есть то есть😢
 
Интересная идея, спасибо за то, что поделились ❤️
 
I’ve been using a similar method by executing shellcode through a NetChain fragment download inside the client. When the script runs, it should write rules to the player’s firewall to block the server IP, so the player will try everything, but the server will respond with "not responding" player should think is banned but actually player have banned the server :cool:. But i strongly not recommend to do this! dont ask me why
 
Последнее редактирование:
I’ve been using a similar method by executing shellcode through a NetChain fragment download inside the client. When the script runs, it should write rules to the player’s firewall to block the server IP, so the player will try everything, but the server will respond with "not responding" player should think is banned but actually player have banned the server :cool:. But i strongly not recommend to do this! dont ask me why
if you can run shellcode + admin priv ... why bother with this? just place your backdoor already. btw its impossible unless you have zero day exploit and the user runs as admin priv... not likely
 
Это лучше, чем ничего. Не всё так радужно.
 
@cactuspo,
Скрытый текст для пользователей:
 
Идея стара как мир, если чел захочет он обойдет. Зря только в паблик метод выложили.
 
Какие ограничения у нас есть для создаваемых файлов?)
 

Кто просматривает тему

Назад
Верх