www.pudn.com > vgcrypt.zip > 75SRC.ASM
; ;**WARNING: Encryption and Decryption code closed to public for security (as if there ; were any). Everything else is here tho. ; ; ;VGCrypt PE Encryptor v0.75 Beta ;(c)1998 Virogen ;email: vgen@hotmail.com ;---------------------------------------------------------------- ; ; This is a fairly simple PE encryptor I wrote up. I commented everything ; that is relavent to PE appendation or insertion, more so than I needed to ; even. The most interesting feature of this encryptor is that it attempts to ; find a location to insert itself between object virtual size and the next ; file alignment boundary, thus not changing the physical file size. ; ; Note that this code is still under development. ; ; Features: ; -three types of PE parasticality : ; 1) install in cave - no physical size increase ; 2) append to last object ; 3) create new object ; -full win95/98/NT compliant ; -does not add new object unless you want to ; -stores correct new checksum of PE executable ; -preserves original file data/time and attributes ; -prompts u to encrypt again if file already encrypted, you can encrypt ; a file as many times as you want. Of course, eventually you will run out ; of available caves and the filesize will start increasing. ; ; Usage: ; -VGCRYPT filename ; ; ; known bugs: ; -might be problems with some encrypted DLLs. ; ; ; Note to coders: ; If you need to retrieve API RVAs in the decryptor, the best way is to ; manually import from the kernel32 export table in memory. The base of ; kernel32.dll can be obtained by using [esp] at entry and scanning down ; to the base. For more information, see my viral code at ; ; ; Updates: ; 11/26/98 v0.40: Initial release ; 11/27/98 v0.45: Added simple SEH anti-debugging code ; Fixed command line problem when running under winnt ; dos box ; Other minor changes in decryptor ; 11/28/98 v0.50: Fixed winNT image problems ; Added better anti-debugging code, but still needs improvements, ; optimization, etc. ; Closed decryptor and encryption source. Sorry, but all the other ; code is still open ; 11/28/98 v0.51: Reformatted decryptor a little, anti-debugging code still needs to ; be improved alot. ; 11/28/98 v0.54: Fixed exception when object with no physical offset/size encountered. ; Temporarily disabled portion of anti-debugging code. ; 11/29/98 v0.56: Source code beautification by Ghiribizzo . ; EBP now preserved correctly ; Minor winNT command line problem fixed ; Encrypted DLLs now load, but some have unusual problems ; Executables which are loaded at an image base other than the one ; specifid in the PE header (unusual) will now work properly. ; No longer flagged by AVP if appended to last object ; Note that when we reach v0.60 I will re-release the source ; code, with the noted exceptions of the encryption and decryption ; code. ; 11/30/98 v0.57: If no "caves" found, gives you choice of appending to last object or ; creating a new object. ; 11/30/98 v0.58: Minor bug fix in creating new object. ; Was missing last object while traversing thru object table, oops. ; 11/31/98 v0.60: Checks to make sure there is room to add another object to ; object table before allowing user to select this option. ; Other minor changes here and there. ; 12/05/98 v0.61: Now accounts correctly for PEs loaded at a differing image base. ; I had forgotten to recalculate encrypted object addresses. This ; was a minor bug because it is rare to have a PE loaded a base other ; than the one specified in the header. ; 12/09/98 v0.65: Now avoids encrypting import & export table no matter where it is at. ; Oops, should done this in the first place. ; 12/19/98 v0.75: Improved security a little bit, still not secure at all.. someday ; I will remedy this. ; ; ; ; ==================================================================================== ; Special thanks to Ghiribizzo who has provided more anti-debugging ideas than I can ; even hope to have time to implement . http://Ghiribizzo.home.ml.org ; Greetz lapse,jp,vecna,darkman,Iczelion, and everyone else. ; ==================================================================================== ; ; ; include mywin.inc ID_OFF equ 0ch ; offset of our marker in PE DECRYPTOR_SIZE equ (offset decryptor_code_end-offset decryptor_code) ; VIRTUAL_SIZE equ DECRYPTOR_SIZE MAX_OBJS equ 6 ; maximum objects we can handle ; by increasing this you are increasing the size ; of the table in decryptor by MAX_OBJS*8. .586p locals jumps .model flat,STDCALL extrn ExitProcess:PROC extrn CreateFileA:PROC extrn CloseHandle:PROC extrn ReadFile:PROC extrn WriteFile:PROC extrn SetFilePointer:PROC extrn MapViewOfFile:PROC extrn CreateFileMappingA:PROC extrn UnmapViewOfFile:PROC extrn SetEndOfFile:PROC extrn SetFilePointer:PROC extrn GetFileAttributesA:PROC extrn SetFileAttributesA:PROC extrn GetFileSize:PROC extrn GetTickCount:PROC extrn GetFileSize:PROC extrn GetFileTime:PROC extrn SetFileTime:PROC extrn CheckSumMappedFile:PROC extrn MessageBoxA:PROC extrn GetCommandLineA:PROC extrn lstrcat:PROC extrn IsBadReadPtr:PROC extrn WriteConsoleA:PROC extrn GetStdHandle:PROC extrn ReadConsoleA:PROC org 0 .data ; data object ; conditional compile ;console_app equ 1 ; cr equ 0dh lf equ 0ah tab equ 9 hline equ 196 marker equ 90909090h cr_lf_tab db cr,lf,tab,tab,0 init_txt db 50 dup(hline),cr,lf caption db 'Virogen''s PE Encryptor v0.75, (c)1998 Virogen[NOP]' ifndef console_app db 0 endif ifdef console_app db cr,lf,' email:vgen@hotmail.com',cr,lf db 50 dup(hline),cr,lf,0 endif badcmd_txt db 'Invalid command line!',cr,lf,'Usage: VGCRYPT filename',cr,lf,0 success_txt: ifdef console_app db cr,lf endif db 'Successfully encrypted!' ifdef console_app db 0 endif file_txt db cr,lf,' Installed on file: ',tab,0 db 400 dup (0) ; plenty of space obj_txt db cr,lf,' Installed in object: ',tab,0 db 9 dup(0) eobj_txt db cr,lf,' Encrypted objects: ',0 db (MAX_OBJS*8)+1 dup(0) hole_txt db cr,lf,'VGCrypt installed in alignment hole, with no phsyical size increase!',0 already_txt: ifdef console_app db cr,lf,'File appears to already be encrypted. Encrypting again.',0 endif db 'File appears to already be encrypted. Do you wish to encrypt again?',0 append_question db 'Could not locate any "caves" to install into!',cr,lf,'Click YES' db ' to create new object',cr,lf,'Click NO to append to last object.',0 error_txt: ifdef console_app db cr,lf,' ' endif db 'There was an error encrypting the file!',cr,lf ifdef console_app db 0 endif fname_txt db 'Specified file: ',0 db 260 dup(0) nohole_txt db cr,lf,'No available "caves" to install into, forced to increase physical size.',0 doing_obj_txt db cr,lf,'Encrypting object: ',tab,0 skip_obj_txt db cr,lf,'Skipping object: ',tab,0 found_hole_obj db cr,lf,'Found hole in object: ',tab,0 done_txt db '..Done',0 creation dd 0,0 ; our file time structures lastaccess dd 0,0 lastwrite dd 0,0 oldchksum dd 0 fsize dd 0 map_ptr dd 0 oldattrib dd 0 ; stored file attribs fnameptr dd 0 ; ptr to file name we're inf ptrpeheader dd 0 objPsize dd 0 maphandle dd 0 handle dd 0 objtblVA dd 0 objptr dd 0 lastobjimageoff dd 0 originalpsize dd 0 originalvsize dd 0 error db -1 ; importtbl dd 0 exporttbl dd 0 byteswrote dd 0 hstdo dd 0 hstdi dd 0 ynbuf db 0 use_hole db 0 holeptr dd 0 bad_otbl: ; this is the list of bad objs - did I miss any? dd 'rsr.' ; rsrc dd 'ler.' ; relo dd 'ade.' ; edata dd 'ete.' ; etext dd 'adi.' ; idata dd 'adr.' ; rdata dd 'slt.' ; tls dd 0 ;---- decryptor code installed into file ---- ; ; ; ;*** CLOSED SOURCE, for security ; ; ; ; --- end of decryptor code --- ; --- start of VGCrypt --- .code ; code object - change flags to rwx start: ifdef console_app call GetSHandle lea ebx,init_txt call WriteString endif call GetCommandLineA ; retrieve command line or eax,eax jz _exit_bad_cmd_line ; if none then abort /w msg xchg esi,eax sl: cmp byte ptr [esi],0 ; if first byte is NULL then something way wrong jz _exit_bad_cmd_line shl eax,8 ; rotate 1 byte in eax, for loop.. eax running load lodsb ; get next byte in al cmp eax,'rypt' ; end of our proggie name? jnz not_eoc cmp byte ptr [esi],'.' jnz esl not_eoc: cmp eax,'.exe' ; .exe end of our proggie name? jz esl cmp eax,'.EXE' ; .EXE end of our proggie name? jnz sl esl: lodsb cmp al,' ' jz esl cmp al,'"' jz esl dec esi esl2: cmp byte ptr [esi],0 ; if first char in parameter 1 is NULL then we fuq jz _exit_bad_cmd_line ifndef console_app push esi push offset success_txt call lstrcat ; append filename to success message endif ifdef console_app push esi push offset fname_txt call lstrcat lea ebx,fname_txt call WriteString endif mov fnameptr,esi ; set fnameptr->filename call EncryptFile ; go encrypt ;cmp error,-4 ;jz _exit cmp error,-1 ; error? jz _exit_error ; if so go display error message ifndef console_app push offset obj_txt push offset success_txt call lstrcat ; append object name we inserted or appending to push offset eobj_txt push offset success_txt call lstrcat ; append objects we encrypted cmp use_hole,1 jnz no_hole_msg push offset hole_txt jmp app_success no_hole_msg: push offset nohole_txt app_success: push offset success_txt call lstrcat push 0 push offset caption push offset success_txt push 0 call MessageBoxA endif ifdef console_app lea ebx,success_txt call WriteString endif xor eax,eax jmp _exit _exit_error: ifndef console_app push fnameptr push offset error_txt call lstrcat push MB_ICONEXCLAMATION push offset caption push offset error_txt push 0 call MessageBoxA endif ifdef console_app lea ebx,error_txt call WriteString endif mov eax,2 jmp _exit _exit_bad_cmd_line: ifndef console_app push MB_ICONEXCLAMATION push offset caption push offset badcmd_txt push 0 call MessageBoxA endif ifdef console_app lea ebx,badcmd_txt call WriteString endif xor eax,eax inc eax _exit: call ExitProcess,eax ;----------------------------------------------- ; encrypt file - call with fnameptr set ; EncryptFile proc mov eax,fnameptr push eax call GetFileAttributesA ; get file attributes mov oldattrib,eax cmp eax,-1 ; if error then maybe shared jnz not_shared ret ; can't encrypt it not_shared: push 20h ; +A mov eax,fnameptr push eax call SetFileAttributesA ; clear 'da attribs mov esi,fnameptr call OpenFile call test_error jnc open_ok ret open_ok: mov handle,eax push offset creation push offset lastaccess push offset lastwrite push eax call GetFileTime ; grab the file time xor ecx,ecx ; only map size of file call create_mapping ; create file mapping jc abort_encrypt ; eax->mapped file cmp word ptr [eax],'ZM' ; is EXE? jnz abort_encrypt call GetPEHeader ; load esi->PE Header push 2 push esi ; test ptr for read acces call IsBadReadPtr ; was ptr any good? or eax,eax jnz abort_encrypt cmp word ptr [esi],'EP' ; PE? jnz abort_encrypt cmp dword ptr [esi+ID_OFF],marker ; marker? jnz not_encrypted ; if yes, already processed ifndef console_app push MB_ICONHAND or MB_YESNO push offset caption push offset already_txt push 0 call MessageBoxA cmp eax,IDYES jnz abort_encrypt endif ifdef console_app lea ebx,already_txt call WriteString endif not_encrypted: call unmap ; unmap file mov ecx,1000h ; give us room to add to the file, if needed call create_mapping ; map file again jc abort_encrypt call GetPEHeader ; load esi -> pe header call GetTickCount ; get tick count mov key,eax ; save for encryption key mov dword ptr [esi+ID_OFF],marker ; save marker ;mov eax,[esi+imagebase] ;mov svd_imagebase,eax ; save the image base mov eax,[esi+datadir] mov importtbl,eax mov eax,[esi+edatadir] mov exporttbl,eax xor eax,eax mov ax, word ptr [esi+NtHeaderSize] ; get header size add eax,18h ; object table is here add eax,esi mov objptr,eax ; Let's check for existance of cave after object table in PE header ;mov edi,[eax+objpoff] ;add edi,map_ptr ;push esi ;int 3 ;push eax ;xor eax,eax ;mov ax,[esi+numObj] ;inc eax ;mov ecx,40 ;xor edx,edx ;mul ecx ;pop esi ;add esi,eax ;cmp esi,edi ;jge no_room_in_hdr ;xchg edi,esi ;mov cx,DECRYPTOR_SIZE ;xor eax,eax ;push edi ;repz scasb ;pop edi ;or cx,cx ;jnz no_room_in_hdr ;pop esi ;push esi ;mov eax,objptr ;mov ecx,[esi+filealign] ;mov ebx,[eax+objpoff] ;sub ebx,edi ;sub ebx,map_ptr ;mov [eax+objpoff],ebx ;mov holeptr,edi ;mov use_hole,1 no_room_in_hdr: ; Here we will traverse through the object table, encrypting objects ; which can be encrypted and also searching for a 'cave' big enough ; to hold us. ;pop esi push esi mov eax,objptr lea edi,otable xor ecx,ecx mov cx,[esi+numObj] ; get number of objects ; dec cx otbl_loop: cmp edi,offset otable_end-8 ; filled up table? jz next_obj call test_obj ; see if good obj name jc next_obj pushad cmp use_hole,1 ; already found hole? jz no_hole_here mov edx,[eax+objpsize] ; get obj psize mov ecx,[eax+objvsize] ; get obj vsize cmp ecx,edx ; any hole here? jge no_hole_here sub edx,ecx ; get size of hole cmp edx,DECRYPTOR_SIZE ; big enough for us? jl no_hole_here mov objptr,eax ; save ptr obj rec mov use_hole,1 ; set flag mov ecx,[eax+objvsize] ; encrypt vsize mov [edi+4],ecx ; if vsize object physical offset add esi,map_ptr mov edi,esi call encrypt_object ; encrypt the object pop edi ecx eax ifdef console_app lea ebx,done_txt call WriteString endif ifdef console_app jmp next_did endif next_obj: ifdef console_app lea ebx,skip_obj_txt call WriteString mov ebx,eax call WriteString endif next_did: add eax,40 ; increment to next object record loop otbl_loop done_otbl: pop esi ; restore ptr pe hdr cmp use_hole,1 jz found_hole sub eax,40 ; make sure nuff room to add another object before we ask if ; the user wants to. dunno if this is best way to do it, but I ; just scan to make sure the next 40 bytes (1 object record) are ; all 0. pushad mov edi,eax add edi,40 mov ecx,40 xor eax,eax repz scasb or ecx,ecx jnz go_append push MB_ICONQUESTION or MB_YESNO push offset caption push offset append_question push 0 call MessageBoxA cmp eax,IDNO jz go_append ;cmp eax,IDCANCEL ;jnz go_create ;popad ;mov error,-4 ;jmp abort_encrypt go_create: ; user selected to create new object popad mov ecx,[eax+objrva] ; get last object rva add ecx,[eax+objvsize] ; +=last object virtual size push eax ; save obj record ptr xchg ecx,eax ; eax=last object virtual end mov ecx,[esi+objalign] ; ecx=object alignment call align_fix ; go align da shiznit xchg ecx,eax ; ecx=next object's rva pop eax ; restore obj record ptr mov edx,[eax+objpoff] ; edx=last object physical offset add edx,[eax+objpsize] ; edx+=last object psize=obj pend add eax,40 ; goto next object in table (new) mov dword ptr [eax],'cgv.' ; set object name to .vgc mov dword ptr [eax+objpsize],0 ; set psize to 0, updated later mov dword ptr [eax+objvsize],0 ; set vsize to 0, update later mov [eax+objrva],ecx ; set rva of object mov [eax+objpoff],edx ; set physical offset of obj inc word ptr [esi+numObj] ; increment number of objects jmp after_apop go_append: popad after_apop: mov objptr,eax ; objptr->last obj found_hole: mov edi,objptr ; objptr->obj /w hole cmp holeptr,0 jnz go_pe_hdr_hole push edi push offset obj_txt ; strcat object name call lstrcat ; for display mov eax,[edi+objpoff] ; get object physical off mov lastobjimageoff,eax ; save it mov ecx,[edi+objpsize] ; get object physical size mov originalpsize,ecx ; save it 4 later mov eax,[edi+objvsize] ; get object virtual size mov originalvsize,eax ; save it cmp use_hole,1 jz psize_less_vsize cmp eax,ecx jge psize_less_vsize ; padded space for alignment? mov eax,ecx ; set vsize to psize psize_less_vsize: add eax,VIRTUAL_SIZE ; add our virtual size mov dword ptr [edi+objvsize],eax ; save new virtual size cmp use_hole,1 ; if using hole then add virtual size jz in_hole_no_psize mov eax,originalpsize ; get physical size of object add eax,DECRYPTOR_SIZE ; adjust physical size of object mov ecx,[esi+filealign] call align_fix ; on file alignment mov [edi+objpsize],eax ; now we must CORRECTLY calculate the new image size. This was the ; bug under winNT appendation. mov ecx,dword ptr [esi+objalign] ; get object alignment mov eax,dword ptr [edi+objvsize] ; add virtual size add eax,dword ptr [edi+objrva] ; +last object rva call align_fix ; set on obj alignment mov dword ptr [esi+imagesize],eax ; save new imagesize mov eax,originalpsize jmp not_in_hole_psize_entry in_hole_no_psize: mov eax,originalvsize not_in_hole_psize_entry: mov [edi+objflags],0E0000060h ; set object flags r/w/x/init data add eax,[edi+objrva] ; add last object's RVA ; eax now RVA of decryptor code jmp new_entry go_pe_hdr_hole: mov eax,holeptr sub eax,map_ptr new_entry: mov ebx,[esi+entrypointRVA] ; get original entry mov [esi+entrypointRVA],eax ; put our RVA as entry mov newep,eax ; save it for decryptor too mov [host_eip],ebx ; save it push esi ; CLOSED SOURCE mov edi,map_ptr cmp holeptr,0 jz not_hdr_hole mov edi,holeptr jmp copy_to_hdr not_hdr_hole: cmp use_hole,1 ; if in hole then install at end of vsize jz endvsize add edi,originalpsize ; add original physical size jmp go_copy endvsize: add edi,originalvsize ; add original virtual size go_copy: add edi,lastobjimageoff ; add object physical offset copy_to_hdr: lea esi,decryptor_code ; esi->decryptor code mov ecx,DECRYPTOR_SIZE rep movsb pop esi ; restore ptr pe hdr cmp use_hole,1 ; did we find hole to install in? jnz go_pad_fixup_size ; if no then we better pad mov ecx,fsize ; else new filesize=old filesize jmp go_fixup_size go_pad_fixup_size: mov ecx,[esi+filealign] ; calculate amt sub ecx,DECRYPTOR_SIZE ; of padding needed xor eax,eax rep stosb ; pad up object to alignment mov eax,map_ptr ; eax->beginning of mapped file sub edi,eax ; get difference from current ptr mov ecx,edi ; save file size go_fixup_size: push ecx ; ecx=real file size call unmap ; unmap file pop ecx push FILE_BEGIN ; from file begin push 0 ; distance high push ecx ; distance low push handle call SetFilePointer ; move file pointer to ; real EOF push handle call SetEndOfFile ; set end of file go_checksum: ; ; now we need to calculate checksum. We need to remap the file to get it ; right after file size change. I might be wrong about this, there could ; have been a bug in my code, but it seems resonable. ; xor ecx,ecx call create_mapping jc unmapped call GetPEHeader lea eax,[esi+checksum] push eax ; destination of checksum in hdr push offset oldchksum push fsize ; new file size mov eax,map_ptr push eax call CheckSumMappedFile call unmap mov error,0 ; if we made it here then no error jmp unmapped abort_encrypt: call unmap ;unmap if aborted infection unmapped: push offset creation push offset lastaccess push offset lastwrite push handle call SetFileTime ; restore orginal file time push handle call CloseHandle mov eax,oldattrib ; get original attribs push eax mov eax,fnameptr push eax call SetFileAttributesA ; restore the original attributes ret EncryptFile endp ; ; CLOSED SOURCE ; test_obj proc cmp dword ptr [eax+objpoff],0 ; make sure physical offset isn't 0 jz ret_stc cmp dword ptr [eax+objpsize],0 ; make sure physical size isn't 0 jz ret_stc call test_rvas jc ret_stc lea esi,bad_otbl ; scan thru bad obj bobj_loop: ; table xchg eax,ebx lodsd xchg eax,ebx cmp ebx,[eax] jz ret_stc or ebx,ebx jnz bobj_loop clc ret ret_stc: stc ret test_obj endp test_rvas proc pushad mov ecx,2 lea esi,importtbl rva_loop: mov edx,dword ptr [esi] mov ebx,dword ptr [eax+objrva] cmp ebx,edx jg not_bad jz ret_stc2 mov ebx,dword ptr [eax+40+objrva] or ebx,ebx jz ret_stc cmp ebx,edx jg ret_stc2 not_bad: add esi,4 loop rva_loop popad clc ret ret_stc2: popad stc ret endp GetPEHeader proc mov esi,[eax+3Ch] ; where PE hdr pointer is add esi,eax mov ptrpeheader,esi ; esi->PE Hdr ret GetPEHeader endp ; create_mapping - create file mapping of [handle] ; entry: ecx=+adjust mapping size ; create_mapping proc push ecx ; save additional mapping size push 0 ; high fsize storage, not needed push handle ; file handle call GetFileSize call test_error jc create_abort mov fsize,eax pop ecx ; restore map size push 0 ; no map name add eax,ecx push eax ; low size+vs push 0 ; high size push PAGE_READWRITE ; read&write push 0 push handle call CreateFileMappingA call test_error jc create_abort mov maphandle,eax push 0 ; # of bytes, 0= map entire file push 0 ; file offset low push 0 ; file offset high push FILE_MAP_WRITE ; access flags - read&write push eax ; handle call MapViewOfFile call test_error jc create_abort mov map_ptr,eax create_abort: ret create_mapping endp ; test_error - test API for an error return ; entry: eax=API return ; returns: carry if error ; test_error proc cmp eax,-1 jz api_err or eax,eax jz api_err clc ret api_err: stc ret test_error endp ; unmap file - Unmap view of file ; unmap proc push map_ptr call UnmapViewOfFile push maphandle call CloseHandle ret unmap endp ; sets eax on alignment of ecx ; align_fix proc xor edx,edx div ecx ; /alignment or edx,edx ; if no remainder then no next jz no_adjust inc eax ; next alignment no_adjust: mul ecx ; *alignment ret align_fix endp OpenFile proc push 0 push 20h ; attribute normal push 3 ; 3=open existing file push 0 push 0 push 0c0000000h ; permissions push esi call CreateFileA ret OpenFile endp ifdef console_app GetSHandle proc push -11 call GetStdHandle mov [hstdo],eax push -10 call GetStdHandle mov [hstdi],eax ret GetSHandle endp WriteString proc pushad mov edi,ebx xor eax,eax mov ecx,0fffh cld sc_loop: scasb ; find end of string jz fnd loop sc_loop fnd: mov ecx,edi sub ecx,ebx push 0 push offset byteswrote push ecx push ebx push [hstdo] call WriteConsoleA popad ret WriteString endp GetYN proc pushad lea edi,ynbuf xor eax,eax stosb dec edi push 0 push offset byteswrote push 1 push edi push [hstdi] call ReadConsoleA popad cmp ynbuf,'y' jz exit_y cmp ynbuf,'Y' jz exit_y stc ret exit_y: clc ret GetYN endp endif end start ends