exeinfo, PEiD, Detect It Easy와 같은 툴을 사용하면, 시그너쳐를 이용해 어떠한 패커로 패킹되었는지 쉽게 알 수 있습니다. 그러나 알려지지 않은 패킹툴이나, Fake Signature의 경우 탐지가 어렵거나, 오탐이 발생합니다. 본 글에서는 시그너쳐를 이용하지 않고 패킹 여부를 확인하는 방법에 대해 다룹니다. 본 글에서 소개할 방법은 엔트로피를 이용하는 방법입니다.
(담에 시간나면(ㅜㅜ) 특징 백터를 이용하는 방법도 포스팅 하겠습니다..)

정보이론에서 엔트로피라 하면은, 데이터가 얼마나 무질서한지 나타내는 지표입니다. 문제는 이걸 어떻게 계산할지 인데, 일반적으로 C. Shannon이 제안한 방법을 사용합니다.

-----

이 식의 계산 결과, 즉 H(x)를 Shannon’s Entropy하고, 보통, PE파일 전체의 엔트로피가 7이상일 때 패킹된 파일로 판단하거나, EP섹션의 엔트로피가 6.85 이상일 때 패킹된 파일로 판단합니다.

아래는 이 값을 이용하여 패킹 여부를 판단하는 예제입니다.

//Example1 : Calc Entropy(PE File)
#include<stdio.h>
#include<windows.h>
#include<math.h>

#define logB(x, base) log(x)/log(base)

int main(int argc, char* argv[])
{
  BYTE *rawData;
  DWORD map[256] = {0,},Len;
  double p[256], sum = 0;
  FILE *fp;
  if(argc == 2)
  {
    fp = fopen(argv[1], "rb");
    fseek(fp, 0, SEEK_END);
    Len = ftell(fp);
    rawData = (BYTE *)calloc(1,Len);
    fseek(fp, 0, SEEK_SET);
    fread(rawData, 1, Len, fp);
    fclose(fp);
    
    for(int i=0;i<Len;i++)
      map[rawData[i]]++;
    for(int i=0;i<256;i++)
      p[i] = (double)map[i]/(double)Len;
    for(int i=0;i<256;i++)
      sum += (p[i] * logB(p[i],2.0));
    sum *= -1;
    if(sum >= 7.0)  
      printf("Packed! : %lf\n",sum);
    else
      printf("Not Packed! : %lf\n",sum);
    
    free(rawData);
    rawData = NULL;
  }
  return 0;

}
//Example2 : Calc Entropy(EP Section)
#include<stdio.h>
#include<windows.h>
#include<math.h>

#define logB(x, base) log(x)/log(base)

int main(int argc, char* argv[])
{
  BYTE *rawData;
  DWORD map[256] = {0,}, Len, EpRAW = 0,EpSize = 0;
  double p[256], sum = 0;
  FILE *fp;
  
  PIMAGE_DOS_HEADER       pDosHeader;
  PIMAGE_NT_HEADERS       pNtHeader;
  PIMAGE_SECTION_HEADER     pSecHeader;
  
  
  if(argc == 2)
  {
    fp = fopen(argv[1], "rb");
    fseek(fp, 0, SEEK_END);
    Len = ftell(fp);
    rawData = (BYTE *)calloc(1,Len);
    fseek(fp, 0, SEEK_SET);
    fread(rawData, 1, Len, fp);
    fclose(fp);
    
    
    pDosHeader = (PIMAGE_DOS_HEADER)rawData;
    pNtHeader = (PIMAGE_NT_HEADERS)(rawData + pDosHeader->e_lfanew);
    pSecHeader = (PIMAGE_SECTION_HEADER)(rawData + pDosHeader->e_lfanew + sizeof(IMAGE_NT_HEADERS));
    
    
    for (int i = 0; i < pNtHeader->FileHeader.NumberOfSections; i++)
	{
    	if (pSecHeader[i].VirtualAddress < pNtHeader->OptionalHeader.AddressOfEntryPoint && 
			pSecHeader[i].PointerToRawData + pSecHeader[i].SizeOfRawData)
		{
      		EpRAW = pSecHeader[i].PointerToRawData;
      		EpSize = pSecHeader[i].SizeOfRawData;
    	}
  	}
    
    for(int i=EpRAW;i<EpRAW+EpSize;i++)
      map[rawData[i]]++;
    for(int i=0;i<256;i++)
      p[i] = (double)map[i]/(double)(EpSize);
    for(int i=0;i<256;i++)
      sum += (p[i] * logB(p[i],2.0));
    sum *= -1;
    if(sum >= 6.85)  
      printf("Packed! : %lf\n",sum);
    else
      printf("Not Packed! : %lf\n",sum);
    
    free(rawData);
    rawData = NULL;
  }
  return 0;

}

물론 이 방법에도 충분히 오탐이 발생할수 있습니다.(특히 PE파일의 전체 엔트로피를 계산해서 7 이상일때 패킹된 파일이라고 하는것은, 무리가 있을수 있습니다.)

--

그러나, 시그너쳐 없이 패킹 여부를 판단할 수 있다는점에서 의의가 있습니다.