基於crc32實現的記憶體的程式碼校驗

epluguo發表於2013-07-10



原理:

a,crc32函式的實現

b,記憶體校驗:顧名思義,執行在記憶體程式碼通過crc32得到一個值,當第二次執行可執行檔案的時候,可以把第一次儲存下來的值和第二次執行的結果相比較,從而根據比較結果判斷時候記憶體資料吧被修改。

 

1,crc32演算法的實現部分:

DWORD CRC32(BYTE* ptr,DWORD Size)
{
  
       DWORDcrcTable[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值
       DWORDcrcTmp2= 0xFFFFFFFF;
       while(Size--)
       {
              crcTmp2 = ((crcTmp2>>8) &0x00FFFFFF) ^ crcTable[ (crcTmp2^(*ptr)) & 0xFF ];
              ptr++;
       }
      
       return(crcTmp2^0xFFFFFFFF);
}


2,程式碼實現:

A,要保護的程式碼:

ProtectStart:   //要保護的程式碼的起始地址
       __asm
       {
              inc eax  //花指令
                dec eax
                push eax
                pop eax
       }
start:
          HMODULE hMod = GetModuleHandle(NULL);//同樣是花指令
          HMODULE hUser32 =LoadLibrary("user32.dll");
ProtectEnd:               //要保護程式碼的終結地址
          DWORD dwThreadId = 0;
 
          STBINGLEPARAM stParam = {0};
      stParam.hEvent = CreateEvent(NULL,FALSE,FALSE,"bingle");
         
          DWORD dwAddr = 0;  //一個快取空間
          __asm mov eax,offset ProtectStart  //計算程式碼的起始地址
          __asm mov dwAddr,eax
          stParam.dwStart = dwAddr; //儲存在我們自己定義的結構體裡
 
      __asm mov eax,offset ProtectEnd //計算保護程式碼的結束地址,同樣儲存在自己定義的                   結構體裡。
          __asm mov dwAddr,eax
          stParam.dwEnd = dwAddr;
         
          printf("開始了\n");
           CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)bingleProc,(LPVOID)&stParam,0,&dwThreadId);


B,建立了一個執行緒,用來計算校驗值。並且將執行緒的建立放在迴圈中,這樣保證在程式執行的過程中,會不斷的監視記憶體的資料是否改變。

CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)bingleProc,(LPVOID)&stParam,0,&dwThreadId);
 
          DWORD dwRet = 0;
          dwRet =WaitForSingleObject(stParam.hEvent,INFINITE);
          while(dwRet == WAIT_OBJECT_0)
          {
              Sleep(5000);
          CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)bingleProc,(LPVOID)&stParam,0,&dwThreadId);
                dwRet = WaitForSingleObject(stParam.hEvent,INFINITE);
          }


上邊的程式碼是建立執行緒的,根據建立執行緒的返回值來作為迴圈條件。其中stParam是我自定義結構體生成的一個物件。這個物件儲存在堆疊中。該結構體的定義如下:

#pragma pack(1)
typedef struct __STBINGLEPARAM
{
 HANDLE hEvent;  //用於同步的一個訊號量
 DWORD dwStart;  //要校驗的程式碼的起始地址
 DWORD dwEnd;   //要校驗的程式碼的終結地址
}STBINGLEPARAM,*PBINGLEPARAM;
#pragma pack()


 

接下來是是執行緒函式了。

STBINGLEPARAM *stParam = (STBINGLEPARAM*)lpParameter;
  
       DWORDdwCodeSize = stParam->dwEnd - stParam->dwStart;
       BYTE*pbyteBuf = NULL;
       pbyteBuf= (BYTE *)stParam->dwStart;
      
       DWORDdwOldProtect = 0;
       VirtualProtect((LPVOID)stParam->dwStart,4*1024,PAGE_EXECUTE_READWRITE,&dwOldProtect);
       if(CRC32(pbyteBuf,dwCodeSize)!= 0xa0eb5866)
       {
          MessageBox(NULL,"bingle","程式碼被修改了",NULL);
          printf("程式碼被修改了\n");
 
          SetEvent(stParam->hEvent);
          ExitProcess(0);
       }
 
 
       SetEvent(stParam->hEvent);//執行完上邊的程式碼開始傳信,讓主執行緒繼續執行



在這裡要說的是中的0xa0eb5866,這個是我在od中讓程式碼執行起來找到的。

在建立的執行緒函式體中找到0x40100a,ctrl+g來到這個地址,F2下斷點,然後就來到執行緒函式了。

一直往下找,找到比較程式碼cmpeax,0xa0eb5866這一句,把這個地址儲存下來,就是我們要校驗的地址。可以直接用在程式碼中。。

 

3,測試:

讓debug版本的程式執行起來,ce附加程式。

點選Memory View,定位到我們要保護的程式碼段。



找到我們要保護的程式碼,然後用ce修改一下記憶體的數值試試,我想修改0x40127a。只要這個記憶體地址在我們要保護的程式碼中就可以。

哈哈哈,還不錯吧。記憶體校驗不難吧。

轉自:http://bbs.pediy.com/showthread.php?t=140471

相關文章