|
|||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Want a new Job?
Chapters
Services
Feature Zones
|
PrefaceThis article was written to provide the better understanding to people who do not have any experience in this field. I am not going to describe about PE structure, so I think it was explained enough in Matt Pietrek’s articles in MSDN. I strongly recommend to read his article before continuing to read this article if you don’t have any previous experience with PE structure. You could find useful the relevant reference link at the end of article. This article will appear in three parts:
This article contains yoda’s protector source from version 1.01 to 1.03. It is first time people can look at its source after six month of appearance on Web. It is based on [6] yoda’s Crypter assembly source by Danilo Bzdok and compression source from [7] UPX library by Markus F.X.J. Oberhumer & László Molnár and [9] aPLib compression library by Joergen Ibsen. Thus, we should appreciate them for helping me to create this tool. I also should be grateful to people for trying and testing it on different Windows versions around the world. I think this article will be a small present to all people who involve and assist to make yoda’s protector. Contents
IntroductionThe Portable Executable format is standard format under Microsoft Windows NT ® operating system. It contains information for code, data, resource, dynamic link libraries importation. It is modifiable by using recent powerful debuggers such as OllyDbg or SoftICE with a little knowledge about assembly language. It causes to waste the time of software development companies to obtain money for their productions. Therefore, they are led to purchase the tool like EXE protector to prevent from illegal copy. My idea is what will happen if every person has owner EXE protector. Cracker person will face different EXE protector with different methods. Thus, I think each of us can have their own EXE protector. A short aspect about PE StructureThe Portable Executable includes information for the MS-DOS, the Windows NT, and Sections. This information is provided for Windows Operating System to allocate memory, import dynamic link libraries, and perform code. Table 1
You can find more about PE file format in [1] “Microsoft Portable Executable and Common Object File Format Specification”. Matt Pietrek clarifies it enough in [2] “Peering Inside the PE: A Tour of the Win32 Portable Executable File Format”, and [3a/b] “An In-Depth Look into the Win32 Portable Executable File Format”. Moreover, PEView [4] by Wayne J. Radburn will help you to find all aspects about PE file format. Figure 1
All the required data structures for PE file format are included in winnt.h file inside your Visual C++. Open PE filesWe have to load PE file format to memory for working with its information. Some Windows API function will help us do it very easy: I make a class to work with PE files. It helps me to open files and put DOS header, NT headers, Section Headers, and Sections into separate places in memory and then rebuild all as new PE files. class PEStructure
{
private:
DWORD ReservedHeaderSize;
DWORD ReservedHeaderRO;
public:
DWORD dwRO_first_section;
IMAGE_DOS_HEADER image_dos_header;
char *reservedheader;
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 Free();
};
void PEStructure::OpenFileName(char* FileName)
{
hFile=CreateFile(FileName,
GENERIC_READ,
FILE_SHARE_WRITE | FILE_SHARE_READ,
NULL,OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,NULL);
if(hFile==INVALID_HANDLE_VALUE)
{
ShowErr(FileErr);
return;
}
dwFsize=GetFileSize(hFile,0);
if(dwFsize == 0)
{
CloseHandle(hFile);
ShowErr(FsizeErr);
return;
}
dwOutPutSize=
dwFsize+IT_SIZE+DEPACKER_CODE_SIZE+ALIGN_CORRECTION;
pMem=(char*)GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT,
dwOutPutSize);
if(pMem == NULL)
{
CloseHandle(hFile);
ShowErr(MemErr);
return;
}
ReadFile(hFile,pMem,dwFsize,&dwBytesRead,NULL);
CloseHandle(hFile);
CopyMemory(&image_dos_header,pMem,sizeof(IMAGE_DOS_HEADER));
ReservedHeaderRO=sizeof(IMAGE_DOS_HEADER);
ReservedHeaderSize=
image_dos_header.e_lfanew-sizeof(IMAGE_DOS_HEADER);
reservedheader=new TCHAR[ReservedHeaderSize];
CopyMemory(&image_nt_headers,
pMem+image_dos_header.e_lfanew,
sizeof(IMAGE_NT_HEADERS));
dwRO_first_section =
image_dos_header.e_lfanew + sizeof(IMAGE_NT_HEADERS);
UpdateHeadersSections(TRUE);
}
Verify if PE file is validIt is important to verify whether the file is a Win32 Portable Executable file by checking if(PEfile.image_dos_header.e_magic!='ZM')
{
GlobalFree(pMem);
CloseHandle(hFile);
if(MakeBackup) DeleteFile(szFnameBackup);
ShowErr(PEErr);
return;
}
if(PEfile.image_nt_headers.Signature!='EP')
{
GlobalFree(pMem);
CloseHandle(hFile);
if(MakeBackup) DeleteFile(szFnameBackup);
ShowErr(PEErr);
return;
}
Make Extra SectionThere is a trick inside yoda’s Protector to create extra section for protection and unpacking purposes. Visual C++ aids it to make this extra part without using of assembler compiler and linker. If you look at DWORD GetFunctionRVA(void* FuncName)
{
void *_tempFuncName=FuncName;
char *ptempFuncName=PCHAR(_tempFuncName);
DWORD _jmpdwRVA,dwRVA;
CopyMemory(&_jmpdwRVA,ptempFuncName+1,4);
dwRVA=DWORD(ptempFuncName)+_jmpdwRVA+5;
return(dwRVA);
}
DWORD GetFunctionSize(void* FuncName)
{
DWORD dwRVA=GetFunctionRVA(FuncName);
char* pFuncBody=PCHAR(dwRVA);
UCHAR _temp;
bool notEnd=TRUE;
char *DepackerCodeEnd=new TCHAR[10];
DWORD l=0;
do
{
CopyMemory(&_temp,pFuncBody+l,1);
if(_temp==0xC3)
{
CopyMemory(DepackerCodeEnd,pFuncBody+l+0x01,10);
DepackerCodeEnd[9]=0x00;
if(strcmp(DepackerCodeEnd,"ETGXZKATZ")==0)
{
notEnd=FALSE;
}
}
l++;
}while(notEnd);
return(l);
}
char* CopyFunction(void* FuncName)
{
DWORD dwRVA=GetFunctionRVA(FuncName);
DWORD dwSize=GetFunctionSize(FuncName);
char* pFuncBody=PCHAR(dwRVA);
char* filebuff=new TCHAR[dwSize+1];
CopyMemory(filebuff,pFuncBody,dwSize);
return(filebuff);
}
This method can illuminate in the following code: char *pDepackerCode;
DWORD DEPACKER_CODE_SIZE;
…
DEPACKER_CODE_SIZE=GetFunctionSize(PE_LOADER_CODE);
pDepackerCode=new TCHAR[DEPACKER_CODE_SIZE];
pDepackerCode=CopyFunction(PE_LOADER_CODE);
…
void PE_LOADER_CODE()
{
_asm
{
//----------------------------------------------------------
//-------------- START OF THE PE LOADER CODE ---------------
DepackerCode:
…
…
…
DepackerCodeEND:
RET
//"ETGXZKATZ" <<-- KEY WORD
INC EBP //'E'
PUSH ESP //'T'
INC EDI //'G'
POP EAX //'X'
POP EDX //'Z'
DEC EBX //'K'
INC ECX //'A'
PUSH ESP //'T'
POP EDX //'Z'
}
}
Pack and Crypt SectionsUPX compressor source [7] code is an alternative choice to pack sections of PE files. I use [8] LZO data compression library by Markus F.X.J. Oberhumer to pack code and data section. Polymorphism encryption and decryption method [6] by Danilo Bzdok is simple and good enough to crypt PE section by some modification in C++ language as you see inside This protector separates Sections in different allocation parts of memory. Afterwards it packs and crypts part of sections by //------ ENCRYPT THE SECTIONS -----
// generate PER
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,
SecDecryptBuff,
SEC_PER_SIZE);
// encrypt !
CompressPE(pMem);
CryptPE(pMem);
RemoveSectionNames(pMem);
newsection.Misc.VirtualSize=DepackCodeVirtualSize+0x2000;
PEfile.image_section_header[
PEfile.image_nt_headers.FileHeader.NumberOfSections-1]
.Misc.VirtualSize = newsection.Misc.VirtualSize;
PEfile.UpdateHeadersSections(FALSE);
//---------------------------------
LZO data compression library [8] has compressor source in C++ and decompressor source in both C++ and assembly. Hence, we have all sources to pack in high level language and unpack in low level language. I used It is important to pay attention to void PEStructure::UpdateHeadersSections(BOOL bSaveAndValidate)
{
DWORD i;
if(bSaveAndValidate)//TRUE = data is being retrieved
{
DWORD SectionNum = PEfile.image_nt_headers
.FileHeader.NumberOfSections;
CopyMemory(&image_dos_header,pMem,sizeof(IMAGE_DOS_HEADER));
ReservedHeaderSize = image_dos_header.e_lfanew –
sizeof(IMAGE_DOS_HEADER);
if((ReservedHeaderSize&0x80000000)==0x00000000)
{
CopyMemory(reservedheader,
pMem+ReservedHeaderRO,
ReservedHeaderSize);
}
CopyMemory(&image_nt_headers,
pMem+image_dos_header.e_lfanew,
sizeof(IMAGE_NT_HEADERS));
dwRO_first_section = image_dos_header.e_lfanew +
sizeof(IMAGE_NT_HEADERS);
CopyMemory(&image_section_header,
pMem+dwRO_first_section,
SectionNum*sizeof(IMAGE_SECTION_HEADER));
for(i=0;i< SectionNum;i++)
{
image_section[i] = (char*)GlobalAlloc(
GMEM_FIXED | GMEM_ZEROINIT,
PEAlign(image_section_header[i].SizeOfRawData,
PEfile.image_nt_headers.OptionalHeader.FileAlignment));
CopyMemory(image_section[i],
pMem + image_section_header[i].PointerToRawData,
image_section_header[i].SizeOfRawData);
}
}
else//FALSE = data is being initialized
{
DWORD SectionNum = PEfile.image_nt_headers
.FileHeader.NumberOfSections;
CopyMemory(pMem,
&image_dos_header,sizeof(IMAGE_DOS_HEADER));
ReservedHeaderSize=image_dos_header.e_lfanew –
sizeof(IMAGE_DOS_HEADER);
if((ReservedHeaderSize&0x80000000)==0x00000000)
{
CopyMemory(pMem + ReservedHeaderRO,
reservedheader,
ReservedHeaderSize);
}
CopyMemory(pMem+image_dos_header.e_lfanew,
&image_nt_headers,
sizeof(IMAGE_NT_HEADERS));
dwRO_first_section = image_dos_header.e_lfanew +
sizeof(IMAGE_NT_HEADERS);
CopyMemory(pMem+dwRO_first_section,
&image_section_header,
SectionNum*sizeof(IMAGE_SECTION_HEADER));
for(i=0;i< SectionNum;i++)
{
CopyMemory(pMem+image_section_header[i].PointerToRawData,
image_section[i],
image_section_header[i].SizeOfRawData);
}
}
}
Built Import Table DirectoryPE unpack section need to import two essential API functions to load dynamically all other API functions in Run-time load. Table 2
Reload Import Table and API RedirectionIt is important to protect import table directory from reverse engineering process. Danilo Bzdok has used technical methods to destroy import thunk data and crypt import information in [6] yoda’s Crypter. This part is retrievable again by loader code section. I applied his methods in my PE Protector with bringing some part of code to C and remind other part in assembly. Anti-debug methodsPE Protector should able to detect if program debugs and prevent from debugging. OllyDbg and SoftICE are two important debuggers that can bypass many tricks which cause to halt debuggers. However, I should introduce some simple methods to detect debuggers. I know all of these methods do not have any effect in recently added plug-ins for the mentioned debuggers.
Eliminate unnecessary dataSome time you need to clean all unnecessary data such as debug information, relocation section and make small DOS header and remove MS-DOS stub Program. You should reserve these items in your PE Protector with some considerations. For instance, Relocation section do not have any effect in running EXE files but it plays an important role in OLE-Active Controls and Dynamic Link Libraries. Sample codeThe project compiles with Visual C++ .NET 2003 and doesn’t require any spare tools. It works under all Windows version except Windows NT 4.0 and Windows 95. ConclusionThis article and its source could be an introduction to PE protector tools and demonstrating how these tools works. I hope it covers the absence of this kind of interesting topics and tools in open source area. References[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. [3a] "An In-Depth Look into the Win32 Portable Executable File Format", part 1, Matt Pietrek, MSDN Magazine, February 2002. [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. | ||||||||||||||||||||||||||||||||||||||||||||||||||