当软件被开发出来时,为了增加软件的安全性,防止被破解,通常情况下都会对自身内存或磁盘文件进行完整性检查,以防止解密者修改程序,我们可以将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); // 计算CRC32
DWORD 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");
else
printf("程序被破解 \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");
else
printf("文件正常 \n"); system("pause");
return 0;
}

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

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

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

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

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

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

C/C++ 使用CRC检测磁盘文件完整性的更多相关文章

  1. 7z常用命令行&7z检测压缩包完整性&7z压缩包错误不执行rsync同步

    7Z简介&常用命令 7Z脚本使用说明 7Z检测压缩包完整性脚本 7Z压缩包错误不执行Rsync脚本 1.7Z简介&常用命令 ⑴简介: 7z,全称7-Zip, 是一款开源软件.是目前公认 ...

  2. 反调试——7——CRC检测

    反调试--7--CRC检测 CRC32: CRC的全称是循环冗余校验,作用是为了检测数据的完整性. CRC32的检测原理: 程序被编译后,代码段是固定的,因为已经被写死了. 我们在调试程序的时候,打断 ...

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

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

  4. 反调试--CRC检测

    #include"CRC32.h" #include<Windows.h> #include<iostream> using namespace std; ...

  5. C#中Bitmap类 对图像の操作 可检测图片完整性

    try { Bitmap bm = new Bitmap(pics[ip]); BitmapToBytes(bm).Reverse().Take(2); } catch (Exception ex) ...

  6. 检测Xcode是否有问题

    之前的XCode中毒事件闹得沸沸扬扬,在网上找到检测XCode完整性的方式,有需要的小伙伴试试吧.忘记哪里转的了,愧对原创者 在终端输入 spctl 命令,并带上安装的 Xcode 的路径: spct ...

  7. OpenCV探索之路(二十七):皮肤检测技术

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

  8. CRC检错技术原理

    一.题外话 说来惭愧,一开始是考虑写关于CRC检错技术更深层次数学原理的,然而在翻看<Basic Algebra>后,我果断放弃了这种不切实际的想法.个人觉得不是因为本人数学水平差或者能力 ...

  9. Modbus通讯错误检测方法

    标准的Modbus串行网络采用两种错误检测方法.奇偶校验对每个字符都可用,帧检测(LRC和CRC)应用于整个消息.它们都是在消息发送前由主设备产生的,从设备在接收过程中检测每个字符和整个消息帧. 用户 ...

  10. Redis-05持久化

    1 Redis持久化 RDB(Redis DataBase) AOF(Append Only File) 2 RBD 2.1 基本说明 在指定的时间间隔内将内存中的数据集快照写入磁盘文件,它恢复时将快 ...

随机推荐

  1. 【JAVA基础】List处理

    List处理 List使用Lists.partition()分片 public static <T> List<List<T>> partition(List< ...

  2. 在 Ubuntu 20.04 上安装 Visual Studio Code

    Visual Studio Code 是一个由微软开发的强大的开源代码编辑器.它包含内建的调试支持,嵌入的 Git 版本控制,语法高亮,代码自动完成,集成终端,代码重构以及代码片段功能. Visual ...

  3. Android 原生 SQLite 数据库的一次封装实践

    本文首发于 vivo互联网技术 微信公众号 链接:https://mp.weixin.qq.com/s/CL4MsQEsrWS8n7lhXCOQ_g作者:Li Bingyan 本文主要讲述原生SQLi ...

  4. 更省更快,如何使用 Serverless 搭建个人专属网盘?

    作者| 西流 阿里云 Serverless 技术专家 1分钟 Serverless 部署个人网盘,赠好礼 随着全球大数据不断增长,未来数据云存储容量需求也将不断扩大,iiMedia Research( ...

  5. P4913【橙】

    蕾姆了,上一道题做的好烦,结果直接把上一题的代码稍微改改就直接五分钟做出了另一道题,就是这道橙题.虽然只是一道橙题,但上一题代码得以复用显得自己没浪费那么多时间,显得自己还是有不少收获的.心里平摊多了 ...

  6. ElasticSearch 映射类型及数据类型区分

    本文为博主原创,未经允许不得转载: 1.ES 中的映射可以分为动态映射和静态映射 动态映射:在关系数据库中,需要事先创建数据库,然后在该数据库下创建数据表,并创建表字段.类型.长度.主键等,最后才能基 ...

  7. 0xGame 2023【WEEK2】Crypto全解

    中间的那个人 题目信息 from secret import flag from Crypto.Util.number import * from Crypto.Cipher import AES f ...

  8. makefile文件详解

    1. make 编译:将源代码文件翻译成处理器可执行的二进制文件的过程,这个过程的时间区间称为编译时 构建:指定多个编译过程的先后顺序 make命令是常用的构建工具,诞生于1977年,主要用于C/C+ ...

  9. [转帖]tidb-系统内核调优及对比

    一.背景 验证系统调优对性能的影响,用sysbench做了一些简单的测试,具体调整方法可见官方文档 二.特殊说明 1.透明大页查看 # 查看透明大页是否开启,[]在always处表示开启,[]在nev ...

  10. [转帖]TiDB 环境与系统配置检查

    https://docs-archive.pingcap.com/zh/tidb/v6.0/check-before-deployment 本文介绍部署 TiDB 前的环境检查操作,以下各项操作按优先 ...