- 静态方法与类字段
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有关的内容。