Chakra除錯筆記 TypedArray
一.TypedArray型別
TypedArray是漏洞中常見到的結構,手冊用法有四
1.new TypedArray(length);
//byteLength=length * sizeof(TypeName);
length
當傳入length引數時,一個內部陣列緩衝區被建立,該快取區的大小是傳入的length乘以陣列中每個元素的位元組數,每個元素的值都為0.(譯者注:每個元素的位元組數是由具體的建構函式決定的,比如Int16Array的每個元素的位元組數為2,Int32Array的每個元素的位元組數為4)
2.new TypedArray(typedArray);
typedArray
當傳入一個包含任意型別元素的任意型別化陣列物件(typedArray) (比如 Int32Array)作為引數時,typeArray被複制到一個新的型別陣列。typeArray中的每個值會在複製到新的陣列之前根據構造器進行轉化.新的生成的型別化陣列物件將會有跟傳入的陣列相同的length(譯者注:比如原來的typeArray.length==2,那麼新生成的陣列的length也是2,只是陣列中的每一項進行了轉化)
3.new TypedArray(object);
object
當傳入一個 object 作為引數時,如同透過 TypedArray.from() 方法一樣建立一個新的型別陣列。
4.new TypedArray(buffer [, byteOffset [, length]]);
//最常見用法,byteOffset、length是位元組數
buffer[, byteOffset, length]
當傳入arrayBuffer和可選引數byteOffset,可選引數length時,一個新的型別化陣列檢視將會被建立,該型別化陣列檢視用於呈現傳入的ArrayBuffer例項。byteOffset和length指定型別化陣列檢視暴露的記憶體範圍,如果兩者都未傳入,那麼整個buffer都會被呈現,如果僅僅忽略length,那麼buffer中偏移(byteOffset)後剩下的buffer將會被呈現.
//MDN規定的型別
Int8Array();
Uint8Array();
Uint8ClampedArray();
Int16Array();
Uint16Array();
Int32Array();
Uint32Array();
Float32Array();
Float64Array();
//但是Chakra在實現上定義如下更多的型別
OBJECT_TYPE(UninitializedObject ) //未初始化時就是這種
// Typed arrays that are optimized by the JIT
OBJECT_TYPE(Int8Array )
OBJECT_TYPE(Uint8Array )
OBJECT_TYPE(Uint8ClampedArray )
OBJECT_TYPE(Int16Array )
OBJECT_TYPE(Uint16Array )
OBJECT_TYPE(Int32Array )
OBJECT_TYPE(Uint32Array )
OBJECT_TYPE(Float32Array )
OBJECT_TYPE(Float64Array )
// Virtual Arrays
//Chakra中一種TypedArray對應兩種OBJECT_TYPE
OBJECT_TYPE(Int8VirtualArray)
OBJECT_TYPE(Uint8VirtualArray)
OBJECT_TYPE(Uint8ClampedVirtualArray)
OBJECT_TYPE(Int16VirtualArray)
OBJECT_TYPE(Uint16VirtualArray)
OBJECT_TYPE(Int32VirtualArray)
OBJECT_TYPE(Uint32VirtualArray)
OBJECT_TYPE(Float32VirtualArray)
OBJECT_TYPE(Float64VirtualArray)
//Mixed Arrays
OBJECT_TYPE(Int8MixedArray)
OBJECT_TYPE(Uint8MixedArray)
OBJECT_TYPE(Uint8ClampedMixedArray)
OBJECT_TYPE(Int16MixedArray)
OBJECT_TYPE(Uint16MixedArray)
OBJECT_TYPE(Int32MixedArray)
OBJECT_TYPE(Uint32MixedArray)
OBJECT_TYPE(Float32MixedArray)
OBJECT_TYPE(Float64MixedArray)
// Typed arrays that are not optimized by the JIT
OBJECT_TYPE(Int64Array)
OBJECT_TYPE(Uint64Array)
OBJECT_TYPE(BoolArray)
OBJECT_TYPE(CharArray)
// SIMD_JS
// SIMD並不是TypedArray,但是與TypedArray在一起處理
// Only Simd128 sub-types. Currently no need to track top Simd128 type
OBJECT_TYPE(Simd128Float32x4 )
OBJECT_TYPE(Simd128Int32x4 )
OBJECT_TYPE(Simd128Int16x8 )
OBJECT_TYPE(Simd128Int8x16 )
OBJECT_TYPE(Simd128Uint32x4 )
OBJECT_TYPE(Simd128Uint16x8 )
OBJECT_TYPE(Simd128Uint8x16 )
OBJECT_TYPE(Simd128Bool32x4 )
OBJECT_TYPE(Simd128Bool16x8 )
OBJECT_TYPE(Simd128Bool8x16 )
OBJECT_TYPE(Simd128Float64x2 ) // !! This is a marker for last SIMD type. Insert new SIMD types above.
二.正文
var tst = new Uint32Array(0x10000);
1.TypedArray<>::NewInstance
率先執行到
template <typename TypeName, bool clamped, bool virtualAllocated>
Var TypedArray<TypeName, clamped, virtualAllocated>::NewInstance(RecyclableObject* function, CallInfo callInfo, ...)
TypedArray類是一個template <typename TypeName, bool clamped, bool virtualAllocated>
模版類
如此設計是為了TypeName
可以指定不同的種類如Uint32
、Int16
等
TypedArray<>::NewInstance
是一個public static方法,提供外部呼叫建立TypedArray
public:
static Var NewInstance(RecyclableObject* function, CallInfo callInfo, ...);
TypedArray::NewInstance
上來首先獲取ScriptContext
和ThreadContext
Var TypedArray<TypeName, clamped, virtualAllocated>::NewInstance(RecyclableObject* function, CallInfo callInfo, ...)
{
function->GetScriptContext()->GetThreadContext()->ProbeStack(Js::Constants::MinStackDefault, function->GetScriptContext());
ARGUMENTS(args, callInfo);
ScriptContext* scriptContext = function->GetScriptContext();
ThreadContext
ThreadContext在CreateRuntimeCore裡,我們可以看到,在建立JsrtRuntime之前我們需要建立一個ThreadContext,而主要的初始化都是在ThreadContext上進行的。在ThreadContext裡面,我們還可以看到比JsrtRuntime多得多的成員變數,並且有很多我們都非常感興趣,比如:
- 和記憶體管理相關的Recycler,各種Page Allocator * 和控制流相關的異常資訊 * 各種統計資訊
我們馬上會提到的JsrtContext的主要實現——ScriptContext的列表 ……等等可以看出,上面我們提到的Runtime提供的主要功能基本都在ThreadContext裡面,可以說它是JsrtRuntime的主要實現。而透過程式碼我們可以看得到,JsrtRuntime和ThreadContext是一對一的,所以在讀ChakraCore的程式碼時,我們基本可以把他們認為是一個東西。
ScriptContext
雖然在ScriptContext並沒有直接被JsrtContext所持有,而是放在了JavascriptLibrary之中,但是我們還是先來看看這個類,因為這個類其實更加重要也更加的靠上層。
在JsrtContext的建構函式里面,我們可以看到第一步就是建立ScriptContext,而在銷燬JsrtContext時,其主要做的事情也是由ScriptContext來完成的,可見ScriptContext其實就是JsrtContext的真實實現。(其實看名字我們也看的出來……)
還記得JsrtContext提供的功能麼?在ScriptContext中,我們都可以在其成員變數中找到蹤跡:
- globalObject:這個就是瀏覽器裡JavaScript中的window變數。
url:當前ScriptContext的建立者的URL。 sourceList:用於儲存每個ScriptContext中載入的程式碼。
之後傳遞TypedArray<>::Create
函式指標進入TypedArrayBase::CreateNewInstance
Var object = TypedArrayBase::CreateNewInstance(args, scriptContext, sizeof(TypeName), TypedArray<TypeName, clamped, virtualAllocated>::Create);
2.TypedArrayBase::CreateNewInstance
static Var CreateNewInstance(Arguments& args, ScriptContext* scriptContext, uint32 elementSize, PFNCreateTypedArray pfnCreateTypedArray );
第一次進入TypedArrayBase::CreateNewInstance
時,arrayBuffer為空。因此會執行scriptContext->GetLibrary()->CreateArrayBuffer(byteLength)
if (arrayBuffer != nullptr)
{
}
else
{
// Null arrayBuffer - could be new constructor or copy constructor.
byteLength = elementCount * elementSize;
arrayBuffer = scriptContext->GetLibrary()->CreateArrayBuffer(byteLength);
}
其中前兩次的函式呼叫
scriptContext ScriptContext
GetLibrary() JavascriptLibrary
最後的CreateArrayBuffer函式,是從JavascriptLibrary中呼叫的ArrayBuffer* JavascriptLibrary::CreateArrayBuffer(uint32 length)
這個函式是JavascriptArrayBuffer::Create
的簡單封裝。
3.JavascriptArrayBuffer::Create
JavascriptArrayBuffer* JavascriptArrayBuffer::Create(uint32 length, DynamicType * type)
{
Recycler* recycler = type->GetScriptContext()->GetRecycler();
JavascriptArrayBuffer* result = RecyclerNewFinalized(recycler, JavascriptArrayBuffer, length, type);
Assert(result);
recycler->AddExternalMemoryUsage(length);
return result;
}
函式在透過ScriptContext獲取到Memory:Recycler之後呼叫了RecyclerNewFinalized
RecyclerNewFinalized函式內部則呼叫了經過過載的new運算子,如下
template <typename TAllocator>
_Ret_notnull_
NO_EXPORT(void *) __cdecl
operator new(DECLSPEC_GUARD_OVERFLOW size_t byteSize, TAllocator * alloc, char * (TAllocator::*AllocFunc)(size_t))
{
AssertCanHandleOutOfMemory();
Assert(byteSize != 0);
void * buffer = (alloc->*AllocFunc)(byteSize);
Assume(buffer != nullptr);
return buffer;
}
這裡分配了72個位元組,使用的是custom heap的記憶體管理,分配出來的是JavascriptArrayBuffer物件。new在分配了記憶體之後開始呼叫JavascriptArrayBuffer的建構函式。
經過一系列建構函式的繼承關係後,最後會呼叫到ArrayBuffer::ArrayBuffer()
這個函式傳遞了length
和allocator
兩引數,最後呼叫buffer = (BYTE*)allocator(length)
template <class Allocator>
ArrayBuffer::ArrayBuffer(uint32 length, DynamicType * type, Allocator allocator) :
ArrayBufferBase(type)
{
buffer = nullptr;
bufferLength = 0;
if (length > MaxArrayBufferLength)
{
JavascriptError::ThrowTypeError(GetScriptContext(), JSERR_FunctionArgument_Invalid);
}
else if (length > 0)
{
Recycler* recycler = GetType()->GetLibrary()->GetRecycler();
if (recycler->ReportExternalMemoryAllocation(length))
{
buffer = (BYTE*)allocator(length);
if (buffer == nullptr)
{
recycler->ReportExternalMemoryFree(length);
}
}
if (buffer == nullptr)
{
recycler->CollectNow<CollectOnTypedArrayAllocation>();
if (recycler->ReportExternalMemoryAllocation(length))
{
buffer = (BYTE*)allocator(length);
if (buffer == nullptr)
{
recycler->ReportExternalMemoryFailure(length);
}
}
}
if (buffer != nullptr)
{
bufferLength = length;
ZeroMemory(buffer, bufferLength);
}
else
{
JavascriptError::ThrowOutOfMemoryError(GetScriptContext());
}
}
}
注意這裡的建構函式是這樣進行傳參的
JavascriptArrayBuffer::JavascriptArrayBuffer(uint32 length, DynamicType * type) :
ArrayBuffer(length, type, IsValidVirtualBufferLength(length) ? AsmJsVirtualAllocator : malloc)
{
}
AsmJsVirtualAllocator是一個宏
#define AsmJsVirtualAllocator ((AllocWrapperType)Js::ArrayBuffer::AllocWrapper<MAX_ASMJS_ARRAYBUFFER_LENGTH>)
跟進buffer = (BYTE*)allocator(length)
之後會進入AllocWrapper
這個函式
template<size_t MaxVirtualSize = MAX_ASMJS_ARRAYBUFFER_LENGTH>
static void* __cdecl AllocWrapper(DECLSPEC_GUARD_OVERFLOW size_t length)
{
LPVOID address = VirtualAlloc(nullptr, MaxVirtualSize, MEM_RESERVE, PAGE_NOACCESS);
//throw out of memory
if (!address)
{
return nullptr;
}
if (length == 0)
{
return address;
}
LPVOID arrayAddress = VirtualAlloc(address, length, MEM_COMMIT, PAGE_READWRITE);
if (!arrayAddress)
{
VirtualFree(address, 0, MEM_RELEASE);
return nullptr;
}
return arrayAddress;
}
#define MAX_ASMJS_ARRAYBUFFER_LENGTH 0x100000000 // 4GB
注意這個函式兩次呼叫了VirtualAlloc,第一次是RESERVE,第二次是COMMIT。分配的思路就是無論申請多大記憶體,只要滿足VirtualArray的範圍那麼就RESERVE 4GB的地址空間,之後再有需要多少直接COMMIT就可以了。
之後再跳回到ArrayBuffer::ArrayBuffer中,執行ZeroMemory清空分配出來的記憶體。
這裡實現的是VirtualBuffer的分配
if (buffer != nullptr)
{
bufferLength = length;
ZeroMemory(buffer, bufferLength);
}
4.TypedArray<>::Create
在經過上面的一系列分配之後,執行流程返回到TypedArrayBase::CreateNewInstance函式中去。
之後在TypedArrayBase::CreateNewInstance函式中執行了如下流程
byteLength = elementCount * elementSize;
if (mappedLength == -1)
{
mappedLength = (byteLength - offset)/elementSize;
}
// Create and set the array based on the source.
TypedArrayBase* newArray = static_cast<TypedArrayBase*>(pfnCreateTypedArray(arrayBuffer, offset, mappedLength, scriptContext->GetLibrary()));
mappedLength也就是等於byteLength,之後在呼叫pfnCreateTypedArray函式時傳遞了之前建立的arrayBuffer
arrayBuffer = scriptContext->GetLibrary()->CreateArrayBuffer(byteLength);
分配出來的arrayBuffer是 ArrayBufferBase*,在後面可以看到ArrayBufferBase物件是建立TypedArray的基礎
pfnCreateTypedArray其實是
template <typename TypeName, bool clamped, bool virtualAllocated>
Var TypedArray<TypeName, clamped, virtualAllocated>::Create(ArrayBufferBase* arrayBuffer, uint32 byteOffSet, uint32 mappedLength, JavascriptLibrary* javascriptLibrary)
首先計算mappedByteLength=元素個數*單個元素尺寸,然後計算totalLength=byteOffSet+mappedByteLength
if (UInt32Math::Mul(mappedLength, sizeof(TypeName), &mappedByteLength) ||
UInt32Math::Add(byteOffSet, mappedByteLength, &totalLength) ||
(totalLength > arrayBuffer->GetByteLength()))
之後依然是呼叫RecyclerNew來分配記憶體,這個函式依然會呼叫過載後的new運算子分配64個位元組,分配出的記憶體作為TypedArray view物件
DynamicType *type = javascriptLibrary->GetTypedArrayType<TypeName, clamped>(0);
return RecyclerNew(javascriptLibrary->GetRecycler(), TypedArray, arrayBuffer, byteOffSet, mappedLength, type)
5.TypedArray<>::TypedArray
在new分配了TypedArray物件的記憶體後,就呼叫它的建構函式
template <typename TypeName, bool clamped, bool virtualAllocated>
TypedArray<TypeName, clamped, virtualAllocated>::TypedArray(ArrayBufferBase* arrayBuffer, uint32 byteOffset, uint32 mappedLength, DynamicType* type) :TypedArrayBase(arrayBuffer, byteOffset, mappedLength, sizeof(TypeName), type)
依據不同的Typed型別來設定屬性
switch (type->GetTypeId())
{
case TypeIds_Int8Array:
VirtualTableInfo<Int8VirtualArray>::SetVirtualTable(this);
break;
case TypeIds_Uint8Array:
VirtualTableInfo<Uint8VirtualArray>::SetVirtualTable(this);
break;
case TypeIds_Uint8ClampedArray:
VirtualTableInfo<Uint8ClampedVirtualArray>::SetVirtualTable(this);
break;
case TypeIds_Int16Array:
VirtualTableInfo<Int16VirtualArray>::SetVirtualTable(this);
break;
case TypeIds_Uint16Array:
VirtualTableInfo<Uint16VirtualArray>::SetVirtualTable(this);
break;
case TypeIds_Int32Array:
VirtualTableInfo<Int32VirtualArray>::SetVirtualTable(this);
break;
case TypeIds_Uint32Array:
VirtualTableInfo<Uint32VirtualArray>::SetVirtualTable(this);
break;
case TypeIds_Float32Array:
VirtualTableInfo<Float32VirtualArray>::SetVirtualTable(this);
break;
case TypeIds_Float64Array:
VirtualTableInfo<Float64VirtualArray>::SetVirtualTable(this);
break;
default:
break;
}
最後返回的是TypedArray*的指標,至此TypedArray建立成功
物件繼承關係
JavascriptArrayBuffer:
ArrayBuffer:
ArrayBufferBase:
DynamicObject:
RecyclableObject:
FinalizableObject
呼叫總覽
1.建立JavascriptArrayBuffer物件
2.建立Virtual Buffer
3.建立TypedArray物件
Virtual buffer建立流程
kernel32.dll!VirtualAlloc
ChakraCore.dll!Js::ArrayBufferBase::AllocWrapper<4294967296>()
ChakraCore.dll!Js::ArrayBuffer::ArrayBuffer<void * __ptr64 (__cdecl*)(unsigned __int64)>()
ChakraCore.dll!Js::JavascriptArrayBuffer::JavascriptArrayBuffer()
ChakraCore.dll!Js::JavascriptArrayBuffer::Create()
ChakraCore.dll!Js::JavascriptLibrary::CreateArrayBuffer()
ChakraCore.dll!Js::TypedArrayBase::CreateNewInstance()
ChakraCore.dll!Js::TypedArray<unsigned int,0,0>::NewInstance()
ChakraCore.dll!amd64_CallFunction()
TypedArray建立流程
ChakraCore.dll!Memory::Recycler::AllocWithAttributesInlined<0,0>()
ChakraCore.dll!Memory::Recycler::AllocInlined(unsigned __int64 size)
ChakraCore.dll!operator new<Memory::Recycler>()
ChakraCore.dll!Js::TypedArray<unsigned int,0,0>::Create()
ChakraCore.dll!Js::TypedArrayBase::CreateNewInstance()
ChakraCore.dll!Js::TypedArray<unsigned int,0,0>::NewInstance()
ChakraCore.dll!amd64_CallFunction()
JavascriptArrayBuffer建立流程
ChakraCore.dll!Js::JavascriptArrayBuffer::Create()
ChakraCore.dll!Js::JavascriptLibrary::CreateArrayBuffer()
ChakraCore.dll!Js::TypedArrayBase::CreateNewInstance()
ChakraCore.dll!Js::TypedArray<unsigned int,0,0>::NewInstance()
ChakraCore.dll!amd64_CallFunction()
相關文章
- JSP筆記-除錯2021-09-28JS筆記除錯
- 筆記|軟體除錯的技巧2019-07-07筆記除錯
- VS斷點除錯簡單筆記2020-12-21斷點除錯筆記
- Bug除錯專項訓練四筆記2021-03-17除錯筆記
- Bug除錯專項訓練三筆記2021-03-15除錯筆記
- spark學習筆記--Spark調優與除錯2018-07-12Spark筆記除錯
- 海康ID2013掃碼槍除錯筆記2024-07-31除錯筆記
- 除錯一記2019-09-20除錯
- 嵌入式開發筆記——除錯元件SEGGER_HardFaultHandle2020-12-20筆記除錯元件
- 嵌入式開發筆記——除錯元件SEGGER_RTT2020-12-20筆記除錯元件
- Android關於Typedarray的使用2019-03-04Android
- GDB除錯使用記錄2020-11-02除錯
- h5學習筆記:動態載入vconsole 除錯工具2019-03-10H5筆記除錯
- 再利用Chakra引擎繞過CFG2020-08-19
- Python 學習除錯記錄2020-10-27Python除錯
- GitHub學習除錯記錄2020-11-09Github除錯
- Supervisor 安裝除錯記錄2021-07-05除錯
- 除錯篇——除錯物件與除錯事件2022-03-02除錯物件事件
- 記憶體洩漏除錯工具2024-03-17記憶體除錯
- Node除錯指南-記憶體篇2019-02-16除錯記憶體
- 【隨筆記】T507 ADC SGM58031 16BIT 4Channel 除錯記錄2023-02-04筆記除錯
- 在Windows筆記本上除錯執行在iOS裝置上的前端應用2018-08-20Windows筆記除錯iOS前端
- SPI轉can晶片CSM300詳解以及Linux驅動移植除錯筆記2020-10-30晶片Linux除錯筆記
- android nfc tag3 除錯日記2018-08-08Android除錯
- FCoE測試重啟除錯記錄2023-02-22除錯
- Windows windbg kernel debug 雙機核心除錯 - USB3.0 除錯 USB除錯 除錯線2021-01-02Windows除錯
- 《Java8實戰》-第八章筆記(重構、測試和除錯)2018-10-14Java筆記除錯
- [翻譯] 除錯 Rxjs(二):日誌記錄2018-12-17除錯JS
- 10.3 除錯事件轉存程式記憶體2023-10-05除錯事件記憶體
- Python 程式碼除錯—使用 pdb 除錯2019-12-26Python除錯
- IsDebuggerPresent的反除錯與反反除錯2022-04-07除錯
- GDB偵錯程式(學習筆記)2020-10-04筆記
- 日誌庫 winston 的學習筆記 - logger.info 的實現原理單步除錯2021-10-27筆記除錯
- nginx 錯誤除錯2021-05-20Nginx除錯
- mysql增加列,刪除列學習筆記2018-07-25MySql筆記
- ArkTS 的記憶體快照與記憶體洩露除錯2024-10-29記憶體洩露除錯
- 記錄一次非常麻煩的除錯2023-05-05除錯
- rk3368 Android9.0 HIDL除錯記錄2020-10-23Android除錯
- 記一次https通訊除錯過程2024-06-22HTTP除錯