首页
社区
课程
招聘
[讨论]如何在反汇编代码中识别C++类及其虚函数表?
发表于: 2006-4-1 22:14 5899

[讨论]如何在反汇编代码中识别C++类及其虚函数表?

2006-4-1 22:14
5899
我现在知道的办法,是首先要找构造函数,一般在构造函数里会引用虚函数表.而构造函数现在比较容易的是查找new调用的后一个调用函数,如果是在堆中分配的类实例,就不知道怎么办了.而且这样子找到若干个虚函数表之后如何识别这些类之间的继承关系呢?
另外,有没有什么可编程的办法(比如写idapro的IDC脚本或者Ollydbg的脚本来自动识别)来查找这些虚函数表呢?(一般的虚函数表应该都是在PE文件的idata段里吧)

不知道哪位大虾对这些有研究,给点主意吧,呵呵.

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 0
支持
分享
最新回复 (13)
雪    币: 1325
活跃值: (507)
能力值: ( LV12,RANK:450 )
在线值:
发帖
回帖
粉丝
2
哦,刚看了一下,VTBL主要是在rdata段中.一会再研究一下看有没有什么规律,直接从rdata段开始在idapro中手动浏览好象也不是特别费劲.
2006-4-1 22:28
0
雪    币: 257
活跃值: (11)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
3
不大清楚,刚才逆向了VC的Dialog看看,如果要找窗口的CDialog类的话,只要在IDA的字符串中搜索 "CDialog",然后取两次引用,就会看到这样的表

.rdata:100356F0 CDlgBasic       dd offset ClassName       ; DATA XREF: sub_10001700+2Do
.rdata:100356F4                 dd offset sub_10001750
.rdata:100356F8                 dd offset nullsub_11
.rdata:100356FC                 dd offset sub_1002C945
.rdata:10035700                 dd offset sub_1002DED0
.rdata:10035704                 dd offset sub_1002C5C4
.rdata:10035708                 dd offset sub_1002ECC4
.rdata:1003570C                 dd offset sub_1002C5CA
.rdata:10035710                 dd offset sub_1002C5CA
.rdata:10035714                 dd offset sub_1002C5CD
.rdata:10035718                 dd offset sub_10001800
.rdata:1003571C                 dd offset sub_1002C653
.rdata:10035720                 dd offset sub_1002C605
.rdata:10035724                 dd offset sub_1002C64D
.rdata:10035728                 dd offset sub_1002C611
.rdata:1003572C                 dd offset sub_1002C60B
.rdata:10035730                 dd offset sub_1002C649
.rdata:10035734                 dd offset sub_1002ECC4
.rdata:10035738                 dd offset sub_1002ECC4
.rdata:1003573C                 dd offset sub_1002ECC4
.rdata:10035740                 dd offset nullsub_4
.rdata:10035744                 dd offset sub_1002DCD7
.rdata:10035748                 dd offset sub_1002DEE1
.rdata:1003574C                 dd offset sub_1002DCB7
.rdata:10035750                 dd offset sub_1002ECC9
.rdata:10035754                 dd offset sub_1002DFEF
.rdata:10035758                 dd offset sub_1002ECC4
.rdata:1003575C                 dd offset sub_1002E2D4
.rdata:10035760                 dd offset sub_1002F81B
.rdata:10035764                 dd offset sub_1002F822
.rdata:10035768                 dd offset sub_1002E9DC
.rdata:1003576C                 dd offset sub_1002EA68
.rdata:10035770                 dd offset sub_1002DF75
.rdata:10035774                 dd offset sub_100017A0
.rdata:10035778                 dd offset sub_100016E0
.rdata:1003577C                 dd offset sub_100016F0
.rdata:10035780                 dd offset sub_1002C8AC
.rdata:10035784                 dd offset sub_1002FC26
.rdata:10035788                 dd offset sub_1002E3FC
.rdata:1003578C                 dd offset sub_1002E440
.rdata:10035790                 dd offset sub_1002DF2E
.rdata:10035794                 dd offset nullsub_4
.rdata:10035798                 dd offset sub_1002EE07
.rdata:1003579C                 dd offset sub_1002D0BB
.rdata:100357A0                 dd offset sub_1002C5CA
.rdata:100357A4                 dd offset sub_1002CCEE
.rdata:100357A8                 dd offset sub_1002CDB0
.rdata:100357AC                 dd offset OnInitDialog
.rdata:100357B0                 dd offset nullsub_12
.rdata:100357B4                 dd offset sub_1002D09A
.rdata:100357B8                 dd offset sub_1002D0B3

ClassName 就是指向CDialog的类名,下边大多数是CDialog类继承来的成员,自己写的成员函数一般在该表的最下边
2006-4-2 01:47
0
雪    币: 1325
活跃值: (507)
能力值: ( LV12,RANK:450 )
在线值:
发帖
回帖
粉丝
4
这样可以通过类名来查找的应该是加了RTTI编译的吧,一般的程序好象加RTTI信息的不多.
2006-4-3 13:03
0
雪    币: 1325
活跃值: (507)
能力值: ( LV12,RANK:450 )
在线值:
发帖
回帖
粉丝
5
我写了一个IDC脚本,用于将rdata段中可能的vtbl写到一个文件中,每个vtbl的开始以@标注,脚本内容如下:(idapro4.9的SegByName函数有BUG,或者是根本没实现,返回总是0xffffffff,只好遍历搜索了)

auto s,a,e,fp,i,name,lastIsNull;
a=FirstSeg();
s=SegName(a);
while(s!=".rdata")
{
        a=NextSeg(a);
        s=SegName(a);
}
e=NextSeg(a);
Message("找到.rdata段:%8.8X-%8.8X,现在开始将其中可能的VTBL写入文件...\r\n",a,e);
lastIsNull=1;
fp=fopen("c:\\vtbl.txt","w");
for(i=a;i<e;)
{
        s=GetDisasm(i);
        if(strstr(s,"dd offset")>=0)
        {
                name=Name(i);
                if(name!="")
                {
                        writestr(fp,form("@ %s\r\n",s));
                }
                else
                {
                        writestr(fp,form("  %s\r\n",s));
                }
                lastIsNull=0;
        }
        else if(!lastIsNull)
        {
                writestr(fp,"\r\n");
                lastIsNull=1;
        }
        i=FindData(i,1);       
}
fclose(fp);
Message("文件构造完毕!\r\n");

这样子先截出VTBL来了,然后再想办法分析继承关系了,我现在的想法是比较各个VTBL的函数地址,然后按vtbl长度排序,有公共函数的vtbl可认为是一个簇的类,vtbl尺寸越小的越可能是基类,同样尺寸的按函数被引用次数递减排序,引用次数多的是基类的可能性大.这个工作手工做有点费劲,得再写脚本了(不过idc脚本的功能有点弱,得换别的脚本做这些分析了).
2006-4-3 15:18
0
雪    币: 260
活跃值: (259)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
6
《Hacker.Disassembling》
好像对面向对象的程序反汇编有比较深入的研究
2006-4-3 19:46
0
雪    币: 257
活跃值: (11)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
7
好象这句很特别
mov     dword ptr [esi], offset CDlgBasic
我看VC的类都是这样放进去的

“.rdata:100356F0 CDlgBasic       dd offset ClassName       ; DATA XREF: sub_10001700+2Do
.rdata:100356F4                 dd offset sub_10001750
.rdata:100356F8                 dd offset nullsub_11
.rdata:100356FC                 dd offset sub_1002C945”
2006-4-3 20:04
0
雪    币: 1325
活跃值: (507)
能力值: ( LV12,RANK:450 )
在线值:
发帖
回帖
粉丝
8
呵呵,突然想起MFC从VC4.0起不再使用RTTI了,它使用的CRuntimeClass的第一个成员就是指向类名的指针.
不过你列出的列表与使用RTTI的C++类的虚函数表布局特别相似,不知道是怎么回事了.
2006-4-3 20:47
0
雪    币: 1325
活跃值: (507)
能力值: ( LV12,RANK:450 )
在线值:
发帖
回帖
粉丝
9
《Hacker.Disassembling》
这书在哪儿能找到?

刚从网上搜到了,谢谢了.
2006-4-3 20:54
0
雪    币: 257
活跃值: (11)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
10
最初由 dreaman 发布
呵呵,突然想起MFC从VC4.0起不再使用RTTI了,它使用的CRuntimeClass的第一个成员就是指向类名的指针.
不过你列出的列表与使用RTTI的C++类的虚函数表布局特别相似,不知道是怎么回事了.


追述到CDialog的祖宗类CObject,第一个成员CRuntimeClass指针就是指向类名。 我帖出的那段是用VC6建立的Dialog based窗口,逆向出来后看到就是这样了,列表下边大多数是CDialog类继承来的成员,一路一路的排下来,如果再在此CDialog类中添加虚函数的话,就会继续往那列表下边加偏移地址
2006-4-3 21:10
0
雪    币: 1325
活跃值: (507)
能力值: ( LV12,RANK:450 )
在线值:
发帖
回帖
粉丝
11
如果不是CObject继承的类,应该就没有类名这样的信息了.那样就不太好分析了.

(晕倒,我今天的发贴数居然满了,只能在这个贴里发了.)
2006-4-3 21:13
0
雪    币: 1325
活跃值: (507)
能力值: ( LV12,RANK:450 )
在线值:
发帖
回帖
粉丝
12
昨天研究了一下idapro 4.9的SDK,没想到IDC脚本的内部函数可以很方便的直接调用,所以就顺便写了一个用DHTML作脚本的idapro 4.9插件IdaHTML,直接将IDC脚本的内部函数用作DHTML的API了,之后我重写了一下取VTBL的代码,这回是用javascript,写完才发觉,IDC的语法与javascript咋就那么像呢:)(c系语言真的太相似了!)

var s,a,e,fp,i,name,lastIsNull;
a=app.FirstSeg();
s=app.SegName(a);
while(s!=".rdata")
{
        a=app.NextSeg(a);
        s=app.SegName(a);
}
e=app.NextSeg(a);
app.Message("找到.rdata段:%8.8X-%8.8X,现在开始将其中可能的VTBL写入文件...\r\n",a,e);
lastIsNull=1;
fp=app.fopen("c:\\vtbl.txt","w");
for(i=a;i<e;)
{       
        s=app.GetDisasm(i);
        if(app.strstr(s,"dd offset")>=0)
        {
                //取出VTBL中的一项,查看是否函数
                var v=app.Dword(i);
                var f=app.GetFlags(v);
                //查看是否有名称,有名称的表明有被引用,可能是VTBL的开始
                name=app.Name(i);
                if(typeof(name)=="string" && name!="")//如果是RTTI或MFC动态类,则第一项可能不是函数
                {
                        app.writestr(fp,app.form("@ %s\r\n",s));
                        lastIsNull=0;
                }
                else if((f& MS_CLS)==FF_CODE)
                {
                        app.writestr(fp,app.form("  %s\r\n",s));
                        lastIsNull=0;
                }
                else if(!lastIsNull)
                {
                        app.writestr(fp,"\r\n");
                        lastIsNull=1;
                }
        }
        else if(!lastIsNull)
        {
                app.writestr(fp,"\r\n");
                lastIsNull=1;
        }
        i=app.ItemEnd(i);/*app.FindData(i,1);        */
}
app.fclose(fp);
app.Message("文件构造完毕!\r\n");

修改了一下,有BUG.
2006-4-4 11:20
0
雪    币: 1325
活跃值: (507)
能力值: ( LV12,RANK:450 )
在线值:
发帖
回帖
粉丝
13
晕倒,今天的发贴机会又用完了.

呵呵,接下来的打算:
1、基于VTBL写分析类簇的脚本;
2、分析各个虚函数间的交叉引用;
3、绘制虚函数间的调用关系图;
4、绘制类的继承关系图。

巨晕!在分析函数间调用关系时idapro不响应了,突然意识到函数太多了,看了一下列表,4500多个函数,如果每个函数200行汇编码,则需要检查4500*200次,每次检查需要调五六个API函数外加一次5以内循环,性能问题严重了。
2006-4-4 16:03
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
有两种情况:
1\当程序new一个对象时,在调用虚函数时,一般在ECX中传递一个对象的指针,然后出现call [ecx+xxx]时,就是在调用某个虚函数了.
2\当程序在堆栈中建立对象时,对象就是一个局部变量,在CALL这种对象的虚函数之前,一定会LEA到ECX这个局部变量.
2006-4-4 19:32
0
游客
登录 | 注册 方可回帖
返回
//