четверг, 2 января 2014 г.

Hacking games with PIN

Сегодня речь пойдет о играх, а точнее, об их взломе.

Может возникнуть вопрос, причем тут игры, ведь блог в основном относится к теме системного программирования / реверсинга / декомпиляции?
Да вот как-то после прочтения заметки в блоге HEX'а про создание игр (http://rebl0g.wordpress.com/2013/12/15/%D0%BC%D0%B5%D1%87%D1%82%D1%8B-%D0%B4%D0%B5%D1%82%D1%81%D1%82%D0%B2%D0%B0/#more-217) навеяло. Но в данной заметке будет рассмотрено прямо противоположное созданию действие - взлом.

Что в основном ломают в играх? Определенно, игровые ресурсы.
Как ломают тоже в принципе давно известно: ищут / меняют значения памяти в программах типа gamehack.
Но, конечно, gamehack я описывать в своем блоге не собираюсь, вместо этого я опишу альтернативный вариант поиска нужных значений в памяти.

Тут как раз и пересекается тематика блога и игр, ибо речь пойдет про PIN.

Итак,  нам нужно найти и изменить определенные игровые ресурсы, скажем, деньги. Для этого нужно как-то найти, где в памяти, а еще лучше, прямо в бинаре лежат нужные нам данные. Зачем нам бинарь, ведь достаточно изменить значения в памяти? К примеру, мы хотим еще и пореверсить код игры, или пропатчить бинарь игры, для этого желательно иметь точку, где происходит работа с игровыми ресурсами.

Как будем искать нужные данные? Подход в чем-то схож с подходом gamehack.

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

Далее, все что нужно, это написать PIN модуль, который смотрит текущие инструкции на предмет sub /add и выводит все инструкции в лог.
После чего, запускаем игру под PIN до изменения наблюдаемых величин( игровой валюты к примеру ) и после изменения.
Наблюдаемую величину можно захардкодить в PIN модуле, чтобы отсеять лишнюю информацию из лога.

В итоге, получаем нечто вроде:

VOID SubReg( ADDRINT ea, string *disasm, ADDRINT reg1Value, ADDRINT reg2Value )
{
...
    if ( reg1Value == VALUE_TO_WATCH )
        cout << hex << ea << " " << disasm << dec << " reg1Value = " << reg1Value << " reg2Value = " << reg2Value << endl;
...
}

VOID Instruction( INS ins, VOID *v )
{
    ADDRINT ea = INS_Address( ins );

    if ( ea < gMainExe.GetModuleStartAddress() || ea > gMainExe.GetModuleEndAddress() )
        return;

    if (INS_Opcode(ins) == XED_ICLASS_SUB && INS_OperandReg(ins, 0) != REG_ESP && !INS_OperandIsImmediate(ins, 1) && INS_OperandIsReg(ins, 0))
    //if (INS_Opcode(ins) == XED_ICLASS_ADD && INS_OperandReg(ins, 0) != REG_ESP && !INS_OperandIsImmediate(ins, 1) && INS_OperandIsReg( ins, 0))
    {
        if ( INS_OperandIsReg( ins, 1 ) )
        {
            INS_InsertCall( ins, IPOINT_BEFORE, (AFUNPTR)SubReg,
                                IARG_ADDRINT, ea,
                                IARG_PTR, new string( INS_Disassemble(ins) ),
                                IARG_REG_VALUE, INS_OperandReg( ins, 0 ),
                                IARG_REG_VALUE, INS_OperandReg( ins, 1 ),
                                IARG_END );
        }   
    }
}

Далее, так как на моем компе видеокарта встроенная и игр не установлено, пришлось покопаться в древних архивах, было найдено три стареньких игры, получены логи.

Пример лога(игра WarGames):

487696 sub edx, ecx reg1Value = 3f7a reg2Value = 9c4
568428 sub edx, edx reg1Value = 3f7a reg2Value = 3f7a
568428 sub edx, edx reg1Value = 3f7a reg2Value = 3f7a
568428 sub edx, edx reg1Value = 3f7a reg2Value = 3f7a

В выделенной строке лог после покупки юнита за 2500(9c4h) игровых денег.
То есть из общего баланса (3f7a) вычитается стоимость юнита. Таким образом, мы вышли на код работы с игровой валютой, дальше можно патчить в памяти через тот же PIN, или на диске или анализировать/реверсить в IDA.

Код в IDA:

.text:00487687                 xor     ecx, ecx
.text:00487689                 mov     cx, word_7470B2[edx]
.text:00487690                 mov     edx, dword_73B65C[eax]
.text:00487696                 sub     edx, ecx
.text:00487698                 mov     eax, [ebp+var_8]
.text:0048769B                 xor     ecx, ecx
.text:0048769D                 mov     cl, [eax+5]

Такой подход оправдал себя и на двух других играх, для Orion2 искалась не sub инструкция, а add, так как доход игровой валюты там меняется с течением времени.

После всех этих манипуляций мое любопытство по поводу жизнеспособности метода с PIN было удовлетворено, и игры благополучно были отправлены обратно в архив.