zoukankan      html  css  js  c++  java
  • 模仿Cheat Engine内存搜索——(Sunday算法)

    想要获取一个进程里面的某个数据,需要先知道这个数据的位置
      对于全局变量:偏移是固定的,可以通过“基址+偏移”直接定位
      对于局部变量:位置是随机的,只能通过拦截或者搜索去定位

      分析企业微信的时候,发现很多数据都不是全局变量,不能像个人微信那样直接跨进程读取。如果能够用代码模仿Cheat Engine进行内存搜索,就可以定位到局部变量,进而实现非注入读取。甚至可以做到兼容所有版本(比如登录二维码,直接内存扫描出来)。
      
      Cheat Engine是开源的,下面是特征码搜索的源码:

    DWORD AOBScan(HANDLE hProcess, const char* pattern, const char* mask, uint64_t start, uint64_t end, int inc, int protection,uint64_t * match_addr)
    {
        // 查询内存块信息,看下是不是可读可写可执行
        VirtualQueryEx(hProcess, (void*)tmp, &rinfo, NULL);
        if((rinfo.protection & protection) != 0) ...
    
        // 读取内存,用暴力算法BF(Brute Force)算法进行字符匹配
        ReadProcessMemory(hProcess, (void*)tmp2, MemoryBuff, 4096)
        for (int i = 0; i < 4096; i += inc)
            for (int k = 0; k < patternLength; k++)
                if (!(mask[k] == '?' || pattern[k] == (MemoryBuff[k])))
    }
    

      可以看出Cheat Engine内存搜索的核心逻辑是
        1、查看内存块信息:VirtualQueryEx
        2、跨进程读取内存:ReadProcessMemory
        3、通过“字符串模式匹配算法”进行比较定位

      
      “字符串模式匹配算法”中比较好的算法是“Sunday算法”,代码如下:

    int SundaySearch(const byte* target, int tLen, const byte* pattern, int pLen)
    {
        const int SHIFT_SIZE = 0x100;
        byte shift[SHIFT_SIZE] = { 0 };
    
        memset(shift, pLen + 1, SHIFT_SIZE);
        for (int i = 0; i < pLen; i++) shift[pattern[i]] = pLen - i;
    
        for (int i = 0; i < tLen - pLen; i += shift[target[i + pLen]])
        {
            for (int j = 0; j < pLen; j++)
            {
                if (target[i + j] != pattern[j]) break;
                if (j == pLen - 1) return i;
            }
        }
    
        return -1;
    }
    

      
      再结合VirtualQueryEx和ReadProcessMemory就可以模仿Cheat Engine 内存搜索,代码如下:

    int ScanPattern(HANDLE hProcess, byte* pattern, int pLen)
    {
        const int MEM_SIZE = 0x1000;
        byte mem[MEM_SIZE] = { 0 };
    
        MEMORY_BASIC_INFORMATION mbi;
        for (int curAddress = 0x0400000; curAddress < 0x3FFFFFFF; curAddress += mbi.RegionSize)
        {
            VirtualQueryEx(hProcess, (void*)curAddress, &mbi, sizeof(MEMORY_BASIC_INFORMATION));
            if ((int)mbi.RegionSize <= 0) break;
            if (mbi.State != MEM_COMMIT) continue;
            //if (mbi.Protect != PAGE_READONLY && mbi.Protect != PAGE_READWRITE) continue;
    
            for (int memIndex = 0; memIndex < (int)mbi.RegionSize / MEM_SIZE; memIndex++)
            {
                int beginAddress = curAddress + memIndex * MEM_SIZE;
                ReadProcessMemory(hProcess, (void*)(beginAddress), mem, MEM_SIZE, 0);
                int offset = SundaySearch(mem, MEM_SIZE, pattern, pLen);
                if (offset >= 0) return beginAddress + offset;
            }
        }
    
        return -1;
    }
    

      
      调用实例

    int main()
    {
        DWORD dwPid = 0x28F4;
        HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, false, dwPid);
    
        int address = 0x14EE4020;
        byte* pattern = (byte*)&address;
        int pLen = 4;
    
        int result = ScanPattern(hProcess, pattern, pLen);
        std::cout << std::hex  << result << "
    ";
    }
    
  • 相关阅读:
    monkey命令
    app性能测试点
    app常见性能测试点
    httprunner中validate的比较方法总结
    ETL工具kettle基本使用
    dbvis MySQL server version for the right syntax to use near 'OPTION SQL_SELECT_LIMIT=DEFAULT' at line 1
    python操作Redis
    mongodb,redis,hbase 三者都是nosql数据库,他们的最大区别和不同定位是什么?
    Redis Desktop manger的下载安装
    Redis的参考文档
  • 原文地址:https://www.cnblogs.com/wwgk/p/15073780.html
Copyright © 2011-2022 走看看