Анализ Frida-RPC Tool-Arida на основе FastAPI

задняя часть обеспечить регресс
Анализ Frida-RPC Tool-Arida на основе FastAPI

Всем привет, тема этого выпуска开源框架的分享, поэтому сегодня я поделюсь с вами инструментом, с которым я недавно соприкоснулся.

Эта статья была впервые опубликована в "Security Guest - Thoughtful Security New Media" и первоначально была опубликована lateautumn4lin от моего имени. Ссылка на исходный текст:Woohoo.Security Section.com/post/ID/218…

На этот раз представленFastAPIосуществленныйFrida-RPCинструмент-Arida(адрес гитхаба), я независимый разработчик этого инструмента, я хотел бы представить вам этот инструмент开发构想И его使用方式.

1 идея развития

Инструменты часто являются производными от повседневной работы.Когда в работе есть связь «повторение, повторение, повторение», рождается инструмент, который может сэкономить время и повысить эффективность работы. моя повседневная работа включает в себя逆向分析APP协议, инструменты, используемые в настоящее время, как правило,Frida, иногда для проверки результатов анализаHookспособ вызвать метод и передать自带RPCМетод выставляет интерфейс, потому что количество APP для ежедневного анализа относительно велико, поэтому я столкнулся с рядом проблем, которые побудили меня разработать для себя набор инструментов для повышения эффективности работы.

1.1 Проблемы, возникающие на работе

1.1.1 Несколько приложений и несколько файлов Frida-J

Просто начал часто использовать его на работеFridaСтуденты, изучающие инструменты, обязательно обнаружат, что им нужно писать новую версию каждый раз, когда они проводят обратный анализ приложения.JavaScriptФайлы, со временем, как сохранить столько файлов? Как запустить соответствующее приложение для разных приложенийJavaScriptдокумент? Как извлечь повторяющийся код каждого файла? это все оFrida-Jsпроблемы с управлением файлами.

1.1.2 Написанный метод Js должен создать соответствующий метод API

Как понять этот вопрос? Все знаютFrida JavaScript FunctionМетод воздействия такой

rpc.exports = {
  decryptData: decrypt_data,
  generateUrl: generate_url,
  encryptData: encrypt_data
}

использоватьrpc.exportsсоответствующийMapуказать выставленный метод и соответствующую функцию, но в этом случае он используется толькоJavaScriptизexportsКлючевое слово раскрывает метод, чтобы его могли вызывать другие скрипты. Как его можно превратить в Http-интерфейс? можно использовать напрямуюNodeJsизHttp框架,НапримерExpress, но мы обычно используем наиболееPython,НапримерFlask,DjangoТакая структура, люди, которые использовали структуру, знают, что нам нужно написать метод для каждого API, например этот

@app.route("/test")
def test():
	return "hello"

В сочетании с этим методом мы называемFrida-RPCКак оно есть

@app.route('/sign')
def sign_test():
	sign = request.args['sign'] 
	res = script.exports.sign(sign)
	return res 

Нам нужно нацелиться на каждыйJavaScriptспособ написать соответствующийPythonметод и параметры, которые будут вызываться напрямую, проблема в том, что чем больше методов мы накопим, тем больше будет наш общий проект, но многие части кода являются повторяющимся и простым вызывающим кодом.

1.1.3 Вопросы сотрудничества

Это также очень неприятная проблема.После того, как вы сделали все вышеперечисленные операции и развернули сервис, другие люди хотят использовать ваш API.Можете ли вы предоставить полныйAPI文档? Вам все еще нужен интерфейс для написания соответствующей документации?

1.2 Какие болевые точки должен решить инструмент

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

  • управлятьJavaScriptфайл, сAPP-文件отношения отображения

  • Автоматически ориентироваться на существующиеJavaScriptметод создания соответствующегоAPIметод

  • Создано автоматическиOpen APIДокументация

1.3 Инструменты Ариды

Когда возникла идея «захотеть разработать инструмент», она взлетела в спешке.На создание простого инструмента, о котором упоминается в этот раз, ушло около двух часов.AridaЭтот инструмент, название происходит отFridaиAPIЭти два слова, просто соединенные вместе, выполняют те же функции, что и упомянутые выше.

1.3.1 Конкретный рабочий процесс

Рабочий процесс выглядит следующим образом:

В основном делится на четыре этапа:

  • Шаг 1: ИспользуйтеJavaScript AST树получитьexportsизMapИмя функции в и количество параметров соответствующей функции для облегчения последующего построенияPydanticизModel.

  • Шаг 2: ГенерацияPydanticДинамические модели облегчаютFastAPIизAPI Docпоколение.

  • Шаг 3: Объедините模型и получилJavaScript的方法名和参数个数генерировать новыеPython方法.

  • Шаг 4: Зарегистрируйте маршрут, соответствующий каждому приложению, и, наконец, зарегистрируйте его в глобальном маршруте.

2 Интерпретация исходного кода

Я говорил об этом раньшеAridaВесь рабочий процесс, следующее в основном объясняет реализацию каждой части.

2.1 Экспорт информации о функциях скрипта Frida JavaScript

ОбщееFrida-JsСкрипт такой

var result = null;
function get_sign(body) {
  Java.perform(function () {
    try {
      var ksecurity = Java.use('com.drcuiyutao.lib.api.APIUtils');
      result = ksecurity.updateBodyString(body)
      console.log("myfunc result: " + result);
    } catch (e) {
      console.log(e)
    }
  });
  return result;
}


rpc.exports = {
  getSign: get_sign,
}

Информация, которую нам необходимо получить,导出函数名и соответствующая функция экспорта内部函数的参数个数, рассматривал использование обычного метода для этого, но обычный метод громоздкий, поэтому изJavaScript ASTНачиная с дерева, мы можем лучше анализировать нужную нам информацию.

Разобранный скрипт выглядит следующим образом:

const babel = require('@babel/core')
var exports = new Map();
var functions = new Map();
var result = new Map();
function parse(code) {
  let visitor = {
    // 处理exports节点,获取导出函数对应表
    ExpressionStatement(path) {
      let params = path.node.expression.right;
      try {
        params = params.properties
        for (let i = 0; i < params.length; i++) {
          exports.set(params[i].value.name, params[i].key.name)
        }
      } catch {

      }
    },
    // 处理function,获取函数名以及对应参数
    FunctionDeclaration(path) {
      let params = path.node;
      var lst = new Array();
      for (let i = 0; i < params.params.length; i++) {
        lst.push(params.params[i].name)
      }
      functions.set(params.id.name, lst)
    }
  }
  babel.transform(code, {
    plugins: [
      {
        visitor
      }
    ]
  })
  exports.forEach(function (value, key, map) {
    result.set(value, functions.get(key))
  })
  return Object.fromEntries(result);
}

В основном анализируетсяfunctionиexportsдва узла и, наконец, возвратMap

2.2 Динамическая генерация модели интерфейса FastAPI API

получил предыдущий шагJavaScriptизMapданные, наверное

{
	"getSign":3
}

Затем вам нужно использовать эту информацию для динамического создания модели интерфейса.Причина создания модели интерфейса заключается в том, что вFastAPIВ этих рамкахPostИнтерфейс используетPydanticизBaseModel,использоватьBaseModelПричина еще и в том, что с одной стороны нужно сгенерировать документ внешнего интерфейса, а с другой стороны требуется проверка типа параметров.Динамически генерируемый код выглядит следующим образом:

from pydantic import create_model
params_dict = {"a":""}
Model = create_model(model_name, **params_dict)

ВводитьPydanticизcreate_model, параметр является типом каждого параметра метода, например.Stringтип напрямую"",Даintтип напрямую0.

2.3 Динамическое создание методов Python на основе Python AST

На последнем шаге с моделью иJavaScriptизMapДанные, которые мы можем динамически генерироватьPythonметод, потому что общие методы API одинаковы, а именно:

def sign_test():
    sign = request.args['sign'] 
    res = script.exports.sign(sign)
    return res

Нам нужно динамически генерировать вышеуказанный формат, мы можем принять два решения.

  • Первый: метод закрытия - функция возвращает функцию, например
def outer():
	def inner():
		return "hello"
	return inner
  • Второе: использоватьPython ASTгенерация дереваPython字节码,использоватьtypes.FunctionDefДля генерации код выглядит следующим образом:
function_ast = FunctionDef(
        lineno=2,
        col_offset=0,
        name=func_name,
        args=arguments(
            args=[
                arg(
                    lineno=2,
                    col_offset=17,
                    arg='item',
                    annotation=Name(lineno=2, col_offset=23,
                                    id=model_name, ctx=Load()),
                ),
            ],
            vararg=None,
            kwonlyargs=[],
            kw_defaults=[],
            kwarg=None,
            defaults=[],
            posonlyargs=[]
        ),
        body=[
            # Expr(
            #     lineno=3,
            #     col_offset=4,
            #     value=Call(
            #         lineno=3,
            #         col_offset=4,
            #         func=Name(lineno=3, col_offset=4,
            #                   id='print', ctx=Load()),
            #         args=[
            #             Call(
            #                 lineno=3,
            #                 col_offset=10,
            #                 func=Name(lineno=3, col_offset=10,
            #                           id='dict', ctx=Load()),
            #                 args=[Name(lineno=3, col_offset=15,
            #                            id='item', ctx=Load())],
            #                 keywords=[],
            #             ),
            #         ],
            #         keywords=[],
            #     ),
            # ),
            Assign(
                lineno=3,
                col_offset=4,
                targets=[Name(lineno=3, col_offset=4,
                              id='res', ctx=Store())],
                value=Call(
                    lineno=3,
                    col_offset=10,
                    func=Attribute(
                        lineno=3,
                        col_offset=10,
                        value=Attribute(
                            lineno=3,
                            col_offset=10,
                            value=Name(lineno=3, col_offset=10,
                                       id='script', ctx=Load()),
                            attr='exports',
                            ctx=Load(),
                        ),
                        attr=func_name,
                        ctx=Load(),
                    ),
                    args=[
                        Starred(
                            lineno=4,
                            col_offset=38,
                            value=Call(
                                lineno=4,
                                col_offset=39,
                                func=Attribute(
                                    lineno=4,
                                    col_offset=39,
                                    value=Call(
                                        lineno=4,
                                        col_offset=39,
                                        func=Name(
                                            lineno=4, col_offset=39, id='dict', ctx=Load()),
                                        args=[
                                            Name(lineno=4, col_offset=44, id='item', ctx=Load())],
                                        keywords=[],
                                    ),
                                    attr='values',
                                    ctx=Load(),
                                ),
                                args=[],
                                keywords=[],
                            ),
                            ctx=Load(),
                        ),
                    ],
                    keywords=[],
                ),
            ),
            Return(
                lineno=4,
                col_offset=4,
                value=Name(lineno=4, col_offset=11, id='res', ctx=Load()),
            ),
        ],
        decorator_list=[],
        returns=None,
    )

Сначала динамически сгенерируйте соответствующий методPython AST树

module_ast = Module(body=[function_ast], type_ignores=[])
module_code = compile(module_ast, "<>", "exec")
function_code = [
c for c in module_code.co_consts if isinstance(c, types.CodeType)][0]

генерировать соответствующиеPython AST树из字节码

function = types.FunctionType(
        function_code,
        {
            "script": script,
            model_name: model,
            "print": print,
            "dict": dict
        }
    )
    function.__annotations__ = {"item": model}

использовать字节码Создайте новый метод, потому что аннотации исходного байт-кода будут потеряны при создании нового метода, то есть__annotations__Поэтому это свойство необходимо добавить вручную после создания нового метода.

3 Как использовать

Давайте поговорим оAridaДля конкретного использования проект включает два простых примера, вAppsНиже каталога информация о конфигурации находится вconfig.pyв файле.

3.1 Создайте новый проект в два этапа

Как создать новый проект? Для этого требуется всего два шага, следуйте шагам, показанным ниже:

  1. Шаг 1: Добавьте информацию о конфигурации, адрес файлаconfig.py
INJECTION_APPS = [
    {
        "name": "我的测试1",
        "path": "yuxueyuan",
        "package_name": "com.drcuiyutao.babyhealth"
    },
    {
        "name": "我的测试2",
        "path": "kuaiduizuoye",
        "package_name": "com.kuaiduizuoye.scan"
    }
]

Как показано в коде, он должен быть вINJECTION_APPSДобавьте конкретную информацию о приложении в список, в основном в трех полях:

  • name: влияет наFastAPI DocИмя группы в , не имеет особого практического значения и может быть понято как улучшение опыта людей, читающих интерфейсный документ.

  • path: В соответствии со значением этого поля вAppsПапка соответствует соответствующейJavaScriptдокумент.

  • package_name: имя пакета, который необходимо внедрить

После его добавления первый шаг завершен.

  1. Шаг 2: Разработайте соответствующее приложениеFrida-Jsсценарий

3.2 Открытость API подписи нескольких приложений корпоративного уровня

Поскольку в нашей повседневной работе мы часто одновременно проводим обратный анализ нескольких приложений, поэтому также важно проводить тестирование интерфейса API нескольких приложений одновременно.AridaПоддерживает одновременный запуск нескольких приложений и ввод соответствующихJavaScriptScript, просто выполните описанные выше шаги, чтобы завершить разработку каждого проекта APP, и соответствующее APP будет автоматически внедрено при его запуске.В то же время при просмотре документа он также будет таким, как показано на рисунке:

4 примечания

4.1 Маркеры типа параметра

так какJavaScriptТип параметра метода не может быть указан, в результате чтениеJavaScriptМетод может иметь только количество параметров и не может получить тип параметров, поэтому сгенерированныйPydantic模型Когда вы можете только унифицировать тип как тип символа, если вы хотите настроить тип параметра, вы можетеmain.pyв файлеfunction_params_hintsнастроить:

function_params_hints = {
    "encryptData": [0, 0, 0, 0, "", 0, 0, 0, 0, 0, 0]
}

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