李沉舟 发表于 2018-7-15 16:51

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指针。

寄存器说明
EAXSelf
EDXsString
ECXnAge

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   , edx
CODE:0044DFC3               mov   ebx, eax      ; EBX - > Self
CODE:0044DFC5               mov   eax,
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:
CODE:0044DFD8               mov   fs:, esp
CODE:0044DFDB               lea   eax,     ; EAX - > Self + 4
CODE:0044DFDE               mov   edx, ; EDX - > sName
CODE:0044DFE1               call    @System@@LStrAsg$qqrpvpxv ; 执行字符串拷贝工作
CODE:0044DFE6               mov   , esi    ; = nAge
CODE:0044DFE9               xor   eax, eax
CODE:0044DFEB               pop   edx
CODE:0044DFEC               pop   ecx
CODE:0044DFED               pop   ecx
CODE:0044DFEE               mov   fs:, edx

此时,Worker的内存布局如下。
00943DB09C DF 44 00 C8 3D 94 00 37 00 00 00            溸D.??7.....


0x943DC8指向字符串,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的内存构造如下。
00953D8C48 27 45 00                                    H'E.

0x452748的内存如下。
0045274854 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,
CODE:00452843               call    dword ptr
CODE:00452845               mov   eax, ebx
CODE:00452847               mov   edx,
CODE:00452849               call    dword ptr
CODE:0045284C               mov   eax, ebx
CODE:0045284E               mov   edx,
CODE:00452850               call    dword ptr
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的内存构造如下。
00953D8CA0 27 45 00                                    ?E..

0x4527A0的内存如下。
004527A0B4 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(),就转给另一个库函数了。该库函数的反汇编代码如下。

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,
CODE:0040366E
CODE:0040366E loc_40366E:                           ; CODE XREF: unknown_libname_28+2j
CODE:0040366E               mov   edi,
CODE:00403671               test    edi, edi
CODE:00403673               jz      short loc_403682
CODE:00403675               movzx   ecx, word ptr
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,
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,
CODE:00403694               pop   edi
CODE:00403695               retn
CODE:00403695 unknown_libname_28 endp

代码我没有标注释,简单解释一下意思吧。首先,从处,得到动态方法的表起始地址。
本例中的动态方法表如下。
0045274801 00 FF FF 58 27 45 00                        .

李沉舟 发表于 2018-7-15 16:52

怎么帖子断了。补发在后面。

李沉舟 发表于 2018-7-15 16:53

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

0045274801 00 FF FF 58 27 45 00                        .

李沉舟 发表于 2018-7-15 16:54

只是取出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有关的内容。

页: [1]
查看完整版本: Delphi的面向对象