<progress id="yueoz"><code id="yueoz"><xmp id="yueoz">

      1. 吾愛破解 - LCG - LSG |安卓破解|病毒分析|www.13ee.cn

         找回密碼
         注冊[Register]

        QQ登錄

        只需一步,快速開始

        搜索
        查看: 4207|回復: 35
        上一主題 下一主題

        [Android 原創] 基于FastAPI實現的Frida-RPC工具-Arida解析

          [復制鏈接]
        跳轉到指定樓層
        樓主
        lateautumn4lin 發表于 2020-10-14 13:48 回帖獎勵
        本帖最后由 qtfreet00 于 2020-10-24 23:18 編輯

        在這里插入圖片描述

        在這里插入圖片描述

        大家好,本期主題是開源框架的分享,所以今天會給大家分享下最近我接觸的一款工具。

        本文首發于《安全客 - 有思想的安全新媒體》,也是以本人身份lateautumn4lin原創發布的,原文鏈接:https://www.anquanke.com/post/id/218915

        這次介紹的是一款基于FastAPI實現的FrIDA-RPC工具-Arida(Github地址:https://github.com/lateautumn4lin/arida),我是這個工具的獨立開發者,想跟大家介紹下這款工具的開發構想以及它的使用方式。

        在這里插入圖片描述

        1 開發設想

        工具往往來源于日常工作,當工作中出現了“重復、重復、又重復”的環節時,一款能夠節約時間、提高工作效率的工具便順應誕生了。我的日常工作會涉及逆向分析APP協議,目前使用的工具一般都是Frida,有時候為了驗證分析結果,都會采用Hook的方式調用方法并通過自帶RPC的方法暴露出接口來,因為日常分析的APP數量比較多,所以碰到了一系列的問題促使我想給自己開發一套工具提升工作效率。

        在這里插入圖片描述

        1.1 工作中遇到的問題

        1.1.1 多APP多個Frida-Js文件

        剛剛開始在工作中頻繁使用Frida工具的同學一定會發現每次逆向分析APP的時候都需要寫不同的JavaScript文件,時間一長,如何維護這么多文件?如何針對不同的APP啟動對應的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來指定暴露方法和對應的函數,但是這樣的話也只是利用了JavaScriptexports關鍵字使方法暴露出來使其他腳本能夠調用。那怎么能夠做成Http接口呢?可以直接利用NodeJsHttp框架,例如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 工具需要解決哪些痛點

        針對以上的這些問題,我們需要一款高效率的工具能夠幫助我們屏蔽這些工作中的細節問題,讓我們能夠更專注于去逆向分析APP中的調用流程。所以,我們需要一款工具能夠完成以下這些功能:

        • 管理JavaScript文件,具備APP-文件的映射關系

        • 自動針對現有的JavaScript方法生成相應的API方法

        • 自動生成Open API文檔

        在這里插入圖片描述

        1.3 Arida工具

        當“想開發一個工具”的想法產生的時候,就風風火火的搞起來了,大概花了兩個小時的時間,完成了一個簡單的工具,也就是這次提到的Arida這個工具,名稱來源于FridaAPI這兩個詞,簡單拼接成的,具備的功能也是如上文提到的一樣。

        1.3.1 具體工作流程

        工作流程如下:

        主要分為四步:

        • 第一步:利用JavaScript AST樹獲取到exportsMap中的函數名稱以及對應的函數的參數個數,以便于后續的構造PydanticModel。

        • 第二步:生成Pydantic動態模型便于FastAPIAPI Doc的生成。

        • 第三步:結合模型以及獲取到的JavaScript的方法名和參數個數產生新的Python方法。

        • 第四步:注冊各個APP相對應的路由,最后注冊到全局路由中。

        在這里插入圖片描述

        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);
        }

        主要解析了functionexports兩個節點,最終返回Map

        在這里插入圖片描述

        2.2 FastAPI API接口模型動態生成

        上一步得到了JavaScriptMap數據,大概是這樣

        {
                "getSign":3
        }

        接下來,需要利用這個信息來動態生成接口模型,之所以要生成接口模型,是因為在FastAPI這個框架當中,Post接口使用的是PydanticBaseModel,使用BaseModel的原因也是因為一方面要生成對外的接口文檔,另一方面要對參數做類型校驗,動態生成的代碼如下:

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

        引入Pydanticcreate_model,參數是各個方法參數的類型,例如是String類型就直接是"",是int類型就直接是0。

        在這里插入圖片描述

        2.3 基于Python AST動態生成Python方法

        到了最后一步,有了模型以及JavaScriptMap數據我們就可以動態生成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. 第一步:添加配置信息,文件地址是config.py
        INJECTION_APPS = [
            {
                "name": "我的測試1",
                "path": "yuxueyuan",
                "package_name": "com.drcuiyutao.babyhealth"
            },
            {
                "name": "我的測試2",
                "path": "kuaiduizuoye",
                "package_name": "com.kuaiduizuoye.scan"
            }
        ]

        如代碼中所示,需要在INJECTION_APPS列表中添加具體APP的信息,主要是三個字段:

        • name:影響的是FastAPI Doc中的分組名稱,沒有具體的實際意義,可以理解成對看接口文檔的人的體驗度的提升。

        • path:根據這個字段的值在Apps文件夾中匹配到對應的JavaScript文件。

        • package_name:需要注入的包名

          添加好之后就完成了第一步。

        1. 第二步:開發對應APP的Frida-Js腳本

        在這里插入圖片描述

        3.2 企業級多APP簽名API暴露

        因為在日常工作中,我們往往會同時去逆向分析多個APP,所以同時暴露多個APP的API接口測試也是必不可少的,Arida支持同時啟動多個APP并注入相應的JavaScript腳本,只需按上面的步驟完成每個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]
        }

        通過這樣來生成合適的參數模型,這樣在使用者使用接口的時候由參數模型根據模型中的參數對應的類型來進行類型轉化。
        在這里插入圖片描述

        免費評分

        參與人數 15威望 +2 吾愛幣 +113 熱心值 +13 收起 理由
        夏青小白狐 + 1 + 1 謝謝@Thanks!
        影視專業 + 1 + 1 我很贊同!
        pandada098 + 1 用心討論,共獲提升!
        n257259 + 1 + 1 謝謝@Thanks!
        0615 + 1 + 1 熱心回復!
        heihei1314 + 1 + 1 先贊后看。。。。
        MFC + 1 + 1 感謝發布原創作品,吾愛破解論壇因你更精彩!
        skylar + 1 謝謝@Thanks!
        skyward + 1 + 1 學習了,感謝
        andrew_a + 1 + 1 已經處理,感謝您對吾愛破解論壇的支持!
        victos + 1 + 1 謝謝@Thanks!
        takpap + 1 我很贊同!
        fengbolee + 1 + 1 用心討論,共獲提升!
        user_52 + 1 + 1 666學習了
        qtfreet00 + 2 + 100 + 1 感謝發布原創作品,吾愛破解論壇因你更精彩!

        查看全部評分

        發帖前要善用論壇搜索功能,那里可能會有你要找的答案或者已經有人發布過相同內容了,請勿重復發帖。

        推薦
         樓主| lateautumn4lin 發表于 2020-11-12 15:39 |樓主
        synodriver 發表于 2020-11-12 10:25
        感謝提供的思路,看來py標準庫里面的好東西還是不少,應該都是運行時服務里面的吧?應該需要對py解釋器有相 ...

        哈哈,沒有,其實文中的實現方法有很多,只是強行往底層、字節碼那里靠了
        推薦
        synodriver 發表于 2020-11-12 10:25
        感謝提供的思路,看來py標準庫里面的好東西還是不少,應該都是運行時服務里面的吧?應該需要對py解釋器有相當深入的了解了
        4#
        sdaq1000 發表于 2020-10-14 13:59
        5#
        yiwanyiwan 發表于 2020-10-14 14:13
        學無止境
        6#
        xixicoco 發表于 2020-10-14 14:26
        獵豹的人
        7#
        subney 發表于 2020-10-14 14:31
        學無止盡,受教了
        8#
        dujuan 發表于 2020-10-14 18:53
        感謝分享,學習了
        9#
        walkershow 發表于 2020-10-15 00:01
        大神大神!
        10#
        tocabd 發表于 2020-10-15 11:37
        學習學習!
        11#
        smith168668 發表于 2020-10-15 12:43
        膜拜大佬
        12#
        lifz888 發表于 2020-10-21 08:19
        非常好的學習資料,支持分享
        您需要登錄后才可以回帖 登錄 | 注冊[Register]

        本版積分規則 警告:本版塊禁止灌水或回復與主題無關內容,違者重罰!

        快速回復 收藏帖子 返回列表 搜索

        RSS訂閱|小黑屋|處罰記錄|聯系我們|吾愛破解 - LCG - LSG ( 京ICP備16042023號 | 京公網安備 11010502030087號 )

        GMT+8, 2020-11-25 01:22

        Powered by Discuz!

        Copyright © 2001-2020, Tencent Cloud.

        快速回復 返回頂部 返回列表
        快三开奖结果