DataLife Engine > Программирование > Взлом Tanks Mobile

Взлом Tanks Mobile


24 августа 2007. Разместил: SlaDER
Автор статьи:  BlackFan
Редактор:  SlaDER

Рассмотрим возможность взлома J2me-игр на примере Tanks Mobile

Инструменты:

  • WinRAR (или другой архиватор который умеет работать с zip архивами), думаю что есть у всех.
  • Декомпилятор, я использую Java Decompiler.
  • Блокнот или любой другой текстовый редактор.
  • Сама игра… Tanks Mobile можно скачать с tanchiki.ru
    Разархивируем игру и смотрим содержимое:
  1. *.map – карты игры
  2. *.pngкартинки
  3. *.mid – музыка
  4. *.class – скомпилированный исходный код игры ( то что нам нужно)
Декомпилируем файлы *.class в Java Decompiler с включенной функцией «Deobfuscate»
Получаем файлы *.java ( исходный код игры), которые можно открыть любым текстовым редактором.
Рассмотрим содержимое этих файлов. В файле k.java находим такой кусок кода:

 

 if(m_aI == 1){

            q.m_bSprite.setFrame(2);

            q.m_bSprite.setPosition(getWidth() - 22, getHeight() - 20);

            q.m_bSprite.paint(g);

            q._aStringI("Благодарим вас за регистрацию! Приятной игры!"

                            , g, getWidth() - 15, getWidth() / 2, (getHeight() / 2 + q.m_agI) - 5, 1, true, true);

            return;

        }

        if(m_aI == 2){

            q.m_bSprite.setFrame(3);

            q.m_bSprite.setPosition(3, getHeight() - 20);

            q.m_bSprite.paint(g);

            q._aStringI("Неверный ключ. Обратитесь за помощью на сайт www.tanchiki.ru"

                            , g, getWidth() - 15, getWidth() / 2, (getHeight() / 2 + q.m_agI) - 5, 1, true, true);

            return;

        } 



То, что разработчики держали строки типа «Благодарим вас за регистрацию! Приятной игры!» в самом коде, очень облегчило нам задачу поиска, но так будет далеко не во всех играх. Чтобы регистрация прошла необходимо, чтобы переменная m_aI = 1

Ищем все, что связано с изменением этой переменной в файле k.java.
 И находим такой кусок кода:


_L6:
        int i = Integer.parseInt(m_aString);
        boolean flag = false;
        if(q.m_afI == 3)
        {
            long l;
            String s;
            long l1;
            l1 = (l1 = Integer.parseInt((s = String.valueOf(l = q.m_aeI)).substring(0, 1))
                       + Integer.parseInt(s.substring(1, 2)) + Integer.parseInt(s.substring(2, 3))
                       + Integer.parseInt(s.substring(3, 4)) + Integer.parseInt(s.substring(4, 5))
                       + Integer.parseInt(s.substring(5, 6)) + Integer.parseInt(s.substring(6, 7))) * 321L + 213L & 255L;
            if((s = String.valueOf(l = (l = (l = (l = (l = (l ^= 0x1c91bfL) ^ l1) ^ l1 << 8) ^ l1 << 16) << 3)
                                   * 3L + 2129L).substring(0, 7)).substring(0, 4).compareTo(m_aString.substring(1, 5)) == 0)
                flag = true;
        }
        if(flag)
        {
            m_aI = 1;
            q._aStringIV("temp", 2, i);
            q.m_eZ = true;
        } else
        {
            m_aI = 2;
        }
        this;
          goto _L8


Переменная m_aI=1, если flag==true. А переменная flag принимает значение true, если выполняется довольно громоздкая и непонятная проверка ( разработчики постарались wink).

В строчках:

       int i = Integer.parseInt(m_aString);


и


        l1 = (l1 = Integer.parseInt((s = String.valueOf(l = q.m_aeI)).substring(0, 1))
                            + Integer.parseInt(s.substring(1, 2)) + Integer.parseInt(s.substring(2, 3))
                            + Integer.parseInt(s.substring(3, 4)) + Integer.parseInt(s.substring(4, 5)) 
                            + Integer.parseInt(s.substring(5, 6)) + Integer.parseInt(s.substring(6, 7))) * 321L + 213L & 255L;


видим 2 неизвестные переменные:


       m_aString
       q.m_aeI


Сначала разберемся с q.m_aeI, так как l1 участвует в проверке кода.
Открываем q. java и ищем переменную m_ aeI. Мы находим ее только в строчке:


public static int m_aeI = 0;


Но этого недостаточно, так как получается, что в k.java вызывается переменная из q.java, которая равна нулю… Следовательно, ищем изменение этой переменной в остальных классах.
В i.java находим такие строчки:


       int k;
       if((k = q._aStringII("temp", 1, 0)) == 0){
                k = q._aIII(0xf462b, 0x98967f);
                k = 0x1c9c380 + k;
                q._aStringIV("temp", 1, k);
       }
      q.m_aeI = k % 0x989680;
      q.m_afI = (k - k % 0x989680) / 0x989680;
      obj = String.valueOf(k);
      obj = ((String) (obj)).substring(0, 4) + " " + ((String) (obj)).substring(4, 8);


Смотрим, какое значение получает переменная k при q._aIII(0xf462b, 0x98967f). Открываем q.java и ищем функцию _aIII


public static int _aIII(int i, int x){
        int i1;
        return (i1 = Math.abs(m_aRandom.nextInt())) % (x - i) + i;
    }


Исходя из этого переменной k задается случайное значение…
Проанализировав получаемые значения k, а так же исходя из этих строчек:


        obj = String.valueOf(k);
        obj = ((String) (obj)).substring(0, 4) + " " + ((String) (obj)).substring(4, 8);


и


        q.m_bString = "T" + obj + " " + s;


Получаем, что k это первые 8 цифр из 12-значного кода, которые генерируется в игре при первом запуске.

Теперь объединяем все воедино и пишем подбор ключа

Используемые переменные:

Kod – первые 8 цифр из кода, который генерируется при запуске игры
Kod1 – дополнительная переменная

             Klych – ключ который необходим для регистрации


   //вначале считываем код в переменную kod
   //далее идет объединенные части из разных классов, которые мы раскопали
        Kod1 = Kod % 0x989680;
        obj = String.valueOf(Kod);
        long l;
        String s;
        long l1;
        boolean progress=true;
        String Klych ="10000000";
        while(progress){
                    Klych = String.valueOf(Integer.parseInt(Klych)+1);
                    l1 = (l1 = Integer.parseInt((s = String.valueOf(l = Kod1)).substring(0, 1))
                    + Integer.parseInt(s.substring(1, 2)) + Integer.parseInt(s.substring(2, 3))
                    + Integer.parseInt(s.substring(3, 4)) + Integer.parseInt(s.substring(4, 5))
                    + Integer.parseInt(s.substring(5, 6)) + Integer.parseInt(s.substring(6, 7))) * 321L + 213L & 255L;
                            if((s = String.valueOf(l = (l = (l = (l = (l = (l ^= 0x1c91bfL) ^ l1) ^ l1 << 8) ^ l1 << 16) << 3)
                                            * 3L + 2129L).substring(0,7)).substring(0,4).compareTo(obj.substring(1, 5)) == 0){
                                            progress=false;
                            //вывод переменной Klych
       }
   }


Для ускорения перебора можно вынести:


       l1 = (l1 = Integer.parseInt((s = String.valueOf(l = Kod1)).substring(0, 1)) + ... + 213L & 255L;
 


и


         String.valueOf(l = (l = (l = (l = (l = (l ^= 0x1c91bfL) ^ l1) ^ l1 << 8) ^ l1 << 16) << 3) * 3L + 2129L).substring(0, 7);


за цикл,так же можно установить "шаг перебора" равным 1000.

Получаем следующий код:

          Kod1=Kod % 0x989680;
          long l;
          String s;
          String Klych  ="10000000";
          long l1;
          boolean progress=true;
                      l1 = (l1 = Integer.parseInt((s = String.valueOf(l = Kod1)).substring(0, 1))
                                    + Integer.parseInt(s.substring(1, 2)) + Integer.parseInt(s.substring(2, 3))
                                    + Integer.parseInt(s.substring(3, 4)) + Integer.parseInt(s.substring(4, 5)) 
                                    + Integer.parseInt(s.substring(5, 6)) + Integer.parseInt(s.substring(6, 7))) * 321L + 213L & 255L;
                      s = String.valueOf(l = (l = (l = (l = (l = (l ^= 0x1c91bfL) ^ l1) ^ l1 << 8) ^ l1 << 16) << 3) * 3L + 2129L).substring(0, 7);
          while(progress){
                      Klych = String.valueOf(Integer.parseInt(buf)+1000);
                         if ( s.substring(0, 4).compareTo(Klych.substring(1, 5)) == 0){
                                       //вывод Klych
                                      progress=false; 
                         }
          }


Теперь этот код можно перенести на любой другой язык программирования или оставить в J2ME.
Вместе со статьей идут два Key-Gen'а в котором он реализован… ( J2ME и VB.NET)
Вот так, просто анализируя код мы создали свой генератор ключей

Генераторы ключей для Tanks Mobile 3.0:
Внимание! У вас нет прав, для просмотра скрытого текста.