<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>

          iOS 恢復調(diào)用棧(適配iOS14)

          共 23449字,需瀏覽 47分鐘

           ·

          2021-06-06 19:13

          作者 | heitanBC 
          來源 | iOS'RE, 點擊閱讀原文查看作者更多文章

          0x00 前言

          之前楊君大佬寫過這個工具:iOS符號表恢復 65,恢復后堆??娠@示函數(shù)信息。

          不知道為何我使用后,堆棧依舊沒有顯示出OC函數(shù)來。所以整理下資料,在restore-symbol 18工程上再適配 iOS14。

          不同場景下,也可以用xia0大佬寫過的 Frida調(diào)用棧符號恢復 28,利用 runtime 機制動態(tài)獲取函數(shù)信息,近似匹配來恢復調(diào)用棧。

          0x01 符號與調(diào)用棧

          1. 調(diào)用棧

          在分析程序 crash 時,一般會查看調(diào)用棧來定位問題。如果可執(zhí)行文件沒有經(jīng)過 strip,符號表還保存著符號信息,調(diào)用棧是可以顯示函數(shù)名稱的。

          • 無符號調(diào)用棧

                "0   AlipayWallet                        0x0000000107545f18 _ZN7gcanvas14GCommandBuffer10parseArrayIfEEbPT_m + 29852464",
          "1 AlipayWallet 0x0000000107567f94 _ZN7gcanvas14GCommandBuffer10parseArrayIfEEbPT_m + 29991852",
          "2 AlipayWallet 0x0000000107571b98 _ZN7gcanvas14GCommandBuffer10parseArrayIfEEbPT_m + 30031792",
          "3 AlipayWallet 0x00000001075a82d8 _ZN7gcanvas14GCommandBuffer10parseArrayIfEEbPT_m + 30254832",
          "4 AlipayWallet 0x000000010758f778 _ZN7gcanvas14GCommandBuffer10parseArrayIfEEbPT_m + 30153616",
          "5 AlipayWallet 0x00000001075745b4 _ZN7gcanvas14GCommandBuffer10parseArrayIfEEbPT_m + 30042572",
          "6 AlipayWallet 0x0000000107557770 _ZN7gcanvas14GCommandBuffer10parseArrayIfEEbPT_m + 29924232",
          "7 libsystem_pthread.dylib 0x0000000183459d8c _pthread_start + 156",
          "8 libsystem_pthread.dylib 0x000000018345d76c thread_start + 8"

          沒有符號的調(diào)用棧無法給出有效函數(shù)信息,導致無法定位問題所在。

          2. 符號

          • 符號(Symbol:類名、函數(shù)名或變量名稱為符號名 (Symbol Name);

          • 按類型分,符號可以分三類:

            • 全局符號:目標文件外可見的符號,可以被其他目標文件引用,或者需要其他目標文件定義;

            • 局部符號:只在目標文件內(nèi)可見的符號,指只在目標文件內(nèi)可見的函數(shù)和變量;

            • 調(diào)試符號:包括 行號 信息的調(diào)試符號信息,行號信息中記錄了函數(shù)和變量所對應(yīng)的 文件和文件行號。

          • 符號表(Symbol Table:符號表是 內(nèi)存地址與函數(shù)名、文件名、行號 的映射表;每個定義的符號有一個對應(yīng)的值,叫做 符號值(Symbol Value),對于變量和函數(shù)來說,符號值就是他們的地址;符號表元素如下所示:

          < 起始地址 > < 結(jié)束地址 > < 函數(shù) > [< 文件名: 行號 >]
          • dSYM(debug symbols):是 iOS 的 符號表文件,存儲 16 進制 地址信息和符號的映射文件;文件名通常為:xxx.app.dSYM,類似 Android 構(gòu)建 release 產(chǎn)生的 mapping 文件;利用 dSYM 文件文件,可以將堆棧信息中地址信息還原成對應(yīng)的符號,幫助問題排查;

          App relaese 包一般會 strip 掉局部符號和調(diào)試符號以減小包體積,去掉這些符號不影響 App 正常運行,也可以一定程度上保護 App。

          App 被 strip 掉符號后,調(diào)用棧無法顯示函數(shù)名稱。如果得到函數(shù)名稱與地址,恢復符號表,將符號表 patch 到可執(zhí)行文件后,理論上調(diào)用棧就可以顯示出信息。

          0x02 獲取函數(shù)信息

          1. objective-c 函數(shù)

          一般情況下 App strip 掉符號后,如果不借助外部 dSYM 文件,是無法恢復的函數(shù)信息,比如 C 函數(shù)在可執(zhí)行文件僅剩地址。

          objective-c 函數(shù)信息除了保存在符號表中,還保存在其他段中

          __TEXT.__objc_methname - Method names for locally implemented methods
          __TEXT.__objc_classname - Names for locally implemented classes
          __TEXT.__objc_methtype - Types for locally implemented method types
          __DATA.__objc_classlist - An array of pointers to ObjC classes
          __DATA.__objc_nlclslist - An array of pointers to classes who implement +load
          __DATA.__objc_catlist - List of ObjC categories
          __DATA.__objc_protolist - List of ObjC protocols
          __DATA.__objc_imageinfo - Version info, not really useful
          __DATA.__objc_const - Constant data, i.e. class_ro_t data
          __DATA.__objc_selrefs - External references to selectors
          __DATA.__objc_protorefs - External references to protocols
          __DATA.__objc_classrefs - External references to other classes
          __DATA.__objc_superrefs - External references to super classes
          __DATA.__objc_ivar - Offsets to ObjC properties
          __DATA.__objc_data - Misc ObjC storage, notably ObjC classes

          strip 僅刪除符號表中的數(shù)據(jù),所以 OC 符號可以恢復。

          2. 解析 __objc_* section

          從 objc 的開源代碼來看如何解析 __objc_* section。

          i. 現(xiàn)用 class 結(jié)構(gòu)

          typedef struct objc_class *Class;

          struct objc_object {
          private:
          isa_t isa; // 一個指針大小
          ...
          }
          struct objc_class : objc_object {
          // Class ISA;
          Class superclass; /
          / 一個指針大小
          cache_t cache; /
          / 兩個指針大小 // formerly cache pointer and vtable
          class_data_bits_t bits; /
          / 一個指針大小 // class_rw_t * plus custom rr/alloc flags
          ...
          }

          class_data_bits_t 是一個指針大小的結(jié)構(gòu)體,保存著類的方法、屬性、遵循的協(xié)議等信息。

          struct class_data_bits_t {
          // Values are the FAST_ flags above.
          uintptr_t bits;
          ...

          class_rw_t* data() {return (class_rw_t *)(bits & FAST_DATA_MASK);
          }

          const class_ro_t *safe_ro() const {class_rw_t *maybe_rw = data();
          if (maybe_rw->flags & RW_REALIZED) {
          // maybe_rw is rw
          return maybe_rw->ro();} else {
          // maybe_rw is actually ro
          return (class_ro_t *)maybe_rw;
          }
          }

          }

          在編譯期間,class_ro_t 結(jié)構(gòu)體就已經(jīng)確定,objc_class 中的 bits 的 data 部分存放著該結(jié)構(gòu)體的地址。

          運行 runtime 的 realizeClass 方法時,會生成 class_rw_t 結(jié)構(gòu)體,該結(jié)構(gòu)體包含了 class_ro_t,并且更新 data 部分,換成 class_rw_t 結(jié)構(gòu)體的地址。

          所以保存在 Mach-O 可執(zhí)行文件中的結(jié)構(gòu)是 class_ro_t。

          在 iOS14 后,class_rw_t 結(jié)構(gòu)體發(fā)生較大變化,class_ro_t 結(jié)構(gòu)體未變化。

          struct class_ro_t {
          uint32_t flags;
          uint32_t instanceStart;
          uint32_t instanceSize;
          #ifdef __LP64__
          uint32_t reserved;
          #endif

          const uint8_t * ivarLayout;

          const char * name;
          method_list_t * baseMethodList;
          protocol_list_t * baseProtocols;
          const ivar_list_t * ivars;

          const uint8_t * weakIvarLayout;
          property_list_t *baseProperties;
          };

          ii、方法、屬性、協(xié)議

          class_ro_t 里面的 method_list_t 、 ivar_list_t 和 property_list_t 都使用 entsize_list_tt 的結(jié)構(gòu)體模板;iOS 14 method_list_t 有較大改變

          // iOS 14 以下
          template <typename Element, typename List, uint32_t FlagMask>
          struct entsize_list_tt {
          uint32_t entsizeAndFlags;
          uint32_t count;
          Element first;
          // other code
          uint32_t entsize() const {return entsizeAndFlags & ~FlagMask;}
          }

          struct method_list_t : entsize_list_tt<method_t, method_list_t, 0x3> {...};

          struct ivar_list_t : entsize_list_tt<ivar_t, ivar_list_t, 0> {...};

          struct property_list_t : entsize_list_tt<property_t, property_list_t, 0> {
          };


          // iOS14 以下
          struct method_t {
          SEL name;
          const char *types;
          MethodListIMP imp;
          ...
          };
          • 方法

          // iOS 14
          template <typename Element, typename List, uint32_t FlagMask, typename PointerModifier = PointerModifierNop>
          struct entsize_list_tt {
          uint32_t entsizeAndFlags;
          uint32_t count;
          uint32_t entsize() const {return entsizeAndFlags & ~FlagMask;}
          uint32_t flags() const {return entsizeAndFlags & FlagMask;}
          };

          struct method_list_t : entsize_list_tt<method_t, method_list_t, 0xffff0003, method_t::pointer_modifier> {uint32_t indexOfMethod(const method_t *meth) const {
          uint32_t i =
          (uint32_t)(((uintptr_t)meth - (uintptr_t)this)/ entsize());
          ASSERT(i < count);
          return i;
          }

          bool isSmallList() const {return flags() & method_t::smallMethodListFlag;
          }
          };


          struct method_t {
          static const uint32_t smallMethodListFlag = 0x80000000;

          method_t(const method_t &other) = delete;

          // The representation of a "big" method. This is the traditional
          // representation of three pointers storing the selector, types
          // and implementation.
          struct big {
          SEL name;
          const char *types;
          MethodListIMP imp;
          };

          private:
          bool isSmall() const {return ((uintptr_t)this & 1)== 1;}

          // The representation of a "small" method. This stores three
          // relative offsets to the name, types, and implementation.
          struct small {
          // The name field either refers to a selector (in the shared
          // cache)or a selref (everywhere else).
          RelativePointer<const void *> name;
          RelativePointer<const char *> types;
          RelativePointer<IMP> imp;

          bool inSharedCache() const {
          return (CONFIG_SHARED_CACHE_RELATIVE_DIRECT_SELECTORS &&
          objc::inSharedCache((uintptr_t)this));}
          };

          small &small() const {ASSERT(isSmall());
          return *(struct small *)((uintptr_t)this & ~(uintptr_t)1);}
          };

          方法以 method_t 結(jié)構(gòu)保存,iOS14 以上該結(jié)構(gòu)發(fā)生巨大變化。struct big 在 64 位的系統(tǒng)上會占用 24 字節(jié),name、types、imp 分別占用 64 bit 大小,與之前一樣。但是 struct small 占用 12 字節(jié),name、types、imp 分別占用 32 bit 大小。

          name、types、imp 分別指向方法的 名稱、參數(shù)數(shù)據(jù)、函數(shù)指針,蘋果考慮到鏡像中的方法都是固定的,不會跑到其他鏡像中去。其實不需要 64 位尋址的指針,只需要 32 位即可 (多余 32 位尋址,可執(zhí)行文件在內(nèi)存中要超過 4G)。small 結(jié)構(gòu)里面的數(shù)據(jù),都是相對地址偏移,不是內(nèi)存中的具體位置。如果要還原,需要進行計算。

          template <typename T>
          struct RelativePointer: nocopy_t {
          int32_t offset;

          T get() const {if (offset == 0)
          return nullptr;
          uintptr_t base = (uintptr_t)&offset;
          uintptr_t signExtendedOffset = (uintptr_t)(intptr_t)offset;
          uintptr_t pointer = base + signExtendedOffset;
          return (T)pointer;
          }
          };

          3. 手動解析

          現(xiàn)在從 mach-o 文件來看怎么一步步找到 method 信息。

          __objc_classlist 節(jié)保存所有類的地址,以第一個 class 為例,地址為:0x010001a340

          跳轉(zhuǎn)到 0x010001a340 地址處,紅框所包即 class 結(jié)構(gòu)所保存的值。方法等信息保存在 class_data_bits_t 處,class_data_bits_t 的值為 0x01000187a8。

          跳轉(zhuǎn)到 0x01000187a8 地址處,里面保存的為 class_ro_t 結(jié)構(gòu)。

          struct class_ro_t {
          uint32_t flags;
          uint32_t instanceStart;
          uint32_t instanceSize;
          #ifdef __LP64__
          uint32_t reserved;
          #endif

          const uint8_t * ivarLayout;

          const char * name;
          method_list_t * baseMethodList;
          protocol_list_t * baseProtocols;
          const ivar_list_t * ivars;

          const uint8_t * weakIvarLayout;
          property_list_t *baseProperties;
          };

          對照結(jié)構(gòu)體內(nèi)容,可知 baseMethodList 的地址為 0x0100010c78。

          前 64 位 分別保存 baseMethodList 的 flag 和 count,flag 判斷后續(xù)保存的 method_t 結(jié)構(gòu)是 struct big 還是 struct small,

          如果是 big 的話,以此取三個 64 位值,就可以得到 函數(shù)名稱、類型、函數(shù)地址。

          struct big {
          SEL name;
          const char *types;
          MethodListIMP imp;
          };

          smallMethodListFlag 值為 0x80000000, flag 值為 0x800000c0,進行 位與 運算,說明以下的 method_t 結(jié)構(gòu)是 small 結(jié)構(gòu)。

          struct small {
          // The name field either refers to a selector (in the shared
          // cache)or a selref (everywhere else).
          RelativePointer<const void *> name;
          RelativePointer<const char *> types;
          RelativePointer<IMP> imp;

          bool inSharedCache() const {
          return (CONFIG_SHARED_CACHE_RELATIVE_DIRECT_SELECTORS &&
          objc::inSharedCache((uintptr_t)this));}
          };

          template <typename T>
          struct RelativePointer: nocopy_t {
          int32_t offset;

          T get() const {if (offset == 0)
          return nullptr;
          uintptr_t base = (uintptr_t)&offset;
          uintptr_t signExtendedOffset = (uintptr_t)(intptr_t)offset;
          uintptr_t pointer = base + signExtendedOffset;
          return (T)pointer;
          }
          };
          • name

          name 的值為 0x9398,對應(yīng)到 RelativePointer 的源代碼,offset 值為 0x9398。

          源碼需要取 offset 的地址,才能計算出真正 name 的地址。offset 的地址可以計算出為 0x0100010c80,baseMethodList 的地址 + sizeof(flag) + sizeof(count) = 0x0100010c78 + 0x4 + 0x4。

          name 真正地址即為 (int)0x9398 + 0x0100010c80 = 0x10001A018

          • type

          Type 保存函數(shù)的參數(shù)和返回值類型,同理可計算出 type 的真正地址:

          (int)0x1ffa + 0x0100010c84 = 0x0100012c7e

          • imp

          imp 是函數(shù)地址,同理可以用計算出:

          (int)0xffff5fd8 + 0x0100010c88 = 0x0100006c60 (這里 0xffff5fd8 是負數(shù))

          0x03 恢復符號表

          符號在符號表中用 nlist 結(jié)構(gòu)體保存。

          1. nlist 結(jié)構(gòu)體

          // Describes an entry in the symbol table. It’s declared in /usr/include/mach-o/nlist.h.
          struct nlist
          {
          union {
          #ifndef __LP64__
          char *n_name; /* for use when in-core */
          #endif
          long n_strx;
          } n_un;
          unsigned char n_type;
          unsigned char n_sect;
          short n_desc;

          #ifdef __LP32__
          /*32 位中 4byte*/
          unsigned long n_value;
          #else
          /*64 位中 8byte*/
          unsigned long long n_value;
          #endif

          };

          i. u_un(n_strx)

          u_un 的 n_strx 代表該符號在字符串表中偏移。

          假如 n_strx 值為 0x22,那么該符號名稱字符串相當于文件的偏移為:stroff + 0x22,空串為偏移為 0,對應(yīng)著 0x00。

          ii. n_type

          n_type 字段主要用來標識符號的種類。n_type 擁有 8 個 bit,分配如下。

          /*
          * Symbols with a index into the string table of zero (n_un.n_strx == 0) are
          * defined to have a null, "", name. Therefore all string indexes to non null
          * names must not have a zero string index. This is bit historical information
          * that has never been well documented.
          */


          /*
          * The n_type field really contains four fields:
          * unsigned char N_STAB:3,
          * N_PEXT:1,
          * N_TYPE:3,
          * N_EXT:1;
          * which are used via the following masks.
          */

          #define N_STAB 0xe0 /* if any of these bits set, a symbolic debugging entry */
          #define N_PEXT 0x10 /* private external symbol bit */
          #define N_TYPE 0x0e /* mask for the type bits */
          #define N_EXT 0x01 /* external symbol bit, set for external symbols */

          /*
          * Only symbolic debugging entries have some of the N_STAB bits set and if any
          * of these bits are set then it is a symbolic debugging entry (a stab). In
          * which case then the values of the n_type field (the entire field) are given
          * in <mach-o/stab.h>
          */


          /*
          * Values for N_TYPE bits of the n_type field.
          */

          #define N_UNDF 0x0 /* undefined, n_sect == NO_SECT */
          #define N_ABS 0x2 /* absolute, n_sect == NO_SECT */
          #define N_SECT 0xe /* defined in section number n_sect */
          #define N_PBUD 0xc /* prebound undefined (defined in a dylib) */
          #define N_INDR 0xa /* indirect */
          • bit[0:1] 是 N_EXT,表示是外部符號。

          • bit[1:4] 是 N_TYPE,表示符號類型。

          分 N_UNDF 未定義、N_ABS 絕對地址、N_SECT 本地符號、N_PBUD 預(yù)綁定符號、N_INDR 同名符號幾種類型。

          • bit[4:5] 是 N_PEXT,表示私有外部符號。

          • bit[5:8] 是 N_STAB,表示調(diào)試符號,具體定義在 /usr/include/mach-o/stab.h。

              nlist_64 fields: n_value         n_type n_sect  n_desc  n_strx  
          0000000100006944 0e 01 0000 00001bc4 -[ViewController locationManager:didUpdateLocations:]
          00000001000077ac 0e 01 0000 00001bfa -[ViewController didReceiveMemoryWarning]
          00000001000077f8 0e 01 0000 00001c24 -[ViewController locationManager:didUpdateHeading:]
          0000000100007950 0e 01 0000 00001c58 -[ViewController writePermissionToFile:]

          stab 類型

          /*
          * Symbolic debugger symbols. The comments give the conventional use for
          *
          * .stabs "n_name", n_type, n_sect, n_desc, n_value
          *
          * where n_type is the defined constant and not listed in the comment. Other
          * fields not listed are zero. n_sect is the section ordinal the entry is
          * refering to.
          */

          #define N_GSYM 0x20 /* global symbol: name,,NO_SECT,type,0 */
          #define N_FNAME 0x22 /* procedure name (f77 kludge): name,,NO_SECT,0,0 */
          #define N_FUN 0x24 /* procedure: name,,n_sect,linenumber,address */
          #define N_STSYM 0x26 /* static symbol: name,,n_sect,type,address */
          #define N_LCSYM 0x28 /* .lcomm symbol: name,,n_sect,type,address */
          #define N_BNSYM 0x2e /* begin nsect sym: 0,,n_sect,0,address */
          #define N_AST 0x32 /* AST file path: name,,NO_SECT,0,0 */
          #define N_OPT 0x3c /* emitted with gcc2_compiled and in gcc source */
          #define N_RSYM 0x40 /* register sym: name,,NO_SECT,type,register */
          #define N_SLINE 0x44 /* src line: 0,,n_sect,linenumber,address */
          #define N_ENSYM 0x4e /* end nsect sym: 0,,n_sect,0,address */
          #define N_SSYM 0x60 /* structure elt: name,,NO_SECT,type,struct_offset */
          #define N_SO 0x64 /* source file name: name,,n_sect,0,address */
          #define N_OSO 0x66 /* object file name: name,,0,0,st_mtime */
          #define N_LSYM 0x80 /* local sym: name,,NO_SECT,type,offset */
          #define N_BINCL 0x82 /* include file beginning: name,,NO_SECT,0,sum */
          #define N_SOL 0x84 /* #included file name: name,,n_sect,0,address */
          #define N_PARAMS 0x86 /* compiler parameters: name,,NO_SECT,0,0 */
          #define N_VERSION 0x88 /* compiler version: name,,NO_SECT,0,0 */
          #define N_OLEVEL 0x8A /* compiler -O level: name,,NO_SECT,0,0 */
          #define N_PSYM 0xa0 /* parameter: name,,NO_SECT,type,offset */
          #define N_EINCL 0xa2 /* include file end: name,,NO_SECT,0,0 */
          #define N_ENTRY 0xa4 /* alternate entry: name,,n_sect,linenumber,address */
          #define N_LBRAC 0xc0 /* left bracket: 0,,NO_SECT,nesting level,address */
          #define N_EXCL 0xc2 /* deleted include file: name,,NO_SECT,0,sum */
          #define N_RBRAC 0xe0 /* right bracket: 0,,NO_SECT,nesting level,address */
          #define N_BCOMM 0xe2 /* begin common: name,,NO_SECT,0,0 */
          #define N_ECOMM 0xe4 /* end common: name,,n_sect,0,0 */
          #define N_ECOML 0xe8 /* end common (local name): 0,,n_sect,0,address */
          #define N_LENG 0xfe /* second stab entry with length information */

          /*
          * for the berkeley pascal compiler, pc(1):
          */

          #define N_PC 0x30 /* global pascal symbol: name,,NO_SECT,subtype,line */

          可以用 nm -a 查看所有符號,顯示 stab 類型。

          0000000100008414 - 01 0000   FUN -[ViewController .cxx_destruct]
          0000000100008414 t -[ViewController .cxx_destruct]
          0000000100006e64 t -[ViewController viewDidLoad]
          0000000100006e64 - 01 0000 FUN -[ViewController viewDidLoad]
          0000000100007cd4 t -[ViewController viewWillAppear:]
          0000000100007cd4 - 01 0000 FUN -[ViewController viewWillAppear:]
          0000000000000000 - 00 0000 GSYM _OBJC_CLASS_$_ViewController
          000000010001a628 S _OBJC_CLASS_$_ViewController
          0000000000000000 - 00 0000 GSYM _OBJC_METACLASS_$_ViewController
          000000010001a650 S _OBJC_METACLASS_$_ViewController
          0000000100018718 - 13 0000 STSYM __OBJC_$_INSTANCE_METHODS_ViewController
          0000000100018718 s __OBJC_$_INSTANCE_METHODS_ViewController
          0000000100018828 s __OBJC_$_INSTANCE_VARIABLES_ViewController
          0000000100018828 - 13 0000 STSYM __OBJC_$_INSTANCE_VARIABLES_ViewController
          0000000100018850 s __OBJC_$_PROP_LIST_ViewController
          0000000100018850 - 13 0000 STSYM __OBJC_$_PROP_LIST_ViewController
          00000001000186b0 s __OBJC_CLASS_PROTOCOLS_$_ViewController
          00000001000186b0 - 13 0000 STSYM __OBJC_CLASS_PROTOCOLS_$_ViewController
          00000001000188a8 - 13 0000 STSYM __OBJC_CLASS_RO_$_ViewController
          00000001000188a8 s __OBJC_CLASS_RO_$_ViewController
          00000001000186d0 s __OBJC_METACLASS_RO_$_ViewController
          00000001000186d0 - 13 0000 STSYM __OBJC_METACLASS_RO_$_ViewController

          iii. n_sect

          section 索引,說明符號保存在哪一個 section 中,比如 -[ViewController .cxx_destruct] 保存在 TEXT 段text 節(jié)中,__text 節(jié)的索引為 1。

          iv. n_desc

          未定義符號和 weak 符號的類型等。鏈接相關(guān)。

          v. n_value

          隨著符號的種類,也就是 n_type 值的不同,n_value 也有不一樣的含義。
          如果是 N_SECT 符號,n_value 是符號所在的地址。

          0x04 patch 可執(zhí)行文件

          1. Mach-O 簡介

          Mach 則是一種操作系統(tǒng)內(nèi)核,Mach 內(nèi)核被 NeXT 公司的 NeXTSTEP 操作系統(tǒng)使用。在 Mach 上,一種可執(zhí)行的文件格是就是 Mach-O(Mach Object file format)。

          Mach-O 文件的格式如下圖所示:

          • Header:保存了 Mach-O 的一些基本信息,包括了平臺、文件類型、LoadCommands 的個數(shù)等等。

          • LoadCommands:這一段緊跟 Header,加載 Mach-O 文件時會使用這里的數(shù)據(jù)來確定內(nèi)存的分布。

          • Data:每一個 segment 的具體數(shù)據(jù)都保存在這里,這里包含了具體的代碼、數(shù)據(jù)等等。

          2. patch 加載命令

          所有 load_command 都有的數(shù)據(jù)結(jié)構(gòu)為:

          struct load_command {
          uint32_t cmd; /* type of load command */
          uint32_t cmdsize; /* total size of command in bytes */
          };

          cmd 字段代表當前加載命令的類型,cmdsize 字段代表當前加載命令的大小。

          Load Commands 直接就跟在 Header 后面,所有 command 占用內(nèi)存的總和在 Mach-O Header 里面已經(jīng)給出了。

          在加載過 Header 之后就是通過解析 LoadCommand 來加載接下來的數(shù)據(jù)了。

          i. LC_SYMTAB

          LC_SYMTAB 數(shù)據(jù)結(jié)構(gòu)如下,保存著符號表以及字符串表在 dylib 文件中的偏移和大小。

          struct symtab_command
          {
          unsigned long cmd;
          unsigned long cmdsize;
          unsigned long symoff;
          unsigned long nsyms;
          unsigned long stroff;
          unsigned long strsize;
          };

          /*
          cmd 以及 cmdsize 如上文所說:cmd 字段代表當前加載命令的類型,cmdsize 字段代表當前加載命令的大小

          symoff: image 文件開頭到符號表位置的字節(jié)偏移,符號表是 **nlist 結(jié)構(gòu)體** 數(shù)組
          nsyms: 符號個數(shù)
          stroff: image 文件開頭到字符串表位置的字節(jié)偏移
          strsize: 字符串表所占大小
          */

          獲取符號表和字符表的偏移后,將恢復的符號信息添加到符號表里,將函數(shù)名的字符串添加到字符串表。

          并且修改 LC_SYMTAB 結(jié)構(gòu)中數(shù)據(jù):

          • 符號的個數(shù)

          一共新增 152 個符號

          • 字符串表偏移

          152 * 16 + 126024 = 128456

          increase_sym_num * sizeof(nlist) + orig_str_off = new_str_off

          • 字符串表的大小。

          新增 152 個符號的名稱,4696 + 5456 = 10152

          orig_str_size + increase_str_size = new_str_size

          ii. LC_DYSYMTAB

          LC_DYSYMTAB 記錄各種符號在符號表和動態(tài)符號表中的索引和個數(shù),一共記錄 9 種符號。

          struct dysymtab_command {
          uint32_t cmd; /* LC_DYSYMTAB */
          uint32_t cmdsize; /* sizeof(struct dysymtab_command) */

          uint32_t ilocalsym; /* index to local symbols */
          uint32_t nlocalsym; /* number of local symbols */

          uint32_t iextdefsym;/* index to externally defined symbols */
          uint32_t nextdefsym;/* number of externally defined symbols */

          uint32_t iundefsym; /* index to undefined symbols */
          uint32_t nundefsym; /* number of undefined symbols */

          uint32_t tocoff; /* file offset to table of contents */
          uint32_t ntoc; /* number of entries in table of contents */

          uint32_t modtaboff; /* file offset to module table */
          uint32_t nmodtab; /* number of module table entries */

          uint32_t extrefsymoff; /* offset to referenced symbol table */
          uint32_t nextrefsyms; /* number of referenced symbol table entries */

          uint32_t indirectsymoff; /* file offset to the indirect symbol table */
          uint32_t nindirectsyms; /* number of indirect symbol table entries */

          uint32_t extreloff; /* offset to external relocation entries */
          uint32_t nextrel; /* number of external relocation entries */

          uint32_t locreloff; /* offset to local relocation entries */
          uint32_t nlocrel; /* number of local relocation entries */

          };

          新增本地符號 140 個,外部符號 12 個,一共 152 個。修改相應(yīng)符號數(shù)據(jù)。

          重定位項表偏移(indSym table offset)因為符號表和字符串表增大,所以也會增大。

          125296 + 152 * 16 = 127728

          Orig_indSym_offset + Increase_sym_num * sizeof(nlist) = new_indSym_offset

          iii. LC_SEGMENT

          表示一個段加載命令,需要將它加載到對應(yīng)的進程空間中。

           /*
          * The 64-bit segment load command indicates that a part of this file is to be
          * mapped into a 64-bit task's address space. If the 64-bit segment has
          * sections then section_64 structures directly follow the 64-bit segment
          * command and their size is reflected in cmdsize.
          */

          struct segment_command_64 { /* for 64-bit architectures */
          uint32_t cmd; /* LC_SEGMENT_64 */
          uint32_t cmdsize; /* includes sizeof section_64 structs */
          char segname[16]; /* segment name */
          uint64_t vmaddr; /* memory address of this segment */
          uint64_t vmsize; /* memory size of this segment */
          uint64_t fileoff; /* file offset of this segment */
          uint64_t filesize; /* amount to map from the file */
          vm_prot_t maxprot; /* maximum VM protection */
          vm_prot_t initprot; /* initial VM protection */
          uint32_t nsects; /* number of sections in segment */
          uint32_t flags; /* flags */
          };
          • segname 16 字節(jié)大小,用來存儲段的名稱

          • vmaddr 段要加載的虛擬內(nèi)存的地址

          • vmsize 段所占的虛擬內(nèi)存的大小

          • fileoff 段數(shù)據(jù)所在文件中的偏移位置

          • filesize 段數(shù)據(jù)實際的大小

          • maxprot 頁面所需要的最高內(nèi)存保護

          • initprot 頁面初始的內(nèi)存保護

          • nsects 段所包含的節(jié)區(qū)

          • flags 段的標志信息

          __LINKEDIT 包含需要被動態(tài)鏈接器使用的信息,包括符號表、字符串表、重定位項表等。

          __LINKEDIT 的 LC_SEGMENT 只需要修改段的大小:

          36048 + 152 * 16 + 5456 = 43936

          file_size + Increase_sym_num * sizeof(nlist) + increase_str_size = new_file_size

          0x05 效果

          恢復支付寶符號并打印調(diào)用棧:

                "0   AlipayWallet                        0x000000010a5e0660 +[AUNetworkInfo bssid] + 232",
          "1 AlipayWallet 0x000000010a5dfe90 +[AUNetworkInfo networkInfo] + 160",
          "2 AlipayWallet 0x000000010a5d99e0 +[AUDeviceInfo deviceInfo] + 132",
          "3 AlipayWallet 0x000000010a5d8c10 +[AUDeviceInfo deviceInfoWithoutAsyncData] + 84",
          "4 AlipayWallet 0x000000010a5d8934 +[AUDeviceInfo deviceInfoWithBlock:] + 224",
          "5 libdispatch.dylib 0x000000018340a610 A3849F96-1C9F-36C5-A15F-70C566F14CFF + 374288",
          "6 libdispatch.dylib 0x000000018340b184 A3849F96-1C9F-36C5-A15F-70C566F14CFF + 377220",
          "7 libdispatch.dylib 0x00000001833e4b50 A3849F96-1C9F-36C5-A15F-70C566F14CFF + 219984",
          "8 libdispatch.dylib 0x00000001833f1110 A3849F96-1C9F-36C5-A15F-70C566F14CFF + 270608",
          "9 libdispatch.dylib 0x00000001833f18b0 A3849F96-1C9F-36C5-A15F-70C566F14CFF + 272560",
          "10 libsystem_pthread.dylib 0x000000018345ab48 _pthread_wqthread + 212",
          "11 libsystem_pthread.dylib 0x000000018345d760 start_wqthread + 8"

          代碼:https://github.com/HeiTanBc/restore-symbol

          參考資料

          • http://blog.imjun.net/posts/restore-symbol-of-iOS-app/

          • https://juejin.cn/post/6844904133321818126#heading-17

          • https://opensource.apple.com/source/objc4/objc4-781/

          • https://opensource.apple.com/source/objc4/objc4-818.2/



          推薦閱讀

          ?  為 iPad 部署基于 VS Code 的遠程開發(fā)環(huán)境
          ?  “And away we code. ” WWDC21 超 200 個 Session 等著你
          ?  Google 正式發(fā)布 Fuchsia OS,F(xiàn)lutter 集成尚存問題
          ?  京東APP訂單業(yè)務(wù)Swift優(yōu)化總結(jié)


          就差您點一下了 ??????

          瀏覽 115
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

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

          手機掃一掃分享

          分享
          舉報
          <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>
                  特级西西人体大胆无码 | 91丨九色丨蝌蚪丨对白 | 福利在线观看中文字幕 | 欧美色图另类图片 | 真人免费一区二区三区 |