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);

	// 定位第一个区块地址,因为默认的话第一个就是.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);

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

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

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

	while (1)
	{
		Sleep(3000);
		DWORD NewCRC32 = CheckMemory();
		if (OriginalCRC32 == NewCRC32)
			printf("程序没有被打补丁. \n");
		else
			printf("程序被打补丁 \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);

	// 以下三条语句可用于确定位置
	// pSecHeader = IMAGE_FIRST_SECTION(pNtHeader);
	// DWORD va_base1 = ImageBase + pSecHeader->VirtualAddress;   // 定位代码节va基地址
	// DWORD sec_len1 = pSecHeader->Misc.VirtualSize;             // 获取代码节长度
	// printf("镜像基址(.text): %x --> 镜像大小: %x \n", va_base1, sec_len1);

	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");
		else
			printf("此区域已被修改\n");

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

通过使用磁盘校验结合内存校验两种方式综合保护,可以极大的提高软件的安全性,绕过方式则是找到哪儿跟全局变量将其修正为正确的值即可,同样的也可以更暴力一些直接将判断条件改掉均可。

文章出处:https://www.cnblogs.com/lyshark

以上就是C++ 使用CRC32检测内存映像完整性的实现步骤的详细内容,更多关于C++用CRC32检测内存映像完整性的资料请关注我们其它相关文章!

(0)

相关推荐

  • 深入理解C++中的new/delete和malloc/free动态内存管理及区别介绍

    malloc/free和new/delete的区别 malloc/free是C/C++标准库的函数:new/delete是C++操作符. malloc/free只是动态分配内存空间/释放空间:new/delete除了分配空间还会调用构造函数和析构函数进行初始化与清理资源. malloc/free需要手动计算类型大小且返回值类型为void*:new/delete可自动计算类型的大小,返回对应类型的指针. malloc/free管理内存失败会返回0:new/delete等的方式管理内存失败会抛出异常

  • 一文读懂C++中指针和内存分配

    指针 指针是保存内存位置地址的变量.我们知道声明的所有变量在内存中都有一个特定的地址.声明一个指针变量来指向内存中的这些地址. 声明指针变量的一般语法是: int p, *ptr; //声明变量p和指针变量ptr p = 4; //赋值4给变量p ptr = &p; //将p的地址分配给指针变量ptr 在内存中,这些声明将表示如下: 这是指针在内存中的内部表示.当地址变量分配给指针变量时,它指向的变量如上图所示. 由于 ptr具有变量 p 的地址,*ptr 将给出变量 p 的值(指针变量 ptr

  • 关于C#调用C++dll传指针释放内存问题

    一.传入dll前,在C#中申请内存空间 c#里面的指针即 IntPtr 申请如下: IntPtr SrcImgData = Marshal.AllocHGlobal(length); 这种需要提前知道空间大小,否则无法确定空间大小,会导致dll内部处理时越界报错. c#里面申请空间了,那么c++里面一般就是在这些空间里面操作了,一般不会重新分配内存,那么就不需要加引用了. c++: uchar* SrcImg c#导入dll函数时申明: IntPtr SrcImg 那么内存释放自然也是由c#来进

  • c++ 排查内存泄漏的妙招

    前言 对于c++而言,如何查找内存泄漏是程序员亘古不变的话题:解决之道可谓花样繁多.因为最近要用到QT写程序,摆在我面前的第一个重要问题是内存防泄漏.如果能找到一个简单而行之有效的方法,对后续开发大有裨益.久思终得诀窍,本文就详细介绍我对此问题的应对之策.(文末符完整代码) 如何判断内存有泄漏 内存分配和释放对应的操作是new.delete.如何判断内存是否释放干净?其实判断起来非常简单:一个独立的模块整个生存周期内new的个数和delete的个数相等.用伪代码标示如下: int newCoun

  • python和C++共享内存传输图像的示例

    原理 python没有办法直接和c++共享内存交互,需要间接调用c++打包好的库来实现 流程 C++共享内存打包成库 python调用C++库往共享内存存图像数据 C++测试代码从共享内存读取图像数据 实现 1.c++打包库 创建文件 example.cpp #include <iostream> #include <cassert> #include <stdlib.h> #include <sys/shm.h> #include "opencv

  • 一文秒懂C语言/C++内存管理(推荐)

    C 语言内存管理指对系统内存的分配.创建.使用这一系列操作.在内存管理中,由于是操作系统内存,使用不当会造成毕竟麻烦的结果.本文将从系统内存的分配.创建出发,并且使用例子来举例说明内存管理不当会出现的情况及解决办法. 一.内存 在计算机中,每个应用程序之间的内存是相互独立的,通常情况下应用程序 A 并不能访问应用程序 B,当然一些特殊技巧可以访问,但此文并不详细进行说明.例如在计算机中,一个视频播放程序与一个浏览器程序,它们的内存并不能访问,每个程序所拥有的内存是分区进行管理的. 在计算机系统中

  • c++ 动态内存分配相关总结

    下面随笔是关于c++动态内存分配. 动态申请内存操作符 new new 类型名T(初始化参数列表) 功能:在程序执行期间,申请用于存放T类型对象的内存空间,并依初值列表赋以初值. 结果值:成功:T类型的指针,指向新分配的内存:失败:抛出异常. 释放内存操作符delete delete 指针p 功能:释放指针p所指向的内存.p必须是new操作的返回值. //例1 动态创建对象举例 #include <iostream> using namespace std; class Point { pub

  • 详解C++ 内存对齐

    操作系统64位和32位有什么区别? 64位操作系统意味着其cpu拥有更大的寻址能力.理论上来说,其性能相比于32位操作系统会提升1倍.但是这也需要在64位操作系统上运行的软件也是64位的. 软件中数据类型的的字节数大小其实和操作系统是多少位的没有关系,而是由编译器决定的.也就是说数据结构占多少位取决于在软件编译时我们选择的是64位还是32位的编译器.其具体占位数在编译器已经决定了. 数据类型对应字节数 下面是不同位数编译器下基本数据类型对应的字节数. 32位编译器: char :1个字节 cha

  • 详解C/C++内存管理

    C/C++赋予程序员管理内存的自由,是C/C++语言特色,虽然这引入了复杂度和危险性,但另一方面,它也增加了控制力和灵活性,是C/C++独特之处,亦是强大之处. C/C++内存分布 让我们先来看看下面这段代码: int globalVar = 1; static int staticGlobalVar = 1; void Test() { static int staticVar = 1; int localVar = 1; int num1[10] = { 1, 2, 3, 4 }; char

  • C++ 使用CRC32检测内存映像完整性的实现步骤

    仅对.text代码段进行校验: 通常程序中至少包括了代码段,数据段,而数据段中所存储的数据是经常会发生变动的,例如我们的全局变量,静态变量等都会默认存储在数据段,而代码段则不会发生变化,我们在检验时只需要注重.text内存段中的数据完整性即可,针对内存的校验同样可以抵御调试器的CC断点,该断点原理就是在下端处写入int3指令,同样可以检测得到. 校验思路如下 1.首先从内存得到PE的代码节的RVA和节大小 2.根据得到的RVA和节大小计算出crc32或是RC4值 3.读取自身保存的原始CRC32

  • Tomcat 检测内存泄漏实例详解

     Tomcat如何检测内存泄漏 一般情况下,如果我们重启web应用是通过重启tomcat的话,则不存在内存泄漏问题.但如果不重启tomcat而对web应用进行重加载则可能会导致内存泄漏,因为重加载后有可能会导致原来的某些内存无法让GC回收,例如web应用使用了JDBC,驱动会进行注册,当web应用停止时没有反注册就会导致内存泄漏. 看看是什么原因导致tomcat内存泄漏的.这个要从热部署开始说起,因为tomcat提供了不必重启容器而只需重启web应用以达到热部署的功能,其实现是通过定义一个Web

  • Android性能优化之利用强大的LeakCanary检测内存泄漏及解决办法

    前言: 最近公司C轮融资成功了,移动团队准备扩大一下,需要招聘Android开发工程师,陆陆续续面试了几位Android应聘者,面试过程中聊到性能优化中如何避免内存泄漏问题时,很少有人全面的回答上来.所以决定抽空学习总结一下这方面的知识,以及分享一下我们是如何检测内存泄漏的.我们公司使用开源框架LeakCanary来检测内存泄漏. 什么是内存泄漏? 有些对象只有有限的生命周期.当它们的任务完成之后,它们将被垃圾回收.如果在对象的生命周期本该结束的时候,这个对象还被一系列的引用,这就会导致内存泄漏

  • 使用Android Studio检测内存泄露(LeakCanary)

    内存泄露,是Android开发者最头疼的事.可能一处小小的内存泄露,都可能是毁千里之堤的蚁穴. 怎么才能检测内存泄露呢? AndroidStudio 中Memory控件台(显示器)提供了一个内存监视器.我们可以通过它方便地查看应用程序的性能和内存使用情况,从而也就可以找到需要释放对象,查找内存泄漏等. 熟悉Memory界面 打开日志控制台,有一个标签Memory ,我们可以在这个界面分析当前程序使用的内存情况. 运行要监控的程序(APP)后,打开Android Monitor控制台窗口,可以看到

  • Android中LeakCanary检测内存泄漏的方法

    最近要对产品进行内存泄漏的检查,最后选择了使用Square公司开源的一个检测内存泄漏的函数库LeakCanary,在github上面搜索了一下竟然有1.6w个star,并且Android大神JakeWharton也是这个开源库的贡献者.那么就赶快拿来用吧. 先说一下我遇到的坑,我当时是直接google的,然后就直接搜索到稀土掘金的一篇关于LeakCanary的介绍,我就按照他们的文章一步步的操作,到最后才发现,他们那个build.gradle中导入的库太老了,会报这样的错误Closed Fail

  • Android LeakCanary检测内存泄露原理

    以LeakCanary2.6源码分析LeakCanary检测内存泄露原理,为减少篇幅长度,突出关键点,不粘贴大量源码,阅读时需搭配源码食用. 如何获取context LeakCanary只需引入依赖,不需要初始化代码,就能执行内存泄漏检测了,它是通过ContentProvider获取应用的context.这种获取context方式在开源第三方库中十分流行.如下AppWatcherInstaller在LeakCanary的aar包中manifest文件中注册. internal sealed cl

  • C++程序检测内存泄漏的方法分享

    一.前言 在Linux平台上有valgrind可以非常方便的帮助我们定位内存泄漏,因为Linux在开发领域的使用场景大多是跑服务器,再加上它的开源属性,相对而言,处理问题容易形成"统一"的标准.而在Windows平台,服务器和客户端开发人员惯用的调试方法有很大不同.下面结合我的实际经验,整理下常见定位内存泄漏的方法. 注意:我们的分析前提是Release版本,因为在Debug环境下,通过VLD这个库或者CRT库本身的内存泄漏检测函数能够分析出内存泄漏,相对而言比较简单.而服务器有很多问

  • C++内存泄漏及检测工具详解

    首先我们需要知道程序有没有内存泄露,然后定位到底是哪行代码出现内存泄露了,这样才能将其修复. 最简单的方法当然是借助于专业的检测工具,比较有名如BoundsCheck,功能非常强大,相信做C++开发的人都离不开它.此外就是不使用任何工具,而是自己来实现对内存泄露的监控,分如下两种情况: 一. 在 MFC 中检测内存泄漏 假如是用MFC的程序的话,很简单.默认的就有内存泄露检测的功能. 我们用VS2005生成了一个MFC的对话框的程序,发现他可以自动的检测内存泄露.不用我们做任何特殊的操作. 仔细

  • 详解Android内存泄漏检测与MAT使用

    内存泄漏基本概念 内存检测这部分,相关的知识有JVM虚拟机垃圾收集机制,类加载机制,内存模型等.编写没有内存泄漏的程序,对提高程序稳定性,提高用户体验具有重要的意义.因此,学习Java利用java编写程序的时候,要特别注意内存泄漏相关的问题.虽然JVM提供了自动垃圾回收机制,但是还是有很多情况会导致内存泄漏. 内存泄漏主要原因就是一个生命周期长的对象,持有了一个生命周期短的对象的引用.这样,会导致短的对象在该回收时候无法被回收.Android中比较典型的有:1.静态变量持有Activity的co

  • C语言中的内存泄露 怎样避免与检测

    有些程序并不需要管理它们的动态内存的使用.当需要内存时,它们简单地通过分配来获得,从来不用担心如何释放它.这类程序包括编译器和其他一些运行一段固定的(或有限的)时间然后终止的程序.当这种类型的程序终止时,所有内存会被自动回收.细心查验每块内存是否需要回收纯属浪费时间,因为它们不会再被使用. 其他程序的生存时间要长一点.有些工具如日历管理器.邮件工具以及操作系统本事经常需要数日及至数周连续运行,并需要管理动态内存的分配和回收.由于C语言通常并不使用垃圾回收器(自动确认并回收不再使用的内存块),这些

随机推荐