VBS技术内幕:数组的内部实现

标签: , , , ,

曾经在《VB6拾遗:数组的内部实现》里写过VB数组的内部实现,而VBS是VB的子集,所以VBS数组的内部实现跟VB的大同小异。

Dim a(3, 4)

和VB6一样,VBS数组内部是使用SAFEARRAY来实现的,通过OllyDbg调试分析,创建数组的汇编代码如下:


02115A62    mov     edi, edi                           ; MakeArray
02115A64    push    ebp                                ; 分析 By Demon
02115A65    mov     ebp, esp                           ; http://demon.tw
02115A67    sub     esp, 204
02115A6D    mov     eax, dword ptr [__security_cookie]
02115A72    xor     eax, ebp
02115A74    mov     dword ptr [ebp-4], eax
02115A77    push    esi
02115A78    mov     esi, ecx                           ; esi = ecx = 维度
02115A7A    test    esi, esi                           ; 维度是否为0
02115A7C    je      0211712D                           ; 如果维度是否为0,例如Dim a()
02115A82    cmp     esi, 40                            ; VBS数组最多64维,而不是文档中说的60维
02115A85    jg      0212F49E                           ; 维度大于64则报错
02115A8B    push    edi
02115A8C    xor     edi, edi                           ; edi = 0
02115A8E    test    esi, esi
02115A90    jle     short 02115AC2                     ; 如果esi <= 0
02115A92    push    ebx
02115A93    mov     eax, esi
02115A95    lea     ebx, dword ptr [edx-10]            ; edx = 参数地址,即a(3,4)中的3,4
                                                       ; ebx = edx - 16

02115A98    shl     eax, 4                             ; eax = eax * 16
                                                       ; VARIANT结构的大小为16字节
02115A9B    add     ebx, eax                           ; ebx = ebx + eax
                                                       ; 此时ebx为第一个参数的地址

02115A9D    and     dword ptr [ebp+edi*8-200], 0       ; 在栈上为每一维度构造SAFEARRAYBOUND结构
                                                       ; lLbound总为0,所以数组的下界总是零
02115AA5    mov     ecx, ebx                           ; exc = ebx
02115AA7    push    3                                  ; VT_I4
02115AA9    call    VAR::PvarGetTypeVal                ; 获取每一维度元素的个数的VARIANT结构
                                                       ; 由于类型是VT_I4,所以最大为2^32-1
02115AAE    sub     ebx, 10                            ; ebx = ebx - 16,即下一个参数地址
02115AB1    mov     eax, dword ptr [eax+8]             ; 取出VARIANT结构中的值,即每一维度元素的个数
02115AB4    inc     eax                                ; 元素的个数+1,所以Dim a(9)一共有10个元素
02115AB5    mov     dword ptr [ebp+edi*8-204], eax     ; SAFEARRAYBOUND.cElements
02115ABC    inc     edi                                ; ++edi
02115ABD    cmp     edi, esi                           ; 循环控制
02115ABF    jl      short 02115A9D                     ; 如果edi < esi,继续循环
02115AC1    pop     ebx
02115AC2    lea     eax, dword ptr [ebp-204]           ; rgsabound的地址
02115AC8    push    eax                                ; 入栈
02115AC9    push    esi                                ; cDims入栈
02115ACA    push    0C                                 ; vt = 0x0C,即VT_VARIANT
02115ACC    call    dword ptr [<&OLEAUT32.#15>]        ; oleaut32.SafeArrayCreate
                                                       ; 调用SafeArrayCreate创建SAFEARRAY指针
02115AD2    pop     edi
02115AD3    test    eax, eax
02115AD5    je      0212F4A9                           ; 返回0则报错 内存不足
02115ADB    mov     ecx, dword ptr [ebp-4]
02115ADE    xor     ecx, ebp
02115AE0    pop     esi
02115AE1    call    __security_check_cookie
02115AE6    mov     esp, ebp
02115AE8    pop     ebp
02115AE9    retn

VBScript只有一种数据类型Variant,所以创建的SAFEARRAY指针还要封装到VARIANT结构中去:


02115B3E    call    MakeArray                    ; 分析 By Demon
02115B43    mov     ecx, eax                     ; http://demon.tw
02115B45    mov     dword ptr [ebp-78], ecx
02115B48    test    ecx, ecx
02115B4A    je      short 02115B51
02115B4C    or      word ptr [ecx+2], 12         ; fFeatures |= FADF_FIXEDSIZE | FADF_STATIC
02115B51    lea     eax, dword ptr [esi+C]       ; esi 为VARIANT结构的地址
                                                 ; eax = esi + 12,即第四个双字
02115B54    shl     edi, 4
02115B57    mov     dword ptr [esi+8], eax       ; 相当于[esi + 8] = esi + 0x0C
                                                 ; 即第三个双字保存第四个双字的地址
02115B5A    mov     dword ptr [eax], ecx         ; 第四个双字保存创建的SAFEARRAY*指针
02115B5C    mov     eax, 600C                    ; VT_ARRAY | VT_BYREF | VT_VARIANT
02115B61    mov     word ptr [esi], ax           ; VARIANT.vt 设置子类型
02115B64    add     dword ptr [ebx+B0], edi
02115B6A  ^ jmp     021027D0

ReDim的过程与Dim是一样的,也是重新创建一个数组,如果使用了 Preserve 关键字,就只能调整数组最后维的大小,并且不能改变数组的维数。

除了Dim和ReDim之外,VBS的Array函数也可以创建数组,不同之处在于Array函数只能创建一维数组。

最后不得不提一下CVE-2014-6332漏洞,因为它与VBS数组有关,感兴趣的可以研究一下。

随机文章:

  1. VBS中LoadPicture函数的正确用法
  2. 鄙视雨林木风
  3. PHP读取纯真IP数据库QQWry.dat
  4. NDS模拟器iDeaS
  5. VBS基础教程第一篇

一条评论 发表在“VBS技术内幕:数组的内部实现”上

  1. tdus说道:

    vb/vbs redim不能改變數組第一維就相當於數組不能動態的增加行,這非常蛋疼.

留下回复