C++ 使用CRC32检测内存映像完整性的实现步骤
程序员文章站
2022-06-25 10:01:37
仅对.text代码段进行校验:通常程序中至少包括了代码段,数据段,而数据段中所存储的数据是经常会发生变动的,例如我们的全局变量,静态变量等都会默认存储在数据段,而代码段则不会发生变化,我们在检验时只需...
仅对.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; }
通过使用磁盘校验结合内存校验两种方式综合保护,可以极大的提高软件的安全性,绕过方式则是找到哪儿跟全局变量将其修正为正确的值即可,同样的也可以更暴力一些直接将判断条件改掉均可。
文章出处:
以上就是c++ 使用crc32检测内存映像完整性的实现步骤的详细内容,更多关于c++用crc32检测内存映像完整性的资料请关注其它相关文章!
上一篇: 分析Go语言接口的设计原则