1、申 请 I D:FreeGod
2、个人邮箱:netfreegod3@126.com
3、原创技术文章: qq反汇编日志
IDA,">标 题: 【原创】qq反汇编日志1
作 者: freeGod
时 间: 2006-06-15,18:20:36
链 接: http://bbs.pediy.com/showthread.php?t=27458
2006年6月15日 9:30
oo,前两天的反汇编记录下来的东西都丢了(昨天重做系统时,忘了),不过还好,丢的东西大部分都是我的曲折错误分析
现在从新开始:(为写远程暴力破解qq密码程序而反汇编qq)
反汇编工具:OllyICE
现在开始:
用OllyICE加载qq.exe,qq登录对话框出现,随便输入密码:123,运行,一个错误对话框出现,
错误
输入密码与上次成功登录得密码不一致,
是否到服务器验证?
好,打开OllyICE的模块窗口,然后找到user32模块,打开右键菜单选中查看名称,OllyICE的名称窗口出现了,这里的函数都是
User32模块中的,好,找到MessageBoxA,打开右键菜单选中反汇编窗口中跟随,这样就到了MessageBoxA函数的入口点,按f2键设下
端点,设好后点刚才qq跳出来的错误对话框上的否,qq用户登录又出现了,再输入123
OllyICE在我们刚才设置的MessageBoxA函数的入口点停了下来,察看堆栈窗口
0012FC2C 60B5C8E7 /CALL 到 MessageBoxA 来自 MFC42.60B5C8E1
说明调用是MFC42库中的60b5c8e1处的指令的前一条指令调用了MessageBoxA,在CALL 到 MessageBoxA 来自 MFC42.60B5C8E1上
右键,打开右键菜单,选中反汇编窗口跟随,就到mfc42地址空间的60b5c8e7指令位置(不同的电脑,可能值不一样),上一条指令
就是调用MessageBoxA函数的,如下所示
60B5C8DC |. FF7424 10 push dword ptr [esp+10] ; |Text
60B5C8E0 |. 51 push ecx ; |hOwner
60B5C8E1 |. FF15 D0B5B960 call [<&USER32.MessageBoxA>] ; \MessageBoxA
60B5C8E7 |. 5E pop esi
60B5C8E8 |. C2 0C00 retn 0C
在 60b5c8e7 pop esi上设置端点,然后点运行,刚才的那个错误对话框又出现了,点否,好,现在程序停在了我们刚才设置端点的位
置605c8e7处,现在在MFC42地址空间中,不管它,我们想到的位置是qq空间,按两下f8键,到了 00415c35 cmp eax,6 指令处,看一
下OllyICE的标题栏 OllyICE - QQ.exe -[cpu -主线程,模块 - qq]
好了这就是我们的目的地。
反汇编就从这里开始,第一个分析的函数就是:包含 指令地址00415c35 的函数
第一步:找出 包含指令地址 00415c35 的 函数入口点
向上滚动反汇编窗口,找到00415b55 地址
00415B52 \. C2 0400 retn 4
00415B55 /$ B8 5CD24C00 mov eax, 004CD25C
00415B5A |. E8 41A10500 call 0046FCA0
00415B5F |. 83EC 48 sub esp, 48
OllyICE 分析出这个是函数的入口点,因为上面紧跟的是retn指令,不过一般的函数入口处:应该是
push ebp
mov ebp,esp
才对,如果是罗函数的话,经过优化编译的话就不是这个样子了,我们先在反汇编的是qq,qq的软件工程师水平应该是可以
的,有可能经过特殊处理,
004CD25C=004CD25C
eax=0012FCBC
本地调用来自 00414FE8, 00416FEE
这个交叉引用说明,的确是函数的入口点
这样函数入口点就确定了:004b5b55
第二步:确定函数结束地址,按OllyICE 的提示 结束地址应为:00415c9b retn 14,一个函数有可能有多个出口
我们来做进一步的鉴定,看看从004b5b55 到 00415c9b 之间的指令有无交叉引用到 00415c9b 以后的指令
答:没有,因此 00415c9b 即为函数的结束地址,至此我们确定了函数的入口点和结束地址
004b5b55--00415c9b
现在我们开始还原 函数 qq004b55 的源代码
第三步:确定参数个数和参数类型
找到调用 004b5b55 函数 指令 :00414fe8 ------------------------------------
|
00414FD6 |. C645 FC 05 mov byte ptr [ebp-4], 5 |
00414FDA |. E8 A1A60500 call <jmp.&MFC42.#535_CString::CString> |
00414FDF |. FF75 EC push dword ptr [ebp-14] |
00414FE2 |. 8BCB mov ecx, ebx |
00414FE4 |. C645 FC 05 mov byte ptr [ebp-4], 5 |
00414FE8 |. E8 680B0000 call 00415B55 ---------------------------------
00414FED |. FFD6 call esi
00414FE2 |. 8BCB mov ecx, ebx --- ecx 应该是个寄存器参数,要不这条指令就是个垃圾指令
函数004b5b55 可能有一个寄存器参数,说可能,是因为编译器生成的垃圾指令到处可见,要对ecx进一步鉴定,要
分析004b5b55 函数的代码
00415B55 /$ B8 5CD24C00 mov eax, 004CD25C
00415B5A |. E8 41A10500 call 0046FCA0
00415B5F |. 83EC 48 sub esp, 48
00415B62 |. 53 push ebx
00415B63 |. 56 push esi
00415B64 |. 33DB xor ebx, ebx
00415B66 |. 895D FC mov [ebp-4], ebx
00415B69 |. 895D F0 mov [ebp-10], ebx
00415B6C |. 8B81 84000000 mov eax, [ecx+84]
在函数的开始处,调用了一个函数 0046fca0,察看一下它的代码,看看和ecx有染没有
0046FCA0 /$ 6A FF push -1
0046FCA2 |. 50 push eax
0046FCA3 |. 64:A1 0000000>mov eax, fs:[0]
0046FCA9 |. 50 push eax
0046FCAA |. 8B4424 0C mov eax, [esp+C]
0046FCAE |. 64:8925 00000>mov fs:[0], esp
0046FCB5 |. 896C24 0C mov [esp+C], ebp
0046FCB9 |. 8D6C24 0C lea ebp, [esp+C]
0046FCBD |. 50 push eax
0046FCBE \. C3 retn
哈哈,这个函数挺简练的,和ecx无关,这就好,再看看00415b6c地址 以上的指令都与ecx无关,而在00415b6c处引用了
ecx,因此断定ecx就是一个寄存器参数,不错,good
下面来看一下004b5b55 函数 有多少个堆栈参数
从函数结束地址 00415C9B \. C2 1400 retn 14
可知 堆栈参数个数为 14h / 4 = 5
到此我们确定了参数的个数:一个寄存器参数 + 5 个堆栈参数 = 6(说明函数采用的是fastcall调用方式)
给他们编号分别为:arg1,arg2,arg3,arg4,arg5,arg6
下面我们来确定参数类型
在指令中识别参数
函数004b5b55 每有标准的函数头 即 push ebp ; mov ebp,esp,如果是采用优化编译的话,参数应该用esp寄存器来寻址
可在函数过程中只在分配局部变量时,用了一次esp
00415B5F |. 83EC 48 sub esp, 48
其他的地方都没有,令人奇怪的是到处都是用ebp寄存器寻址的,对了,函数入口点,调用了一个函数:0046fca0
看看它都实现了什么功能
0046FCA0 /$ 6A FF push -1
执行指令后堆栈数据
相对esp的地址 数据
esp -1
esp+4 ret addr
0046FCA2 |. 50 push eax
执行指令后堆栈数据
相对esp的地址 数据
esp eax (通过寄存器eax传递过来的参数进栈)
esp+4 -1
esp+8 ret addr
0046FCA3 |. 64:A1 0000000>mov eax, fs:[0] ;eax 指向 seh (结构化异常处理)
0046FCA9 |. 50 push eax
执行指令后堆栈数据
相对esp的地址 数据
esp eax (seh 指针)
esp+4 eax (通过寄存器eax传递过来的参数进栈)
esp+8 -1
esp+c ret addr
0046FCAA |. 8B4424 0C mov eax, [esp+C] ;把ret addr 传递给eax
执行指令后堆栈数据
相对esp的地址 数据
esp eax (seh 指针)
esp+4 eax (通过寄存器eax传递过来的参数进栈)
esp+8 -1
esp+c ret addr
0046FCAE |. 64:8925 00000>mov fs:[0], esp ;此时esp指向一个 EXCEPTION_REGISTRATION 结构
;表明同过寄存器eax传递给函数的是异常回调函数地址
;-1 则是seh 的附加数据
执行指令后堆栈数据
相对esp的地址 数据
esp eax (seh 指针)<-------------------------------fs:[0]
esp+4 eax (通过寄存器eax传递过来的参数进栈)
esp+8 -1
esp+c ret addr
0046FCB5 |. 896C24 0C mov [esp+C], ebp ;保存ebp 到 原来 ret addr 所占堆栈位置
执行指令后堆栈数据
相对esp的地址 数据
esp eax (seh 指针)<-------------------------------fs:[0]
esp+4 eax (通过寄存器eax传递过来的参数进栈)
esp+8 -1
esp+c ebp ----- 注意发生变化了
0046FCB9 |. 8D6C24 0C lea ebp, [esp+C] ;ebp指向 保存ebp的位置(也即原来 ret addr 的位置)
执行指令后堆栈数据
相对esp的地址 数据
esp eax (seh 指针)<-------------------------------fs:[0]
esp+4 eax (通过寄存器eax传递过来的参数进栈)
esp+8 -1
esp+c ebp ----- 注意发生变化了<------------------------ ebp
0046FCBD |. 50 push eax
执行指令后堆栈数据
相对ebp的地址 相对esp的地址 数据
ebp-10 esp eax(函数的返回地址)
ebp-c esp+4 eax (seh 指针)<-------------------------------fs:[0]
ebp-8 esp+8 eax (通过寄存器eax传递过来的参数进栈)
ebp-4 esp+c -1
ebp esp+10 ebp ----- 注意发生变化了<------------------------ ebp
0046FCBE \. C3 retn
执行指令后堆栈数据
相对ebp的地址 相对esp的地址 数据
ebp-c esp eax (seh 指针)<-------------------------------fs:[0]
ebp-8 esp+4 eax (通过寄存器eax传递过来的参数进栈)
ebp-4 esp+8 -1
ebp esp+c ebp ----- 注意发生变化了<------------------------ ebp
因为 函数0046fca0没有堆栈参数,所以 ebp+4 指向的是它的父函数即 004b5b55 结束时要返回的地址
相对ebp的地址 相对esp的地址 数据
ebp-c esp eax (seh 指针)<-------------------------------fs:[0]
ebp-8 esp+4 eax (通过寄存器eax传递过来的参数进栈)
ebp-4 esp+8 -1
ebp esp+c ebp ----- 注意发生变化了<------------------------ ebp
ebp +4 esp+10 004b5b55函数 结束时要返回的地址
综上所述:0046fca0 实现的功能为:《1》注册异常回调函数
《2》实现了和 push ebp
mov ebp,esp
查不多的功能,调用0046fca0函数的函数的第一个堆栈参数地址为 ebp +8
这和push ebp ;move ebp,esp 是一样的,但是局部变量的寻址就不一样了
调用0046fca0 函数的函数局部变量是从ebp-d 开始的,而不是从ebp-4开始的
搞定……^_^
现在我们就可以很轻松的识别出参数了,开始吧
识别参数类型
首先我用32位汇编语言来实现00415b55,我给他起个名字叫 _lastStep 吧,我把寄存器传参也改成堆栈方式
_LastStep proc _arg1,_arg2,_arg3,_arg4,_arg5,_arg6
mov eax,004cd25c
call 0046fca0
sub esp,48h
push ebx
push esi
xor ebx,ebx
mov [ebp-4],ebx ;[ebp-4] 是seh附加数据的地址,原来值为-1,现在要把它置零了
mov @dwVar1,ebx
mov ecx,_arg1
mov eax,[ecx+84] ;可知_arg1 是一个指针
lea edx,@dwVar1
push edx
push 004e7460
mov ecx,[eax]
push eax
mov byte ptr[ebp-4],1 ;[ebp-4] 是seh附加数据的地址
call [ecx+1c] ;表明_arg1 是一个函数指针的指针的指针
test eax,eax
jnz lable1
mov eax,@dwVar1
lea edx,@dwVar2
push edx
push 004e8940 ;ascii "ewh.db"
mov ecx,[eax]
push eax
call [ecx+14]
test eax,eax
je label2
lable1:
mov eax,_arg4 ;可知_arg4 是一个int*
mov dword ptr[eax],2
jmp lable3
lable2:
mov eax,_arg3
push edi
mov edx,@dwVar1
push 1
mov ecx,[eax-8]
pop esi
mov edi,[edx]
push esi
push eax
push ecx
push ebx
push dword ptr _arg2
push edx
call [edi+1c]
test eax,eax
pop edi
je lable4
cmp _arg6,esi
je lable5
mov eax,_arg4
mov [eax],ebx ;可知_arg4是个指针
jmp lable3
lable5:
lea ecx,_arg6
call CString::CString
lea ecx,_arg2
mov byte ptr [ebp-4],2
call CString::CString
mov esi,BasicCtrDll.BasicLoadStr
lea eax,_arg6
push 281
push eax
mov byte prt[ebp-4],3
call esi
lea eax,_arg2
push 28d
push eax
call esi
add esp,10
lea ecx,[ebp-54] ;ebp-54 指向一个结构
call CWNd::CWnd
push 114
lea ecx,[ebp-54]
push dword ptr _arg2
mov byte prt[ebp-4],4
push dword ptr _arg6
call CWnd::MessageBoxA
cmp eax,6
mov eax,_arg4
jnz Lable6
mov dword prt[eax],2
jmp lable7
lable6:
mov [eax],ebx
lable7:
lea ecx,[ebp-54]
mov byte ptr[ebp-4],3
call CWnd::~Cwnd
lea ecx,_arg2
mov byte ptr[ebp-4],2
call CString::~CString
lea ecx,_arg6
mov byte ptr[ebp-4],1
call CString::~Cstring
jmp lable3
lable4:
mov eax,_arg4
mov [eax],esi
lable3:
mov eax,@dwVar1
mov [ebp-4],bl
cmp eax,ebx
je lable8
mov ecx,[eax]
push eax
call [ecx+8]
lable8:
or dwword ptr[ebp-4],0ffffffffh
lea ecx,_arg3
call CString::~CString
mov ecx,[ebp-c] ; pre seh handler 指针
pop esi
pop ebx
mov fs:[0],ecx
leave
retn 14
_LastSetp enp
今天就到这里,明天继续
2006-6-15 13:20
|