首页 > 系统相关 >5.1 内存CRC32完整性检测

5.1 内存CRC32完整性检测

时间:2023-09-19 09:44:49浏览次数:51  
标签:5.1 begin addr 内存 printf CRC32 DWORD

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,begin,addr,内存,printf,CRC32,DWORD
From: https://www.cnblogs.com/LyShark/p/17713797.html

相关文章

  • 5.2 磁盘CRC32完整性检测
    CRC校验技术是用于检测数据传输或存储过程中是否出现了错误的一种方法,校验算法可以通过计算应用与数据的循环冗余校验(CRC)检验值来检测任何数据损坏。通过运用本校验技术我们可以实现对特定内存区域以及磁盘文件进行完整性检测,并以此来判定特定程序内存是否发生了变化,如果发生变化......
  • 解决Visual Studio 2022中无法编译 .NET Framework 4.5/4.5.1项目(Visual Studio 2022
    最新【一键处理】方法:https://github.com/MrXhh/VSTools/releases1)下载VS2022Net4NotCompileFix2)右键管理员执行3)重启VS https://github.com/MrXhh/VSTools/releases......
  • 内存泄漏和内存溢出
    内存溢出outofmemory,是指程序在申请内存时,没有足够的内存空间供其使用,出现outofmemory;比如申请了一个integer,但给它存了long才能存下的数,那就是内存溢出。内存溢出就是你要求分配的内存超出了系统能给你的,系统不能满足需求,于是产生溢出。内存泄漏是指你向系统申请分配内存......
  • idea设置项目启动的JVM运行内存大小
    idea设置项目启动的JVM运行内存大小场景在开发当中,idea默认服务启动要占用1G内存。其实每个项目本地开发和调试的时候,根本不需要1G内存,200M左右足以如果在微服务体系下,那效果更明显,相同的内存可以启动更多的服务刚好本人的电脑只有8G,公司的微服务项目启动后,电脑风扇疯狂的转动......
  • 动态内存分配(callco,realloc,笔试题目)2
    相比于malloc加了有一个自动初始化的功能intmain(){ int*p=(int*)calloc(10,sizeof(int));//创建之后就默认数据初始化为0 if(p==NULL) { printf("%s\n",strerror(errno)); } else { inti=0; for(i=0;i<10;i++) { *(p+i)=i; } for(i......
  • 从内核世界透视 mmap 内存映射的本质(原理篇)
    本文基于内核5.4版本源码讨论之前有不少读者给笔者留言,希望笔者写一篇文章介绍下mmap内存映射相关的知识体系,之所以迟迟没有动笔,是因为mmap这个系统调用看上去简单,实际上并不简单,可以说是非常复杂的一个系统调用。如果想要给大家把mmap背后的技术本质,正确地,清晰地还原......
  • C/C++中结构体占用内存大小的计算方法
    两个值:对齐系数:一般为8个字节。#pragmapack(8)设置对齐系数为8。有效对齐值:假设结构体中最长的类型的长度为len,则有效对齐值=min(len,对齐系数)。计算规则:计算存放的位置:第一个成员放在位置0,后面的成员A存放的时候,会先计算size=min(A大小,有效对齐值),A只放在size的整数倍......
  • JAVA 线上故障排查完整套路,从 CPU、磁盘、内存、网络、GC 一条龙!
    https://mp.weixin.qq.com/s/zaoypK8nn1egoKFFLKxNLQ   (给Java日知录加星标,提高Java技能)线上故障主要会包括cpu、磁盘、内存以及网络问题,而大多数故障可能会包含不止一个层面的问题,所以进行排查时候尽量四个方面依次排查一遍。同时例如jstack、jmap等工具也是不囿于一个......
  • C#中的高级主题:内存管理、性能优化和安全性
    简介:欢迎来到C#语言入门指南的第十三篇博客!在之前的博客中,我们已经学习了C#的基础和一些高级编程概念。今天,我们将深入研究C#中的高级主题,包括内存管理、性能优化和安全性,以帮助您构建更出色的应用程序。让我们开始吧!1.内存管理:了解C#中的内存管理是非常重要的,它可以帮助您避免......
  • CPU/内存/磁盘/网络/redis/MQ测试工具合集
    闲余时间为大家整理了CPU性能测试、内存带宽测试、内存延迟测试、磁盘IOPS测试、网络测试、数据库测试、Kafka/rabbitMQ性能测试工具合集,后续也会对工具进行简单使用说明。序号工具名称监控策略及内容1UnixBench-5.1.4CPU性能测试2stream内......