zoukankan      html  css  js  c++  java
  • windows运算符和数据类型重载反CE查询搜索

      1、windows下搞逆向,一般都是从数据搜索开始的。数据搜索最常见的工具就是CE了;由于CE自己重写了关键的系统函数,想从驱动层完全杜绝CE读内存数据很难,怎么才能在应用层防止或干扰CE读取自己的内存了?

           

       要反CE搜索,就要先学习CE搜索的原理和数据在内存存放的原理;这里以32位CPU为例:总所周知,理论上讲,内存的读写速度只有cpu寄存器的1/100左右,所以cpu的厂家设计了各种缓存来存储cpu从内存读取的数据,同时也会尽量减少从内存读数据;为了高效从内存读数据,cpu一般都是按照32/8=4字节对齐来写内存数据的,char、byte、short等不满4字节的也占用4自己的空间,比如:

      struct{
           char a;
           short b;
            int  c;
      }

      char占用1字节,short占用2字节,int占用4字节,整个结构体占用7字节???  哈哈,哪有这么简单啊! 为了4字节对齐,char和short一共占用4字节,int占用4字节,那么这个结构体一共占用8字节。cpu读内存的时候按照4字节的颗粒度读取(地址线是32位的),也就是每次读连续的4字节,这也刚好是int的长度 CE搜索的类型展示如下: 

          

        CE搜索也要通过CPU去读内存的数据,所以也是按照4字节的宽度从内存读数据的!然后根据用户搜索不同的数据长度做拼接或裁剪;这里以int为例,CE会按照4字节的对齐方式和颗粒度在内存搜索,所以这里反CE的方式也就很明显了:

    •  对数据加密:在屏幕显示的数据并不直接存放在内存,而是简单加密,比如异或某个数字。需要在屏幕显示的时候再异或解密
    •    或则不加密,把int整型的4个字节分别存放在4个不同的、不连续的内存空间,然后自己重载所需的算术运算;
    •    或则把int的4个字节存在4个连续的byte空间,但顺序打乱,读取的时候自己恢复

      这里顺便扩展一下:我们用椭圆曲线做非对称加密的时候,密钥一般不低于128位,远超了普通int 32位的长度。为了表达128位的私钥,专门定制了bigNum类型来保存私钥!其原理和这里是一样的,没有本质区别!

      下面代码演示通过重载int类型、算术运算来反CE搜索!先写段简单的代码模拟被攻击少血的场景:角色有1500滴血,每被攻击一次就减少50滴血;

    #include <iostream>
    
    int main()
    {
        int hp = 1500;
        while (true) 
        {
            hp = hp - 50;
            printf("剩余血量:%d
    ",hp); 
            system("pause");
        }
        
    }

      由于是int类型,在CE里面选择4字节的精准搜索;这里很容易就搜到了,然后改成任意数字;

        

          通过CE的”find out what access this address“还能进一步定位到读写这个内存地址的代码,如下:

          

      这里甚至可以把sub改成add,导致血量越来越多:

       

      血量越来越多:

       

       2、先在怎么通过重载反CE搜索了?

       (1)重写int类型

        上面说了,CE是从4字节对齐的地址一次性读取4字节来搜索和比对的。这里有两种思路:4字节还是挨着,但是顺序打乱;或4字节顺序不打乱,但是地址随机分配,隔开了!这里为了节约内存、便于管理,我们采用第一种思路,即4字节还是挨着,但是人为把字节存储的顺序打乱!核心代码如下:

    myInt::myInt(int val) 
    {
        mem[0] = new char;
        mem[1] = new char;
        mem[2] = new char;
        mem[3] = new char;
    
        *this = val;//只有一个成员变量,所以*this就是取这个成员变量的值
    }
    
    void myInt::operator=(int val)
    {
        /*
        1、把原int顺序打散
        2、mem这4个byte还是顺序相接的
        */
        char* read = (char*)&val;
        mem[0][0] = read[3];
        mem[1][0] = read[1];
        mem[2][0] = read[0]^0xa5;
        mem[3][0] = read[2];
    }

      这里重载了赋值号,方便给成员变量赋值;同时也打乱了原有int中4字节的顺序来存储;为了显示打印,还有做四则运算,还需要重载int类型,核心代码如下:

    myInt::operator int()
    {
        int val;
        char* read = (char*)&val;
        read[3] = mem[0][0];
        read[1] = mem[1][0];
        /*255以内的数,1个字节和4个字节的效果是一样的,所以这里用个随机数加密一下,避免被CE搜索到*/
        read[0] = mem[2][0]^0xa5;
        read[2] = mem[3][0];
        return val;
    }

      有一点需要注意:如果数字在255以内,也就是只用了1字节,那么打乱顺序也是没用的,所以这里随机选一个数给最后一个自己的数加密;

           完整的代码如下:

      (1)自定义myInt类型来重载int类型的数据变量。头文件如下:

    #pragma once
    class myInt
    {
        char* mem[4];
    public:
        myInt(int val = 0);
        ~myInt();
        operator int();
        void operator=(int val);
    };

      myInt.cpp实现如下:

    #include "myInt.h"
    
    //内存分配io效率低,建议一次性分配足够空间,每次按需取用就行
    myInt::myInt(int val) 
    {
        mem[0] = new char;
        mem[1] = new char;
        mem[2] = new char;
        mem[3] = new char;
    
        *this = val;//只有一个成员变量,所以*this就是取这个成员变量的值
    }
    
    myInt::~myInt() 
    {
        delete mem[0];
        delete mem[1];
        delete mem[2];
        delete mem[3];
    }
    
    myInt::operator int()
    {
        int val;
        char* read = (char*)&val;
        read[3] = mem[0][0];
        read[1] = mem[1][0];
        /*255以内的数,1个字节和4个字节的效果是一样的,所以这里用个随机数加密一下,避免被CE搜索到*/
        read[0] = mem[2][0]^0xa5;
        read[2] = mem[3][0];
        return val;
    }
    
    void myInt::operator=(int val)
    {
        /*
        1、把原int顺序打散
        2、mem这4个byte还是顺序相接的
        */
        char* read = (char*)&val;
        mem[0][0] = read[3];
        mem[1][0] = read[1];
        mem[2][0] = read[0]^0xa5;
        mem[3][0] = read[2];
    }

      (2)使用myInt类型的代码如下:

    #include <iostream>
    #include "myInt.h"
    
    myInt hp = 1500;
    int main()
    {
        //myInt hp = 1500;
        while (true) 
        {
            hp = hp - 50;
            //printf("剩余血量:%d
    ",hp); 
            std::cout << "剩余血量:" << hp;
            system("pause");
        }
        
    }

      效果:这里用CE已经搜不到了!

         

       这种方式可以有效反CE搜索,但是由于每次调用生成myInt类型数据都要分配、“逆序”读写内存,大量的IO操作会导致效率很低,所以只适用于关键数据的保护,比如key之类的!

    扩展:这里使用了随机数组来操控内存地址。数组是一种非常简单的数据存储结构或方式,最大的特点就是数据是连续存储的;举个例子:char  a[5],内存中的存储方式如下:

           

       这个很简单,大家都能理解。因为本文重载int类型用了二维数组,所里这里有必要探究二维数组的存储方式,比如char a[5][5],大部分同学理解的存储方式是如下的:

         

       可能是为了方便开发人员理解,上图这里实际上是抽象出来的逻辑存储方式。在实际的物理内存中也是这样存储的么?我们做个小测试打印出来看看:

      

       看到了么,实际上是挨着存储的,并不是我们想象的在物理内存中也是“二维”存储的!数组元素的下标仅仅是索引,不代表实际的物理存储方式!所以代码中的mem[0][0]、mem[1][0]、mem[2][0]、mem[3][0]就是通过这种方式隔开存储的!

           

  • 相关阅读:
    vue中路由跳转传递参数
    父组件向子孙组件传递数据provide/inject
    微信、QQ等内置浏览器定位失败
    Java ArrayList类
    java 生成 [1, n] 之间的随机数
    Java 构造方法
    Java this关键字
    Java private关键字及作用
    Java 随笔
    Java 内存划分
  • 原文地址:https://www.cnblogs.com/theseventhson/p/14691347.html
Copyright © 2011-2022 走看看