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

    【技術分享】《Chrome V8 源碼》44. Runtime_StringToArray 源碼、觸發條件

    VSole2022-01-20 16:07:46

    介紹

    Runtime 是一系列采用 C++ 語言編寫的功能方法,它實現了大量 JavaScript 運行期間需要的 native 功能。接下來幾篇文章將介紹一些 Runtime 方法。本文分析 Runtime_StringToArray 方法的源碼和重要數據結構,講解 Runtime_StringToArray 方法的觸發條件。

    注意: Runtime 方法的加載、調用以及 RUNTIME_FUNCTION 宏模板請參見第十六篇文章。—allow-natives-syntax 和 %-prefix 不是本文的講解重點。

    StringToArray 測試用例

    字符串轉數組(StringToArray)主要應用在 String.prototype.split 源碼中,StringToArray 功能的實現有兩個,一個是 StringBuiltinsAssembler::StringToArray()(參見第 39 篇文章),另一個是 Runtime_StringToArray()。字符串轉數組時,先使用 StringBuiltinsAssembler::StringToArray() 功能,當該功能 不適合 時轉而采用效率較低的 Runtime_StringToArray() 功能。下面從 split 源碼說起:

    1.  TF_BUILTIN(StringPrototypeSplit, StringBuiltinsAssembler) {2.    // If the separator string is empty then return the elements in the subject.3.    {//.............省略.....................4.      Label next(this);5.      GotoIfNot(SmiEqual(LoadStringLengthAsSmi(separator_string), smi_zero),6.                &next);7.      TNode<Smi> subject_length = LoadStringLengthAsSmi(subject_string);8.      GotoIf(SmiEqual(subject_length, smi_zero), &return_empty_array);9.      args.PopAndReturn(10.          StringToArray(context, subject_string, subject_length, limit_number));11.      BIND(&next);12.    }//.............省略.....................13.  }
    

    上述代碼中,第 5 行代碼檢測 separator_string 的長度不為零時,跳轉到 11 行;

    第 7-8 行代碼判斷字符串是否為空,如果為空則返回空數組;

    第 9 行代碼生成并返回由字符組成的數組,也就是 string.split(“”) 的結果。

    下面給出 StringBuiltinsAssembler::StringToArray() 源碼:

    1.  TNode<JSArray> StringBuiltinsAssembler::StringToArray(2.      TNode<NativeContext> context, TNode<String> subject_string,3.      TNode<Smi> subject_length, TNode limit_number) {4.  //省略..................5.    to_direct.TryToDirect(&call_runtime);6.    BIND(&call_runtime);7.    {8.      result_array = CAST(CallRuntime(Runtime::kStringToArray, context,9.                                      subject_string, limit_number));10.      Goto(&done);11.    }12.    BIND(&done);13.    return result_array.value();14.  }
    

    上述代碼的詳細功能參見第 39 篇文章,第 5 行代碼轉換失敗時則跳轉到第 8 行代碼(Runtime_StringToArray),也就符合了前面提到的 “不適合” 條件。

    TryToDirect 功能參見之前的文章,下面給出最終的測試用例:

    var str1="chromium";var str2="blink";var str3 = "Understanding"var cons= str1+str2+str3.substring(3);console.log(cons.split(""));
    

    上述代碼中, cons 變量中包括了 “str1 + str2” 的結果,該結果的類型是 ConsString,也包括了 str3 的子部分,所以會導致 TryToDirect 失敗。

    Runtime_StringToArray 源碼

    源碼如下:

    1.  RUNTIME_FUNCTION(Runtime_StringToArray) {2.    HandleScope scope(isolate);3.    DCHECK_EQ(2, args.length());4.    CONVERT_ARG_HANDLE_CHECKED(String, s, 0);5.    CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[1]);6.    s = String::Flatten(isolate, s);7.    const int length =8.        static_cast<int>(std::min(static_cast<uint32_t>(s->length()), limit));9.    Handle elements;10.    int position = 0;11.    if (s->IsFlat() && s->IsOneByteRepresentation()) {12.      elements = isolate->factory()->NewFixedArray(length);13.      DisallowGarbageCollection no_gc;14.      String::FlatContent content = s->GetFlatContent(no_gc);15.      if (content.IsOneByte()) {16.        base::Vector<const uint8_t> chars = content.ToOneByteVector();17.        position = CopyCachedOneByteCharsToArray(isolate->heap(), chars.begin(),18.                                                 *elements, length);19.      } else {20.        MemsetTagged(elements->data_start(),21.                     ReadOnlyRoots(isolate).undefined_value(), length);22.      }23.    } else {24.      elements = isolate->factory()->NewFixedArray(length);25.    }26.    for (int i = position; i < length; ++i) {27.      Handle str =28.          isolate->factory()->LookupSingleCharacterStringFromCode(s->Get(i));29.      elements->set(i, *str);30.    }31.  #ifdef DEBUG32.    for (int i = 0; i < length; ++i) {33.      DCHECK_EQ(String::cast(elements->get(i)).length(), 1);34.    }35.  #endif36.    return *isolate->factory()->NewJSArrayWithElements(elements);37.  }
    上述代碼中,第 6 行代碼執行字符串 Flatten;
    第 7 行獲得字符串長度 length,并做最大長度限制檢測 limit;
    第 11 行檢測字符串是否為 OneByte 類型;
    第 12 行申請長度為 length 的 Array 數組 elements;
    第 14 行獲得 FlatContent 內存;
    第 16~17 行把 FlatContent 內容以字符為粒度逐個拷貝到 elements 數組中;
    第 20~24 行創建 Array 數組 elements;
    第 26~29 行從 Cache 中查找相應的字符并填充進 elements;
    第 36 行使用 elements 創建 JSArray 并返回結果。
    下面說明 Runtime_StringToArray 中使用的重要函數:
    (1) LookupSingleCharacterStringFromCode,V8 內部字符串緩存功能
    1.  Handle Factory::LookupSingleCharacterStringFromCode(uint16_t code) {2.    if (code <= unibrow::Latin1::kMaxChar) {3.      {4.        Object value = single_character_string_cache()->get(code);5.      }6.      Handle result =7.          InternalizeString(base::Vector<const uint8_t>(buffer, 1));8.      single_character_string_cache()->set(code, *result);9.      return result;10.    }11.    uint16_t buffer[] = {code};12.    return InternalizeString(base::Vector<const uint16_t>(buffer, 1));13.  }
    V8 使用 string cache 保存常用的、可以復用的字符,這些字符的類型是內部字符。上述代碼中,第 2-4 行代碼使用 int16 類數值 code 在 ASCII 范圍內查找 cache;
    第 6 行申請新的內部字符串,并保存到 string cache 中;
    第 11~12 行申請新的內部字符串,這些字符串超了 ASCII 范圍,重復使用的概率很小,所以不緩存 string cache。
    (2) InternalizeString,內部字符串生成函數
    1.  template <typename Impl>2.  Handle FactoryBase::InternalizeString(3.      const base::Vector<const uint8_t>& string, bool convert_encoding) {4.    SequentialStringKey<uint8_t> key(string, HashSeed(read_only_roots()),5.                                     convert_encoding);6.    return InternalizeStringWithKey(&key);7.  }8.  template <typename Impl>9.  Handle FactoryBase::InternalizeString(10.      const base::Vector<const uint16_t>& string, bool convert_encoding) {11.    SequentialStringKey<uint16_t> key(string, HashSeed(read_only_roots()),12.                                      convert_encoding);13.    return InternalizeStringWithKey(&key);14.  }
    上述代碼從 int8 和 int16 兩個方面分別實現 InternalizeString(),核心功能由 InternalizeStringWithKey() 實現。
    Runtime_StringToArray 方法的最后一行代碼將 elements 包裝成 NewJSArrayWithElements 數組并返回結果。
    (3) NewJSArrayWithElements,JSArray生成函數
    源碼如下:
    1.  Handle<JSArray> Factory::NewJSArrayWithUnverifiedElements(2.      Handle<FixedArrayBase> elements, ElementsKind elements_kind, int length,3.      AllocationType allocation) {4.    DCHECK(length <= elements->length());5.    NativeContext native_context = isolate()->raw_native_context();6.    Map map = native_context.GetInitialJSArrayMap(elements_kind);7.    if (map.is_null()) {8.      JSFunction array_function = native_context.array_function();9.      map = array_function.initial_map();10.    }11.    Handle<JSArray> array = Handle<JSArray>::cast(12.        NewJSObjectFromMap(handle(map, isolate()), allocation));13.    DisallowGarbageCollection no_gc;14.    JSArray raw = *array;15.    raw.set_elements(*elements);16.    raw.set_length(Smi::FromInt(length));17.    return array;18.  }
    上述代碼中,第 2 行 elements 是之前申請的字符串數組;elements_kind 采用默認的 TERMINAL_FAST_ELEMENTS_KIND;
    第 6 行根據 elements_kind 獲取初始 map;
    第 8~9 行 map 為空時采用 array_function 初始化 map;
    第 11 行創建 JSArray 數組對象 array;
    第 15~16 行把 elements 和 length 保存到 array 中。
    (4) NewJSObjectFromMap,申請JSArray內存空間
    1.  Handle<JSObject> Factory::NewJSObjectFromMap(2.      Handle<Map> map, AllocationType allocation,3.      Handle<AllocationSite> allocation_site) {4.    // JSFunctions should be allocated using AllocateFunction to be5.    // properly initialized.6.    DCHECK(!InstanceTypeChecker::IsJSFunction((map->instance_type())));7.    // Both types of global objects should be allocated using8.    // AllocateGlobalObject to be properly initialized.9.    DCHECK(map->instance_type() != JS_GLOBAL_OBJECT_TYPE);10.    JSObject js_obj = JSObject::cast(11.        AllocateRawWithAllocationSite(map, allocation, allocation_site));12.    InitializeJSObjectFromMap(js_obj, *empty_fixed_array(), *map);13.    DCHECK(js_obj.HasFastElements() || js_obj.HasTypedArrayElements() ||14.           js_obj.HasFastStringWrapperElements() ||15.           js_obj.HasFastArgumentsElements() || js_obj.HasDictionaryElements());16.    return handle(js_obj, isolate());17.  }
    上述代碼中,第 6、9 行代碼檢測 type 類型;第 10 行代碼根據 map 獲取內存空間 js_obj;第 12 行代碼 InitializeJSObjectFromMap 使用 empty_fixed_array 初始化 js_obj。
    返回 js_obj 到 NewJSArrayWithUnverifiedElements 方法中,然后設置 elements 和 length,最終完成 JSArray 的申請。
    圖 1 給出 Runtime_StringToArray 的調用堆棧,供讀者復現。
    技術總結
    (1) StringBuiltinsAssembler::StringToArray 方法效率最高,Runtime_StringToArray 是它的備選方案;
    (2) JSArray 對象使用 FixArray 存儲數據;
    (3) INTERNALIZED_STRING_TYPE 是 V8 的字符串類型,此外還有 ConsString、Sliced 等,具體參見枚舉類 InstanceType。
    好了,今天到這里,下次見。
    個人能力有限,有不足與紕漏,歡迎批評指正
    微信:qq9123013 備注:v8交流 郵箱:v8blink@outlook.com
    
    stringruntime
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    JSP Webshell的檢測工具
    2021-12-13 12:04:53
    在11月初,我做了一些JSP Webshell的免殺研究,主要參考了三夢師傅開源的代碼。然后加入了一些代碼混淆手段,編寫了一個免殺馬生成器JSPHorse,沒想到在Github上已收獲500+的Star
    這樣就獲取了system權限,接下來就是對機器進行信息搜集,獲取對我們橫向滲透有幫助的信息。其中記錄了tomcat部署的路徑,以及備份的源碼文件。經過一番爆破,發現了一臺機器oracle數據庫system用戶密碼未修改,為manager。大家應該知道oracle數據庫是可以執行系統命令的,而system用戶完全符合執行系統命令的條件。后面發現是火絨攔截了異常行為。
    //把Map的購物車轉換為Item列表??//應付總價=商品總價+運費總價-總優惠??然后實現針對 VIP 用戶的購物車邏輯。所以,這部分代碼只需要額外處理多買折扣部分:public?
    業務同學抱怨業務開發沒有技術含量,用不到設計模式、Java 高級特性、OOP,平時寫代碼都在堆?CRUD,個人成長無從談起。其實,我認為不是這樣的。設計模式、OOP 是前輩們在大型項目中積累下來的經驗,通過這些方法論來改善大型項目的可維護性。
    前言最近一段時間在研究Android加殼和脫殼技術,其中涉及到了一些hook技術,于是將自己學習的一些hook技術進行了一下梳理,以便后面回顧和大家學習。主要是進行文本替換、宏展開、刪除注釋這類簡單工作。所以動態鏈接是將鏈接過程推遲到了運行時才進行。
    漏洞分析花了蠻多時間
    獲取到類之后,我們就可以通過反射來間接調用里面的方法,獲取里面的變量等。
    java反射機制是什么
    介紹Runtime 是一系列采用 C++ 語言編寫的功能方法,它實現了大量 JavaScript 運行期間需要的 native 功能。本文分析 Runtime_StringToArray 方法的源碼和重要數據結構,講解 Runtime_StringToArray 方法的觸發條件。
    介紹Runtime 是一系列采用 C++ 語言編寫的功能方法,它實現了大量 JavaScript 運行期間需要的 native 功能。
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类