指针(上)

admin 2023年4月9日12:37:47评论23 views字数 3471阅读11分34秒阅读模式

审稿&指导とある名前のない大佬

前言

我不是卷,我只是得了能力不足恐惧症,要么你带飞我,要么我卷死你

世界上只有两种卷,一种是卷死所有人,一种是卷铺盖走人

指针(上)

什么是指针

指针是我们常见的一个常见的类型,也就是常说的保存地址的玩意儿

个人将其分为以下并按照章节进行讲解:

  • 普通指针

  • 指针数组

  • 结构体指针

  • 数组指针

  • 函数指针

在学指针之前,我们还是先了解一下一些相关的知识

子程序

在C语言写出来的程序中,一般我们把调用的函数称为子程序

比如,我们简单的写一个程序

指针(上)

现在,挺简单的,有手就行

这里的子程序,就是这个add()函数

指针(上)

也就是这部分

指针(上)

我们将其称之为子程序,也就是说,子程序就是我们自己定义的啥intcharvoid类型的函数

但是为什么讲这个呢,因为后面能用到,不过得是下一章节

我们先讲一下变量、常量还有函数

常量是放在常量区的,也就是一个地址空间,是不允许修改的,结束之后释放

指针(上)

变量是程序执行的地儿,比如子程序运行那会儿,可以修改读取,结束子程序之后直接留在地址空间

函数也就是子程序

子程序在运行的时候会开辟一个临时的空间,我们也叫做栈空间,也叫做缓冲区

为了大家能看懂我反汇编分析的,这里我先给大家讲一下子程序运行时候进行的操作

首先,我们提升栈空间,

push ebpmov ebp,espsub esp,40h

这里是先将ebp地址放到栈空间里,也就是返回地址

然后基地址对准栈顶指针

减去一个值,也就是分配40h的空间

现在,我们开始保留现场,也就是之前运行时的值保留下来,以免数据丢失

push ebxpush esipush edi

现在,我们对这个地址空间进行初始化,就是往里边儿填充cc

lea edi,dword ptr ss:[ebp-40]mov ecx,10mov eax,ccccccccrep stos dword ptr es:[edi]

现在就到了他的主要功能点

mov dword ptr ss:[ebp-4],2mov eax,3add eax,dword ptr ss:[ebp-4]

[ebp-4]是栈内变量,也就是第一个参数,[ebp-8]就是第二个参数,记住这个,后边反汇编能用到

这里实现的就是一个加法,传进去的参数是2和3,通过eax这个寄存器传递出来,也就相当于是这个函数

int add(int x,int y){ return x+y;}

接下来要做的就是恢复现场

pop edipop esipop ebxmov esp,ebppop ebpret

指针

一般指针

注:这儿我直接使用一次指针来称呼一级指针,二次指针来称呼二级指针,多次指针称呼多级指针,因为我感觉很像一次函数二次函数这种所以就这样叫了

也就是int* p;这种

那我们来实战检验一下

int main(int argc, char[] argv){    int* p;    int a = 100;    p = &a;    printf("%dn",p);    printf("%dn",*p);    return 0;}

指针(上)

我们都知道,&是取地址符号,直接打印输出p是输出a的地址,只有*p才是输出a的值

指针的作用真的只有这些嘛?我们是不是可以试试,指针的加减法?

如果指针自加或者自减甚至是相互加减会发生什么呢?

那就来上手操作呗

指针加法

我们先定义一段代码

int main(int argc, char* argv[]){
char* p1; short* p2; int* p3;
//类型强制转换,因为100是一个数,需要强制转换成char*指针类型,不然会报错 p1 = (char*)100; p2 = (short*)100; p3 = (int*)100;
p1++; p2++; p3++;
printf("%d %d %dn",*p1,*p2,*p3);
return 0;}

开始实践

指针(上)

发现没有,很奇怪对不对,三个值,居然都不一样,但是神奇的却是,char是连续的1个字节,short是2,int是4

很显然,这和定义它的类型有关

自减验证一下

指针(上)

看来确实是与它自身长度有关

那我们试试,如果加上更多的*,他还能不能正常运行

指针(上)

自加好像也就那样

那我们试试,如果都给它加上一个常数3,它会变成啥样呢?

指针(上)

哦豁,访问失败,莫得权限,但是很简单,我们可以反汇编分析啊,上边讲的就起作用了

指针(上)

在这儿也就是上边儿讲的参数,我们看到p1就是加3,p2就是加6,p3就是加12

前三个是加了一个*的指针,我们能发现的规律是

char*类型的指针是1times3

short*类型的指针是2times3

int*类型的指针是4times3,也就是0xCh

指针(上)

那么,我们现在推测一下,

我们现在给*这种取个名字,我们就叫一次指针吧,这种我在这儿命名的一次指针遵循一种特殊规律,那就是,他们加上一个数得到的结果用公式算,那就是 类型的长度*加上的常数  (ps:markdown语法解析不了,我很苦恼)

接着,我们看后三位,那就叫做二次指针,这种二次指针貌似也遵循一种规律,那就是无论是什么类型,他都是 4*常数

那么,我们再实践一下以证明我的观点

指针(上)

在这儿,十六进制的0Ah也就是十进制的10,这儿的14h也就是十进制的20,这就证明了我们的观点,那个公式是正确的,而下边的三个也是一样的,无论是int**还是是char**,他们加上一个数都是4times常数

那我们再往上延伸一下呢?那int***呢?他们也是遵循这样的嘛?

我就偷个懒哈

指针(上)

发现没有?也就是说,只有一次指针遵循那个公式,其他的二次指针甚至多次指针都是4times常数

我们来总结一下,刨开一次指针,二次甚至三次及其以上无论任何类型他的宽度都是4

那么,减法运算会是怎样的呢?

指针减法

现在我们来定义呗

指针(上)

他打印出来的值是100,那么换成int*类型呢?

指针(上)

欸?居然返回的结果是25?为啥嘞?

我们试试short

指针(上)

发现了吗?他跟一次指针一样,它遵循着一个规则

那就是两个指针的值相减的时候,相减结果的值是需要除以类型的宽度的

那我们紧接着试试二次指针

之前我们得出结论,那就是二次指针及其以上的宽度是4,那我们来验证一下

指针(上)

发现了吗?指针的加减法都是与他的类型宽度息息相关的

指针数组

指针数组里边存放的全是字符串的地址

比如

int* a[5];a[1] = "hello world";printf("%dn",*a[1]);

我们在vc6中debug再disassembly一下,我们alt+5/6打开内存,选中arr[1],将其选中拖拽进去,可以看到,在此处有一个地址

指针(上)

这个就是保存的地址

也就是说,指针数组内保存的是常量的地址而非存储的内容

结构体指针

我们先定义一个由三个int组成的结构体指针

指针(上)

现在我们发现,他自加结果是12也就是  3*4

我们试试其他的呢

指针(上)

发现了吗?加的是3,我们大致能判断出应该是自身宽度

再试试呢?

指针(上)

发现没有这次居然是8?难道不应该是4+1+1吗?再试试呢?

指针(上)

发现没有?这次还是8

那我们猜测一下,这究竟是什么原理?

首先我们画个图

指针(上)

第一个会不会是因为上边是4,所以按照4的倍数来算,不满4的填充到4的倍数?

所以我们需要再做个实验

指针(上)

我们再来猜想一下,为啥长度是12?

我们再来画图一下

指针(上)

是不是因为中间的最长,所以按照最长的来摆,上下各填充成宽度为4?

那就好办了,来,上实验

指针(上)

看到没,结果还是12,这说明,就是按照最长的来进行填充

那么示意图就是如下:

指针(上)

也就是说,以最长的那个类型为基准,他横在中间的话,那就得靠堆叠,堆不满一层的按照一层算,也就是  最宽类型*堆叠层数

指针(上)

问了下师傅

结构体的对齐原则:

  • 数据成员对齐规则,结构的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的七点位置要从该成员大小的整数倍(比如int为4字节,则要从4的整数倍地址开始存储)

  • 结构体的总大小,也就是sizeof的结果,必须是其内部最大成员的整数倍,不足要补齐

  • 如果一个结构体里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储(比如:struct   a里有struct   b,b里有char、int、double等元素,那b就应该从8的整数倍开始存储)

  • 对齐参数如果比结构体成员的sizeof小,该成员的偏移量应该以此值为准,也就是说,结构体成员的偏移量应该取二者的最小值

结构体指针赋值就比较简单了

struct A{    int a;    int b;    int c;};
int main(int argc, char* argv[]){ A* p; //赋值 p -> a = 15; //取值 printf("%dn",p->a); return 0;}

今天群里的师傅说过代码能力基本决定了安全的上限,我觉得很有道理,比如下一篇中的函数指针,如何将shellcode隐藏到数据段(ps:我是懂卡文的)

你学废了吗

原文始发于微信公众号(深夜笔记本):指针(上)

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年4月9日12:37:47
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   指针(上)http://cn-sec.com/archives/1662493.html

发表评论

匿名网友 填写信息