建立自己的PE Protector (部分1): Your first EXE Protector
By Ashkbiz Danehkar
原文:http://www.codeproject.com/cpp/peprotector1.asp
译者:arhat
时间:2006年4月18日
关键词:PE Protector 调试器 反汇编器 OEP SEH 导入表
本文主要是描述在Visual C++编译器下,开发属于自己的PE Protector。
这部分介绍用Visual C++ Win32 Programming开发一个强大的EXE protector和packer。
这部分介绍除EXE文件之外的其它类型的PE文件。会介绍一些你应该知道的、保护OCX和DLL的小技巧。
这部分将演示怎样用Microsoft 的Cryptography API函数加密/解密PE区段信息。
- Introduction
- A short aspect about PE Structure
- Open PE files
- Verify if PE file is valid
- Make Extra Section
- Pack and Crypt Sections
- Built Import Table Directory
- Reload Import Table and API
Redirection
- Anti-debug methods
- Eliminate unnecessary data
- Sample code
- Conclusion
- References
PE包括MS-DOS,Windows
NT,和区段信息。Windows操作系统通过这些信息来分配内存,导入DLL,执行代码。
此外,Wayne J. Radburn的PEView [4] 将帮助你看清楚PE文件格式的方方面面。
我为了和PE文件共事,专门编写了一个类。它帮助我打开文件,把DOS头,NT头,区段头,和区段放到内存中分开的地方,然后把它们重建成新的PE文件。
IMAGE_DOS_HEADER image_dos_header;
IMAGE_NT_HEADERS image_nt_headers;
IMAGE_SECTION_HEADER
image_section_header[MAX_SECTION_NUM];
char
*image_section[MAX_SECTION_NUM];
void
OpenFileName(char* FileName);
void
UpdateHeaders(BOOL bSaveAndValidate);
void
UpdateHeadersSections(BOOL bSaveAndValidate);
void
PEStructure::OpenFileName(char* FileName)
FILE_SHARE_WRITE | FILE_SHARE_READ,
if(hFile==INVALID_HANDLE_VALUE)
dwFsize+IT_SIZE+DEPACKER_CODE_SIZE+ALIGN_CORRECTION;
pMem=(char*)GlobalAlloc(GMEM_FIXED
| GMEM_ZEROINIT,
ReadFile(hFile,pMem,dwFsize,&dwBytesRead,NULL);
CopyMemory(&image_dos_header,pMem,sizeof(IMAGE_DOS_HEADER));
ReservedHeaderRO=sizeof(IMAGE_DOS_HEADER);
image_dos_header.e_lfanew-sizeof(IMAGE_DOS_HEADER);
reservedheader=new
TCHAR[ReservedHeaderSize];
pMem+image_dos_header.e_lfanew,
image_dos_header.e_lfanew + sizeof(IMAGE_NT_HEADERS);
为了防止不可预知的错误,需要检查image_nt_header中image_dos_header和Signature的e_magic来检验文件是否是Win32 PE文件。
if(PEfile.image_dos_header.e_magic!='ZM')
if(MakeBackup)
DeleteFile(szFnameBackup);
if(PEfile.image_nt_headers.Signature!='EP')
if(MakeBackup)
DeleteFile(szFnameBackup);
DWORD GetFunctionRVA(void* FuncName)
char
*ptempFuncName=PCHAR(_tempFuncName);
CopyMemory(&_jmpdwRVA,ptempFuncName+1,4);
dwRVA=DWORD(ptempFuncName)+_jmpdwRVA+5;
DWORD GetFunctionSize(void* FuncName)
DWORD dwRVA=GetFunctionRVA(FuncName);
char
*DepackerCodeEnd=new TCHAR[10];
CopyMemory(&_temp,pFuncBody+l,1);
CopyMemory(DepackerCodeEnd,pFuncBody+l+0x01,10);
if(strcmp(DepackerCodeEnd,"ETGXZKATZ")==0)
char*
CopyFunction(void* FuncName)
DWORD
dwRVA=GetFunctionRVA(FuncName);
DWORD
dwSize=GetFunctionSize(FuncName);
char*
filebuff=new TCHAR[dwSize+1];
CopyMemory(filebuff,pFuncBody,dwSize);
DEPACKER_CODE_SIZE=GetFunctionSize(PE_LOADER_CODE);
pDepackerCode=new TCHAR[DEPACKER_CODE_SIZE];
pDepackerCode=CopyFunction(PE_LOADER_CODE);
//----------------------------------------------------------
//-------------- START OF THE PE LOADER CODE ---------------
这个protector把区段分别放在不同内存单元。然后,通过CompressPE() 和CryptPE()打包并加密区段。
//------ ENCRYPT THE SECTIONS -----
PEfile.UpdateHeadersSections(TRUE);
SecEncryptBuff=new TCHAR[SEC_PER_SIZE];
SecDecryptBuff=new TCHAR[SEC_PER_SIZE];
MakePER(SecEncryptBuff,SecDecryptBuff,SEC_PER_SIZE);
CopyMemory(pDepackerCode+dwRO_SEC_DECRYPT,
newsection.Misc.VirtualSize=DepackCodeVirtualSize+0x2000;
PEfile.image_nt_headers.FileHeader.NumberOfSections-1]
.Misc.VirtualSize
= newsection.Misc.VirtualSize;
PEfile.UpdateHeadersSections(FALSE);
//---------------------------------
void
PEStructure::UpdateHeadersSections(BOOL bSaveAndValidate)
if(bSaveAndValidate)//TRUE = data is being retrieved
DWORD
SectionNum = PEfile.image_nt_headers
CopyMemory(&image_dos_header,pMem,sizeof(IMAGE_DOS_HEADER));
ReservedHeaderSize
= image_dos_header.e_lfanew –
if((ReservedHeaderSize&0x80000000)==0x00000000)
pMem+image_dos_header.e_lfanew,
dwRO_first_section
= image_dos_header.e_lfanew +
CopyMemory(&image_section_header,
SectionNum*sizeof(IMAGE_SECTION_HEADER));
image_section[i] = (char*)GlobalAlloc(
PEAlign(image_section_header[i].SizeOfRawData,
PEfile.image_nt_headers.OptionalHeader.FileAlignment));
pMem + image_section_header[i].PointerToRawData,
image_section_header[i].SizeOfRawData);
else//FALSE
= data is being initialized
DWORD SectionNum
= PEfile.image_nt_headers
&image_dos_header,sizeof(IMAGE_DOS_HEADER));
ReservedHeaderSize=image_dos_header.e_lfanew –
if((ReservedHeaderSize&0x80000000)==0x00000000)
CopyMemory(pMem + ReservedHeaderRO,
CopyMemory(pMem+image_dos_header.e_lfanew,
dwRO_first_section
= image_dos_header.e_lfanew +
CopyMemory(pMem+dwRO_first_section,
SectionNum*sizeof(IMAGE_SECTION_HEADER));
CopyMemory(pMem+image_section_header[i].PointerToRawData,
image_section_header[i].SizeOfRawData);
重载Import Table和API Redirection
- IsDebuggerPresent Windows API: 无论如何,只要当前的进程运行在调试器的上下文中,IsDebuggerPresent() 总是返回非零值。
- SoftICE detection:它通过检查windows NT下的NTICE 驱动及windows 98下的SICE 驱动是否正在使用来发现SoftICE调试器。
3.
if(CreateFile( "\\\\.\\NTICE",
GENERIC_READ | GENERIC_WRITE,
4.
FILE_SHARE_READ
| FILE_SHARE_WRITE,
5.
NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
6.
NULL)!=INVALID_HANDLE_VALUE)
8.
There is
10.
if(CreateFile( "\\\\.\\SICE",
GENERIC_READ | GENERIC_WRITE,
11.
FILE_SHARE_READ
| FILE_SHARE_WRITE,
12.
NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
13.
NULL)!=INVALID_HANDLE_VALUE)
15.
There is SoftICE98 on
your system;
18.
void GetFileNameFromPath(char*
szSource)
20.
char *szTemp=strrchr(szSource,'\\');
24.
DWORD
l=DWORD(strlen(szTemp))+1;
25.
CopyMemory(szSource,szTemp,l);
31.
char lpszSystemInfo[MAX_PATH];
34.
DWORD PID_parent,PID_explorer;
36.
PROCESSENTRY32 pe32 = {0};
37.
pe32.dwSize
= sizeof(PROCESSENTRY32);//0x128;
38.
PID_child=GetCurrentProcessId();//getpid();
39.
hSnapshot
= CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
40.
if (Process32First(hSnapshot, &pe32))
42.
while (Process32Next(hSnapshot, &pe32))
44.
GetFileNameFromPath(pe32.szExeFile);
45.
CharUpperBuff(pe32.szExeFile,strlen(pe32.szExeFile));
46.
if(strcmp(pe32.szExeFile,"EXPLORER.EXE")==0)
48.
PID_explorer=pe32.th32ProcessID;
50.
if(pe32.th32ProcessID==PID_child)
52.
PID_parent=pe32.th32ParentProcessID;
56.
if(PID_parent!=PID_explorer)
58.
hh_parnet=
OpenProcess(PROCESS_ALL_ACCESS,
60.
TerminateProcess(hh_parnet,
0);
65.
me32.dwSize
= sizeof(MODULEENTRY32);
66.
hSnapshot
= CreateToolhelp32Snapshot(TH32CS_SNAPMODULE,
68.
if (Module32First(hSnapshot, &me32))
72.
if(PID_explorer==me32.th32ProcessID)
74.
GetWindowsDirectory(lpszSystemInfo,
76.
strcat(lpszSystemInfo,"\\");
77.
strcat(lpszSystemInfo,"EXPLORER.EXE");
78.
CharUpperBuff(me32.szExePath,
80.
if(strncmp(me32.szExePath,
84.
GetFileNameFromPath(me32.szExePath);
89.
OpenProcess(PROCESS_ALL_ACCESS,
91.
TerminateProcess(hh_parnet,
0);
95.
}while (Module32Next(hSnapshot, &me32));
此外,我推荐你用Mark Russinovich的[11] Process Explorer探究你系统中正在运行的进程。它可以帮助你更好的理解它们。
这个项目可以在Visual C++ .NET 2003下编译,而不需要任何其它的工具。除了Windows NT 4.0和Windows 95外,它可以在任何版本的Windows系统下工作。
本文及它的源码演示了PE protector工具的工作原理,可以作为它们的入门读物。我希望它覆盖了开源领域有关此主题所缺少的内容。
[1] "Microsoft Portable Executable and Common Object File
Format Specification", Microsoft Corporation, Revision
6.0, February 1999.
[2]
" Peering Inside the PE: A Tour of the Win32 Portable
Executable File Format", Matt Pietrek, MSDN Library,
March 1994.
[
[3b] "An In-Depth Look into the Win32 Portable Executable File
Format", part 2, Matt Pietrek, MSDN Magazine, March 2002.
[4]
PEview Version 0.67, Wayne J. Radburn.
[5] MSDN Library, Microsoft Corporation, April 2003.
[6] yoda’s
Crypter, Danilo Bzdok.
[7] UPX, the Ultimate Packer for eXecutables, Markus
F.X.J. Oberhumer & László Molnár.
[8] LZO real-time data compression library, Markus
F.X.J. Oberhumer.
[9] aPLib compression library, Joergen Ibsen.
[10] "Windows NT (2000) Native API reference",
Gary Nebbet.
[11] Process Explorer, Mark Russinovich.