• 指向指针的指针


    • 占有内存空间就有地址,有地址就可以被指针指向,如果指针作为一个参数,那么改变它就需要指针的地址,指针的指针在这种场景下就应孕而生

    注意,命令double *pp = &p;

    在c++中编译错误,在c中也会产生警告信息

    void main()
    {
        int earning = 12000;
        double *p = &earning;
        //double *pp = &p;//p的地址为4个字节,pp指向的数据类型double占用8个字节
        //pp++执行后前进8个字节 p++前进4个字节,不匹配所以,此行代码非法
        printf("p的大小是%d,double类型大小%d
    ", sizeof(p), sizeof(double));
        double**pp = &p;//这样引入了一个崭新的数据类型(double*)double指针类型
        printf("p的大小是%d,double*  指针类型大小%d
    ", sizeof(p), sizeof(double*));
        system("pause");
    }

     一级指针装载变量的地址

    二级指针装载指针地址,由此我们可以改变指针的指向,游戏外挂中使用很多。

    • 模拟游戏外挂原理
    第一步:

    准备一段代码模拟苏联图-128截击机的载油情况,这段代码试图捕捉到代码执行过程中各关键变量的地址,然后交给指针的指针修改 #include<stdio.h> #include<stdlib.h> void main() { int fuelindex = 100; int fuelindex_m = 66; int fuelindex_l = 23; printf("fuelindex:%x ",&fuelindex); printf("fuelindex_m:%x ", &fuelindex_m); printf("fuelindex_l:%x ", &fuelindex_l); char fuellevelhigh = 'h', fuellevelmedium='m', fuellevellow='l'; printf("高级载油量地址为:%x ", &fuellevelhigh); printf("中级载油量地址为:%x ", &fuellevelmedium); printf("低级载油量地址为:%x ", &fuellevellow); char *fuellevel = &fuellevelhigh; system("pause"); }

    输出结果:

     第二步:编写模块

    如果你使用了微软的vs2013集成开发环境,选中解决方案----添加----新建项----visual C++-----常规-----空项目,命名完成后,选中源文件----添加----新建项,开启一个.c文件,我命名为gametrick.c,

    然后选中gametrick.c----属性----配置属性----目标文件扩展名选择.dll

    往这个文件写入一个函数,在函数体之前添加 _declspec(dllexport);(尽管到目前为止,我也弄不清这是什么,但是就先加上吧)

    _declspec(dllexport) void go()
    {
        //int *p = 0xcffbcf;//这样还不够,因为仅仅知道表示地址的值0xcffbcf,但并不知道是什么类型
        //也就无从知道执行前进操作时,前进几步
        //所以
        int *p = (int *)0x12ff964;
        *p = 87;
    }

    写完后在IDE 找到生成--生成,这样就生成了一个动态链接库文件

    接下来运行你的主程序,打开DLL注入文件,选中主程序文件对应的那个进程

    #include<stdio.h>
    #include<stdlib.h>
    #include<Windows.h>
    
    
    void main()
    {
        int fuelindex = 100;
        int fuelindex_m = 66;
        int fuelindex_l = 23;
        printf("fuelindex:%x
    ",&fuelindex);
        printf("fuelindex_m:%x
    ", &fuelindex_m);
        printf("fuelindex_l:%x
    ", &fuelindex_l);
    
        char fuellevelhigh = 'h', fuellevelmedium='m', fuellevellow='l';
        printf("高级载油量地址为:%x
    ", &fuellevelhigh);
        printf("中级载油量地址为:%x
    ", &fuellevelmedium);
        printf("低级载油量地址为:%x
    ", &fuellevellow);
        char *fuellevel = &fuellevelhigh;
        while (1)
        {
            printf("
    载油量是%d,%c", fuelindex, fuellevelhigh);
            Sleep(3000);//为避免打印过于频繁,需要暂缓执行,引入windows.h函数
        }
        system("pause");
    }

    注入,生成的模块

     看到图中第一行显示的载油量地址为 12ff964,添加到模块文件时,需要改为0x12ff964

     通过指针的指针改变值,不知你是否也这样倒霉,弄了一下午,就是改不对

    _declspec(dllexport) void cpc()
    {
        //char*p = 0xcffbcf;//这样还不够,因为仅仅知道表示地址的值0xcffbcf,但并不知道是什么类型
        //也就无从知道执行前进操作时,前进几步
        //所以
        char **p = (char **)0xeffd47;
        *p = (char*)0xeffd3b;
    }

    这种改法,感觉很不稳定

     总之指针的指针赋值的时候:

    类型适配规范:

    下列代码侥幸会成功

    #include<stdlib.h>
    
    void main()
    {
        char mark = 'Z';
        char *tomark = &mark;
        char **tochar = tomark;
        printf("%x", tomark);
        system("pause");
    }

    而下列代码即使运行时不报错,也一定会导致异常结果

    void main()
    {
        double mark = 3.1415826;
        double *tomark = &mark;
        double *tochar = &tomark;
        printf("mark大小:%d,tomark大小:%d,tochar大小:%d
    ", sizeof(double *), sizeof(&mark), sizeof(tomark));
        printf("tochar的结果:%lf
    ",*tochar);
        getchar();
    }

    输出结果:

     

     究其原因是一个一级指针A变量,如果存入了另一个一级指针B的地址,而不声明类型的话,调用指针A(**A)时很可能因为不知道往前读几个字节而导致对值读取的失败。(也有观点认为会造成溢出,从而造成实际结果不准确)

    有影像资料显示,2014年时,vs2013平台上该上机实验会导致报错,现在是2019年可能c语言的标准发生了一些改变,或者微软做了某些优化处理,但类型不匹配导致的数值不准确的风险的确存在!!!

    void main()
    {
        double mark = 3.1415826;
        double *tomark = &mark;
        double *tochar = (double *)tomark;//所以这一步的类型强转必不可少
        printf("tochar的结果:%lf
    ", *tochar);
        getchar();
    }

     输出结果:

     这种四舍五入是正常的,因为double只能容纳8位

    这一问题也引出了C指针的使用原则,要避免指针存储与自身数据类型不同的值的情况(说人话就是别用字符型指针取存储整型值的地址,反之亦然),因为数据类型不同,分配内存大小不同

    很可能出现读取不完整或者读取过量的情况,如果一个double*类型数据去读取一个char类型数据,看起来没什么问题,但别忘了内存中存在大量垃圾数据!也可能一并读进去了。

    • 同类型指针之间相互赋值
    void main()
    {
        int mynum = 8;
        int *hisnum = &mynum;//?木马的控制端
        int *hernum = hisnum;//相当于木马的被控制端?
        *hernum = 88;
        printf("数据通信,共用一个变量mynum:%d,*hisnum:%d,*hernum:%d
    ", mynum, *hisnum, *hernum);
        getchar();
    }
    • 为什么指针和要读取的数据要同一类型
    void main()
    {
        int mynum = 8;
        double *p = &mynum;
        printf("指针p指向%x,该地址上存的值为%f
    ",p,*p);//这句注意为了再现错误,一定要用占位符%f
        getchar();
    }

    输出结果:

     这又引出一个重要原则,两个指针地址一样,只能说明首地址一样,但是别忘了指针类型不同,因此结束的地址并不相同(长度不同,前进距离不同)解析的方式也不一样,所以不能混用!!

    •  应用借助DLL注册工具----给列兵瓦洛佳授予军籍等级(小提示:使用外挂时,不从vs2013IDE运行主程序,因为编译外挂程序时,会中断运行中的主程序,而外挂需要运行中的变量的内存地址

    从vs项目文件夹中运行主程序.exe)

    #include<stdio.h>
    #include<stdlib.h>
    #include<Windows.h>
    void main()
    {
        int a[10] = {1,2,3,4,5,6,7,8,9,10};
        int *p = a;
        printf("数组的首地址%x,二级指针地址为%x
    ", a,&p);
    
        while (1)
        {
            printf("当前军籍%d级
    ", *p);
            Sleep(3000);
        }
        getchar();
    }
    外挂程序
    _declspec(dllexport) void cpclovesme()
    {
        int **p = (int **)0xaffe68;//这一地址是指向数组首元素地址的指针的地址,也就是主程序中的&p
        *p = (int*)(0xaffe74 + 36);//0xaffe74是数组首元素的地址,这行的意义在于让p指向数组的第10个元素的地址,因为整型占4个字节,9个单位就是9*4=36
    }

    运行结果

  • 相关阅读:
    Kubernetes之Replica Set
    Kubernetes之Replication Controller
    Kubernetes之Deployment
    golang channel select
    golang slice
    epoll的由来
    ceph crush 之 crush_do_rule
    libevent
    P2P资料
    混沌理论学习笔记
  • 原文地址:https://www.cnblogs.com/saintdingspage/p/11944389.html
Copyright © 2020-2023  润新知