标题: VBS技术内幕:数组的内部实现
作者: Demon
链接: https://demon.tw/reverse/vbs-internal-array.html
版权: 本博客的所有文章,都遵守“署名-非商业性使用-相同方式共享 2.5 中国大陆”协议条款。
曾经在《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 ; https://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 ; https://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数组有关,感兴趣的可以研究一下。
赞赏微信赞赏支付宝赞赏
随机文章:
vb/vbs redim不能改變數組第一維就相當於數組不能動態的增加行,這非常蛋疼.