freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

CTFpwn中的结构体
2021-10-06 15:34:19

这段时间我一直在做CTF的堆溢出题目,漏洞利用其实并不算太难,做多了就有经验了。难点在代码审计,使用IDA进行反汇编以后的代码实在太难看了,遇到了很多问题,这里遇到了一个二重指针,搞得我花了一天时间重新学习结构体

反汇编出二级指针

image.png
image.png
其实这个问题早就出现了,但是之前都是硬着头皮过了,但如果不搞明白这个问题,我就无法继续好好做题。这里ptr本身就是个指针类型,结果再次进行取址斌且求其偏移,最开始以为是一个二维数组,但是看wp的时候大佬分析出了结构体,我就明白了我对C语言还是不太了解

结构体的相关知识

我们之前很早就了解过结构体,但是真的了解结构体嘛?首先写一段代码

#include <stdio.h>
typedef struct Node{
    int a;
    char *b;
    char c;
} node;

int main(){
    node n1;
    printf("%d\n",sizeof(n1.a));
    printf("%d\n",sizeof(n1.b));
    printf("%d\n",sizeof(n1.c));
    printf("%d\n",sizeof(n1));

}

然后我们进行编译一下
image.png我们发现这三个变量大小相加以后小于变量n1的大小。
系统在读取代码的时候,其实是按照一个标准字长的跨步是最快的。所以我们申请了结构体变量以后会存在内存对齐。int类型占4个字节,指针占一个标准字长(64位就是八个字节),而一个char类型占1个字节。大部分的程序编译为汇编语言以后,在栈中都是以RBP寄存器来定位的(大部分,因为我之前做过有的题里面是以RSP来定位的),因为RBP在栈帧中是不会变的。我们可以再看一下它的地址

#include <stdio.h>
typedef struct Node{
    int a;
    char *b;
    char c;
} node;

int main(){
    node n1;
    printf("%p\n",&n1.a);
    printf("%p\n",&n1.b);
    printf("%p\n",&n1.c);


}

image.png画个图大概是这个样子
image.png

结构体的变量名

我们都知道&n1代表了结构体的首地址,也是上面代码中n1.a的地址image.png

#include <stdio.h>
typedef struct Node{
    int a;
    char *b;
    char c;
} node;

int main(){
    node n1;
    printf("%p\n",&n1.a);
    printf("%p\n",&n1.b);
    printf("%p\n",&n1.c);
    printf("%p\n",&n1);
    printf("%p\n",n1);

}

image.png我这次把n1也放进去了,我想知道结构体的变量名表示什么。当然我们已经知道了&n1=&n1.a,那么单一个变量名又是什么意思?我在gdb中进行了一波调试。
首先是运行的结果image.png然后我们调试,我们都知道在64位操作系统中先将参数放入寄存器然后再call函数。image.png没错,与我们的输出是一样的,但是RSI里面存放的到底是什么?
image.png
image.png虽然挺离谱,但是不得不相信,我们第五次调用print之前,是没有对RSI做出改动的。也就是说RSI里面的值是第四次printf的时候用剩下的产物。注意看RSI的地址中存放的内容image.png这是在第四次printf之前RSI里面存放的内容,而调用完printf以后image.png也就是说我们的RSI是在第四次printf中发生了改变,并且把之前的地址放入另外一个地址中。

综上所述,结构体变量名什么也不能代表,而是上一次变动完以后剩下来的产物

没错,结果就是挺离谱的。花了这么长的时间,结果就是它什么也不是。但是这并不是没有任何意义的,至少我们探寻出了一个结果。那么还剩下一个问题就是为什么RSI最后的地址,存放着上一次RSI的值?这究竟是巧合还是作者有意为之?大家可以自己再调试一下看一看,我估计是有意为之

二级指针

我们已经完全了解了结构体。但是二级指针又怎么说呢?如何就会出现二级指针呢?首先我们要知道IDA的反汇编并不一定是绝对正确的。在这样的一个前提下,反汇编的内容我们可以相信一部分,我们还需要脑补一部分(如果不脑补的话有些语句我们确实无法理解),于是我们可以这样构造一个结构体

typedef struct Test{
    char *a;
    int b;
    char c;
} test;
test *t1;
int main(){
    t1 = malloc(num);
}

这样我们看一下如何出现二级指针
我们已经知道了a占8字节,b占4字节,c占1字节,那么我们应该如何寻找a?t1->a这个就代表一个字符串。它还可以这么写*(&t1->a)

好的,现在已经出现二级指针,那么我们该如何寻找b呢?也就是说t1->a加上a和b的偏移量也就是8,我们可以这么写*(&t1->a+1),我们回过头看看CTF中截的那张图image.png格式一模一样。那么为什么是加1呢?这里的a由于是指针类型,所以加1就相当于加8.例如

int a[10]

那么a+1是和头指针偏移量为1吗?并不是,应该是一个int的大小,同理这里也一样,如果想要改,可以进行强制类型转换(char*)a+4,效果一样。于是我们可以得出b的位置就是(char*)&n1->a+8。同理c也一样。

#include <stdio.h>
#include <stdlib.h>
typedef struct Node{
    char *a;
    int b;
    char c;
} node;
node *n1;
int main(){
    n1 = malloc(100);
    printf("%p\n",((char*)&n1->a+8));
    printf("%p\n",(&n1->b));

}

image.png

这篇文章说了很多的废话和十分基础的东西,可能大佬都已经会了,但是这些都是我在写的过程中遇到的坑,小白可能更容易接受。我的C也是两年前学的,很多基础的知识后来都没有使用过,所以忘了不少东西。但是这次确实学会很多东西

本文章参考的文章太多了,都是有关pwn的,这里就不写了,总之感谢前辈大佬

# c语言 # 汇编语言 # pwn # 寄存器 # 指针
本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者