6.2 Sunday搜索内存特征

这篇具有很好参考价值的文章主要介绍了6.2 Sunday搜索内存特征。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Sunday 算法是一种字符串搜索算法,由Daniel M.Sunday于1990年开发,该算法用于在较长的字符串中查找子字符串的位置。算法通过将要搜索的模式的字符与要搜索的字符串的字符进行比较,从模式的最左侧位置开始。如果发现不匹配,则算法将模式向右滑动一定数量的位置。这个数字是由当前文本中当前模式位置的最右侧字符确定的。相比于暴力方法,该算法被认为更加高效。

6.2.1 字符串与特征码转换

GetSignatureCodeArray函数,该函数用于将给定的十六进制串表示的字节码特征码转换为十进制数,存储在一个整型数组中,以便后续进行搜索。同时,特征码中的未知标记符号?会被用256 替代,方便后续搜索对特征码的匹配。

其中,参数SignatureCode为一串十六进制字符串,描述要搜索的字节码特征码,参数BytesetSequence为一个整型数组,用于存储将十六进制数转为十进制后的结果。该函数首先计算给定的十六进制串中包含的字节码个数,因为每个字节对应两个十六进制字符,再加上每两个字符间的空格,故需要将十六进制字符串长度除以三,再加上一。

接下来,函数逐个字符读入特征码串中的每一个十六进制数,如果是有效的十六进制数,则转化为十进制数存入BytesetSequence数组中。如果遇到未知的标记符号?,则在BytesetSequence数组中用256表示该位置的值。最后,返回特征码数组中字节码的个数。

// 定义全局变量
#define BLOCKMAXSIZE 409600  // 每次读取内存的最大大小
BYTE* MemoryData;            // 每次将读取的内存读入这里
SHORT Next[260];             // 搜索下一个内存区域

// 将传入的SignatureCode特征码字符串转换为BytesetSequence特征码字节集
WORD GetSignatureCodeArray(char* SignatureCode, WORD* BytesetSequence)
{
    int len = 0;

    // 用于存储特征码数组长度
    WORD SignatureCodeLength = strlen(SignatureCode) / 3 + 1;

    // 将十六进制特征码转为十进制
    // 依次遍历SignatureCode中的每一个十六进制数
    for (int i = 0; i < strlen(SignatureCode);)
    {
        char num[2];

        // 分别取出第一个和第二个十六进制字符
        num[0] = SignatureCode[i++];
        num[1] = SignatureCode[i++];
        i++;

        // 如果两个字符都是有效的十六进制数,则将它们转换成十进制并存储到 BytesetSequence 中
        if (num[0] != '?' && num[1] != '?')
        {
            int sum = 0;
            WORD a[2];

            // 分别将两个十六进制字符转换成十进制数
            for (int i = 0; i < 2; i++)
            {
                // 如果是数字
                if (num[i] >= '0' && num[i] <= '9')
                {
                    a[i] = num[i] - '0';
                }
                // 如果是小写字母
                else if (num[i] >= 'a' && num[i] <= 'z')
                {
                    a[i] = num[i] - 87;
                }
                // 如果是大写字母
                else if (num[i] >= 'A' && num[i] <= 'Z')
                {
                    a[i] = num[i] - 55;
                }
            }

            // 计算两个十六进制数转换后的十进制数,并将其存储到 BytesetSequence 数组中
            sum = a[0] * 16 + a[1];
            BytesetSequence[len++] = sum;
        }
        else
        {
            BytesetSequence[len++] = 256;
        }
    }
    return SignatureCodeLength;
}

6.2.2 搜索内存区域特征

SearchMemoryBlock函数,该函数用于在指定进程的某一块内存中搜索给定的字节码特征码,查找成功则将匹配地址存入结果数组中。其中,参数hProcess为指向要搜索内存块所在进程的句柄,SignatureCode为给定特征码的数组指针,SignatureCodeLength为特征码长度,StartAddress为搜索的起始地址,size为搜索内存的大小,ResultArray为存储搜索结果的数组引用。

通过调用ReadProcessMemory函数读取进程内存中指定地址和大小的数据,将读取的数据存入变量MemoryData中,然后对读取的数据进行匹配,查找特征码。若匹配成功,则将特征码匹配的起始地址存入结果数组中。在匹配时,采用了KMP算法。如果找到与特征码中的字节码不匹配的字节,就根据Next数组记录的回溯位置,重新从失配的位置开始匹配,以降低匹配的时间复杂度,提高搜索效率。在代码中,若特征码中存在问号,则匹配位置从问号处开始重新匹配,如果没有则继续按照Next数组回溯进行匹配。

// 获取GetNextArray数组
void GetNextArray(short* next, WORD* SignatureCode, WORD SignatureCodeLength)
{
    // 特征码字节集的每个字节的范围在0-255(0-FF)之间
    // 256用来表示问号,到260是为了防止越界
    for (int i = 0; i < 260; i++)
    {
        next[i] = -1;
    }
    for (int i = 0; i < SignatureCodeLength; i++)
    {
        next[SignatureCode[i]] = i;
    }
}

// 搜索一块内存区域中的特征
void SearchMemoryBlock(HANDLE hProcess, WORD* SignatureCode, WORD SignatureCodeLength, unsigned __int64 StartAddress, unsigned long size, vector<unsigned __int64>& ResultArray)
{
    // 读取指定进程的内存数据到MemoryData缓冲区中
    if (!ReadProcessMemory(hProcess, (LPCVOID)StartAddress, MemoryData, size, NULL))
    {
        return;
    }

    // 循环遍历内存数据缓冲区
    for (int i = 0, j, k; i < size;)
    {
        j = i; k = 0;

        // 逐个比对内存数据缓冲区中的字节和特征码中的字节
        for (; k < SignatureCodeLength && j < size && (SignatureCode[k] == MemoryData[j] || SignatureCode[k] == 256); k++, j++);

        // 如果特征码完全匹配到内存数据缓冲区中的一段数据
        if (k == SignatureCodeLength)
        {
            // 将该段数据的起始地址保存到结果数组中
            ResultArray.push_back(StartAddress + i);
        }

        // 如果已经处理到缓冲区的末尾
        if ((i + SignatureCodeLength) >= size)
        {
            return;
        }

        int num = Next[MemoryData[i + SignatureCodeLength]];

        // 如果特征码中有问号,从问号处开始匹配
        if (num == -1)
        {
            // 如果特征码有问号,就从问号处开始匹配,如果没有就 i += -1
            i += (SignatureCodeLength - Next[256]);
        }
        else
        {
            // 否则从匹配失败的位置开始
            i += (SignatureCodeLength - num);
        }
    }
}

6.2.3 搜索整块内存区域

SearchMemory函数,该函数用于在指定进程的内存空间中搜索给定特征码的内存块,并把搜索到的内存地址存入结果数组中。函数为一层循环枚举给定的内存块,内部则调用SearchMemoryBlock函数进行内存块搜索。其中,参数hProcess为指向要搜索内存块所在进程的句柄,SignatureCode为给定特征码的字符串指针,StartAddress为搜索的起始地址,EndAddress为搜索的结束地址,InitSize为搜索结果数组初始空间大小,ResultArray为存储搜索结果的数组引用。

该函数首先通过调用VirtualQueryEx函数获取可读可写和可读可写可执行的内存块信息,并遍历每个内存块,对内存块进行搜索。之所以不直接搜索整个内存区域,是因为那样可以减少非必要的搜索,提高效率。

内存块的搜索通过调用SearchMemoryBlock函数实现。搜索采用了KMP算法,先通过GetNextArray函数和GetSignatureCodeArray函数将特征码转换为对应的变量,再对每个内存块逐个匹配,在匹配过程中若找到与特征码中的字节码不匹配的字节,就根据Next数组记录的回溯位置从失配的位置开始重新匹配,以降低匹配的时间复杂度。在内存块搜索过程中,若匹配成功,则将特征码匹配的起始地址存入结果数组中,最终函数返回结果数组大小。

// 实现搜索整个程序
int SearchMemory(HANDLE hProcess, char* SignatureCode, unsigned __int64 StartAddress, unsigned __int64 EndAddress, int InitSize, vector<unsigned __int64>& ResultArray)
{
    int i = 0;
    unsigned long BlockSize;
    MEMORY_BASIC_INFORMATION mbi;

    WORD SignatureCodeLength = strlen(SignatureCode) / 3 + 1;
    WORD* SignatureCodeArray = new WORD[SignatureCodeLength];

    // 实现特征码字符串与数组转换
    GetSignatureCodeArray(SignatureCode, SignatureCodeArray);
    GetNextArray(Next, SignatureCodeArray, SignatureCodeLength);

    // 初始化结果数组
    ResultArray.clear();
    ResultArray.reserve(InitSize);

    // 查询内存属性并循环
    while (VirtualQueryEx(hProcess, (LPCVOID)StartAddress, &mbi, sizeof(mbi)) != 0)
    {
        // 判断并获取具有PAGE_READWRITE读写,或者PAGE_EXECUTE_READWRITE读写执行权限的内存
        if (mbi.Protect == PAGE_READWRITE || mbi.Protect == PAGE_EXECUTE_READWRITE)
        {
            i = 0;

            // 得到当前块长度
            BlockSize = mbi.RegionSize;
            
            // 搜索这块内存
            while (BlockSize >= BLOCKMAXSIZE)
            {
                // 调用内存块搜索功能依次搜索内存
                SearchMemoryBlock(hProcess, SignatureCodeArray, SignatureCodeLength, StartAddress + (BLOCKMAXSIZE * i), BLOCKMAXSIZE, ResultArray);
                BlockSize -= BLOCKMAXSIZE;
                i++;
            }
            SearchMemoryBlock(hProcess, SignatureCodeArray, SignatureCodeLength, StartAddress + (BLOCKMAXSIZE * i), BlockSize, ResultArray);
        }

        // 开始地址增加下一块长度继续搜索
        StartAddress += mbi.RegionSize;
        if (EndAddress != 0 && StartAddress > EndAddress)
        {
            return ResultArray.size();
        }
    }

    // 释放特征码数组并返回搜索计数器
    free(SignatureCodeArray);
    return ResultArray.size();
}

将上述代码理解后读者可以自行使用

int main(int argc, char *argv[])
{
    // 通过进程名获取进程PID号
    DWORD Pid = GetPidByName("PlantsVsZombies.exe");
    printf("[*] 获取进程PID = %d \n", Pid);

    // 初始化MemoryData大小
    MemoryData = new BYTE[BLOCKMAXSIZE];

    // 存储搜索返回值
    vector<unsigned __int64> ResultArray;

    // 通过进程ID获取进程句柄
    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, false, Pid);

    // 开始搜索
    // 搜索特征码 FF 25 ?? 从0x0000000到0xFFFFFFF 初始长度为3 返回值放入ResultArray
    SearchMemory(hProcess, "FF 25 ??", 0x0000000, 0xFFFFFFF, 3, ResultArray);

    // 输出结果
    for (vector<unsigned __int64>::iterator it = ResultArray.begin(); it != ResultArray.end(); it++)
    {
        printf("0x%08X \n", *it);
    }

    system("pause");
    return 0;
}

编译并运行上述程序片段,则会枚举hProcess进程内特征码时FF 25 ??的片段,枚举位置为0x0000000-0xFFFFFFF枚举长度为3个特征,最终将枚举结果输出到ResultArray数组内,输出效果图如下所示;

6.2 Sunday搜索内存特征文章来源地址https://www.toymoban.com/news/detail-709971.html

到了这里,关于6.2 Sunday搜索内存特征的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Matlab 一种自适应搜索半径的特征提取方法

    在之前的博客(C++ ID3决策树)中,提到过一种 信息熵 的概念,其中它表达的大致意思为:香农认为熵是指“当一件事情有多种可能情况时,这件事情发生某种情况的不确定性”,也就是指如果一个事情的不确定性越大,那么这个信息的熵值就越大,基于这个理论我们就可以

    2024年02月16日
    浏览(54)
  • 算法学习——LeetCode力扣补充篇11(64. 最小路径和、48. 旋转图像 、169. 多数元素、394. 字符串解码、240. 搜索二维矩阵 II )

    64. 最小路径和 - 力扣(LeetCode) 描述 给定一个包含非负整数的 m x n 网格 grid ,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。 说明:每次只能向下或者向右移动一步。 示例 示例 1: 输入:grid = [[1,3,1],[1,5,1],[4,2,1]] 输出:7 解释:因为路径 1→3→1→

    2024年04月23日
    浏览(37)
  • 【C语言】字符函数,字符串函数,内存函数

    大家好!今天我们来学习C语言中的字符函数,字符串函数和内存函数。 目录 1. 字符函数 1.1 字符分类函数 1.2 字符转换函数 1.2.1 tolower(将大写字母转化为小写字母) 1.2.2 toupper(将小写字母转化为大写字母) 2. 字符串函数 2.1 字符串输入函数 2.1.1 gets() ​2.1.2 fgets() 2.2 字符串

    2024年02月10日
    浏览(57)
  • 字符串和内存函数(2)

    2.13 memcpy void* memcpy(void* destination, const void* source, size_t num); 函数memcpy从source的位置开始向后复制num个 字节 的数据到destination的内存位置。 这个函数在遇到 ‘\\0’ 的时候并不会停下来。 如果source和destination有任何的重叠,复制的结果都是未定义的。 memcpy是内存拷贝,它可以拷

    2024年02月04日
    浏览(36)
  • 字符串函数+内存函数(详解)

    本期带大家一起来学习字符串函数+内存函数😀 😃 😄 1. 求字符串长度 strlen🚀🚀 字符串 已经 ‘\\0’ 作为结束标志 ,strlen函数返回的是在字符串中 ‘\\0’ 前面出现的字符个数(不包含 ‘\\0’ )。 strlen在库里面的参数是如此的 ⌛️⌛️ 接下来我们来模拟实现strlen ,接下来

    2023年04月17日
    浏览(41)
  • .NET字符串内存管理:常量字符串、动态创建和字符串池的巧妙结合

      在 .NET 中,字符串是不可变的,这意味着一旦创建,字符串的内容就不能被修改。字符串在内存中以不同的方式存储,具体取决于它是常量字符串还是动态创建的字符串。 常量字符串在编译时就被解析,并在程序的元数据(Metadata)中存储。多个相同的字符串常量可能会共

    2024年01月20日
    浏览(49)
  • <C语言> 字符串内存函数

    C语言中对字符和字符串的处理很是频繁,但是C语言本身是没有字符串类型的,字符串通常放在 常量字符串 或者 字符数组 中。 字符串常量 适用于那些对它不做修改的字符串函数. 注意:字符串函数都需要包含头文件string.h strlen() 函数用于计算字符串的长度,即字符串中字符

    2024年02月15日
    浏览(46)
  • 字符串函数和内存操作函数

    目录 0.字符串回顾 1.函数介绍 1.1 strlen 1.2 strcpy 1.3 strcat 1.4 strcmp 1.5 strncpy 1.6 strncat 1.7 strncmp 1.8 strstr 1.9 strtok 1.10 strerror 1.11 memcpy 1.12 memmove 1.13 memcmp 1.14 memset 1.15 字符相关函数 字符分类函数 字符转换函数 2.函数模拟实现 2.1模拟实现strlen 2.2模拟实现strcpy 2.3模拟实现strcat 2.4模拟

    2024年02月15日
    浏览(107)
  • 字符串函数和内存函数详解(2)

    🐵本文会将会对剩余的 字符串库函数 和 内存函数 进行讲解 strstr函数原型: strstr用于在字符串中找子串,strstr会返回str1中出现str2的起始地址,如果在str1中没有找到str2,则返回空指针 strstr会返回arr1中第一次出现arr2的地址,这里就是\\\'l\\\'的地址,将其传给指针ret,在打印时

    2024年02月07日
    浏览(39)
  • C语言进阶---字符串+内存函数

    重点介绍处理字符和字符串的库函数的使用和注意事项。 求字符串长度 strlen() 长度不受限制的的字符串函数 strcpy() strcat() strcmp() 长度受限制的的字符串函数 strncpy() strncat() strncmp() 字符串查找 strstr() strtok() 错误信息报告 strerror() 字符操作 内存操作函数 memcpy() memmove() memset(

    2024年02月12日
    浏览(52)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包