首页
社区
课程
招聘
[原创]识别c++ RTTI的ida脚本
发表于: 2008-3-25 00:37 16573

[原创]识别c++ RTTI的ida脚本

2008-3-25 00:37
16573

大型的c++程序, 往往都开启了RTTI, 利用此脚本能很方便的识别类名, 极大的提高分析效率.

背景:
RTTI(RuniTime TypeInfo)是c++的一种运行时类型识别机制
当一个类的引用或者指针的值去进行“类型识别",  叫做动态识别
当一个类的实例去进行"类型识别", 叫做静态识别
动态识别需要编译打开了/GR开关才可有效, 否则会引起一个运行时错误, 而静态的不需要
对于大型工程而言, 静态识别并没有多少意义
实现原理:
这里假设你对类的虚表非常熟悉, 如果一个类存在虚函数, 或者他的基类存在虚函数,
或者virtual继承了其他类,或者开启了RTTI, 那么编译器会为该类生成一个虚表,
该表是一个该类需要虚实现的函数的指针表,一般类的头4个字节为该表的指针
(当基类是多重继承的时候, 非第一个继承的基类的虚表不是在头4字节)
当有RTTI在的时候,  虚表-4的位置是一个叫做RTTI Complete Object Locator的描述体
该描述体+c的位置是一个叫做RTTI Type Descriptor的描述体, 该描述体+8的位置是一段
原始类名的buf, 我们只要得到了该buf, 就能得到类的名字.

实现:
RTTI Type Descriptor这个描述体在c++中叫做type_info, 他的定义如下
class type_info {
public:
    _CRTIMP virtual ~type_info();
    _CRTIMP int operator==(const type_info& rhs) const;
    _CRTIMP int operator!=(const type_info& rhs) const;
    _CRTIMP int before(const type_info& rhs) const;
    _CRTIMP const char* name() const;
    _CRTIMP const char* raw_name() const;
private:
    void *_m_data;
    char _m_d_name[1];
    type_info(const type_info& rhs);
    type_info& operator=(const type_info& rhs);
};
typeid作为一个c++关键字, 给它传递一个任意类型的实例, 他能够返回一个type_info的引用。
假如有下列类的定义
Class Base {}
Class Derived : public Base {}
-------------------------
代码片一
Base instance;
typeid(instance);
c++采用静态识别, 对应的伪汇编代码为
mov ecx,  offset Base_type_info
-------------------------
代码片二
Base instance;
Base &rinstance  = instance;
typeid(rinstance);
c++采用动态识别, 对应的伪汇编代码为
mov ecx, rinstance_vtbl
call ___RTtypeid
mov ecx, eax
-------------------------
代码片三
该代码对于大型工程经常使用, 目的就是为了知道传递的类型是不是需要的类型
达到 RTTI 的类型识别的初衷
bool IsBaseClass(Base &base)
{
   return typeid(base) == typeid(Base);
}
代码的实质其实就是strcmp前面说到的buf(type_info中的_m_d_name)是不是和Base的_m_d_name相等
因为代码片三的存在, 所以让我们利用这个ida脚本反c++类名的可能性存在。
--------------------------

前面所说, buf(_m_d_name)其实是一个c++类名的内部表达, 比如我们的类Base的_m_data其实
显示为.?AVBase@@, 则么还原成我们认识的 class Base这样的形式呢?
参考下列代码
printf("%s", typeid(base).name());
该函数能正确的显示出class Base, 所以, 我们如果有了raw_name(前文的buf, 又type_info的_m_d_name),
可以通过type_info的 const char *name() const函数来实现
现在怎么调用这个函数? 这里采用常用的欺骗方法, 构造一个假的type_info *, 然后去访问他
代码如下
struct _fake_typeinfo
{
        void *vtbl;
        void *name;
        char raw_name[1024];
        _fake_typeinfo(const char *rname)
                : vtbl(NULL), name(NULL)
        {
                strcpy(raw_name, rname);
        }
};
_fake_typeinfo ti(".?AVBase@@);
type_info * pti = reinterpret_cast<type_info *>(&ti);
printf("%s", pti->name());
可以看到正确的显示出class Base了.

脚本:
ida无法利用c++的欺骗方法调用c++的内部库, 但是脚本支持2个指令exec, readstr,
可以认为他是idc跟外界打交道的两个途径.
当需要吧一个raw_name转化成一个name的时候, idc先执行一个外部程序, 把raw_name作为commandline传递
该外部程序把name生成一个文件, 然后idc读取该文件的第一行来得到返回值.
该脚本需要光标在在有RTTI的工程的类的虚表的-4的位置(即RTTI Complete Object Locator所在的位置)执行,
并且存在一个c:\111.exe用来翻译name
执行后会给该RTTI Complete Object Locator备注上类的显示名字

// begin copy here
// filename: GetCppRTTI.idc
// author: jjnet

#include <idc.idc>

static EnsureMakeDword(ea)
{
                MakeUnknown(ea, 4, DOUNK_SIMPLE);
                MakeDword(ea);
}
static EnsureMakeStr(ea)
{
  auto ea_end;
  ea_end = ea;
  while(Byte(ea_end)!=0 && ea_end-ea<=255) ++ea_end;
  ++ea_end; // append tial zero.
  MakeUnknown(ea, ea_end-ea, DOUNK_SIMPLE);
  MakeStr(ea, ea_end);
}

static CommentRTTI(ea)
{
        auto ea1;
       
        ea1 = Dword(ea);
        EnsureMakeDword(ea1);
        EnsureMakeDword(ea1+4);
        EnsureMakeDword(ea1+8);
        EnsureMakeDword(ea1+12);
       
        ea1 = Dword(ea1+12);
        EnsureMakeDword(ea1);
        EnsureMakeDword(ea1+4);
        EnsureMakeStr(ea1+8);

        return GetString(ea1+8, BADADDR, ASCSTR_C);
}

static main()
{
  auto str_type, raw_name, fp, name;
  str_type = GetLongPrm(INF_STRTYPE);
  SetLongPrm(INF_STRTYPE, ASCSTR_C);
  
  raw_name = CommentRTTI(ScreenEA());
        Exec("C:\\111.exe " + raw_name);
        fp = fopen("c:\\111.txt", "r");
        name = readstr(fp);
        fclose(fp);
        MakeRptCmt(ScreenEA(), name=="" ? raw_name : name);
       
        SetLongPrm(INF_STRTYPE, str_type);
}
// end copy

// 123.exe的原码

#include <stdlib.h>
#include <stdio.h>

struct _fake_typeinfo
{
        void *vtbl;
        void *name;
        char raw_name[1024];
        _fake_typeinfo(const char *rname)
                : vtbl(NULL), name(NULL)
        {
                strcpy(raw_name, rname);
        }
};

int _tmain(int argc, _TCHAR* argv[])
{       
        FILE *fp = fopen("C:\\111.txt", "w");
        if (argc != 2)  { fclose(fp); return -1; }
        type_info *p_tinfo = reinterpret_cast<type_info *>(new _fake_typeinfo(argv[1]));
        fprintf(fp, "%s", p_tinfo->name());
        fclose(fp);               
        return 0;
}


[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 7
支持
分享
最新回复 (7)
雪    币: 6075
活跃值: (2236)
能力值: (RANK:1060 )
在线值:
发帖
回帖
粉丝
2
ida作者也写了一个
2008-3-25 01:10
0
雪    币: 189
活跃值: (152)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
thj
3
不错,思路清晰,学习了,谢谢。
2008-4-26 11:12
0
雪    币: 200
能力值: (RANK:10 )
在线值:
发帖
回帖
粉丝
PON
4
支持idc脚本。
2008-4-26 17:40
0
雪    币: 334
活跃值: (22)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
5
8错8错,ding...
2008-4-26 17:58
0
雪    币: 87
活跃值: (47)
能力值: ( LV12,RANK:250 )
在线值:
发帖
回帖
粉丝
6
support.
希望以后能看懂
2008-8-11 18:12
0
雪    币: 101
活跃值: (12)
能力值: ( LV12,RANK:210 )
在线值:
发帖
回帖
粉丝
7
// 加了点东西, 更容易知道基类拥有的派生类

// author jjnet

#include <idc.idc>

static EnsureMakeDword(ea)
{
                MakeUnknown(ea, 4, DOUNK_SIMPLE);
                MakeDword(ea);
}
static EnsureMakeStr(ea)
{
  auto ea_end;
  ea_end = ea;
  while(Byte(ea_end)!=0 && ea_end-ea<=255) ++ea_end;
  ++ea_end; // append tial zero.
  MakeUnknown(ea, ea_end-ea, DOUNK_SIMPLE);
  MakeStr(ea, ea_end);
}

static CommentRTTI(ea)
{
        auto ea1, ea2, ea3, ea4, ea5, count;
        EnsureMakeDword(ea);
        ea1 = Dword(ea);
        EnsureMakeDword(ea1);
        EnsureMakeDword(ea1+4);
        EnsureMakeDword(ea1+8);
        EnsureMakeDword(ea1+12);
        EnsureMakeDword(ea1+16);
       
        ea2 = Dword(ea1+12);
        EnsureMakeDword(ea2);
        EnsureMakeDword(ea2+4);
        EnsureMakeStr(ea2+8);

        ea3 = Dword(ea1+16);
        EnsureMakeDword(ea3);
        EnsureMakeDword(ea3+4);
        EnsureMakeDword(ea3+8);
        EnsureMakeDword(ea3+12);
       
        count = Dword(ea3+8);
        ea4 = Dword(ea3+12);
        while(count>0)
        {
                EnsureMakeDword(ea4);
                ea5 = Dword(ea4);
                EnsureMakeDword(ea5);
                EnsureMakeDword(ea5+4);
                EnsureMakeDword(ea5+8);
                EnsureMakeDword(ea5+12);
                EnsureMakeDword(ea5+16);
                EnsureMakeDword(ea5+20);               
                ea4 = ea4 + 4;
                count = count -1;
        }
        EnsureMakeDword(ea4);

        return GetString(ea2+8, BADADDR, ASCSTR_C);
}

static main()
{
  auto str_type, raw_name, fp, name;
  str_type = GetLongPrm(INF_STRTYPE);
  SetLongPrm(INF_STRTYPE, ASCSTR_C);
  
  raw_name = CommentRTTI(ScreenEA());
        Exec("C:\\111.exe " + raw_name);
        fp = fopen("c:\\111.txt", "r");
        name = readstr(fp);
        fclose(fp);
        MakeRptCmt(ScreenEA(), name=="" ? raw_name : name);
        ExtLinA(ScreenEA(), 0, "-----------------------------------------------------");
       
        SetLongPrm(INF_STRTYPE, str_type);
}
2008-9-20 18:09
0
雪    币: 204
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
太高深了!!!
2008-10-15 17:22
0
游客
登录 | 注册 方可回帖
返回
//