《Advanced Query Tool V8 的注册分析》之补遗
前段时间曾在“新兵论坛”上发表拙文《Advanced Query Tool V8 的注册分析》。文章发表后,还受到了几位同志的的关注。加之用注册机产生的注册码注册后,注册信息窗体中回显了预期的信息,所以心里着实得意了好一阵子。殊不知接下来发生的事情给了我当头棒喝。
那是因为公司里的一个数据库应用系统需要维护。这个系统采用的是Microsoft SQL Server 2000的数据库。心里想着这回可以尝试一下自己的劳动成果了吧,于是便踌躇满志地启动了AQT。
因为是“已经注册了”,所以直接来到了“Database Sign on”窗口。输入UserID和Password,AQT得主操作界面就完整地呈现在了眼前(心里头有点儿激动)。因为查询量比较小,所以查询等操作基本正常,没有出现大家所说的只能显示前50条记录的情况。但就在我单击Create菜单下的Procedure,准备创建一个存储过程的时候,显示屏上跳出了让我大吃一惊的提示窗口(如下)。
看来我得继续努力了……
最直接的莫过于察看对窗口中出现的内容的访问情况。这时我们请出IDA Pro 5.0,反汇编AQT,在反汇编窗口中查找上面那个窗口中的第一段文字,发现这段文字位于0x4E26D0处,其调用点只有一个,在0xBDD644处,属于起始于0xBDD588的子程序。这段子程序仅仅只是显示了那个窗口,从中不能分析出显示这个窗口的任何原因,加上只在这个子程序中访问了那段文字,所以我们断定这是一个公用的过程。我们再向上追溯,发现这个子程序是从0x42A0E8跳转过来的,而该地址是0x429A84处所描述的窗体(进一步分析可知,该窗体名为“dbaregx”。)的方法入口之一。
要想调用这个方法,就必须访问窗体对象。我们在0x429A84的交叉引用列表中查找,发现代码中有5个地方引用了0x429A84,分别是0x8C194E、0x8C897B、0x8E82AC、0x8EBD44和0xBDB40F。这5个地址分别属于0x8C17FB、0x8C88E1、0x8E820C、0x8EBC9B和0xBDB2CB始的子程序。我们重新用OllyICE加载AQT,在上面那5个子程序的开始处设置断点,然后执行。当执行创建存储过程的操作时,程序果然断在了0x8C17FB处。向下单步跟踪我们发现,原来是程序要比较0xC7E4CC处存放的字符是不是"X"。如果不是,程序才会显示上面那个窗口。而此时0xC7E4CC处存放的字符是"F"。
0xC7E4CC处的内容是如何变化的呢?
我们重新开始调试,在数据窗格中观察0xC7E4CC处的内容,发现在“Database Sign on”窗口中点击“Sign on”按钮之前,0xC7E4CC处的字符一直都是"X",在此之后就变成了"F"。所以我们重新调试时,在点击“Sign on”按钮之前对0xC7E4CC处的内容设置内存写入断点。点击“Sign on”按钮后,程序会中断在MSVBVM60中。从中返回后,会来到AQT的0xC48E09处。看上面一条指令,方知程序是调用了MSVBVM60的__vbaLsetFixstr来设置0xC7E4CC处的内容。这条指令位于0xC47F30开始的子程序。我们重新开始调试,在0xC47F30处设置断点后开始执行。程序被断下后,我们仔细跟踪,发现下面一段程序值得关注:
00C4833C
; 比较0xC7E4CC处的字符是不是"S"或"X"
... ...
00C483BA test eax, eax
00C483BC je short 00C483C3 ; 若是则继续(实际上是"X")
00C483BE jmp 00C48E39 ; 否则跳转
00C483C3 mov dword ptr [ebp-4], 12
00C483CA cmp dword ptr [C7E90C], 0
00C483D1 jnz short 00C483EE
00C483D3 push 00C7E90C
00C483D8 push 004446C4 ; IDA中显示是名为"dbamdi"的窗体对象
00C483DD call <jmp.&MSVBVM60.__vbaNew2>
00C483E2 mov dword ptr [ebp-138], 00C7E90C
00C483EC jmp short 00C483F8
00C483EE mov dword ptr [ebp-138], 00C7E90C
00C483F8 push 004A9C90 ; CLSID {2C247F24-8591-11D1-B16A-00C0F0283628}
00C483FD push 0
00C483FF push 4
00C48401 mov eax, dword ptr [ebp-138]
00C48407 mov eax, dword ptr [eax]
00C48409 mov ecx, dword ptr [ebp-138]
00C4840F mov ecx, dword ptr [ecx]
00C48411 mov ecx, dword ptr [ecx]
00C48413 push eax
00C48414 call dword ptr [ecx+380]
00C4841A push eax
00C4841B lea eax, dword ptr [ebp-70]
00C4841E push eax
00C4841F call <jmp.&MSVBVM60.__vbaObjSet>
00C48424 push eax
00C48425 lea eax, dword ptr [ebp-88]
00C4842B push eax
00C4842C call <jmp.&MSVBVM60.__vbaLateIdCallLd>
00C48431 add esp, 10
00C48434 push eax
00C48435 call <jmp.&MSVBVM60.__vbaCastObjVar>
00C4843A push eax
00C4843B lea eax, dword ptr [ebp-74]
00C4843E push eax
00C4843F call <jmp.&MSVBVM60.__vbaObjSet>
00C48444 mov dword ptr [ebp-F4], eax
00C4844A mov dword ptr [ebp-90], 3
00C48454 mov dword ptr [ebp-98], 2
00C4845E lea eax, dword ptr [ebp-78]
00C48461 push eax
00C48462 lea eax, dword ptr [ebp-98]
00C48468 push eax
00C48469 mov eax, dword ptr [ebp-F4]
00C4846F mov eax, dword ptr [eax]
00C48471 push dword ptr [ebp-F4]
00C48477 call dword ptr [eax+24]
00C4847A fclex
00C4847C mov dword ptr [ebp-F8], eax
00C48482 cmp dword ptr [ebp-F8], 0
00C48489 jge short 00C484AB
00C4848B push 24
00C4848D push 004A9C90
00C48492 push dword ptr [ebp-F4]
00C48498 push dword ptr [ebp-F8]
00C4849E call <jmp.&MSVBVM60.__vbaHresultCheckObj>
00C484A3 mov dword ptr [ebp-13C], eax
00C484A9 jmp short 00C484B2
00C484AB and dword ptr [ebp-13C], 0
00C484B2 mov eax, dword ptr [ebp-78]
00C484B5 mov dword ptr [ebp-FC], eax
00C484BB lea eax, dword ptr [ebp-A8]
00C484C1 push eax
00C484C2 mov eax, dword ptr [ebp-FC]
00C484C8 mov eax, dword ptr [eax]
00C484CA push dword ptr [ebp-FC]
00C484D0 call dword ptr [eax+2C] ; 该调用返回一个包含16个数字的字符串
00C484D3 fclex
00C484D5 mov dword ptr [ebp-100], eax
00C484DB cmp dword ptr [ebp-100], 0
00C484E2 jge short 00C48504
00C484E4 push 2C
00C484E6 push 004A9CA0 ; CLSID {2C247F26-8591-11D1-B16A-00C0F0283628}
00C484EB push dword ptr [ebp-FC]
00C484F1 push dword ptr [ebp-100]
00C484F7 call <jmp.&MSVBVM60.__vbaHresultCheckObj>
00C484FC mov dword ptr [ebp-140], eax
00C48502 jmp short 00C4850B
00C48504 and dword ptr [ebp-140], 0
00C4850B lea eax, dword ptr [ebp-A8]
00C48511 push eax
00C48512 call <jmp.&MSVBVM60.__vbaStrVarMove>
00C48517 mov edx, eax
00C48519 lea ecx, dword ptr [ebp-24]
00C4851C call <jmp.&MSVBVM60.__vbaStrMove>
... ...
00C48556 mov dword ptr [ebp-4], 13
00C4855D push dword ptr [ebp-24]
00C48560 call <jmp.&MSVBVM60.__vbaLenBstr>
00C48565 test eax, eax
00C48567 jnz short 00C4856E ; 前面返回的字符串非空则继续
00C48569 jmp 00C48E39
00C4856E mov dword ptr [ebp-4], 16
00C48575 lea eax, dword ptr [ebp-24]
00C48578 mov dword ptr [ebp-B0], eax
00C4857E mov dword ptr [ebp-B8], 4008
00C48588 lea eax, dword ptr [ebp-B8]
00C4858E push eax
00C4858F call <jmp.&MSVBVM60.rtcIsNumeric>
00C48594 movsx eax, ax
00C48597 test eax, eax
00C48599 jnz short 00C485A0 ; 前面返回的字符串纯数字则继续
00C4859B jmp 00C48DF1
00C485A0 mov dword ptr [ebp-4], 19
00C485A7 and word ptr [ebp-4C], 0 ; 累加值清零
00C485AC mov dword ptr [ebp-4], 1A
00C485B3 push dword ptr [ebp-24]
00C485B6 call <jmp.&MSVBVM60.__vbaLenBstr>
00C485BB mov dword ptr [ebp-108], eax ; 循环终值为串长
00C485C1 mov dword ptr [ebp-104], 1 ; 循环步长为1
00C485CB mov dword ptr [C7D304], 1 ; 循环初值为1
00C485D5 jmp short 00C485ED
00C485D7 mov eax, dword ptr [C7D304]
00C485DC add eax, dword ptr [ebp-104]
00C485E2 jo 00C48EFC
00C485E8 mov dword ptr [C7D304], eax
00C485ED mov eax, dword ptr [C7D304]
00C485F2 cmp eax, dword ptr [ebp-108]
00C485F8 jg 00C486D8 ; 超过字符串尾部则结束循环
00C485FE mov dword ptr [ebp-4], 1B
00C48605 mov dword ptr [ebp-80], 1
00C4860C mov dword ptr [ebp-88], 2
00C48616 lea eax, dword ptr [ebp-24]
00C48619 mov dword ptr [ebp-B0], eax
00C4861F mov dword ptr [ebp-B8], 4008
00C48629 lea eax, dword ptr [ebp-88]
00C4862F push eax
00C48630 push dword ptr [C7D304]
00C48636 lea eax, dword ptr [ebp-B8]
00C4863C push eax
00C4863D lea eax, dword ptr [ebp-98]
00C48643 push eax
00C48644 call <jmp.&MSVBVM60.rtcMidCharVar> ; 取一个字符
00C48649 lea eax, dword ptr [ebp-98]
00C4864F push eax
00C48650 lea eax, dword ptr [ebp-58]
00C48653 push eax
00C48654 call <jmp.&MSVBVM60.__vbaStrVarVal>
00C48659 push eax
00C4865A call <jmp.&MSVBVM60.rtcR8ValFromBstr> ; 转换成数值
00C4865F fstp qword ptr [ebp-F0]
00C48665 movsx eax, word ptr [ebp-4C]
00C48669 mov dword ptr [ebp-144], eax
00C4866F fild dword ptr [ebp-144]
00C48675 fstp qword ptr [ebp-14C]
00C4867B fild dword ptr [C7D304]
00C48681 fstp qword ptr [ebp-154]
00C48687 fld qword ptr [ebp-F0]
00C4868D fmul qword ptr [ebp-154] ; 乘以循环变量
00C48693 fadd qword ptr [ebp-14C] ; 累加
00C48699 fstsw ax
00C4869B test al, 0D
00C4869D jnz 00C48EF7
00C486A3 call <jmp.&MSVBVM60.__vbaFpI2> ; 取整
00C486A8 mov word ptr [ebp-4C], ax ; 回存
... ...
00C486CC mov dword ptr [ebp-4], 1C
00C486D3 jmp 00C485D7
00C486D8 mov dword ptr [ebp-4], 1D
00C486DF cmp word ptr [ebp-4C], 2EA ; 累加值等于746?
00C486E5 je short 00C486F4
00C486E7 cmp word ptr [ebp-4C], 257 ; 累加值等于599?
00C486ED je short 00C486F4
00C486EF jmp 00C48DF1 ; 都不是则去修改0xC7E4CC处的内容为"F"
事情已经很清楚了,只要0xC484D0处的调用所取得的字符串是数字串,且各位的值与其序号乘积的累加和为746或599,就可以保证0xC7E4CC处的内容维持不变。但0xC484D0处的调用是一个什么样的调用,所取得的字符串是从哪里来的呢?
很显然,0xC484D0处的调用一定是取得某个控件的属性值。那个控件就是0xC484E6处的指令中的CLSID所标识的控件,因为接下来要在调用失败时检查控件是否存活。
这个控件是什么控件呢?我们启动RegEdit,在注册表中搜索这个CLSID,发现这个控件原来是IImage控件,也就是ImageList中的ListImage。
然而,那个调用又是取得了ListImage的那一个属性呢?
刚好机器上装的有VB 6.0,急忙启动开,写了一个纯粹关于ImageList控件属性访问和方法调用的短程序,编译后用IDA 5.0反汇编,经过对比发现,0xC48477处的指令实际上就是取得ImageList中的某个ListImage对象,而0xC484D0处的指令访问的就是ListImage对象的Tag属性,
对ListImage对象的Tag属性进行设置的指令应该是“call dword ptr [eax+30]”的形式。
我们立即在IDA 5.0下AQT的反汇编结果中查找对0x4A9CA0的引用,而且其附近的某个指令符合前面那个形式。我们发现只有0x8DC71C始的子程序的三个地方符合要求,它们分别是0x8DCB34、0x8DCD4E和0x8DCF68。
我们用OllyICE重新加载AQT,在0x8DC71C处设置断点后执行之。当程序被中断后仔细跟踪,分析结果如下:
008DC8F0
; 比较0xC7E4CC处的字符是不是"S"或"X"
... ...
008DC959 je 008DCFBA ; 不是则另当别论
008DC95F mov edi, 004B9624 ; UNICODE "##########"
008DC964 mov dword ptr [ebp-168], edi
008DC96A mov dword ptr [ebp-170], 8
008DC974 lea edx, dword ptr [ebp-170]
008DC97A lea ecx, dword ptr [ebp-100]
008DC980 call <jmp.&MSVBVM60.__vbaVarDup>
008DC985 mov dword ptr [ebp-158], 00C7D3E4
008DC98F mov dword ptr [ebp-160], 4005
008DC999 push 1
008DC99B push 1
008DC99D lea eax, dword ptr [ebp-100]
008DC9A3 push eax
008DC9A4 lea eax, dword ptr [ebp-160]
008DC9AA push eax
008DC9AB lea eax, dword ptr [ebp-110]
008DC9B1 push eax
008DC9B2 call <jmp.&MSVBVM60.rtcVarFromFormatVar> ; 0xC7D3E4处的数值格式化为字符串
008DC9B7 mov dword ptr [ebp-188], edi
008DC9BD mov dword ptr [ebp-190], 8
008DC9C7 lea edx, dword ptr [ebp-190]
008DC9CD lea ecx, dword ptr [ebp-120]
008DC9D3 call <jmp.&MSVBVM60.__vbaVarDup>
008DC9D8 mov dword ptr [ebp-178], 00C7D3EC
008DC9E2 mov dword ptr [ebp-180], 4005
008DC9EC push 1
008DC9EE push 1
008DC9F0 lea eax, dword ptr [ebp-120]
008DC9F6 push eax
008DC9F7 lea eax, dword ptr [ebp-180]
008DC9FD push eax
008DC9FE lea eax, dword ptr [ebp-130]
008DCA04 push eax
008DCA05 call <jmp.&MSVBVM60.rtcVarFromFormatVar> ; 0xC7D3EC处的数值格式化为字符串
008DCA0A lea eax, dword ptr [ebp-110]
008DCA10 push eax
008DCA11 lea eax, dword ptr [ebp-130]
008DCA17 push eax
008DCA18 lea eax, dword ptr [ebp-140]
008DCA1E push eax
008DCA1F call <jmp.&MSVBVM60.__vbaVarCat> ; 第二个串连接到第一个串后面
008DCA24 push eax
008DCA25 call <jmp.&MSVBVM60.__vbaStrVarMove>
008DCA2A mov edx, eax
008DCA2C lea ecx, dword ptr [ebp-20]
008DCA2F call <jmp.&MSVBVM60.__vbaStrMove>
... ...
008DCA61 mov eax, dword ptr [C7E90C]
008DCA66 cmp eax, esi
008DCA68 jnz short 008DCA7E
008DCA6A push 00C7E90C
008DCA6F push 004446C4
008DCA74 call <jmp.&MSVBVM60.__vbaNew2>
008DCA79 mov eax, dword ptr [C7E90C]
008DCA7E mov edi, 004A9C90
008DCA83 push edi
008DCA84 push esi
008DCA85 push 4
008DCA87 mov ecx, dword ptr [eax]
008DCA89 push eax
008DCA8A call dword ptr [ecx+380]
008DCA90 push eax
008DCA91 lea eax, dword ptr [ebp-E8]
008DCA97 push eax
008DCA98 call <jmp.&MSVBVM60.__vbaObjSet>
008DCA9D push eax
008DCA9E lea eax, dword ptr [ebp-100]
008DCAA4 push eax
008DCAA5 call <jmp.&MSVBVM60.__vbaLateIdCallLd>
008DCAAA add esp, 10
008DCAAD push eax
008DCAAE call <jmp.&MSVBVM60.__vbaCastObjVar>
008DCAB3 push eax
008DCAB4 lea eax, dword ptr [ebp-EC]
008DCABA push eax
008DCABB call <jmp.&MSVBVM60.__vbaObjSet>
008DCAC0 mov dword ptr [ebp-1CC], eax
008DCAC6 mov dword ptr [ebp-108], 3
008DCAD0 mov dword ptr [ebp-110], 2
008DCADA mov ecx, dword ptr [eax]
008DCADC lea edx, dword ptr [ebp-F0]
008DCAE2 push edx
008DCAE3 lea edx, dword ptr [ebp-110]
008DCAE9 push edx
008DCAEA push eax
008DCAEB call dword ptr [ecx+24]
008DCAEE fclex
008DCAF0 cmp eax, esi
008DCAF2 jge short 008DCB03
008DCAF4 push 24
008DCAF6 push edi
008DCAF7 push dword ptr [ebp-1CC]
008DCAFD push eax
008DCAFE call <jmp.&MSVBVM60.__vbaHresultCheckObj>
008DCB03 mov eax, dword ptr [ebp-F0]
008DCB09 mov dword ptr [ebp-1D4], eax
008DCB0F mov ecx, dword ptr [ebp-20]
008DCB12 mov dword ptr [ebp-168], ecx
008DCB18 mov dword ptr [ebp-170], 8
008DCB22 mov ecx, dword ptr [eax]
008DCB24 sub esp, 10
008DCB27 lea esi, dword ptr [ebp-170]
008DCB2D mov edi, esp
008DCB2F movs dword ptr es:[edi], dword ptr [esi]
008DCB30 movs dword ptr es:[edi], dword ptr [esi]
008DCB31 movs dword ptr es:[edi], dword ptr [esi]
008DCB32 movs dword ptr es:[edi], dword ptr [esi]
008DCB33 push eax
008DCB34 call dword ptr [ecx+30] ; 设置ListImage的Tag属性
0xC7D3E4和0xC7D3EC是不是有些似曾相识?这不就是《Advanced Query Tool V8 的注册分析》一文中存放NumOfKey除以CheckSumOfName的商的高位和低位的地址吗?再仔细确认一下,现在这个值就是NumOfKey除以CheckSumOfName的商。
一切都真相大白了。AQT的注册信息不但要符合《Advanced Query Tool V8 的注册分析》一文的要求,而且NumOfKey除以CheckSumOfName的商的各位值与序号乘积的累加和还必须是746或599。马上修改注册机,生成注册码,重新注册、试用。哇!我的AQT已经是全功能版的了。这心里那个爽啊……