《C++反汇编与逆向分析技术揭秘》笔记-第8章数组和指针的寻址

若没有另外说明,都是运行在VS2022,x86,Debug下。

8.1数组在函数内
1.对于数组的识别,应判断数据在内存中是否连续(数组排列顺序由低地址到高地址)且类型一致。
2.字符串本身就是数组,该数组最后一个数据统一使用\0作为字符串结束符。为字符串类型的数组赋值(初始化)其实就是复制字符串的过程。这里不是单字节复制,而是每次复制一个寄存器大小的数据,因为两个内存间的数据传递需要借用寄存器,而每个寄存器一次可以保存4或8字节的数据(如eax或rax),如果以单字节的方式复制就会浪费3或7字节的空间,而且多次数据传递也会降低执行效率。
3.例子如下。分析:在最后一次不等于4字节的数据复制过程中按照1或者2字节的方式复制即可。

 

8.2数组作为参数
1.当数组作为函数形参时,函数参数中保存的时数组的首地址,这是一个指针变量。注意,实参数组名为常量值,sizeof()可以获取数组大小;而指针或者形参数组为变量,sizeof只能得到当前平台的指针长度,strlen()可以获取字符串长度。
2.例子如下(Release版)。分析:
(1)报错【 'strcpy': This function or variable may be unsafe. Consider using strcpy_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS】 
解决如下。


(2)报错【Run-Time Check Failure #2 - Stack around the variable 'buffer' was corrupted】
解决:这是超出了数组范围,增大数组大小。
(3)字符串处理函数(如strlen()、strcpy()等)优化后,被编译为内联函数,使用循环取代函数调用,因此没有了函数调用指令call。
(4)字符串初始化优化后,利用16字节的xmm寄存器,效率更高。

 

 

8.3数组作为返回值
1.数组作为函数的返回值与作为函数的参数,都是将数组的首地址以指针的形式进行传递。不同在于,当数组作为参数时,其定义所在的作用域必然在函数调用外,在调用前就已经存在;而当数组作为返回值时,存在隐患,因为数组是作为局部变量存在的,其内存空间在当前函数的栈内,函数退出时平衡栈,虽然使用eax保存的数组首地址存在,但地址中的数据不稳定,随时可能被其他对栈空间的读写操作而破坏。
2.例子如下,VS可能会警告【返回局部变量或临时变量的地址: buffer】。解决:可以使用全局数组、静态数组或上层调用函数中定义的局部数组。

 

8.4下标寻址和指针寻址
1.指针寻址方式要经过2次寻址,第一次取指针的内容即目标数据的地址,第二次取目标数据。而下标寻址方式只需要1次寻址,因为数组名本身就是常量地址。所以指针寻址效率较低。
2.计算数组某下标元素地址,以int arr[3]为例:arr[i]地址=arr[0]地址+sizeof(int)*i。
3.下标值可以使用3中类型表示:整型常量、整型变量、计算结果为整型的表达式。当下标为变量或者变量表达式时,会发生2次寻址,与指针寻址不同,第一次访问下标。
4.编译器不会对数组下标进行访问检查,当下标值小于0或大于数组下标最大值时,会访问到数组邻近定义的数据,造成越界访问,VS可能会警告【正在从 "buffer" 读取无效数据】【索引“15”超出了“0”至“11”的有效范围(对于可能在堆栈中分配的缓冲区“buffer”)】。
5.例子如下。

8.5多维数组
1.编译器将多维数组通过转化重新变为一维数组,也就是在内存中根本没有多维数组。多维数组的出现只是为了方便开发者计算偏移地址、寻址数组数据。
2.计算多维数组某下标元素地址,以int arr[2][3]为例:arr[i][j]地址=arr[0]地址+sizeof(int)*3*i+sizeof(int)*j。
3.例子如下。


4.Relase版如下。分析:
(1)初始化优化:由于数据一样,使用了公共表达式进行优化。
(2)寻址优化:通过公式转换,节省了一次寻址计算,提升效率。如int arr2[2][2]:arr[i][j]地址=arr[0]地址+sizeof(int)*2*i+sizeof(int)*j=arr[0]地址+sizeof(int)*(2*i+j)。

8.6存放指针类型数据的数组
1.指针数组主要用于管理同种类型的指针,一般用于处理若干字符串。使用指针数组处理多字符串数据更加方便、简洁、高效。
2.例子如下。分析:
(1)报错【"const char *" 类型的值不能用于初始化 "char *" 类型的实体】
解决如下。


(2)指针数组中存储的各字符串的首地址,而二维字符数组中存储的是各字符串的字符数据,这是它们之间本质的不同。

 

8.7指向数组的指针变量
1.数组指针保存了数组首地址,指针加法:指针变量+=数值等价指针变量地址数据+=sizeof(指针类型)*数值。
2.例子如下。分析:
(1)使用指针p1指向二维字符数组arr1,p1保存arr1首地址,数组指针p1类型为char[10],其偏移长度10字节。若要获取单个字符数据,要2次寻址。
(2)使用指针p2指向一维指针数组arr2,p2保存arr2首地址,二级指针p2类型为指针类型,其偏移长度4字节。若要获取单个字符数据,要3次寻址。

 

8.8函数指针
1.函数指针与函数调用最大区别在于函数是直接调用的,而函数指针需要取出指针变量中保存的地址数据,间接调用函数。
2.例子如下。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值