<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Frida Internal - Part 1: 架構(gòu)、Gum 與 V8

          共 9902字,需瀏覽 20分鐘

           ·

          2022-04-09 20:33

          frida 是一個非常優(yōu)秀的開源項目,因為項目活躍,代碼整潔,接口清晰,加上用靈活的腳本語言(JS)來實現(xiàn)指令級代碼追蹤的能力,為廣大的安全研究人員所喜愛。雖然使用人群廣泛,但對其內(nèi)部實現(xiàn)的介紹卻相對較少,因此筆者就越俎代庖,替大胡子寫寫 frida 內(nèi)部實現(xiàn)介紹,同時也作為自己的閱讀理解記錄。

          系列文章傳送門(占坑):

          • ??Frida Internal - Part 1: 架構(gòu)、Gum 與 V8 (本文)[1]

          • ??Frida Internal - Part 2: frida-core

          • ??Frida Internal - Part 3: frida-java-bridge 與 ART hook

          • ??Frida Internal - Part 4: 檢測 frida 的 100 種方法

          項目總覽

          在?frida[2]?的主倉庫中,我們一般是直接在其 release 頁面下載 CI 的成品使用,其中可以看到有許多可供下載的組件,比如:

          • ??frida-server

          • ??frida-gadget

          • ??frida-inject

          • ??frida-core/gum/gumjs-devkit

          • ??frida-clr

          • ??frida-portal

          • ??frida-tools

          • ??...

          按照封裝層級來劃分可以分為 4 級,分別是:

          1. 1. CPU 指令集級別的 inline-hook 框架: frida-gum;

          2. 2. 使用 JavaScript 引擎對 gum 進行封裝實現(xiàn)腳本拓展的能力: gum-js;

          3. 3. 運行時進程注入、腳本加載、RPC 通信管理等功能: frida-core;

          4. 4. 針對特殊運行環(huán)境的 js 模塊及其接口,如 frida-java-bridge、frida-objc-bridge 等;

          還有一些其他輔助組件如 clr、portal 和針對不同語言的 binding 等功能相對花哨,就不額外進行討論了。

          由于筆者的關(guān)注點主要在系統(tǒng)層的實現(xiàn)上,因此對于 frida-gum 和 gum-js 的實現(xiàn)細節(jié)不會太過深入,就讓二老委屈點擠在本文中一并進行介紹了。后續(xù)針對 frida-core 和 frida-java-bridge 都會有單獨的文章進行分析。

          frida-gum

          說起?frida-gum[3]?大家都知道它提供了 inline-hook 的核心實現(xiàn),但實際上它還包含了許多其他的模塊,比如用于代碼跟蹤 Stalker、用于內(nèi)存訪問監(jiān)控的 MemoryAccessMonitor,以及符號查找、棧回溯實現(xiàn)、內(nèi)存掃描、動態(tài)代碼生成和重定位等功能,可以說是上層豐富功能的關(guān)鍵基礎(chǔ)設(shè)施。

          Interceptor

          Interceptor[4]?是 inline-hook 的封裝,從接口上看,Interceptor 的使用方法大致如下:

          GumInterceptor?*?interceptor;
          GumInvocationListener?*?listener;
          gum_init?();
          interceptor?=?gum_interceptor_obtain?();
          listener?=?g_object_new?(EXAMPLE_TYPE_LISTENER,?NULL);

          //?開始?hook?`open`?函數(shù)
          gum_interceptor_begin_transaction?(interceptor);
          gum_interceptor_attach_listener?(interceptor,
          ??????GSIZE_TO_POINTER?(gum_module_find_export_by_name?(NULL,?"open")),
          ??????listener,
          ??????GSIZE_TO_POINTER?(EXAMPLE_HOOK_OPEN));
          gum_interceptor_end_transaction?(interceptor);

          //?測試?hook?效果
          close?(open?("/etc/hosts",?O_RDONLY));

          //?結(jié)束?hook
          gum_interceptor_detach_listener?(interceptor,?listener);
          g_object_unref?(listener);
          g_object_unref?(interceptor);

          其中?listener?是一個?GumInvocationListener *?類型的接口,用戶可以自行實現(xiàn)?on_enter?和?on_leave?來控制注入的邏輯:

          typedef?void?(*?GumInvocationCallback)?(GumInvocationContext?*?context,?gpointer?user_data);

          struct?_GumInvocationListenerInterface?{
          ??GTypeInterface?parent;

          ??void?(*?on_enter)?(GumInvocationListener?*?self,
          ??????GumInvocationContext?*?context);
          ??void?(*?on_leave)?(GumInvocationListener?*?self,
          ??????GumInvocationContext?*?context);
          };

          一般直接從 release 中下載?frida-gum-devkit?去靜態(tài)編譯使用,內(nèi)部包含了一個靜態(tài)庫、頭文件和示例程序,并且頭文件有豐富的注釋說明:

          $?tar?-xvf?frida-gum-devkit-15.1.17-android-arm.tar.xz
          x?frida-gum.h
          x?libfrida-gum.a
          x?frida-gum-example.c

          frdia-gum 使用 C 語言來編寫,但主要依賴于?glib[5]?去實現(xiàn)面向?qū)ο缶幊蹋@是由 GNOME 開發(fā)并從 GTK 中分離出來的一個基礎(chǔ)庫。使用純 C 編程的話這是個非常好用的工具庫,其提供了許多有用的數(shù)據(jù)類型、宏定義、類型轉(zhuǎn)換、字符串/文件處理以及線程的抽象支持。在 Linux 內(nèi)核中也能看到很多 glib 封裝設(shè)計的思想在,因此若是有 C 開發(fā)需求比如嵌入式場景,也可以考慮使用 glib 去進行輔助。

          Inline hook 的原理這里就不展開了,但是值得一提的是在構(gòu)造目標函數(shù)的跳板函數(shù)時需要根據(jù)用戶指定的地址去動態(tài)生成跳板代碼,因此使用了 GumWriter 來實現(xiàn);同時因為要備份原函數(shù)還需要使用 GumRelocator 去動態(tài)修復(fù)(重定位)地址相關(guān)的代碼。frida-gum 實現(xiàn)了 5 種指令集的代碼生成和重定向功能(基于 capstone),分別是 arm、arm64、x86、x64 和 mips。以 arm64 為例,其實現(xiàn)在?gum/backend-arm64/guminterceptor-arm64.c?的?_gum_interceptor_backend_create_trampoline?中。

          拓展閱讀:

          • ??frida-example[6]

          • ??Profiling C++ code with Frida[7]

          • ??FRIDA-GUM源碼解讀[8]

          Stalker

          Stalker[9]?又稱為尾行癡漢(?),可以實現(xiàn)指定線程中所有函數(shù)、所有基本塊、甚至所有指令的跟蹤。

          一般而言調(diào)試器實現(xiàn)函數(shù)或者指令跟蹤是通過斷點,但是斷點有幾個問題。一是斷點容易被反調(diào)試檢測到,軟件斷點自不必說,會在原指令中插入斷點指令,如果函數(shù)本身有完整性校驗的話會檢測出異常,而硬件斷點本身也很容易被檢測或者破壞掉;斷點的另一個問題是性能,代碼觸發(fā)斷點后會先中斷到內(nèi)核態(tài),然后再返回到用戶態(tài)(調(diào)試器)執(zhí)行跟蹤回調(diào),處理完后再返回內(nèi)核態(tài),然后再回到用戶態(tài)繼續(xù)執(zhí)行,這來來回回的黃花菜都涼了。

          因此,Stalker 不是使用斷點,而是基于動態(tài)修改代碼的方式實現(xiàn)。其基本思想很簡單,在線程即將執(zhí)行下一條指令前,先將目標指令拷貝一份到新建的內(nèi)存中,然后在新的內(nèi)存中對代碼進行插樁,如下圖所示:

          img

          這其中使用到了代碼動態(tài)重編譯的方法,好處是原本的代碼沒有被修改,因此即便代碼有完整性校驗也不影響,另外由于執(zhí)行過程都在用戶態(tài),省去了多次中斷內(nèi)核切換,性能損耗也達到了可以接受的水平。由于代碼的位置發(fā)生了改變,如前文 Interceptor 一樣,同樣要對代碼進行重定位的修復(fù)。

          Stalker 中可以以任意單位基本進行跟蹤,越細粒度的跟蹤性能損耗越高。比如在流量分析時可以只針對 SSL_read、SSL_write 進行跟蹤獲取流量明文;又比如在 Fuzzing 中經(jīng)常以基本塊為單位進行覆蓋率反饋,此時可以以塊為單位進行跟蹤,只對 blx、br、jmp、call 等跳轉(zhuǎn)指令進行插樁;或者針對某些自定義的加密算法,可以以指令級別進行跟蹤,通過動態(tài)運行的路徑記錄來輔助逆向分析還原加密流程,等等等等。

          此外 Stalker 中還針對動態(tài)編譯進行了大量的性能優(yōu)化,進一步減少運行時的額外開銷。由于動態(tài)重編譯與系統(tǒng)架構(gòu)關(guān)系較大,代碼中需要對當前平臺的指令集進行準確的歸類和處理,因此當前 Stalker 只支持常用的 ARM64、X86 和 IA32 架構(gòu),而且對于動態(tài)自修改的代碼支持也不完善,但即便如此也足以滿足大部分日常的需求了。

          拓展閱讀:

          • ??Anatomy of a code tracer[10]

          • ??frida docs - stalker[11]

          內(nèi)存監(jiān)控

          frida-gum 中另外一個很有意思的模塊是?MemoryAccessMonitor,可以實現(xiàn)對指定內(nèi)存區(qū)間的訪問監(jiān)控,在目標內(nèi)存區(qū)間發(fā)生讀寫行為時可以觸發(fā)用戶指定的回調(diào)函數(shù)。

          通過閱讀源碼發(fā)現(xiàn)這個功能的實現(xiàn)方法非常簡潔,本質(zhì)上是將目標內(nèi)存頁設(shè)置為不可讀寫,這樣在發(fā)生讀寫行為時會觸發(fā)事先注冊好的中斷處理函數(shù),其中會調(diào)用到用戶使用?gum_memory_access_monitor_new?注冊的回調(diào)方法中。

          gboolean
          gum_memory_access_monitor_enable?(GumMemoryAccessMonitor?*?self,
          ??????????????????????????????????GError?**?error)
          {
          ??if?(self->enabled)
          ????return?TRUE;
          ??//?...
          ??self->exceptor?=?gum_exceptor_obtain?();
          ??gum_exceptor_add?(self->exceptor,?gum_memory_access_monitor_on_exception,
          ??????self);
          ??//?...
          }

          事實上目前市面上也有一些加殼的應(yīng)用使用這種方法來進行加固,在?art::DexFile?的某些地址區(qū)間中加上內(nèi)存監(jiān)控的功能,一旦發(fā)現(xiàn)讀取行為就崩潰退出以實現(xiàn)代碼保護的目的。至于效果嘛,就見仁見智了?!?/p>

          其他

          作為一個 inline-hook 框架,自然還需要有一定的內(nèi)省能力,比如搜索當前虛擬內(nèi)存中已加載的動態(tài)庫信息,在動態(tài)庫中查找符號地址,在內(nèi)存中搜索數(shù)據(jù)/代碼等功能,這些 frida-gum 中都有實現(xiàn),并且支持 Linux、Darwin、FreeBSD、QNX、Windows 等操作系統(tǒng)環(huán)境。在不同平臺中往往有不同的實現(xiàn)方法,比如搜索符號在 Android 中就是通過 linker 的一些內(nèi)部函數(shù)去實現(xiàn)模塊查找并通過解析 ELF 的方式去定位符號。對其中哪些平臺的實現(xiàn)感興趣的,去查看對應(yīng) backend 的代碼即可。

          gum-js

          frida-gum?雖然功能強大,但由于使用了 C 語言的接口,調(diào)用起來各種宏和 typedef 頗為不便,因此就有了以此為基礎(chǔ)的上層封裝(binding)。目前倉庫中只有兩種語言的封裝,分別是 C++(gumpp) 和 JavaScript(gumjs),后者也是 frida-core 和大多數(shù)人日常使用的接口底座。

          大多數(shù)人一開始接觸 frida 應(yīng)該也和筆者一樣很奇怪為什么 frida 使用 JavaScript 作為編寫 hook 的語言,為什么還特地集成了一個 JS 腳本引擎,具體是如何實現(xiàn)的,……

          其實這個問題可以簡化成: JS 代碼如何與 native 代碼進行交互?我們以 v8 為例,先看如何從 native 代碼中去解析 JS 腳本。一般來說,v8 解析腳本的流程可以概況如下:

          int?main(int?argc,?char*?argv[])?{
          ??//?1.?初始化?V8.
          ??v8::V8::InitializeICUDefaultLocation(argv[0]);
          ??v8::V8::InitializeExternalStartupData(argv[0]);
          ??std::unique_ptr?platform?=?v8::platform::NewDefaultPlatform();
          ??v8::V8::InitializePlatform(platform.get());
          ??v8::V8::Initialize();
          ??
          ??//?2.?新建?Isolate?并將其設(shè)為當前默認.
          ??v8::Isolate::CreateParams?create_params;
          ??create_params.array_buffer_allocator?=?v8::ArrayBuffer::Allocator::NewDefaultAllocator();
          ??v8::Isolate*?isolate?=?v8::Isolate::New(create_params);

          ??{
          ????v8::Isolate::Scope?isolate_scope(isolate);
          ????v8::HandleScope?handle_scope(isolate);
          ????//?3.?創(chuàng)建上下文
          ????v8::Local?context?=?v8::Context::New(isolate);
          ????//?4.?進入?JS?腳本編譯和執(zhí)行的上下文
          ????v8::Context::Scope?context_scope(context);
          ????//?5.?創(chuàng)建腳本
          ????v8::Local?source?=
          ????????v8::String::NewFromUtf8(isolate,?"'Hello'?+?',?World!'",
          ????????????????????????????????v8::NewStringType::kNormal)
          ????????????.ToLocalChecked();
          ????//?6.?編譯腳本
          ????v8::Local?script?=
          ????????v8::Script::Compile(context,?source).ToLocalChecked();
          ????//?7.?執(zhí)行腳本并獲取結(jié)果
          ????v8::Local?result?=?script->Run(context).ToLocalChecked();
          ????//?8.?將結(jié)果轉(zhuǎn)換為字符串并打印輸出
          ????v8::String::Utf8Value?utf8(isolate,?result);
          ????printf("%s\n",?*utf8);
          ??}

          ??//?n.?關(guān)閉?V8,釋放相關(guān)資源
          ??isolate->Dispose();
          ??v8::V8::Dispose();
          ??v8::V8::ShutdownPlatform();
          ??delete?create_params.array_buffer_allocator;

          如果我們不僅僅是從 JS 中獲取執(zhí)行結(jié)果,而是需要向 JS 動態(tài)傳遞參數(shù)呢?比如在 frida 中?Interceptor.attach?的參數(shù)之一實際上就是目標函數(shù)(指令)的 native 地址值,我們需要在 JS 中將這個值進行處理并傳遞到?frida-gum?的?gum_interceptor_attach_listener?函數(shù)中。

          這種情況下需要先在 JS 腳本中定義一個函數(shù),姑且稱之為?Attach,前面的步驟都一樣,先創(chuàng)建腳本并編譯執(zhí)行,執(zhí)行之后可以從當前的 Isolate 中獲取到目標函數(shù)對象,進而轉(zhuǎn)換為可以調(diào)用的 Function 類型從而進行調(diào)用,如下所示:

          //?1-7?步類似,執(zhí)行腳本
          //?定義函數(shù)名稱
          Local?attach_name?=
          ??????String::NewFromUtf8Literal(GetIsolate(),?"Attach");
          //?判斷對象是否存在,以及類型是否是函數(shù)
          Local?attach_val;
          if?(!context->Global()->Get(context,?attach_name).ToLocal(&attach_val)?||?!attach_val->IsFunction())?{
          ????return?false;
          }
          //?如果是,則轉(zhuǎn)換為函數(shù)類型
          Local?attach_func?=?attach_val.As();

          //?將調(diào)用參數(shù)封裝為?JS?對象
          Local?obj?=
          ??????templ->NewInstance(GetIsolate()->GetCurrentContext()).ToLocalChecked();
          obj->SetInternalField(0,?0xdeadbeef);
          //?使用自定義的參數(shù)調(diào)用該?JS?函數(shù),并獲取返回結(jié)果
          TryCatch?try_catch(GetIsolate());
          const?int?argc?=?1;
          Local?argv[argc]?=?{obj};
          Local?result;
          attach_func->Call(context,?context->Global(),?argc,?argv).ToLocal(&result);

          有了這個理論基礎(chǔ),后面的實現(xiàn)就是工程化編碼的問題了。早期 gum-js 默認使用?Duktape[12]?作為腳本引擎進行集成,后來也增加了對?QuickJS[13]?和?V8[14]?的支持,實際上 frida 對于不同的腳本引擎也做了一層封裝,可以對不同引擎的接口實現(xiàn)透明的切換。

          gum-js 同樣提供了類似的 devkit,里面包含對應(yīng)的接口介紹和示例程序,可自行下載食用。

          代碼調(diào)試

          軟件有 Bug 其實是在所難免的事情,尤其是對于 frdia 這種偏底層而又復(fù)雜的項目而言,因此在出現(xiàn)未知錯誤時需要能夠通過調(diào)試器或者日志去進行定位。另外從學(xué)習(xí)的角度來說,也希望可以通過實時調(diào)試去加深對于 frida 中一些操作比如 trampoline/shellcode 生成的理解。

          首先是調(diào)試。在 frida 主倉庫的?config.mk?中去掉?--strip?以保留符號,或者加上 make 參數(shù)?FRIDA_ASAN=true?加上 ASAN 信息輔助定位內(nèi)存問題。在 hook 代碼中可以加入以下代碼來等待調(diào)試器的掛載:

          while?(!gum_process_is_debugger_attached?())
          {
          ??g_printerr?("Waiting?for?debugger?in?PID?%u...\n",?getpid?());
          ??g_usleep?(G_USEC_PER_SEC);
          }

          或者直接在 JS 代碼中實現(xiàn):

          while?(!Process.isDebuggerAttached())?{
          ??console.log('Waiting?for?debugger?in?PID:',?Process.id);
          ??Thread.sleep(1);
          }

          除了使用調(diào)試器進行調(diào)試,也可以使用 print 大法在代碼的關(guān)鍵位置插入打印日志來幫助理解代碼的執(zhí)行流程,常用的打印函數(shù)是:

          //?輸出到系統(tǒng)日志中
          g_info?("Test?%d\n",?__line__);

          //?輸出到?stderr?
          g_printerr?("Test?%d\n",?__line__);

          作者還專門寫過一個 gist 來介紹使用日志功能來輔助分析的 Tricks[15],主要是通過 patch 實現(xiàn)向?/data/local/tmp?中寫入指定的日志文件。

          后記

          本文介紹了 frida 的主要工程結(jié)構(gòu)以及 frida-gum 和 gumjs 這兩大基礎(chǔ)設(shè)施的基本原理。當然文章所提及的也只是冰山一角,感興趣的人自然會去閱讀對應(yīng)模塊的具體實現(xiàn)。希望這個系列能起到個拋磚引玉的作用,讓后來者不至于搜索 frida internal 或者 frida 源碼解析時再經(jīng)歷一遍鄙人的痛苦 :)

          參考資料

          • ??frida.re/Hacking[16]

          • ??Ole André Vadla Ravn?s - Frida: The engineering behind the reverse-engineering (Video)[17]

          引用鏈接

          [1]?Frida Internal - Part 1: 架構(gòu)、Gum 與 V8 (本文):?https://evilpan.com/2022/04/05/frida-internal/
          [2]?frida:?https://github.com/frida/frida
          [3]?frida-gum:?https://github.com/frida/frida-gum
          [4]?Interceptor:?https://github.com/frida/frida-gum/blob/main/gum/guminterceptor.h
          [5]?glib:?https://wiki.gnome.org/Projects/GLib
          [6]?frida-example:?https://gist.github.com/oleavr/3edc47c9f69eb048de9d70ed45998f9c
          [7]?Profiling C++ code with Frida:?https://lief-project.github.io/blog/2021-03-10-profiling-cpp-code-with-frida/
          [8]?FRIDA-GUM源碼解讀:?http://jmpews.github.io/2017/06/27/pwn/frida-gum%E6%BA%90%E7%A0%81%E8%A7%A3%E8%AF%BB/
          [9]?Stalker:?https://frida.re/docs/stalker
          [10]?Anatomy of a code tracer:?https://medium.com/@oleavr/anatomy-of-a-code-tracer-b081aadb0df8
          [11]?frida docs - stalker:?https://frida.re/docs/stalker
          [12]?Duktape:?https://duktape.org/
          [13]?QuickJS:?https://bellard.org/quickjs/
          [14]?V8:?https://v8.dev/docs/
          [15]?使用日志功能來輔助分析的 Tricks:?https://gist.github.com/oleavr/00d71868d88d597ee322a5392db17af6
          [16]?frida.re/Hacking:?https://frida.re/docs/hacking/
          [17]?Ole André Vadla Ravn?s - Frida: The engineering behind the reverse-engineering (Video):?https://www.youtube.com/watch?v=uc1mbN9EJKQ&ab_channel=OSDCNordic


          瀏覽 167
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                    <th id="afajh"><progress id="afajh"></progress></th>
                    息子交尾一区二区三区 | 欧美激情精品成人 | 欧美成人网在线观看 | 黄色片在线播放 | 18禁欧美日韩 |