ET199加密方案——文件MD5校验

来源:互联网 发布:南京泛成生物 知乎 编辑:程序博客网 时间:2024/06/06 15:46

ET199(http://www.jansh.com.cn/product/detail.php?cid=13)采用安全强度最高的智能卡芯片,硬件不能被复制,多重安全级别,并且集成了16位CPU,8KRAM,64K存储空间等模块。运行在ET199内部的程序是使用C51语言进行开发的,本方案示例主要用到MD5计算的内部系统函数,通过内部MD5计算对程序添加校验。方案的测试过程需要首先在ET199加密锁中预先设置公私钥文件和一个可执行文件,详见ET199内部文件配置。

 

方案原理说明:

本方案示例,通过调用内部系统函数,将源程序除去最后16个字节二进制文件做MD5散列运算,将运算的结果保存到二进制文件的最后16个字节中,每次程序运行时,程序通过加密锁内部MD5计算对自身做MD5运算校验,如果程序被修改了,那么运算之后的结果一定与之前的的校验结果不相同,则说明程序被改动过,如果运算之后的结果与之前保存的结果相同,那么说明源程序没有被修改过。

 

方案示例:

本说明文档中仅列举核心代码或函数,详细的代码设计,请参看方案源代码。所有操作均在插有ET199加密锁的状态下完成,并且加密锁属性都是出厂默认设置。本方案示例将需要重复使用的枚举ET199、打开ET199等函数封装到一个函数:

DWORD  CMD5FileCheckDlg::OpenDog(ET_CONTEXT* pETC)

{

    DWORD dwRet;

       DWORD dwCount;

       ET_CONTEXT *pContext;

    //===============================

       //枚举ET199并返回数量

       dwCount = 0;

       dwRet   = ETEnum(NULL,&dwCount);

       if(dwRet != ET_E_INSUFFICIENT_BUFFER && dwRet != ET_S_SUCCESS)     return dwRet;

       //根据数量分配内存用于存储CONTEXT结构数组

       pContext = new ET_CONTEXT[dwCount];

       //清0缓冲区

       memset(pContext,0,sizeof(ET_CONTEXT)*dwCount);

    //使用分配的缓冲区重新进行枚举

       dwRet=ETEnum(pContext,&dwCount);

       if(dwRet != ET_S_SUCCESS) return dwRet;

       if(dwCount == 0) return ET_E_KEY_REMOVED;

       //打开ET199

       dwRet = ETOpen(&pContext[0]);

       if(dwRet != ET_S_SUCCESS) return dwRet;

       * pETC = pContext[0];

       return ET_S_SUCCESS;

}

将执行过程封装到函数:

DWORD CMD5FileCheckDlg::ExecuteDog(ET_CONTEXT* pETC, LPCSTR lpszFileID, BYTE* pInbuf, int len_In, BYTE* pOutbuf, int* len_Out)

{

       DWORD          leng;

       DWORD          dwRet;

       BYTE           tmpbuf[256];

       // =================    

       //检查加密锁句柄

    if(pETC->hLock == INVALID_HANDLE_VALUE)  return -1;

       //

       memset(tmpbuf, 0, sizeof(tmpbuf));

       //执行芯片中的1000可执行文件

       dwRet = ETExecute(pETC, lpszFileID, pInbuf, len_In, pOutbuf, *len_Out, &leng);

       if(dwRet != ET_S_SUCCESS) return dwRet;

       //tmpbuf[0]是芯片中代码执行的返回值

       if(tmpbuf[0] != 0) //ET_SUCCESS

       {

        return tmpbuf[0];

       }

       //返回数据

       *len_Out = leng;

       return 0; 

}

以上两个函数的封装,仅是为了方便起见。

 

一、运行MD5FileMake.exe,该程序实现对某一程序添加MD5校验

 

图1

选择需要添加检验的源程序MD5FileCheck.exe,单击“MD5固化文件”,会在相应目录下产生一个校验文件ChangeLess.exe。

//校验MD5

BOOL CMD5FileCheckDlg::MakeFileMD5(char* pFilePath)

{

    HANDLE  handle;

       long    file_len;

       BYTE*   pbuf;

       DWORD   bytesread;

       //==================

       //打开文件

    handle=CreateFile(pFilePath,GENERIC_READ,0,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);

       if(handle == INVALID_HANDLE_VALUE)

       {

       return FALSE;

       }

    file_len = GetFileSize(handle, 0);

       pbuf = (BYTE*)malloc(file_len);

       ReadFile(handle, pbuf, file_len, &bytesread, 0);

       CloseHandle(handle);

    GetTrueMD5(pbuf, file_len-16, pbuf+file_len-16);

    handle=CreateFile("ChangeLess.exe", GENERIC_WRITE,0,0,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,0);

       if(handle == INVALID_HANDLE_VALUE)

       {

        free(pbuf);

              //

              return FALSE;

       }

       WriteFile(handle, pbuf, file_len, &bytesread, 0);

       CloseHandle(handle);

       free(pbuf);

       return TRUE;

}

二、二进制文件的比较

源程序二进制文件和检验程序二进制文件的比较如下图所示,其中图1是源程序MD5FileCheck.exe最后16个字节的二进制,图3是检验文件ChangeLess.exe的最后16字节的二进制。

 

图2

 

图3

三、运行添加校验的程序ChangeLess.exe

 

图4

该程序实现对自身的检验,单击“校验文件MD5值”如果源程序没有被修改过,运行检验程序时会提示“文件完整性检验OK”。

 

图5

如果源程序被修改过,运行校验文件是则会提示“文件完整新检验ERR”。

 

图6

//校验MD5

BOOL CMD5FileCheckDlg::CheckFileMD5(char*  pFilePath)

{

    HANDLE  handle;

       long    file_len;

       BYTE*   pbuf;

       DWORD   bytesread;

       BYTE    TrueMD5[16];

       BYTE    FileMD5[16];

       //==================

       //打开文件

    handle=CreateFile(pFilePath,GENERIC_READ,0,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);

       if(handle == INVALID_HANDLE_VALUE)

       {

       return FALSE;

       }

    file_len = GetFileSize(handle, 0);

       pbuf = (BYTE*)malloc(file_len);

       ReadFile(handle, pbuf, file_len, &bytesread, 0);

       CloseHandle(handle);

       memset(TrueMD5, 0, sizeof(TrueMD5));

    GetMD5(pbuf, file_len-16, TrueMD5);

       memcpy(FileMD5, pbuf+file_len-16, 16);

       free(pbuf);

    if(memcmp(FileMD5, TrueMD5, 16) == 0) return TRUE;

       else  return FALSE;

}

 

//MD5计算

void  CMD5FileCheckDlg::GetMD5(BYTE* pbuf, long len,  BYTE* pOutMD5)

{

       int            leng;

       DWORD          dwRet;

       ET_CONTEXT     etc;

       BYTE           tmpbuf[1+128];

       BYTE           md5[16];

       // =================

       //打开加密锁

    if(OpenDog(&etc) != ET_S_SUCCESS)

       {

              AfxMessageBox("打开加密锁失败!");

              return;

       }

       //校验PIN码

       if(ETVerifyPin(&etc, ET_DEFAULT_USER_PIN, ET_USER_PIN_LEN, ET_USER_PIN) != ET_S_SUCCESS)

       {

              AfxMessageBox("加密锁校验用户PIN码失败!");

              return;

       }

       //文件128字节块xor

    XorBlock128(pbuf, len,  tmpbuf+1);

       //执行芯片中的1000可执行文件,功能号:MD5运算

    tmpbuf[0] = Func_MD5;      

       leng  = sizeof(tmpbuf);

    dwRet = ExecuteDog(&etc, "1000", tmpbuf, sizeof(tmpbuf), tmpbuf, &leng);

    if(dwRet == 0 && tmpbuf[0] == 0)

       {

          //返回数据

          memcpy(md5, tmpbuf+1, 16);

       }

       else

       {

       AfxMessageBox("执行加密锁内算法失败!");

       //关闭加密锁

          ETClose(&etc);

          return;

       }

       memcpy(pOutMD5, md5, 16);

       //关闭加密锁

       ETClose(&etc);

       return;

}

 

方案特点:

本加密方案的设计,首先通过对一个文件添加MD5校验,然后每次运行程序师通过该文件自身做一次MD5的校验,如果程序有一点的“风吹草动”,那么校验值一定同之前存储的校验值是不一样的,从而可以判断文件是否被修改过,以此可以有效验证源程序是否被破解者恶意篡改。