zoukankan      html  css  js  c++  java
  • 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可以指定不同的种类如Uint32Int16

    TypedArray<>::NewInstance是一个public static方法,提供外部调用创建TypedArray

    public:
     static Var NewInstance(RecyclableObject* function, CallInfo callInfo, ...);
    

    TypedArray::NewInstance上来首先获取ScriptContextThreadContext

    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()
    这个函数传递了lengthallocator两参数,最后调用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()
    
  • 相关阅读:
    链表实现python list数据类型
    python图形图像处理--验证码的制作
    Java封装、继承和抽象的实例
    C#指定几所城市的天气预报
    我的第一个全站项目纪实
    activiti集成spring
    刷新token并继续之前的请求
    微信小程序开发准备
    zookeeper和dubbo
    zookeeper
  • 原文地址:https://www.cnblogs.com/Ox9A82/p/7308360.html
Copyright © 2011-2022 走看看