Thursday 1 December 2011

Prerequisites

1: encrypts “.text” section
2: you must have enough free space at the end of the section for the stub
3: there may not be transparency in the RVA (like – raw size = 1000, virutal size = 2000) for that section, or it may malfunction
4: *edited* how did i miss that, there must be no relocations for “.text” section
He have also mentioned that “at first i was going for a more complex one, but seeing that there were some problems which easily took me a whole day to solve (damn RVA’s!) and as the source size became larger, i decided to go with this. After all, this is just an example. It is heavily commented, though unnecessary at most.
Im not doing a tutorial (and im not fond of them), because it is indeed easy, and if you dont understand this code, im sorry, but then you need to learn about PE and/or go back and continue with C/C++…”
If you do not know much about executable file format, you might want to take a look at Peering Inside the PE: A Tour of the Win32 Portable Executable File Format by Matt Pietrek.  Another good source that mindlessdeath has updated me with was The PE File Format.

Source Code

Before you look at the source code below and try to execute them, you might want to read what the comments are about. You might also want to read on updates from the author himself in the thread that he has originally posted here.
001<pre>#pragma comment(linker, "/OPT:NOREF") // this tells the linker to keep the machine code of unreferenced source code
002#pragma optimize("", off) // disable all optimizations in order for our stub to run smootly, though im not sure if it really helps, i just saw some guy doing it this way lolz :)
003 
004#include <windows.h> // familiar?
005#include <stdio.h> // i wonder what this might be, hmm...
006 
007// gets the first sections header offset
008#define SECHDROFFSET(a) ((LPVOID) ( (LPBYTE) a           + \
009                        ((PIMAGE_DOS_HEADER)a)->e_lfanew + \
010                        sizeof(IMAGE_NT_HEADERS)))
011 
012// those are the offsets to the
013#define OEP_o 21 // original entry point
014#define SEG_o 11 // virtual address of section
015#define BSZ_o 1  // block size, must be a multiple of 8
016#define  SZ_o 6  // section size, must be a multiple of the chosen block size
017                 // values in the stub
018 
019// a simple block xor
020// every byte in the given block is XOR'ed with its index
021void _xor_block(unsigned char *pblock, unsigned int blocksize)
022{
023    unsigned int i;
024 
025    for(i = 0; i < blocksize; i++)
026        pblock[i] ^= i;
027 
028    return;
029}
030 
031// just a wrapper around the above function
032int _xor_chunk(unsigned char* pchunk, unsigned long chunksize, unsigned int blocksize)
033{
034    if(chunksize % blocksize || blocksize % 8)
035        return -1;
036 
037    unsigned long index = 0;
038 
039    while(index < chunksize)
040    {
041        _xor_block(pchunk + index, blocksize);
042        index += blocksize;
043    }
044 
045    return 0;
046}
047 
048// this is our stub and the new entry point for the encrypted PE
049__declspec(naked) void __stdcall _stub(void)
050{
051    __asm
052    {
053        push 0xFEFEFEFE //blocksize
054        push 0xFDFDFDFD //chunksize
055        push 0xFCFCFCFC //pchunk
056 
057        call _xor_chunk //decrypt
058 
059        mov eax, 0x7FFFFFFF //oep
060        jmp eax //go go
061    }
062}
063 
064// a placeholder, used for stub size calculation
065__declspec(naked) int _end(void)
066{
067    __asm ret 8
068}
069 
070// so basicly the ASM code of the above 3 (w/o _end) functions will be added to the end of the ".text" section
071// after updating the proper values in the stub, ofc
072// then the PE header is updated along with the section header
073// and with the entry point at _stub's code its all done! wow that was easy oO
074 
075// GO GO POWER RANGERS!!!
076int main(void)
077{
078    // im not going to lecture you about those, if you are not familiar with these structures, you should go read about PE format...
079    PIMAGE_DOS_HEADER     pDosH;
080    PIMAGE_NT_HEADERS     pNtH;
081    PIMAGE_SECTION_HEADER pSecH;
082 
083    // variables
084    HANDLE hFile;
085 
086    DWORD  dwFileSize, dwSectionSize, dwStubSize,
087           dwVSize, dwOldProt, dwSpot, dwGap, bytes;
088 
089    LPBYTE FileBuffer, SectionBuffer;
090    CHAR FileName[MAX_PATH];
091 
092    // get the filename to encrypt
093    printf("File to encrypt: ");
094    scanf("%s", &FileName);
095 
096    // open it and get the size
097    hFile = CreateFile(FileName, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
098    dwFileSize = GetFileSize(hFile, 0);
099 
100    // load in memory
101    FileBuffer = (LPBYTE) malloc(dwFileSize);
102    ReadFile(hFile, FileBuffer, dwFileSize, &bytes, 0);
103 
104    pDosH = (PIMAGE_DOS_HEADER) FileBuffer;
105 
106    // check if it is valid PE, i would say that this is merely a proper check, for a proper one you would need to calculate all the RVA's and see if they are valid
107    if(pDosH->e_magic != IMAGE_DOS_SIGNATURE)
108        return -1;
109 
110    pNtH = (PIMAGE_NT_HEADERS) (FileBuffer + pDosH->e_lfanew);
111 
112    if(pNtH->Signature != IMAGE_NT_SIGNATURE)
113        return -2;
114 
115    pSecH = (PIMAGE_SECTION_HEADER) SECHDROFFSET(FileBuffer);
116 
117    while(memcmp(pSecH->Name, ".text", 5)) // get the ".text" section header
118        pSecH++;
119 
120    dwVSize          = pSecH->Misc.VirtualSize; // the virtual size of the section, later this will be used as chunksize in our stub, after proper alignment
121    dwSectionSize    = pSecH->SizeOfRawData; // speaks for itself
122    dwStubSize       = (DWORD) _end - (DWORD) _xor_block; // the stubsize, in bytes
123 
124    SectionBuffer = (LPBYTE) malloc(dwSectionSize); // allocate memory enough to hold our raw section data
125    memcpy(SectionBuffer, FileBuffer + pSecH->PointerToRawData, dwSectionSize); // ... copy the data
126 
127    _xor_chunk(SectionBuffer, dwSectionSize, 256); // aaand encrypt it! you can use different block sizes here - 8, 16, 32, 64, 128, 256, 512...
128    memset(SectionBuffer + pSecH->Misc.VirtualSize, 0, (dwSectionSize - pSecH->Misc.VirtualSize)); // fill with zeros after the end of actual data
129 
130    dwSpot = pSecH->Misc.VirtualSize; // this will be the offset (relative to the beginning of the section) where we will place our stub
131 
132    while(dwSpot % 16) // align it to 16 byte boundary
133        dwSpot++;
134 
135    dwSpot += 256; // this is in order to prevent the stub from corruption by overwriting its own code, since we will place it after the end of the section data
136    dwGap   = dwSpot - pSecH->Misc.VirtualSize; // the gap between our stub and the end of the data
137 
138    DWORD oep = pNtH->OptionalHeader.AddressOfEntryPoint + pNtH->OptionalHeader.ImageBase; // the original entry point, this is a linear address
139    DWORD seg = pSecH->VirtualAddress + pNtH->OptionalHeader.ImageBase; // the section address, you guessed right, this too is a linear one
140    DWORD bsz = 256; // you know what this is
141 
142    while(dwVSize % bsz) // we need to align it to block size
143        dwVSize++;
144 
145    VirtualProtect(_xor_block, dwStubSize, PAGE_EXECUTE_READWRITE, &dwOldProt); // to be able to update the stub...
146 
147    // and update it, blah, blah, blah...
148    memcpy((void *)((unsigned long) _stub + OEP_o), &oep, 4);
149    memcpy((void *)((unsigned long) _stub + SEG_o), &seg, 4);
150    memcpy((void *)((unsigned long) _stub + BSZ_o), &bsz, 4);
151    memcpy((void *)((unsigned long) _stub +  SZ_o), &dwVSize, 4);
152 
153    memcpy(SectionBuffer + dwSpot, _xor_block, dwStubSize); // place the damn thing already!
154 
155    pSecH->Characteristics                        = 0xE0000060; // R/W/E, executable code, initialized data. although my experience shows that you are just fine with R/W...
156    pSecH->Misc.VirtualSize                      += dwStubSize + dwGap; // update the virtual size of the section
157    pNtH->OptionalHeader.AddressOfEntryPoint  = pSecH->VirtualAddress + dwSpot + ( (DWORD)_stub - (DWORD)_xor_block ) ;
158 
159    // and finally update the file
160    SetFilePointer(hFile, pSecH->PointerToRawData, 0, FILE_BEGIN);               //new section data
161    WriteFile(hFile, SectionBuffer, dwSectionSize, &bytes, 0);
162 
163    SetFilePointer(hFile, pDosH->e_lfanew, 0, FILE_BEGIN);                       //new PE header
164    WriteFile(hFile, pNtH, sizeof(IMAGE_NT_HEADERS), &bytes, 0);
165 
166    SetFilePointer(hFile, ((DWORD) pSecH - (DWORD) FileBuffer), 0, FILE_BEGIN); //new section header
167    WriteFile(hFile, pSecH, sizeof(IMAGE_SECTION_HEADER), &bytes, 0);
168 
169    // some good habits :)
170    CloseHandle(hFile);
171 
172    free(FileBuffer);
173    free(SectionBuffer);
174 
175    return 0;
176}
177 
178// bye, bye, EOF

0 comments:

Post a Comment

CEX.io