5.1 内存CRC32完整性检测

内存,crc32,完整性,检测 · 浏览次数 : 33

小编点评

**内存校验技术** 内存校验技术是一种用于检测数据传输或存储过程中是否出现了错误的一种方法。校验算法可以通过计算应用与数据的循环冗余校验(CRC)检验值来检测任何数据损坏。 **CRC校验值** CRC (cyclic redundancy check) 校验值是一个用于检测数据损坏的算法。它是一个循环的序列,其中每个字节与其后一个字节进行比较。如果两个字节值相同,则说明数据没有损坏。 **内存校验的用途** 内存校验技术可用于以下用途: * **防止软件破解或打补丁:** 通过计算 CRC 校验值,可以检测恶意软件或恶意代码试图更改内存数据。 * **实现对特定内存区域和磁盘文件的完整性检测:** 通过计算 CRC 校验值,可以检测内存中的更改或损坏。 * **判定特定程序内存是否发生了变化:** 通过计算 CRC 校验值,可以检测程序内存是否发生变化。 **代码示例** 以下代码展示了如何使用 C++ 进行内存 CRC32 校验: ```cpp #include #include // 获取代码节的长度 DWORD CalculateMemoryCRC32(DWORD va_base, DWORD sec_len) { PIMAGE_DOS_HEADER pDosHeader = NULL; PIMAGE_NT_HEADERS pNtHeader = NULL; PIMAGE_SECTION_HEADER pSecHeader = NULL; DWORD ImageBase; // 获取基地址 ImageBase = (DWORD)GetModuleHandle(NULL); // 定位到PE头结构 pDosHeader = (PIMAGE_DOS_HEADER)ImageBase; pNtHeader = (PIMAGE_NT_HEADERS32)((DWORD)pDosHeader + pDosHeader->e_lfanew); // 定位第一个区块地址,因为默认的话第一个就是.text节 pSecHeader = IMAGE_FIRST_SECTION(pNtHeader); // 获取代码节长度 DWORD sec_len = pSecHeader->Misc.VirtualSize; // 返回 CRC 校验值 return CheckCRC32((BYTE*)(va_base), sec_len); } int main() { // 初始化 CRC 校验值 DWORD OriginalCRC32 = 0; // 循环计算 CRC 校验值 while (1) { // 计算新的 CRC 校验值 DWORD NewCRC32 = CalculateMemoryCRC32(0, sizeof(int)); // 打印 CRC 校验值 if (OriginalCRC32 == NewCRC32) { printf("[+] 当前内存没有被修改 \\n"); } else { printf("[-] 当前内存已被修改\\n"); } // 延迟 3 秒 Sleep(3000); } return 0; } ``` **注意** 该代码示例仅用于演示 purposes,在实际应用中需要考虑安全性并对代码进行修改。

正文

CRC校验技术是用于检测数据传输或存储过程中是否出现了错误的一种方法,校验算法可以通过计算应用与数据的循环冗余校验(CRC)检验值来检测任何数据损坏。通过运用本校验技术我们可以实现对特定内存区域以及磁盘文件进行完整性检测,并以此来判定特定程序内存是否发生了变化,如果发生变化则拒绝执行,通过此种方法来保护内存或磁盘文件不会被非法篡改。总之,内存和磁盘中的校验技术都是用于确保数据和程序的完整性和安全性的重要技术。

内存CRC32特征检测通常用于防止软件破解或打补丁,内存特征码检查实现原理是通过定位到.text节表的首地址及该节的长度,然后计算该节的CRC32值并存入全局变量,通过在程序内部打开一个子线程用于实时监测内存,一旦发现CRC32值发生了变化,则可执行终止程序运行等操作,以此来实现防止破解或打补丁的目的。

我们来看这样一段代码,程序通过GetModuleHandle(NULL)函数获取到自身程序的句柄,并通过PE结构定位到.text节,取出该节内的VirtualAddress虚拟地址,以及VirtualSize虚拟长度,最后调用CRC32((BYTE*)(va_base), sec_len)获取到该节的CRC数据。

// 检查内存中CRC32特征值
DWORD CalculateMemoryCRC32()
{
    PIMAGE_DOS_HEADER pDosHeader = NULL;
    PIMAGE_NT_HEADERS pNtHeader = NULL;
    PIMAGE_SECTION_HEADER pSecHeader = NULL;
    DWORD ImageBase;

    // 获取基地址
    ImageBase = (DWORD)GetModuleHandle(NULL);

    // 定位到PE头结构
    pDosHeader = (PIMAGE_DOS_HEADER)ImageBase;
    pNtHeader = (PIMAGE_NT_HEADERS32)((DWORD)pDosHeader + pDosHeader->e_lfanew);

    // 定位第一个区块地址,因为默认的话第一个就是.text节
    pSecHeader = IMAGE_FIRST_SECTION(pNtHeader);
    DWORD va_base = ImageBase + pSecHeader->VirtualAddress;   // 定位代码节va基地址
    DWORD sec_len = pSecHeader->Misc.VirtualSize;             // 获取代码节长度
    printf("镜像基址(.text): %x | 镜像大小: %d \n", va_base, sec_len);

    DWORD CheckCRC32 = CRC32((BYTE*)(va_base), sec_len);
    printf(".text节CRC32 = %x \n", CheckCRC32);

    return CheckCRC32;
}

当主程序执行时,我们首先通过CalculateMemoryCRC32函数获取到当前代码段的校验码,并存储到OriginalCRC32全局变量内,在循环体内通过不断的计算CRC数据并与全局初始值做对比,以此来实现防止破解的作用。

int main(int argc, char *argv[])
{
    // 用于保存初始化时 .text 节中的CRC32值
    DWORD OriginalCRC32 = 0;

    // 初始化时,给全局变量赋值,记录下初始的CRC32值
    OriginalCRC32 = CalculateMemoryCRC32();

    while (1)
    {
        // 每隔3秒计算一次
        Sleep(3000);

        // 计算新的CRC
        DWORD NewCRC32 = CalculateMemoryCRC32();
        if (OriginalCRC32 == NewCRC32)
        {
            printf("[+] 当前CRC [ %x ] 程序没有被打补丁 \n",NewCRC32);
        }
        else
        {
            printf("[-] 当前CRC [ %x ] 已被打补丁 \n", NewCRC32);
        }
    }

    system("pause");
    return 0;
}

编译并运行上述程序片段,当读者使用x64dbg修改内存中的字节时,此处将int3修改为nopCRC32会提示我们内存已经被打补丁,输出效果如下图所示;

当然上述方法虽然可以对全局进行保护,但如果程序过大则此类验证效率将变得很低,我们需要通过使用打标签的方式对特定内存区域进行保护,如下代码中所示,我们通过begin设置开始保护标签,通过end设置结束保护标签,通过size = end_addr - begin_addr;计算即可获取到当前所需要保护的内存长度,最后通过CalculateMemoryCRC32实现计算内存CRC的目的,读者可以在当前进程内启动子线程用于实现专门的内存检测。

// 检查内存中CRC32特征值
DWORD CalculateMemoryCRC32(DWORD va_base, DWORD sec_len)
{
    DWORD CheckCRC32 = CRC32((BYTE*)(va_base), sec_len);
    return CheckCRC32;
}

int main(int argc, char *argv[])
{
    // 用于保存初始化时 .text 节中的CRC32值
    DWORD OriginalCRC32 = 0;

    DWORD begin_addr, end_addr, size;
    
    // 获取到两个位置的偏移地址
    __asm mov begin_addr, offset begin;
    __asm mov end_addr, offset end;

    // 计算出 两者内存差值
    size = end_addr - begin_addr;

    // 校验指定内存位置
    OriginalCRC32 = CalculateMemoryCRC32(begin_addr, size);

    while (1)
    {
        // 标记为需要保护的区域
    begin:
        printf("hello lyshark \n");
        printf("hello lyshark \n");
        printf("hello lyshark \n");

        // 保护区域声明结束
    end:

        // 计算并对比
        if (OriginalCRC32 == CalculateMemoryCRC32(begin_addr, size))
        {
            printf("[+] 此区域没有被修改 \n");
        }
        else
        {
            printf("[-] 此区域已被修改\n");
        }

        Sleep(3000);
    }
    system("pause");
    return 0;
}

当保护区域内的参数发生变化时则会弹出数据被篡改,如下所示我们通过填充一个nop指令,观察下图,读者能够发现我们的检测生效了;

本文作者: 王瑞
本文链接: https://www.lyshark.com/post/541f4225.html
版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出

与5.1 内存CRC32完整性检测相似的内容:

5.1 内存CRC32完整性检测

CRC校验技术是用于检测数据传输或存储过程中是否出现了错误的一种方法,校验算法可以通过计算应用与数据的循环冗余校验(CRC)检验值来检测任何数据损坏。通过运用本校验技术我们可以实现对特定内存区域以及磁盘文件进行完整性检测,并以此来判定特定程序内存是否发生了变化,如果发生变化则拒绝执行,通过此种方法来保护内存或磁盘文件不会被非法篡改。总之,内存和磁盘中的校验技术都是用于确保数据和程序的完整性和安全性

5.2 磁盘CRC32完整性检测

CRC校验技术是用于检测数据传输或存储过程中是否出现了错误的一种方法,校验算法可以通过计算应用与数据的循环冗余校验(CRC)检验值来检测任何数据损坏。通过运用本校验技术我们可以实现对特定内存区域以及磁盘文件进行完整性检测,并以此来判定特定程序内存是否发生了变化,如果发生变化则拒绝执行,通过此种方法来保护内存或磁盘文件不会被非法篡改。总之,内存和磁盘中的校验技术都是用于确保数据和程序的完整性和安全性

5.0 CRC32校验技术概述

CRC校验技术是用于检测数据传输或存储过程中是否出现了错误的一种方法,校验算法可以通过计算应用与数据的循环冗余校验(CRC)检验值来检测任何数据损坏。通过运用本校验技术我们可以实现对特定内存区域以及磁盘文件进行完整性检测,并以此来判定特定程序内存是否发生了变化,如果发生变化则拒绝执行,通过此种方法来保护内存或磁盘文件不会被非法篡改。总之,内存和磁盘中的校验技术都是用于确保数据和程序的完整性和安全性

【转帖】JVM 内存模型与垃圾回收

文章目录 1. JVM内存模型1.1. 程序计数器 (线程私有)1.2. Java 虚拟机栈 (线程私有)1.3. 本地方法栈 (线程私有)1.4. Java 堆 (线程共享)1.5. 方法区 (线程共享)1.6. 运行时常量池 (线程共享)1.7. 直接内存 (堆外内存) 2. 垃圾查找算法2.1

5.1 缓冲区溢出与攻防博弈

在黑客安全圈子中,基于内存攻击技术的攻击手段在随着时代的变化而不断发展着,内存攻击是指通过利用软件的安全漏洞,构造恶意的输入,从而使正常程序造成拒绝服务或者是远程获得控制权,内存攻击技术中最先登上历史舞台的就是缓冲区溢出漏洞,时至今日能够被广泛利用的并具有较大破坏性的高危漏洞(CVE)几乎都属于缓冲区溢出。首先读者应该明白缓冲区溢出(Buffer Overflow),它分为栈溢出与堆溢出,此类漏洞

5.1 C/C++ 使用文件与指针

C/C++语言是一种通用的编程语言,具有高效、灵活和可移植等特点。C语言主要用于系统编程,如操作系统、编译器、数据库等;C语言是C语言的扩展,增加了面向对象编程的特性,适用于大型软件系统、图形用户界面、嵌入式系统等。C/C++语言具有很高的效率和控制能力,但也需要开发人员自行管理内存等底层资源,对于初学者来说可能会有一定的难度。

[转帖]Linux 调优篇:虚拟化调优(hugepage 大页内存)* 叁

一. 大页(HugePages)概念 Hugepage的引入二. hugepages相关概念三.Regular Pages 与 HugePages a、Regular Pages b、Huge Pages四. hugepage 优点五.调优方法 5.1 在Host侧查看各个numa节点上的大页分配情

【转帖】Linux 调优篇:虚拟化调优(hugepage 大页内存)* 叁

一. 大页(HugePages)概念 Hugepage的引入二. hugepages相关概念三.Regular Pages 与 HugePages a、Regular Pages b、Huge Pages四. hugepage 优点五.调优方法 5.1 在Host侧查看各个numa节点上的大页分配情

万字长文总结与剖析C语言关键字 -- <>

C总结与剖析:关键字篇 -- <> 目录C总结与剖析:关键字篇 -- <>程序的本质:二进制文件变量1.变量:内存上的某个位置开辟的空间2.变量的初始化3.为什么要有变量4.局部变量与全局变量5.变量的大小由类型决定6.任何一个变量,内存赋值都是从低地址开始往高地

.NET周报 【5月第1期 2023-05-06】

国内文章 聊一聊 Valgrind 监视非托管内存泄露和崩溃 https://www.cnblogs.com/huangxincheng/p/17374315.html、 只要是程序总会出现各种莫名其妙的问题,比如:非托管内存泄露,程序崩溃,在 Windows 平台上一般用微软自家的官方工具 App