3.23~3.25

switch 结构,内存类函数,标准库函数和底层函数的区别

样本和代码以及所需要预习的已经上传

#include<windows.h>
#include<stdio.h>
#include<malloc.h>


int main()
{
	int a = 0;
	printf("select:");
	scanf_s("%d", &a);
	switch (a) {
	case 1:
		{
			//从堆中申请内存,需要释放
			PCHAR tmp = (PCHAR)malloc(100);
			free(tmp);
		}
		break;
	case 2:
		{
			//从堆中申请内存,需要释放
			PCHAR tmp = (PCHAR)malloc(50);
			PCHAR pnew = (PCHAR)realloc(tmp, 100);
			free(pnew);
		}
		break;
	case 3:
		{	//从堆中申请内存,已经初始化,需要释放
			PCHAR tmp = (PCHAR)calloc(8, 4);
			free(tmp);
		}
		break;
	case 4:
		{
			//从栈中申请内存,
			PCHAR tmp = (PCHAR)_alloca(20);
			_freea(tmp);
		}
		break;
	default:
		break;
	}
	return 0;
}

跟以往一样还是用IDA,OD工具
特别注意:
switch 汇编形式,每一个C函数F7,进去看看,底层实现是什么API,还有三组API,注意观察,C函数和API函数的参数个数和每个函数参数含义

先找变量,改名
在这里插入图片描述
之前不怎么会找API函数

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
有具体名字的才算API函数

如何找主函数,od工具的使用在之前的博客中已经讲到
这里我们直接跟进

在这里插入图片描述
按F7进入

在这里插入图片描述得到malloc ---- ntdll.RtlAllocateHeap
malloc 是高层函数, 实际上内部调用了ntdll.RtlAllocateHeap 底层函数
而在源码中一共有四个内存申请函数
底层调用的是什么API函数找出来
malloce 和free这里我们跳过
找个有区别的
realloc— ntdll.RtReALLcateHeap
仔细发现,从1~3case代码都有申请的API函数,而4呢?
没有

原因:
4 是堆栈申请,所以是可以通过汇编代码实现的,并没有底层函数
栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈
堆区(heap) — 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收

这里看到知识链接:https://blog.csdn.net/study_live/article/details/5304354

汇编层面,switch 是怎么构造的?(静态不明白,就动态跳,注意寄存器的操作)

在这里插入图片描述首先将把得到的数放入edx中sub 1减去1
因为据算计都是从0开始的
将这个数放入case的栈中
用cmp跟3进行判断
补充:ja 是判断当前的值,如果不是下列case 中的任何一个,则进入default分支

ja指令后将case的值转入EAX,跳转数据段

在这里插入图片描述在这里插入图片描述

根据信息
是去执行匹配对应的case段的命令
也就是下面的指令

在这里插入图片描述

但准确来说是计算所要跳转地址的偏移

最后switch 编译器会生成一个跳转地址表,然后根据值,算出来之后,eax 就是索引,取里面的偏移,到对应的分支继续执行

内存分配函数的接口,每个函数的参数个数是多少,每个参数的含义是什么?

各个函数:
malloc
realloc
calloc
_alloca
VirtualAlloc
VirtualAllocEx
HeapAlloc
HeapFree
GlobalAlloc
GlobalFree
RtReALLcateHeap

malloc

需要一个参数字节的整数型

calloc

需要两个参数

提问:calloc 能不能用malloc 来替代?

不能
malloc不能初始化所分配的内存空间,而calloc可以
calloc会将分配的内存空间中的每一位初始化为零
但这只是一方面;

单单从分配内存大小角度来看

那分配一块0x40大小的内存,两个函数的参数分别怎么填写?

/分配一块0x40大小的内存,两个函数的参数分别怎么填写?
int *p = (int*)malloc(16*sizeof(int))
int *p = (int*)calloc(16,sizeof(int))

realloc

重新分配内存并返回void类型, 如果没有足够的内存扩展内存块,则原来的指向的内存指针无变化, 并返回NULL;
如果重新分配大小设为0, 而释放原来的内存块, 并返回NULL
原文链接:https://blog.csdn.net/a063225/article/details/103156819

realloc 有两种可能,一种是扩大范围,另一种是缩小范围

面对缩小范围的情况,大概率是不需要重新调用内存分配机制算法

去重新寻找一个内存块的,只需要在原来基础上砍字节而已

扩大范围且尾部空间不足时realloc会重新分配内存,此时指针会改变

而且,当内存指针发生变化之后,之前内存区域的内容,会被拷贝到新的内存区域

这个地方就涉及到系统内存管理的机制问题了,在调试任务中,这种点往往是解决问题的关键

_alloca

参数个数一个
作用是分配一个连续栈内存

在汇编层面,具体实现算法是怎样的?(_alloca)

最开始弄错了
在这里插入图片描述
这函数,是对堆栈的一个上限进行检查

下面这个才是

在这里插入图片描述
如果,当前申请的内存空间小于堆栈的总大小
那么直接返回当前堆栈地址,作为内存指针
关键还是那个堆栈顶的校验
堆栈是容易溢出的

VirtualAlloc

在这里插入图片描述

一共四个参数
1要分配的区域的起始地址
2区域的大小(以字节为单位)
3内存分配的类型
4要分配的页面区域的内存保护

参数1是否必要存在?
可以是0
此时虚拟内存的地址由系统分配

存在和不存在,区别在哪里?(该函数所分配的内存区域是在堆区,栈区,或者其他什么区域?)

该函数分配的内存空间是调用进程的虚拟空间

堆,和栈 包括进程虚拟空间,这些都是系统进程管理里面的概念

堆区和栈区也是在进程虚拟空间内

简单可以理解为堆区和栈区只是之前规划使用的住宅用地

而进程虚拟空间,是该进程所拥有的所有土地资源

也就是说这些都在虚拟内存空间

进程加载之后,所有的东西

都会在总的土地资源上进行规划使用

分配的类型分别有哪些,内存保护属性有什么作用?

分配类型有提交物理内存和不提交物理内存

内存保护属性可以设置读、写和执行的权限

第三个参数一共有几个可选项?

MEM_COMMIT
0x00001000
MEM_RESERVE
0x00002000
MEM_RESET
0x00080000
MEM_RESET_UNDO
0x1000000
MEM_LARGE_PAGES
0x20000000
MEM_PHYSICAL
0x00400000
MEM_TOP_DOWN
0x00100000
MEM_WRITE_WATCH
0x00200000

提交物理内存,和不提交物理内存,如果不提交物理内存,分配机制上和物理内存有什么差异?

这个就是系统内存管理机制中的缓存机制

有时候,物理内存紧张的时候,可以先给一个虚拟空间

也就是有一张借条

等其他的物理内存页

释放后,内存管理子系统

再给你兑现成真实物理内存

当系统内存紧张时

内存管理子系统会把溢出的内存写到文件存储磁盘上

这个在windows 上有设置,叫虚拟内存大小

其实,就是通过占用文件存储空间,充当内存,而不是内存条

第四个,参数这些属性,有什么作用,限制不同内存区域的操作,在实际应用中有什么作用?

比如,你在程序中,存放用户密码的内存,是不是不想让其他程序访问

这个时候,申请什么类型的内存块呢?

或者,不希望被其他程序修改的数据时,你由申请什么属性的内存块呢?
PAGE_READONLY

我想申请一块内存,把代码拷贝上去,再运行,这个时候申请什么属性的内存块?
PAGE_EXCUTE_WRITECOPY

这个标志就是这么用的

具体的其他标志可以上官网搜索,建议后面再做细致了解

在这里插入图片描述

延申

目前流行的网络攻击工具,CobaltStrike 生成的网络攻击载荷
就是利用可执行内存页,去加载解密后的shellcode 然后运行的
正常使用和攻击使用
仅仅是看,你的目的是什么
接触底层接口,更多可以了解到系统内部的运作机制

那些C语言函数参数个数 和这种底层接口参数个数,哪个更加复杂?

铭记这句话:搞安全,不懂底层机制,那是不合格的

显然底层更难

所以说,引出另一个知识点,上层接口,更多的是方便,代码编写,把很多参数封装了

目前得知道:
底层内存申请接口,可以控制内存块的使用属性,和控制内存块申请的顺序

VirtualAllocEx

还是先看看参数
在这里插入图片描述
和之前没有EX的进行对比后发现

Ex 兼容了没有Ex的所有参数

看到这个参数,我们就知道,其实系统分配内存的最小单元,是进程

从系统接口中,就能读到系统的机制

这个就是学习底层接口的意义

所以这个Ex 能作为 没有ex的内部实现

其实,在系统API中,有很多对,又Ex和没有Ex的同名函数

而往往,没有Ex 是作为一个外层封装接口,具体内部,就是这些Ex函数。

HeapAlloc

在这里插入图片描述

**补充:**HANDLE 也叫句柄

是windows 内部管理系统的一个身份标志

刚才的VirtualAllocEx 第一个参数,也是句柄

能表示,当前这个函数操作,是在那个区域内去完成的

进程句柄,表示进程ID,堆句柄,表示哪个堆的ID

通俗点,就是门牌号

简单的,你再单元楼一,申请了一块内存

然后你释放的时候,没告诉函数,这块内存再单元楼一

那这个函数怎么释放呢?

或者给你个,错误的门牌号

快递都有地址

理解成快递的地址,都可以

从数据管理的角度来说,这个就是一个索引

找东西的依据

HeapFree 包括这个对应释放函数也是一样的

如果,你给的堆门牌号不对,函数肯定会返回失败

因为它找不对,对应的地址

通过,句柄去管理不同的内存区域,这个是windows系统内部一个重要的机制

GlobalAlloc

UINT uFlags
SIZE_T dwBytes

GlobalAlloc是从什么区域分配的内存?

是在全局的、公用的远堆上分配

uFlags 有没有标记,可以完成内存块的全0的初始化

GHND
GMEM_FIXED
GMEM_MOVEABLE
GMEM_ZEROINIT
GPTR

GMEM_ZEROINIT 将所申请内存初始化为0

和 calloc 等效

GlobalFree

[in] _ Frees_ptr_opt HGLOBAL hMem

全局内存对象的句柄

这个句柄,是不是之前 GlobalAlloc 的返回值呢?


所以,这对函数的关联对象,就是这HGLOBAL

往往系统内部很多函数,都是需要一个句柄参数

去做关联的

根据某个句柄去做操作

逆向的时候

当你已知这个地址是句柄之后

可以设置相应的访问断点

然后F9 就可以快速的找到

其他的操作这个句柄的其他代码位置

关联地址,的用法之一,要记得

定位完成后,汇编代码,一个个看
怎么滴都能看得懂
往往费时间的地方就是定位

一定要学会,通过系统函数接口的学习,理解系统内部的机制

操作系统内部,会划分无数的逻辑内存块单元,不编号,怎么管理?

操作系统子系统,都是管理自己这些一亩三分地
所以这就是为什么最初要认识和了解各大子系统的功能

题外话

搞安全,一定得有个大致的子系统概念,安全场景,都是根据各个子系统展开的

补充:虚拟内存和物理内存怎么理解?

物理内存,就是实际占用内内存条上存储颗粒的内存块

虚拟内存,是系统内部的逻辑内存块,不一定会被实际分配到内存条上的存储颗粒

当内存条资源紧张时,虚拟内存中的东西会被装入其他缓存区域,比如文件磁盘

但是,从程序的角度看,都是内存,且都能访问到里面的数据,只是读写速度不同

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Back~~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值