读书人

关于VMT地址,该如何处理

发布时间: 2012-03-15 11:50:39 作者: rapoo

关于VMT地址

Delphi(Pascal) code
unit Unit1;interfaceuses  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,  Dialogs, Contnrs;type  TForm1 = class(TForm)    procedure FormCreate(Sender: TObject);  private    { Private declarations }    S: TLIst;  public    { Public declarations }  end;var  Form1: TForm1;implementation{$R *.dfm}procedure TForm1.FormCreate(Sender: TObject);begin//此处下断点  S := TList.createend;end.


调试时的反汇编代码:
Unit1.pas.27: begin
004552B0 53 push ebx
004552B1 8BD8 mov ebx,eax
Unit1.pas.28: S := TList.create
004552B3 B201 mov dl,$01
004552B5 A17C1D4100 mov eax,[$00411d7c]
004552BA E811E7FAFF call TObject.Create
004552BF 898360030000 mov [ebx+$00000360],eax
Unit1.pas.29: end;
004552C5 5B pop ebx
004552C6 C3 ret
004552C7 90 nop

请问红色部分的地址是肿么得到的?
(我跟进TObject.Create,发现eax、edx实际上是System.pas的_ClassCreate的第一、二个参数)

[解决办法]
编译器统筹规划安排的。
[解决办法]
Delphi(Pascal) code
procedure TForm1.FormCreate(Sender: TObject);begin  ShowMessage(IntToHex(Cardinal(TList), 8));  TList.Create;end;
[解决办法]
mov到eax的其实是指向Tlist类的指针地址,为 Integer(PPointer(S)^) + vmtSelfPtr, 它的值为 PInteger(Integer(PPointer(S)^) + vmtSelfPtr)^ 也就是 Integer( TList),Tlist的入口地址
[解决办法]
编译器在编译完TList时,地址就已经确定了,就好比你调用函数,函数已经被编译了,地址也就确定了.
不过这个函数是有区别的
[解决办法]
BASM中,不能直接得到类地址(TList),如MOV EAX , TList,当然了LEA指令也是不行的,在BASM中,类地址没被编译进去(类地址直接编译成了[00000000].不知道是否有编译开关可以打开

可以变相引用
如:
Delphi(Pascal) code
Const  PPList : Pointer = TList;procedure TForm1.FormCreate(Sender: TObject);var  n : integer;begin  Memo1.Lines.Add(IntToHex(Integer(TList) , 8));  asm    PUSH PPList;    POP  n;  end;  Memo1.Lines.Add(IntToHex(n , 8));end;
[解决办法]
每个类在编译的时候VMT地址就是确定的了.所以mov eax,[$00411d7c]里面写死的常量.
另外如果是DLL的话如果被加载地址不是ImageBase的预定地址,操作系统的加载器会根据DLL的重定位节中所指的地址进行纠偏.就是把这个常量地址+(实际加载地址-ImageBase地址).
一般这种写死的常量地址编译器都会把它放在重定位节中.

[解决办法]
一个类实例的生成需要经过对象内存分配、内存初始化、设置对象执行框架三个步骤。
编译器首先调用 System._ClassCreate 进行对象内存分配、内存初始化的工作。而 System._ClassCreate 调用 TObject 类的虚方法 NewInstance 建立对象的实例空间,继承类通常不需要重载 TObject.NewInstance,除非你使用自己的内存管理器,因此缺省是调用 TObject.NewInstance。TObject.NewInstance 方法将根据编译器在类信息数据中初始化的对象实例尺寸(TObject.InstanceSize),调用系统缺省的 MemoryManager.GetMem 过程为该对象在堆(Heap)中分配内存,然后调用 TObject.InitInstance 方法将分配的空间初始化。InitInstance 方法首先将对象空间的头4个字节初始化为指向对象类的 VMT 的指针,然后将其余的空间清零。如果类中还设计了接口,它还要初始化接口表格(Interface Table)。
当对象实例在内存中分配且初始化后,开始设置执行框架。所谓设置执行框架就是执行你在 Create 方法里真正写的代码。设置执行框架的规矩是先设置基类的框架,然后再设置继承类的,通常用 Inherited 关键字来实现。
上述工作都做完后,编译器还要调用 System._AfterConstruction 让你有最后一次机会进行一些事务的处理工作。System._AfterConstruction 是调用虚方法 AfterConstruction 实现的。 在 TObject 中 AfterConstruction 中只是个 Place Holder,你很少需要重载这个方法,重载这个方法通常只是为了与 C++ Builder 对象模型兼容。
最后,编译器返回对象实例数据的地址指针。

-----------------------
摘自:http://hi.baidu.com/xingwei0005/blog/item/8c177200991335df277fb5cb.html
[解决办法]
其实我觉得倒也不用那么深入吧 ,看到这类地址,就是个常量, 游戏外挂常说的找基址; 编译后就保存在那里的, 用文件特征码定位可以定位到。 或者用od类调试器加载这个地址的时候访问断点看调用情况。

读书人网 >.NET

热点推荐