1
2006-6-17 20:09
识别函数中的参数和局部变量
经过上面的分析我们已经知道在qq00405b55函数中参数和局部变量都是用ebp寄存器来表示的,现在我们来找出函数中的参数,和局部变量
首先:我们把函数中使用ebp寄存器寻找的变量找出来,删除重复的并按从低地址到高地址排序,得到如下结果:
ebp-54h
ebp-14h
ebp-10h
ebp-ch
ebp-4h
ebp+8h
ebp+ch
ebp+10h
ebp+18h
我们知道堆栈参数是:从ebp+8开始向高地址方向的,局部变量是从ebp-d开始向低地址方向的
现在给他们分组:
堆栈参数有:ebp+8h,ebp+ch,ebp+10h,ebp+18h,堆栈参数名我们规定从低地址开始编号_arg1,_arg2,_arg3,....,所以就有 :
ebp+8h = _arg1
ebp+ch = _arg2
ebp+10h = _arg3
ebp+18h = _arg4 少了一个 怎么没有ebp+14h 呢,_arg3 占了8个字节的地盘???先不管它,呆会分析参数类型的时候在看
局部变量有:ebp-10h,ebp-14h,ebp-54h,局部变量我们规定从高地址开始编号@var1,@var2,@var3,.....所以有:
ebp-10h = @var1 因为局部变量是从ebp-d开始的所以可知这个局部变量的大小为(10h-d)+1 4 byte
ebp-14h = @var2 同理:这个紧挨着@var1 可知他也为(14h-11h)+1 4 byte
ebp-54h = @var3 这是最后一个变量,它的大小为:(54h-15h)+1 = 40h byte
还有几个孤儿,没有组的,他们是ebp-ch,和ebp-4h,日至1中已经知道他们分别是seh指针和seh的附加数据
第二步:我们将参数名、局部变量名替换到反汇编代码中去,还有跳转地址我们也用相应的标号替换掉就得到了
mov eax, 004CD25C
call 0046FCA0
sub esp, 48
push ebx
push esi
xor ebx, ebx
mov [ebp-4], ebx
mov @var1, ebx
mov eax, [ecx+84]
lea edx, @var1
push edx
push 004E7460
mov ecx, [eax]
push eax
mov byte ptr [ebp-4], 1
call [ecx+1C]
test eax, eax
jnz lable1 ;short 00415B9F
mov eax, @var1
lea edx, @var2
push edx
push 004E8940
mov ecx, [eax]
push eax
call [ecx+14]
test eax, eax
je lable2 ;short 00415BAD
lable1:
mov eax, _arg3
mov dword ptr [eax], 2
jmp lable3 ;00415C72
lable2:
mov eax, _arg2
push edi
mov edx, @var1
push 1
mov ecx, [eax-8]
pop esi
mov edi, [edx]
push esi
push eax
push ecx
push ebx
push dword ptr _arg1
push edx
call [edi+1C]
test eax, eax
pop edi
je lable4 ;00415C6D
cmp _arg4, esi
je lable5 ;short 00415BDF
mov eax, _arg3
mov [eax], ebx
jmp lable3 ;00415C72
lable5:
lea ecx, _arg4
call
lea ecx, _arg1
mov byte ptr [ebp-4], 2
call
mov esi, [; BasicCtr.BasicLoadStr
lea eax, _arg4
push 281
push eax
mov byte ptr [ebp-4], 3
call esi ;
lea eax, _arg1
push 28D
push eax
call esi
add esp, 10
lea ecx, @var3
call
push 114
lea ecx, @var3
push dword ptr _arg1
mov byte ptr [ebp-4], 4
push dword ptr _arg4
call
cmp eax, 6
mov eax, _arg3
jnz label6 ;short 00415C45
mov dword ptr [eax], 2 ; [eax]可能保存的是某个标志,2代表本地登陆失败,到服务器上去验证去
jmp lable7 ;short 00415C47
lable6:
mov [eax], ebx
lable7:
lea ecx, @var3
mov byte ptr [ebp-4], 3 ; [12fcac]
call
lea ecx, _arg1
mov byte ptr [ebp-4], 2
call
lea ecx, _arg4
mov byte ptr [ebp-4], 1
call
jmp lable3 ;short 00415C72
lable4:
mov eax, _arg3
mov [eax], esi
lable3:
mov eax, _arg3
mov [ebp-4], bl
cmp eax, ebx
je lable8 ;short 00415C82
mov ecx, [eax]
push eax
call [ecx+8]
lable8:
or dword ptr [ebp-4], FFFFFFFF
lea ecx, _arg2
call
mov ecx, [ebp-C] ; seh 指针
pop esi
pop ebx
mov fs:[0], ecx
leave
retn 14 ; 父函数传递了5个参数
靠,终于整完了
下一步:去掉编译器自己添加的或与函数功能无关的,异常处理东东
删掉
mov eax, 004CD25C
call 0046FCA0
注册异常处理函数,打开堆栈页面
sub esp, 48 ;分配局部变量空间
删掉
mov ecx, [ebp-C] ; seh 指针
删掉
mov fs:[0], ecx
leave
retn 14
将注册的异常处理函数删除掉,堆栈平衡
剩下的就是裸函数了
push ebx
push esi
xor ebx, ebx
mov [ebp-4], ebx
mov @var1, ebx
mov eax, [ecx+84]
lea edx, @var1
push edx
push 004E7460
mov ecx, [eax]
push eax
mov byte ptr [ebp-4], 1
call [ecx+1C]
test eax, eax
jnz lable1
mov eax, @var1
lea edx, @var2
push edx
push 004E8940
mov ecx, [eax]
push eax
call [ecx+14]
test eax, eax
je lable2
lable1:
mov eax, _arg3
mov dword ptr [eax], 2
jmp lable3
lable2:
mov eax, _arg2
push edi
mov edx, @var1
push 1
mov ecx, [eax-8]
pop esi
mov edi, [edx]
push esi
push eax
push ecx
push ebx
push dword ptr _arg1
push edx
call [edi+1C]
test eax, eax
pop edi
je lable4
cmp _arg4, esi
je lable5
mov eax, _arg3
mov [eax], ebx
jmp lable3
lable5:
lea ecx, _arg4
call
lea ecx, _arg1
mov byte ptr [ebp-4], 2
call
mov esi, [; BasicCtr.BasicLoadStr
lea eax, _arg4
push 281
push eax
mov byte ptr [ebp-4], 3
call esi ;
lea eax, _arg1
push 28D
push eax
call esi
add esp, 10
lea ecx, @var3
call
push 114
lea ecx, @var3
push dword ptr _arg1
mov byte ptr [ebp-4], 4
push dword ptr _arg4
call
cmp eax, 6
mov eax, _arg3
jnz label6
mov dword ptr [eax], 2
jmp lable7
lable6:
mov [eax], ebx
lable7:
lea ecx, @var3
mov byte ptr [ebp-4], 3
call
lea ecx, _arg1
mov byte ptr [ebp-4], 2
call
lea ecx, _arg4
mov byte ptr [ebp-4], 1
call
jmp lable3
lable4:
mov eax, _arg3
mov [eax], esi
lable3:
mov eax, _arg3
mov [ebp-4], bl
cmp eax, ebx
je lable8
mov ecx, [eax]
push eax
call [ecx+8]
lable8:
or dword ptr [ebp-4], FFFFFFFF
lea ecx, _arg2
call
pop esi
pop ebx
让函数光着身子不好,现在给它穿上衣服吧
我先给它穿上win32asm 这套衣服,以后再给她穿c++这套衣服,给这个过程起个名字:叫 _TempName 吧,现在还不知道她是做什么用的,知道了,再换
寄存器参数我用 _regArg1,_regArg2,...表示
_TempName Proc _regArg1,_arg1,_arg2,_arg3,_arg?,_arg4 (因为不知道倒数第二个为什么没有在函数中引用过,所以先用?表示)
push ebx
push esi
xor ebx, ebx
mov [ebp-4], ebx
mov @var1, ebx
mov eax, [ecx+84]
lea edx, @var1
push edx
push 004E7460
mov ecx, [eax]
push eax
mov byte ptr [ebp-4], 1
call [ecx+1C]
test eax, eax
jnz lable1
mov eax, @var1
lea edx, @var2
push edx
push 004E8940
mov ecx, [eax]
push eax
call [ecx+14]
test eax, eax
je lable2
lable1:
mov eax, _arg3
mov dword ptr [eax], 2
jmp lable3
lable2:
mov eax, _arg2
push edi
mov edx, @var1
push 1
mov ecx, [eax-8]
pop esi
mov edi, [edx]
push esi
push eax
push ecx
push ebx
push dword ptr _arg1
push edx
call [edi+1C]
test eax, eax
pop edi
je lable4
cmp _arg4, esi
je lable5
mov eax, _arg3
mov [eax], ebx
jmp lable3
lable5:
lea ecx, _arg4
call
lea ecx, _arg1
mov byte ptr [ebp-4], 2
call
mov esi, [; BasicCtr.BasicLoadStr
lea eax, _arg4
push 281
push eax
mov byte ptr [ebp-4], 3
call esi ;
lea eax, _arg1
push 28D
push eax
call esi
add esp, 10
lea ecx, @var3
call
push 114
lea ecx, @var3
push dword ptr _arg1
mov byte ptr [ebp-4], 4
push dword ptr _arg4
call
cmp eax, 6
mov eax, _arg3
jnz label6
mov dword ptr [eax], 2
jmp lable7
lable6:
mov [eax], ebx
lable7:
lea ecx, @var3
mov byte ptr [ebp-4], 3
call
lea ecx, _arg1
mov byte ptr [ebp-4], 2
call
lea ecx, _arg4
mov byte ptr [ebp-4], 1
call
jmp lable3
lable4:
mov eax, _arg3
mov [eax], esi
lable3:
mov eax, _arg3
mov [ebp-4], bl
cmp eax, ebx
je lable8
mov ecx, [eax]
push eax
call [ecx+8]
lable8:
or dword ptr [ebp-4], FFFFFFFF
lea ecx, _arg2
call
pop esi
pop ebx
ret
_TempName endp
下一步分析qq00415b55 调用的函数
第一个 call [ecx+1C] oo 是个间接调用指令,那来动态跟踪以下调用的是什么?00410ee2
00410EE2 . B8 CCC84C00 mov eax, 004CC8CC
00410EE7 . E8 B4ED0500 call 0046FCA0
00410EEC . 51 push ecx
00410EED . 51 push ecx
00410EEE . 8B45 08 mov eax, [ebp+8]
00410EF1 . 56 push esi
00410EF2 . 8D4D EC lea ecx, [ebp-14]
00410EF5 . FF70 D8 push dword ptr [eax-28]
00410EF8 . 8D70 BC lea esi, [eax-44]
00410EFB . E8 94E80500 call
00410F00 . FF75 10 push dword ptr [ebp+10] ; /Arg2
00410F03 . 8365 FC 00 and dword ptr [ebp-4], 0 ; |
00410F07 . 8BCE mov ecx, esi ; |
00410F09 . FF75 0C push dword ptr [ebp+C] ; |Arg1
00410F0C . E8 71040000 call 00411382 ; \QQ.00411382
00410F11 . 8B4D F0 mov ecx, [ebp-10]
00410F14 . 8B55 EC mov edx, [ebp-14]
00410F17 . 834D FC FF or dword ptr [ebp-4], FFFFFFFF
00410F1B . 5E pop esi
00410F1C . 8951 04 mov [ecx+4], edx
00410F1F . 8B4D F4 mov ecx, [ebp-C]
00410F22 . 64:890D 00000>mov fs:[0], ecx
00410F29 . C9 leave
00410F2A . C2 0C00 retn 0C
分析过程和 分析qq00415b55 一样
经分析可知 函数没有寄存器参数,c/4=3个堆栈参数,这里要注意的是
00410EEC . 51 push ecx
00410EED . 51 push ecx
这两条指令是分配局部变量空间的,因为函数中有两个局部变量,这是编译器快速分配局部变量而使得花招
00410ee2 的函数原型为:
00410ee2 proto _arg1,_arg2,_arg3
下一个qq00415b55调用的函数是 call [ecx+14] = 0044c62b
0044C62B . B8 84424D00 mov eax, 004D4284
0044C630 . E8 6B360200 call 0046FCA0
0044C635 . 51 push ecx
0044C636 . 51 push ecx
0044C637 . 8B45 08 mov eax, [ebp+8]
0044C63A . 56 push esi
0044C63B . 8D4D EC lea ecx, [ebp-14]
0044C63E . FF70 FC push dword ptr [eax-4]
0044C641 . 8D70 E0 lea esi, [eax-20]
0044C644 . E8 4B310200 call
0044C649 . FF75 10 push dword ptr [ebp+10]
0044C64C . 8B06 mov eax, [esi]
0044C64E . 8365 FC 00 and dword ptr [ebp-4], 0
0044C652 . FF75 0C push dword ptr [ebp+C]
0044C655 . 56 push esi
0044C656 . FF50 64 call [eax+64]
0044C659 . 8B4D F0 mov ecx, [ebp-10]
0044C65C . 8B55 EC mov edx, [ebp-14]
0044C65F . 834D FC FF or dword ptr [ebp-4], FFFFFFFF
0044C663 . 5E pop esi
0044C664 . 8951 04 mov [ecx+4], edx
0044C667 . 8B4D F4 mov ecx, [ebp-C]
0044C66A . 64:890D 00000>mov fs:[0], ecx
0044C671 . C9 leave
0044C672 . C2 0C00 retn 0C
看起来好像和上个函数长得很像
函数原型为:
0044c62b proto _arg1,_arg2,_arg3
next function: 0044c6c5
函数原型为:
0044c6c5 proto _arg1,_arg2,_arg3,_arg4,_arg5,_arg6
下一个函数是 BasicCtr.BasicLoadStr
60092057 >/$ 55 push ebp
60092058 |. 8BEC mov ebp, esp
6009205A |. 56 push esi
6009205B |. 57 push edi
6009205C |. E8 5FCD0000 call
60092061 |. 8B70 0C mov esi, [eax+C]
60092064 |. 8B3D BC8B0B60 mov edi, [600B8BBC] ; BasicCtr.60090000
6009206A |. E8 51CD0000 call
6009206F |. FF75 0C push dword ptr [ebp+C]
60092072 |. 8B4D 08 mov ecx, [ebp+8]
60092075 |. 8978 0C mov [eax+C], edi
60092078 |. E8 91CD0000 call
6009207D |. E8 3ECD0000 call
60092082 |. 8970 0C mov [eax+C], esi
60092085 |. 5F pop edi
60092086 |. 5E pop esi
60092087 |. 5D pop ebp
60092088 \. C3 retn
可算见到一个正常函数,函数有两个堆栈参数参数,没有寄存器参数,这个函数调用方式属于_cdel方式,平很堆栈交给了
qq00415b55,qq00415b55调用完这个函数后并没有做平衡堆栈,而是在第二次调用完这个函数后一起做了堆栈平衡,这可能是
优化编译的结果,这样节省一条指令
我们从basicCtr.BasicLoadStr中也可以找出参数,[ebp+8],[ebp+c]这两个
在qq00415b55中平衡堆栈代码
00415C06 |. FFD6 call esi ; BasicCtr.BasicLoadStr;
00415C08 |. 8D45 08 lea eax, [ebp+8] ; _arg2
00415C0B |. 68 8D020000 push 28D
00415C10 |. 50 push eax
00415C11 |. FFD6 call esi
00415C13 |. 83C4 10 add esp, 10 ---------------------平衡了两次调用,一个函数的参数个数=10h/2/4=2个
所以这个函数的原型应为:
BasicCtr.BasicLoadStr proto _arg1,_arg2
下一个,也是qq00415b55调用qq自定义函数中的最后一个 0044cf97 函数只有一个堆栈参数,没有寄存器参数,函数原型为:
0044cf97 proc _arg1
经过上面的分析,qq00415b55 的win32asm 源代码应为:
BasicCtr.BasicLoadStr proto _arg1,_arg2
0044cf97 proto _arg1
_TempName Proc _regArg1,_arg1,_arg2,_arg3,_arg?,_arg4 (因为不知道倒数第二个为什么没有在函数中引用过,所以先用?表示)
local @dwVar1,@dwVar2
local @wnd:CWnd (这个以后再解释)
push ebx
push esi
xor ebx, ebx
mov [ebp-4], ebx
mov @var1, ebx
mov eax, [ecx+84]
mov ecx, [eax]
mov byte ptr [ebp-4], 1
invoke [ecx+1c],eax,004e7460,addr @var1 (这里不管 addr 会覆盖掉 eax的值)
test eax, eax
jnz lable1
mov eax, @var1
mov ecx, [eax]
invoke [ecx+14],eax,004e8940,addr @var2(这里不管 addr 会覆盖掉 eax的值)
test eax, eax
je lable2
lable1:
mov eax, _arg3
mov dword ptr [eax], 2
jmp lable3
lable2:
mov eax, _arg2
push edi
mov edx, @var1
push 1
mov ecx, [eax-8]
pop esi
mov edi, [edx]
invoke [edi+1c],edx,_arg1,ebx,ecx,eax,esi
test eax, eax
pop edi
je lable4
cmp _arg4, esi
je lable5
mov eax, _arg3
mov [eax], ebx
jmp lable3
lable5:
invoke CString::CString,_arg4
mov byte ptr [ebp-4], 2
invoke CString::CString>,_arg1
mov byte ptr [ebp-4], 3
invoke BasicCtrlDll.BasicLoadStr,addr _arg4,281h
invoke BasicCtrlDll.BasicLoadStr,addr _arg1,28dh
invoke CWnd::CWnd,addr @wnd
mov byte ptr [ebp-4], 4
invoke CWnd::MessageBox,addr @wnd,_arg4,_arg1,MB_YESNO or MB_DEFBUTTON2 or MB_ICONHAND (114h) ;这个就是登录失败的时候的标题和信息
cmp eax, 6
mov eax, _arg3
jnz label6
mov dword ptr [eax], 2
jmp lable7
lable6:
mov [eax], ebx
lable7:
mov byte ptr [ebp-4], 3
invoke CWnd::~CWnd,addr @wnd
mov byte ptr [ebp-4], 2
invoke CString::~CString,addr _arg1
mov byte ptr [ebp-4], 1
invoke CString::~CString,_arg4
jmp lable3
lable4:
mov eax, _arg3
mov [eax], esi
lable3:
mov eax, _arg3
mov [ebp-4], bl
cmp eax, ebx
je lable8
mov ecx, [eax]
invoke [ecx+8],eax
lable8:
or dword ptr [ebp-4], FFFFFFFF
invoke CString::~CString,_arg2
pop esi
pop ebx
ret
_TempName endp
有点累了,今天就到这里了,明天继续.......
2006-06-17 20:00