首页
社区
课程
招聘
Class逆向[ZT]
发表于: 2004-10-6 00:30 9908

Class逆向[ZT]

nbw 活跃值
24
2004-10-6 00:30
9908
Classes restoration

Classes restoration is a complicated procedure which requires knowledge of OOP and the way this OOP is organized in specific compiler.

Our task is to get class, its methods and members. Let’s begin with Delphi, because it’s relatively easy to find a class here.

Class restoration begins with looking for constructor, because here is the memory for object is being allocated and also we can gain some insight into constructor’s components.

It’s easy to find a constructor in Delphi ? we just need to look for a string in which the class name occurs. For example, for TList the next structure can be found:  

CODE:0040D598 TList           dd offset TList_VTBL

CODE:0040D59C                 dd 7 dup(0)

CODE:0040D5B8                 dd offset aTlist        ; "TList"

CODE:0040D5BC SizeOfObject    dd 10h

CODE:0040D5C0                 dd offset off_4010C8

CODE:0040D5C4                 dd offset TObject::SafeCallException

CODE:0040D5C8                 dd offset nullsub_8

CODE:0040D5CC                 dd offset TObject::NewInstance

CODE:0040D5D0                 dd offset TObject::FreeInstance

CODE:0040D5D4                 dd offset sub_40EA08

CODE:0040D5D8 TList_VTBL           dd offset TList::Grow

CODE:0040D5DC                 dd offset unknown_libname_107

CODE:0040D5E0 aTlist          db 5,'TList'

This is, if we can say so, an ‘object descriptor’.

Pointer to it is being passed to the constructor. The constructor takes from it the data required for object creation. Using Xref on 40D598 we can find all the places where the constructor is being called. Here is an example of one of such calls:

CODE:0040E72E                 mov     eax, ds:TList

CODE:0040E733                 call    CreateClass

CODE:0040E738                 mov     ds:dword_4A45F8, eax

The constructor function we named by ourselves. We can determine whether it is really a  CreateClass by the contents of the function:

CODE:00402F48 CreateClass     proc near               ; CODE XREF: @BeginGlobalLoading+17p

CODE:00402F48                                         ; @CollectionsEqual+48p ...

CODE:00402F48                 test    dl, dl

CODE:00402F4A                 jz      short loc_402F54

CODE:00402F4C                 add     esp, 0FFFFFFF0h

CODE:00402F4F                 call    __linkproc__ ClassCreate

CODE:00402F54

CODE:00402F54 loc_402F54:                             ; CODE XREF: CreateClass+2j

CODE:00402F54                 test    dl, dl

CODE:00402F56                 jz      short locret_402F62

CODE:00402F58                 pop     large dword ptr fs:0

CODE:00402F5F                 add     esp, 0Ch

CODE:00402F62

CODE:00402F62 locret_402F62:                          ; CODE XREF: CreateClass+Ej

CODE:00402F62                 retn

CODE:00402F62 CreateClass     endp

I.e., if there is __linkproc__ ClassCreate inside the function, it’s a constructor. Now we can look at how particularly the class creation happens:

CODE:00403200 __linkproc__ ClassCreate proc near      ; CODE XREF: CreateClass+7p

CODE:00403200                                         ; sub_40AA58+Ap ...

CODE:00403200

CODE:00403200 arg_0           = dword ptr  10h

CODE:00403200

CODE:00403200                 push    edx

CODE:00403201                 push    ecx

CODE:00403202                 push    ebx

CODE:00403203                 call    dword ptr [eax-0Ch]

CODE:00403206                 xor     edx, edx

CODE:00403208                 lea     ecx, [esp+arg_0]

CODE:0040320C                 mov     ebx, fs:[edx]

CODE:0040320F                 mov     [ecx], ebx

CODE:00403211                 mov     [ecx+8], ebp

CODE:00403214                 mov     dword ptr [ecx+4], offset loc_403225

CODE:0040321B                 mov     [ecx+0Ch], eax

CODE:0040321E                 mov     fs:[edx], ecx

CODE:00403221                 pop     ebx

CODE:00403222                 pop     ecx

CODE:00403223                 pop     edx

CODE:00403224                 retn

CODE:00403224 __linkproc__ ClassCreate endp

So, the command

CODE:0040E72E mov eax, ds:TList

loads contents into EAX to the address of TList, i.e. it’s TList_VTBL. Since we use Delphi, here is the Borland’s convention of __fastcall is being used (parameters are being passed in the next order: EAX, EDX, ECX, stack...).

It means that the pointer to the virtual methods table is being passed to the function CreateClass as a first parameter. Further EAX is not changing and gets into __linkproc__ClassCreate, and here we see:

CODE:00403203                 call    dword ptr [eax-0Ch]

Where is it going? The pointer to TList_VTBL=0х40D5D8 is still lying in EAX.  0x40D5D8-0xC=40D5CC, and this is

CODE:0040D5CC                 dd offset TObject::NewInstance

This is the ancestor’s constructor. So, TList is inherited by TObject. Let’s look what is in the depth:

CODE:00402F0C TObject::NewInstance proc near          ; DATA XREF: CODE:004010FCo

CODE:00402F0C                                         ; CODE:004011DCo ...

CODE:00402F0C                 push    eax

CODE:00402F0D                 mov     eax, [eax-1Ch]

CODE:00402F10                 call    __linkproc__ GetMem

CODE:00402F15                 mov     edx, eax

CODE:00402F17                 pop     eax

CODE:00402F18                 jmp     TObject::InitInstance

CODE:00402F18 TObject::NewInstance endp

The value of EAX is the same, so 0х40D5D8-0x1C=0x40D5BC.
Thus, the object size which is stored in 0x40D5BC, is being passed into GetMem

CODE:0040D5BC SizeOfObject    dd 10h

So, the total size of object members =0x10.

The function TObject::InitInstance doesn’t do anything special, it’s just stuffs object members with zero and sets the value of pointer to VTBL in the just created instance of the object. Then the exit from CreateClass will happen and the pointer to the instance of the object will be returned into EAX.

That’s why the call of constructors looks like:

CODE:0040E72E                 mov     eax, ds:TList

CODE:0040E733                 call    CreateClass

CODE:0040E738                 mov     ds:dword_4A45F8, eax

Restoration of the object structure

We have known the object size already. It’s 0x10, where 0x4 bytes were taken by the pointer to VTBL. But there are 0xC bytes left and they contain object members, so we need to find them. Here an intuition is required. First of all, objects can’t be created for no particular reason and members can be filled either in constructor (fully or partly) or after creating by Set-methods. Our TList in the constructor is being stuffed with zero through rep stosd (in TObject::InitInstance). So there is no info about class members in the constructor. Thus let’s trace life cycle after the creation.

In our example the pointer to the instance of the class is being driven into global variable  dword_4A45F8. So we can just set breakpoint on reading from dword_4A45F8 and look at how the object methods will be called.

First event:

CODE:0041319D mov     eax, [ebp+var_4]

CODE:004131A0 mov     edx, ds:pTList

CODE:004131A6 mov     [eax+30h], edx  ; copied a pointer to the instance of an object
CODE:004131A9 jmp     short loc_4131BD

.............

CODE:004131BD

CODE:004131BD loc_4131BD:                             ; CODE XREF: sub_4130BC+EDj

CODE:004131BD xor     eax, eax

CODE:004131BF push    ebp

CODE:004131C0 push    offset loc_413276

CODE:004131C5 push    dword ptr fs:[eax]

CODE:004131C8 mov     fs:[eax], esp

CODE:004131CB mov     eax, [ebp+var_4]

CODE:004131CE mov     edx, [eax+18h]

CODE:004131D1 mov     eax, [ebp+var_4]

CODE:004131D4 mov     eax, [eax+30h] ;’implicit passing of a pointer to the object itself’

CODE:004131D7 call    Classes::TList::Add(void *)

Now look into Classes::TList::Add:

CODE:0040EA28 __fastcall Classes::TList::Add(void *) proc near

CODE:0040EA28                                         ; CODE XREF: @RegisterClass+9Bp

CODE:0040EA28                                         ; @RegisterIntegerConsts+20p ...

CODE:0040EA28 push    ebx

CODE:0040EA29 push    esi

CODE:0040EA2A push    edi

CODE:0040EA2B mov     edi, edx

CODE:0040EA2D mov     ebx, eax ; a kind of This

CODE:0040EA2F mov     esi, [ebx+8] ; addressing to the object member №1

CODE:0040EA32 cmp     esi, [ebx+0Ch] ; addressing to the object member №3

CODE:0040EA35 jnz     short loc_40EA3D

CODE:0040EA37 mov     eax, ebx

CODE:0040EA39 mov     edx, [eax] ;addressing to TList->pVTBL

CODE:0040EA3B call    dword ptr [edx]

CODE:0040EA3D

CODE:0040EA3D loc_40EA3D:                             ; CODE XREF: Classes::TList::Add(void *)+Dj

CODE:0040EA3D mov     eax, [ebx+4] ; addressing to the object member №2

CODE:0040EA40 mov     [eax+esi*4], edi

CODE:0040EA43 inc     dword ptr [ebx+8]

CODE:0040EA46 mov     eax, esi

CODE:0040EA48 pop     edi

CODE:0040EA49 pop     esi

CODE:0040EA4A pop     ebx

CODE:0040EA4B retn

CODE:0040EA4B __fastcall Classes::TList::Add(void *) endp

That is… 3 last members have been found

All of them are of 4 bytes size. To simplify the work with classes in IDA Pro we use structures. Classes are the same structures, anyway:)))

After using the next structure:

00000000 TList_obj struc ; (sizeof=0X10)

00000000 pVTBL dd ?

00000004 Property1 dd ?

00000008 Property2 dd ?

0000000C Property3 dd ?

00000010 TList_obj ends

things become more clear:

CODE:0040EA28 __fastcall Classes::TList::Add(void *) proc near

CODE:0040EA28                                         ; CODE XREF: @RegisterClass+9Bp

CODE:0040EA28                                         ; @RegisterIntegerConsts+20p ...

CODE:0040EA28 push    ebx

CODE:0040EA29 push    esi

CODE:0040EA2A push    edi

CODE:0040EA2B mov     edi, edx

CODE:0040EA2D mov     ebx, eax

CODE:0040EA2F mov     esi, [ebx+TList_obj.Property2]

CODE:0040EA32 cmp     esi, [ebx+TList_obj.Property3]

CODE:0040EA35 jnz     short loc_40EA3D

CODE:0040EA37 mov     eax, ebx

CODE:0040EA39 mov     edx, [eax+TList_obj.pVTBL]

CODE:0040EA3B call    dword ptr [edx] ;TList::Grow

CODE:0040EA3D

CODE:0040EA3D loc_40EA3D:                             ; CODE XREF: Classes::TList::Add(void *)+Dj

CODE:0040EA3D mov     eax, [ebx+TList_obj.Property1]

CODE:0040EA40 mov     [eax+esi*4], edi

CODE:0040EA43 inc     [ebx+TList_obj.Property2]

CODE:0040EA46 mov     eax, esi

CODE:0040EA48 pop     edi

CODE:0040EA49 pop     esi

CODE:0040EA4A pop     ebx

CODE:0040EA4B retn

CODE:0040EA4B __fastcall Classes::TList::Add(void *) endp

Think of VBTL look and it will be easy to guess that:

CODE:0040EA3B call    dword ptr [edx]

is  TList::Grow,
because

CODE:0040D5D8 pVTBL dd offset TList::Grow  

Now we can make a deeper analyze of the class members. For example, if we have a look at the next code:

CODE:0040EA3D mov     eax, [ebx+TList_obj.Property1]

CODE:0040EA40 mov     [eax+esi*4], edi

CODE:0040EA43 inc     [ebx+TList_obj.Property2]

we can say that Property2 is a counter for the list elements, because it increases when an element is added.

And Property1 is the pointer to the array of list elements. Property 2 in this array is an index. Property 3 is the maximum number of the elements in a list, as method TList::Grow is being called just when Property2==Property3. We found out this by using  logic. Now, when all is clear, we may look in Help and give names to the members:

CODE:0040EA28 __fastcall Classes::TList::Add(void *) proc near

CODE:0040EA28                                         ; CODE XREF: @RegisterClass+9Bp

CODE:0040EA28                                         ; @RegisterIntegerConsts+20p ...

CODE:0040EA28                 push    ebx

CODE:0040EA29                 push    esi

CODE:0040EA2A                 push    edi

CODE:0040EA2B                 mov     edi, edx

CODE:0040EA2D                 mov     ebx, eax

CODE:0040EA2F                 mov     esi, [ebx+TList_obj.Count]

CODE:0040EA32                 cmp     esi, [ebx+TList_obj.Capacity]

CODE:0040EA35                 jnz     short loc_40EA3D

CODE:0040EA37                 mov     eax, ebx

CODE:0040EA39                 mov     edx, [eax+TList_obj.pVTBL]

CODE:0040EA3B                 call    dword ptr [edx]

CODE:0040EA3D

CODE:0040EA3D loc_40EA3D:                             ; CODE XREF: Classes::TList::Add(void *)+Dj

CODE:0040EA3D                 mov     eax, [ebx+TList_obj.Items]

CODE:0040EA40                 mov     [eax+esi*4], edi

CODE:0040EA43                 inc     [ebx+TList_obj.Count]

CODE:0040EA46                 mov     eax, esi

CODE:0040EA48                 pop     edi

CODE:0040EA49                 pop     esi

CODE:0040EA4A                 pop     ebx

CODE:0040EA4B                 retn

CODE:0040EA4B __fastcall Classes::TList::Add(void *) endp

We have restored the structure, let’s look into the class methods.

Looking for the class methods

Methods can be: public/private (protected), virtual/non-virtual and static.

Static methods can’t be found because after the compilation was made they look like common procedures. Affiliation of such function with a specific class is also impossible to determine. But is there a sense in such search? If the function is called somewhere in the class methods, it, anyway, will be viewed while the code is being extracted. Otherwise, it is wasting of time.

Virtual functions are easy to find to? they all are in VTBL.

But how we should look for non-virtual ones? Let’s think of OOP: when the object methods are called, the pointer to the object itself is implicitly passed to them.  In fact, it means that each method accepts the pointer to the object as its first parameter.  I.e., if the method was declared as __fastcall, the pointer to the object will be pushed into EAX. But for __cdecl or __stdcall methods it’s the first parameter in the stack. Let’s look on where is the pointer to the object is stored…absolutely right! In dword_4A45F8. On XREF to 4A45F8 we can find lots of non-virtual methods. Further we can set a breakpoint on 4A45F8 and trace the copying of a pointer to the instance to find where else the call of methods can take place. All is easy in our example, because global variable is used.  But what we should do, if the local variable is used or if the code can’t be executed (for example, we research driver’s code or the code is not allowed for execution)? Here we need a specific method.

Step-by-step:

1)      We have to find all the points of constructor’s calls.

For each call

2)      Trace where the pointer to the instance of an object is being written (local variable)

3)      Looking through the function which has called the constructor  for all the calls of the object methods

4)      If there are no such calls, look at the next call of the constructor, otherwise look for all xref to the method that had been found. In such way we can find calls that are not beside the constructor. And, as we know that the first parameter is the pointer to an object, we can go to each xref and look where else the pointer to an object was used. And in such way we are going up the levels of the code, till we reach a deadlock or the method that had been found.

5)      Reviewing the next method that had been found

For example, we have found Classes::TList::Add method. On one of the Xref we find Classes::TList::Add method here:

CODE:0040F020 TThreadList::Add proc near              ; CODE XREF: TCanvas::`...'+9Ep

CODE:0040F020                                         ; Graphics::_16725+C4p

CODE:0040F020

CODE:0040F020 var_4           = dword ptr -4

CODE:0040F020

CODE:0040F020                 push    ebp

CODE:0040F021                 mov     ebp, esp

CODE:0040F023                 push    ecx

CODE:0040F024                 push    ebx

CODE:0040F025                 mov     ebx, edx

CODE:0040F027                 mov     [ebp+var_4], eax

CODE:0040F02A                 mov     eax, [ebp+var_4]

CODE:0040F02D                 call    TThreadList::LockList

CODE:0040F032                 xor     eax, eax

CODE:0040F034                 push    ebp

CODE:0040F035                 push    offset loc_40F073

CODE:0040F03A                 push    dword ptr fs:[eax]

CODE:0040F03D                 mov     fs:[eax], esp

CODE:0040F040                 mov     eax, [ebp+var_4]

CODE:0040F043                 mov     eax, [eax+4]

CODE:0040F046                 mov     edx, ebx

CODE:0040F048                 call    TList::IndexOf

CODE:0040F04D                 inc     eax

CODE:0040F04E                 jnz     short loc_40F05D

CODE:0040F050                 mov     eax, [ebp+var_4]

CODE:0040F053                 mov     eax, [eax+4]

CODE:0040F056                 mov     edx, ebx

CODE:0040F058                 call    Classes::TList::Add(void *)

I.e. we have found TList::IndexOf method.

Further we see that we are in the method of TthreadList object and TList is its member. Here we have nothing to look at. Let’s assume that there are no more xref to Classes::TList::Add. Go in TList::IndexOf method and look at its xref. One of them directs us here:

CODE:0040EE38 TList::Remove   proc near               ; CODE XREF: TThreadList::Remove+28p

CODE:0040EE38                                         ; TCollection::RemoveItem+Bp ...

CODE:0040EE38                 push    ebx

CODE:0040EE39                 push    esi

CODE:0040EE3A                 mov     ebx, eax

CODE:0040EE3C                 mov     eax, ebx

CODE:0040EE3E                 call    TList::IndexOf

CODE:0040EE43                 mov     esi, eax

CODE:0040EE45                 cmp     esi, 0FFFFFFFFh

CODE:0040EE48                 jz      short loc_40EE53

CODE:0040EE4A                 mov     edx, esi

CODE:0040EE4C                 mov     eax, ebx

CODE:0040EE4E                 call    TList::Delete

CODE:0040EE53

CODE:0040EE53 loc_40EE53:                             ; CODE XREF: TList::Remove+10j

CODE:0040EE53                 mov     eax, esi

CODE:0040EE55                 pop     esi

CODE:0040EE56                 pop     ebx

CODE:0040EE57                 retn

CODE:0040EE57 TList::Remove   endp

So, TList::Delete and TList::Remove are found.

And so forth for all xref and variables that contain a pointer to the instance of a class.

Here is an example of looking through the variable:

CODE:0041319D mov     eax, [ebp+var_4]

CODE:004131A0 mov     edx, ds:pTList

CODE:004131A6 mov     [eax+30h], edx  ;a pointer to the instance of an object is being copied

CODE:004131A9 jmp     short loc_4131BD

We see below:

CODE:00413236 mov     eax, [eax+30h]

CODE:00413239 mov     edx, [ebp+var_10]

CODE:0041323C call    TList::Get

How we can identify public or private methods?

We can try to do that only when all the set of methods is found. Private methods are called only inside the other object methods. I.e. we should look at xref.

While looking for methods we advise to number them first. It means as you find the method, you name it Object1::Method1, Object1::Method2 and so on, and when all the methods are found you may begin restoration of type and number of elements.

Determination of the number of method arguments

For  __cdecl и __stdcall there are few things to tell about, you just need to look on how much of them have IDA found and subtract the 1 (i.e. the 1 is a pointer to the instance of an object, and others are method arguments). There are more complications for __fastcall. First we need to remember the sequence order of arguments: EAX,EDX,ECX,stack.

The analyze begins with  how much arguments that had been  transmitted via stack does IDA have counted. If there are at least one, we add to it 3 (3 register’s plus the ones for stack). As first argument is allocated for This, we need to subtract the 1 from the number. The summary value is the net number of arguments.

If there are no stack arguments, we look at the beginning of the function. Delphi tries not to spoil arguments values, so each __fastcall function begins with

copying from registers EAX, EDX and ECX in such way:

mov esi, edx ; first parameter
mov ebx, eax ; pThis
mov edi, ecx ; second parameter

Depending on the number of registers that are being copied, one can conclude what is the number of arguments.

For example:

CODE:0040EBE0 TList::Get      proc near               ; CODE XREF: @GetClass+1Dp

CODE:0040EBE0                                         ; @UnRegisterModuleClasses+24p ...

CODE:0040EBE0

CODE:0040EBE0 var_4           = dword ptr -4

CODE:0040EBE0

CODE:0040EBE0                 push    ebp

CODE:0040EBE1                 mov     ebp, esp

CODE:0040EBE3                 push    0

CODE:0040EBE5                 push    ebx

CODE:0040EBE6                 push    esi

CODE:0040EBE7                 mov     esi, edx

CODE:0040EBE9                 mov     ebx, eax

CODE:0040EBEB                 xor     eax, eax

There are 2 arguments, 1 of them is pThis, thus TList::Get has 1 argument.

CODE:004198CC                 push    ebp

CODE:004198CD                 mov     ebp, esp

CODE:004198CF                 add     esp, 0FFFFFF8Ch

CODE:004198D2                 push    ebx

CODE:004198D3                 push    esi

CODE:004198D4                 push    edi

CODE:004198D5                 mov     [ebp+var_C], ecx

CODE:004198D8                 mov     [ebp+var_8], edx

CODE:004198DB                 mov     [ebp+var_4], eax

There are 3 arguments, 1 of them is for pThis, so total is 2 arguments.

We should  remind you that we restore the number of arguments in initial method which is described in Delphi, and in IDA, naturally, while declaring the function type we should write all the arguments in consideration with This.

Types of arguments try to determine on your own.

[课程]Linux pwn 探索篇!

收藏
免费 1
支持
分享
最新回复 (7)
雪    币: 16
能力值: (RANK:10 )
在线值:
发帖
回帖
粉丝
2
我看的头昏脑胀!
2004-10-18 11:33
0
雪    币: 339
活跃值: (1510)
能力值: ( LV13,RANK:970 )
在线值:
发帖
回帖
粉丝
3
我就不头晕,根本没看  :D
2004-10-18 20:33
0
雪    币: 8026
活跃值: (2511)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
where did u find this article? can u give me the detail address?
2004-11-1 15:34
0
雪    币: 242
活跃值: (418)
能力值: ( LV11,RANK:188 )
在线值:
发帖
回帖
粉丝
5
这篇文章太好了,非常感谢转帖,非常有帮助。翻译完成我会共享在这儿。
2011-9-4 04:47
0
雪    币: 4580
活跃值: (992)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
6
伸手党路过,坐等中文版
2011-9-4 13:15
0
雪    币: 321
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
http://www.apriorit.com/our-company/dev-blog/78-classes-restoration
2011-9-4 17:06
0
雪    币: 242
活跃值: (418)
能力值: ( LV11,RANK:188 )
在线值:
发帖
回帖
粉丝
8
前面的一些译者废话,既然有这么好的一篇文章,我又说了自己想翻译它,那么,就来翻译它吧!
这篇文章的思路很清晰,我在阅读之后也有一点点阅读的笔记,在翻译过程中也把它一点点添进来作为补充的内容好了
我突然发现原来另一位朋友已经翻译过了! http://www.pediy.com/bbshtml/BBS6/pediy6935.htm
最后,希望对你能有帮助:

1.class的代码还原

class的代码还原是一个相当曲折恼人心的过程,需要对OOP思想的程序设计有清晰的了解,同时还需要对特定的编译器对OOP的实现方式有所熟悉。
在这个任务里,我们的目标是要找到class(类),并找到它的成员函数,成员变量。就让我们从 delphi开始吧,它的编译器对于类的实现方
式相对其他语言而言,要找到类的位置是很简单的。
//--译者补充,相对其他语言-- 主要是指c++ --而言,delphi的类是容易找到的,但第一次碰到delphi程序的话,会对delphi默认使用__fastcall类型的函数调用感到头痛,但几个小时后,就会习惯了。


类的代码还原,一般都要从类的构造函数开始。原因是这儿是给这类的对象申请内存空间的地方,同时,我们根据构造函数对类的成员的初始化的方式,可以有一览类结构,管中窥豹的益处的。
那么,在delphi里找到一个构造函数简单不呢?是的,我们只需要寻找一个看起来是类名的字符串出现的地方就可以了。例如,查找对

"TList"的引用,可以发现下面的这个结构:

CODE:0040D598 TList           dd offset TList_VTBL

CODE:0040D59C                 dd 7 dup(0)

CODE:0040D5B8                 dd offset aTlist        ; "TList"

CODE:0040D5BC SizeOfObject    dd 10h

CODE:0040D5C0                 dd offset off_4010C8

CODE:0040D5C4                 dd offset TObject::SafeCallException

CODE:0040D5C8                 dd offset nullsub_8

CODE:0040D5CC                 dd offset TObject::NewInstance

CODE:0040D5D0                 dd offset TObject::FreeInstance

CODE:0040D5D4                 dd offset sub_40EA08

CODE:0040D5D8 TList_VTBL           dd offset TList::Grow

CODE:0040D5DC                 dd offset unknown_libname_107

CODE:0040D5E0 aTlist          db 5,'TList'

这是,我们可以这样说这一段数据,它是特定"类型的描述"
//我把类型的描述用ClassInfo结构来表示,它是有个固定格式的。
一个指向"类型的描述"的指针,要被传递给类的构造函数,而构造函数就根据它,来找到创建一个对象所需要的数据们。对着它的地址0040D598 查找Xref(引用),我们可以发现所有的调用到了构造函数的地方。下面是一个例子:

CODE:0040E72E                 mov     eax, ds:TList

CODE:0040E733                 call    CreateClass

CODE:0040E738                 mov     ds:dword_4A45F8, eax


这个构造函数,是我们自己给它起了个名字的。但我们可以确定它真是一个Create(创建) Class(类)的函数,看看它的样子就明白了。

CODE:00402F48 CreateClass     proc near               ; CODE XREF: @BeginGlobalLoading+17p

CODE:00402F48                                         ; @CollectionsEqual+48p ...


CODE:00402F48                 test    dl, dl

CODE:00402F4A                 jz      short loc_402F54

CODE:00402F4C                 add     esp, 0FFFFFFF0h

CODE:00402F4F                 call    __linkproc__ ClassCreate

CODE:00402F54

CODE:00402F54 loc_402F54:                             ; CODE XREF: CreateClass+2j

CODE:00402F54                 test    dl, dl

CODE:00402F56                 jz      short locret_402F62

CODE:00402F58                 pop     large dword ptr fs:0


CODE:00402F5F                 add     esp, 0Ch

CODE:00402F62

CODE:00402F62 locret_402F62:                          ; CODE XREF: CreateClass+Ej

CODE:00402F62                 retn

CODE:00402F62 CreateClass     endp


即是说,如果那有一个 __linkproc__ ClassCreate在函数里面的话,你就可以确定它是一个构造用的函数。现在我们看看具体的一个类的创建过程是怎么样的:

CODE:00403200 __linkproc__ ClassCreate proc near      ; CODE XREF: CreateClass+7p

CODE:00403200                                         ; sub_40AA58+Ap ...

CODE:00403200

CODE:00403200 arg_0           = dword ptr  10h

CODE:00403200

CODE:00403200                 push    edx

CODE:00403201                 push    ecx

CODE:00403202                 push    ebx

CODE:00403203                 call    dword ptr [eax-0Ch]

CODE:00403206                 xor     edx, edx

CODE:00403208                 lea     ecx, [esp+arg_0]

CODE:0040320C                 mov     ebx, fs:[edx]

CODE:0040320F                 mov     [ecx], ebx

CODE:00403211                 mov     [ecx+8], ebp

CODE:00403214                 mov     dword ptr [ecx+4], offset loc_403225

CODE:0040321B                 mov     [ecx+0Ch], eax

CODE:0040321E                 mov     fs:[edx], ecx

CODE:00403221                 pop     ebx

CODE:00403222                 pop     ecx

CODE:00403223                 pop     edx

CODE:00403224                 retn

CODE:00403224 __linkproc__ ClassCreate endp


综上所列的代码,创建类的过程就是如此这般那般啊。首先,这句

CODE:0040E72E mov eax, ds:TList 


把 TList的地址载入到eax中,补充下,其实通常叫它做TList_VTBL(virtual functions table,虚函数表)。既然我们现在是在用 delphi,这儿就是 Borland的 __fastcall函数调用方式,来作为公民守则了 (如是,参数按这样的顺序来传递的: EAX, EDX, ECX, 堆栈...)
它意味着那个指针,那个指向虚函数表的指针作为了第一个参数给传递到函数CreateClass了。接下来,EAX的值又是一直没被改变的,然后我们就进到 __linkproc__ClassCreate里了(意味着TList又是 __linkproc__ClassCreate的第一个参数),同时我们看到了:

CODE:00403203                 call    dword ptr [eax-0Ch] ; 


它要往哪儿跑了呢?那个指针 TList_VTBL=0х40D5D8 还躺在EAX中。0x40D5D8-0xC=40D5CC,于是就得到了:

CODE:0040D5CC                 dd offset TObject::NewInstance


这,就是一路追踪到底找到的老祖宗级别的那个构造函数了(意指真正的构造函数)。综合起来,TList,它就是一个继承了 TObject的类。那我们就再继续往里面瞅:
CODE:00402F0C TObject::NewInstance proc near          ; DATA XREF: CODE:004010FCo

CODE:00402F0C                                         ; CODE:004011DCo ...

CODE:00402F0C                 push    eax

CODE:00402F0D                 mov     eax, [eax-1Ch]

CODE:00402F10                 call    __linkproc__ GetMem

CODE:00402F15                 mov     edx, eax

CODE:00402F17                 pop     eax

CODE:00402F18                 jmp     TObject::InitInstance

CODE:00402F18 TObject::NewInstance endp


指向到此的时候,EAX的值还是没变的,所以0x40D5D8-0x1C=0x40D5BC.
看样子,对象的内存大小是保存到0x40D5BC的,它就是传递给 GetMem的参数。

CODE:0040D5BC SizeOfObject    dd 10h


啊,原来整个对象占用的内存大小是 = 0x10
最后跳过去的函数 TObject::InitInstance 没干啥特别的事情,它只是给对象的内存里塞点东西,塞点0,同时也设置了刚刚创建出来的对象的VTBL的指针(//就在对象的内存里的第一个int位置)。然后就进到CreateClass的结束流程了,指向这个新对象的内存片的地址会放到EAX返回去。
这也是为什么对构造函数的调用会是这种样子的:

CODE:0040E72E                 mov     eax, ds:TList

CODE:0040E733                 call    CreateClass

CODE:0040E738                 mov     ds:dword_4A45F8, eax

2.对象结构的还原

我们已经知道对象的内存大小了。它是0x10,里面有0x4字节用做保存VTBL的指针。但就还有0xc字节没清楚,而且他们就包含了成员变量,所以啊,我们接下来需要找着这几个。这个步骤,是需要逆向人士的直觉来帮忙的。不管三七二十一,一个对象绝不会没它特定的原因就给创建出来,成员变量也一定要填充好,或者在构造函数里面,或者在创建完成之后的设置变量值的函数里。我们的TList在构造函数中只是用stosd(在TObject::InitInstance中)塞了些0进去。所以在构造函数中我们没找到任何和类成员变量相关的信息。那就让我们跟踪这个类型在创造出来后的生命线吧。
在我们的例子中,指向我们的新生命、新对象的指针保存到了全局变量dword_4A45F8中。所以我们就可以对dword_4A45F8设置读写断点,然后看看成员函数会怎么样的被调用。
第一个事件:

CODE:0041319D mov     eax, [ebp+var_4]

CODE:004131A0 mov     edx, ds:pTList

CODE:004131A6 mov     [eax+30h], edx  ; 把指针复制到某个类的一个实例对象里
CODE:004131A9 jmp     short loc_4131BD

.............

CODE:004131BD

CODE:004131BD loc_4131BD:                             ; CODE XREF: sub_4130BC+EDj

CODE:004131BD xor     eax, eax

CODE:004131BF push    ebp

CODE:004131C0 push    offset loc_413276

CODE:004131C5 push    dword ptr fs:[eax]

CODE:004131C8 mov     fs:[eax], esp

CODE:004131CB mov     eax, [ebp+var_4]

CODE:004131CE mov     edx, [eax+18h]

CODE:004131D1 mov     eax, [ebp+var_4]

CODE:004131D4 mov     eax, [eax+30h] ;’偷偷的把指向它自己的指针传递给它自己的成员函数

CODE:004131D7 call    Classes::TList::Add(void *)

进Classes::TList::Add看看:

CODE:0040EA28 __fastcall Classes::TList::Add(void *) proc near

CODE:0040EA28                                         ; CODE XREF: @RegisterClass+9Bp

CODE:0040EA28                                         ; @RegisterIntegerConsts+20p ...

CODE:0040EA28 push    ebx

CODE:0040EA29 push    esi

CODE:0040EA2A push    edi

CODE:0040EA2B mov     edi, edx

CODE:0040EA2D mov     ebx, eax ; a kind of This

CODE:0040EA2F mov     esi, [ebx+8] ; 第1个成员变量的地址

CODE:0040EA32 cmp     esi, [ebx+0Ch] ; 第3个成员变量的地址

CODE:0040EA35 jnz     short loc_40EA3D

CODE:0040EA37 mov     eax, ebx

CODE:0040EA39 mov     edx, [eax] ; TList->pVTBL的地址

CODE:0040EA3B call    dword ptr [edx]

CODE:0040EA3D

CODE:0040EA3D loc_40EA3D:                             ; CODE XREF: Classes::TList::Add(void *)+Dj

CODE:0040EA3D mov     eax, [ebx+4] ; 第2个成员变量的地址

CODE:0040EA40 mov     [eax+esi*4], edi

CODE:0040EA43 inc     dword ptr [ebx+8]

CODE:0040EA46 mov     eax, esi

CODE:0040EA48 pop     edi

CODE:0040EA49 pop     esi

CODE:0040EA4A pop     ebx

CODE:0040EA4B retn

CODE:0040EA4B __fastcall Classes::TList::Add(void *) endp

那是...3个在后面的成员变量被发现了
他们全都是4字节的大小。为了简化和class相关的工作,我们在ida里使用自创建的结构。不管咋说,类就和结构一样嘛 :)))
在使用了那个(下面的结构),之后,

00000000 TList_obj struc ; (sizeof=0X10)

00000000 pVTBL dd ?

00000004 Property1 dd ?

00000008 Property2 dd ?

0000000C Property3 dd ?

00000010 TList_obj ends

看起来好多了:

CODE:0040EA28 __fastcall Classes::TList::Add(void *) proc near

CODE:0040EA28                                         ; CODE XREF: @RegisterClass+9Bp

CODE:0040EA28                                         ; @RegisterIntegerConsts+20p ...

CODE:0040EA28 push    ebx

CODE:0040EA29 push    esi

CODE:0040EA2A push    edi

CODE:0040EA2B mov     edi, edx

CODE:0040EA2D mov     ebx, eax

CODE:0040EA2F mov     esi, [ebx+TList_obj.Property2]

CODE:0040EA32 cmp     esi, [ebx+TList_obj.Property3]

CODE:0040EA35 jnz     short loc_40EA3D

CODE:0040EA37 mov     eax, ebx

CODE:0040EA39 mov     edx, [eax+TList_obj.pVTBL]

CODE:0040EA3B call    dword ptr [edx] ;TList::Grow

CODE:0040EA3D

CODE:0040EA3D loc_40EA3D:                             ; CODE XREF: Classes::TList::Add(void *)+Dj

CODE:0040EA3D mov     eax, [ebx+TList_obj.Property1]

CODE:0040EA40 mov     [eax+esi*4], edi

CODE:0040EA43 inc     [ebx+TList_obj.Property2]

CODE:0040EA46 mov     eax, esi

CODE:0040EA48 pop     edi

CODE:0040EA49 pop     esi

CODE:0040EA4A pop     ebx

CODE:0040EA4B retn

CODE:0040EA4B __fastcall Classes::TList::Add(void *) endp

回想一下VTBL看起来是啥样的,然后很容易可以发现:

CODE:0040EA3B call    dword ptr [edx]


是 TList::Grow,
因为TList::VTBL中的第一个地址是TList::Grow

CODE:0040D5D8 pVTBL dd offset TList::Grow  


现在我们该对成员变量做一个深入的探讨了。
举个例子,如果我们看了一下这段代码:

CODE:0040EA3D mov     eax, [ebx+TList_obj.Property1]

CODE:0040EA40 mov     [eax+esi*4], edi

CODE:0040EA43 inc     [ebx+TList_obj.Property2]

我们可以很理智的做出新的解释,Property2它就是一个计数器(counter),计list的元素数量的计数器,因为每次list多元素时它都增加了一下自己。
而Property1就是一个指针,指向的是list元素的数组。Property2是数组的一个索引。Property3则是数组的当前最大容量,这从

TList:Grow要在Property2==Property3时被调用可以看出来。我们利用逻辑思维,把这些事情给搞清楚了。那现在,在所有东西都水落石出的时候,我们也许到时候帮成员函数起名字的时候了。

CODE:0040EA28 __fastcall Classes::TList::Add(void *) proc near

CODE:0040EA28                                         ; CODE XREF: @RegisterClass+9Bp

CODE:0040EA28                                         ; @RegisterIntegerConsts+20p ...

CODE:0040EA28                 push    ebx

CODE:0040EA29                 push    esi

CODE:0040EA2A                 push    edi

CODE:0040EA2B                 mov     edi, edx

CODE:0040EA2D                 mov     ebx, eax

CODE:0040EA2F                 mov     esi, [ebx+TList_obj.Count]

CODE:0040EA32                 cmp     esi, [ebx+TList_obj.Capacity]

CODE:0040EA35                 jnz     short loc_40EA3D

CODE:0040EA37                 mov     eax, ebx

CODE:0040EA39                 mov     edx, [eax+TList_obj.pVTBL]

CODE:0040EA3B                 call    dword ptr [edx]

CODE:0040EA3D

CODE:0040EA3D loc_40EA3D:                             ; CODE XREF: Classes::TList::Add(void *)+Dj

CODE:0040EA3D                 mov     eax, [ebx+TList_obj.Items]

CODE:0040EA40                 mov     [eax+esi*4], edi

CODE:0040EA43                 inc     [ebx+TList_obj.Count]

CODE:0040EA46                 mov     eax, esi


CODE:0040EA48                 pop     edi

CODE:0040EA49                 pop     esi

CODE:0040EA4A                 pop     ebx

CODE:0040EA4B                 retn

CODE:0040EA4B __fastcall Classes::TList::Add(void *) endp

我们已经把类的内存结构给恢复了,让我们跑到类成员函数的地方看吧

3.对类成员函数的寻找

一个成员函数,它可以是: public/ private (protected), virtual/ non-virtual,还有 static的。静态成员函数是找不着的,原因是在编译之后,他们看起来就跟一个普通的函数过程没两样了。把这样一个函数和某一个类联系到一起,是不可能准备的解释通顺的。那对于这种函数的寻找有什么意义吗?假设,这个函数它在某个类成员函数的某处给调用到了,它,不管咋样,它在从成员函数出发做的代码分析的完成到七七八八时,也还一样显露出来了。其他情况的话,寻找一个特定的static的成员函数就是浪费时间。
接着回来正体,虚拟函数容易找到吗?嘿,它们统统的都在VTBL中。
但我们怎么找一个非虚函数呢?让我们从OOP的角度考虑下事情: 当一个成员函数被调用到的时候,指向自身对象的类指针被隐式的(implicitly)传递给了它们。事实上,这意味着每个成员函数都是把类的指针当成它第一个参数的。即是,如果成员函数被声明成了__fastcall,类的指针就压到EAX中。但对于__cdecl或__stdcall的成员函数,它是堆栈上的第一个参数。让我们回上面看看指向类实例的指针是在哪儿保存的吧...完全正确!在dword_4A45F8保存着。XREF(查引用)4A45F8 我们可以发现许许多多对虚函数的调用。进一步的我们设置一个对4A45F8 的读写断点,然后跟踪复制对象地址的过程来看看有哪儿可以发现成员函数的动静。所有要做的事情在我们这个例子里都是

很简单的情况,因为它用了一个全局变量来保存对象指针了。但如果指针是用局部变量保存时,我们应该干什么呢,或者是代码无法执行起来的情况中如何做呢?我们现在需要一中特殊的方法。
//比方说你要分析一个delphi驱动程序了。pediy就有一个置顶帖是特地和delphi写驱动相关的来。
步步向前:
1)      我们需要发现所有访问到构造函数的地方。

接着对于每个调用

2)      跟踪着对象的指针的保存方向(局部变量)

3)      在调用了构造函数的那个函数里,从头到尾查找调用到了对象中成员函数的东西。

4)      要是没的话, 看下一个对构造函数调用的代码, 还是没有找到的情况下,就要XREF(查引用)那个我们找到的其他成员函数了. 透过这种方法的,我们可以不断找到新的成员函数. 同时,就如我们知道第一个函数参数是对象的指针, 我们可以去每个XREF到实例指针被引用的地方看看. 然后我们就沿着代码一层层的寻找, 直到我们碰到死角或者找到一个成员函数了.

5)      对每个找到的函数都阅读下功能然后标记出来

举下例子,我们找到了Classes::TList::Add。在XREF到它的一个地方,我们发现了:

CODE:0040F020 TThreadList::Add proc near              ; CODE XREF: TCanvas::`...'+9Ep

CODE:0040F020                                         ; Graphics::_16725+C4p

CODE:0040F020

CODE:0040F020 var_4           = dword ptr -4

CODE:0040F020

CODE:0040F020                 push    ebp

CODE:0040F021                 mov     ebp, esp

CODE:0040F023                 push    ecx

CODE:0040F024                 push    ebx

CODE:0040F025                 mov     ebx, edx

CODE:0040F027                 mov     [ebp+var_4], eax

CODE:0040F02A                 mov     eax, [ebp+var_4]

CODE:0040F02D                 call    TThreadList::LockList

CODE:0040F032                 xor     eax, eax

CODE:0040F034                 push    ebp

CODE:0040F035                 push    offset loc_40F073

CODE:0040F03A                 push    dword ptr fs:[eax]

CODE:0040F03D                 mov     fs:[eax], esp

CODE:0040F040                 mov     eax, [ebp+var_4]

CODE:0040F043                 mov     eax, [eax+4]

CODE:0040F046                 mov     edx, ebx

CODE:0040F048                 call    TList::IndexOf

CODE:0040F04D                 inc     eax

CODE:0040F04E                 jnz     short loc_40F05D

CODE:0040F050                 mov     eax, [ebp+var_4]

CODE:0040F053                 mov     eax, [eax+4]

CODE:0040F056                 mov     edx, ebx

CODE:0040F058                 call    Classes::TList::Add(void *)

即,我们发现了一个叫做TList::IndexOf 的成员函数。
更需要注意到是,我们知道我们是在TthreadList类的成员函数里面,而且它有一个TList类型的成员变量。所以这儿没什么感兴趣的。我们先假设那儿没XREF到Classes::TList::Add的地方了。接下来我们找找看TList::IndexOf 成员函数的XREF。其中一个直接把我们引到这:

CODE:0040EE38 TList::Remove   proc near               ; CODE XREF: TThreadList::Remove+28p


CODE:0040EE38                                         ; TCollection::RemoveItem+Bp ...

CODE:0040EE38                 push    ebx

CODE:0040EE39                 push    esi

CODE:0040EE3A                 mov     ebx, eax

CODE:0040EE3C                 mov     eax, ebx

CODE:0040EE3E                 call    TList::IndexOf

CODE:0040EE43                 mov     esi, eax

CODE:0040EE45                 cmp     esi, 0FFFFFFFFh

CODE:0040EE48                 jz      short loc_40EE53

CODE:0040EE4A                 mov     edx, esi

CODE:0040EE4C                 mov     eax, ebx

CODE:0040EE4E                 call    TList::Delete

CODE:0040EE53

CODE:0040EE53 loc_40EE53:                             ; CODE XREF: TList::Remove+10j

CODE:0040EE53                 mov     eax, esi

CODE:0040EE55                 pop     esi

CODE:0040EE56                 pop     ebx

CODE:0040EE57                 retn

CODE:0040EE57 TList::Remove   endp

嗯,TList::Delete 和TList::Remove 被找到了
而且等等等等其他所有的XREF和包含了对象指针的变量都要这样查找一番。
这儿是一个透过变量来查找的例子:

CODE:0041319D mov     eax, [ebp+var_4]

CODE:004131A0 mov     edx, ds:pTList

CODE:004131A6 mov     [eax+30h], edx  ;a pointer to the instance of an object is being copied

CODE:004131A9 jmp     short loc_4131BD

我们在其下看到:

CODE:00413236 mov     eax, [eax+30h]

CODE:00413239 mov     edx, [ebp+var_10]

CODE:0041323C call    TList::Get


哈。对了,我们有没有标记函数的public和pravate呢?
我们可以试着那样做,但是在所有的成员函数都找到了之后那样做(//一般情况下,你找不到的)。private成员函数是只在其他成员函数里调用到的,具体是,我们分析完成之后看看对它的XREF才能确定到这一点。
找成员函数的时候,我们首先按照顺序把它们按数字命名。就是说,你找到了成员函数,然后你把它改名作Object1::Method1, Object1::Method2等等,然后等所有的成员函数都找全了,你也许就又开始给成员变量写数字名字了。
//我发自内心的几句话,如果是100字节以下的类,你应该这样做,虽然挑三拣四的分析可以让你有机会突然跑到需要找的地址,
//但理清类的形状和结构会让你的工作效率翻10倍,这真的是大牛2天分析完成,而你10天才分析完成的区别所在。
//但是100字节以上,比方我就跑到了一个30000字节的类,4个0没打错,几千个成员变量的巨类,
//里面有几十个类作为成员变量而不是指针来使用,别傻了,放弃吧。

4. 确定成员函数的参数数量

关于__cdecl和__stdcall,有一些特殊的事情提一下,你只需要看看关于它们ida找到了多少,然后把ida的参数数量减1 (那个1是类实例的指针,剩下的是其他的参数)。__fastcall的话,会有更多的复杂之处。首先我们要记着参数的传递顺序: EAX, EDX, ECX, stack.
//其实这儿我喜欢说 压栈顺序,更直白,但似乎不准确了。
分析开始于ida找到了多少个通过堆栈来传递的参数。如果有至少一个堆栈传递的参数,我们就可以把它加上3(3个寄存器+1个堆栈参数)。第一个寄存器参数被this指针占用了,我们就需要把数字再减掉1。最后算出来的结果,就是我们要知道的参数数量了。
如果堆栈上没有参数传递进来,我们得要在函数的开始位置看一看。delphi总是很努力得惯着其他函数对寄存器的使用,所以每个函数的开始都会以类似的复制EAX,EDX,ECX寄存器参数来开始:

mov esi, edx ; first parameter 
mov ebx, eax ; pThis 
mov edi, ecx ; second parameter 


参数数量就这样取决于有多少个寄存器被复制过去,我们可以依此明确的知道一个复制代表一个参数。
例子:

CODE:0040EBE0 TList::Get      proc near               ; CODE XREF: @GetClass+1Dp

CODE:0040EBE0                                         ; @UnRegisterModuleClasses+24p ...

CODE:0040EBE0

CODE:0040EBE0 var_4           = dword ptr -4

CODE:0040EBE0

CODE:0040EBE0                 push    ebp

CODE:0040EBE1                 mov     ebp, esp

CODE:0040EBE3                 push    0

CODE:0040EBE5                 push    ebx

CODE:0040EBE6                 push    esi

CODE:0040EBE7                 mov     esi, edx

CODE:0040EBE9                 mov     ebx, eax

CODE:0040EBEB                 xor     eax, eax


上面那函数有2个参数,一个要被用作pThis,所以TList:Get只有一个参数。

CODE:004198CC                 push    ebp

CODE:004198CD                 mov     ebp, esp

CODE:004198CF                 add     esp, 0FFFFFF8Ch

CODE:004198D2                 push    ebx

CODE:004198D3                 push    esi

CODE:004198D4                 push    edi

CODE:004198D5                 mov     [ebp+var_C], ecx

CODE:004198D8                 mov     [ebp+var_8], edx

CODE:004198DB                 mov     [ebp+var_4], eax

上面的有3个参数,一个要用作pThis,所以是总共有2个参数的函数。

我们应该再提醒一下你这个情况,当你在Delphi写恢复出来的代码的函数声明时候,在ida中写也一样,理所当然的,你把函数的参数都写下去的时候,要考虑到函数的this指针有没有被你写进去。


但逆向成员函数类型的最后一步,标明函数的参数类型这个东西啊,我们就留给你来决定了

/*译者补充一段,首先,这一段是我从名叫李战的delphi大师的书里面偷出来的,可以在这儿试读:
http://blog.csdn.net/starsky2006/article/details/5497082
但里面的东西也经过了我自己的亲身逆向的确认和测试,当然主要也是学习新思路和结构设计。虽然我对于delphi的整个结构仍是一团浆糊,但这几个基础的,还是很确定李战大师的文章是完全正确的 :P
上面的那个ClassInfo结构,其结构格式是:
struct ClassInfo
{
+0x00:SefPtr 指向虚方法表指针
+0x04:IntfTable 指向接口表指针
+0x08:AutoTable 指向自动化信息表的指针
+0x0c:InitTable 指向实例初始化表的指针
+0x10:TypeInfo 指向类项信息表的指针,指向一个PType类型的指针
+0x14:FieldTable 指向域定义表的指针(published field)
+0x18:MethodTable 指向方法定义表的指针(published method)
+0x1c:DynamicTable 指向动态方法表的指针
+0x20:ClassName 指向类名字符串的指针
+0x24:InstanceSize 指向对象实例的大小
+0x28:Parent 指向父类的指针
+0x2c:SafeCallException 以下都是虚拟方法指针
+0x30:AfterConstruction
+0x34:BeforeDestruction
+0x38:Dispatch
+0x3c:DefaultHandler
+0x40:NewInstance
+0x44:FreeInstance
+0x48:Destroy
+0x4c:VirtualFunction0 //虚函数0
+0x50:VirtualFunction1 //虚函数1
...
+0x54:VirtualFunctionn //虚函数n
+0x58:ClassNameStrAry //class类名,当然是pascal格式的字符串
};
其中delphi固定使用的结构大小有0x4C,也就是VTBL之前的0x4c字节。其含义李战大师的结构已经标明很清楚了,我只把自己逆向觉得挺重要的标出来。
在constructor中,首先调用System::ClassCreate,它其中会使用函数表的 地址- 0xc位置的类特例的InitInstance函数,在其中调用System::TObject::InitInstance来做必备的初始化实例,此时通常会调用System::__linkproc__ GetMem(int)来申请到对象占用的内存。
class的destructor,则是InitInstance成员函数之下的二个,
一个是class自身的类似于~Class析构函数(FuncTable-0x4),一般这儿都是调用System::__linkproc__ ClassDestroy(System::TObject *),在其中调用类自身的Destroy函数的。
一个具体特张于特定类,来释放对象的内容的Destroy函数(FuncTable-0x8),通常这个里面就是一步用System::__linkproc__ FreeMem(void *)来释放对象占用的内存。
因此,delphi里的class的析构(release),看起来总是类似这个样子的:
CALL DWORD PTR [EDX-4] ; System::TObject::~TObject(void)
在其中则会调用
CALL DWORD PTR [EDX-8] ; Destory
一个实例
CODE:00413BF4 THook_ClassInit dd offset FuncTable
CODE:00413BF8 dd 2 dup(0)
CODE:00413C00 dd 5 dup(0)
CODE:00413C14 ClassName_Ptr ; "THook"
CODE:00413C18 dd 18h
CODE:00413C1C dd offset _cls_System_TObject_SubClassInit
CODE:00413C20 System::TObject::SafeCallException(System::TObject *,void *) ;
CODE:00413C24 nullsub
CODE:00413C28 nullsub
CODE:00413C2C System::TObject::Dispatch(void *) ;
CODE:00413C30 nullsub
CODE:00413C34 Comctrls::TTreeNodes::GetFirstNode(void) ;
CODE:00413C38 Destructor_THook
CODE:00413C3C System::TObject::~TObject(void) ;
CODE:00413C40 FuncTable:
CODE:00413C40 ClassName_Ptr: db 5,'THook'
*/
2011-9-7 05:33
0
游客
登录 | 注册 方可回帖
返回
//