继续更新个人的学习笔记,
其它笔记传送门
逆向基础笔记一 进制篇
逆向基础笔记二 数据宽度和逻辑运算
逆向基础笔记三 通用寄存器和内存读写
逆向基础笔记四 堆栈篇
逆向基础笔记五 标志寄存器
逆向基础笔记六 汇编跳转和比力指令
逆向基础笔记七 堆栈图(重点)
逆向基础笔记八 反汇编分析C语言
逆向基础笔记九 C语言内联汇编和调用协定
逆向基础笔记十 汇编寻找C步伐入口
逆向基础笔记十一 汇编C语言基本类型
逆向基础笔记十二 汇编 全局和局部 变量
逆向基础笔记十三 汇编C语言类型转换
逆向基础笔记十四 汇编嵌套if else
逆向基础笔记十五 汇编比力三种循环
逆向基础笔记十六 汇编一维数组
逆向基础笔记十七 汇编二维数组 位移 乘法
逆向基础笔记十八 汇编 布局体和内存对齐
逆向基础笔记十九 汇编switch比力if else
逆向基础笔记二十 汇编 指针(一)
逆向基础笔记二十一 汇编 指针(二)
逆向基础笔记二十二 汇编 指针(三)
逆向基础笔记二十四 汇编 指针(五) 系列完结
指针四
指针数组
什么是指针数组
首先回顾一下先前关于数组的知识:
所谓数组就是用于存储相同数据类型的聚集
再联合先前关于指针的知识:指针的本质也是一种数据类型
于是当数组中存储的成员的数据类型为指针时,该数组就可以称为指针数组(本质是数组)
代码
#include "stdafx.h"void function(){ int** arr[5]={(int**)1,(int**)2,(int**)3,(int**)4,(int**)5};}int main(int argc, char* argv[]){ function(); return 0;}反汇编代码
9: int** arr[5]={(int**)1,(int**)2,(int**)3,(int**)4,(int**)5};00401038 mov dword ptr [ebp-14h],10040103F mov dword ptr [ebp-10h],200401046 mov dword ptr [ebp-0Ch],30040104D mov dword ptr [ebp-8],400401054 mov dword ptr [ebp-4],5小总结
- 可以看到指针数组其实并没有什么特别之处,只不外存储的数构成员的数据类型为指针而已
- 指针数组的赋值也和先前对指针的赋值没有什么区别
布局体指针
什么是布局体指针
所谓布局体指针就是在布局体后加上多少个*使其称为一个指针类型
代码
#include "stdafx.h"#include struct S1{ int a;};void function(){ S1* s1=(S1*)0x12345678; printf("%x\n",s1);}int main(int argc, char* argv[]){ function(); return 0;}运行结果
结果分析
可以看到,这里关于布局体指针的使用貌似和普通的指针没有什么区别,但此时会发现这里还没有操作布局体内部的成员
所以布局体指针的现实使用也并不是如许,下面看一个错误的例子
错误代码
void function(){ S1* s1=(S1*)0x12345678; int a=s1->a;}只是在上面代码的基础上添加了一个读取布局体成员的语句,查看运行结果
运行结果
运行结果不出所料出错了,开始分析错误的原因
反汇编代码
14: S1* s1=(S1*)0x12345678;00401038 mov dword ptr [ebp-4],12345678h15: int a=s1->a;0040103F mov eax,dword ptr [ebp-4]00401042 mov ecx,dword ptr [eax]00401044 mov dword ptr [ebp-8],ecx反汇编分析
0.实验前s1和s1->a的状态
s1:
s1->a:
1.为布局体指针s1赋值
14: S1* s1=(S1*)0x12345678;00401038 mov dword ptr [ebp-4],12345678h
此时再看看s1->a:
可以发现对s1的赋值操作,改变的不是s1->a的值,而是改变了s1->a的地点
其实从实验前s1和s1->a的状态就可以看出,s1存储的内容并不是直接存储布局体成员的内容,而是存储指向布局体成员的地点
所以这里对于先前对于s1的赋值操作改变的只是成员的地点,而没有改酿成员的值
并且刚开始时,布局体成员并没有被分配对应的内存地点
2.访问s1->a
15: int a=s1->a;0040103F mov eax,dword ptr [ebp-4]00401042 mov ecx,dword ptr [eax]00401044 mov dword ptr [ebp-8],ecx此时出错的原因已经显而易见了,先前对s1的赋值操作修改了s1->a的地点,使其指向了一个不可访问的地点而导致出错
精确代码
前面已经知道了出错的原因是访问了不可访问的地点导致出错,并且刚开始布局体成员没有被分配对应的内存地点
于是只要手动为布局体成员分配内存地点即可,这里将使用到malloc函数来进行分配内存地点
malloc函数
void *malloc(size_t size)参数:size,内存块的大小,以字节为单位
返回值:返回一个指针 ,指向已分配大小的内存。如果请求失败,则返回 NULL
相干头文件:malloc.h、alloc.h、stdlib.h
大抵了解了malloc函数,如今来看代码:
#include "stdafx.h"#include //这里使用了malloc.hstruct S1{ int a; int b; int c;};void function(){ S1* s1=(S1*) malloc(sizeof(S1)); //申请一块空间大小正好为S1大小的内存 s1->a=610; s1->b=666; s1->c=52; printf("%d\n",s1->a); printf("%d\n",s1->b); printf("%d\n",s1->c);}int main(int argc, char* argv[]){ function(); return 0;}运行结果
可以看到布局体的成员可以或许正常地被改写和访问
反汇编代码
15: S1* s1=(S1*) malloc(sizeof(S1));0040D778 push 0Ch0040D77A call malloc (00401150)0040D77F add esp,40040D782 mov dword ptr [ebp-4],eax16: s1->a=610;0040D785 mov eax,dword ptr [ebp-4]0040D788 mov dword ptr [eax],262h17: s1->b=666;0040D78E mov ecx,dword ptr [ebp-4]0040D791 mov dword ptr [ecx+4],29Ah18: s1->c=52;0040D798 mov edx,dword ptr [ebp-4]0040D79B mov dword ptr [edx+8],34h反汇编分析
1.先看这个malloc函数
15: S1* s1=(S1*) malloc(sizeof(S1));0040D778 push 0Ch0040D77A call malloc (00401150)0040D77F add esp,40040D782 mov dword ptr [ebp-4],eax
- 压入了参数0C,对应十进制为12,也就是S1的大小
- 调用malloc函数
- 堆栈外均衡
- 将返回值eax赋值给S1
看看返回值eax的内容:
可以看到eax就对应了布局体中的成员
eax=布局体成员首地点,里面的布局体成员连续存储
2.赋值,将610对应十六进制262赋值给[eax],对应前面的003807B8
16: s1->a=610;0040D785 mov eax,dword ptr [ebp-4]0040D788 mov dword ptr [eax],262h实验后:
3.赋值,将666对应十六进制29A赋值给[ecx+4],对应前面的003807BC
17: s1->b=666;0040D78E mov ecx,dword ptr [ebp-4]0040D791 mov dword ptr [ecx+4],29Ah实验后:
4.赋值,将52对应十六进制34赋值给[edx+4],对应前面的003807C0
18: s1->c=52;0040D798 mov edx,dword ptr [ebp-4]0040D79B mov dword ptr [edx+8],34h实验后:
小总结
- 布局体指针和普通的指针现实上并没有什么不同
- 在对布局体成员进行操作时,须要先对其进行初始化(为每个布局体成员分配内存地点)
- 布局体指针并不直接存储布局体成员,而是存储了指向布局体成员的地点,该地点里存放着所有布局体成员
数组指针
前面学了指针数组,如今又来个数组指针,中间用布局体指针作了过渡,避免混淆
什么是数组指针
所谓数组指针,就是指向数组的指针(本质是指针)
既然是指针天然满意先前指针的一切特性:指针的赋值、指针的数据宽度、指针的加减、指针类型相减、指针之间比力
这里就不再赘述先前的内容,有须要可回顾:逆向基础笔记二十 汇编 指针(一)
数组指针的声明
int (*px)[2];声明如上,数组指针变量为px,类型为:int(*)[2];该数组指针指向的数组为int[2]
数组指针和指向数组的指针区别
代码
#include "stdafx.h"void function(){ int arr[6]={1,2,3,4,5,6}; //声明一个数组指针,该指针指向数组为:int[2] int (*px)[2]; //给数组指针赋值,使该数组指针指向arr数组的首地点 px=(int (*)[2]) &arr[0]; //用一个临时变量parr2 存储数组指针 int (*parr2)[2]=px; //*px为数组的首地点,也就是arr,这里就相当于int* arr2=arr;此时的arr2就是指向数组的指针 int* arr2=*px; //初始化变量,准备循环 int i; //循环遍历数组 for(i=0;i |