从C++带你了解程序的底层布局

前言

        小伙伴们大家好,四月的风,揉揉拂面,和煦爽心。四月的天空,不离不弃,若及若离。四月的阳光,温和不燥,使人心旷神怡。作者又双叒叕来咯,昨天是作者注册CSDN账号两年整的日子,今天就是第三年的第一天,于是决定写一篇文章来纪念一下这个日子。

1.我对安全行业的看法

1.1我对安全的看法

1.1.1什么是安全

        安全这个词,在各行各业都有,我们平日熟悉的有人身安全、社会安全、国家安全还有我们的网络安全、再加上目前新兴的数据安全、云安全和AI安全等。由此我们可以发现安全一定是基于某一事物而存在,而并非一个单独的概念。因此在作者看来,安全是一种思维而非一项独立的技术。以此为由,类比到我们计算机行业,不论是网络安全也好、数据安全也罢还是听上去更高级的云安全和AI安全,它们的本质都是由于代码编写的不安全,从而引发的安全性问题。也正是因为这一理念,作者虽然作为安全从业者,但大部分文章都是与程序和底层知识相关而非安全漏洞的根本原因。编程能力才是核心能力,基于此之上才有安全。

1.1.2编程语言学习的建议

1.1.2.1综述

        作者认为作为安全从业者,想要长久立足,掌握多门编程语言是必须的,因为与开发人员不同,我们会遇到的场景可能基于任何一门编程语言,因此需要对多门编程语言都有一定程度的了解,才不会碰到时无从下手,因此我们应该选择一门编程语言作为主修语言,其它编程语言则达到能满足工作所需即可。

1.1.2.2Python

        Python是我们大部分同行都掌握的编程语言,大多数的简易渗透工具和脚本也都使用python编写,它的语法十分简便,因此上手难度极低,简单场景下的开发效率极高,一般是黑盒测试下的不二之选,我们平日遭受到的无差别批量网络攻击大多也是基于python编写。

        但是python作为一门胶水语言,在这些场景下虽然占尽优势,但是python自身的设计十分简易,这也是它易上手的原因。但也正因为如此,它并不适合企业级这种高要求的开发场景,而我们的客户都是来自于企业的,因此它只能算必备的基础技能,在黑盒的场景下可以解决一些简单问题,并不能作为核心编程语言的选择。

1.1.2.3Java

        Java想必大家都了解,即便是不了解开发的安全业内朋友,也都知道代码审计在国内几乎就默认了是Java代码审计。因此它也是普通渗透测试从业者选择转行的热门方向,Java目前在国内也确实如日中天,web层面开发绝大多数都是基于Java的。因此Java可以作为主修编程语言来学习,不过因为种种原因作者个人对其未来发展持怀疑态度,因此不作为自己的主修编程语言。

1.1.2.4C/C++

        C/C++就不用多说了,某种程度来说也可以算作大部分编程人员接触到的最底层,我们的操作系统内核和建设在操作系统上的大部分应用程序都是基于C/C++开发的。它是作者最看好的编程语言,也是学习难度最高的编程语言,不过正因为其难,技术壁垒也足够高。Java在很多层面上与C++十分类似,它们都是基于面向对象的编程思想,只是Java在这方面做的更加彻底。具体区别还有很多,作者就不多赘述,总之它也可以作为主修编程语言,也是作者自身的选择。

1.1.2.4php

        php是曾经的web项目常用编程语言,但现在在国内已经十分衰落,至于php审计这里就不提了,我认为五年之内,国内企业级应用就不再会有php开发,大多也是不发达国家和个人网站开发可能会图简单而使用php进行开发,作者这里直接不推荐将其作为主修编程语言。

2.C/C++下的程序布局

2.1内存布局

2.1.1程序在内存中的划分

        我们的程序在内存中会划分为几个区域,分别是静态区、代码区、常量区、堆和栈。

2.1.2代码区

        顾名思义,我们程序的代码片段就会存放在内存中的代码区,比如我们代码中要进行的各种操作,if判断、循环条件判断、数学运算等代码都是存放在代码区中。

2.1.3静态区

        全局静态区是存放全局变量的区域,全局变量指的就是定义在函数之外的变量(静态变量除外),即它的作用域是全局的,而不是某一函数内的局部变量,对于全局变量即便我们在定义时不初始化,编译器也会自动帮我们初始化为0(因为这片区域的数据在程序没有运行之前就已经确定,因此必须初始化来占用对应的内存空间,也是我们二进制逆向中常提到的基址,很多游戏外挂就算对此处数据的修改)。下面这里举个例子说明一下自动初始化:

(可以看到,我们定义了一个全局变量a,并未对其进行初始化赋值,直接打印值为0,编译器自动帮我们进行了初始化)

(我们再将a的定义移动到函数内,直接进行打印,可以发现报错使用了未初始化变量,因为函数内的变量并不存放在静态区,编译器不会替我们初始化)

2.1.4常量区

        顾名思义存放常量的区域就是常量区,即不可修改的数据即为常量,一般通过宏的形式进行定义,也可以通过const关键字修饰进行定义(注意:定义在函数内的常量,并不存放在常量区)。它也是在程序内提前已确定好,即便程序未运行也已经写在了对应位置,对于熟悉代码编程过程的小伙伴,应该知道在预编译阶段就会进行宏替换。

2.1.5堆

        堆是一片特殊的区域,其它的区域都是由操作系统或编译器进行管理的,唯有堆是提供给我们程序员自己进行管理的区域,在C/C++中,堆内存的申请和释放都要手动进行,如果不释放就会造成我们熟知的内存泄漏问题。

        补充:对于熟悉Java的小伙伴,应该知道在使用new关键字创建对象时,就是开辟了堆(heap)空间来存放对象实例,但是并不需要手动释放堆内存,这是因为释放内存的操作由jvm代替程序员进行,因此Java和其它高级编程语言一样,已经替程序员解决了所有的底层操作,程序员只需要专注功能的实现即可,这么做虽然使得开发更容易,但也会使得程序员对计算机底层变得陌生。在C++中同样可以通过new关键字创建存储在堆中的对象实例,但是需要程序员自己在合适的时候释放堆内存空间,否则就会导致内存泄漏。

2.1.6栈

        栈是我们最常听到的一个区域,当我们运行一个程序时操作系统就是将main函数加入栈底,然后开始执行程序,因此我们声明在函数内的局部变量(除静态变量)都是存放在栈中的,当一个程序运行完毕之后,就会出栈,因此其数据是在对应函数运行时短期存在的。

        栈空间是由操作系统进行分配的,因此它是有限的,这也是我们触发无限递归函数会爆出栈溢出错误的原因。

                     (如上图,作者写了一个无限递归函数,爆出了栈溢出错误"stack overflow")

2.2静态变量

2.2.1什么是静态变量

        作者前面多次提到了静态变量,所谓静态变量就是使用static修饰的变量,它即便定义在函数或对象内部,也是存储在全局静态区中的,与全局变量保存在同一片区域。

2.2.2静态变量存在的意义

        我们在创建对象时,很多时候有这样一个需求:即虽然某个变量我们定义在了对象内(即希望只能通过该对象调用该变量),但是我们却希望所有对象实例共用同一个变量,因为正常情况下,每一个对象实例彼此都是独立的,它们是无法共用数据的,因此就提出了静态变量来解决这一问题。

2.2.3静态变量本质

        经过上述描述,我们可以知道静态变量只需要一份数据,让所有的对象可以共用即可,因此它并不保存在对象内(也就是堆或栈空间内),而是存储在全局静态区中,静态变量的本质就是特殊的全局变量(即被限制了作用域的全局变量,仅能被特定类对象进行访问)。

3.函数

3.1函数为什么无法对原数据产生影响

3.1.1无法产生影响的原因

        前面我们讲到了函数在运行时会被加载入栈中,所以函数内的所有数据都是保存在栈中的,包括我们传入的参数,它们会存放在栈中的参数列表内,本质上就是程序将我们传入的数据复制了一份副本保存在了函数内,因此它在函数的整个生命周期内有效,一旦函数运行完毕,生命周期结束,栈空间被释放,该数据也自然丢失。

3.1.2持久化影响的方法

        我们最常用的方法就是通过返回值的形式,来使得栈中的临时数据被保存到全局变量内,以此对函数外产生影响。但对于C/C++这类底层操作的编程语言而言,还可以通过引用数据类型和多级指针的方式(文件操作函数就利用二级指针)来达到此目标,灵活性更加强大。

3.2对象中的函数

3.2.1对象中函数的本质

        与前面讲解的静态变量类似,我们在类内定义的函数,由于它也是所有对象实例共用的,并且不会被修改,因此它本质上也是特殊的全局函数。它也不保存在对象实例内,而是保存在特定位置的,底层只是通过特定方式限制了只能通过该对象来调用该函数。

3.2.2序列化只有成员变量的原因

        通过上述的讲解,我们知道成员函数不是保存在对象内的,那么在进行序列化操作将对象进行序列化时,自然就不包含函数的内容,因此并不是因为序列化操作在底层处理时去除了函数部分,而是实例对象本身就不包含函数部分。

4.总结

        还是老样子,没什么想说的,本次文章可能不太适合基础较差的小伙伴,都是干货知识,可以先收藏,等到时机成熟再来回看本文,感谢大家支持,拜了个拜.......

  • 22
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值