第5篇终于出来了……wuwu
今天是个特别的日子,先乱弹一番。
巴勒斯坦的英雄――亚西尔・阿拉法特去世了。在这里,谨向他致以最高敬意!
另外,今天也是中国人民解放军空军部队成立55周年纪念日!缅怀一下!想当年,新生的中国空军在朝鲜大展神威……
最后,今天和好友联系时偶尔得知她还是单身,呵呵……于是心情大好,决定今天把这篇东西赶出来。
好,废话少说,开讲!
===========================================================
抱歉,最近一直很忙,所以拖了很长时间。
这部分超长,自我感觉也是最难的部分。和这部分相比,前面的分析基本就是大路货了。请
有点耐心,否则还是请赶快离开的好。
=================================================================================
OK,LicenseProc的最后一部分――WM_PAINT。应该有却还没有的效果应该就在里
面。
0000:00404EBD loc_404EBD:
0000:00404EBD lea eax, [ebp+Paint]
0000:00404EC0 push eax ; lpPaint
0000:00404EC1 mov eax, [ebp+hWnd]
0000:00404EC4 push eax ; hWnd
0000:00404EC5 call BeginPaint
0000:00404ECA push offset sRect
0000:00404ECF push 0
0000:00404ED1 push 767676h
0000:00404ED6 mov ecx, offset dword_4052F0
0000:00404EDB mov edx, ds:dword_4076E8//结合下面可以看到,这是一个hIcon,但奇怪的是,IDA显示没有其它的
//ref。然而我翻了一下前面的代码,发现这就是主程序的h_Icon!
//看来IDA的确有问题。
0000:00404EE1 call near ptr sub_403FF8//好吧,又来一个公共call。
//记得以前专门研究过TP/Delphi的传值问题,但当时写的东西不知道丢
//哪里了。不过幸好D5的HLP已经很详细了。
//从这里看应该有5个参数,但是考虑到Register方式的特点,我怀疑还
//有第6个参数eax。从逻辑上看也应该是这样。查看sub_403FF8发现,
//需要在eax里放一个hdc,而BeginPaint的返回值就是hdc。
//那么dword_4052F0是什么呢?查看Hex可知,就是窗口标题 。这样,我
//以前的一个分析就被证明是错误的,不过不影响已经得到的代码。
0000:00404EE6 lea eax, [ebp+Paint]
0000:00404EE9 push eax ; lpPaint
0000:00404EEA mov eax, [ebp+hWnd]
0000:00404EED push eax ; hWnd
0000:00404EEE call EndPaint
0000:00404EF3 jmp loc_404F8F
公共call:
0000:00403FF8 sub_403FF8 proc far
0000:00403FF8
0000:00403FF8 rc = RECT ptr -50h
0000:00403FF8 var_40 = LOGBRUSH ptr -40h
0000:00403FF8 Rect = tagRECT ptr -34h
0000:00403FF8 var_24 = word ptr -24h//这个是循环变量
0000:00403FF8 var_22 = word ptr -22h
0000:00403FF8 var_20 = word ptr -20h
0000:00403FF8 var_1E = word ptr -1Eh
0000:00403FF8 var_1C = word ptr -1Ch
0000:00403FF8 var_1A = word ptr -1Ah
0000:00403FF8 var_18 = word ptr -18h//从var_18到var_22都用了一次
0000:00403FF8 var_16 = word ptr -16h
0000:00403FF8 var_14 = word ptr -14h
0000:00403FF8 var_12 = word ptr -12h//上面3个有点象rgb,范围都是字节,变量
0000:00403FF8 hbr = dword ptr -10h//必须说明变量
0000:00403FF8 var_C = dword ptr -0Ch//仅用一次
0000:00403FF8 hIcon = dword ptr -8//内部没有改变
0000:00403FF8 hDC = dword ptr -4//内部没有改变
0000:00403FF8 arg_4 = dword ptr 10h
0000:00403FF8
0000:00403FF8 push ebp
0000:00403FF9 mov ebp, esp
0000:00403FFB add esp, 0FFFFFFB0h
0000:00403FFE push ebx
0000:00403FFF push esi
0000:00404000 push edi
0000:00404001 mov esi, [ebp+arg_4]//公共的Rect变量
0000:00404004 lea edi, [ebp+Rect]//局部Rect变量
0000:00404007 push ecx
0000:00404008 mov ecx, 4
0000:0040400D rep movsd
0000:0040400F pop ecx//把公共变量复制过来
0000:00404010 mov [ebp+var_C], ecx
0000:00404013 mov [ebp+hIcon], edx
0000:00404016 mov [ebp+hDC], eax//三个通过reg传递的参数
0000:00404019 xor eax, eax
0000:0040401B call nullsub_1//空过程,为什么?延时?还是Align?
0000:00404020 and eax, 0FFh
0000:00404025 mov [ebp+var_18], ax//置0
0000:00404029 xor eax, eax
0000:0040402B call sub_403C90//右移8位
0000:00404030 and eax, 0FFh
0000:00404035 mov [ebp+var_1A], ax
0000:00404039 xor eax, eax
0000:0040403B call sub_403C94//右移16位,就是取高位
0000:00404040 and eax, 0FFh
0000:00404045 mov [ebp+var_1C], ax//置0
//很多废代码
0000:00404049 mov eax, 767676h//这里很奇特,不知道和那个废参数有什么关系。怀疑原来就是
//那个参数,因为调试的需要临时注释掉了,或者相反。
0000:0040404E call nullsub_1
0000:00404053 and eax, 0FFh
0000:00404058 mov [ebp+var_1E], ax
0000:0040405C mov eax, 767676h
0000:00404061 call sub_403C90
0000:00404066 and eax, 0FFh
0000:0040406B mov [ebp+var_20], ax
0000:0040406F mov eax, 767676h
0000:00404074 call sub_403C94
0000:00404079 and eax, 0FFh
0000:0040407E mov [ebp+var_22], ax//字节分离,太象RGB了!
0000:00404082 mov ebx, [ebp+Rect.right]
0000:00404085 sub ebx, [ebp+Rect.left]
0000:00404088 sar ebx, 1
0000:0040408A jns short loc_40408F
0000:0040408C adc ebx, 0//取水平中点,或者说水平长度的一半
0000:0040408F
0000:0040408F loc_40408F:
0000:0040408F xor eax, eax
0000:00404091 mov [ebp+rc.top], eax//清0
0000:00404094 mov eax, [ebp+Rect.bottom]
0000:00404097 sub eax, [ebp+Rect.top]
0000:0040409A mov [ebp+rc.bottom], eax//这里应该是求高
0000:0040409D xor eax, eax
0000:0040409F mov [ebp+var_40.lbStyle], eax//清0
0000:004040A2 xor eax, eax
0000:004040A4 mov [ebp+var_40.lbHatch], eax//同上
0000:004040A7 test bx, bx
0000:004040AA jl loc_4041C8
0000:004040B0 mov eax, ebx
0000:004040B2 inc eax
0000:004040B3 mov [ebp+var_24], ax//sar以后最高位是保留的
0000:004040B7 xor edi, edi//从下面可以看到,edi是个计数器,而var_24是循环变量
//一般说循环变量会用reg以加快速度的,但是这里例外
//看循环变量就看循环末尾的dec什么的
0000:004040B9
0000:004040B9 loc_4040B9://一个Damn循环,很长,到004041C2
0000:004040B9 movsx esi, bx//注意movsx,这说明ebx不会很大
//但是ebx>$7f=127可以说明什么呢?
0000:004040BC push esi ; nDenominator
0000:004040BD push esi ; nNumerator
0000:004040BE movsx eax, di
0000:004040C1 push eax ; nNumber
0000:004040C2 call MulDiv//这个地方虽然调用了MulDiv,但是由于不知道它是不是由Delphi自动调用的,
//所以必须写段程序实验一下。用两个Longword相乘,再除以另外一个Longword。
//可以看到,Delphi用了浮点指令。所以结论是,MulDiv是源程序调用的。
//另外,注意到MulDiv的参数意义,可以看出这个调用实际是没有实际作用的。
//这样的例子下面还有。
0000:004040C7 mov [ebp+rc.left], eax
0000:004040CA push esi ; nDenominator
0000:004040CB push esi ; nNumerator
0000:004040CC movsx eax, di
0000:004040CF inc eax
0000:004040D0 push eax ; nNumber
0000:004040D1 call MulDiv
0000:004040D6 mov [ebp+rc.right], eax
0000:004040D9 push esi ; nDenominator
0000:004040DA movsx eax, [ebp+var_1E]
0000:004040DE push eax ; nNumerator
0000:004040DF movsx eax, di
0000:004040E2 push eax ; nNumber
0000:004040E3 call MulDiv
0000:004040E8 add ax, [ebp+var_18]
0000:004040EC mov [ebp+var_12], ax
0000:004040F0 push esi ; nDenominator
0000:004040F1 movsx eax, [ebp+var_20]
0000:004040F5 push eax ; nNumerator
0000:004040F6 movsx eax, di
0000:004040F9 push eax ; nNumber
0000:004040FA call MulDiv
0000:004040FF add ax, [ebp+var_1A]
0000:00404103 mov [ebp+var_14], ax
0000:00404107 push esi ; nDenominator
0000:00404108 movsx eax, [ebp+var_22]
0000:0040410C push eax ; nNumerator
0000:0040410D movsx eax, di
0000:00404110 push eax ; nNumber
0000:00404111 call MulDiv
0000:00404116 add ax, [ebp+var_1C]
0000:0040411A mov [ebp+var_16], ax
0000:0040411E cmp [ebp+var_12], 0FFh
0000:00404124 jle short loc_40412C
0000:00404126 mov [ebp+var_12], 0FFh//最大变化到255
0000:0040412C
0000:0040412C loc_40412C:
0000:0040412C cmp [ebp+var_14], 0FFh
0000:00404132 jle short loc_40413A
0000:00404134 mov [ebp+var_14], 0FFh
0000:0040413A
0000:0040413A loc_40413A:
0000:0040413A cmp [ebp+var_16], 0FFh
0000:00404140 jle short loc_404148
0000:00404142 mov [ebp+var_16], 0FFh
0000:00404148
0000:00404148 loc_404148:
0000:00404148 mov cl, byte ptr [ebp+var_16]
0000:0040414B mov dl, byte ptr [ebp+var_14]
0000:0040414E mov al, byte ptr [ebp+var_12]
0000:00404151 call unknown_libname_15 ; Borland Visual Component Library & Packages//来,拼一下RGB分量
0000:00404156 mov [ebp+var_40.lbColor], eax//返回的分量
0000:00404159 lea eax, [ebp+var_40]
0000:0040415C push eax ; LOGBRUSH *
0000:0040415D call CreateBrushIndirect
0000:00404162 mov [ebp+hbr], eax
0000:00404165 mov eax, [ebp+hbr]
0000:00404168 push eax ; hbr
0000:00404169 lea eax, [ebp+rc]
0000:0040416C push eax ; lprc
0000:0040416D mov eax, [ebp+hDC]
0000:00404170 push eax ; hDC
0000:00404171 call FillRect
0000:00404176 push esi ; nDenominator
0000:00404177 push esi ; nNumerator
0000:00404178 movsx eax, di
0000:0040417B push eax ; nNumber
0000:0040417C call MulDiv
0000:00404181 mov edx, [ebp+Rect.right]
0000:00404184 sub edx, [ebp+Rect.left]
0000:00404187 sub edx, eax
0000:00404189 mov [ebp+rc.left], edx
0000:0040418C push esi ; nDenominator
0000:0040418D push esi ; nNumerator
0000:0040418E movsx eax, di
0000:00404191 inc eax
0000:00404192 push eax ; nNumber
0000:00404193 call MulDiv
0000:00404198 mov edx, [ebp+Rect.right]
0000:0040419B sub edx, [ebp+Rect.left]
0000:0040419E sub edx, eax
0000:004041A0 mov [ebp+rc.right], edx
0000:004041A3 mov eax, [ebp+hbr]
0000:004041A6 push eax ; hbr
0000:004041A7 lea eax, [ebp+rc]
0000:004041AA push eax ; lprc
0000:004041AB mov eax, [ebp+hDC]
0000:004041AE push eax ; hDC
0000:004041AF call FillRect
0000:004041B4 mov eax, [ebp+hbr]
0000:004041B7 push eax ; HGDIOBJ
0000:004041B8 call DeleteObject
0000:004041BD inc edi
0000:004041BE dec [ebp+var_24]
0000:004041C2 jnz loc_4040B9//循环末尾
0000:004041C8
0000:004041C8 loc_4041C8:
0000:004041C8 mov [ebp+var_40.lbColor], 9E6A54h
0000:004041CF lea eax, [ebp+var_40]
0000:004041D2 push eax ; LOGBRUSH *
0000:004041D3 call CreateBrushIndirect
0000:004041D8 mov [ebp+hbr], eax
0000:004041DB mov eax, [ebp+hbr]
0000:004041DE push eax ; hbr
0000:004041DF lea eax, [ebp+Rect]
0000:004041E2 push eax ; lprc
0000:004041E3 mov eax, [ebp+hDC]
0000:004041E6 push eax ; hDC
0000:004041E7 call FrameRect
0000:004041EC mov eax, [ebp+hbr]
0000:004041EF push eax ; HGDIOBJ
0000:004041F0 call DeleteObject
0000:004041F5 push 0DCDCDCh ; COLORREF
0000:004041FA mov eax, [ebp+hDC]
0000:004041FD push eax ; HDC
0000:004041FE call SetTextColor
0000:00404203 push 1 ; int
0000:00404205 mov eax, [ebp+hDC]
0000:00404208 push eax ; HDC
0000:00404209 call SetBkMode
0000:0040420E mov [ebp+Rect.left], 2
0000:00404215 mov [ebp+Rect.top], 2
0000:0040421C sub [ebp+Rect.bottom], 2
0000:00404220 push offset locret_4042A4 ; LPCSTR
0000:00404225 push 0 ; DWORD
0000:00404227 push 0 ; DWORD
0000:00404229 push 0 ; DWORD
0000:0040422B push 0 ; DWORD
0000:0040422D push 1 ; DWORD
0000:0040422F push 0 ; DWORD
0000:00404231 push 0 ; DWORD
0000:00404233 push 0 ; DWORD
0000:00404235 push 2BCh ; int
0000:0040423A push 0 ; int
0000:0040423C push 0 ; int
0000:0040423E push 0 ; int
0000:00404240 push 0FFFFFFF4h ; int
0000:00404242 call CreateFontA
0000:00404247 mov ebx, eax
0000:00404249 push ebx ; HGDIOBJ
0000:0040424A mov eax, [ebp+hDC]
0000:0040424D push eax ; HDC
0000:0040424E call SelectObject
0000:00404253 cmp [ebp+hIcon], 0
0000:00404257 jz short loc_40427B
0000:00404259 push 3 ; diFlags
0000:0040425B push 0 ; hbrFlickerFreeDraw
0000:0040425D push 0 ; istepIfAniCur
0000:0040425F push 10h ; cyWidth
0000:00404261 push 10h ; cxWidth
0000:00404263 mov eax, [ebp+hIcon]
0000:00404266 push eax ; hIcon
0000:00404267 push 2 ; yTop
0000:00404269 push 2 ; xLeft
0000:0040426B mov eax, [ebp+hDC]
0000:0040426E push eax ; hdc
0000:0040426F call DrawIconEx
0000:00404274 mov [ebp+Rect.left], 14h
0000:0040427B
0000:0040427B loc_40427B:
0000:0040427B push 24h ; uFormat
0000:0040427D lea eax, [ebp+Rect]
0000:00404280 push eax ; lpRect
0000:00404281 push 0FFFFFFFFh ; nCount
0000:00404283 mov eax, [ebp+var_C]
0000:00404286 call @System@@LStrToPChar$qqrv ; System::__linkproc__ LStrToPChar(void)
0000:0040428B push eax ; lpString
0000:0040428C mov eax, [ebp+hDC]
0000:0040428F push eax ; hDC
0000:00404290 call DrawTextA
0000:00404295 push ebx ; HGDIOBJ
0000:00404296 call DeleteObject
0000:0040429B pop edi
0000:0040429C pop esi
0000:0040429D pop ebx
0000:0040429E mov esp, ebp
0000:004042A0 pop ebp
0000:004042A1 retn 0Ch
0000:004042A4
0000:004042A4 ; const CHAR locret_4042A4
0000:004042A4 locret_4042A4:
0000:004042A4 retf
0000:004042A4 sub_403FF8 endp ; sp = -60h
0000:00403C8C nullsub_1 proc near
0000:00403C8C retn
0000:00403C8C nullsub_1 endp
0000:00403C90 sub_403C90 proc near
0000:00403C90 shr eax, 8
0000:00403C93 retn
0000:00403C93 sub_403C90 endp
0000:00403C94 sub_403C94 proc near
0000:00403C94 shr eax, 10h
0000:00403C97 retn
0000:00403C97 sub_403C94 endp
0000:00403C70 ; Borland Visual Component Library & Packages
0000:00403C70 ; Attributes: library function
0000:00403C70
0000:00403C70 unknown_libname_15 proc near
0000:00403C70 and eax, 0FFh
0000:00403C75 and edx, 0FFh//取al和dl
0000:00403C7B shl edx, 8
0000:00403C7E or eax, edx
0000:00403C80 xor edx, edx
0000:00403C82 mov dl, cl
0000:00403C84 shl edx, 10h
0000:00403C87 or eax, edx//非常象是RGB的拼装,字节顺序是
//CL:DL:AL
0000:00403C89 retn
0000:00403C89 unknown_libname_15 endp
首先看看参数。这个公共的call从形式上有6个参数,前3个依次用eax,edx,ecx传,
后3个通过stack传。从代码看,call前push了12个字节,ret时也pop了12个,堆栈是平
衡的。如果有谁怀疑,不妨自己数数整个call的push和pop,可以锻炼一下。我自己没
有数,但不反对别人数。
这个call有很多局部变量和3个堆栈参数,至少形式是这样,但是IDA只分析出一个
堆栈参数。我一开始以为IDA发飚了,于是画了一下堆栈,结果发现程序没有用到第4和
第5个参数,调的时候直接传了个数的。但是看上去第4个参数好象是个RGB数。对不对,
请laoqian指正。
分析这个call不能象前面几个call一样可以直接写出框架。只有逐步来。首先写出
过程首部。
Procedure Paint(dc:HDC;Icon:hIcon;Caption:PChar;dummy:DWORD;dummyRGB:DWORD;Rect:TRect);
然后直接把ASM改成Pas,但是寄存器名和形如var_18的名称依然作为变量保留。下
面是精简变量。简单的说,就是只赋值和使用一次的变量可以去掉,其他的作为局部变
量保留。最后根据变量使用的情况,分别改用适当的名称,这样便于阅读理解。
下面就是得到的代码了:
Procedure Paint(dc:HDC;Icon:hIcon;Caption:PChar;dummy:DWORD;dummyRGB:DWORD;Rect:TRect);
然后直接把ASM改成Pas,但是寄存器名和形如var_18的名称依然作为变量保留。下
面是精简变量。简单的说,就是只赋值和使用一次的变量可以去掉,其他的作为局部变
量保留。最后根据变量使用的情况,分别改用适当的名称,这样便于阅读理解。
下面就是得到的代码了:
Procedure Paint(dc:HDC;Icon:hIcon;Caption:PChar;BeginRGB:DWORD;EndRGB:DWORD;Rect:TRect);
Var
LogBrush:TLogBrush;
LRect,rc:TRect;
LhBR:HBRUSH;
R,G,B,Ri,Gi,Bi,Rt,Gt,Bt:Word;
LFont:HFONT;
Width:Word;
i:Word;
Begin
LRect:=Rect;
B:=EndRGB AND $FF;
G:=(EndRGB SHR 8) AND $FF;
R:=(EndRGB SHR 16) AND $FF;
Bi:=BeginRGB AND $FF;
Gi:=(BeginRGB SHR 8) AND $FF;
Ri:=(BeginRGB SHR 16) AND $FF;
Width:=(LRect.right-LRect.left) DIV 2;
rc.top:=0;
rc.bottom:=LRect.bottom-LRect.top;
LogBrush.lbStyle:=0;
LogBrush.lbHatch:=0;
If Width>=0 Then
Begin
For i:=0 To (Width AND $FF) Do
Begin
rc.left:=i;
rc.right:=i+1;
Bt:=MulDiv(i,Bi,Width)+B;
Gt:=MulDiv(i,Gi,Width)+G;
Rt:=MulDiv(i,Ri,Width)+R;
If Bt>$FF Then Bt:=$FF;
If Gt>$FF Then Gt:=$FF;
If Rt>$FF Then Rt:=$FF;
LogBrush.lbColor:=(Rt SHL 16) OR (Gt SHL 8) OR (Bt);
LhBR:=CreateBrushIndirect(LogBrush);
FillRect(dc,rc,LhBR);
rc.left:=Rect.right-Rect.left-i;
rc.right:=Rect.right-Rect.left-i-1;
FillRect(dc,rc,LhBR);
DeleteObject(LhBR);
End;
End;
LogBrush.lbColor:=$9E6A54;
LhBR:=CreateBrushIndirect(LogBrush);
FrameRect(dc,Rect,LhBR);
DeleteObject(LhBR);
SetTextColor(dc,$DCDCDC);
SetBkMode(dc,1);
Rect.left:=2;
Rect.top:=2;
Dec(Rect.bottom,2);
LFont:=CreateFont(-$C,0,0,0,$2BC,0,0,0,1,0,0,0,0,'宋体');
SelectObject(dc,LFont);
If Icon<>0 Then
Begin
DrawIconEx(dc,2,2,Icon,$10,$10,0,0,3);
Rect.left:=$14;
End;
DrawText(dc,Caption,-1,Rect,$24);
DeleteObject(LFont);
End;
Shit!效果是:按钮上的字没有,标题栏的颜色有问题。标题颜色估计是对signed
int处理不当,但按钮的字就有点头痛。只能说,应该是WM_DRAWITEM的问题,但是我传
的参数肯定是对的,否则是没有那个三维按钮效果的!
按钮的问题,要么是SetColor出了问题,要么就是Get/Set文本出问题。
好,调试一下。发现问题!GetWindowText得到的Str是空的!看来还是String的
转换存在问题。把Str定义为Array of Byte,成功!
下面就是改正的ItemDraw:
Procedure ItemDraw(lpDIS:PDrawItemStruct);
Var
Str:Array[1..11] of Byte;
Begin
FillRect(lpDIS^.hDC,lpDIS^.rcItem,h_Brush);
SetTextColor(lpDIS^.hDC,$A0A0A0);
SetBkMode(lpDIS^.hDC,TRANSPARENT);
DrawEdge(lpDIS^.hDC,lpDIS^.rcItem,BDR_RAISEDOUTER,BF_RECT);
GetWindowText(lpDIS^.hwndItem,@Str,10);
DrawText(lpDIS^.hDC,@Str,-1,lpDIS^.rcItem,DT_SINGLELINE OR DT_VCENTER OR DT_CENTER);
If lpDIS^.itemState MOD 2=1 Then
Begin
SetTextColor(lpDIS^.hDC,$DDFF);
DrawText(lpDIS^.hDC,@Str,-1,lpDIS^.rcItem,DT_SINGLELINE OR DT_VCENTER OR DT_CENTER);
DrawEdge(lpDIS^.hDC,lpDIS^.rcItem,BDR_SUNKENOUTER,BF_RECT);
End;
End;
那么余下的问题就是颜色了。这个,负数的处理恰好是我头痛的问题。下次再说
吧……另外,为什么必须用Array of Byte(上面刚刚提到的嘛),这个也下次分析
了……
当然,不要指望上面的代码和原始代码一样(哪怕是99%的相似),因为我是做了
点优化的,去掉了从逻辑上无用的代码。
OK,我很想知道这么一大篇东西有没有人会真的有耐心看完,特别是对dump出的代
码的分析,而不仅仅是Delphi的代码。我在上面的分析里提到以前的一个判断被证明是
错误的。知道我在说什么的人有兴趣的话请在FCG论坛发短信告诉我,ID嘛,一样的。 ====================================================
补充:
For i:=0 To (Width AND $FF) Do
Begin
----> rc.left:=1;
rc.right:=i+1;
箭头指的地方,1应该改为i。这实际是我的笔误。改了就对了。
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)