*** IMPORTANT *** Disclaimer: Use this program at your own risc, should the world blow up when you run this program it's your own responsibility! *** IMPORTANT TOO *** The source is included in seperate files may be slightly different to the source in this file. The source in this file is more vigurously commented How to use this program: I'm one lazy mofo so there is no user interface in this program - besides it would be outta thread with the purpose of this program, I'll explain that later. So what you do, is you take your .exe file, put it in the directory with execrypt.com renames it to input.exe - then you run execrypt.com and the output will be stored as output.exe. Simple and annoying - I know! Also remember this was'nt meant for actual use, it's not well suited for anything but enlightenment and as the very basic for building your own execrypter. Why did I write this program: I wrote the comcrypter two month ago and released the sourcecode. Despite that I've made *NO* effort what so ever to spread it I've been getting a few mails about it, and a few comments on irc/phone. Some of the comments was requests to make a .exe fileencrypter. So I went ahead and did it. Again the purpose was a easy-to-read sourcecode with no intention of actual use. Hence no user- interface and a poor encryption routine. Due to the more advanced struture of a exe-encrypter and my inabillities in pascal (I'm no programmer so my asm sucks too) I chose to do this in pure assembler opposed to the com-encrypter. Tech stuff: So far so good, time for some technical stuff - if you already know how to make exe-encryptors, don't read this it would be a waste of time. First I wanted to deciede on an decryption/encryption scheme. To keep it simple and most understandable I choose the simplest scheme possible - the scheme I used for comcrypt too. This scheme is remarkably similar to the one I used when passing around love notes in a distant past. The scheme is to exchange each letter with the next in the alphabet - the computer equivalence is ofcouse to add 1 to each your encrypting. Decrypting is then just subtracting one from each byte. Very simple I think you'll have to agree on that! So having decieded on that, the next we need is to figure out how an exe-file is build and how it's loaded. Any exe-file is seperated into two parts: A header and a part where the code is stored. This part is called the image of the exefile. The header among other information contains where in the image the execution of the program should start. So what we want to do is: 1: encrypt the image of the exefile 2: append a decryption routine to the image 3: modify the header so our decryption routine is run first 4: jump to the place the execution would've normally started Unfortunately it get's a little more complicated than that - first we need to examine the header a little more To understand the header of a exe-file two data-types must be understood. Like a word is two bytes, a paragraph is 16 bytes and a page is 512 bytes. The first part of the header, the part we're interested in looks as follows: Offset Description 00h 00d ID word, either 'MZ' or 'ZM' 02h 02d Number of bytes in the last (512 byte) page in the image 04h 04d Total number of 512 byte pages in the file 06h 06d Number of entries in the segment table 08h 08d Size of the header in (16 byte) paragraphs 0Ah 10d Minimum memory required in paragraphs 0Ch 12d Maximum memory requested in paragraphs 0Eh 14d Initial offset in paragraphs to stack segment from header 10h 16d Initial offset in bytes of stack pointer from stack segment 12h 18d Negative checksum (ignored) 14h 20d Initial offset in bytes of instruction pointer from code segment 16h 22d Initial offset in paragraphs of code segment from header 18h 24d Offset of relocation table from start of file 1Ah 26d Overlay number (ignored) The word at offset 4 is the filesize in pages, rounded up. E.g. a 517 bytes large exefile would have a 2 here. A 1025 would have a 3 here. The offset at 2 is'nt usually used - due to a bug in an early microsoft linker that made this word wrong - but if you're doing like you're supposed to, and we are. This should be: (filesize-headersize) mod 512. So the words at 2 and 4 is connected like this: (offset4-1)*512+offset2 = filesize. With an exeception, that is if offset2=0 then offset4*512=filesize. At offset A (10d) we find the minimum memory in paragraphs. We should be aware than when we add our decryption code this should be increased with the decryptioncode, in paragraphs, since our decryption code needs to be loaded into memory too. Offset 0Eh and 10h (14d and 16d) is important to us too. We need to make sure to get the stack far away from our decryption code, and we would also like the program to have the same stack once we return return to the program. Offsets 12h and 14h is very important to us too. This is where the code starts executing. So these needs to point to our code, and we also need to save these so when we're done decrypting we can jmp to the right place. Note this is written as a displacement from where the first segment of the code will be loaded. These are the relevant parts of the exe-header. This short description will hopefully be elaborated in the code. Moving on from the header to how the exefile is loaded. First the exeheader is read and checked for inconsistensies. Then memory is allocated. Then a PSP is created (Program Start Prefix) - this is 100h bytes large, e.g. 10 paragraphs loaded in each it's own segment. It can however be addressed just like the image - more about that just below. Then the image is loaded, on top of the PSP, paragraph for paragrah, each on top of each other all in their own segment. E.g. 1132:0000 are the same as 1131:0010 again the same as 1130:0020. and so forth. Then DS and ES is made to point to the PSP's start. So ds+10h is where our code is at. Then SP and SS is loaded correctly and finally a jmp to the entry point is made. Having covered the theory I'll now present parts of the code with as much explaining as possible. I will not cover the basic procedure, the file stuff or anything like that - just what is relevant to exe-encryption in general. The over all structure of the program is: 1:Find the file to encrypt 2:copy the header 3:copy and encrypt the image 4:calculate a new header 5:write the new header to the file 6:append the decryption code 7:exit The two parts of this I'm not gonna comment on - if you cannot understand what I'm doing in the code, I doubt you have a chance of understanding it at all - you should go read an assembly book! The encryption routine is as follows: enc_buffer: ; Procedure to encrypt a ; paragraph in cryptbuffr xor bp,bp ; xero bp encrypt_loop: sub byte ptr ds:[bp+cryptbuffr],1; encrypt byte at ; cryptbuffr +bp inc bp ; let bp point to the next byte cmp bp,10h ; are we done yet? jnz encrypt_loop ; next byte ret This encrypts the paragraph we have loaded onto thr cryptbuffr, using the above mentioned encryption method. The header calculation: calc_header: les ax, dword ptr [header+14h] ; Save old entry point mov word ptr [jmpsave], ax ; patch final-jump with entrypoint mov word ptr [jmpsave+2], es ^^^^ Here we get the original entry point from the original header. We patch this into the code we're gonna append in a short while les ax, dword ptr [header+0Eh] ; Save old stack mov word ptr [stacksave], es mov word ptr [stacksave+2], ax ^^^^^The same for the stack mov ax, word ptr [header + 8] ; Get header size mov cl, 4 ; convert to bytes shl ax, cl ; shl ax,4 <=> ax*16 xchg bx, ax ; let dx be the header size in bytes We now get the header size in paragraphs and convert it to bytes. les ax, dword ptr [newDTA+26] ; Get file size mov dx, es ; to DX:AX push ax push dx Get the filesize from the DTA that was build when we did the findfirst sub ax, bx ; Subtract header size from sbb dx, 0 ; file size Then subtract header size from the totalsize leaving DX:AX to be the image size in bytes mov cx, 10h ; Convert to number of paragraphs div cx ; form We then make a division with 10h to make it into paragraphs. AX is now the number of full paragraphs and DX is the number of bytes in the last. mov word ptr header+14h, dx ; Save New entry point in header mov word ptr header+16h, ax We now save this in the header so we have a correct entry mov word ptr header+0Eh, ax ; and stack Make the SS in the header point to where we are gonna add the code. pop dx ; get file length from stack pop ax Get the filelength again add ax,code_end-append_this ; add decrypter size adc dx, 0 find the length of the exe-file after we have appended our decryption program mov cl, 9 ; 2**9 = 512 push ax ; save ax shr ax, cl ror dx, cl stc adc dx, ax ; filesize in pages pop ax and ah, 1 ; mod 512 Transform it from bytes into pages. And find the number of bytes in the last page. mov word ptr [header+4], dx ; new file size mov word ptr [header+2], ax save this on the header push cs ; restore ES pop es We have been messing with ES - we better leave it pointing at CS Next I'll figure that you yourself can easily figure out how the rest of the steps works. Nothing in it is too difficult. So I'll move on to the decryption routine Append_this: ; decryption code starts here call next ; calculate delta offset next: pop bp ; bp = IP next sub bp,offset next ; bp = delta offset Given that in our original execrypter the offset of this is displaced with the number of bytes in all the code above this point +100h for the psp we need to figure out a nice way to point at our data. A call instruction pushes the return offset onto the stack. We use this and call the next instruction wich is a pop bp. So that the current address is in bp. We then subtract how much this code was displaced in our exe-encryption program and we now have a way of pointing to our data. push ds ; save ds and es (*IMPORTANT*) push es push cs ; DS = CS pop ds push cs ; ES = CS pop es We now use the codesegment as extrasegment and datasegment so we don't over write anything else and we can keep this little thing in one segment. We also remember to save the ds and es that we entered with since these will point to the PSP and therefore is our clue to where the image is loaded. ;*** Decrypt all paragraphs from start till end pop es ; fetch es pop ax ; fetch the address of the PSP We then fetch this right way. push es ; see to it that the stack is push ax ; unchanged Saving them again offparag: mov cx, 0ffffh ; mov cx, imagesize in paragraphs ; 0ffffh was patched by the ; encryption add ax,0fh ; let ax point to the last segment ; of the PSP the mov cx instruction was patched in the by the exeencrypter program before it was written to the exefile. with adding 0f to ax it now points to the last segment of the PSP - or the segment just before where the image was loaded. loopme: inc ax ; point one segment further mov ds,ax xor bx,bx ; byte ptr ds:[bx]= first byte in ds loopme1: add byte ptr ds:[bx],1 ; decrypt! this byte inc bx ; move the point to next byte cmp bx,10h ; are we done with this paragraph? jne loopme1 loop loopme ;**** end decrypt all paragraphs This is basically just the decryption routine. ;**** Decrypt the last couple of bytes offsetdx: ; Reference so we can patch ; next line mov cx, 0ffffh ; is patched to mov cx, ???? ; where ???? = number of bytes ; not in any paragraph The mov CX was also patched by the execrypting program before it was written to the exefile. CX is now the number of bytes in the last paragraph we hav'nt decrypted yet. test cx,cx ; is there more bytes to decrypt? jz no_more If this is zero bytes we just want to continiue inc ax ; Update ds to point to mov ds,ax ; current segment xor bx,bx ; ds:bx = first byte in current segment more: add byte ptr ds:[bx],1 ; decrypt it inc bx ; point to next byte loop more ; do it for all bytes nessecary no_more: Otherwise we should decrypt these bytes in that segment pop es pop ds mov ax,es ; AX = PSP segment add ax,10h ; Adjust for PSP We now fetch the es and ds as they were given to us when we started our code. We move it into ax and add 10 so that ax points past the PSP onto the start of where the image is loaded. add word ptr cs:[bp+jmpsave+2],ax add ax,word ptr cs:[bp+stacksave+2] We now add this starting segment to where we are gonna jump to and where the stack is gonna end up. cli ; Clear intrpts for stack manip. mov sp,word ptr cs:[bp+stacksave] mov ss,ax sti We now disable interupts so it all does'nt fuck up while were messing with the stack. We leave the stack pointing to where we would like it. db 0eah ; jmp ssss:oooo jmpsave dd ? ; Original CS:IP ea???????? is the opcode for a jmp to ????:???? so when we get here we'll jump right into the original execode since jmpsave has been patched by the execrypter and the starting segment of the program has been added. stacksave dd ? ; Original SS:SP Here the stack is patched by the execrypter That's it and that's that! Now as stated earlier - this code is simplyfied to the maximum, no error checking no user interface, no nothing. Any cracker with 3 mins of time could decrypt this no problem! But changing the encryption algorithm to something powerful, adding antidebug code and crc-sums should be very straight forward!! Another way to use this is to remove the encryption/decryption and instead make another program run before the exe... adding intro's to things you crack or whatever... Useful litterature: 40hex no.8 - article on exe-virii by Dark Avenger, various other 40 hex's, Ralph Brown's interrupt list, turbo assembler manual... I can be emailed as stone@ftp.one.se - I will try to answer all emails! Greets goes to my favorite bird in the world, Winterhawk, my regular cracking partner, landlord, cook and friend, Patriarch, Sci my drinking partner, Uvejr another drinking partner and last but not least Mr. Mox. This program, the sourcecode and txt file is to beconsidered beerware - meaning that if you like it and use it you gotta gimme a beer if you ever meet me! /Stone 1996