Delphi面向对象编程  
官方Delphi 学习QQ群: 682628230(三千人)
频道

Delphi面向对象编程


Delphi面向对象编程


静态方法与类字段


implementation


{$R *.dfm}

type


  TWorker = class(TObject)

  public

    Name: string;

    Age: Cardinal;

    procedure SetWorker(sName: string; nAge: Cardinal);

  end;


procedure TWorker.SetWorker(sName: string; nAge: Cardinal);

begin

  Name := sName;

  Age := nAge;

end;


procedure TForm1.Button1Click(Sender: TObject);

var

  Worker: TWorker;

begin

  Worker := TWorker.Create;

  Worker.SetWorker('Marx', 55);

end;


 


反汇编代码如下。


CODE:0044E00C _TForm1_Button1Click proc near          ; DATA XREF: CODE:0044DF10o

CODE:0044E00C                 mov     dl, 1

CODE:0044E00E                 mov     eax, off_44DF50

CODE:0044E013                 call    unknown_libname_26 ; BDS 2005-2007 and Delphi6-7 Visual Component Library ;TWorker.Create

CODE:0044E018                 mov     ecx, 37h ;nAge

CODE:0044E01D                 mov     edx, offset _str_Marx.Text ;sString

CODE:0044E022                 call    sub_44DFB8 ;Worker.SetWorker

CODE:0044E027                 retn

CODE:0044E027 _TForm1_Button1Click endp


 


调用TWorker.Create后,成功返回一个对象的实例,接着,按照fastcall调用约定,调用了Worker.SetWorker方法。


此处实际隐含了一个Self指针,即C++里的this指针。


寄存器 说明

EAX Self

EDX sString

ECX nAge

 


Work.SetWorker的反汇编代码如下。



CODE:0044DFB8 sub_44DFB8      proc near               ; CODE XREF: _TForm1_Button1Click+16p

CODE:0044DFB8

CODE:0044DFB8 var_4           = dword ptr -4

CODE:0044DFB8

CODE:0044DFB8                 push    ebp

CODE:0044DFB9                 mov     ebp, esp

CODE:0044DFBB                 push    ecx

CODE:0044DFBC                 push    ebx

CODE:0044DFBD                 push    esi

CODE:0044DFBE                 mov     esi, ecx

CODE:0044DFC0                 mov     [ebp+var_4], edx

CODE:0044DFC3                 mov     ebx, eax        ; EBX - > Self

CODE:0044DFC5                 mov     eax, [ebp+var_4]

CODE:0044DFC8                 call    @System@@LStrAddRef$qqrpv ; System::__linkproc__ LStrAddRef(void *)

CODE:0044DFCD                 xor     eax, eax

CODE:0044DFCF                 push    ebp

CODE:0044DFD0                 push    offset loc_44DFFF

CODE:0044DFD5                 push    dword ptr fs:[eax]

CODE:0044DFD8                 mov     fs:[eax], esp

CODE:0044DFDB                 lea     eax, [ebx+4]    ; EAX - > Self + 4

CODE:0044DFDE                 mov     edx, [ebp+var_4] ; EDX - > sName

CODE:0044DFE1                 call    @System@@LStrAsg$qqrpvpxv ; 执行字符串拷贝工作

CODE:0044DFE6                 mov     [ebx+8], esi    ; [Self + 8] = nAge

CODE:0044DFE9                 xor     eax, eax

CODE:0044DFEB                 pop     edx

CODE:0044DFEC                 pop     ecx

CODE:0044DFED                 pop     ecx

CODE:0044DFEE                 mov     fs:[eax], edx



此时,Worker的内存布局如下。


00943DB0  9C DF 44 00 C8 3D 94 00 37 00 00 00              溸D.??7.....


 


0x943DC8指向[Marx]字符串,0x37(55)即nAge。


类的继承与虚方法


implementation


{$R *.dfm}

type

  TClassA = class(TObject)

  public

    procedure GetName(); virtual;

  end;


procedure TClassA.GetName();

begin

  ShowMessage('My Name is "TClassA"');

end;


procedure TForm1.Button1Click(Sender: TObject);

var

  ClassA: TClassA;

begin

  classA := TClassA.Create;

  ClassA.GetName;

  ClassA.Free;

end;


 


反汇编代码如下。


此时ClassA的内存构造如下。

00953D8C  48 27 45 00                                      H'E.

0x452748的内存如下。

00452748  54 27 45 00                                      T'E.

0x452754,即虚方法地址。


反汇编代码如下。


00452754   .  B8 68274500   mov eax,Project1.00452768

00452759   .  E8 2E8DFDFF   call Project1.0042B48C

0045275E   .  C3            retn

 


implementation

{$R *.dfm}

type

  TClassA = class(TObject)

  public

    procedure GetName(); virtual;

  end;

  TClassB = class(TClassA)

  public

    procedure GetAge(); virtual;

    procedure GetInfor(); virtual;

  end;


procedure TClassA.GetName();

begin

  ShowMessage('My Name is "TClassA"');

end;


procedure TClassB.GetAge();

begin

  ShowMessage('I am 16');

end;


procedure TClassB.GetInfor();

begin

  ShowMessage('I am a senior school teacher');

end;


procedure TForm1.Button1Click(Sender: TObject);

var

  ClassB: TClassB;

begin

  ClassB := TClassB.Create;

  ClassB.GetName();

  ClassB.GetAge();

  ClassB.GetInfor();

  ClassB.Free;

end;


 


反汇编代码如下。


复制代码

CODE:00452830 _TForm1_Button1Click proc near          ; DATA XREF: CODE:004526BCo

CODE:00452830                 push    ebx

CODE:00452831                 mov     dl, 1

CODE:00452833                 mov     eax, off_452754

CODE:00452838                 call    unknown_libname_26 ; BDS 2005-2007 and Delphi6-7 Visual Component Library

CODE:0045283D                 mov     ebx, eax

CODE:0045283F                 mov     eax, ebx

CODE:00452841                 mov     edx, [eax]

CODE:00452843                 call    dword ptr [edx]

CODE:00452845                 mov     eax, ebx

CODE:00452847                 mov     edx, [eax]

CODE:00452849                 call    dword ptr [edx+4]

CODE:0045284C                 mov     eax, ebx

CODE:0045284E                 mov     edx, [eax]

CODE:00452850                 call    dword ptr [edx+8]

CODE:00452853                 mov     eax, ebx        ; this

CODE:00452855                 call    @System@TObject@Free$qqrv ; System::TObject::Free(void)

CODE:0045285A                 pop     ebx

CODE:0045285B                 retn

CODE:0045285B _TForm1_Button1Click endp



此时ClassB的内存构造如下。

00953D8C  A0 27 45 00                                      ?E..

0x4527A0的内存如下。

004527A0  B4 27 45 00 E0 27 45 00 FC 27 45 00              ?E.?E.?E.TC

0x4527B4、0x4527E0、0x4527FC。这是虚方法表,分别对应了3个虚方法。其中第一个是从父类TClassA里继承下来的。


 

类的动态方法


implementation


{$R *.dfm}

type

  TClassA = class(TObject)

  public

    procedure GetName(); dynamic;

  end;


procedure TClassA.GetName();

begin

  ShowMessage('I am Mike');

end;


procedure TForm1.Button1Click(Sender: TObject);

var

  ClassA: TClassA;

begin

  ClassA := TClassA.Create;

  ClassA.GetName();

  ClassA.Free;

end;


反汇编代码如下。


CODE:00452778 _TForm1_Button1Click proc near          ; DATA XREF: CODE:004526BCo

CODE:00452778                 push    ebx

CODE:00452779                 push    esi

CODE:0045277A                 mov     dl, 1

CODE:0045277C                 mov     eax, off_4526FC

CODE:00452781                 call    unknown_libname_26 ; BDS 2005-2007 and Delphi6-7 Visual Component Library

CODE:00452786                 mov     ebx, eax

CODE:00452788                 mov     eax, ebx

CODE:0045278A                 mov     si, 0FFFFh

CODE:0045278E                 call    @System@@CallDynaInst$qqrv ; System::__linkproc__ CallDynaInst(void)

CODE:00452793                 mov     eax, ebx        ; this

CODE:00452795                 call    @System@TObject@Free$qqrv ; System::TObject::Free(void)

CODE:0045279A                 pop     esi

CODE:0045279B                 pop     ebx

CODE:0045279C                 retn

CODE:0045279C _TForm1_Button1Click endp



与虚方法的调用不同,动态方法使用索引调用,而且是通过CallDynaInst这个库函数实现。


而该库函数只是简单的从Self指针里取出了VMT([Self + 0]),就转给另一个库函数了。该库函数的反汇编代码如下。


CODE:00403668 unknown_libname_28 proc near            ; CODE XREF: System::__linkproc__ CallDynaInst(void)+4p

CODE:00403668                                         ; System::__linkproc__ FindDynaInst(void)+5p ...

CODE:00403668                 push    edi

CODE:00403669                 xchg    eax, esi

CODE:0040366A                 jmp     short loc_40366E

CODE:0040366C ; ---------------------------------------------------------------------------

CODE:0040366C

CODE:0040366C loc_40366C:                             ; CODE XREF: unknown_libname_28+1Fj

CODE:0040366C                 mov     esi, [esi]

CODE:0040366E

CODE:0040366E loc_40366E:                             ; CODE XREF: unknown_libname_28+2j

CODE:0040366E                 mov     edi, [esi-30h]

CODE:00403671                 test    edi, edi

CODE:00403673                 jz      short loc_403682

CODE:00403675                 movzx   ecx, word ptr [edi]

CODE:00403678                 push    ecx

CODE:00403679                 add     edi, 2

CODE:0040367C                 repne scasw

CODE:0040367F                 jz      short loc_40368B

CODE:00403681                 pop     ecx

CODE:00403682

CODE:00403682 loc_403682:                             ; CODE XREF: unknown_libname_28+Bj

CODE:00403682                 mov     esi, [esi-24h]

CODE:00403685                 test    esi, esi

CODE:00403687                 jnz     short loc_40366C

CODE:00403689                 pop     edi

CODE:0040368A                 retn

CODE:0040368B ; ---------------------------------------------------------------------------

CODE:0040368B

CODE:0040368B loc_40368B:                             ; CODE XREF: unknown_libname_28+17j

CODE:0040368B                 pop     eax

CODE:0040368C                 add     eax, eax

CODE:0040368E                 sub     eax, ecx

CODE:00403690                 mov     esi, [edi+eax*2-4]

CODE:00403694                 pop     edi

CODE:00403695                 retn

CODE:00403695 unknown_libname_28 endp


代码我没有标注释,简单解释一下意思吧。首先,从[VMT– 0x30]处,得到动态方法的表起始地址。


本例中的动态方法表如下。


00452748  01 00 FF FF 58 27 45 00                          .X'E.



偏移 长度 说明

0x0 2 动态方法数,本例中为1

0x2 2 第一个动态方法的索引序号

0x4 4 第一个动态方法的地址

 


然后调用串操作指令对该动态方法表进行扫描,本例中ClassA.GetName动态方法的索引序号为0xFFFF。


is


procedure TForm1.Edit1MouseDown(Sender: TObject; Button: TMouseButton;

  Shift: TShiftState; X, Y: Integer);

begin

  if Button = mbLeft then

    (Sender as TEdit).BeginDrag(False);

end;


procedure TForm1.Memo1DragOver(Sender, Source: TObject; X, Y: Integer;

  State: TDragState; var Accept: Boolean);

begin

  if (Source is TEdit) then

    Accept := True;

end;


procedure TForm1.Memo1DragDrop(Sender, Source: TObject; X, Y: Integer);

begin

  if (Sender is TMemo) and (Source is TEdit) then

    Memo1.Lines.Add((Source as TEdit).Text);

end;



这是拖放事件的一个小应用。


用户将Edit拖动到Memo上时,则Edit里的内容会被添加到Memo中。


反汇编代码如下。



CODE:0044FCF0 _TForm1_Memo1DragOver proc near         ; DATA XREF: CODE:0044FC6Fo

CODE:0044FCF0

CODE:0044FCF0 arg_0           = dword ptr  8

CODE:0044FCF0

CODE:0044FCF0                 push    ebp

CODE:0044FCF1                 mov     ebp, esp

CODE:0044FCF3                 push    ebx

CODE:0044FCF4                 mov     ebx, ecx

CODE:0044FCF6                 mov     eax, ebx

CODE:0044FCF8                 mov     edx, off_425848

CODE:0044FCFE                 call    @System@@IsClass$qqrp14System@TObjectp17System@TMetaClass ; System::__linkproc__ IsClass(System::TObject *,System::TMetaClass *)

CODE:0044FD03                 test    al, al

CODE:0044FD05                 jz      short loc_44FD0D

CODE:0044FD07                 mov     eax, [ebp+arg_0]

CODE:0044FD0A                 mov     byte ptr [eax], 1

CODE:0044FD0D

CODE:0044FD0D loc_44FD0D:                             ; CODE XREF: _TForm1_Memo1DragOver+15j

CODE:0044FD0D                 pop     ebx

CODE:0044FD0E                 pop     ebp

CODE:0044FD0F                 retn    10h

CODE:0044FD0F _TForm1_Memo1DragOver endp


 



CODE:00403628                 test    eax, eax

CODE:0040362A                 jz      short locret_403642

CODE:0040362C                 mov     ecx, eax

CODE:0040362E

CODE:0040362E loc_40362E:                             ; CODE XREF: System::__linkproc__ AsClass(System::TObject *,System::TMetaClass *)+11j

CODE:0040362E                 mov     ecx, [ecx]

CODE:00403630                 cmp     ecx, edx

CODE:00403632                 jz      short locret_403642

CODE:00403634                 mov     ecx, [ecx-24h]

CODE:00403637                 test    ecx, ecx

CODE:00403639                 jnz     short loc_40362E

CODE:0040363B                 mov     al, 0Ah

CODE:0040363D                 jmp     @System@Error$qqr20System@TRuntimeError ; System::Error(System::TRuntimeError)

CODE:00403642 ; ---------------------------------------------------------------------------

CODE:00403642

CODE:00403642 locret_403642:                          ; CODE XREF: System::__linkproc__ AsClass(System::TObject *,System::TMetaClass *)+2j

CODE:00403642                                         ; System::__linkproc__ AsClass(System::TObject *,System::TMetaClass *)+Aj

CODE:00403642                 retn

复制代码

只是取出Source的VMT进行比对。


虚表HOOK


implementation


{$R *.dfm}


type

  TClassA = class(TObject)

  public

    procedure GetName(); virtual;

  end;


procedure TClassA.GetName();

begin

  ShowMessage('我是ClassA');

end;


procedure MyGetName(Self: Cardinal);

begin

  ShowMessage('我才不是什么ClassA');

end;


procedure TForm1.Button1Click(Sender: TObject);

var

  ClassA: TClassA;

  pVMT: Pointer;

  dwFlag: Cardinal;

begin

  ClassA := TClassA.Create;

  ClassA.GetName(); //第一次正常调用

  //开始HOOK

  pVMT := Pointer(PCardinal(ClassA)^); //得到VMT

  VirtualProtect(pVMT, 4, PAGE_READWRITE, dwFlag); //修改VMT的内存属性为可写

  PCardinal(pVMT)^ := Cardinal(@MyGetName); //替换

  VirtualProtect(pVMT, 4, dwFlag, dwFlag); //还原VMT的内存属性

  ClassA.GetName(); //第二次非正常调用

  ClassA.Free;

end;

 


只要得到VMT,并对其中的虚方法表进行替换就可以了。前提必须知道要HOOK的虚方法的索引位置。



结尾

本文权当抛砖引玉,如果想要更深入研究,请阅读Delphi帮助文件中和RTTI有关的内容。


https://www.cnblogs.com/galano/p/9314039.html


推荐分享
图文皆来源于网络,内容仅做公益性分享,版权归原作者所有,如有侵权请告知删除!
 

Copyright © 2014 DelphiW.com 开发 源码 文档 技巧 All Rights Reserved
晋ICP备14006235号-8 晋公网安备 14108102000087号

执行时间: 0.1441969871521 seconds