当软件被开发出来时,为了增加软件的安全性,防止被破解,通常情况下都会对自身内存或磁盘文件进行完整性检查,以防止解密者修改程序,我们可以将exe与dll文件同时做校验,来达到相互认证的目的,解密者想要破解则比较麻烦,当我们使用的互认证越多时,解密者处理的难度也就越大。

实现磁盘文件检测,我们可以使用CRC32算法或者RC4算法来计算程序的散列值,以CRC32为例,其默认会生成一串4字节CRC32散列,我们只需要计算后将该值保存在文件或程序自身PE结构中的空缺位置即可。

具体实现:通过使用CRC32算法计算出程序的CRC字节,并将其写入到PE文件的空缺位置,这样当程序再次运行时,来检测这个标志,是否与计算出来的标志一致,来决定是否运行程序,一旦程序被打补丁,其crc32值就会发生变化,一旦发生变化程序就废了。

实现CRC32完整性检查: 生成CRC32的代码如下,其中的CRC32就是计算过程,这个过程是一个定式,我们只需要使用CreateFile打开文件,并将文件字节数全部读入到BYTE *pFile = (BYTE*)malloc(dwSize);中,然后调用crc32计算其硬盘中的hash散列值即可。

#include <stdio.h>
#include <stdlib.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);
}int main(int argc, char* argv[])
{char *FileName = "c://test.exe";// 验证文件是否存在,不存在则退出if (GetFileAttributes(FileName) == 0xFFFFFFFF)return 0;HANDLE hFile = CreateFile(FileName, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);DWORD dwSize = GetFileSize(hFile, NULL);// 开辟一段内存空间BYTE *pFile = (BYTE*)malloc(dwSize);// 将数据读入文件DWORD dwNum = 0;ReadFile(hFile, pFile, dwSize, &dwNum, 0);// 计算CRC32DWORD dwCrc32 = CRC32(pFile, dwSize);if (pFile != NULL){printf("CRC32 = 0x%x \n", dwCrc32);free(pFile);pFile = NULL;}system("pause");return 0;
}

1.我们将程序自身放入C://test.exe中,然后计算其hash散列值,最终得到CRC32 = 0x70122091,接着我们去找PE文件头,其结构中有很多空字节可以使用,我我们就选择PE头之前的最后4个字节作为替换位置。

2.接着就是如何定位并读出节表中是的数据了,读取数据可以这样写。

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>  int main(int argc, char* argv[])
{char szFileName[MAX_PATH] = { 0 };char *pBuffer;DWORD pNumberOfBytesRead;int FileSize = 0;// 获取自身文件,并打开文件GetModuleFileName(0, szFileName, MAX_PATH);HANDLE hFile = CreateFile(szFileName, GENERIC_READ, 1, 0, 3, FILE_ATTRIBUTE_NORMAL, 0);// 为空则打开失败,退出if (hFile == INVALID_HANDLE_VALUE) return FALSE;// 获取文件大小读入缓冲区FileSize = GetFileSize(hFile, 0);pBuffer = new char[FileSize];ReadFile(hFile, pBuffer, FileSize, &pNumberOfBytesRead, 0);CloseHandle(hFile);PIMAGE_DOS_HEADER pDosHeader = NULL;PIMAGE_NT_HEADERS32 pNtHeader = NULL;// 获取到DOS头数据pDosHeader = (PIMAGE_DOS_HEADER)pBuffer;// 获取到NT头pNtHeader = (PIMAGE_NT_HEADERS32)((DWORD)pDosHeader + pDosHeader->e_lfanew);// 定位到PE文件头前4字节处DWORD OriginalCRC32 = *(DWORD *)((DWORD)pNtHeader - 4);printf("读出节表值: %x \n", OriginalCRC32);system("pause");return 0;
}

首先编译器生成以上代码片段,然后我们使用前面的CRC32计算工具计算出其hash散列值,CRC32 = 0x92e05c8a 将此地址,反写到程序中。

会发现,当我们尝试修改程序中的数据时,crc32散列值也会随之变化,也就是说我们动了程序crc32也就重新就算了,这好像是一个死结无法被解开,那么该如何解决这个问题呢?

我们只需要更改以下CRC32计算程序,让其跳过PE头前面的DOS头部分,不让其参与到计算中,即可解决这个冲突问题,由于DOS头没什么实际作用,跳过也无妨,将计算代码进行更改。

#include <stdio.h>
#include <stdlib.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);
}BOOL CheckCRC32()
{char szFileName[MAX_PATH] = { 0 };char *pBuffer;DWORD pNumberOfBytesRead;int FileSize = 0;// 获取自身文件,并打开文件GetModuleFileName(0, szFileName, MAX_PATH);HANDLE hFile = CreateFile(szFileName, GENERIC_READ, 1, 0, 3, FILE_ATTRIBUTE_NORMAL, 0);if (hFile == INVALID_HANDLE_VALUE) return FALSE;FileSize = GetFileSize(hFile, 0);pBuffer = new char[FileSize];ReadFile(hFile, pBuffer, FileSize, &pNumberOfBytesRead, 0);CloseHandle(hFile);PIMAGE_DOS_HEADER pDosHeader = NULL;PIMAGE_NT_HEADERS32 pNtHeader = NULL;pDosHeader = (PIMAGE_DOS_HEADER)pBuffer;// 获取到NT头pNtHeader = (PIMAGE_NT_HEADERS32)((DWORD)pDosHeader + pDosHeader->e_lfanew);// 定位到PE文件头前4字节处DWORD OriginalCRC32 = *(DWORD *)((DWORD)pNtHeader - 4);printf("读出节表值: %x \n", OriginalCRC32);// 我们只需要计算PE结构的CRC32值,不需要计算DOS头FileSize = FileSize - DWORD(pDosHeader->e_lfanew);DWORD CheckCRC32 = CRC32((BYTE*)(pBuffer + pDosHeader->e_lfanew), FileSize);printf("计算出 CRC32 = %x \n", CheckCRC32);if (CheckCRC32 == OriginalCRC32)printf("程序没有被破解 \n");elseprintf("程序被破解 \n");
}int main(int argc, char* argv[])
{CheckCRC32();system("pause");return 0;
}

编译程序,并记下 CRC32 = 86906a18 hash数值。

写入到文件中,即可实现磁盘文件的完整性检测,注意写入时应该是反写,且前面要补0.

在此次打开会提示程序没有被破解,当用户认为的修改指令时,就会提示已破解,无法继续运行下去。

如何破解: 如果目标磁盘文件进行了CRC32磁盘校验,我们该如何破解呢?思路差不多就是找到CRC32算号位置,然后观察其结果到底时与谁进行的比较,将指令取反,也可实现破解。

定位CRC32位置我们可以观察期算法特征,首先他会用到0xEDB88320L,0xFFFFFFFF,0x00FFFFFF这三个关键常数,我们可以将其作为识别条件的一部分。

其次CRC32会有一个256此的循环也可以作为识别条件,或者拦截ReadFile也可,因为计算之前必定会读取,也是一个思路。

将对比过程取反,同样可以过掉其磁盘CRC32的检测。

MapFileAndCheckSum 校验和: 通过使用系统提供的API实现反破解,该函数主要通过检测,PE可选头IMAGE_OPTIONAL_HEADER中的Checksum字段来实现的,一般的EXE默认为0而DLL中才会启用,当然你可以自己开启,让其支持这种检测.

#include <stdio.h>
#include <windows.h>
#include <Imagehlp.h>
#pragma comment(lib,"imagehlp.lib")int main(int argc,char *argv[])
{DWORD HeadChksum = 1, Chksum = 0;char text[512];GetModuleFileName(GetModuleHandle(NULL), text, 512);if (MapFileAndCheckSum(text, &HeadChksum, &Chksum) != CHECKSUM_SUCCESS)return 0;if (HeadChksum != Chksum)printf("文件校验和错误 \n");elseprintf("文件正常 \n");system("pause");return 0;
}

在编译上方代码之前,需要将编译器进行一定的设置,以确保支持校验和。

C/C++ -> 常规 -> 调试信息格式 --> 程序数据库

连接器 -> 常规 -> 启用增量链接 -> 否

连接器 -> 高级 -> 设置校验和 -> 是

启用校验和后,IMAGE_OPTIONAL_HEADER中的Checksum字段保存有该程序的hash数据。

磁盘校验还可以用于反脱壳,我们可以加壳后在壳子的PE结构中留下一些记号,当我们的程序被脱壳后程序中的判断语句将会起作用,从而让脱壳后的程序无法正常运行,也是一种思路。

C/C++ 使用 CRC32 检测磁盘文件完整性相关推荐

  1. SHA256sum系列命令检测文件完整性

    1 sha256sum sha256sum是一个检测文件完整性的命令,一般下载的文件都会附带一个哈希值,使用sha256sum计算下载文件的哈希值再与目标哈希值比较即可确定文件是否完整,类似的命令还有 ...

  2. C/C++ 使用 CRC32 检测内存映像完整性

    前面的那一篇文章中所使用的技术只能有效抵抗解密者直接修改硬盘文件,当我们使用动态补丁的时候,那么内存中同样不存在校验效果,也就无法抵御对方动态修改机器码了,为了防止解密者直接对内存打补丁,我们需要在硬 ...

  3. linux怎么检测文件完整性,Linux如何基于AIDE检测文件系统完整性

    一.AIDE AIDE(Advanced Intrusion Detection Environment)是一款针对文件和目录进行完整性对比检查的程序,它被开发成Tripwire的一个替代品. AID ...

  4. Linux 大文件crc计算,Windows和Linux下使用MD5、SHA1、CRC32校验备份文件的完整性

    因为经常会用到备份加密压缩过的数据到不同的服务器,然而这些服务器是否可靠,服务器的提供者是否值得信任,为了保证文件的完整性不被篡改,也为了多个地方存储的同一个大文件的备份自己能够快速的识别出来是否是同 ...

  5. 文件服务器恢复测试,基于文件传输中文件损坏检测和恢复办法.doc

    基于文件传输中文件损坏检测和恢复办法 基于文件传输中文件损坏检测和恢复办法 摘 要:在网络上文件传输是一种常见的应用,讨论在文件传输完成后检测错误和恢复数据的办法. 关键词:文件传输;文件校验;恢复 ...

  6. C/C++ 通过CRC32实现反破解

    我们可以通过使用CRC32算法计算出程序的CRC字节,并将其写入到PE文件的空缺位置,这样当程序再次运行时,来检测这个标志,是否与计算出来的标志一致,来决定是否运行程序,一旦程序被打补丁,其crc32 ...

  7. 干货|深度学习实现零件的缺陷检测

    全世界只有不到2%的人关注了视觉IMAX 你真是个特别的人 视觉IMAX的第34篇原创文章 前言 本文由Oliver Cui根据实战经验,撰稿而成,同时他也是「视觉IMAX」知识星球特邀嘉宾. 介绍 ...

  8. 基于VMM的Rootkit检测技术及模型分析

    Linux通过其特有的虚拟文件系统(Virtual Filesystem)实现对多种文件系统的兼容.虚拟文件系统又称虚拟文件系统转换(Virtual Filesystem Switch vFs),是一 ...

  9. OpenCV精进之路(二十二):实例——皮肤检测技术

    好久没写博客了,因为最近都忙着赶项目和打比赛==| 好吧,今天我打算写一篇关于使用opencv做皮肤检测的技术总结.那首先列一些现在主流的皮肤检测的方法都有哪些: RGB color space Yc ...

最新文章

  1. 技术解析系列 阿里 PouchContainer 资源管理探秘
  2. 音视频技术开发周刊 56期
  3. PAT1054 求平均值 (20 分)【从非法输入字符串中获取合法输入 cin.putback()】
  4. transform再次理解
  5. 【论文阅读】PrivPy: General and Scalable Privacy-Preserving Data Mining
  6. pytorch:属性统计
  7. STM32学习:外部中断
  8. mysql5.7未生成初始密码.mysql_secert文件,登陆数据库
  9. LeetCode学习记录(10)
  10. 每个人心中都有一片极乐净土
  11. 读Thinking in Java(1~4)
  12. 一键打开常用软件(bat/vbs)
  13. python编程题自动评分系统_基于数据分析的评分系统改进
  14. 外观检验人员一致性(Kappa)分析
  15. Ural 1671. Anansi's Cobweb
  16. 顺应EMM趋势,烽火星空重磅出击
  17. (栈帧和函数调用一)栈帧,函数调用与栈的关系
  18. Android - ProGuard简介
  19. matlab 眼图 值,Matlab通信仿真——带限系统下的基带信号
  20. 数据预处理(纯干货,适合小白学习)

热门文章

  1. Mooc_AutoCAD绘制建筑施工图_楼梯平面详图单元测验题
  2. 现在的00后,真是卷死了呀,辞职信已经写好准备提交了·····
  3. BUCK电路硬件在环测试
  4. 百度云推送相关问题记录
  5. 【行研报告】2021年中国剧本杀行业用户研究及标杆企业案例分析报告—附下载链接
  6. 【行研报告】2020年香蜜湖金融科技创新蓝皮书——附下载链接
  7. 可以免费下载字体包的地址
  8. 计算机自考论文怎么参考,计算机自考毕业论文.docx
  9. linux中的***
  10. 液压水位控制阀原理液压水位控制阀厂家