Windows逆向安全(一)之基础知识(十一)

news2024/10/6 22:26:24

二维数组

二维数组初始化

int arr[3][4]={
        {1,2,3,4},
        {5,6,7,8},
        {9,10,11,12}
};

查看反汇编

7:        int arr[3][4]={
8:            {1,2,3,4},
0040D498   mov         dword ptr [ebp-30h],1
0040D49F   mov         dword ptr [ebp-2Ch],2
0040D4A6   mov         dword ptr [ebp-28h],3
0040D4AD   mov         dword ptr [ebp-24h],4
9:            {5,6,7,8},
0040D4B4   mov         dword ptr [ebp-20h],5
0040D4BB   mov         dword ptr [ebp-1Ch],6
0040D4C2   mov         dword ptr [ebp-18h],7
0040D4C9   mov         dword ptr [ebp-14h],8
10:           {9,10,11,12}
0040D4D0   mov         dword ptr [ebp-10h],9
0040D4D7   mov         dword ptr [ebp-0Ch],0Ah
0040D4DE   mov         dword ptr [ebp-8],0Bh
0040D4E5   mov         dword ptr [ebp-4],0Ch
11:       };

可以发现其存储方式和一维数组并没有什么不同,仍然是从低地址开始连续存储

对比一维数组

int arr[12]={1,2,3,4,5,6,7,8,9,10,11,12};

查看反汇编代码:

15:       int arr[12]={1,2,3,4,5,6,7,8,9,10,11,12};
00401038   mov         dword ptr [ebp-30h],1
0040103F   mov         dword ptr [ebp-2Ch],2
00401046   mov         dword ptr [ebp-28h],3
0040104D   mov         dword ptr [ebp-24h],4
00401054   mov         dword ptr [ebp-20h],5
0040105B   mov         dword ptr [ebp-1Ch],6
00401062   mov         dword ptr [ebp-18h],7
00401069   mov         dword ptr [ebp-14h],8
00401070   mov         dword ptr [ebp-10h],9
00401077   mov         dword ptr [ebp-0Ch],0Ah
0040107E   mov         dword ptr [ebp-8],0Bh
00401085   mov         dword ptr [ebp-4],0Ch
16:   }

可以看到,其分配方式一模一样

得出结论

无论是一维数组,二维数组或者其它多维数组,其存储方式实质上并没有区别,都是在内存中连续存储,并没有所谓的行和列的概念

对于一个二维数组来说,编译器为其分配空间实际上也是按一维数组来进行分配的

int arr[m][n] 等同于 int arr[m*n]

拿上面的例子而言就是

int arr[3][4] 等同于 int arr[3*4]=int arr[12] 

因此也可以使用下面这种方式初始化二维数组

int arr[3][4]={1,2,3,4,5,6,7,8,9,10,11,12};

省略成员的二维数组

前面声明的二维数组每个数组成员都有对应的数值,如果省略了二维数组某些数组成员,又会如何?

int arr[3][4]={
                {1,2},
                {5,6,7},
                {9}
        };

查看反汇编代码:

7:        int arr[3][4]={
8:            {1,2},
00401038   mov         dword ptr [ebp-30h],1
0040103F   mov         dword ptr [ebp-2Ch],2
00401046   xor         eax,eax
00401048   mov         dword ptr [ebp-28h],eax
0040104B   mov         dword ptr [ebp-24h],eax
9:            {5,6,7},
0040104E   mov         dword ptr [ebp-20h],5
00401055   mov         dword ptr [ebp-1Ch],6
0040105C   mov         dword ptr [ebp-18h],7
00401063   xor         ecx,ecx
00401065   mov         dword ptr [ebp-14h],ecx
10:           {9}
00401068   mov         dword ptr [ebp-10h],9
0040106F   xor         edx,edx
00401071   mov         dword ptr [ebp-0Ch],edx
00401074   mov         dword ptr [ebp-8],edx
00401077   mov         dword ptr [ebp-4],edx
11:       };

在反汇编代码中,存储内容一目了然,对于没有填充的数组成员,缺省(默认)值为0

也就是说上面的数组等同于

int arr[3][4]={
                {1,2,0,0},
                {5,6,7,0},
                {9,0,0,0}
        };

同样对于另一种声明方式也支持不填满

int arr[3][4]={1,2,3,4,5,6,7,8,9,10};

查看反汇编代码

7:        int arr[3][4]={1,2,3,4,5,6,7,8,9,10};
00401038   mov         dword ptr [ebp-30h],1
0040103F   mov         dword ptr [ebp-2Ch],2
00401046   mov         dword ptr [ebp-28h],3
0040104D   mov         dword ptr [ebp-24h],4
00401054   mov         dword ptr [ebp-20h],5
0040105B   mov         dword ptr [ebp-1Ch],6
00401062   mov         dword ptr [ebp-18h],7
00401069   mov         dword ptr [ebp-14h],8
00401070   mov         dword ptr [ebp-10h],9
00401077   mov         dword ptr [ebp-0Ch],0Ah
0040107E   xor         eax,eax
00401080   mov         dword ptr [ebp-8],eax
00401083   mov         dword ptr [ebp-4],eax
8:    }

依旧是缺省(默认)值为0

省略维数的二维数组

前面知道了二维数组支持省略某些数组成员,同样的,二维数组也支持省略维数

int arr[][4]={1,2,3,4,5,6,7,8,9,10,11,12};

省略了维数之后,这里编译器会自动分组,这里为4个一组

在省略维数的情况下能否省略成员?

答案是可以的

int arr[][4]={1,2,3,4,5,6,7,8,9,10};

此时的编译器依旧是以4个为一组,后面不够的部分自动会补0

编译器不支持省略后面的维数,如:

int arr[][4]={1,2,3,4,5,6,7,8,9,10};

因为最后面的维数是作为组数,进行分组的

为什么使用二维数组

经过前面对二维数组初始化的了解,发现二维数组实际上和一维数组并没有什么不同,那么为什么要使用二维数组?

因为使用二维数组更为直观,方便对数据进行管理

二维数组的寻址

了解完二维数组的初始化后,再来看看二维数组如何寻址

int arr[3][4]={
                {1,2,3,4},
                {5,6,7,8},
                {9,10,11,12}
        };
int a=arr[2][3];
int i=1,j=2;
int b=arr[i][j];
int c=arr[i+j][i*2];

查看反汇编

7:        int arr[3][4]={
8:            {1,2,3,4},
0040103E   mov         dword ptr [ebp-30h],1
00401045   mov         dword ptr [ebp-2Ch],2
0040104C   mov         dword ptr [ebp-28h],3
00401053   mov         dword ptr [ebp-24h],4
9:            {5,6,7,8},
0040105A   mov         dword ptr [ebp-20h],5
00401061   mov         dword ptr [ebp-1Ch],6
00401068   mov         dword ptr [ebp-18h],7
0040106F   mov         dword ptr [ebp-14h],8
10:           {9,10,11,12}
00401076   mov         dword ptr [ebp-10h],9
0040107D   mov         dword ptr [ebp-0Ch],0Ah
00401084   mov         dword ptr [ebp-8],0Bh
0040108B   mov         dword ptr [ebp-4],0Ch
11:       };
12:   int a=arr[2][3];
00401092   mov         eax,dword ptr [ebp-4]
00401095   mov         dword ptr [ebp-34h],eax
13:   int i=1,j=2;
00401098   mov         dword ptr [ebp-38h],1
0040109F   mov         dword ptr [ebp-3Ch],2
14:   int b=arr[i][j];
004010A6   mov         ecx,dword ptr [ebp-38h]
004010A9   shl         ecx,4
004010AC   lea         edx,[ebp+ecx-30h]
004010B0   mov         eax,dword ptr [ebp-3Ch]
004010B3   mov         ecx,dword ptr [edx+eax*4]
004010B6   mov         dword ptr [ebp-40h],ecx
15:   int c=arr[i+j][i*2];
004010B9   mov         edx,dword ptr [ebp-38h]
004010BC   add         edx,dword ptr [ebp-3Ch]
004010BF   shl         edx,4
004010C2   lea         eax,[ebp+edx-30h]
004010C6   mov         ecx,dword ptr [ebp-38h]
004010C9   shl         ecx,1
004010CB   mov         edx,dword ptr [eax+ecx*4]
004010CE   mov         dword ptr [ebp-44h],edx

常数数组下标的寻址

12:   int a=arr[2][3];
00401092   mov         eax,dword ptr [ebp-4]
00401095   mov         dword ptr [ebp-34h],eax

可以看到,当指明了数组下标后,编译器就可以直接找到对应的数组成员地址

变量数组下标的寻址

14:   int b=arr[i][j];
004010A6   mov         ecx,dword ptr [ebp-38h]
004010A9   shl         ecx,4
004010AC   lea         edx,[ebp+ecx-30h]
004010B0   mov         eax,dword ptr [ebp-3Ch]
004010B3   mov         ecx,dword ptr [edx+eax*4]
004010B6   mov         dword ptr [ebp-40h],ecx

稍微分析一下这段代码

首先将 i 赋给ecx

004010A6   mov         ecx,dword ptr [ebp-38h]

然后对ecx左移4位,相当于ecx=ecx2^4=ecx16,关于左移右移的详细说明在后面

004010A9   shl         ecx,4

执行前:

在这里插入图片描述

执行后:

在这里插入图片描述
可以看到原本的ecx从1变成了0x10=16

为什么是乘以16?具体在下面的总结寻址方式里说明

接着向下看:

004010AC   lea         edx,[ebp+ecx-30h]

这里先不管ecx,看看[ebp-30h]对应什么

0040103E   mov         dword ptr [ebp-30h],1

可以发现[ebp-30h]正好对应数组的一个数组成员

所以这里便是从数组的第一个成员开始,加上ecx的偏移,先找到目标数组成员所在行数的第一个成员地址

再接着向下看:

004010B0   mov         eax,dword ptr [ebp-3Ch]

这里是将 j 的值赋给eax

再看:

004010B3   mov         ecx,dword ptr [edx+eax*4]

用前面得到的edx,也就是目标成员数组成员所在行数的第一个成员地址加上偏移:eax*4,即数组下标 × 数据宽度得到目标数组成员

然后将目标数组成员的值赋给ecx

最后:

004010B6   mov         dword ptr [ebp-40h],ecx

将ecx,也就是目标数组成员的值赋给 b

再下面的变量计算无非就是先算出值再操作,这里就不再赘述了

总结寻址方式

二维数组的寻址方式大体可分为两种:

  • 常量
  • 变量

常量

通过常量给定下标来寻址时 和 一维数组 一样,编译器可以直接通过下标来找到对应的数组成员地址

变量

相比之下,通过变量给定下标来寻址时则相对麻烦一些

为使得说明不那么抽象就拿前面的数组为例

int arr[3][4]={
                {1,2,3,4},
                {5,6,7,8},
                {9,10,11,12}
        };

首先是拿出数组的行数:3,并将这个数 × 16,为什么是乘以16?

这里的16=4*4,一个4为数组的组数,也就是arr[3][4]中的4

另一个4为数组成员的数据宽度:4(单位为字节),int类型在32位系统中占4字节

再举一个例子:

int arr[3][5]={
                {1,2,3,4,0},
                {5,6,7,8,0},
                {9,10,11,12,0}
        };

此时再查看对应的反汇编代码:

15:   int b=arr[i][j];
004010B5   mov         ecx,dword ptr [ebp-44h]
004010B8   imul        ecx,ecx,14h

可以看到原本的shl 4变成了imul ecx,ecx,14h

14h对应的十进制为20=4*5,4为数组成员的数据宽度,5则为arr[3][5]中的5

然后和一维数组的寻址有些类似,都是从数组的第一个成员地址开始,加上偏移,只不过二维数组需要二次寻址

  1. 第一次寻址找到数组成员所在行数
  2. 第二次寻址才真正找到数组成员

第一次寻址就是将通过数组第一个成员地址+ i × j × 数组成员类型的数据宽度 得到的

第二次寻址则是通过第一次寻址结果+ j*数组成员类型的数据宽度得到的

二维数组变量寻址流程图

将上述的分析画成流程图:

在这里插入图片描述

位移

前面在寻址的过程中分别用到了乘法,当乘数为2的n次方时,可以直接使用左移来实现,无需imul指令

汇编中有常用的两种位移指令:shl和shr

使用方法并没有太大的区别,这里就拿shl指令作为例子

shl指令

SHL是一个汇编指令,作用是逻辑左移指令,将目的操作数顺序左移1位或CL寄存器中指定的位数。左移一位时,操作数的最高位移入进位标志位CF,最低位补零。

运算例子:

在这里插入图片描述
乘法对应例子:

int i=1;
i=i*4;
i=i*8;
i=i*16;
8:        i=i*4;
0040103F   mov         eax,dword ptr [ebp-4]
00401042   shl         eax,2
00401045   mov         dword ptr [ebp-4],eax
9:        i=i*8;
00401048   mov         ecx,dword ptr [ebp-4]
0040104B   shl         ecx,3
0040104E   mov         dword ptr [ebp-4],ecx
10:       i=i*16;
00401051   mov         edx,dword ptr [ebp-4]
00401054   shl         edx,4
00401057   mov         dword ptr [ebp-4],edx

可以看到*4时,对应左移两位,*8则对应左移3位,*16对应左移4

乘法

imul指令

imul指令使用起来和div指令有些类似

MUL(有符号数乘法)指令执行有符号整数乘法

x86 指令集支持三种格式的 IMUL 指令:单操作数、双操作数和三操作数。单操作数格式中,乘数和被乘数大小相同,而乘积的大小是它们的两倍

例子

int i=1;
i=i*5;
i=i*6;
i=i*7;

查看汇编代码

7:        int i=1;
00401038   mov         dword ptr [ebp-4],1
8:        i=i*5;
0040103F   mov         eax,dword ptr [ebp-4]
00401042   imul        eax,eax,5
00401045   mov         dword ptr [ebp-4],eax
9:        i=i*6;
00401048   mov         ecx,dword ptr [ebp-4]
0040104B   imul        ecx,ecx,6
0040104E   mov         dword ptr [ebp-4],ecx
10:       i=i*7;
00401051   mov         edx,dword ptr [ebp-4]
00401054   imul        edx,edx,7
00401057   mov         dword ptr [ebp-4],edx

可以看到:这里使用了三操作数的imul指令,分别乘以了5、6、7

当imul指令为三操作数时,就是将第二个操作数和第三个操作数的乘积保存到第一个操作数中

拿上面的例子来说:

00401042   imul        eax,eax,5

就是(第一个操作数)eax=(第二个操作数)eax × (第三个操作数)5

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/451744.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CentOS(linux)使用Docker安装nacos

1. 拉取nacos镜像 docker pull nacos/nacos-server:2.0.3 2. 创建所需文件夹(以安装在home目录下为例) 1) 创建conf文件夹 mkdir -p /home/nacos/conf a. 新增文件application.properties(或者不增加该文件,会使用默认的) 文件内容如下: # spring server.servlet.contextP…

不知道玩什么游戏的你看过来

推荐一:原神 《原神》游戏设定在一个名为“蒂瓦特”的奇幻世界。 被神选中的人,将被赐予“神之眼”,引导“元素之力”,成为“旅者”。 在旅途中,结识性格各异、才华横溢、能力各异的小伙伴。 我们将一起战胜强大的敌…

PyTorch实战2:彩色图片识别(CIFAR10)

🍨 本文为🔗365天深度学习训练营 中的学习记录博客🍦 参考文章:365天深度学习训练营-第P2周:彩色图片识别🍖 原作者:K同学啊|接辅导、项目定制 目录 一、数据准备二、构建简单CNN网络⭐1. torch…

图扑软件 | 数字孪生智慧水泥工厂

前言 近年来,随着我国经济的发展和人民生活水平的提高,我国对于水泥行业的关注程度也越来越高,为了保证水泥行业的健康稳定发展,许多地方都在大力推动水泥生产技术创新工作。当前水泥行业的发展正处于新旧动能更迭的关键阶段&…

JavaWeb开发 —— SpringBootWeb综合案例

通过综合案例,我们来了解和学习在开发Web程序时,前端程序和后端程序以及数据库三者之间是如何交互、如何协作的,而通过综合案例也来掌握通过接口文档开发服务端接口的能力。 目录 一、准备工作 1. 需求说明 2. 环境搭建 3. 开发规范 二…

Postgis导出shp和gdb数据库(Postgre入门九)

背景 有时候我们需要将postgis数据库中的空间数据表导出shp格式,而PG自带的PostGIS Shapefile Import/Export Manager 导出shp大部分时候是可以用的,但是它有个缺点是,当shp字段名称超过10个字节时,字段会被切掉,如字段“afdskskkfkfjdj”被切掉后是“afdskskkfk”,所以…

文案自动修改软件-文案自动改写的免费软件下载

文章生成器ai写作机器人 随着人工智能技术的飞速发展,越来越多的新型产品被推向市场。其中,文章生成器AI写作机器人是一个备受关注的新兴行业。它使用机器学习和自然语言处理等技术,为用户自动生成高质量的文章和内容,帮助用户在…

基于OpenCV与深度神经网络——实现证件识别扫描并1比1还原证件到A4纸上

前言 1.用拍照的证件照片正反面,实现用证件去复印到A4纸上的效果,还有证件的格式化识别。 图1:把拍照的证件1比1还原证件到A4纸上 图2:证件OCR格式化识别 2.使用Yolo做目标识别,Enet做边缘检测,Paddle OCR做文字识别&…

【数据结构与算法】常用数据结构(一)

😀大家好,我是白晨,一个不是很能熬夜,但是也想日更的人✈。如果喜欢这篇文章,点个赞👍,关注一下👀白晨吧!你的支持就是我最大的动力!💪&#x1f4…

燃气管道定位83KHZ地下电子标识器探测仪ED-8000操作说明1

1、功能简要说明 ED-8000地下电子标识器探测仪是华翔天诚推出的一款可支持模拟电子标识器(无 ID)探测和数字 ID 电子标识器 探测两种工作模式,在模拟电子标识器(无 ID)探测模式下,可探测 所有按标准频率生…

Unity-ML-Agents安装

目录 1.下载ML-Agents 1.1 前往官网 1.2 选择版本 1.3 下载文件 2.下载Anaconda 3.虚拟环境 3.1 构建虚拟环境 3.2 创建项目,导入package.json 3.2.1 创建项目,导入package.json 3.2.2 导入成功 3.2.3 将模板项目拖入unity项目中 3.3 开始训练 …

低代码感觉很能打——可视化搭建系统,把格局做大

有人说「可视化搭建系统」说到底只是重复造轮子产生的玩具; 有人说「可视化搭建系统」本质是组件枚举,毫无意义。 片面的认知必有其产生的道理,但我们不妨从更高的角度出发,并真切落地实践,也许你会发现:我…

Java面试题总结 | Java面试题总结5- 数据结构模块(持续更新)

数据结构 文章目录 数据结构顺序表和链表的区别HashMap 和 Hashtable 的区别Java中用过哪些集合,说说底层实现,使用过哪些安全的集合类Java中线程安全的基本数据结构有哪些ArrayList、Vector和LinkedList有什么共同点与区别?ArrayList和Linke…

怎样正确做web应用的压力测试?

web应用,通俗来讲就是一个网站,主要依托于浏览器实现其功能。 提到压力测试,我们想到的是服务端压力测试,其实这是片面的,完整的压力测试包含服务端压力测试和前端压力测试。 下文将从以下几部分内容展开&#xff1a…

源码简读 - AlphaFold2的2.3.2版本源码解析 (1)

欢迎关注我的CSDN:https://spike.blog.csdn.net/ 本文地址:https://blog.csdn.net/caroline_wendy/article/details/130323566 时间:2023.4.22 官网:https://github.com/deepmind/alphafold AlphaFold2是一种基于深度学习的方法…

torch中fft和ifft新旧版本使用

pytorch旧版本(1.7之前)中有一个函数torch.rfft(),但是新版本(1.8、1.9)中被移除了,添加了torch.fft.rfft(),但它并不是旧版的替代品。 torch.fft label_fft1 torch.rfft(label_img4, signal…

25岁走出外包后,感到迷茫了.....

我认识一个老哥,他前段时间从外包出来了,他在外包干了3年左右的点工,可能也是自身的原因,也没有想到提升自己的技术水平,后面觉得快废了,待着没意思就出来了,出来后他自己更迷茫了,本…

Linux安装Jenkins搭配Gitee自动化部署Springboot项目

目录 前言一、环境准备二、全局工具配置jdk、maven、git三、配置Gitee四、新建任务-部署Springboot项目 前言 Jenkins是一款流行的开源持续集成(CI)和持续交付(CD)工具。它可以帮助开发人员自动构建、测试和部署软件应用程序&…

广州蓝景分享—快速了解Typescript 5.0 中重要的新功能

作为一种在开发人员中越来越受欢迎的编程语言,TypeScript 不断发展,带来了大量的改进和新功能。在本文中,我们将深入研究 TypeScript 的最新迭代版本 5.0,并探索其最值得注意的更新。 1.装饰器 TypeScript 5.0 引入了改进的装饰…

二、SQLServer 的适配记录

SQLServer 适配记录 说明:由于 SQLSERVER 数据库本身和MYSQL数据库有一定的语法,创表结构,物理模式等差别,在适配过程中,可能会出现各种错误情况,可参考本次适配记录。 当前环境: 适配项目:JDK11,SpringBoot服务。 适配数据库:SELECT @@VERSION,得 Microsoft SQL …