C/C++ 使用 CRC32 检测内存映像完整性
前面的那一篇文章中所使用的技术只能有效抵抗解密者直接修改硬盘文件,当我们使用动态补丁的时候,那么内存中同样不存在校验效果,也就无法抵御对方动态修改机器码了,为了防止解密者直接对内存打补丁,我们需要在硬盘校验的基础上,增加内存校验,防止动态补丁的运用。
仅对.text代码段进行校验:
通常程序中至少包括了代码段,数据段,而数据段中所存储的数据是经常会发生变动的,例如我们的全局变量,静态变量等都会默认存储在数据段,而代码段则不会发生变化,我们在检验时只需要注重.text内存段中的数据完整性即可,针对内存的校验同样可以抵御调试器的CC断点,该断点原理就是在下端处写入int3指令,同样可以检测得到。
校验思路如下
1.首先从内存得到PE的代码节的RVA和节大小
2.根据得到的RVA和节大小计算出crc32或是RC4值
3.读取自身保存的原始CRC32值,与校验结果进行比较
1.先来实现第一步,读取内存映像的起始地址与大小,我们可以这样做。
#include <stdio.h>
#include <windows.h>int main(int argc, char *argv[])
{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;// 定位到NT头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 --> 镜像大小: %x \n", va_base, sec_len);system("pause");return 0;
}
2.第二部就是计算校验和,然后计算该节的CRC32值,并存入全局变量,也就是程序打开后自动初始化计算一次内存crc32值并放入全局变量中,然后开一个线程,每三秒检测一次内存变化,如果变化则终止执行或弹窗提示,你也可以提前计算处校验和并写入PE空缺位置。
#include <stdio.h>
#include <windows.h>DWORD CRC32(BYTE* ptr, DWORD Size)
{DWORD crcTable[256], crcTmp1;// 动态生成CRC-32表for (int i = 0; i<256; i++){crcTmp1 = i;for (int j = 8; j>0; j--){if (crcTmp1 & 1) crcTmp1 = (crcTmp1 >> 1) ^ 0xEDB88320L;else crcTmp1 >>= 1;}crcTable[i] = crcTmp1;}// 计算CRC32值DWORD crcTmp2 = 0xFFFFFFFF;while (Size--){crcTmp2 = ((crcTmp2 >> 8) & 0x00FFFFFF) ^ crcTable[(crcTmp2 ^ (*ptr)) & 0xFF];ptr++;}return (crcTmp2 ^ 0xFFFFFFFF);
}// 检查内存中CRC32特征值
DWORD CheckMemory()
{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);pSecHeader = IMAGE_FIRST_SECTION(pNtHeader);DWORD va_base = ImageBase + pSecHeader->VirtualAddress; // 定位代码节va基地址DWORD sec_len = pSecHeader->Misc.VirtualSize; // 获取代码节长度DWORD CheckCRC32 = CRC32((BYTE*)(va_base), sec_len);// printf(".text节CRC32 = %x \n", CheckCRC32);return CheckCRC32;
}int main(int argc,char *argv[])
{DWORD OriginalCRC32 = 0;OriginalCRC32 = CheckMemory();while (1){Sleep(3000);DWORD NewCRC32 = CheckMemory();if (OriginalCRC32 == NewCRC32)printf("程序没有被打补丁. \n");elseprintf("程序被打补丁 \n");}system("pause");return 0;
}
上方代码是保护了整个程序,在实际应用中,为了提高效率,有时我们只需要保护其中一个片段代码就好,这样可以提高效率,所有我们对上面代码稍作修改即可实现针对特定片段的内存校验。
#include <stdio.h>
#include <windows.h>DWORD CRC32(BYTE* ptr, DWORD Size)
{DWORD crcTable[256], crcTmp1;// 动态生成CRC-32表for (int i = 0; i<256; i++){crcTmp1 = i;for (int j = 8; j>0; j--){if (crcTmp1 & 1) crcTmp1 = (crcTmp1 >> 1) ^ 0xEDB88320L;else crcTmp1 >>= 1;}crcTable[i] = crcTmp1;}// 计算CRC32值DWORD crcTmp2 = 0xFFFFFFFF;while (Size--){crcTmp2 = ((crcTmp2 >> 8) & 0x00FFFFFF) ^ crcTable[(crcTmp2 ^ (*ptr)) & 0xFF];ptr++;}return (crcTmp2 ^ 0xFFFFFFFF);
}// 检查内存中CRC32特征值
DWORD CheckMemory(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);pDosHeader = (PIMAGE_DOS_HEADER)ImageBase;pNtHeader = (PIMAGE_NT_HEADERS32)((DWORD)pDosHeader + pDosHeader->e_lfanew);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 = CheckMemory(begin_addr, size);while (1){begin: // 标记为需要保护的区域printf("hello lyshark \n");printf("hello lyshark \n");printf("hello lyshark \n");end: // 保护区域声明结束if (OriginalCRC32 == CheckMemory(begin_addr, size))printf("此区域没有被破解 \n");elseprintf("此区域已被修改\n");Sleep(3000);}system("pause");return 0;
}
通过使用磁盘校验结合内存校验两种方式综合保护,可以极大的提高软件的安全性,绕过方式则是找到哪儿跟全局变量将其修正为正确的值即可,同样的也可以更暴力一些直接将判断条件改掉均可。
C/C++ 使用 CRC32 检测内存映像完整性相关推荐
- unity如何检测内存泄漏_如何排查Java内存泄漏?看懂这一篇就够用了
原文:https://www.toptal.com/java/hunting-memory-leaks-in-java 作者:Jose Ferreirade Souza Filho 译者:Emma来源 ...
- C++ 检测内存泄露
本文描述了如何检测内存泄露.最主要的是纯C,C++的程序如何检测内存泄露. 现在有很多专业的检测工具,比如比较有名的BoundsCheck, 但是这类工具也有他的缺点,我认为首先BoundsCheck ...
- 动态检测内存错误利器ASan
ASan,即Address Sanitizer,是一个适用于c/c++程序的动态内存错误检测器,它由一个编译器检测模块(LLVM pass)和一个替换malloc函数的运行时库组成,在性能及检测内存错 ...
- iis多进程下的全局变量_Linux下c程序的内存映像
前言 -----今天开始分享C语言里面的存储类型.作用域.生命周期.链接属性等知识点,我们写完一个程序,不只说知其,更要知其所以然. 概念简介: - 存储类 - (1)存储类就是存储类型,也 ...
- VC使用CRT调试功能来检测内存泄漏
信息来源:csdn C/C++ 编程语言的最强大功能之一便是其动态分配和释放内存,但是中国有句古话:"最大的长处也可能成为最大的弱点",那么 C/C++ 应用程序正好印证 ...
- 如何在linux下检测内存泄漏
1.开发背景 在 windows 下使用 VC 编程时,我们通常需要 DEBUG 模式下运行程序,而后调试器将在退出程序时,打印出程序运行过程中在堆上分配而没有释放的内存信息,其中包括代码文件名.行号 ...
- VC使用CRT调试功能检测内存泄漏(转载)
/*********************************************************************************** 检测内存泄漏的基本工具是调试器 ...
- Android Studio检测内存泄露和性能
韩梦飞沙 yue31313 韩亚飞 han_meng_fei_sha 313134555@qq.com 首先需要明白一个概念, 内存泄露就是指,本应该回收的内存,还驻留在内存中. 一般情况下,高密度 ...
- VC++ 6.0 中如何使用 CRT 调试功能来检测内存泄漏[转]
/C++ 编程语言的最强大功能之一便是其动态分配和释放内存,但是中国有句古话:"最大的长处也可能成为最大的弱点",那么 C/C++ 应用程序正好印证了这句话.在 C/C++ 应用程 ...
最新文章
- r语言 plot_R和Python的特点对比,这样你就知道该怎么选择了
- Spring boot快速创建工程
- workarea8php,oracle PGA管理(算法)
- OpenGL Compute Shader Particle System计算着色器粒子系统的实例
- .dat文件写入byte类型数组_《计算机导论》课程实验报告(文件)
- maven java管理_java – 依赖管理与maven
- 性能优化篇(3):避免空的图片src
- mybatis多个foreach_MyBatis中使用foreach完成复杂查询
- Java校招笔试题-Java基础部分(五)
- 置信区间的置信区间_什么是置信区间,为什么人们使用它们?
- Cocos2d-x之TileMap
- 华为s5720默认用户名和密码_华为S5720常用命令
- 从零搭建hyperf docker镜像
- web网页设计期末课程大作业:红色中国文化主题网站设计——灌篮高手(4页)HTML+CSS
- Android中Alarm的机制
- 双向可控硅的触发电路设计
- 2022年玩具泡泡机市场前景分析及研究报告
- 阿里云服务器ECS新手搭建
- Modern Data Stack 下 Data Integration 生态(下)
- Node.js 从零开发web server博客项目--项目初始化