• 标 题:Delphi逆向工程笔记[5]
  • 作 者:firstrose
  • 时 间:004-11-11,22:24
  • 链 接:http://bbs.pediy.com

  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嘛,一样的。

  • 标 题:Delphi逆向工程笔记5(续)——关于String局部变量
  • 作 者:firstrose
  • 时 间:004-11-15,00:34
  • 链 接:http://bbs.pediy.com

没想到居然写了这么多……写完后打算去blogcn抒发一下高兴的心情,却发现blogcn被黑了……不过那个Hacker算是手下留情的,把主页备份了。

=================================================================================

  为什么我在ItemDraw里用了String后没有达到预想的效果呢?从反汇编的结果
看,问题有点复杂。按道理说,过程/函数的局部简单变量是放在堆栈里的,但是
这里似乎有点例外情况。还是应该跟踪一下。
  用od跟,首先找到GetWindowText的调用,前面有个push 0ah,下个HWBP,运
行。下面是IDA在那里的disasm结果:

CODE:00013D06                 mov     eax, [ebp+var_4]
CODE:00013D09                 call    sub_12DA4
//这里是检查eax是否为0,如是,则eax就被设为var_4的地址
CODE:00013D0E                 mov     esi, eax
CODE:00013D10                 push    esi             ; lpString
CODE:00013D11                 mov     eax, [ebx+14h]
CODE:00013D14                 push    eax             ; hWnd
CODE:00013D15                 call    GetWindowTextA

  继续,在00013D0E处,eax=12da9,但是这好象是CODE段。run。Exception!
这时程序中断在User32里。很明显,Delphi在处理String时有点小小的例外。那么,
来分析一下。

  一、PChar

  PChar是Delphi为和C兼容而引入的数据类型,对应于char*。对程序做如下改动:

Procedure ItemDraw(lpDIS:PDrawItemStruct);
Var
  Str:PChar;
Begin
  ...
  GetWindowText(lpDIS^.hwndItem,Str,10);

  运行结果是,按钮文字乱码,od报告Exception。反汇编结果如下:

CODE:00013CE5                 push    0Ah             ; nMaxCount
CODE:00013CE7                 push    esi             ; lpString
CODE:00013CE8                 mov     eax, [ebx+14h]
CODE:00013CEB                 push    eax             ; hWnd
CODE:00013CEC                 call    GetWindowTextA

  前面没有给esi赋值的地方,这也是意料之中。跟踪发现,esi=00013FB8,
指向LicenseProc。那么,看到的乱码就是LicenseProc的开始部分。大概是
User32里有SEH,直接运行程序没有Exception。
  注意,Str没有在堆栈里分配,哪怕是个指针。但是这个可以理解。
  改改代码:

Procedure ItemDraw(lpDIS:PDrawItemStruct);
Var
  Str:PChar;
Begin
  ...
  GetWindowText(lpDIS^.hwndItem,@Str,10);

  可以正常运行。反汇编:

CODE:00013CA4 String          = byte ptr -4
CODE:00013CA4
CODE:00013CA4                 push    ebx
CODE:00013CA5                 push    ecx
CODE:00013CA6                 mov     ebx, eax
CODE:00013CA8                 mov     eax, ds:hbr
...
CODE:00013CE5                 push    0Ah             ; nMaxCount
CODE:00013CE7                 lea     eax, [esp+8+String]
CODE:00013CEB                 push    eax             ; lpString
CODE:00013CEC                 mov     eax, [ebx+14h]
CODE:00013CEF                 push    eax             ; hWnd
CODE:00013CF0                 call    GetWindowTextA
...
CODE:00013D4B                 pop     edx
CODE:00013D4C                 pop     ebx
CODE:00013D4D                 retn


  有一个局部变量!跟踪到00013CA4,发现esi指向LicenseProc,esp=0013FA7C。
到00013CA6时,esp=0013FA74,没问题。到00013CE7,esp=0013FA70。向下,
eax=0013FA74,指向push的ecx。好了,得到的字符串覆盖了堆栈里的ecx和ebx。
那么,“正常运行”的结果应该是一个巧合。但是这个地方非常有趣,ebx当然要保
存,但是ecx完全不必保存的。这从过程的首尾可以看到。怎么回事呢?很简单,
PChar只是一个Point,push ecx就是分配空间。但是ebx呢?Delphi的HLP里明确说明
必须保护ebx。很巧,程序是SDK写的,没有用VCL。所以ebx坏了没事,而且最长的文
字才6字节。否则肯定出问题了。

  玩个花样:

Var
  Str:PChar;
Begin
  Str:='123';
  ...
  GetWindowText(lpDIS^.hwndItem,Str,10);

  按钮的字全部是123。很容易就想到,123是常量,很可能放在CODE里。异常,写
不进。反汇编也证实了这点:

CODE:00013CA4 sub_13CA4       proc near
CODE:00013CA4                 push    ebx
CODE:00013CA5                 push    esi
CODE:00013CA6                 mov     ebx, eax
CODE:00013CA8                 mov     esi, offset loc_13D48
...
CODE:00013CEA                 push    0Ah             ; nMaxCount
CODE:00013CEC                 push    esi             ; lpString
CODE:00013CED                 mov     eax, [ebx+14h]
CODE:00013CF0                 push    eax             ; hWnd
CODE:00013CF1                 call    GetWindowTextA
...
CODE:00013D46                 retn
CODE:00013D46 sub_13CA4       endp
CODE:00013D46
CODE:00013D47                 align 4
CODE:00013D48
CODE:00013D48 loc_13D48:
CODE:00013D48                 xor     [edx], esi
CODE:00013D4A                 xor     eax, [eax]

  但是用Aspack2.12加壳后,运行程序,可以正常显示文字。很明显,代码段可
写。这个结果很有趣,在加密上会有用的。顺便说一句,此时由于部分代码被破坏,
窗口重画时就异常退出了。

  二、String

  1、直接用String

Var
  Str:String;
Begin
  GetWindowText(lpDIS^.hwndItem,PChar(Str),10);

  没有文字。

CODE:00013CB0 sub_13CB0       proc near
CODE:00013CB0
CODE:00013CB0 var_4           = dword ptr -4
CODE:00013CB0
CODE:00013CB0                 push    ebp
CODE:00013CB1                 mov     ebp, esp
CODE:00013CB3                 push    0
CODE:00013CB5                 push    ebx
CODE:00013CB6                 push    esi
CODE:00013CB7                 mov     ebx, eax
CODE:00013CB9                 xor     eax, eax
CODE:00013CBB                 push    ebp
CODE:00013CBC                 push    offset loc_13D7E
CODE:00013CC1                 push    dword ptr fs:[eax]
CODE:00013CC4                 mov     fs:[eax], esp
...
CODE:00013D04                 push    0Ah             ; nMaxCount
CODE:00013D06                 mov     eax, [ebp+var_4]
CODE:00013D09                 call    sub_12DA4
CODE:00013D0E                 mov     esi, eax
CODE:00013D10                 push    esi             ; lpString
CODE:00013D11                 mov     eax, [ebx+14h]
CODE:00013D14                 push    eax             ; hWnd
CODE:00013D15                 call    GetWindowTextA
...
CODE:00013D68
CODE:00013D68 loc_13D68:
CODE:00013D68                 xor     eax, eax
CODE:00013D6A                 pop     edx
CODE:00013D6B                 pop     ecx
CODE:00013D6C                 pop     ecx
CODE:00013D6D                 mov     fs:[eax], edx
CODE:00013D70                 push    offset loc_13D85
CODE:00013D75
CODE:00013D75 loc_13D75:
CODE:00013D75                 lea     eax, [ebp+var_4]
CODE:00013D78                 call    sub_12D80
CODE:00013D7D                 retn
CODE:00013D7D sub_13CB0       endp ; sp = -14h

CODE:00012DA4 sub_12DA4       proc near
CODE:00012DA4                 test    eax, eax
CODE:00012DA6                 jz      short loc_12DAA
CODE:00012DA8                 retn
CODE:00012DA9 byte_12DA9      db 0
CODE:00012DAA
CODE:00012DAA loc_12DAA:
CODE:00012DAA                 mov     eax, offset byte_12DA9
CODE:00012DAF                 retn
CODE:00012DAF sub_12DA4       endp

  00013CB0处,esp=0013FA7C。00013D06处,eax=0。那么,看来局部的String是
分配一个指针,和PChar一样。这是在{H+}条件下的结果。注意,这里用了SEH。加
上Str:='123';一句,结果和PChar的情况一样。但是这里有个额外的call,上面没有
列出。

  改一下:

Var
  Str:String;
Begin
  ...
  GetWindowText(lpDIS^.hwndItem,@Str,10);

  程序异常了。经过跟踪可以得知,异常点在12D8C:

CODE:00012D80 sub_12D80       proc near
CODE:00012D80                 mov     edx, [eax]
CODE:00012D82                 test    edx, edx
CODE:00012D84                 jz      short locret_12DA2
CODE:00012D86                 mov     dword ptr [eax], 0
CODE:00012D8C                 mov     ecx, [edx-8]
CODE:00012D8F                 dec     ecx
CODE:00012D90                 jl      short locret_12DA2
CODE:00012D92                 lock dec dword ptr [edx-8]
CODE:00012D96                 jnz     short locret_12DA2
CODE:00012D98                 push    eax
CODE:00012D99                 lea     eax, [edx-8]
CODE:00012D9C                 call    sub_1249C
CODE:00012DA1                 pop     eax
CODE:00012DA2
CODE:00012DA2 locret_12DA2:
CODE:00012DA2                 retn
CODE:00012DA2 sub_12D80       endp

  说明一下,00012D80处,eax就指向堆栈里的那个指针。那么,edx应该就是
String的实际地址了,而edx-8很可能是一个引用计数。很明显,对@Str的写入破坏
了指针(正常应该是0)。

  但是这里有个情况,如果对Str初始化一下

Var
  Str:String;
Begin
  Str:='1423423';

  那么,如果使用PChar(Str),按钮的文字显示为1423423。而使用@Str[1]则正
常。
  用PChar转换时,部分反汇编结果如下

CODE:00013CDC sub_13CDC       proc near
CODE:00013CDC
CODE:00013CDC var_4           = dword ptr -4
CODE:00013CDC
...
CODE:00013CF3                 lea     eax, [ebp+var_4]
CODE:00013CF6                 mov     edx, offset a1423423 ; "1423423"
CODE:00013CFB                 call    sub_12DA4

  执行完sub_12DA4后,var_4被放上了位于CODE里的a1423423的地址。

  而用后一种形式时,'1423423'仍然在CODE里,但是在00013DA9的call以后,eax
变了!

CODE:00013DA4                 push    0Ah             ; nMaxCount
CODE:00013DA6                 lea     eax, [ebp+var_4]
CODE:00013DA9                 call    sub_12E0C
CODE:00013DAE                 push    eax             ; lpString
CODE:00013DAF                 mov     eax, [ebx+14h]
CODE:00013DB2                 push    eax             ; hWnd
CODE:00013DB3                 call    GetWindowTextA

  看来是新分配了一段空间。应该是堆空间。

  2、用String[xx]形式:

Var
  Str:String[20];
Begin
  ...
  GetWindowText(lpDIS^.hwndItem,@Str[1],10);

  正常运行。这时局部变量已经变了

CODE:00013CA4 sub_13CA4       proc near
CODE:00013CA4
CODE:00013CA4 String          = byte ptr -17h
CODE:00013CA4
CODE:00013CA4                 push    ebx
CODE:00013CA5                 add     esp, 0FFFFFFE8h
...
CODE:00013CE7                 push    0Ah             ; nMaxCount
CODE:00013CE9                 lea     eax, [esp+1Ch+String]
CODE:00013CED                 push    eax             ; lpString
CODE:00013CEE                 mov     eax, [ebx+14h]
CODE:00013CF1                 push    eax             ; hWnd
CODE:00013CF2                 call    GetWindowTextA
...
CODE:00013D4D
CODE:00013D4D loc_13D4D:
CODE:00013D4D                 add     esp, 18h
CODE:00013D50                 pop     ebx
CODE:00013D51                 retn
CODE:00013D51 sub_13CA4       endp

  直接看分析代码可以看到,对于20字节长的ShortStr,分配了24个堆栈字节。加
一下偏移就看得到,长度标志还是1字节。4个字节的差别是为了对齐。

  结论是:在{H+}条件下,对于String[xx]形式,就是ObjectPascal的String,直
接在堆栈分配。PChar是一个放在堆栈里的指针,内容视情况而定。String则是在堆
栈里分配一个指针,指向一个结构体。限于时间,我没有对这个结构体做进一步探
索。但是,如果用PChar转换,则指针一般是指向CODE的。
  那么,为什么开始的程序没有出问题也就很明白了。也是巧合,我用了PChar强
制转换,所以指针实际是指向CODE的。由于User32很可能有SEH什么的,结果是什么
都显示不出。加上对Message不熟悉,这样我以为按钮的文字可能在WM_PAINT里,否
则肯定早就发现原因了。

================================================================

如果你看到这里觉得我没有讲什么新东西,别打我……