找回密码
 加入
搜索
查看: 12701|回复: 24

[效率算法] 检测程序自身的CRC

 火.. [复制链接]
发表于 2011-5-18 22:42:46 | 显示全部楼层 |阅读模式
本帖最后由 alice148 于 2011-5-20 19:22 编辑

AU3 如何实现检测程序自身的CRC  从而来实现编译好的可执行文件不被破解!!!

查了好多资料看到这个文章,但看不明白。。。大家一起研究下

老话题,程序自校验。不过放在 VS2008 下,情况又发生了变化。

作为软件开发者,我们一般不希望程序出来之后被人非法修改,比如破解或冠以 xxx 版等称谓,所以都会跟反调试反跟踪作斗争,当然,提到反跟踪作斗争,就像矛与盾,这个话题永远是无止境的,较量也是无止境的。再怎么想方设法保护程序,总有被 cracker 攻破的时候,只是时间的问题。当然我们也不能觉得既然这样那保护不保护也就无所谓了,就像我们明知道人生总有尽头,但不是会立即去报到一样:)

呵呵,婆婆妈妈这么多,意思只有一个,条件允许的话,还是适当的加上一些保护未必就是坏事。至于软件保护,可能稍微懂点的人立马会想到一个字,壳。是的,好的壳能给 cracker 增加不少的难度,那也是很大的话题,一般壳都具有自校验的功能,那我们可以尝试一下自己来实现这个小功能,而常用的自校验算法莫过于 CRC32 了,因为它加密之后的长度正好符合某些特定结构的长度。

之所以单独记下来作为备忘,是因为不同于以往 ASM 或者 VC6.0 等编译的程序,情况发生了些不痛不痒的变化,影响到最终方案的实施了。

读这篇文章,假设您已经知道了 CRC32 的基本概念,正在尝试实现程序自校验功能的开发者。我分成了下面几个部分:

一、不得不提的 CRC32 码表的生成:

查表法计算 CRC32 值是最快的。通常,您也可以保留一大段的 0xXXXXXXXX 等静态码表在程序里面,这并没有错,也完全可以。但是,PEid 等,具有一些插件,可以依据特征码来判定程序所使用的算法,所以在程序里面保留大段的静态码表会提前暴露身份,因此最好采用动态生成的方法。一个经典的动态码表的生成代码如下所示:

void CXCrc32::GenerateCrc32Table()
{
    ::SecureZeroMemory(&arCrc32Table, 256 * sizeof(DWORD));

    for (int i = 0; i < 256; i++)
    {
        DWORD dwTempCrc = i;
        for (int j = 0; j < 8; j++)
        {
            dwTempCrc = (dwTempCrc & 1) ? (dwTempCrc >> 1) ^ 0xEDB88320 : dwTempCrc >> 1;
        }
        arCrc32Table = dwTempCrc;
    }
}

这里的 arCrc32Table 就是我们要使用的 CRC32 码表,您最好放在类里面,在构造函数里面调用,这样后续的加密等就有了依据的表了。

二、加密字符串和文件,计算过程很简单,按 BYTE 进行,代码也很少:

DWORD CXCrc32::CalculateBufferCrc(const LPBYTE pbtBuffer, DWORD& dwSize)
{
    ASSERT(dwSize >= 0);

    LPBYTE pbtTempBuffer = pbtBuffer;
    DWORD dwTempCrc = 0xFFFFFFFF;
    while (dwSize--)
    {
        dwTempCrc = ((dwTempCrc >> 8) & 0x00FFFFFF) ^ arCrc32Table[(dwTempCrc ^ (*pbtTempBuffer)) & 0xFF];
        pbtTempBuffer++;
    }

    return dwTempCrc ^ 0xFFFFFFFF;
}

三、了解一下 PE 文件结构是很有必要的:


  |----------------|
  |IMAGE_DOS_HEADER|  ------ IMAGE_DOS_HEADER
  |----------------|
  |     Dos STUB   |  ------ 16位DOS 代码
  |                |
  |----------------|
  |                |  ------ Signature
  |                |
  |IMAGE_NT_HEADERS|  ------ IMAGE_FILE_HEADER
  |                |
  |                |  ------ IMAGE_OPTIONAL_HEADER32
  |----------------|
  |                |
  | SECTION_HEADER |
  |                |  ------ 节表结构
  |----------------|
  |                |
  |    SECTION     |  ------ 节
  |                |
  |----------------|

四、生成文件的 CRC32 值后,写入文件,这是最头疼的问题,这就是前面所说的变化:

以往通常都是保存在 IMAGE_OPTIONAL_HEADER.Win32VersionValue 中,因为这是一个保留值,没有使用,其长度 DWORD 正好符合 CRC32 加密之后的长度, 4 个字节。所以他是一个理想的存储位置。

但是,在 VS2008 + XP Sp3 下(Vista 下我没有测试),情况很不同了:似乎这个所谓的保留值不再是保留值了,虽然编译器编译出来默认仍然是 0,我们仍然可以修改,但是对大小已经有了严格的限制,DWORD 值大小不能超过 5 !!

这一点就浪费了我不少的时间,一开始以为老办法仍然可行,没想到双击修改 Win32VersionValue 后的程序根本没反应,就跟没运行一样,而且,这样的东西根本没办法调试,所以只好一点一点试,然后拿 PE 工具查看 PE 头信息。最后测试发现不能超过 5,那实在是一件很沮丧的事,因为一旦 cracker 知道你是使用 CRC 来做自校验(比如我在第一点里面提到的静态码表),这个地方他肯定会关注,枚举不超过 5 次程序就被破了,那也太让他们扫兴了。

在网上搜索了一下,发现有人写的博客有:http://hi.baidu.com/zuikee/blog/ ... 6d9ad4562c8452.html,但是很可惜,这个值在 VS2008 下根本不管用,9.0 的 VC 运行库似乎就卯定了 0-5 了,哎,愁啊!



于是另想办法

查找 MSDN 关于 PE 的说明

http://msdn.microsoft.com/en-us/library/ms680339(VS.85).aspx

发现有个成员被标记为 obsolete,大喜,That's it! 就是他了,你不要了我正好需要,呵呵

于是编译、写入文件,测试,一切 OK:



五、单纯的 FileSize 是不可靠的,不是说不行,因为 cracker 也可以计算现在文件的 CRC32 值,回写过去,那就又挂了,所以计算最终需要写入的 CRC32 值的时候,可以加上一些特殊信息,比如,你的生日,女朋友的手机号等等,当然你在程序里面校验文件是够被修改的时候,也是需要加入同样的特殊信息的。

六、也可以把这个 CRC32 值也在自身 exe 文件末尾或者另外挂个 dll,但,似乎都没与这种方案妥当。

参考:老罗的经典文章,做 CRC 自校验不得不看的:

         矛与盾的较量(2)——CRC原理篇
         矛与盾的较量(3)——CRC实践篇

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/DavidHsing/archive/2009/05/14/4184240.aspx



本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/inforum/archive/2009/12/29/5101226.aspx

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/inforum/archive/2009/12/29/5101226.aspx

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/inforum/archive/2009/12/29/5101226.aspx
发表于 2011-5-18 22:54:28 | 显示全部楼层
笨办法1:
在网络服务器放一个保存有程序的CRC值的文件.
在程序里面计算自身文件的CRC值.拿来和这个值比较.

笨办法2:
在程序目录放一个保存有程序的CRC值的加密文件.
在程序里面计算自身文件的CRC值.解密文件的CRC值,两个值相互比较.

借贴等待聪明的办法
发表于 2011-5-19 12:10:59 | 显示全部楼层
本帖最后由 飘云 于 2011-5-19 12:12 编辑

LZ你要的是这个吗?CRC计算函数是直接搬用论坛里的

$hTimer = TimerInit()

$sData = _CRC32ForFile(@ScriptFullPath)
$iTimer = TimerDiff($hTimer)
MsgBox (0, "本程序CRC结果", "CRC值:"&$sData&@CRLF&@CRLF&"耗时:"&$iTimer&"毫秒")


Func _CRC32ForFile($sFile)
        
    Local $a_hCall = DllCall("kernel32.dll", "hwnd", "CreateFileW", _
            "wstr", $sFile, _
            "dword", 0x80000000, _ ; GENERIC_READ
            "dword", 1, _ ; FILE_SHARE_READ
            "ptr", 0, _
            "dword", 3, _ ; OPEN_EXISTING
            "dword", 0, _ ; SECURITY_ANONYMOUS
            "ptr", 0)

    If @error Or $a_hCall[0] = -1 Then
        Return SetError(1, 0, "")
    EndIf

    Local $hFile = $a_hCall[0]

    $a_hCall = DllCall("kernel32.dll", "ptr", "CreateFileMappingW", _
            "hwnd", $hFile, _
            "dword", 0, _ ; default security descriptor
            "dword", 2, _ ; PAGE_READONLY
            "dword", 0, _
            "dword", 0, _
            "ptr", 0)

    If @error Or Not $a_hCall[0] Then
        DllCall("kernel32.dll", "int", "CloseHandle", "hwnd", $hFile)
        Return SetError(2, 0, "")
    EndIf

    DllCall("kernel32.dll", "int", "CloseHandle", "hwnd", $hFile)

    Local $hFileMappingObject = $a_hCall[0]

    $a_hCall = DllCall("kernel32.dll", "ptr", "MapViewOfFile", _
            "hwnd", $hFileMappingObject, _
            "dword", 4, _ ; FILE_MAP_READ
            "dword", 0, _
            "dword", 0, _
            "dword", 0)

    If @error Or Not $a_hCall[0] Then
        DllCall("kernel32.dll", "int", "CloseHandle", "hwnd", $hFileMappingObject)
        Return SetError(3, 0, "")
    EndIf

    Local $pFile = $a_hCall[0]
    Local $iBufferSize = FileGetSize($sFile)

    Local $a_iCall = DllCall("ntdll.dll", "dword", "RtlComputeCrc32", _
            "dword", 0, _
            "ptr", $pFile, _
            "int", $iBufferSize)

    If @error Or Not $a_iCall[0] Then
        DllCall("kernel32.dll", "int", "UnmapViewOfFile", "ptr", $pFile)
        DllCall("kernel32.dll", "int", "CloseHandle", "hwnd", $hFileMappingObject)
        Return SetError(4, 0, "")
    EndIf

    DllCall("kernel32.dll", "int", "UnmapViewOfFile", "ptr", $pFile)
    DllCall("kernel32.dll", "int", "CloseHandle", "hwnd", $hFileMappingObject)

    Local $iCRC32 = $a_iCall[0]

    Return SetError(0, 0, Hex($iCRC32))

EndFunc   ;==>_CRC32ForFile
发表于 2011-5-19 22:35:17 | 显示全部楼层
回复 2# easefull

办法1:你的前提是,使用的计算机要能够连接到网络,而这个不一定都能够满足~~

办法2:虽然可以计算程序自身的CRC(像3楼的朋友那样),但是显然,这个值必须要包含在程序本身里面,才能保证编译后可以比较以检查自身没有被修改,但是这个值你原先是不知道的,如果是计算后再加入去,程序的CRC值又会改变
发表于 2011-5-19 22:46:45 | 显示全部楼层
回复  easefull

办法1:你的前提是,使用的计算机要能够连接到网络,而这个不一定都能够满足~~

办法2:虽 ...
annybaby 发表于 2011-5-19 22:35


办法2,你可以把编译好的exe的CRC值保存到一个文件里面.然后
if CRC(*.exe) <> fileread(*.txt) then exit
当然.实际应用的时候这里的txt文件是需要加密解密的.
发表于 2011-5-19 22:56:56 | 显示全部楼层
回复 5# easefull


    这个方法是可行的,只不过一般情况下,使用者大多数希望自己的程序看起来只有一个EXE文件,而不希望还需要另外还有其它配置文件,尤其是这个程序对于使用者来说,并没有功能上的帮助,据我所知,是有程序可以做得到,比如两个非常著名的手工杀毒辅助工具SnipeSword和Wsyscheck都有自校验的
发表于 2011-5-19 23:00:38 | 显示全部楼层
所以我才说是笨办法
发表于 2011-5-20 18:58:13 | 显示全部楼层
楼主请测试

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?加入

×
 楼主| 发表于 2011-5-20 19:02:04 | 显示全部楼层
回复 7# easefull


    我都没一点头绪呀。。
 楼主| 发表于 2011-5-20 19:33:42 | 显示全部楼层
回复 8# pcbar


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?加入

×
 楼主| 发表于 2011-5-20 19:41:44 | 显示全部楼层
这样子不行吧。图标都自己没了。。。
发表于 2011-5-20 20:01:24 | 显示全部楼层
回复 11# alice148


在我的2台电脑上测试均正常
 楼主| 发表于 2011-5-20 20:11:26 | 显示全部楼层
回复 12# pcbar


    pcbar  你是用什么去修改的???我是用

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?加入

×
发表于 2011-5-20 20:24:07 | 显示全部楼层
回复 8# pcbar


    厉害,经测试,有效果~~

请问:是怎么做到的,可以公布下源码么??想学习下~~

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?加入

×
发表于 2011-5-20 20:25:54 | 显示全部楼层
回复 13# alice148


    这样改的话,应该不行吧??都破坏了文件结构了~~
您需要登录后才可以回帖 登录 | 加入

本版积分规则

QQ|手机版|小黑屋|AUTOIT CN ( 鲁ICP备19019924号-1 )谷歌 百度

GMT+8, 2024-5-2 04:10 , Processed in 0.082230 second(s), 24 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表