首页
社区
课程
招聘
[翻译]Delphi中类的逆向工程
发表于: 2004-12-9 18:37 15585

[翻译]Delphi中类的逆向工程

2004-12-9 18:37
15585

前段时间看到NB王(就是说nbw:D……喂,nbw你不生气吧?)转的文章,觉得很有用。于是想翻译一下。无奈本人太懒,拖到现在。不过总算在今天晚饭以前赶完了:D

没有完全按照原文翻译,有些地方是意译的。大家凑合看吧。原贴地址是http://www.apriorit.com/our-articles/classes-restoration.html

注意:原文版权归原文作者所有,译文版权由译者所有。请尊重译者的劳动,不得转贴!但得到译者明确同意的情况除外!

==============================================================

类的逆向工程

译者:firstrose

类的逆向工程是一项需要OOP相关知识以及特定编译器如何处理OOP部分的知识的复杂工作。

我们的任务是得到类、方法和成员。由于在用Delphi编写的程序里查找类相对容易,这里就用Delphi做示范。

类的逆向先要从查找构造函数开始。因为类在这里被分配内存,而且我们可以从中得到构造函数的一些信息。

在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'

    我们把这个结构称为“object descriptor”,即“对象描述符”。指向它的指针
被传递给构造函数,构造函数则从中取得创建对象所需要的数据。通过查找对40D598
的交叉引用,可以得到对构造函数的所有调用。

    下面是其中的一个:

CODE:0040E72E                 mov     eax, ds:TList
CODE:0040E733                 call    CreateClass
CODE:0040E738                 mov     ds:dword_4A45F8, eax

这里的构造函数的名字是我们自己起的。通过查看函数,我们可以知道它是否真的是一个构造函数(CreateClass)

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结构(也就是TList_VTBL)的地址放到EAX里。

由于我们使用的是Delphi,可以看到,这里使用了Borland的fastcall调用模式(参数按照以下次序传递:EAX,ECX,EDX和堆栈)。这意味着,指向虚方法表的指针是作为CreateClass的第一个参数传递的。此外,EAX在__linkproc__ClassCreate里没有改变。我们可以看到:

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

它调用了什么呢?指向TList_VTBL=0х40D5D8的指针依然在EAX里,即

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的指针。并没有做什么特别的工作。

然后CreateClass就结束了。EAX中返回了指向刚刚创建的对象的指针。

这样,对构造函数的调用看起来就象下面这样:

CODE:0040E72E                 mov     eax, ds:TList
CODE:0040E733                 call    CreateClass
CODE:0040E738                 mov     ds:dword_4A45F8, eax

分析对象的结构

我们现在知道对象所占内存的大小是0x10,其中的4个字节是VTBL的指针。但是还剩下0xC个包含了对象成员的字节,我们必须找出它们。这里就有点直觉的成分了。首先,对象从来不会无缘无故被创建。对象的成员,或者由构造函数赋值(可能是全部,也可能是一部分),或者由相应的设置方法来赋值。

由于TList在构造函数里被以0填充(具体在TObject::InitInstance中),在构造函数里就找不到类成员的有关信息。Thus let’s trace life cycle after the creation.

在本例中,指向对象实例的指针被放在全局变量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 ;可以看作是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

好了,最后的3个成员找到了。它们都是4字节长。

要使用IDA分析类的工作变得简单一点,可以使用结构体功能。实际上,类和结构是一样的:)))
用了下面的结构定义以后:
00000000 TList_obj struc ; (大小=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

考虑到VBTL的结构,很容易想到:

CODE:0040EA3B call    dword ptr [edx]

就是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是TList中元素的计数器。因为增加一个元素时,它也被加一。Property1是指向元素数组的指针。Property 2可以看作是数组的索引。而Property 3则是一个list里最多允许的元素数目。此外,只有当Property2等于Property3时,TList::Grow被调用。通过逻辑推理,我们知道了这些。现在,一切都清楚起来了。顺便看看帮助文档,给这些成员命名吧:

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

对象的结构已经分析好了,下面是对象成员。

查找对象方法

对象的方法可以是以下几种:公开/私有(保护),虚方法/非虚方法以及静态方法.

由于编译后的静态方法和普通的过程没有什么区别,所以静态方法是无法被识别的。这些函数和某个特定的类之间的关系也是无法确定的。但是,应该指出的是,如果某个静态方法在类的方法里被调用,那么,它是可见的。否则寻找静态方法的企图只是在浪费时间。

虚方法很容易找到――它们都位于VTBL里。但是我们应该如何查找一般的方法呢?想想OOP:当对象方法被调用时,指向对象本身的指针被隐含地传递给该方法。实际上,这就意味着每个方法的第一个参数就是指向对象的指针。也就是说,如果该方法被声明为fastcall类型,指向对象的指针是放在EAX里的。而对于cdecl或stdcall类型的方法,首个参数是放在堆栈里的。让我们来看看指向对象的指针被放在什么地方……好!在dword_4A45F8里。通过查找对4A45F8的交叉引用,我们可以找到很多非虚拟方法。我们还可以在4A45F8下一个断点,追踪对对象实例指针的复制以找出余下的方法。

在本例中,由于使用了全局变量,一切都很容易。但是如果使用的是局部变量或者代码无法被执行(比如说,一个驱动程序。或者该代码不允许被执行),又应该怎么做呢?这就需要一个特别的办法。

一步一步来:
1)首先要找到所有调用构造函数的地方。
对每个调用重复以下步骤
2)跟去看看指向当前对象实例的指针被写到哪里了。
3)把所有调用了构造函数的函数作为对象方法。
4)如果没有这样的函数调用,就看构造函数下面的一个调用。否则就查看所有对已经找到的方法的交叉引用。这样就可以找到不在构造函数附近的调用。由于我们已经知道方法的首个参数是指向对象本身的指针,于是就可以查找对象指针的交叉引用。用这样的方法,我们可以一层一层地分析代码,直到出现僵局或者找到对象方法。
5)分析下一个已经找到的方法。

例如,我们已经找到了Classes::TList::Add,而且也找到了对Classes::TList::Add的一个引用:

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是它的成员之一。这里没有什么可以看的东西。假定一下,没有其他对Classes::TList::Add的引用。进到TList::IndexOf方法并且查看对它的引用。下面是其中的一个:

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就有了。

下面就是所有对象指针的交叉引用和相关变量。

这里是查找变量的例子:
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:00413236 mov     eax, [eax+30h]
CODE:00413239 mov     edx, [ebp+var_10]
CODE:0041323C call    TList::Get

如何分辨公开方法和私有方法呢?
只有当所有的方法全部找到以后才可以做这件事。私有方法只有在其它方法里才有调用。就是说,必须查看交叉引用了。查找方法以前,建议先把它们编号。也即把你找到的方法依次命名为Object1::Method1,Object1::Method2……所有的方法全部出来以后,就可以开始分析它们的参数(主要是个数和类型)了。

确定方法参数的个数

关于cdecl和stdcall几乎没有什么可说的。只要把IDA找到的参数个数减去1就可以了(还记得吗?第一个参数是对象指针,其它的才是真正的参数)。

fastcall要复杂点儿。首先我们要记住参数的次序:EAX,EDX,ECX,堆栈。首先要看看IDA找到了几个通过堆栈传递的参数。如果至少有一个,那么参数的个数要加3(3个寄存器参数加上堆栈参数)。由于第一个参数是对象指针This,这个数目还要减去1才是真正的参数个数。如果没有堆栈参数的话,就要看看函数的开头了。由于Delphi试图不去搅乱寄存器的值,结果每个fastcall函数的开头都要保存EAX,EDX和ECX:

mov esi, edx ; 第一个参数
mov ebx, eax ; This指针
mov edi, ecx ; 第二个参数

根据被复制的寄存器个数就可以判断出参数的个数。比如:

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个参数,其中一个是This指针。那么TList::Get有1个参数。

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个参数,其中一个是This指针。那么真正的参数是2个。
值得指出的是,由于我们是在用IDA分析Delphi程序,基于上面的原因,写函数头时一定要考虑到对象指针This。

参数的类型就要靠你去分析了。
================================================================


[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

收藏
免费 7
支持
分享
最新回复 (16)
雪    币: 133
活跃值: (22)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
好!:D
2004-12-9 18:52
0
雪    币: 332
活跃值: (479)
能力值: ( LV9,RANK:330 )
在线值:
发帖
回帖
粉丝
3
好好啊
2004-12-9 18:59
0
雪    币: 47147
活跃值: (20460)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
4
firstrose 真不简单。
因你对Delphi逆向工程有基础,所以感觉意译比直译更好。
2004-12-9 19:11
0
雪    币: 390
活跃值: (707)
能力值: ( LV12,RANK:650 )
在线值:
发帖
回帖
粉丝
5
过奖了,其实还是有点烂。
因为想在吃饭前赶出来,所以几乎是赶着写了差不多一半。所以后面一半不是很妥当。

想看的最好对照原文。尤其是那个“特别方法”

不灌了,吃去也……
2004-12-9 19:33
0
雪    币: 3686
活跃值: (1036)
能力值: (RANK:760 )
在线值:
发帖
回帖
粉丝
6
:D
2004-12-9 21:13
0
雪    币: 332
活跃值: (479)
能力值: ( LV9,RANK:330 )
在线值:
发帖
回帖
粉丝
7
firstrose 再灌2贴 ,你就 中级了,恭喜!!!
2004-12-9 21:33
0
雪    币: 339
活跃值: (1510)
能力值: ( LV13,RANK:970 )
在线值:
发帖
回帖
粉丝
8
刚刚看到。本来打算翻译一下,水平太烂,没想到兄弟一出手就搞定了 :D   这篇文章在老外论坛被转载多次,的确有些含量,可惜原创是为了商业广告,有些不爽。。
2004-12-10 14:16
0
雪    币: 390
活跃值: (707)
能力值: ( LV12,RANK:650 )
在线值:
发帖
回帖
粉丝
9
最初由 nbw 发布
刚刚看到。本来打算翻译一下,水平太烂,没想到兄弟一出手就搞定了 :D 这篇文章在老外论坛被转载多次,的确有些含量,可惜原创是为了商业广告,有些不爽。。


不好意思。其实我也是断断续续翻了一个月(真翻的话3天就可以了)。

说实话,这文章技术含量是不错,但感觉拿来做破解不是很实用。主要是操作起来花时间长,麻烦。
2004-12-10 15:27
0
雪    币: 390
活跃值: (707)
能力值: ( LV12,RANK:650 )
在线值:
发帖
回帖
粉丝
10
最初由 laoqian 发布
firstrose 再灌2贴 ,你就 中级了,恭喜!!!


多谢。没想到那么快……前些时间还为这个去建议开水区的
2004-12-10 15:29
0
雪    币: 339
活跃值: (1510)
能力值: ( LV13,RANK:970 )
在线值:
发帖
回帖
粉丝
11
原来站点还有一篇逆向教程,可惜我的资料丢了,找不到那个连接了
2004-12-10 17:44
0
雪    币: 61
活跃值: (160)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
12
强烈支持先!!!
2004-12-16 09:47
0
雪    币: 260
活跃值: (81)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
13
看雪论坛真的是人才济济!
2005-1-24 01:09
0
雪    币: 201
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
正在学习delphi 谢谢楼主
2008-11-9 08:54
0
雪    币: 563
活跃值: (95)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
好文章啊支持下
2009-3-17 23:08
0
雪    币: 201
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
向楼主学习,膜拜
2009-6-11 08:47
0
雪    币: 256
活跃值: (11)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
17
楼主多讲讲DELPH的逆向啊,特别是如何用IDC来注释出VCL中的类函数
2010-1-11 14:20
0
游客
登录 | 注册 方可回帖
返回
//