Лисья Нора

Оглавление


§ Старт

shot.png
– Так, интересно, а подойдет ли пароль от Утиных Истории 2, тот самый, который 13324124? Утенок ведь ерунды не скажет! Попробуем набрать, что ли. Так... Ух ты! Открылся! Вот дела... 13 уровень? 81 минута осталась, что за?...
Как же так то? Почему он набрался и подошел?
В детстве я задавал себе этот вопрос и не мог найти ни единого разумного ответа. Это казалось какой-то фантастикой, невероятным совпадением и глэдстоунским везением. Да и я сейчас так считаю, на самом деле.
Так как же на самом деле работают эти пароли? Все оказалось не настолько сложно, как я думал.

§ Позиции цифр

digits.png
Всего в пароле 8 цифр, каждая из которых кодирует некоторую свою часть. Есть две цифры, которые кодируют номер уровня – на позиции 1 и 7, две цифры, которые кодируют время – на позиции 0 и 3. Максимальное время может быть от нуля до 99, а максимальный уровень – 16. Причем, что интересно, уровень 15 и 16 – не существуют и представляют из себя лишь мусор. Попасть на них можно лишь только набрав код. Почему тогда они существуют? Это баг игры, при разработке игры никто даже и представить не мог, что код будет расшифрован и использован, потому защиту от переполнения делать, естественно, никто не стал.
Если же кодировать надо лишь время и уровень, а это 4 цифры, то что делают остальные 4? Оказывается, там есть такой параметр, который состоит из двух цифр, называется random, это некоторое случайное число, которое примешивается к коду и занимает позиции 2 и 5.
А что делают остальные 2 оставшиеся цифры на позиции 4 и 6? А там как раз содержится контрольная сумма для того, чтобы проверить, что введенный код был сформирован правильно и что тот, кто его набирает, не жульничал. Ведь без контрольного кода в любом случае на какой-нибудь уровень с неким рандомным временем, да было бы попадание. А тут шанс ровно в 99% процентов, что введенный от балды код будет ошибочным.

§ Контрольная сумма

Поскольку мы разобрались с тем, на каких позициях что находится, теперь приступим к самому интересному – это раскодированию пароля от уровня.
Первым делом надо проверить то, что сумма цифр не является нулями. Если это так, то пароль не подходит сразу же. Вторым этапом необходимо проверить контрольную сумму, сосчитать все нужные цифры и сверить, что пароль корректен.
Получится некоторое значение. Как можно отметить, не были использованы в поиске контрольной суммы числа в позиции 4 и 6. Это все потому, что там находится значение контрольной суммы, с которой как раз и сверять будем, а иначе бы просто никогда контрольную сумму свести нельзя было бы. Это примерно из того рода, чтобы сделать равным уравнение x + y = x, при этом y не равен 0.
Итак, теперь надо проверить. В позиции 6 будет старший десятичный разряд суммы, а в 4-й позиции – младший разряд. Допустим, если в позиции 6 будет число 3, а в 4-й позиции число 1, то тогда контрольная сумма для сверки будет числом 31.
Если число совпадает с высчитанным ранее значением, то пароль является правильным и начинается его раскодирование.

§ Раскодирование уровня и времени

Здесь не так все просто, хотя проблема скорее не в том, что это очень уж сложно, а тут больше имеем дело с кольцами вычетов по модулю 10. Хотя, так сказал, что сам не понял.
Разберемся по шагам.
Как можно заметить, на самом деле L1 и L2 это части одного и того же числа. L2 – это младшие 2 бита уровня, а L1 – это старшие 2 бита. Уровень же кодируется 4 битами, но они специально разнесены на 2 десятичные цифры, чтобы запутать.
Время кодируется аналогичным образом, что и уровень.
Код, которые реализует декодирование:
int prince_decode(const char password[], int& level, int& time)
{
int sum = 0;
int t[8];
for (int i = 0; i < 8; i++)
{
t[i] = password[i];
if (t[i] >= '0') t[i] -= '0';
sum += t[i];
}
 
if (!sum) return 0; //code 00000000
 
int r1 = t[2];
int r2 = t[5];
 
int n1 = t[0] + t[1] + r1 + t[3];
int n2 = n1 + (n1 % 10) + t[5];
int n3 = n2 + (n2 / 10) + t[7];
int valid = (t[6]*10 + t[4]) == (n3);
 
if (!valid) {
return 0;
}
 
int level1 = ((10 + (t[7] - r2)) % 10) << 2;
int level2 = ((10 + (t[1] - r1)) % 10) & 15;
level = ((level1 + level2) & 15);
 
int time1 = ((10 + (t[0] - r1)) % 10)*10;
int time2 = ( 10 + (t[3] - r2)) % 10;
time = (time1 + time2);
 
return 1;
}

§ Кодирование

А теперь давайте сделаем обратный ход и закодируем в пароль из заранее известных номера уровня L (от 0 до 15) и времени T (от 0 до 99).
Номер уровня закодирован успешно. Теперь кодируем время.
Шесть и восьми цифр заполнено. Теперь осталось найти контрольную сумму. Алгоритм поиска контрольной суммы абсолютно идентичен тому, что разбиралось ранее. Я просто представляю его код:
n1 = t[0] + t[1] + t[2] + t[3];
n2 = n1 + (n1 % 10) + t[5];
n3 = n2 + (n2 / 10) + t[7];
То есть, сначала считаются 4 первые цифры, потом полученную сумму складываем с младшим десятичным разрядом и прибавляем +t[5], и эту сумму складываем со старшим разрядом и добавляем +t[7].
Старший разряд (десятки), помещаются в t[6], а младший – в t[4]. Пароль готов!

§ Получение пароля по параметрам

Представлены параметры level, time и random (любое число). Результат будет записал в аргумент t.
void prince_encode(int level, int time, int random, char t[])
{
int r1 = random / 10;
int r2 = random % 10;
 
t[2] = r1;
t[5] = r2;
 
t[1] = ((level & 3) + r1) % 10;
t[7] = ((level >> 2) + r2) % 10;
 
t[0] = ((time / 10) + r1) % 10;
t[3] = ((time % 10) + r2) % 10;
 
int n1 = t[0] + t[1] + r1 + t[3];
int n2 = n1 + (n1 % 10) + t[5];
int n3 = n2 + (n2 / 10) + t[7];
 
t[4] = (n3 % 10);
t[6] = (n3 / 10);
 
// Если необходимо перевести строку в ASCII
for (int i = 0; i < 8; i++) t[i] += '0'; t[8] = 0;
}

§ Пример функции main

int main(int argc, char* argv[]) {
 
int level, time;
char t[9];
 
// Генератор пароля
prince_encode(2, 51, 33, t);
 
printf("PASSWORD: %s\n", t);
 
// Декодирование пароля
if (prince_decode(t, level, time)) {
printf("LEVEL=%d TIME=%d\n", level, time);
} else {
printf("PASSWORD INVALID\n");
}
 
return 0;
}

§ Кодер и декодер online