Хай
.
На самом деле эта тема создана ради того, что бы собрать способы которыми можно идентифицировать клиента, если у вас есть наработки, которыми вы не хотите делиться - пишите в личку
(но лучше публично). Я в долгу не останусь, я придумал такой придолдес, которым с вами сейчас поделюсь.
Так же если вам покажется что я несу какую-то чушь - говорите, что бы я знал. Приступим.
По каким сигнатурам мы можем забанить игрока? Вот что мне с ходу пришло в голову:
SteamID - работает для steam игроков. Хуже работает с пираткой и совсем не работает с протектором, который позволяет менять SteamID по желанию левой пятки.
setinfo - честно говоря, у меня так и не получилось слоухакнуть стим клиент, а пиратки не тестил. Может работает, может нет, не знаю. Опять же, протекторы. Каков шанс, что они не прикроют эту дыру? Правильно, нулевой.
Что же делать?
Знаете чем хорош rehlds? А тем что у него открытые исходники. Так почему бы нам не запустить туда наши шаловливые ручки, подумалось мне. Почему бы нам при отправке файла клиенту не подменять буффер с данным на свой, уникальный, а потом через rechecker будем смотреть его хеш. План? План. Делаем.
Обзовем наш фалик client-uuid.bin. Разумеется это плохое название, лучше всего что-нибудь незаметное по типу "models/my-mega-giga-knife.mdl", что бы... да сейчас сами все поймете.
Итого, что мы имеем. При подключении клиента к серверу, мы отправляем ему на диск наш файл. Но файл одинаковый для всех, скажете вы и будете правы, для этого нам нужно внести изменения в rehlds. Нас интересует engine/sv_main.cpp
Именно в этом месте реализована логика передачи ресурсов сервера клиенту. Подменяем функцию Netchan_CreateFileFragments() на нашу, я обозвал ее custom_netchan_create_file_fragmetns()
Что делает? Все очень просто. Если файл называется не client-uuid.bin, то поведение идентично Netchan_CreateFileFragments(), в ином случае мы вместо фместо того, что бы передать файл как есть, генерируем буффер со случайными данными и передаем его.
Git diff, если угодно:
Готово. Компилируем. Запускаемся. Подключаемся. Тестим.
Как вы помните, мы пишем в консоль следующую строку:
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
Проверяем файл на диске:
Круть? Круть. Но надо еще разок. Удаляем файл с диска и повторяем процедуру.
--- FileConsistencyProcess: Client 1, File: client-uuid.bin, Cmd: , iHash: -774309969
О май гадбл данила, файлик то уже другой!
И хешик другой. Вот и все.
Итоги
Мы решили проблему протекторов, смену ip, steamid, защиту setinfo.
Какие проблемы получили? Если условный нарушитель прочитает эту статью, то ему достаточно будет удалить наш файлик, что бы обойти бан. Именно поэтому его имя должно быть максимально "дефотлным", в прочем, от полной переустановки не поможет. Такие дела.
Что думаете? Так же делитесь вашими наработками по идентификации клиента, а то нечестно получится
UDP: А еще надо следить за тем, что бы этот файл не попал в fastdl, ибо нам не надо, что бы клиент качал его с cdn.
На самом деле эта тема создана ради того, что бы собрать способы которыми можно идентифицировать клиента, если у вас есть наработки, которыми вы не хотите делиться - пишите в личку
Так же если вам покажется что я несу какую-то чушь - говорите, что бы я знал. Приступим.
По каким сигнатурам мы можем забанить игрока? Вот что мне с ходу пришло в голову:
- IP
- SteamID
- setinfo
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.
Последнее редактирование: