首页
社区
课程
招聘
[原创]逆向角度看C++部分特性
发表于: 2022-3-10 16:20 22087

[原创]逆向角度看C++部分特性

2022-3-10 16:20
22087

LOG日志
LOG日志

内存情况

可以看到实际上单继承就是把 baseClass 的成员变量完全copy了一份放在了我们childClass的前面

其实也都是成员变量按顺序往后排就完事

由上我们可以看到这两个Class的地址的开始位置都多了一个指针,指针后面的才是我们真实的结构体值,这第一个指针就是 vptr(虚函数指针),指向了虚函数表,然后再去读一下这个指针

此时打开IDA验证一下这前两个地址就是真实的函数地址
// IDA查看地址

同理我们去看看另一个childClass类也会得到类似的结果

第一二三个:明显就是对应的虚函数具体的函数地址
第四五个:应该是和 C++中的RTTI机制 相关

// IDA查看地址

简单归纳一下:

这里简单的提及了一下,更详细的关于虚函数的介绍可以查看 这篇文章

至于里面提到的关于 安全性 的反思:

// 全局视图

列举出以下的几种情况

// 由我们自己编写的拷贝构造函数

// 虚函数表

由此可见调用子类的拷贝构造函数会先调用父类的构造函数,然后在调用当前类的拷贝构造,这里的off_85600就是 vptr ,从虚函数表中也可以看见,子类覆盖了父类的虚函数就会指向子类的虚函数,若没有覆盖,表项中依旧是指向父类的函数地址,而且顺序是按照父类的虚函数表顺序排列,子类中父类没有的虚函数会按顺序继续排在后面,不同类的虚函数表其实都是在编译期就已经确定了的,不同类的虚函数表处于临近的内存区域

主要是针对 dynamic_cast 向下转型的情况

// 向下转型

// 效果图

最后两条日志可见,我们对同一对象调用 showLOG() 一个是父函数,一个是子函数,对应代码 815 和 819 行

引用传递和值传递

构造以及调用testFunction

testFunction汇编也可以明显看到被IDA识别为了lambda表达式

从这里我们可以看到虽然源码后面两个lambda表达式虽然没有捕获参数,但是依旧有一个栈地址的传递(可以理解为一个空 this)

传递栈上地址逐个相差一个指针长度

读取类成员变量日志

表达式位于类外无捕获参数

中间函数用来返回lambda函数真实的地址

中间跳板函数

lambda函数的实现,和普通函数没有啥差别

testB 日志

IDA反汇编

testC 日志

从 sub_319EC(v7, 3, 4) → testNoCatch(3, 4)
可以看出 [] 捕获的参数其实都在第一个参数,lambda的传参在 第二个参数往后,结合把lambda理解为一个重载 () 运算符的类也是自洽的

从 sub_31A4C(v6, 2) → testCatch(2);
再去对应看v6的参数,也就可以更加理解,lambda 表达式引用传递和值传递的区别,源码中的 c为一个类 (理解为→ 构造 : sub_3198C(v10, &v9); | 析构 : sub_31BB4(v10);),栈传参的时候源码中的引用传递放在最前面,其次按顺序传递参数

从 sub_3198C(v10, &v9); 和 sub_31BB4(v10)
sub_3198C(v10, &v9) → auto c = make_unique<int>(12);
sub_31BB4(v10) → 作用域结束,对unique指针的析构

从 上图 29 30 行可见:对带捕获参数的 lambda 表达式取地址得到的只是 匿名类(分配在栈上)的首地址,其实从栈的角度看也是待传参数数组的首地址

没有带捕获参数的 lambda 表达式 基本上可以等价于一个普通函数,函数地址通过 来获得(编译器针对表达式特殊处理的);带捕获参数的 lambda 表达式 不能使用 ,如果使用 & 只能获得该匿名类首地址,而且 匿名lambda类的构造函数可以理解为inline构造

lambda 表达式可以使用 [=] / [&] 捕获外部 值传递 / 引用传递,编译器只会把使用到的变量按照对应传递方式传递给匿名lambda类,没用到的变量不会被拷贝

由此上结论我们可以将 dobby hook 稍微封装一下

registerHook的重载第三个参数(Callback)本来是想用模板的但是好像不太行

得到一个类似于java函数回调一样的写法
这里srcCall可以用一个变长参数简写一下代码

“SUB SP, SP, #24” 4*6 总计开辟了 6 个位置,最下面的那个位置(fp-0x4) 用于存放栈检查的fp或者说成sp

正常情况下 class() 构造出来的类就在当前函数栈中,但是这里有一个特例:对于在 函数getTestClass 中 创建在栈上的class(tmp),实际上他真实存在的位置是在 函数testRetClass 的栈上 位置 {fp-0x8,fp-0x14},最顶上的那个位置(sp)是空的(在这里分配栈最小差值0x8),结合上述描述再去看地址 0x4F5FC 就是logd中的第三个参数 “tt.d”
ps:如果这里的 class 只有三个成员变量,这里的 “SUB SP, SP, #0x18” 将会变成 “SUB SP, SP, #0x10”,刚好用满栈的四个位置

对于地址 0x4F5E8 这里的这个函数调用就是对 类testClass 的初始化,传递了第一个地址(函数testRetClass栈地址)进去 对 int a,b,c,d 的初始化就放在 “LOGD("testClass");” 之前

模板类的实现

实现

该文章作为日常学习理解的记录,理解可能又不准确的地方,欢迎大佬们指出 0.0

#define MAIN __attribute__((constructor))
#define NOINLINE __attribute__((__noinline__))
 
class BaseClass{
public:
    int a,b;
    BaseClass(int mA=1,int mB=2,int mC=3,int mD=4){
        this->a = mA;
        this->b = mB;
        this->c = mC;
        this->d = mD;
    }
private:
    int c;
protected:
    int d;
};
 
class ChildClass: public BaseClass{
public:
    int m,n;
    ChildClass(int mM=5,int mN=6){
        this->m = mM;
        this->n = mN;
    }
};
 
MAIN
void test0(){
    auto* baseClass = new BaseClass();
    LOGD("baseClass   : %p sizeof: %d ",baseClass,sizeof(*baseClass));
 
    auto* child1 = new ChildClass(10,20);
    LOGD("child1  : %p sizeof: %d", child1, sizeof(*child1));
}
#define MAIN __attribute__((constructor))
#define NOINLINE __attribute__((__noinline__))
 
class BaseClass{
public:
    int a,b;
    BaseClass(int mA=1,int mB=2,int mC=3,int mD=4){
        this->a = mA;
        this->b = mB;
        this->c = mC;
        this->d = mD;
    }
private:
    int c;
protected:
    int d;
};
 
class ChildClass: public BaseClass{
public:
    int m,n;
    ChildClass(int mM=5,int mN=6){
        this->m = mM;
        this->n = mN;
    }
};
 
MAIN
void test0(){
    auto* baseClass = new BaseClass();
    LOGD("baseClass   : %p sizeof: %d ",baseClass,sizeof(*baseClass));
 
    auto* child1 = new ChildClass(10,20);
    LOGD("child1  : %p sizeof: %d", child1, sizeof(*child1));
}
 
 
// 新增一个BaseNewClass,让ChildClass:BaseClass继承这两个Class
class BaseNewClass{
public:
    int p,q;
    BaseNewClass(int mP=10,int mQ=11){
        this->p = mP;
        this->q = mQ;
    }
};
 
class ChildClass:BaseClass,BaseNewClass{
public:
    int m,n;
    ChildClass(int mM=5,int mN=6){
        this->m = mM;
        this->n = mN;
    }
};
// 新增一个BaseNewClass,让ChildClass:BaseClass继承这两个Class
class BaseNewClass{
public:
    int p,q;
    BaseNewClass(int mP=10,int mQ=11){
        this->p = mP;
        this->q = mQ;
    }
};
 
class ChildClass:BaseClass,BaseNewClass{
public:
    int m,n;
    ChildClass(int mM=5,int mN=6){
        this->m = mM;
        this->n = mN;
    }
};
// LOG()日志
D/ZZZ: baseClass   : 0xf216dd70 sizeof: 16
D/ZZZ: childClass  : 0xea17e280 sizeof: 32
 
// 内存情况
[Pixel XL::XXX]-> seeHexA(0xea17e280,32)
           0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F  0123456789ABCDEF
ea17e280  01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00  ................
ea17e290  0a 00 00 00 0b 00 00 00 05 00 00 00 06 00 00 00  ................
// LOG()日志
D/ZZZ: baseClass   : 0xf216dd70 sizeof: 16
D/ZZZ: childClass  : 0xea17e280 sizeof: 32
 
// 内存情况
[Pixel XL::XXX]-> seeHexA(0xea17e280,32)
           0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F  0123456789ABCDEF
ea17e280  01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00  ................
ea17e290  0a 00 00 00 0b 00 00 00 05 00 00 00 06 00 00 00  ................
// 测试源码
class BaseClass{
public:
    int a,b;
    BaseClass(int mA=1,int mB=2,int mC=3,int mD=4){
        this->a = mA;
        this->b = mB;
        this->c = mC;
        this->d = mD;
    }
    virtual void showLOG(){
        LOGD("Called BaseClass showLOG");
    }
    virtual void showLOG1(){
        LOGD("Called BaseClass showLOG1");
    }
private:
    int c;
protected:
    int d;
};
 
class ChildClass:BaseClass{
public:
    int m,n;
    ChildClass(int mM=5,int mN=6){
        this->m = mM;
        this->n = mN;
    }
    virtual void showLOG(){
        LOGD("Called ChildClass showLOG");
    }
    virtual void showLOG1(){
        LOGD("Called ChildClass showLOG1");
    }
    virtual void showLOG2(){
        LOGD("Called ChildClass showLOG2");
    }
};
 
MAIN
void test0(){
    auto* baseClass = new BaseClass();
    LOGD("baseClass   : %p sizeof: %d",baseClass,sizeof(*baseClass));
 
    auto* childClass = new ChildClass();
    LOGD("childClass  : %p sizeof: %d",childClass,sizeof(*childClass));
}
// 测试源码
class BaseClass{
public:
    int a,b;
    BaseClass(int mA=1,int mB=2,int mC=3,int mD=4){
        this->a = mA;
        this->b = mB;
        this->c = mC;
        this->d = mD;
    }
    virtual void showLOG(){
        LOGD("Called BaseClass showLOG");
    }
    virtual void showLOG1(){
        LOGD("Called BaseClass showLOG1");
    }
private:
    int c;
protected:
    int d;
};
 
class ChildClass:BaseClass{
public:
    int m,n;
    ChildClass(int mM=5,int mN=6){
        this->m = mM;
        this->n = mN;
    }
    virtual void showLOG(){
        LOGD("Called ChildClass showLOG");
    }
    virtual void showLOG1(){
        LOGD("Called ChildClass showLOG1");
    }
    virtual void showLOG2(){
        LOGD("Called ChildClass showLOG2");
    }
};
 
MAIN
void test0(){
    auto* baseClass = new BaseClass();
    LOGD("baseClass   : %p sizeof: %d",baseClass,sizeof(*baseClass));
 
    auto* childClass = new ChildClass();
    LOGD("childClass  : %p sizeof: %d",childClass,sizeof(*childClass));
}
// 日志
D/ZZZ: baseClass   : 0xe75a7648 sizeof: 20
D/ZZZ: childClass  : 0xe75d8d00 sizeof: 28
 
// 内存情况
[Pixel XL::XXX]-> seeHexA(0xe75a7648,20)
           0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F  0123456789ABCDEF
e75a7648  24 59 5f d2 01 00 00 00 02 00 00 00 03 00 00 00  $Y_.............
e75a7658  04 00 00 00                                      ....
[Pixel XL::XXX]-> seeHexA(0xe75d8d00,28)
           0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F  0123456789ABCDEF
e75d8d00  3c 59 5f d2 01 00 00 00 02 00 00 00 03 00 00 00  <Y_.............
e75d8d10  04 00 00 00 05 00 00 00 06 00 00 00              ............
// 日志
D/ZZZ: baseClass   : 0xe75a7648 sizeof: 20
D/ZZZ: childClass  : 0xe75d8d00 sizeof: 28
 
// 内存情况
[Pixel XL::XXX]-> seeHexA(0xe75a7648,20)
           0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F  0123456789ABCDEF
e75a7648  24 59 5f d2 01 00 00 00 02 00 00 00 03 00 00 00  $Y_.............
e75a7658  04 00 00 00                                      ....
[Pixel XL::XXX]-> seeHexA(0xe75d8d00,28)
           0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F  0123456789ABCDEF
e75d8d00  3c 59 5f d2 01 00 00 00 02 00 00 00 03 00 00 00  <Y_.............
e75d8d10  04 00 00 00 05 00 00 00 06 00 00 00              ............
//读取vptr指向的位置
[Pixel XL::XXX]-> seeHexA(ptr(0xe75a7648).readPointer(),0x20)
           0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F  0123456789ABCDEF
d25f5924  b1 fa 5a d2 c9 fa 5a d2 d4 60 5f d2 39 29 5f d2  ..Z...Z..`_.9)_.
d25f5934  00 00 00 00 48 59 5f d2 e1 fa 5a d2 f9 fa 5a d2  ....HY_...Z...Z.
 
[Pixel XL::XXX]-> Module.findBaseAddress("libdynamic.so")
"0xd2593000"
[Pixel XL::XXX]-> ptr(0xd25f5924).readPointer().sub(0xd2593000)
"0x1cab1"
[Pixel XL::XXX]-> ptr(0xd25f5928).readPointer().sub(0xd2593000)
"0x1cac9"
[Pixel XL::XXX]-> ptr(0xd25f592c).readPointer().sub(0xd2593000)
"0x630d4"
[Pixel XL::XXX]-> ptr(0xd25f5930).readPointer().sub(0xd2593000)
"0x5f939"
//读取vptr指向的位置
[Pixel XL::XXX]-> seeHexA(ptr(0xe75a7648).readPointer(),0x20)
           0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F  0123456789ABCDEF
d25f5924  b1 fa 5a d2 c9 fa 5a d2 d4 60 5f d2 39 29 5f d2  ..Z...Z..`_.9)_.
d25f5934  00 00 00 00 48 59 5f d2 e1 fa 5a d2 f9 fa 5a d2  ....HY_...Z...Z.
 
[Pixel XL::XXX]-> Module.findBaseAddress("libdynamic.so")
"0xd2593000"
[Pixel XL::XXX]-> ptr(0xd25f5924).readPointer().sub(0xd2593000)
"0x1cab1"
[Pixel XL::XXX]-> ptr(0xd25f5928).readPointer().sub(0xd2593000)
"0x1cac9"
[Pixel XL::XXX]-> ptr(0xd25f592c).readPointer().sub(0xd2593000)
"0x630d4"
[Pixel XL::XXX]-> ptr(0xd25f5930).readPointer().sub(0xd2593000)
"0x5f939"
 
//
[Pixel XL::XXX]-> seeHexA(ptr(0xe75d8d00).readPointer(),0x20)
           0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F  0123456789ABCDEF
d25f593c  e1 fa 5a d2 f9 fa 5a d2 11 fb 5a d2 30 61 5f d2  ..Z...Z...Z.0a_.
d25f594c  44 29 5f d2 00 00 00 00 01 00 00 00 2c 59 5f d2  D)_.........,Y_.
 
[Pixel XL::XXX]-> ptr(0xd25f593c).readPointer().sub(0xd2593000)
"0x1cae1"
[Pixel XL::XXX]-> ptr(0xd25f5940).readPointer().sub(0xd2593000)
"0x1caf9"
[Pixel XL::XXX]-> ptr(0xd25f5944).readPointer().sub(0xd2593000)
"0x1cb11"
[Pixel XL::XXX]-> ptr(0xd25f5948).readPointer().sub(0xd2593000)
"0x63130"
[Pixel XL::XXX]-> ptr(0xd25f594c).readPointer().sub(0xd2593000)
"0x5f944"
//
[Pixel XL::XXX]-> seeHexA(ptr(0xe75d8d00).readPointer(),0x20)
           0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F  0123456789ABCDEF
d25f593c  e1 fa 5a d2 f9 fa 5a d2 11 fb 5a d2 30 61 5f d2  ..Z...Z...Z.0a_.
d25f594c  44 29 5f d2 00 00 00 00 01 00 00 00 2c 59 5f d2  D)_.........,Y_.
 
[Pixel XL::XXX]-> ptr(0xd25f593c).readPointer().sub(0xd2593000)
"0x1cae1"
[Pixel XL::XXX]-> ptr(0xd25f5940).readPointer().sub(0xd2593000)
"0x1caf9"
[Pixel XL::XXX]-> ptr(0xd25f5944).readPointer().sub(0xd2593000)
"0x1cb11"
[Pixel XL::XXX]-> ptr(0xd25f5948).readPointer().sub(0xd2593000)
"0x63130"
[Pixel XL::XXX]-> ptr(0xd25f594c).readPointer().sub(0xd2593000)
"0x5f944"
 
 
 
NOINLINE
void test1(ChildClass* cls){
    cls->showLOG();
}
 
NOINLINE
void test2(ChildClass &cls){
    cls.showLOG();
}
 
NOINLINE
void test3(ChildClass cls){
    cls.showLOG1();
}
 
NOINLINE
ChildClass test4(ChildClass cls){
    return cls;
}
 
MAIN
void test0(){
    auto* baseClass = new BaseClass();
    LOGD("baseClass   : %p sizeof: %d  typeid: %s",baseClass,sizeof(*baseClass),typeid(baseClass).name());
 
    auto* child1 = new ChildClass(10,20);
    LOGD("child1  : %p sizeof: %d  typeid: %d", child1, sizeof(*child1), typeid(child1).hash_code());
 
    auto* child2 = new ChildClass(*child1);
    child2->showLOG();
 
    test1(child2);
    test2(*child2);
    test3(*child2);
    test4(*child2);
}
NOINLINE
void test1(ChildClass* cls){
    cls->showLOG();
}
 
NOINLINE
void test2(ChildClass &cls){
    cls.showLOG();
}
 
NOINLINE
void test3(ChildClass cls){
    cls.showLOG1();
}
 
NOINLINE
ChildClass test4(ChildClass cls){
    return cls;
}
 
MAIN
void test0(){
    auto* baseClass = new BaseClass();
    LOGD("baseClass   : %p sizeof: %d  typeid: %s",baseClass,sizeof(*baseClass),typeid(baseClass).name());

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

最后于 2022-3-30 11:52 被唱过阡陌编辑 ,原因: 完善文章
收藏
免费 12
支持
分享
打赏 + 50.00雪花
打赏次数 1 雪花 + 50.00
 
赞赏  Editor   +50.00 2022/04/02 恭喜您获得“雪花”奖励,安全圈有你而精彩!
最新回复 (7)
雪    币: 116
活跃值: (1012)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
支持一下
2022-3-11 11:21
0
雪    币: 5330
活跃值: (5464)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
3
真的好,花了20分钟仔细看完了。希望大佬能多写一些基础的,复习或者补充遗漏的知识
2022-3-11 11:40
0
雪    币: 2032
活跃值: (471)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
真的好
2022-3-11 16:13
0
雪    币: 959
活跃值: (66)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
请问Pixel XL::XXX 是什么工具,开源的还是楼主自己写的,谢谢
2022-3-12 11:39
0
雪    币: 3269
活跃值: (2964)
能力值: ( LV3,RANK:25 )
在线值:
发帖
回帖
粉丝
6
ybhdgggset 请问Pixel XL::XXX 是什么工具,开源的还是楼主自己写的,谢谢
Frida连接Android手机。其实也可以使用调试器实现相同效果。
2022-3-13 09:58
0
雪    币: 959
活跃值: (66)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
xhyeax Frida连接Android手机。其实也可以使用调试器实现相同效果。
谢谢
2022-3-20 17:17
0
雪    币: 70
活跃值: (2010)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
细 并且耐心 
2023-6-21 18:31
0
游客
登录 | 注册 方可回帖
返回
//