第五章 3 指针(110)

一 内存与指针

1、 内存是什么?

从硬件形态上说,内存就是一条形物理设备,从功能上讲,内存是一个数据仓库,程序内在执行前都要被装载到内存中,才能被中央处理器执行。
内存是由按顺序编号的一系列存储单元组成的,在内存中,每个存储单元都由唯一的地址,通过地址可以方便地在内存单元中存储信息。
在计算机中,一切信息都是以二进制数据的形式体现的,每个内存单元的容量是1B,即8bit(8个0、1二进制位)。

2、内存与CPU读写速度快,断电就没有了,容量比较小,成本比较高,高级服务器,读写很频繁的文件全部存放内存
3、把一个函数名通过%x打印出main的首地址

4、一个程序载入内存,代码数据都有地址,外挂就是调用函数,修改数据
而函数就是代码,变量就是数据
5、32位的计算机最多的内存容量是4G

6、变量名就是对内存一段空间里面数据的抽象

指针变量可以指向任何的一个变量

int x=10;
int *p=&x; //p是一个指针变量,p可以是任何变量的地址




外挂强制修改(用dllinject注射)

二 指针详解

1、指针变量在使用之前必须进行初始化。
int num=100;
int *p;
p=# //非法,可以编译,运行报错,会把100当做一个地址

2、指针只是一个地址,大小是固定的,就是四个字节。
int *p1;
double *p2;
char *p3;
sizeof(p1); //结果为 4
sizeof(p2); //结果为4
sizeof(p3); //结果为4

3、 指针和地址的区别

两个要点:一、指针是个量,对应着一块内存区域,二,指针存储的信息是某个内存单元的地址。

比如: int num=10;
int *p=#

//&num 是一个地址,是一个常量
//而p是一个指针变量,可以存储一个地址

比如300500是一个地址,
int *p=(int *)300500 是一个指针,p存储的是地址,指针有类型,从哪里开始,长度是多少,从哪里结束,得知了类型以后,就知道这片内存数据是如何解析

4、指针变量的声明与初始化

在声明一个指针后,编译器并不会自动完成其初始化,此时,指针的值是不确定的,也就是说,该指针指向那块内存单元是完全随机的。

如果在指针变量声明之初确实不知道该将此指针指向何处,最简单的方式是将其置“0”,C语言中提供了关键字NULL

其基本形式为:
类型* 指针变量名;

int *pNum=NULL;

值为NULL的指针称为空指针,这意味着,指针并不指向任何地址。
在头文件 stdio.h 中,NULL 定义为常量。

三 间接访问和直接访问

1、取地址运算符&, 间接运算符*

&运算符: 取地址运算符,&m即是变量m在内存中的实际地址。

*运算符: 指针运算符 (通常称为间接引用运算符),它返回其操作数 (即一个指针)所指向的对象的值.
如图所示:
&num 是直接访问变量num的地址,而 (&num)中则是间接访问

2、 直接访问: 按变量地址存取变量值

间接访问: 通过存放变量地址的变量去访问变量

定义指针变量p, &num 直接访问num 的地址,*p通过num的地址间接的访问变量num

四 打印指针地址

1、地址格式符:%p %x

%x: 按照十六进制打印,无意义的0就不打印
%p: 显示地址的位数,32位,8个十六进制,2^4=16,32位
显示地址的位数,64位,16个十六进制位,64个二进制位

int num=10;
int *p=#
printf("%x,%x",p,&num);
printf("\n%p,%p",p,&num);

打印的格式:

32为的情况下:

64位的情况下:

五 scanf初始化指针


指针不可以乱指,否则会程序崩溃

六 指针与函数参数

c语言要改变外部变量只有传地址,java,c++有引用,c没有

数组当做参数的时候,传递的是指针

数组作为参数的时候,改变的是原来的数组

七 指向指针的指针(二级指针)

作用

1、 指针变量也是变量,占据一定的内存空间,有地址,因此可以用一个指针指向它,这称为指向指针的指针,或二级指针。

2、 函数形式参数,除了数组以外,传递的任何数据,变量,都会新建一个变量接收传入的变量的值。不影响原来的变量,如果是一个数据,传递数据的地址(指针),如果是一个指针,传递指针的地址。

db是double类型的变量。

运行结果:

可以发现main函数的 p的地址和 change函数的 p的地址不相同,
而是新建一个变量接收传入的变量的值

3、 *pp=&bd 则改变了指针的指向,**pp=bd

4、二级指针在外挂应用较多
编写一个外挂程序,通过二级指针改变一级指针的指向。

程序的部分代码

运行生成一个exe程序,打印出各个变量的地址,每3秒打印一次。

要向上面这个程序中注入dll。通过二级指针改变一级指针的指向来改变
创建外挂:需要建模块,模块不需要main函数
新建项目,右键属性将配置类型改为动态库(dll)。 将目标文件名改为
goA
需要用到_declspec(dllexport) dll函数导出 接口

需要改的是指针那么就需要用到二级指针
打印的结果

类型不匹配有时会出现偶然的成功现象

但大多数情况是失败的

八 指针的类型和指针所指向的类型

1、 所谓指针类型,指的是声明指针变量时位于变量名前的“类型*”,而所谓指针所指向的类型,指的是为指针初始化或赋值的变量类型。

2、 不是同一类型的指针,不可以任意赋值。
不同的数据类型,大小不一样(如果强制赋值的话,就会少读取或多读取,内存有很多垃圾0,1),解析方式不一样

(1) p1、px 是同一类型的指针,解析px并没有什么错误

(2) p1、p2 不是同一类型的指针,此时,打印出p2则并不是A.

3、 同类型指针的赋值
这是最常见的一种情况,如所示,pN1和pN2是两个相同类型的指针,执行“pN2=pN1;”这样一个赋值操作后,pN1和pN2指向同样的地址,也就是说,两个指针指向同一个内存单元,对pN2的任何改动都会影响pN1的值,反之亦然。

九 指针的类型和指针所指向的类型不同

1、 指针除了地址以外还有类型,类型决定了指向的数据大小,决定了数据的解析方式。指针的类型必须要与指针指向的类型一致,一定会出现偏差。即使地址相同,大小相同。

2、 (1)指向内存字节数大于指针类型占据的字节数

指针的类型必须要与指针指向的类型一致,不一致,大小不一样,解析方式不一样。
指针的类型double的大于指针指向的类型int

	运行结果:

2)指向内存字节数小于指针类型占据的字节数

指针的类型int的小于指针指向的类型double

	运行结果:

十 指针变量的值

1、 指针变量的值:
是指针本身存储的数值,这个值将被编译器当作一个地址,而不是
一个一般的数值。在32位程序里,所有类型的指针的值都是一个32位整数,因为32位程序里内存地址长度都为32位。
指针所指向的内存区:
就是从指针的值所代表的那个内存地址开始,长度为sizeof(指针所指向的类型)的一片内存区。
指针存储的是地址,地址是首地址,从哪里开始,从哪里结束,由类型决定,
类型决定长度,决定如何解析。

2、自己编写外挂改变,通过另外一个程序改变这个进程。
在上面的代码中添加如下代码,使其打印数据:
while(1)
{
printf("我的级别是%c,我的年龄是%d,我的人民币是%fW",ch,num,db);

Sleep(5000);

}
要注入的dll程序
通过指针直接访问变量的地址,使其通过注入的方式,从而改变它的值,从而改变另一个程序的运行结果

原文地址:https://www.cnblogs.com/xingkongcanghai/p/10413872.html