看流星社区

 找回密码
 注册账号
查看: 3996|回复: 1

[C/C++源码] C++ sunday算法 极速定位指定进程内存特征码!

[复制链接]

该用户从未签到

发表于 2020-2-11 21:38:35 | 显示全部楼层 |阅读模式
C++ sunday算法 极速定位指定进程内存特征码!


  1. #include <windows.h>
  2. #include <time.h>
  3. #include <iostream>
  4. using namespace std;

  5. /*
  6. findMatchingCode() 参数说明:
  7. 1) hProcess                要打开的进程句柄
  8. 2) markCode                特征码,支持通配符(??),如: 55 8b ec ?? 56 83 ec 20 ?? ?? 08 d9 ee
  9. 3) memBeginAddr                起始搜索地址
  10. 4) memEndAddr                结束搜索地址
  11. 5) retAddr[]                记录找到的地址,传入这个参数前一定要清0,如 DWORD retAddr[32] = {0};  或者 DWORD *retAddr = new DWORD[32]();
  12. 6) deviation                特征码地址离目标地址的偏移距离,上负下正
  13. 7) isCall                是否为找CALL的跳转地址,true 则 retAddr[] 返回的是CALL跳转的地址
  14. 8) isAll                是否查找所有符合的地址,false找到第一个符合的地址后就结束搜索,true继续搜索,直到搜索地址大于结束地址(memEndAddr)
  15. return返回值                找到的地址总数
  16. */
  17. DWORD findMatchingCode(HANDLE hProcess, string markCode, DWORD memBeginAddr, DWORD memEndAddr, DWORD retAddr[], int deviation, bool isCall, bool isAll = false);




  18. DWORD findMatchingCode(HANDLE hProcess, string markCode, DWORD memBeginAddr, DWORD memEndAddr, DWORD retAddr[], int deviation, bool isCall, bool isAll)
  19. {
  20.         //----------------------处理特征码----------------------//
  21.         //去除所有空格
  22.         if (!markCode.empty())
  23.         {
  24.                 int index = 0;
  25.                 while ((index = markCode.find(' ', index)) >= 0)
  26.                 {
  27.                         markCode.erase(index, 1);
  28.                 }
  29.                 index = 0;
  30.                 while (true)
  31.                 {
  32.                         //删掉头部通配符
  33.                         index = markCode.find("??", index);
  34.                         if (index == 0) {
  35.                                 markCode.erase(index, 2);
  36.                         }
  37.                         else {
  38.                                 break;
  39.                         }       
  40.                 }
  41.         }

  42.         //特征码长度不能为单数
  43.         if (markCode.length() % 2 != 0) return 0;

  44.         //特征码长度
  45.         int len = markCode.length() / 2;

  46.         //Sunday算法模板数组的长度
  47.         int nSundayLen = len;

  48.         //将特征码转换成byte型
  49.         BYTE *pMarkCode = new BYTE[len];
  50.         for (int i = 0; i < len; i++)
  51.         {
  52.                 string tempStr = markCode.substr(i*2, 2);
  53.                 if (tempStr == "??") {
  54.                         pMarkCode[i] = 0x3F;
  55.                         if (nSundayLen == len) nSundayLen = i;
  56.                 }                 
  57.                 else{
  58.                         pMarkCode[i] = strtoul(tempStr.c_str(), 0, 16);
  59.                 }       
  60.         }
  61.         //--------------------------end-------------------------//

  62.         //Sunday算法模板数组赋值,+1防止特征码出现FF时越界
  63.         int aSunday[0xFF + 1] = { 0 };
  64.         for (int i = 0; i < nSundayLen; i++){
  65.                 aSunday[pMarkCode[i]] = i + 1;
  66.         }

  67.         //起始地址
  68.         const DWORD dwBeginAddr = memBeginAddr;
  69.         //结束地址
  70.         const DWORD dwEndAddr = memEndAddr;
  71.         //当前读取的内存块地址
  72.         DWORD dwCurAddr = dwBeginAddr;
  73.         //存放内存数据的缓冲区
  74.         BYTE *pMemBuffer = NULL;
  75.         //计算参数retAddr[]数组的长度,该参数传入前一定要清0
  76.         int nArrayLength = 0;
  77.         for (int i = 0; ; i++) {
  78.                 if (*(retAddr + i) != 0) {
  79.                         nArrayLength = i;
  80.                         break;
  81.                 }
  82.         }
  83.         //偏移量
  84.         int nOffset;
  85.         //数组下标:内存、特征码、返回地址
  86.         int i = 0, j = 0, nCount = 0;

  87.         //内存信息
  88.         MEMORY_BASIC_INFORMATION mbi;

  89.         //记录起始搜索时间
  90.         clock_t nBeginTime = clock();

  91.         //扫描内存
  92.         while (dwCurAddr < dwEndAddr)
  93.         {
  94.                 //查询地址空间中内存地址的信息
  95.                 memset(&mbi, 0, sizeof(MEMORY_BASIC_INFORMATION));
  96.                 if (::VirtualQueryEx(hProcess, (LPCVOID)dwCurAddr, &mbi, sizeof(mbi)) == 0) {
  97.                         goto end;
  98.                 }

  99.                 //过滤内存空间, 根据内存的状态和保护属性进行过滤
  100.                 //一般扫描(读写及执行)即可,速度极快,扫不到的话在尝试添加(读写)这一属性
  101.                 if (MEM_COMMIT == mbi.State &&                        //已分配的物理内存
  102.                         //MEM_PRIVATE == mbi.Type ||                //私有内存,不被其他进程共享
  103.                         //MEM_IMAGE == mbi.Type &&
  104.                         //PAGE_READONLY == mbi.Protect ||        //只读
  105.                         //PAGE_EXECUTE_READ == mbi.Protect ||        //读及执行
  106.                         //PAGE_READWRITE == mbi.Protect ||        //读写
  107.                         PAGE_EXECUTE_READWRITE == mbi.Protect)        //读写及执行
  108.                 {
  109.                         //申请动态内存
  110.                         if (pMemBuffer) {
  111.                                 delete[] pMemBuffer;
  112.                                 pMemBuffer = NULL;
  113.                         }
  114.                         pMemBuffer = new BYTE[mbi.RegionSize];
  115.                         //读取进程内存
  116.                         ReadProcessMemory(hProcess, (LPCVOID)dwCurAddr, pMemBuffer, mbi.RegionSize, 0);
  117.                         i = 0;
  118.                         j = 0;
  119.                         while (j < len)
  120.                         {
  121.                         nextAddr:
  122.                                 if (pMemBuffer[i] == pMarkCode[j] || pMarkCode[j] == 0x3F)
  123.                                 {
  124.                                         i++;
  125.                                         j++;
  126.                                 }
  127.                                 else
  128.                                 {
  129.                                         nOffset = i - j + nSundayLen;
  130.                                         //判断偏移量是否大于缓冲区
  131.                                         if (nOffset > mbi.RegionSize - len) break;
  132.                                         //判断 aSunday模板数组 里有没有 内存偏移后的值,有则回溯,否则+1
  133.                                         if (aSunday[pMemBuffer[nOffset]])
  134.                                         {
  135.                                                 i = nOffset - aSunday[pMemBuffer[nOffset]] + 1;
  136.                                                 j = 0;
  137.                                         }
  138.                                         else
  139.                                         {
  140.                                                 i = nOffset + 1;
  141.                                                 j = 0;
  142.                                         }
  143.                                 }
  144.                         }

  145.                         if (j == len)
  146.                         {
  147.                                 //计算找到的目标地址:
  148.                                 //特征码地址 = 当前内存块基址 + i偏移 - 特征码长度
  149.                                 //目标地址 = 特征码地址 + 偏移距离
  150.                                 //CALL(E8)跳转的地址 = E8指令后面的4个字节地址 + 下一条指令地址(也就是目标地址 + 5)
  151.                                 retAddr[nCount] = dwCurAddr + i - len + deviation;
  152.                                 if (isCall) {
  153.                                         DWORD temp;
  154.                                         memcpy(&temp, &pMemBuffer[i - len + deviation + 1], 4);
  155.                                         retAddr[nCount] += 5;
  156.                                         retAddr[nCount] += temp;
  157.                                 }

  158.                                 if (++nCount >= nArrayLength)
  159.                                 {
  160.                                         //传入的数组下标越界就结束搜索
  161.                                         goto end;
  162.                                 }

  163.                                 if (isAll) {                               
  164.                                         i = i - len + 1;
  165.                                         j = 0;
  166.                                         goto nextAddr;
  167.                                 }
  168.                                 else {
  169.                                         goto end;
  170.                                 }
  171.                         }               
  172.                         dwCurAddr += mbi.RegionSize; //取下一块内存地址
  173.                 }
  174.                 else
  175.                 {
  176.                         dwCurAddr += mbi.RegionSize;
  177.                 }
  178.         }


  179. end:
  180.         //计算搜索用时(ms)
  181.         clock_t nEndTime = clock();
  182.         int nUseTime = (nEndTime - nBeginTime);       
  183.         //释放内存
  184.         if (pMemBuffer) {
  185.                 delete[] pMemBuffer;
  186.                 pMemBuffer = NULL;
  187.         }
  188.         delete[] pMarkCode;
  189.         pMarkCode = NULL;
  190.         return nCount;
  191. }

复制代码


注意:一定要根据查询到的内存页大小动态分配内存。千万不要像有些人写的,固定一页大小4096或其他的等等,这样的会存在一种情况,当你的特征码跨2页的时候,你会发现找不到,除非你特殊处理,而且处理起来极为麻烦!

该用户从未签到

发表于 2020-4-11 13:31:22 | 显示全部楼层
请楼主发使用例子 可以吗
点击按钮快速添加回复内容: 支持 高兴 激动 给力 加油 苦寻 生气 回帖 路过 感恩
您需要登录后才可以回帖 登录 | 注册账号

本版积分规则

小黑屋|手机版|Archiver|看流星社区 |网站地图

GMT+8, 2024-4-19 05:29

Powered by Kanliuxing X3.4

© 2010-2019 kanliuxing.com

快速回复 返回顶部 返回列表