---
Миф о защищенности мотра родился очень давно, и с подачи дядьки спака, который кстати совсем недавно хвастался, что мол «раньше у нас защита была похожа на тележку с ручкой, а теперь это как реактивный самолет с турбодвигателем», сей миф кочует из уст в уста. Что-ж, статья для тех, кто желает на практике поглядеть, как устроен «самолет с турбиной» %)
Часть 1. ruro.exe
Руро написан задней левой лапой. Это так же очевидно, как и дважды два - четыре. Мало того, что Расул (которому, по древнему преданию, принадлежит авторство руро) поленился хотя бы включить какую-нибудь оптмизацию при сборке (это, впрочем, самый простительный его грех, да и к лучшему - он играет нам на руку, ибо хорошо видно и структуру кода, и что забавно - допущенные ошибки), так кроме того, руро изобилует неиспользуемым и неработающим кодом, а в работающем (почему-то :)) коде допущены тривиальные ошибки типа “if ( x = 100 )”, которые проходят во втором классе. (к чести Расула тот исправил их весьма оперативно. Месяца за два :)). Более того, большинство алгоритмов, которые он применяет, – либо сдвинутые на голову, либо неэффективные, либо нерациональные. А большинство баек о руро, в большинстве своем, не соответствуют действительности. Но это лирическое отступление.
Руро - это своеобразный загрузчик, патчер и античит в одном флаконе. Итак, начнем ковырять с начала – что же скрывается внутри этой хитрой программы, так мешающей нам жить? Если вы захотите открыть руро в отладчике, то скорее всего, ничего осмысленного не увидите. Если вы немного искушеннее обычного пользователя в части знаний в программировании, то вы можете предположить, что екзешник, скорее всего, упакован каким-то паковщиком. Это действительно так, и этот паковщик не UPX, как можно было бы подумать, увидев знакомые буквы в заголовке, а – FSG версии 2.0. Так что, если вы обладаете адвансед скиллами, то наверное даже сможете протрейсить экзешник до получения рабочего образа программы. Но гораздо удобнее иметь уже распакованный образ, и в этом поможет один из имеющихся в сети распаковщиков FSG, они более или менее корректно работают, по крайней мере экзешник потом нормально запускается и работает, несмотря на некоторые глючки. :)
Владеющий распакованным экзешником обладает большой силой :) однако, для ее обладания потребуется открутить от руро проверку собственного размера, и если захочется запускать руро под отладчиком – проверку на отладчик (коих там, по сути, две – на стандартный системный отладчик, и на софтайс). Откручивание проверок заключается как обычно в замене одного байта. Как вы думаете, зачем нужна проверка на софтайс, которая откручивается заменой оператора ? :)
Следующий вопрос, который нам интерсен – “ну и как эта хрень работает?”. Так вот. В задачи руро входит запуск апдейтера (при необходимости), запуск процесса ragexe.exe, патч нужных мест в коде процесса, и – собственно, осуществление работы этого процесса. Кто-то наверняка помнит очередные байки администрации в ключе “наш патчер – это не просто патчер – это умный патчер, который сам находит места патчей, и адаптируется под разные клиенты”. В части это действительно правда – это не просто патчер. :) А в остальном администрация как всегда наврала. Руро жестко привязан к текущей версии ragexe, и просто не будет работать ни с каким другим клиентом. По крайней мере, до тех пор, пока не скачает апдейтером ту версию, для которой он предназначен.
Ключевой момент в руро – это то, что это не просто патчер, а патчер в режиме отладчика. Другими словами, руро выступает в роли дебаггера для процесса ragexe. Кто разбирается в программировании – сразу понимают, что это, фактически, неограниченная власть над процессом. Все байки администрации мотра, мол “мы не можем модифицировать клиент”, “мы не можем исправить ошибки клиента” – все это полнейшая чушь. Руро позволяет делать с клиентом абсолютно все, что угодно. И делает это.
Грубо говоря, для своей работы руро расставляет в нужных местах брекпойнты, и корректирует поведение клиента в нужную ему сторону. Через руро проходит загрузка и выгрузка процессом (ragexe) библиотек (системных, и не только), запуст потоков, и тд. Где-то в этом механизме кроется защита от инжектора xkore, и еще несколько сигнатур каких-то редиректоров. Более подробно я этот момент не исследовал, так что конкретно что там делается, сказать не могу. Но если исключить изменение сетевых пакетов, то защита от ботов в руро заканчивается на поиске сигнатур типа “x-kor”, “edire”, и им подобных в библиотеках ragexe.
Основная же работа руро, котрой он занят большинство времени, пока висит в памяти – это патч протокола обмена с сервером. Как вы думаете, почему мотровский клиент не совместим с аутпостом? Именно благодаря своему эксклюзивному механизму, основанном на отладке. Расставив брекпойнты в нужных местах, руро ждет, пока ragexe в них не попадет, изменяет нужным образом регистры и память процесса, после чего возвращает управление. Таких мест не очень много, и их все можно легко вычислить, запустив руро в дебаггере. Их тех, которые ковырял – это момент логина (пакет 0x64), передвижение перса (пакет 0x85) (именно здесь руро по странной причине не переваривает аутпост), атака (пакет 0x89), и использование предметов (не помню уже какой пакет). Но наверняка это не все, и особенности обмена еще вылезут не раз. То есть, грубо говоря, если в ragexe есть код отсылки пакета на сервер типа
Код: Выделить всё
buf[2] = x;
buf[3] = y;
send(buf, 5);
Часть 2. Пакет перемещения.
Дисклаймер: все ниже описанные алгоритмы относятся к предыдущей (примерно до мая что-ли) версии руро, и приведены для ознакомления. В данный момент алгоритмы генерации чисел для шифрования изменены.
Каждый раз, когда вы тыкаете в клиенте мышой на землю, на сервер отсылается пакет перемещения с координатами X Y куда надо пойти. Вот так:
Код: Выделить всё
0x85 0x00 bXXXXXXXX bXXYYYYYY bYYYY****
Итак, 10 бит. Чистые координаты передаются везде, кроме мотра. Если запустить сниффер на мотре, то все, что мы увидим, это что передаются некоторые числа, отдаленно напоминающие координаты. Проведя минут 10 над ловлей пакетов, можно догадаться, что координаты кодируются примерно следующим образом:
Код: Выделить всё
hx = h ^ y;
hy = hy ^ 0x18D;
Код: Выделить всё
void hash_coords(unsigned int x, unsigned int y, unsigned int* hx, unsigned int *hy)
{
unsigned int hval;
*hx = 0;
*hy = 0;
hval = hash_int(x, 0, 1, 0x00, 0xFFF); *hx += hash_table[ hval ];
hval = hash_int(x, 1, hval, 0x2C, 0x3FC); *hx += hash_table[ hval ];
hval = hash_int(x, 2, hval, 0x5E, 0x3FC); *hx += hash_table[ hval ];
hval = hash_int(x, 3, hval, 0x2B, 0x3FC); *hx += hash_table[ hval ];
hval = hash_int(x, 4, hval, 0x39, 0x3FC); *hx += hash_table[ hval ];
hval = hash_int(x, 5, hval, 0x53, 0x3FC); *hx += hash_table[ hval ];
hval = hash_int(x, 6, hval, 0x41, 0x3ED); *hx += hash_table[ hval ];
hval = hash_int(x, 7, hval, 0x4E, 0x3FC); *hx += hash_table[ hval ];
hval = hash_int(x, 8, hval, 0x0B, 0x3FC); *hx += hash_table[ hval ];
hval = hash_int(y, 0, 1, 0x00, 0x3FC); *hy += hash_table[ hval ];
hval = hash_int(y, 1, hval, 0x22, 0x3FC); *hy += hash_table[ hval ];
hval = hash_int(y, 2, hval, 0x40, 0x3FC); *hy += hash_table[ hval ];
hval = hash_int(y, 3, hval, 0x20, 0x3ED); *hy += hash_table[ hval ];
hval = hash_int(y, 4, hval, 0x50, 0x3FC); *hy += hash_table[ hval ];
hval = hash_int(y, 5, hval, 0x17, 0x3ED); *hy += hash_table[ hval ];
hval = hash_int(y, 6, hval, 0x2E, 0x3FC); *hy += hash_table[ hval ];
hval = hash_int(y, 7, hval, 0x3A, 0x3FC); *hy += hash_table[ hval ];
hval = hash_int(y, 8, hval, 0x11, 0x3FC); *hy += hash_table[ hval ];
*hx = x | ((*hx & 0x01) << 9);
*hy = y | ((*hy & 0x01) << 9);
}
static unsigned int hash_int(unsigned int x, int shift, unsigned int prev, int c, int div)
{
unsigned int m = (x >> shift) & 1;
return ((m+1) * prev + c) % div ;
}
НО расул кроме всего прочего умудрился составить хеш-таблицу таким образом, что она один раз из трех тупо периодическая!
Впрочем, что такое хеш-таблица? Это просто массив случайных значений. Не столь важно каких, главное что не предсказуемых. Генератор:
Код: Выделить всё
static unsigned char hash_table[0x400];
magic = data[2] | (data[3]<<8);
for( i = 0; i < 0x400; i++) {
magic = (magic << 1) ^ (magic >> 8) ^ (magic << 2) ^ (magic * 3);
hash_table[i] = magic & 0xFF;
}
Код: Выделить всё
0x64 00 ML MH …….
Вот и все в общих чертах.
Итого: есть некоторое случайное число, которое генерится каждый раз при логине, и передается на сервер. По этому числу генерится хеш-таблица, и с этой таблицей патчится старший бит координат пакета перемещения. Эта таблица еще пригодится нам в дальнейшем. Не забывайте ее :)
Идея вобщем, на четверочку, даже с минусом, реализация – я промолчу. Даже офф с их примитивным паддингом впереди планеты всей
Часть 3. Кто убил Лору Палмер?
Последний на повестке дня у нас пакет “атаки”. Вот такой:
Код: Выделить всё
0x89 0x00 ID.1 ID.2 ID.3 ID.4 TYPE
Код: Выделить всё
void hash_ids(unsigned int *id, char *type)
{
unsigned char i;
i = 0;
i = (*id & 0x01) << 3;
i = i | ((*id >> 1) & 0x01) << 2;
i = i | ((*id >> 3) & 0x01) << 1;
i = i | ((*id >> 5) & 0x01);
i = i ^ (hash_table[(i % 0x3FC)] & 0x0F);
i = ((i ^ 0x0A) << 4);
*type = *type | i;
*type = * type ^ (*id & 0xFF);
*id = *id ^ 0x83FA229C;
}