Это был еще один рабочий день, когда наш коллега сбросил ссылку на игру под названием Wordle. На тот момент я никогда не слышал об игре, можно сказать, я жил под скалой, но на нашей работе стало обычным делом начинать день. Теперь, будучи любопытным разработчиком, я решил взглянуть на исходный код веб-сайта и нашел много интересных вещей, которыми хотел бы поделиться с вами.

В этой статье мы узнаем об реверсировании javascript веб-сайта, а затем, используя то, что мы узнали, мы создадим скрипт, который будет получать для нас ежедневный ответ (когда мы, очевидно, застряли).

Получение исходного кода игры

Прежде чем мы сможем написать скрипт, который доставит нам слово, нам нужно понять, как игра работает внутри. Благодаря замечательным инструментам разработчика, встроенным в современные веб-браузеры, это довольно просто.

Сначала мы откроем инструменты разработчика в Google Chrome. Для этого вы можете нажать F12 на клавиатуре. После открытия у нас будет несколько вкладок, нам нужна вкладка «источники». На этом этапе у вас должна быть страница, которая выглядит следующим образом:

Теперь вы заметите, что код представляет собой минимизированный беспорядок, который не читается. Хорошей новостью является то, что в Chrome есть встроенный инструмент, который поможет и в этом! Если вы посмотрите прямо над исходным кодом, вы найдете синюю кнопку с надписью «Pretty-print». Нажмите на эту кнопку и наблюдайте, как происходит волшебство, когда код становится читаемым!

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

Понимание кода игры

На этом этапе у вас должен быть открыт редактор кода с исходным кодом игры. Прежде чем мы сможем написать наш сценарий решения, нам нужно выяснить, как работает исходный код игры. Первое, что вы заметите, это то, что хотя код отформатирован так, чтобы его можно было прочитать, имена функций и переменных не имеют смысла. Это связано с тем, что код прошел обфускацию из того, что выглядит как webpack. Это не имеет большого значения, поскольку нам все равно, как называются вещи, но об этом нужно знать.

Для начала рассмотрим скрипт. Есть несколько подходов, которые мы можем использовать, но самый простой в данном случае — это просто поиск полезных слов. В нашем случае поищем в коде слово «решение».

Пролистав результаты, вы наткнетесь на действительно интересный фрагмент кода, где они устанавливают e.solution в значение функции Da(e.today).

Давайте проследим это и выясним, что он делает! Найдите код функции Da(). Как только вы найдете его, вы увидите такой код:

function Da(e) {
    var a, s = Ga(e);
    return a = s % La.length,
        La[a]
}

Эта функция вызывает другую функцию Ga(e) (к которой мы вернемся через секунду), а также возвращает значение, используя переменную La. Давайте начнем с La, чтобы увидеть, правильная ли это функция. Если вы будете искать переменную La, вы в конечном итоге найдете переменную, которая выглядит так:

Это похоже на список слов для меня! Это говорит нам о том, что мы, вероятно, находимся в правильной функции. Давайте создадим новый скрипт с именем index.js, куда мы будем сбрасывать наши выводы, чтобы создать наш скрипт. Возьмите переменную La и вставьте ее в свой скрипт. Пока вы это делаете, возьмите функцию Da и также вставьте ее. У вас должно получиться что-то вроде этого:

var La = [
    "cigar",
    "rebut",
    // truncated for readability...
];

function Da(e) {
    var a,
        s = Ga(e);
    return (a = s % La.length), La[a];
}

Теперь, если вы помните, функция Da также вызывает функцию Ga, так что нам, вероятно, следует выяснить, что она делает, давайте найдем ее!

function Ga(e) {
    return Na(Ha, e)
}

Эта функция очень проста, она принимает параметр e, а затем передает его функции Na() с переменной Ha. Давайте добавим это в нашу жизнь сценария, а затем найдем Ha. Если мы ищем Ha, вы найдете переменную, установленную на сегодняшний день.

var Ha = new Date(2021,5,19,0,0,0,0);

Давайте возьмем все это и добавим в наш скрипт. Теперь у нас должно быть это:

var La = [
    ... // truncated for readability
];
var Ha = new Date(2021,5,19,0,0,0,0);

function Da(e) {
    var a, s = Ga(e);
    return a = s % La.length,
        La[a]
}

function Ga(e) {
    return Na(Ha, e)
}

Итак, следующая часть — это функция Na(), давайте найдем ее:

function Na(e, a) {
    var s = new Date(e)
        , t = new Date(a).setHours(0, 0, 0, 0) - s.setHours(0, 0, 0, 0);
    return Math.round(t / 864e5)
}

Прохладный! Это конец пути для нашей цепочки функций! Как и раньше, давайте возьмем его и добавим в наш скрипт, в результате чего получится следующее:

var La = [
    ... // truncated
];
var Ha = new Date(2021,5,19,0,0,0,0);

function Na(e, a) {
    var s = new Date(e)
        , t = new Date(a).setHours(0, 0, 0, 0) - s.setHours(0, 0, 0, 0);
    return Math.round(t / 864e5)
}

function Da(e) {
    var a, s = Ga(e);
    return a = s % La.length,
        La[a]
}

function Ga(e) {
    return Na(Ha, e)
}

Теперь у нас есть все части, необходимые для завершения нашего решателя! Если вы помните, что исходный код игры вызывает функцию Da() для запуска, мы тоже должны это сделать. Давайте создадим переменную и сохраним результат цепочки функций:

const answer = Da(); // Note this won't work yet! Read on.

Теперь Da() нужен параметр с именем e. Вернемся к тому, с чего начали, и посмотрим, что это такое.

e.solution = Da(e.today)

Они предоставляют e.today в качестве параметра, который является переменной, которую они устанавливают, так что давайте возьмем ее.

e.today = new Date;

Таким образом, используя эту информацию, мы можем изменить нашу переменную на:

const answer = Da(new Date);
console.log(answer);

Осталось только запустить. Вы можете запустить его, используя:

$ node index.js
// Result
❯ node index.js
whack

Поздравляю! У вас есть рабочий решатель Wordle!

Заключение

Используя наш код выше, я пошел дальше и создал базовую веб-страницу, которая будет обновляться с ответом для простоты использования, чтобы показать, что вы можете сделать с этим кодом.

Спасибо за прочтение!

Ссылки: