Что нужно?

  • Как не странно - нам понадобится apk нашего приложения

  • Так же для автоматизации процесса понадобится немного собственного софта. Я предпочитаю использовать питон

  • Среда для разработки, у меня это vs-code

  • Любой 16-ричный редактор, у меня это расширение для vs code

Let`s start!

Для начала посмотрим что мы вообще имеем

APK файлы представляют из себя самый обычный zip архив, который можно открыть любым архиватором. Распакуем файлы в какую нибудь папку

Файлы в приложении
Файлы в приложении

Бегло пробежав по файлам можно обнаружить файлы с кодом на lua

Файлы приложения
Файлы приложения

Открыть эти файлы просто так не получится - они зашифрованы. Но все эти файлы объеденяет одно - слово signatr в самом начале. Пока что отложим эту информацию и пойдем дальше

Зайдем в папку lib и перейдем к нужной архитектуре - я обнаружил один единственный .so файл

папка lib
папка lib

Попробуем просмотреть его через hex-редактор

Много непонятного текста, но если полистать ниже - можно найти читаемые строки!

Введем в поиск слово которое получили ранее - signatr

Слева от этого результата есть непримечательное слово. Чтож, это слово - это наш ключ шифрования. Давайте попробуем им воспользоваться

Напишу простой скрипт на питоне для дешифровки методом xxtea

import xxtea # pip install xxtea
file = 'client.luac'
sign = 'signatr'
key = 'anyxteakey'
with open(file, 'rb') as f:
    filedata = f.read()
    
filedata = filedata[len(sign):] # обрежем начало файла чтоб убрать 'signatr'

data = xxtea.decrypt(filedata, key) # Дешифруем ключом

with open('test.lua', 'wb') as f:
    f.write(data)
    
    

Запустим скрипт и получим файл test.lua

Все сработало! Мы получили расшифрованный файл
Осталось только сделать обертку для скрипта декодирования и реализовать рекурсивное декодирование в папках

import os
import xxtea

sign = 'signatr'
key = 'anyxteakey'

def recursive_decrypt(directory):
    files = os.listdir(directory)
    for f in files:
        if os.path.isdir(directory + "/" + f):
            recursive_decrypt(directory + "/" + f)
        else:
            if f.endswith(".luac"):
                print("Decrypting", f)
                data = b""
                with open(directory + "/" + f, "rb") as file:
                    data = file.read()[len(sign):]
                decrypted = xxtea.decrypt(data, key)
                if decrypted:
                    with open(directory + "/" + f[:-1], "wb") as file:
                        file.write(decrypted)
                        os.remove(directory + "/" + f)
                else:
                    print("Failed to decrypt", f)

От себя я так же добавил функцию очистки, что бы убрать пустые папки и не lua файлы

import shutil
def post_clean(directory):
    # delete all not lua files
    ls = os.listdir(directory)
    if not ls:
        shutil.rmtree(directory)
        print("Removed", directory)
        return    
    for file in os.listdir(directory):
        if os.path.isdir(directory + "/" + file):
            post_clean(directory + "/" + file)
        elif not file.endswith(".lua"):
            os.remove(os.path.join(directory, file))
            print("Removed", file)
    if not os.listdir(directory):
        shutil.rmtree(directory)
        print("Removed", directory)

Вот и все.
Спасибо за внимание :)

Комментарии (3)


  1. dimitrii_z
    05.11.2023 04:01

    Плохая защита продукта.. С одной стороны, мало кто до этого додумается, с другой теперь статья будет гуглиться. Это opensource продукт, а думали придумать как улучшить защиту, и/или обсудить это с основными участниками проекта https://github.com/cocos2d/cocos2d-x ?


    1. guhbap Автор
      05.11.2023 04:01
      +1

      Как говорится - кто ищет, тот найдет. Я был бы рад помочь продукту, но мне недостаточно знаний в области безопасности и шифрования в частности. В своей статье я лишь привел способ, к которому пришел после поиска и анализа десятков тредов по реверсу с различных форумов и статей. Описанная проблема в шифровании файлов существует уже не первый год и была описана например тут.


    1. domix32
      05.11.2023 04:01

      Некоторые студии делят основную игру и загружаемый бандл, который подтягивается отдельно. Раньше это делалось для того чтобы пройти в лимиты сторов (когда-то можно было быть не тяжелее 50 мб в гуглсторе, например), теперь фактически используется для обновлений и защиты - зашивается сертификат к родным серверам и качается через HTTPS - возись теперь с MITM. Обычно заморачиваться с бОльшей защитой скриптов смысла нет. Альтернативный вариант - пережимать все скрипты и распаковывать их при запуске, но опять же - защита скриптов особо не нужна.

      Экономический эффект для игровой кампании от этого примерно никакой и даже если кому-то и удастся их выковырнуть - что он с ними делать будет?

      В качестве кода использовать не сможет - сторы могут и забанить за такое, если апроприация чужого кода будет обнаружена

      сварганить на основе этого свою игру - да ради бога, код не самое ценное в данной ситуации с учётом предыдущего пункта

      соберёт кастомный бандл и выложит на какой-нибудь 4pda - опять же особых проблем с этим нет, т.к. экономический эффект для игровой кампании обычно достаточно низкий. В случае чего задетектить и прописать бан игроку с палёным клиентом будет достаточно просто, но обычно никто с этим не заморачиается, т.к. большинство мобильных игр и без того f2p.

      Вся финансовая логика (вроде внутренней валюты) обычно зашита в компилируемой части приложения и не страдает от вскрытия бандлов.