<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    CallBuiltin()調用過程詳解

    一顆小胡椒2021-12-02 16:31:15

    01、摘要

    本篇文章是Builtin專題的第五篇,詳細分析Builtin的調用過程。在Bytecode handler中使用CallBuiltin()調用Builtin是最常見的情況,本文將詳解CallBuiltin()源碼和相關的數據結構。本文內容組織方式:重要數據結構(章節2);CallBuiltin()源碼(章節3)。

    02、數據結構

    提示:Just-In-Time Compiler是本文的前導知識,請讀者自行查閱。

    Builtin的調用過程主要分為兩部分:查詢Builtin表找到相應的入口函數、計算calldescriptor。下面解釋相關的數據結構:

    (1) Builtin名字(例如Builtin::kStoreGlobalIC),名字是枚舉類型變量,CallBuiltin()使用該名字查詢Builtin表,找到相應的入口函數,源碼如下:

    class Builtins {
    //...............省略....................
      enum Name : int32_t {
    #define DEF_ENUM(Name, ...) k##Name,
        BUILTIN_LIST(DEF_ENUM, DEF_ENUM, DEF_ENUM, DEF_ENUM, DEF_ENUM, DEF_ENUM,
                     DEF_ENUM)
    #undef DEF_ENUM
            builtin_count,  
    }
    

    展開之后如下:

    enum Name:int32_t{kRecordWrite, kEphemeronKeyBarrier, kAdaptorWithBuiltinExitFrame, kArgumentsAdaptorTrampoline,......}
    

    (2) Builtin表存儲Builtin的地址。Builtin表的位置是isoate->isolatedata->builtins_,源碼如下:

    class IsolateData final {
     public:
      explicit IsolateData(Isolate* isolate) : stack_guard_(isolate) {}
    //............省略....................
      Address* builtins() { return builtins_; }
    }
    

    builtins_是Address類型的數組,與enum Name:int32_t{}配合使用可查詢對應的Builtin地址(下面的第2行代碼就完成了地址查詢),源碼如下:

    1.Callable Builtins::CallableFor(Isolate* isolate, Name name) {
    2.  Handle code = isolate->builtins()->builtin_handle(name);
    3.  return Callable{code, CallInterfaceDescriptorFor(name)};
    4.}
    

    上述代碼第3行CallInterfaceDescriptorFor返回Builtin的調用信息,該信息與code共同組成了Callable。

    (3) Code類,該類包括Builtin地址、指令的開始和結束以及填充信息,它的作用之一是創建snapshot文件,源碼如下:

    1.  class Code : public HeapObject {
    2.   public:
    3.  #define CODE_KIND_LIST(V)   \
    4.    V(OPTIMIZED_FUNCTION) V(BYTECODE_HANDLER)       \
    5.    V(STUB)  V(BUILTIN)  V(REGEXP)  V(WASM_FUNCTION)   V(WASM_TO_CAPI_FUNCTION)  \
    6.     V(WASM_TO_JS_FUNCTION)   V(JS_TO_WASM_FUNCTION)   V(JS_TO_JS_FUNCTION)      \
    7.     V(WASM_INTERPRETER_ENTRY)   V(C_WASM_ENTRY)
    8.     inline int builtin_index() const;
    9.     inline int handler_table_offset() const;
    10.     inline void set_handler_table_offset(int offset);
    11.     // The body of all code objects has the following layout.
    12.     //  +--------------------------+  <-- raw_instruction_start()
    13.     //  |       instructions       |
    14.     //  |           ...            |
    15.     //  +--------------------------+
    16.     //  |     embedded metadata    |  <-- safepoint_table_offset()
    17.     //  |           ...            |  <-- handler_table_offset()
    18.     //  |                          |  <-- constant_pool_offset()
    19.     //  |                          |  <-- code_comments_offset()
    20.     //  |                          |
    21.     //  +--------------------------+  <-- raw_instruction_end()
    22.     // If has_unwinding_info() is false, raw_instruction_end() points to the first
    23.     // memory location after the end of the code object. Otherwise, the body
    24.     // continues as follows:
    25.     //  +--------------------------+
    26.     //  |    padding to the next   |
    27.     //  |  8-byte aligned address  |
    28.     //  +--------------------------+  <-- raw_instruction_end()
    29.     //  |   [unwinding_info_size]  |
    30.     //  |        as uint64_t       |
    31.     //  +--------------------------+  <-- unwinding_info_start()
    32.     //  |       unwinding info     |
    33.     //  |            ...           |
    34.     //  +--------------------------+  <-- unwinding_info_end()
    35.     // and unwinding_info_end() points to the first memory location after the end
    36.     // of the code object.
    37.   };
    

    上述代碼第3-7行說明了當前Code是哪種指令類型;第9-10代碼是異常處理程序;第11-36行注釋說明了Code的內存布局。寫snapshot文件時內存布局會有細微變化,詳情請參考mksnapshot.exe源碼。

    (4) CallInterfaceDescriptor描述了Builtin入口函數的寄存器參數、堆棧參數和返回值等信息,調用Builtin時會使用這些信息,源碼如下:

    1.  class V8_EXPORT_PRIVATE CallInterfaceDescriptor {
    2.   public:
    3.    Flags flags() const { return data()->flags(); }
    4.    bool HasContextParameter() const {return (flags() & CallInterfaceDescriptorData::kNoContext) == 0;}
    5.    int GetReturnCount() const { return data()->return_count(); }
    6.    MachineType GetReturnType(int index) const {return data()->return_type(index);}
    7.    int GetParameterCount() const { return data()->param_count(); }
    8.    int GetRegisterParameterCount() const {return data()->register_param_count();}
    9.    int GetStackParameterCount() const {return data()->param_count() - data()->register_param_count();}
    10.    Register GetRegisterParameter(int index) const {return data()->register_param(index);}
    11.    MachineType GetParameterType(int index) const {return data()->param_type(index);}
    12.    RegList allocatable_registers() const {return data()->allocatable_registers();}
    13.  //..............省略...................
    14.  private:
    15.  const CallInterfaceDescriptorData* data_;
    16.  }
    

    上述代碼第5行是Builtin的返回值數量;第6行是返回值的類型;第7行是參數數量;第8代是寄存器參數的數量;第9行是棧參數的數量;第10-12行是獲取參數;第15行代碼CallInterfaceDescriptorData存儲上述代碼中所需的信息,即返回值數量、類型等信息,源碼如下:

    1.  class V8_EXPORT_PRIVATE CallInterfaceDescriptorData {
    2.   private:
    3.    bool IsInitializedPlatformSpecific() const {
    4.  //.........省略..............
    5.    }
    6.    bool IsInitializedPlatformIndependent() const {
    7.  //.........省略..............
    8.    }
    9.    int register_param_count_ = -1;
    10.    int return_count_ = -1;
    11.    int param_count_ = -1;
    12.    Flags flags_ = kNoFlags;
    13.    RegList allocatable_registers_ = 0;
    14.    Register* register_params_ = nullptr;
    15.    MachineType* machine_types_ = nullptr;
    16.    DISALLOW_COPY_AND_ASSIGN(CallInterfaceDescriptorData);
    17.  };
    

    上述代碼第9-15行定義的變量就是CallInterfaceDescriptor中提到的返回值、參數等信息。

    以上內容是CallBuiltin()使用的主要數據結構。

    03、CallBuiltin()

    來看下面的使用場景:

    IGNITION_HANDLER(LdaNamedPropertyNoFeedback, InterpreterAssembler) {
      TNode<Object> object = LoadRegisterAtOperandIndex(0);
      TNode<Name> name = CAST(LoadConstantPoolEntryAtOperandIndex(1));
      TNode<Context> context = GetContext();
      TNode<Object> result = CallBuiltin(Builtins::kGetProperty, context, object, name);
      SetAccumulator(result);
      Dispatch();
    }
    

    LdaNamedPropertyNoFeedback的作用是獲取屬性,例如從document屬性中獲取getelementbyID方法,該方法的獲取由CallBuiltin調用Builtins::kGetProperty實現,源碼如下:

      template <class... TArgs>
      TNode CallBuiltin(Builtins::Name id, SloppyTNode context,                            TArgs... args) {
        return CallStub(Builtins::CallableFor(isolate(), id), context,                            args...);
      }
    上述代碼中id代表Builtin的名字,即前面提到的枚舉值;args有兩個成員:args[0]代表object(上述例子中的document),args[1]代表name(getelementbyID方法)。CallStub()源碼如下:
    1.    template <class T = Object, class... TArgs>
    2.    TNode<T> CallStub(Callable const& callable, SloppyTNode<Object> context,
    3.                      TArgs... args) {
    4.      TNode<Code> target = HeapConstant(callable.code());
    5.      return CallStub<T>(callable.descriptor(), target, context, args...);
    6.    }
    7.  //..............分隔線..................
    8.    template <class T = Object, class... TArgs>
    9.    TNode<T> CallStub(const CallInterfaceDescriptor& descriptor,
    10.                      SloppyTNode<Code> target, SloppyTNode<Object> context,
    11.                      TArgs... args) {
    12.      return UncheckedCast<T>(CallStubR(StubCallMode::kCallCodeObject, descriptor,
    13.                                        1, target, context, args...));
    14.    }
    15.  //..............分隔線..................
    16.    template <class... TArgs>
    17.    Node* CallStubR(StubCallMode call_mode,
    18.                    const CallInterfaceDescriptor& descriptor, size_t result_size,
    19.                    SloppyTNode<Object> target, SloppyTNode<Object> context,
    20.                    TArgs... args) {
    21.      return CallStubRImpl(call_mode, descriptor, result_size, target, context,
    22.                           {args...});
    23.    }
    上述代碼第4行創建target對象,該對象是Builtin的入口地址;第5行代碼調用CallStub()方法(第9行),最終進入CallStubR()。在CallStubR()中調用CallStubRImpl(),源碼如下:
    1.  Node* CodeAssembler::CallStubRImpl( ) {
    2.    DCHECK(call_mode == StubCallMode::kCallCodeObject ||
    3.           call_mode == StubCallMode::kCallBuiltinPointer);
    4.    constexpr size_t kMaxNumArgs = 10;
    5.    DCHECK_GE(kMaxNumArgs, args.size());
    6.     NodeArray<kMaxNumArgs + 2> inputs;
    7.     inputs.Add(target);
    8.     for (auto arg : args) inputs.Add(arg);
    9.     if (descriptor.HasContextParameter()) {
    10.       inputs.Add(context);
    11.     }
    12.     return CallStubN(call_mode, descriptor, result_size, inputs.size(),
    13.                      inputs.data());
    14.   }
    上述代碼第7-10行將所有參數添加到數組inputs中,inputs內容依次為:Builtin的入口地址(code類型)、object、name、context。進入第12行代碼,CallStubN()源碼如下:
    1.  Node* CodeAssembler::CallStubN() {2.    // implicit nodes are target and optionally context.3.    int implicit_nodes = descriptor.HasContextParameter() ? 2 : 1;4.    int argc = input_count - implicit_nodes;5.    // Extra arguments not mentioned in the descriptor are passed on the stack.6.    int stack_parameter_count = argc - descriptor.GetRegisterParameterCount();7.    auto call_descriptor = Linkage::GetStubCallDescriptor(8.        zone(), descriptor, stack_parameter_count, CallDescriptor::kNoFlags,9.        Operator::kNoProperties, call_mode);10.    CallPrologue();11.    Node* return_value =12.        raw_assembler()->CallN(call_descriptor, input_count, inputs);13.    HandleException(return_value);14.    CallEpilogue();15.    return return_value;16.  }
    上述第7行代碼call_descriptor的返回值類型如下:
    CallDescriptor(          // --
          kind,                                  // kind
          target_type,                           // target MachineType
          target_loc,                            // target location
          locations.Build(),                     // location_sig
          stack_parameter_count,                 // stack_parameter_count
          properties,                            // properties
          kNoCalleeSaved,                        // callee-saved registers
          kNoCalleeSaved,                        // callee-saved fp
          CallDescriptor::kCanUseRoots | flags,  // flags
          descriptor.DebugName(),                // debug name
          descriptor.allocatable_registers())
    上述信息為調用Builtin做準備工作。CallStubN()中第11行代碼:完成Builtin的調用,第13行代碼:異常處理。圖1給出了CodeAssembler()的調用堆棧,此時正在建立Builtin,Builtin的建立發生在Isolate初始化階段。
    
    

    技術總結

    (1) Builtin名字與Builtin的入口之間存在一一對應的關系,這種關系由isoate->isolatedata->builtins表示,builtins是在Isolate初始化過程中創建的Address數組;

    (2) inputs數組除了包括參數之外還有target和context;

    (3) Builtin的參數、返回值等信息的詳細說明可在BUILTIN_LIST宏中查看。

    好了,今天到這里,下次見。

    源碼target
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    需要llvm 11+,這是當前afl支持的效率最高的選擇,也意味著編譯要花更長時間。實現了編譯級插樁,效果比匯編級插樁更好。從編譯的實現流程上理解插樁模式差異:afl-gcc插樁分析考慮到afl的插樁方式隨編譯器的選擇而變化,從最簡單的afl-gcc開始入手。
    假如想在x86平臺運行arm程序,稱arm為source ISA, 而x86為target ISA, 在虛擬化的角度來說arm就是Guest, x86為Host。這種問題被稱為Code-Discovery Problem。每個體系結構對應的helper函數在target/xxx/helper.h頭文件中定義。
    本系列將以官網資料為基礎主要通過動態跟蹤來解析DynamoRIO的源代碼。因為如果不結合實例只是將各函數的作用寫出來,實在無法很好的說明問題,我們將以代碼覆蓋工具drcov為例,分析DynamoRIO的執行流程。
    Android Java層源碼調試編譯idegen成功會在源碼根目錄生成android.iml 和 android.ipr兩個文件。編輯導入配置sudo deepin-editor android.iml搜索excludeFolder,在下面加入這些配置。過濾不需要的源碼模塊。排除tests 目錄 右鍵mark Directory as Excluded配置 Android源碼項目點擊File -> Project Structure–>SDKs配置項目的JDK、SDK。根據源碼版本選擇對應API級別 這里使用的Android10 對應29。配置 VScode 運行和調試獲取vscodelunch.json 配置注意先選擇C/C++ 源碼 下好斷點 此時按F5 觸發。
    Dobby一共兩個功能,其一是inlinehook,其二是指令插樁,兩者原理差不多,主要介紹指令插樁。所謂指令插樁,就是在任意一條指令,進行插樁,執行到這條指令的時候,會去執行我們定義的回調函數
    相信大家一定對源碼泄露并不陌生,這里也不贅述這個漏洞的理和危害了,網上一搜也都有好多好多,看都看不完~~~那今天這里我們講啥呢?那就直入主題吧~今天這里我就貼出我自己參考的加上自己寫的fofa爬蟲 + 源碼泄露PoC之夢幻聯動,希望能對大家有幫助。
    本文主要討論學習mimikatz中與Kerberos協議相關的代碼
    本文講解 Turbofan 的工作流程、梳理 PrepareJob、ExecuteJob 和 FinalizeJob 的主要功能以及重要數據結構。
    Builtin實現了V8中大量的核心功能,可見它的重要性。
    crawlergo是一個使用chrome headless模式進行URL收集的瀏覽器爬蟲。它對整個網頁的關鍵位置與DOM渲染階段進行HOOK,自動進行表單填充并提交,配合智能的JS事件觸發,盡可能的收集網站暴露出的入口。內置URL去重模塊,過濾掉了大量偽靜態URL,對于大型網站仍保持較快的解析與抓取速度,最后得到高質量的請求結果集合。調研1.
    一顆小胡椒
    暫無描述
      亚洲 欧美 自拍 唯美 另类